sqlkit-0.9.5/0000755000175000017500000000000011714210425012367 5ustar sandrosandrosqlkit-0.9.5/setup.py0000755000175000017500000000564511714210425014116 0ustar sandrosandro#!/usr/bin/python """GUI to edit databases. Many backend supported (Postgres, Sqlite, MySql,...). Based on python, GTK, sqlalchemy. It's split into a GUI and a very rich package to create database interfaces """ classifiers = """\ Intended Audience :: Developers Intended Audience :: Education Intended Audience :: End Users/Desktop License :: OSI Approved :: GNU Affero General Public License v3 Operating System :: MacOS Operating System :: Microsoft Operating System :: OS Independent Operating System :: POSIX Operating System :: POSIX :: Linux Programming Language :: Python Topic :: Database :: Front-Ends Topic :: Software Development :: Libraries Topic :: Software Development :: Libraries :: Application Frameworks Topic :: Software Development :: Libraries :: Python Modules""" import os import sys try: from setuptools import setup, find_packages except ImportError, e: from distribute_setup import use_setuptools use_setuptools() from setuptools import setup, find_packages REQUIRES = [] f = open('sqlkit/__init__.py') for line in f: if line.startswith('__version__'): version = line.split()[2].strip("'") if sys.argv[1] == 'install': try: import pygtk pygtk.require('2.0') except ImportError: print "You need to install also pygtk and I was not able to work out" print " a correct dependency in setup.py" sys.exit(1) # setuptools really fails in understanding which packages are already installed # pip is much better! try: import sqlalchemy except ImportError: REQUIRES = ['sqlalchemy >= 0.5.4, < 0.7', ] try: import babel except ImportError: REQUIRES += ['Babel'] try: import dateutil except ImportError: REQUIRES += ['python-dateutil'] try: from sphinx.setup_command import BuildDoc class BuildDocFromDir(BuildDoc): def run(self): """ Run the normal build but first chdir to the documentation root. """ orig_params = [os.path.realpath( '.' ), self.builder_target_dir, self.doctree_dir] os.chdir( self.source_dir ) self.builder_target_dir = os.path.relpath( self.builder_target_dir, self.source_dir ) self.doctree_dir = os.path.relpath( self.doctree_dir, self.source_dir ) BuildDoc.run( self ) curdir, self.builder_target_dir, self.docree_dir = orig_params os.chdir( curdir ) cmdclass = {'build_sphinx': BuildDocFromDir} except ImportError: cmdclass = {} setup( name='sqlkit', version=version, description=__doc__, author='Alessandro Dentella', author_email='sandro@e-den.it', url='http://sqlkit.argolinux.org/', install_requires=REQUIRES, packages = find_packages('.'), classifiers= classifiers.split('\n'), include_package_data=True, zip_safe=False, cmdclass=cmdclass, entry_points = { 'gui_scripts': [ 'sqledit = sqlkit.scripts.sqledit:main', ] } ) sqlkit-0.9.5/changelog0000644000175000017500000007274611714210425014261 0ustar sandrosandrosqlkit - 0.9.5 - Feb 7 2012 * porting to SQLA 0.7+ * table: + if treestore is used, row deletions of a parent implies deletion of children + on image field, no entry to cshow image if the image is missing + added a tooltip for the image name * docs: minor changes * fields: enum now only suggest empty if null is allowed fixes in image rendering and fix in path * sqledit: fix in option parsing (Opt -T) * filter_panel: it's now possible to get rid of filters already added * minor fixes sqlkit - 0.9.4 - Apr 11 2011 * printing: This release adds support for printing from sqlkit using OpenOffice templates that in turn uses 'uno' module provided from the OpenOffice project and a custom system that allows to write report templates w/o any programming know-how. This system called oootemplates, can be used from any Python program independently from the rest of sqlkit. sqlkit - 0.9.3.2 - Mar 31 2011 * demo: - now the schema is more standard and can be fed to other db backend (but postgresql requires TRUE/FALSE and not 1/0) - the db is created using tempdir * mask: default layout now automatically sqitches to multicolumn if table has many fields * types: work around to Bytea fields that where not present in 0.5.* * completion: fix the case in which a search_field is defined in _sqlkit_field. previously this rased an error on completion. * totals: fix: pressing 't' on the table on a non -numeric field must not try to generate a total * setup: now creates sqledit suitable for each OS (Mainly now windows has its own one, the others where already ok) * sqledit: added option --create-tables, to create table after reading the models sqlkit - 0.9.3.1 - Mar 11 2011 * localization: added german localization * mask: fix in set_records() when pk option was used and no record found * fields: enum field clean_value should not validate NOT NULL sqlkit - 0.9.3 - Mar 3 2011 * table/mask: added possibility to duplicate a record * fields: added * images as different render options of a varchar field in model column definition * enum as varchar field as render option of varchar or integer field * now fields liste to changes in the model and react possibly changing the widget, this is an important step toward a better MVC support * completion: added possibility to add completion to fields that * normally don't have it (e.g.: numbers) * fix dynamic completion also in enum mode * fixed an invalidation error of the fey key * hooks: * on_change_value now requires one more arg and is issued als for masks when the value is changed (not when initially set) * generally more consistent and tested hooks * package: fix in the package that adds demo * docs: * completely new home page, with slider of features * updated docs on how to install as now all is much easier with pygtk-all-in-one installer * improved documentation on widgets and fields * dependancies: removed dependancy on glade!!! Now gtk.Builder is used * widgets: * fixed a useless dialog that prompted to save when not needed * improvement on some error messages * added signal record-new * table: added api: * added edited_row_model_path * get_selected_row() * get_obj_at_path() * hide_filed(field_name) * added possibility to add a row as child of another (when TreeStore is choosen) * totals: * now method sum has model, path and iter also to be able to build special totals of just some hierarchical level * added signal 'computed' when the sum is computed * miniwidgets: added signal set-value * layout: * now date widget does not expand anymore in useless mode. defaults to 10 chars * added tooltip do date popup to sy how to abort (Esc!) * fk_key now uses an icon in the widget rather that an arrow separately * demo: * added option -i that drops you in an interactive ipython shell if you have it installed * filters: added possibility to filter a foreign key on it's real value rather than default behaviour of remote value * table browsers: fixed table reflection that was not working ny more sqlkit - 0.9.2.2 - Feb 1 2011 setup: setup tuning in MANIFEST that didn't add demo and tests sqlkit - 0.9.2.1 - Jan 27 2011 translations: added polish translation docs: removal of javacript that I'm not sure can redistribute sqlkit - 0.9.2 -December 18 2010 setup.py: modified so that it should be easier to install by using simply 'python setup.py install'. Uses distribute_setup.py if needed to install distribute demo: now the model is recreated each time the demo is started debian: removed debian staff: Pietro Battiston started the debian packaging as a standalone project sqlkit -0.9.1 - Spetember 29 2010 usability: added tooltips in many menu entries backends: full support for firebird. _sqlkit_table is called sqlkit_table for that backend hooks: minor modification to 'on_activate' hook for table widget so that it behaves as 'on_activate' for masks fields: * added std_cleaup als as classmethod, usefull for integers and for some corner cases * lookup now raises a warning rather then an errore when a lookup returns multiple values sqledit: * minor improvement in the way fkey are represneted when introspecting a table * added sqledit customization via nick subdirectories of .sqledit. and possibility to use very simple addon table: * minor fix to RecordInMask defaults: improved docmentation on hook "on_completion" docs: improved documentation on completion, fields and validation validation: delayed till the save dialog pops the dfferences widget so that it's only triggered if needed dateedit: minor fix. DateEdit widget didn't set data from a handwritten entry in some circumstancies sqlkit 0.9 - September 8 2010 * support: fixes to sqlalchemy 0.6 (now my current reference) * tests: fixed some tests that didn't pass under python2.6 * orphan: changed the way orphan are handled, now an explicit parameter must be passed * layout: changed the way xml is read to bypass an odd segfault on accented letters * layout: fixed completely broken demo snippets * table: added possibility to hide some demos improved look of headers under std ubuntu theme * totals: minor fix * filter_panel: improved interaction (Ctrl-l, Ctrl-o, Ctrl-r) * filters: added negation of regexp for postgresql * mask: added a method to quickly add labels to frame (.set_frame_label) * docs: improved look and added section "advanced" * completion: minor improvement: now you can add 2 conditions on the same field in some occasions * sqledit: added icons for main toolbar actions * tooltips: added tooltip to some actions sqlkit 0.9.rc1 - August 7 2010 * support: * support for sqlalchemy 0.6 has been added * sqlwidget: * syntax change: now the first argument is not a keywork. It's the table_name/table/class/mapper. keyword args table, mapper class_ are now deprecated * improved handling of ForeignKey when the model has a backref that prevents orphans * added django-like syntax to .reload() * mapper: now the mapper can have a function * get_title() * now decimal from the keypad works nicely (inserts "." or "," according to locale) * fixed 'delete-event' callback: self.current needed to be cleaned and session-extensions needed to be cleaned to prevent several calls to hooks on commit/flush * now NumLock is ignored when using hot keys (Control-n, Tab...) was totally misleading * fixed 'set_mode' * added documentation on using UIManager proficiently * format: keywork format is handled over to related table too, as for col_width. * added hook 'on_pre_layout' to be able to set a mapping fields in hooks * now any char can be used for 'title' as is process by gobject.markup_escape_text * refresh after save. Now after each save a refresh is done so that any db-computed field is correctly displayed. This is propagated to the possibly existing parent widgets. * filters: * fixed filtering an integer with value 0 (previously was ignored) * filter for boolean: added IS NOT TRUE/ IS NOT FALSE * selection of widgets now reflects widget displayed * the filter_panel is now a view, you can highly customize the appearence, creating new fields or displaying fields from the record. Methods add_column and replace_column help you customize it. The possiblity to have grouped lines in the filter panel (as in a 2 level tree) is now supported. You can eve have totals in the filter panel * added notnull filter (tat was already documented) * completion: * added possibility to add attributes to completion result. That opens new possibility to on_completion hook * fields: * internal important restructure. * now imported from sqlkit.fields * fixed import loop. No gtk is implied when importing sqlkit.fields * Now attributes nullable and editable are propertyies and affect the widget, if any * now fields in a widget can be calculated from other fields. you can add a field just inheriting a sqlkit.field.Field and overwriting cleaned_value(). A field that is not persisted will be turned into read-only and will not be validated on saving. * now you can pass a mapping that forces which field will be used, a signal has been added to be able to do that in Hooks. * slightly changed behaviour of BLANK_OK in fields. Now no change is proposed on fields that are not edited. * table: * views have been added. The same model can now be represented in differet TreeViews displaying possibly different columns. Views have the selection sincronized so that get_selected_obj() point to the same object. (Note that 'button-press-event' call back now needs one more arg, the view). * added possibility to sort locally (w/o hitting the database). This also adds the possibility to sort related tables. * Control-x is now an alias for Control-k * Improved RecordInMask (minor fixes). Added Control-m to start it. * FKeyRecord now can use a custom layout/nick * fix in get_value() * better edit experience (no more vanishing text if not saving) * several minor fixes * totals: added possibility to hide the grand-total * totals: added the local sort tat is faster as it's done in the treeview, w/o fetching from the database. * Total and sorting in tables works for not persisted fields as well. * subtotal can be toggled using the letter 'b' on a focused treeview (record...) * export is now different for each view * record_in_mask now checks for usaved record in session * record_in_mask/fkey_record_in_mask can be called programmatically * hooks: added on_activate that was not implemented for tables * self.current now points to the currently edited OR SELECTED record. previously was only on currently edited on None. This makes it much more usable. * demo: * Fixed demo (opt -g) * tests: * added some tests * mask: * added signal 'pre-display' * fixed BooleanNullRenderer that occasionally failed to set correctly the values * layout: fixed alignment problem that prevented from being able to set entry length sqlkit 0.8.7 - December 21 2009 * docs: general improvement: - started backword incompatibilities - fix errors and missing parts on hooks - completion: fixex and integration * completion: improved warning messaged on lookup lookup values that use search_filter as in '%(field_name)s', will now use DictLike to prevent empty field_name values to be filled with ugly looking 'None' * filters: * django syntax now has also 'notlike', 'inotlike' and 'notin' * dates now accept also date object * added reasonable message when filter values cannot be converted to usable values * diff dialog: - improved representation of differences - now it's possible to open the dialog from menu (not only when saving) * using generic selectable: - fixed sorting with joined selectables * portability: - minor fixes to be used on MacOS! - diminished warnings when used with gtk 2.16 - forced cast to unicode to prevent some mistakes in menus with diacritics under windows. * usability: - column header now hilight to show it's sensitive - RecordInMask and FKeyMask now use different icons - zoom-fit for tables is bound to Control-z - sqledit has now --limit option - a right click in a ForeignKey in a Mask allows to edit the foreign Mask (as was already possible with Table) - filter panel's output treeview selection now follow represented object in mask - completion improved: when text matches possible solution no entry-completion is used and value is accepted * internals: - a modelproxy has been added to allow both liststores and treestores - rationalized get_selected_obj() and get_current_obj() and self.current * signals & hooks: * added record-saved * fixes in 'on_field_name_validation' when field is fk * defaults: fixed several bugs in default module, added tests * sqlwidget: introduced a new and easy way to set the mode of a sqlwidget: mode can be iudb (INSERT, UPDATE, DELETE, BROWSE and more) * tables: Now Table can show content as hierarchy on objects sqlkit 0.8.6.1 - September 29 2009 * translation: added spanish translation * datetools: added @2m syntax and added Y-1m capability sqlkit 0.8.6 - September 10 2009 * package: - improved setup.py: install_requires is now handled by the script rather than by setuptools since often setuptools fails detecting already present packages - debian package now depends on psycopg & mysql also - added binary version via pyinstaller sqlkit 0.8.6 rc5- August 6 2009 * sqledit: - gui improved - possibility to edit directly the configuration tables - improved the editing mode of the configuration tables via completion - added toggle of casting of blank fields to NULL - improved documentation - added dialog to interactively write backend uri: it may attempt connection immediately - added possibility to run the demo from the starting dialog - added possibility to count records in a table - modified to be used inside pyinstaller - added workaround when LANG is not defined - bug fixes: sometime was not possible to add new records - now delete-orpahn is correctly handles at least in simple cases * completion: - added dynamic filters for Foreign Key - added set_values - fix fixed group_by - fixed completion + postgres inn start mode (was always regexp) - corrected markup excape for completion - fix autocompletion on SimpleCompletion * sqlwidget: - now resize accepts also -1 to let the present value - improved has_changed for FKey field and Collection (m2m) - added post-commit signal - deleting a record without having the privileges now correctly handles the (not) deletion of the row from the gtk table. Similarly when adding a record. - differences dialog: db NULL values will show up as None (rather than '') - order_by: improved, now several columns can be addressed, and will be translated in column object behind the scene to ensure good behaviour even in joins. * table: - now clicking on a non selected table wwill not toggle a boolean field. Everithing behaves better... - fix in totals. Subtotal by year raise exception if missing - fix in the way a new record is detected in m2m - added possibility to set the layout nick for foreign key columns; this is the nick of the registered layout we want to use in a mask used to edit a foreign key starting from the menu of the cell - improved cell validation. Two more bugs fixed - fixed the delete record function that removed the object even when eletion was aborted by backend - fixed case in m2m relation in which no more that one row could be added * filters: a wrong relative-date string will pop a dialog box rather than an unhadled exception * db.utils: - added get_fields - fixed dateutils that evaluated TODAY at import time - added get_create_statement for single tables - added undocumented sqlkit.misc.conf with reader of the configuration (.sqledit) * many bug fixes: - typo in completion that prevented filters - improved parsing in timedelta ugly widget - label maps now works as expected sqlkit 0.8.6-rc4 - * completion: deep rewrite that enable many fetures: * now completion is configurable directly with sqlalchemy syntax also and is more clear what you expect. * conditions can be set on relation also * a bunch of tests show exactly the expected behaviour * some more examples have been added * added possibility to have 2 completion behaviuor on the same field * completion now works also on fkey of an m2m * filters: * added '@' syntax for sqlkit.misc.datetools * filters now work for expressions too. See example ex_30. * signals: * added 'changed_context' signal for tables to make it easy to follow changes in RecordInMask/RecordMaskFkey * hooks: added after_flush_postexec hook * sqlwidget: * DifferencesDialog (to show which differences need to be saved) was improved * table: Added default menu entry to edit the ForeignKey field when clicking on a cell that holds a ForeignKey. This feature is called RecordMaskFkey * utils: Added register_class/get_class to store/restrieve classes. Needed by RecordMaskFKey * miniwidgets: fixed nullable boolean widget * postgres: added handling of pg_interval sqlkit 0.8.6-rc3 - * hooks: - hooks are now in a separated class that can be registered globally under a nickname. If present 'default' hook is used - added after_flush and on_init * layout: now layout can be registered globally in the same way hooks can. * validation: - added ValidationWarnings that allow to warn the user but don't abort saving operation - fix cases in which validation didn't allow to save * fixes: after_flush signal * fields: get_human value returs a formatted value (date/numbers) * mask: fix tips. In some cases accented letters would crash right cick on label pops info on field * mysql: fixed reflection of some Types and REGEXP operator * export: now ForeignKey are translated on exporting * table: better with of columns some Types are mapped to Varchar and handled (eg. PGInet) Modify menu allows to re-show columns that have been hidden interactively added function select_path sqlkit 0.8.6-rc2 - March, 26 2009 * sqlwidget: record_has_changed forces setting attributes in the object: this makes validation more stable save dialogs: much better representation of differences that are to be saved readonly mode now correctly sets actiongroup_edit not sensitive * uimanager: menus are now handled with uimanager so that was possible to make them sensitive o context * signals: added signal after-flush (added demo) added delete-event to sqlwidget 'button-press-event'now has also a 'menu' arg * filters: filters on relationships are now aliased (see note in docs on relationship) * hooks: hooks have been added: + on_completion__%s + on_change_value__%s + on_validation + on_field_validation__%s + on_activate__%s Added 5 examples in demo * table: added popup menu on right click on records to: + delete + view in mask (see below) + add new record popup menu is changable in a simple way from 'button-press-event' signal (see demo) better handling of silent deletion of object not changed added View Record that can also show with a layout (see demo) * miniwidgets: rewritten DateEdit to warn and complein of parse errors * django2sa: now join are made in a way that is accepted by sa rel 0.5+ * utils: fixed get_differences to return all modified attributes and added get_history * validation: no validation on empty values (if not nullable is triggered just before saving) * fixes: as usual many fixes: - back the possibility to delete foreign keys in table - completion in m2m corner cases - fixed hide_fields() * fields: float now have format=None you can now set BLANK_OK globally if instead of setting it on all fields * demo: many more examples and added not nullable fields and set cascade on relation between director and movies. sqlkit 0.8.6-rc1 - March, 10 2009 * table: * complete restructure of TreeColumn/CellRenderers creation now will be easyer to customize them * fix in height in netsed table definition (e.g.:m2m=field:3) * great improve in dialog on saveing/deleteing records now is possible to see changes * added signal 'press-button-event' * improved detection of useless save dialog * added Contrl-x shortcut as Contr-k * fix in deleting when in m2m mode: obj was not expunjed * date cell renderer now is a normal varchar. t loast calendar but can be deleted!!! * order_by: now column obj are used that make it possible to use it also when join constraints are used * gui_fields: now any loop on fields is done on gui_fields, that can be mapped in the class (persisted) or not. irst step to adding normal, not presisted, fields. No longer vfield list. * validation: this is the main improvement of this release. Any check that is known by the fields is done before committing AND all errors are dispayed in a single dialog In table: now table correctly handles errors in validation: it's not possible to leave a field with wrong value. Nullable values are checkd only when committing * mask: * added undo menu entry (trivial,triggers discard_changes) * layoutgenerator; change in syntax. Now TXS=description will correctly add label while TXS==description will not * MultiLine: fix for multiline * input: now keypd work correctly and handles KP_Decimal according to locale, when in interger/decimal or float fields * fields: * fixed default format for floats. Now #.###.@. Before it truncated decimals * added get_human_value() * defaults: it's now possible to have defaults local to an instance of sqlwidget * sqledit: improved GUI(now open correct N. of lines _ C-l) * completion: added 'enum' mode to mimic enum widget with doubleclick several bug-fixes * debug: get_widget_signals now returns signals for all __bases__ * layout: stock imaes can be used as in i=gtk-save * layout: added MenuItem that accepts StockItem and Text. * defaults: now local defaults act as global defaults (eg: fk method) * sqlwidget: title can be set in any moment sqlkit 0.8.5.1 - 23.1.09 * fix release: + filter resulted not working + sqlite demo database was corrupted + completion fixes * added labels for explanation of operators * uses clean_value to validate input from filters, so that float and dates are correctly parsed * improved completion behaviour: when a match is selected the editing finishes * table columns: added an informative label * improved browsing of data: now boolean and date/datetime do not enter in edit mode directly * status bar: added a "seconds" argument indicating how many seconds a message should stay in the status bar * widgets: resize() uses idle_add() so does not interfere width algorithm that decides optimal width sqlkit 0.8.5 - 16.1.09 * localization: added localization for numbers and dates added localization for messages -- now waiting for translations ;-) -- * table: - added a smart way to automatically set dimentions of the table based on info retrieved from the database (thanks to Pietro Battiston) - complete rewrite of the column setup function - added a basic multiline cellRenderer - grately improved the navigation with Tab that now correctly triggers validation/completion * fields: many improvements in clean_value/validation * mask: - added handling of comments - fixed integer/float miniwiget (0 was rendered as '') - text now uses gtk.WRAP_WORD - fixed current_idx was a class attribute not an instance attr * layout: added check and hints on errors for Panes widgets * django_syntax: fix in typo * filters: fixed handlung of boolena & NULL check in related tables * signals: - record_selected -> record-selected to follow gtk standard - added records-displayed * widgets/layout: added label_map support * sqlwidget: rationalized menu entries sqlkit 0.8.4 - 20.12.08 * totals: fix in date when date is missing * completion: many fixes group_by beahaviour now works correctly .filter works also for fkey fields * postgres: fixed PGChar handling * sqledit: implemented 'copy' feature in .sqleditdb * table: added sort DESC entry in each column added export in .csv format of a table added entry to hide/show columns set_field_list: now columns are rearranged to reflect list order * speed: many fixes to speed up browsing of data when working with a remote db: + session: 'expire_on_commit=False' as default of dbProxy + table: fixed caching of values * debug: fixed a situation in which printing debug caused an error added a function that prints metadata.create_all() statement * django_syntax: cleanup and creation of tests * filter_panel: added 'tree' attribute that determines if records in output page must be rendered as tree * icons: fixex bug that prevented to show some icons in Windows * mask: added set_records (as table has) * demo: more demo examples added button to pop debug * join: introduced join between table (tables="table1 table2" not yet documented) * m2m: introduced 'set_editable' attribute (feature still in progress) sqlkit 0.8.3.3 - 22.11.08 * import statement - complete restructure: 1. now SqlMask and SqlTable are imported from sqlkit.widgets. 2. dbg now works for gtk debug as well (was broken after unifyinng debug , layout and sqlkit package) * DbProxy.get_session returns a session with autoflush=False * unsaved_changes_exist: now uses session.is_modified(passive=True) to prevent flushing if the session has autoflush=True. Needs sqlalchemy after 5312 * m2m: now m2m uses a separate completion so that fields can not be edited but onnly picked up. Now adding a record does not save, to prevent flushing a (possibly) not yet completed field in master SqlMask * completion: uses self.format field to show the possible completions, that can be set in _sqlkit_table or via sqlkit.db.utils * table: added method set_opts * defaults: improved (but far from optimal) handling of defaults: not callable default set in sqlalchemy are used. * fixes: many minor fixes sqlkit 0.8.3.2 - 12.11.08 * fixed sqlite reflection for SLDate & SLDateTime sqlkit 0.8.3 - 10.11.08 * Initial Release. sqlkit-0.9.5/pyinstaller-sqledit.spec0000644000175000017500000000230711714210425017256 0ustar sandrosandro# -*- mode: python -*- # spec file for pyinstaller. Read README.pyinstaller for more info import os import sys ROOT = os.path.dirname(sys.argv[1]) + '/' #sqlkit = Tree(ROOT + 'sqlkit', prefix='sqlkit') icon = Tree(ROOT + 'sqlkit/layout', excludes='*.py') locale = Tree(ROOT + 'sqlkit/locale', prefix='sqlkit/locale') demo = Tree(ROOT + 'demo', prefix='demo', excludes= ROOT + 'demo/layout') a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), ROOT + '/bin/sqledit'], pathex=[os.path.abspath(os.path.dirname(sys.argv[0]))] ) pyz = PYZ(a.pure ) exe = EXE(pyz, a.scripts, exclude_binaries=1, name=os.path.join('build/pyi.linux2/sqledit', 'sqledit'), debug=False, strip=True, upx=True, console=1 ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, demo, icon, locale, #sqlkit, strip=False, upx=True, name=os.path.join('pyinstaller', 'sqledit') ) sqlkit-0.9.5/demo/0000755000175000017500000000000011714210425013313 5ustar sandrosandrosqlkit-0.9.5/demo/__init__.py0000644000175000017500000000000011714210425015412 0ustar sandrosandrosqlkit-0.9.5/demo/demo_tour.py0000644000175000017500000003634511714210425015675 0ustar sandrosandro# DISCLAIMER: This is an awful peace of code... I'm aware of it # but it works and it dosn't hurt anybody it you don't try to read it!... import os import re import tokenize import keyword import gtk import gobject import pango from sqlkit.layout import Layout, layout from sqlkit import debug as dbg ( TITLE_COLUMN, MODULE_COLUMN, DEMO_COLUMN, ITALIC_COLUMN, MAPPED, TOOLTIP, ) = range(6) CHILDREN_COLUMN = 3 class InputStream(object): """ Simple Wrapper for File-like objects. [c]StringIO doesn't provide a readline function for use with generate_tokens. Using a iterator-like interface doesn't succeed, because the readline function isn't used in such a context. (see /tokenize.py) """ def __init__(self, data): self.__data = [ '%s\n' % x for x in data.splitlines() ] self.__lcount = 0 def readline(self): try: line = self.__data[self.__lcount] self.__lcount += 1 except IndexError: line = '' self.__lcount = 0 return line class Demo(object): example_pattern = 'ex_.*.py$' def __init__(self, pattern=None, xml=True, debug=False, exclude="demo.py"): self.pattern = pattern or self.example_pattern lay = """ {V.header } {v {h TVS=a:120.300 {N TXS=txt:250. TVS=tree TVS=el TVS=pack TXS=xml} } TXS=note } """ self.l = Layout(lay, opts="s", title="Esempi" , geom=(600,700)) self.l.notebook('N.0', ['src','tree', 'widget properties','pack properties','xml']) self.w = self.l.show(x='q') if not xml: self.w['N.0'].remove_page(-1) tx_src = self.w['TX=txt'] tx_note = self.w['TX=note'] tx_xml = self.w['TX=xml'] self.src = tx_src.get_buffer() self.xml = tx_xml.get_buffer() self.configure_buffer(self.src) self.note = tx_note.get_buffer() self.tv_tree = self.w['TV=tree'] self.create_widget_tree(new=True, toplevel = self.w['Window']) self.ts_el = self.prepare_treeview_elem(self.w['TV=el']) self.ts_pack = self.prepare_treeview_tree(self.w['TV=pack']) self.demos = self.create_demos(self.pattern, exclude) self.tv = self.w['TV=a'] self.model = self.configure_treeview(self.tv, self.demos, self.src, self.note) self.prepare_actions() # self.l.menu('m=_File', # ('inspect widgets' , 'activate', self.pop_inspect, ), # ('gtk-quit' , 'activate', gtk.main_quit, ), # ) def prepare_actions(self): UI = """ """ self.actiongroup = gtk.ActionGroup('Main') self.actiongroup.add_actions([ ('TbMain', None, None), ('Execute', 'gtk-execute', 'Debug', None, "Debug via a gtk logger", self.execute_clicked_cb), ('Quit', 'gtk-quit', None, "q", None, gtk.main_quit), ]) self.ui_manager = gtk.UIManager() self.ui_manager.add_ui_from_string(UI) self.ui_manager.insert_action_group(self.actiongroup, 10) self.accel_group = self.ui_manager.get_accel_group() self.w['Window'].add_accel_group(self.accel_group) header = self.w['V.header'] toolbar = self.ui_manager.get_widget('/TbMain') header.add(toolbar) header.show_all() def configure_treeview(self, treeview, demos, src, note): model = gtk.TreeStore( gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN, gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, ) treeview.set_model(model) selection = treeview.get_selection() selection.set_mode(gtk.SELECTION_BROWSE) treeview.set_size_request(200, -1) sections = [] for ex in demos: if ex.section not in sections: sections += [ex.section] iters = {} for section in sections: iter = model.append(None) model.set(iter, TITLE_COLUMN, section, ) iters[section] = iter for demo in demos: iter = model.append(iters[demo.section]) model.set(iter, TITLE_COLUMN, demo.title, MODULE_COLUMN, demo.filename, DEMO_COLUMN, demo, TOOLTIP, "%s\n\n%s" % (demo.title, demo.doc), ) cell = gtk.CellRendererText() column = gtk.TreeViewColumn("Widget", cell, text=TITLE_COLUMN) treeview.append_column(column) selection.connect('changed', self.selection_changed_cb) treeview.set_tooltip_column(TOOLTIP) treeview.expand_all() treeview.show_all() return model def model_foreach_func(self, model, path, iter): name = model.get_value(iter, 1) if name: self.snippet_names += [name] def get_names(self): self.snippet_names = [] self.model.foreach(self.model_foreach_func) return self.snippet_names def execute_clicked_cb(self, widget=None): """ toggle visibility of the window that shows debug """ try: logger = dbg.get_logger() if logger.easytv.w['Window'].get_property('visible'): logger.hide() else: logger.show() except: pass def selection_changed_cb(self, selection): model, iter = selection.get_selected() if not iter: # get_selected returns None when you click on the arrow return False if model.iter_has_child(iter): return True if not iter: return False demo = model.get_value(iter, DEMO_COLUMN) self.load_module(demo) self.l.sb("file: %s" % demo.filename) def load_module(self, demo): self.insert_source(demo.body, self.src) self.note.set_text(demo.doc) ### exec the example GLOB = {'Layout': Layout, 'layout': layout, 'gtk': gtk} execfile(demo.filename, GLOB) self.xml.set_text(GLOB['l'].xml()) self.last_lo = GLOB['l'] self.last_w = GLOB['w'] self.prepare_treestore_for_elements(GLOB['l'].elements) self.create_widget_tree(toplevel=GLOB['w']['Window']) GLOB['w']['Window'].set_title(demo.filename) self.w['Window'].set_title("Esempio: %s" % (demo.filename)) return GLOB['l'] def load_module_by_idx(self, idx): """ Used to open directly an example:: demo.py 03 """ for demo in self.demos: if demo.idx == idx: return self.load_module(demo) def prepare_treestore_for_elements(self, elements): tv = self.w['TV=el'] ts = self.ts_el ts.clear() for el, val in elements.iteritems(): if not isinstance(val, int): iter = ts.append(None) ts.set(iter, 0, el, 1, val.gtkClass) # properties for p,v in val.properties.iteritems(): child_iter = ts.append(iter) ts.set(child_iter, 2, p, 3, v) ts = self.ts_pack ts.clear() for el, val in elements.iteritems(): if not isinstance(val, int): iter = ts.append(None) ts.set(iter, 0, el, 1, val.container_flag, 2, val.gtkClass) # PACK properties for p,v in val.pack_properties.iteritems(): child_iter = ts.append(iter) ts.set(child_iter, 3, p, 4, v) def prepare_treeview_elem(self, tv): tv.set_rules_hint(True) selection = tv.get_selection() selection.connect('changed', self.selection_changed_prop_cb) ts = gtk.TreeStore(str,str, str, str) tv.set_model(ts) ## TreeViewColumn & CellRenderer heads = ['elem', 'class', 'property', 'value' ] for i, v in enumerate(heads): tvc = gtk.TreeViewColumn(v, gtk.CellRendererText(), text=i) tvc.set_sort_column_id(i) tv.append_column(tvc) return ts def prepare_treeview_tree(self, tv): tv.set_rules_hint(True) ts = gtk.TreeStore(str, str, str, str, str) tv.set_model(ts) ## TreeViewColumn & CellRenderer heads = ['elem', 'container', 'class', 'property', 'value' ] for i, v in enumerate(heads): tvc = gtk.TreeViewColumn(v, gtk.CellRendererText(), text=i) tvc.set_sort_column_id(i) tv.append_column(tvc) return ts def selection_changed_prop_cb(self, selection): model, iter = selection.get_selected() if not iter: return False ## get the class name, position 1 el_key = model.get_value(iter, 0) class_name = model.get_value(iter, 1) data = [] for prop in gobject.list_properties(class_name): el = self.last_lo.elements[el_key] try: val_lay = el.properties[prop.name] except KeyError: val_lay = None try: val_gtk = self.last_w[el_key].get_property(prop.name) except TypeError: val_gtk = None data.append((prop.name, prop.nick, prop.value_type, val_gtk, val_lay)) TreeShow(data) def pop_inspect(self, *args): dbg.gtk_dbg.show_widgets() def create_demos(self, pattern=None, exclude=None): file_list = [x for x in os.listdir('.') if re.match(pattern, x) and not re.match(exclude, x)] file_list.sort() demo_list = [Example(ex) for ex in file_list] return demo_list def create_widget_tree(self, new=False, toplevel=None): from sqlkit.debug import gtk_dbg global show_widgets global model if new: show_widgets = gtk_dbg.show_widgets(lay=False) model = show_widgets.create_model(self.tv_tree) show_widgets.fill_model(toplevel, model) self.tv_tree.expand_all() def insert_source(self, data, buf): buf.delete(*buf.get_bounds()) iter = buf.get_iter_at_offset(0) #print buf.get_tag_table() last_erow, last_ecol = 0, 0 was_newline = False # multiline statement detection for x in tokenize.generate_tokens(InputStream(data).readline): # x has 5-tuples tok_type, tok_str = x[0], x[1] srow, scol = x[2] erow, ecol = x[3] # The tokenizer 'eats' the whitespaces, so we have to insert this again # if needed. if srow == last_erow: # Same line, spaces between statements if scol != last_ecol: buf.insert_with_tags_by_name(iter, ' '*(scol-last_ecol), 'source') else: # New line. # First: Detect multiline statements. There is no special in the tokenizer stream. if was_newline is False and last_erow != 0: buf.insert_with_tags_by_name(iter, ' \\\n', 'source') # new line check if it starts with col 0 if scol != 0: buf.insert_with_tags_by_name(iter, ' '*scol, 'source') last_erow = erow last_ecol = ecol if tok_type == tokenize.COMMENT: was_newline = True # newline is in tok_str included. buf.insert_with_tags_by_name(iter, tok_str, 'source', 'comment') continue elif tok_type == tokenize.NAME: if tok_str in keyword.kwlist: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'keyword') continue elif tok_type == tokenize.STRING: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'string') continue # No special format for use. Check for newline. was_newline = tok_type in (tokenize.NEWLINE, tokenize.NL) buf.insert_with_tags_by_name(iter, tok_str, 'source') def configure_buffer(self, buf): #print "Configuro %s" % buf tag = buf.create_tag('title') tag.set_property('font', 'Sans 18') tag = buf.create_tag('source') tag.set_property('font', 'monospace') tag.set_property('pixels_above_lines', 0) tag.set_property('pixels_below_lines', 0) tag = buf.create_tag('keyword', foreground='#00007F', weight=pango.WEIGHT_BOLD) tag = buf.create_tag('string', foreground='#007F00') tag = buf.create_tag('comment', foreground='#7F007F', style=pango.STYLE_ITALIC) def iconify(self): self.w['Window'].iconify() class TreeShow(object): def __init__(self, data): l = layout.Layout( "TVS=a", opts="Vs") w = l.show() tv = w['TV=a'] ts = gtk.ListStore(str, str, str, str, str) tvc = gtk.TreeViewColumn('prop', gtk.CellRendererText(),text=0) tvc.set_sort_column_id(0) tv.append_column(tvc) cell = gtk.CellRendererText() #cell.set_property('style', pango.STYLE_BOLD) tvc = gtk.TreeViewColumn('lay val', cell, text=4) tvc.set_sort_column_id(4) tv.append_column(tvc) cell = gtk.CellRendererText() cell.set_property('style', pango.STYLE_ITALIC) tvc = gtk.TreeViewColumn('gtk val', cell, text=3) tvc.set_sort_column_id(3) tv.append_column(tvc) cell = gtk.CellRendererText() cell.set_property('style', pango.STYLE_ITALIC) tvc = gtk.TreeViewColumn('nick', cell, text=1) tvc.set_sort_column_id(1) tv.append_column(tvc) tv.set_model(ts) for i in data: iter = ts.append(None) ts.set(iter, 0, i[0], 1, i[1], 2, i[2], 3, i[3], 4, i[4]) class Example(object): def __init__(self, filename): self.doc, self.body = self.readexample(filename) self.filename = filename self.description = self.doc.splitlines()[0] self.doc = "\n".join(self.doc.splitlines()[1:]) # ex_01b_import_table.py m = re.match('(ex_)?(?P.*?)_(?P.*).py', self.filename) self.idx = m.group('idx') self.name = m.group('name') self.section, self.title = self.description.split('/') def readexample(self, file_name): """ read the __doc__ of the example and separate it from the body """ idf = file(file_name,'r') txt = idf.read() idf.close() pat = re.compile(r''' ^"""(?:(?P.*?)""")\s*(?P(.*)) ''', re.MULTILINE| re.DOTALL|re.VERBOSE) m = pat.search(txt) if m: return m.group('doc','body') else: return (None, None) def __str__(self): return self.filename def __repr__(self): return "" % self.filename sqlkit-0.9.5/demo/layout/0000755000175000017500000000000011714210425014630 5ustar sandrosandrosqlkit-0.9.5/demo/layout/00_frame.py0000644000175000017500000000035211714210425016573 0ustar sandrosandro"""Bricks/frames An example of frame """ lay = """ uno {|T.table abra cadabra dumba balu } - """ import gtk l = Layout(lay, opts="-") w = l.show() #w['F.table'].set_label('frame label') sqlkit-0.9.5/demo/layout/cal.gif0000644000175000017500000000351011714210425016055 0ustar sandrosandroGIF89a UQQU`[^YdYQ^[X[b0X)c&q0g+n8j/j0q3o9rDmH`0}2ug_ǂYjRFWjݾcof}8sE"GN9p̅[T \u1v=mҥ +Mۆ=k/5eD?j/*haeHɒ ,k@Fl4kwM_0G"Cp<R,0 6(P!C%AK#eXAb PN¢. XPO$sd8X!N&?p7٣g+^V,Q|SÔtDU-}@ O< C'èM-B/B+`Byp8@"ߌ#4jE)?RH;3̊y7@O>T +ɴ =ɬc/d0 Waڒd=:xҎ D x/hW"BsI.`+ > 4N ` i9S*R H;3;ANsh>,23HB/(0" rhR \IB7N;dY 4 >:@1N#|X 1@q$څE,0 CN#4C2J;tM\L @ x<<'7P17"T .hh2lM3<(? n̠!0Po\>؁%2(648@xB dp`S zG(86 $A:>FRh[l1G/LA A/3?59Fq%1EtAI@B$QLȃL"h1 @8G2袇-bFc4h" @*P )?q  PCd|؅L[L#dVb 2 _c 1/QJRy`F=ӝF y&<|;"鏬-tyG?OF.v2ecbL6Drz(/1z[5-G 4<8RTUEċ=uVId͙cpw;`jM/IR7l|pцE$(D1bG: YߓPMH2&G*BЖ'IZ85cuuYxgfc(H*)$9?mAIXx c-n #~S<$)$A0z# 9d0<@R+ jڅjqv"70z]PY E {VB *$OĩUejbю;= )Z91 <.ʪ=$R%<FiVse.ַmҺH?<4iC< 6G5)廐 B7n*MrMi,HD+vW,ysh*Y0y2CvǕS\Y:D#nXb}Ir,G=$D'!8EsH`C|4sI0C暃#+9_{ۋ:mH˲C9UbV$UƁT h'`hDSS4L[ (2_RB)׹?&PA8]´v#گmi"F\cRjHI;Jnk[pL5YLDX~@Xgp+!W! 7c\W EU*daTH; X.?etn:MRI6~q܏nok)7eUP}Y JI)J~'ϑN^$aTΌ8n^}IV ͥfc&Ŕ;rѐ6z^K>22BT,S)AA7;1֛1dK2O*65,X8!qlA${*gT: oQ^ڱ'M"JU(k>;ʫ6bƑa0nFxaېp,x7c ʁ2ҲFh[݉@>W'ɕ$ 66@5WVe(͎dCJ@c_;ߞĒfPjDTR]9\Y6c #I(z=L b#Y4[z`J_,$Q2YAmbZ6B&O)hsktYM/9 R®,ڷ|#t!aPwdy&Եx [b2$JM WmP+4{pxTLkHZWG@¼)IEG ټʹY6 H)]g3Q=wҡLdŃlqP@ۓ@H2(TmX&@f9'sZ&g~=!Ϭ4Z$ (>zR-Ic"ߒDzBI锏z5MqڴXC>\4y=qΫ2BG*=^;ڳdC H$HV$߽.uH`T8bo\{޹gY|8KC~*T)F\wǶ뽟{G6D$P3VctGƘoۊ#:ۻglG 4bVUK԰!=Wl{~;swN&GѼJ3M.7Lp2 QRw\fTXPC32HF!o6Y b=_46ZGmG:Z3J `S7NTnU# ,Hq,c 66kʚIj5<׳r(#VWƙIRbQjH o,ie"(1xV4E4r-U m\ƫ FgSK!]#8l|]{s=Yv$8ozoЄ±,(Ð O'hjqcLY'Xg,.0XҰ5y=Eeo%`+&ENQٶXv:$Fx vHCȠo~V{>BjPO)]7~^Էv>GvM,yEmۨ_]͕WYVF%vT{8gѦ̂*4XznO=\<|cq3݋ 7s@w R{GR-aRJsD-Pjxh#YURc1QCe̔(weVlf͆!ݽh>h h`i3Dws@^P#B @LX!Jn`c _08dv9y {:#ذH߽a؞MC#w%R*6h8a~ <8A7`n}~U}F۶ $ѮY}<谱9yHRfŋ{~,DgmX=h$!VI n9R3~4q+rʏG⬟N|0d¹(L<+R #btR23 ,ʣԧa僊Z+ŝ t&m87f@Y _CL C^B>#d ǒHUW3Kl}Vݼn$܏{,G䑣`6_SoƼ|10p "ZD@AWr5uh6g}.˿ >~=[ _$DnY"baAM?} B4% R9YD$%E%H,BŞt;Nǔ?,N+W{˨Y썣N,$= EzeBZ='hX@ݑ,r@XH+q!Ybsն1@ݩ_Vd0QdD&Aǖo'7*y#sza{s!ؒ+DI4y.Ƈa'71&FE-U4{zfWrK 4dBX5*&:ӾzχMcgdx̂)^@65Khqd:n O,3H.ܐϺʔn=\5Zٱc1ԂfTͅh`S,)_JqcX#agyFRLy"-$QpwԌ$7MD8CϙAEye߱Ȗ]k/HIM IkA(62"+ o,:([U+" @bG>#<.ЄU3 suRc&80E{YX^Yas-}"?=POӲF@}f$vOF YMN+ȿYR*<6< DOOYB+xe PU+vRtdPtڬw@Su1"b;SM@MJ `T)#N}j1X(.ѳD;YCJMHS@N)g$*iz v1'8òP%3+dy׏j}L(KXI#0'p1CnM顕ZT^|c(O-هBd!ZhAnG@g <6 ;Gc+wcЅEV41eԁ`W1CՈ1c2$"Am(dH}K0kː&?, V?fi#!RfuTR@{Є܋r ern>`Cݹmer U%E#ݷUk_P:yqM3@$7d[A@aިIrXY(n5۾O9XOƊ_6X;Ъ_˿zЙRYYBb@ ?Qc|qc/u_4,0Q@@8>5w hrY/$*CUtH"@]34iPw[V+͒fysblV_%v!#@fWmw[4Bsd{u+- 2FG ڧ#$PWWYX{A$/@/!א 8]呪;jOj#HҕzB)=ƳHc8Lq~ J#>\&M( ncŝ;H%Ȓ)`DlHj%Wmܴ8RȫPA\/G21_ȏ4Ms!gew,'y\+L7ߤ ܏q`Ʊ9͑#I4s{Ygw?FAgUi9aaH{{_c (m_|č>wh_#*a$-KDNlvE6K1W;>iEo@IxvK+!*e)+2ؤH#k3vY;I}?ʵ^KW=~7îk4QI 'P: PKGex!K$!.%vI?Tj9)Fɕ4 XLQ>#ƲIccC7C$SJK/G山$6j#ZgMҞNk+]ڣλ1,fd$r[cߧx922:|'$7ӤDs\#Bʡ6H<G#_=-j&n %6/`Wx~!mBg0py_VM3szaYy/S]Hq"ֱ#yӥx~Cw? =K}_`,"EZgsX6DU$s ]MGO_g]P63H #α'2Ha&5h"esٺ?TSy9R:6Aӄ$MųmA>becY"CdcJ[+^?l:l$ ^OP7WZ2J$Nhkە꼡]51W?>!]_L;U$h39G$]ZREx^ l?܎+@=?~x'%71}5WOAsY8YC2@="uQ]p K6'Fh$>J>~޼Qqfdx 1H܀&q[w:/1ou2: )1xޣunk:/ou_5eEbPUTw}ε? ٯz̥@7_餷لjz c^pM|csa4A{l +KZ>D%o1[Gs]-q]Ò2G4]W)êX~Od+FҜ$q OQҏ+?Z^ >̜xdH1#klaى[ ߐuͤiV-Ih4 [x|Q& ~q,G=mfWqG7/!\8ʇGV?AxAN;7xљo5At xc:y"q݊6~gn! VX)cםsIWgF_ qtl?H$X4VkP=cc+=(]y,ٛʵRN?E8qM֬ؖm@*u&^ՀV='\W7<ՁZ_Ŭ4x2|s<_^ۣ#AB vZ9rWЛ7 !$BW|2ߏmdwΛfhzG=%>EUxE6bEu,t^vԄm` ~ /k(އ^g=]xO}:^d4%AV$X&XV|Izo?&2<ę-\qw?GFJ#66RI!mCMcug8Nr"ef>boI'G@ky#{Њt~yOp1z͌$",zXl{XC`tn׺s##~'!? }c?CY :q2Xi,DWw&kOrzO!0 eMx=pޛ|GU2>7ƚ>C,_ՓXhZݞ,ǐ,$5jxWcb_jb4}Xt]7/cȓX1bG=@=odYO;>ګ<nu!` Y^^AEwV?"tYe(7ZZUjlymUɎ )'494-wED\]`*߹k(+%菓*ofζ ϾNʀ' VôGr[pe)CJ&K7?7,r"+z@&[&|rdd#-<ܨQ">' forces expanded position """ lay = """ {>>.main {>>|.a t=mare t=montagna t=campagna t=citta } {>.b C=mare2 Ce=montagna2 C=campagna2 Ce=citta2 } } """ l = Layout(lay,opts="V") w = l.show() sqlkit-0.9.5/demo/layout/demo.py0000755000175000017500000000053211714210425016131 0ustar sandrosandro#!/usr/bin/env python # -*- coding: utf-8 -*- -*- mode: python -*- import gtk import sys import os pdir = os.path.dirname(os.getcwd()) ppdir = os.path.dirname(pdir) sys.path.insert(0,pdir) sys.path.insert(0,ppdir) from demo_tour import Demo class LayoutDemo(Demo): example_pattern = '\d\d.*.py' d = LayoutDemo() gtk.main() sqlkit-0.9.5/demo/layout/00_date.py0000644000175000017500000000056111714210425016420 0ustar sandrosandro"""Bricks/date uses dateedit a composed custom widget. NOTE!!!! Setting of properties is only garantied after widget creation """ lay = """ d=date1 d=date2 """ import datetime l = Layout(lay) w = l.show() ### NOTE: these settings must follow l.show()!!! date = datetime.datetime(2006, 01, 31) l.prop('d=date1', 'date', date ) o = w['d=date1'] sqlkit-0.9.5/demo/layout/50_notebook_client.py0000644000175000017500000000051211714210425020662 0ustar sandrosandro"""more/Notebook client """ lay = """ {N { %generali_RIGHT indirizzo - - cod_cliente rs:.30 - citta cap:.5 pv:.2 } { %vendite settore data_prima_vendita fatturato_medio } } """ l = Layout(lay) w = l.show() sqlkit-0.9.5/demo/layout/frame_compact.py0000644000175000017500000000122511714210425020002 0ustar sandrosandro#!/usr/bin/python # -*- coding: utf-8 -*- -*- mode: python -*- import sys sys.path.append('../..') from layout import Layout simple_menu = """ {|.gen nome cognome indirizzo } {|.2gen { l=sesso r=m/sesso r=f/sesso } s=eta scuola C=classe } {|.misc altezza piedi hobby - } """ a = """ """ l = Layout(simple_menu, opts="Vs") # s for status bar l.frame('F.gen',['Generalita',], "right") l.frame('F.2gen',['Altre banalita',], "left") l.frame('F.misc',['Amenita',], "left") l.elements['C=classe'].properties['items'] = "1a\n2a\n3a" #l.xml('/tmp/sd.glade') # we can write it to a file w = l.show() sqlkit-0.9.5/demo/layout/00_translation.py0000644000175000017500000000122611714210425020040 0ustar sandrosandro"""Bricks/translation & tips You can write translations and tips in a separate file or a string, unique for the whole application. The first column sets the key, the second the label, the third the tip. If you use a key w/o a specifier (e= part), it will be applied to all widgets with that name. If both are present, the more specific apply. """ trad = """ nome Nome proprio Label doesn't show this text since it does not have sensitivity e=nome Nome Label doesn't ... l=cognome il tuo cognome Non vergognarti, scrivilo... """ layout.map_labels(buf=trad) simple_menu = """ nome cognome indirizzo - """ l = Layout(simple_menu) w = l.show() sqlkit-0.9.5/demo/layout/00_img.py0000644000175000017500000000044511714210425016260 0ustar sandrosandro"""Bricks/images A simple image. You can reset the image file locations as in: l.elements['i=img'].properties['pixbuf'] = os.path.join(os.getvwd(), 'img.jpg') """ lay = """ i=logo.jpg { i=gtk-dialog-error:6 i=gtk-dialog-question:6} """ l = Layout(lay) w = l.show() sqlkit-0.9.5/demo/layout/notebook_menu_paned.py0000644000175000017500000000374211714210425021223 0ustar sandrosandro#!/usr/bin/python # -*- coding: utf-8 -*- -*- mode: python -*- import sys sys.path.append('../..') from sqlkit.layout import Layout import gtk simple_menu = """ {B m=_File m=Produzione m=Commesse } - {N.uno {T.a cal=uno - nome cognome indirizzo - b=registra b=chiudi } TVS=due } """ l = Layout(simple_menu, opts="s") # s for status bar # tips written before show() are written in glade l.tip('b=registra', 'Questo deve essere un nome') l.elements['b=chiudi'].properties['use_stock'] = 'True' l.elements['b=chiudi'].properties['label'] = 'gtk-cancel' #l._dbg_show_objs() l.notepad('N.uno',['Prima scheda', 'Seconda scheda'], "right") # siccome nella definizione del layout il widget nome e` un combo # sappiamo che viene scomposto in una entry e una label dunque basta # passargli i singoli widget l.tip("e=nome", "Questo è un tip per la entry nome") l.tip("l=nome", "tip per la label") l.tip("cal=uno", "Questo è un tip per il calendario") l.xml('/tmp/sd.glade') # we can write it to a file w = l.show() #w['T.a'].set_row_spacing(4, 20) #sys.exit() w['sb=StBar'].push(w['sb=StBar'].get_context_id('prova'),'Prima Prova') # tips written after use gtk.Tooltip l.tip('b=chiudi', 'Chiudi tutto') def di_ciao(*args): print 'ciao' def nuovo_layout(*args): lay = """ {B m=Nobilitazione m=Subbiatura m=Commesse } {O b=tools} {h TVS=uno TXS=due} - """ print "Ora un nuovo layout" new = Layout(lay,opts='s') new.show() new.sb('Seconda Prova') l.menu('m=_File', ('_Fine' , 'activate', gtk.main_quit ), # Normal ('inizio' , 'activate', di_ciao, ), ('gtk-open' , 'activate', gtk.main_quit), # w/ stock image ('gtk-new' , 'activate', nuovo_layout), # w/ stock image ('gtk-save' , 'activate', gtk.main_quit), # w/ stock image ) l.sig( ('b=chiudi',lambda wid: gtk.main_quit(), 'clicked'), # ('l=nome',lambda wid: gtk.main_quit(), 'enter'), # enter non esiste! ) gtk.main() sqlkit-0.9.5/demo/layout/00_connect.py0000644000175000017500000000067411714210425017141 0ustar sandrosandro"""Bricks/connect""" simple_menu = """ { nome cognome indirizzo - b=registra b=chiudi } """ l = Layout(simple_menu, opts="Vs") w = l.show() def di_ciao(*args): for i, val in enumerate(args): print i, val def close_window(widget, window): print type(window) window.destroy() l.connect( ('b=chiudi', 'clicked', close_window, w['Window']), ('b=registra', 'clicked', di_ciao, 'uno', 'due'), ) sqlkit-0.9.5/demo/layout/00_toolbar.py0000644000175000017500000000106611714210425017146 0ustar sandrosandro"""Bricks/toolbar toolbar example. I had to force a width to let the icons to be visible. Otherwise just an arrow would appear. """ lay = """ {O.1 tb=gtk-save tb=gtk-delete tb=gtk-new tb=gtk-quit} {O.2 tb=gtk-save-as tb=gtk-file tb=gtk-zoom tb=gtk-zoom-fit} {O.3 tb=gtk-exit tb=gtk-no tb=gtk-ok tb=gtk-cut} """ l = Layout(lay) l.prop('Window','width_request',300) l.prop('O.1', 'toolbar_style','GTK_TOOLBAR_TEXT') l.prop('O.2', 'toolbar_style','GTK_TOOLBAR_ICONS') l.prop('O.3', 'toolbar_style','GTK_TOOLBAR_BOTH') #l.xml('/tmp/a.glade') w = l.show() sqlkit-0.9.5/demo/layout/50_eurotex.py0000644000175000017500000000106711714210425017205 0ustar sandrosandro"""more/esempio logo """ lay = """ i=logo.jpg {B m=_Richieste } {B m=_Commesse } {B m=_Articolo } """ l = Layout(lay, title='Eurotex') w = l.show() l.menu('m=_Richieste', ('Nuova richiesta offerta', 'activate', lambda e: nuova()) ) l.menu('m=_Commesse', ('Inserimento commessa', 'activate', lambda e: nuova()), ('Pianificazione commesse', 'activate', lambda e: nuova()) ) l.menu('m=_Articolo', ('Progettazione Articolo', 'activate', lambda e: nuova()), ('Disegno Modelli', 'activate', lambda e: nuova()) ) sqlkit-0.9.5/demo/layout/01_connect.py0000644000175000017500000000067411714210425017142 0ustar sandrosandro"""Bricks/connect""" simple_menu = """ { nome cognome indirizzo - b=registra b=chiudi } """ l = Layout(simple_menu, opts="Vs") w = l.show() def di_ciao(*args): for i, val in enumerate(args): print i, val def close_window(widget, window): print type(window) window.destroy() l.connect( ('b=chiudi', 'clicked', close_window, w['Window']), ('b=registra', 'clicked', di_ciao, 'uno', 'due'), ) sqlkit-0.9.5/demo/layout/00_label.py0000644000175000017500000000130211714210425016554 0ustar sandrosandro"""Bricks/label con dim Table e VBox a simple label is obtained w/ a l= flag A label of fixed width is obtained with a trailing :width """ lay = """ # comment l=label:< ae=short_lbl:2 l=label2:> e=long_lbl ae=short_lbl2:2> """ lt = Layout(lay, opts='T', title="Table") lt.prop('e=short_lbl','xalign',1) #lt.xml("/tmp/g.glade") #lt.prop('e=short_lbl2','max-length',2) w = lt.show() #print w.keys() # lv = Layout(lay, opts='V', title="VBox") # lv.prop('e=short_lbl2','max-length',2) # lv.xml("/tmp/g.glade") # w = lv.show() w['Window'].show_all() # tour.py si aspetta che ogni esempio inizializzi l, xml e ogni altra # propriet`a viene fatta guardando l... quindi l = lt #l = lv sqlkit-0.9.5/demo/layout/00_handle.py0000644000175000017500000000026611714210425016740 0ustar sandrosandro"""Bricks/handleBox """ lay = """ {H {Z { uno due tre b=gtk-ok} } {Z { abc def b=gtk-cancel} } } """ l = Layout(lay, opts='s') w = l.show() sqlkit-0.9.5/demo/layout/50_entry_menu.py0000644000175000017500000000131011714210425017666 0ustar sandrosandro"""more/Entry e menu""" simple_menu = """ { {B m=_File m=Produzione m=Commesse } {O tb=gtk-close } } { nome cognome indirizzo - { l=address e=address } b=registra b=chiudi } """ l = Layout(simple_menu, opts="Vs") l.tip('b=registra', 'Questo deve essere un nome') w = l.show() l.tip('b=chiudi', 'Chiudi tutto') def di_ciao(*args): for i, val in enumerate(args): print i, val l.menu('m=_File', ('fine' , 'activate', gtk.main_quit), ('inizio' , 'activate', di_ciao, ), ('gtk-open' , 'activate', gtk.main_quit, ) ) #print l.elements.keys l.connect( ('b=chiudi', 'clicked', gtk.main_quit), ('b=registra', 'clicked', di_ciao, 'uno', 'due'), ) sqlkit-0.9.5/demo/layout/00_spin.py0000644000175000017500000000056211714210425016455 0ustar sandrosandro"""Bricks/spinbutton """ simple_menu = """ {O ts=limit tb=gtk-refresh } s=limit2 """ def show(*args): global w w['s=limit'].update() print w['s=limit'].get_value() l = Layout(simple_menu, opts="Ts") w = l.show() adj = w['s=limit'].get_adjustment() adj.set_all(23,10,100,10,10,0) l.connect( ('tb=gtk-refresh', 'clicked', show) ) sqlkit-0.9.5/demo/layout/00_event.py0000644000175000017500000000047711714210425016632 0ustar sandrosandro"""Bricks/event-label+entry""" simple_menu = """ nome """ l = Layout(simple_menu, opts="Ts-") l.prop('Window', 'visible', True) w = l.show() def pressed(wdg, event): print "wdg: %s, ev: %s" % (wdg, event) w['e=nome'].connect('key_press_event', pressed) w['E=nome'].connect('button-press-event', pressed) sqlkit-0.9.5/demo/layout/frame_long.py0000644000175000017500000000100711714210425017311 0ustar sandrosandro#!/usr/bin/python # -*- coding: utf-8 -*- -*- mode: python -*- import sys sys.path.append('../..') from layout import Layout import gtk simple_menu = """ {F.gen { nome cognome indirizzo } } {F.2gen { sesso eta scuola classe } } {F.misc { altezza piedi hobby - } } """ l = Layout(simple_menu, opts="Vs") # s for status bar l.frame('F.gen',['Generalita',], "left") l.frame('F.2gen',['Altre banalita',], "left") l.frame('F.misc',['Amenita',], "left") w = l.show() sqlkit-0.9.5/demo/layout/00_layout.py0000644000175000017500000000101711714210425017015 0ustar sandrosandro"""Bricks/gtk.Layout """ import gtk lay = """ LS=a """ l = Layout(lay, opts="T") w = l.show() e = gtk.Entry() def drag_begin_cb(*args): print 'BEGIN' def drag_end_cb(*args): dbg.write('END') def drag_motion_cb(*args): dbg.write('MOTION') lay = w['Lay=a'] lay.connect("drag_begin", drag_begin_cb) lay.connect("drag_end", drag_end_cb) lay.connect("drag_motion",drag_motion_cb) w['Lay=a'].put(e, 50,50) l.prop('Lay=a', 'width-request', 300) l.prop('Lay=a', 'height-request', 300) e.show() sqlkit-0.9.5/demo/layout/50_scrolled.py0000644000175000017500000000044211714210425017315 0ustar sandrosandro"""more/scrolled Simple example of a scrolled window. We use a 'cluster' of label/entry in a table, in a view-port, in a scrolled Window """ lay = "" for i in range(40): lay += " l=label%s e=label%s:10\n" % (i, i) l = Layout("{S {p { %s}}}" % lay) l.xml('/tmp/x.glade') w = l.show() sqlkit-0.9.5/demo/layout/00_button.py0000644000175000017500000000036411714210425017017 0ustar sandrosandro"""Bricks/button xxx Just a button """ lay = """ l=stinga_molto_piu_lunga_delle_altre # b=pigia1:20 b=pigia:10 """ #l = Layout(lay, opts="T") l = Layout(lay, opts="V") #l.xml('/tmp/b.xml') #w = l.show() w = l.show() sqlkit-0.9.5/demo/layout/00_tool_calendar.py0000644000175000017500000000067411714210425020316 0ustar sandrosandro"""Bricks/toolbutt calendar icons can be added directly to the IconFactory and called as a normal stock image as in tb=cal I Have not understood how to use themes, thought """ import os from sqlkit import layout from sqlkit.layout import misc icon_path = os.path.join( os.path.dirname(layout.__file__), 'cal15.png') misc.add_stock_icons() lay = """ {O tb=sk-calendar tb=gtk-new } l=esempio_calendario """ l=Layout(lay) w = l.show() sqlkit-0.9.5/demo/layout/00_check.py0000644000175000017500000000045411714210425016561 0ustar sandrosandro"""Bricks/check-radio radiobuttons are grouped by the group parameter. A radiobutton whose name has a /group1 part is aggregated to group1. only one radiobutton at a time can be switched on. """ lay = """ c=check r=radio1 r=radio2/radio1 """ l = Layout(lay) w = l.show() sqlkit-0.9.5/demo/layout/00_combobox.py0000644000175000017500000000031411714210425017307 0ustar sandrosandro"""Bricks/ComboBox """ lay = """ C=mare2 C=mare3 Ce=montagna2 """ l = Layout(lay,opts="V") w = l.show() # for item in ('uno', 'due', 'tre'): # l.widgets['C=mare2'].append_text(item) sqlkit-0.9.5/demo/layout/00_alignment.py0000644000175000017500000000030311714210425017453 0ustar sandrosandro"""Bricks/alignement """ lay = """ {A { e=uno e=due e=tre } } """ l = Layout(lay, opts="T") w = l.show() #dbg.gtk_dbg.show_widgets(w['Window']) sqlkit-0.9.5/demo/layout/01_toolbar.py0000644000175000017500000000045711714210425017152 0ustar sandrosandro"""Bricks/toolbar singolo toolbar example. I had to force a width to let the icons to be visible. Otherwise just an arrow would appear. """ lay = """ {O tb=gtk-save tb=gtk-delete tb=gtk-new tb=gtk-quit} """ l = Layout(lay) l.prop('Window','width_request',300) #l.xml('/tmp/t.glade') w = l.show() sqlkit-0.9.5/demo/sql/0000755000175000017500000000000011714210425014112 5ustar sandrosandrosqlkit-0.9.5/demo/sql/ex_08_table_noup.py0000644000175000017500000000046111714210425017620 0ustar sandrosandro"""base/options: noup year and description will not be editable (but they can be changed programmatically) """ t = SqlTable('movie', dbproxy=db, order_by='director_id', noup='year, description', ) t.noup = '+director_id' # alternative way t.reload() sqlkit-0.9.5/demo/sql/ex_20_relations_movie.py0000644000175000017500000000064711714210425020667 0ustar sandrosandro"""relation/o2m Adding a relation is as easy as adding the relation name to the layout. Note that in this case the table name is not sufficient, we need to know the mapping so we pass a class_ attribute that is really a mapped class (in this example built with declarative layer) """ lay = """ first_name last_name nation m2m=movies - - - """ t = SqlMask(model.Director, dbproxy=db, layout=lay) t.reload() sqlkit-0.9.5/demo/sql/ex_25_relations_m2m.py0000644000175000017500000000121711714210425020242 0ustar sandrosandro"""relation/m2m To add a relation is as easy as adding the relation name to the layout note that in this case the table name is not sufficient, we need to know the mapping so we pass a class_ attribute that is really a class build with declarative layer In this case you cannot add actors from the interface, but you can select them and add them to the cast of the film (as well as genres). You can set actors'table editable if you like. Have a look at one of the next examples. """ lay = """ title year director_id - - - m2m=genres - m2m=actors - """ t = SqlMask(model.Movie, dbproxy=db , layout=lay) sqlkit-0.9.5/demo/sql/ex_02a_table_movie.py0000644000175000017500000000103611714210425020110 0ustar sandrosandro"""base/tree table Tree Table ---------- In this example a table is displayed with gerarchical grouping """ from sqlkit.widgets.table.modelproxy import Header, ModelProxy class MyModel(ModelProxy): def make_header_obj(self, field_value): return Header(self.master, 'title', self.master.gui_fields.director_id.get_human_value(field_value)) t = SqlTable('movie', dbproxy=db, order_by='title', ) t.modelproxy = t.modelproxy.copy(MyModel) t.modelproxy.tree_field_name = 'director_id' t.hide_fields('director_id') t.reload() sqlkit-0.9.5/demo/sql/ex_15_base_all_types.py0000644000175000017500000000127211714210425020455 0ustar sandrosandro"""base/all types simple mask showing all supported types. Some are clearly poorely rendered. """ lay = """ {|.text varchar10 varchar200 text - - uni uni_text - - } {|.date date time # datetime datetime_tz datetime time_tz interval } {|.numbers integer float numeric } {|.boolean bool bool_null } """ t = SqlMask(model.AllTypes, layout=lay, dbproxy=db ) for name in ('text','date', 'numbers', 'boolean'): t.set_frame_label(name, name) t.reload() t1 = SqlTable(model.AllTypes, dbproxy=db ) t1.reload() sqlkit-0.9.5/demo/sql/ex_13c_opts_order_by.py0000644000175000017500000000045311714210425020502 0ustar sandrosandro"""base/opts: order_by - foreign_key Order by can use django_syntax too """ t = SqlTable('movie', dbproxy=db, order_by='director_id__last_name', geom=(800,300), ) # the following is ok too: # t.order_by = 'director_id__last_name' t.reload() sqlkit-0.9.5/demo/sql/ex_63_hooks_field_validation.py0000644000175000017500000000134711714210425022175 0ustar sandrosandro"""hooks/on_field_validation on_save_as you can set hooks on single field validation. It will be called from within the field. The hook 'on_save_as' is called when a record is duplicated. """ from sqlkit.exc import ValidationError class Hooks(object): def on_field_validation__year(self, mask, field_name, field_value, field): if field_value > 2020: raise ValidationError("Hei: how can you know the future!") def on_save_as(self, mask, old, new): print "Old title: %s, \nnew title: %s" % (old.title, new.title) lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) t.reload() sqlkit-0.9.5/demo/sql/ex_04_table_filter.py0000644000175000017500000000033611714210425020121 0ustar sandrosandro"""base/filters This in one of the key oint of sqlkit. A filter can be added with django_like syntax """ t = SqlTable('director', dbproxy=db, order_by='last_name', ) t.add_filter(last_name__icontains='a') t.reload() sqlkit-0.9.5/demo/sql/demo.py0000755000175000017500000001161511714210425015417 0ustar sandrosandro#!/usr/bin/env python # -*- coding: utf-8 -*- -*- mode: python -*- """ opens the demo console or directly an example defined by its number: ./demo.py 25 ./demo.py -N 40b usage: %prog [opts] ex_number -N, --no-gtk-debug: gtk debug -g, --gtk: gtk debug in window -i, --interactive: set interactive so that ipython can interact -t, --test: run all snippets and run t.reload() for each of them -o, --offset=n: skip the first 'n' snippets -n, --number=l: stop after 'n' snippets -r, --reload: reload all snippets (some will stop for confirmation) -e, --exit: exit on exception -x, --existent-db: don't delete the demo db if it exists """ from __future__ import with_statement import sys import os import sqlite3 import weakref from contextlib import closing pdir = os.path.dirname(os.getcwd()) ppdir = os.path.dirname(pdir) sys.path.insert(0,pdir) sys.path.insert(0,ppdir) import sqlkit from sqlkit.misc import optionparse opts, args = optionparse.parse(__doc__) import gtk if opts.gtk: from sqlkit import debug as dbg dbg.debug(True) dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|get)_value' + \ '|lookup_value|is_fkey' dbg.trace_function(exclude=but) from demo_tour import Demo from sqlkit.widgets import SqlMask, SqlTable from sqlkit.db import proxy, defaults, utils import model class MissingExampleError(Exception): pass def init_db(): with closing(sqlite3.connect(model.DB_FILE)) as db: with open('model/schema.sql', 'r') as schema: db.cursor().executescript(schema.read()) db.commit() class DemoSql(Demo): def load_module(self, demo): ### fill text: src, notes, and xml self.insert_source(demo.body, self.src) self.note.set_text(demo.doc) ### exec the example GLOB = { 'sqlkit': sqlkit, 'SqlMask': SqlMask, 'SqlTable': SqlTable, 'gtk': gtk, 'db': model.db, 'model': model, } execfile(demo.filename, GLOB) self.last_lo = GLOB['t'].lay_obj self.last_w = GLOB['t'].widgets self._t = weakref.ref(GLOB['t']) try: self.t1 = GLOB['t1'] except: pass #self.xml.set_text(GLOB['l'].xml()) self.create_widget_tree(toplevel=GLOB['t'].widgets['Window']) self.prepare_treestore_for_elements(GLOB['t'].lay_obj.elements) GLOB['t'].widgets['Window'].set_title(demo.filename) self.w['Window'].set_title("Example: %s" % (demo.filename)) while gtk.events_pending(): gtk.main_iteration() return GLOB['t'] @property def t(self): try: return self._t() except AttributeError: raise MissingExampleError("The example was not found") if not opts.existent_db and os.path.exists(model.DB_FILE): try: os.remove(model.DB_FILE) except OSError: print "No way to remove file", model.DB_FILE # No write permission - no problem, a temporary db will be created pass if not os.path.exists(model.DB_FILE): init_db() d = DemoSql(xml=False, debug=True) d.tv.collapse_all() if not os.access('images', os.W_OK): print "WARNING: \n Missing write permission on 'images' directory" print " Image upload is not possible\n" if args: d.iconify() d.load_module_by_idx(args[0]) def quit(widget): gtk.main_quit() try: hid = d.t.connect('delete-event', quit) d.t._ids['delete_event'] = (d.t, hid) except MissingExampleError: print "No such example ", args[0] sys.exit(1) except AttributeError, e: pass if opts.test: start = opts.offset and int(opts.offset) or 0 stop = opts.number and start + int(opts.number) or None print start, ':', stop for demo in d.demos[start:stop]: print "------------------ %s %s --------------" % (d.demos.index(demo), demo) try: t = d.load_module(demo) if opts.reload: t.reload() except Exception, e: print e if opts.exit: sys.exit(1) if opts.gtk: d.execute_clicked_cb if __name__ == '__main__': if opts.interactive: try: gtk.set_interactive(True) except AttributeError: print "This version of gtk doesn't have 'set_interactive', sorry." sys.exit(1) try: import IPython from IPython.Shell import IPShellEmbed ipshell = IPShellEmbed([]) print "The last opened table is hold in variable 'd.t' (i.e.: demo.table)" ipshell() except ImportError, e: print "Interactive demo needs ipython. Quitting." else: print "Try option -i to try sqlkit interactively" gtk.main() sqlkit-0.9.5/demo/sql/ex_56_signals-after-flush.py0000644000175000017500000000154511714210425021355 0ustar sandrosandro"""signals/after-flush after-flush is triggered from withing SessionExtension as after_flush method. It's emitted when flushing has occurred but state of the session still retains all info on objects: session.dirty, session.new, session.deleted are available and each object has info on it's history Try changing some values and read the output """ t = SqlTable(model.Director, dbproxy=db, ) def after_flush_cb(sqlwidget, obj, session): from sqlkit.db.utils import get_differences for o in session.new: print "New object: %s" % o for o in session.dirty: print "Updated objects: %s" % o for field_name, old, new in get_differences(o): print "%s: %s ==> %s" % (field_name, old, new) for o in session.deleted: print "Deleted objects: %s" % o t.connect('after-flush', after_flush_cb) t.reload() sqlkit-0.9.5/demo/sql/ex_13_opts_rows.py0000644000175000017500000000036711714210425017530 0ustar sandrosandro"""base/options: rows rows sets the number of rows it should display. Almost working... Depending on style. """ t = SqlTable('movie', dbproxy=db, order_by='director_id', rows = 15, ) t.reload() sqlkit-0.9.5/demo/sql/ex_30_mapper_count.py0000644000175000017500000000137211714210425020161 0ustar sandrosandro"""mapper & fields/nested select this is an example of a mapper with a nested select. You can still use filters and constraints """ from sqlalchemy.orm import mapper, column_property from sqlalchemy.sql import select, func class Director2(object): pass m = mapper(Director2, model.Director.__table__, properties={ 'film_number': column_property( select( [func.count(model.Movie.__table__.c.id)], model.Director.__table__.c.id == model.Movie.__table__.c.director_id ).label('film_number') ) } ) field_list = "last_name, first_name, nation, film_number" t = SqlTable(m, field_list=field_list, dbproxy=db) t.add_filter(film_number=3) t.add_constraint(film_number__lt = 5) t.reload() sqlkit-0.9.5/demo/sql/ex_63b_hooks_field_warning.py0000644000175000017500000000151511714210425021647 0ustar sandrosandro"""hooks/warning - on_field_validation A warning will not make validation to abort, but will just warn the user and give an opportunity to proceed or go back editing. In this case you may want to implement firther actions in after_flush """ from sqlkit.exc import ValidationWarning class Hooks(object): def on_field_validation__year(self, mask, field_name, field_value, field): if field_value > 2020: raise ValidationWarning("You can go on, but it's strange!...") def on_after_flush(self, mask, obj, session): if mask.current.year > 2020: print "Funny guy who know the future (%s)!" % mask.current.year lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) t.reload() sqlkit-0.9.5/demo/sql/ex_64_hooks_validate.py0000644000175000017500000000221411714210425020464 0ustar sandrosandro"""hooks/on_validation - related you can also change a related validation be sure you understand when you have to check a sqlkit or a related one. If you want to prevent a change in a related table where you cannot edit (as is the genres table), you must set the check on the validation of attribute 'genres' of the main Mask. On the contrary the completion on genres takes place in the related table, so that you need to add __genres__ to the name of the method """ from sqlkit.exc import ValidationError from sqlkit.db.utils import get_differences class Hooks(object): def on_field_validation__genres(self, mask, field_name, field_value, field): for field_name, old, new in get_differences(mask.current): if field_name == 'genres': raise ValidationError("I don't want you to change the genre!!!") def on_completion__genres__name(self, mask, field_name, obj): print "No, please, don't change the genres..." lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) t.reload() sqlkit-0.9.5/demo/sql/ex_63c_change_value.py0000644000175000017500000000254111714210425020256 0ustar sandrosandro"""hooks/on_change_value On change value works differently according to the type of the widget. varchar/integer field will invoke it for each typed char enum/foreign key fields invoke it when the item is choosen table's widget invoke it when editing is terminated """ lay = """ {|.text varchar10 varchar200 text - - uni uni_text - - } {|.date date time # datetime datetime_tz datetime time_tz interval } {|.numbers integer float numeric } {|.boolean bool bool_null } """ class Hooks(object): def on_change_value__text(self, sqlwidget, field_name, value, fkvalue, field): print "Changed: ", sqlwidget, field_name, value, fkvalue on_change_value__varchar10 = \ on_change_value__date = \ on_change_value__time = \ on_change_value__datetime = \ on_change_value__integer = \ on_change_value__float = \ on_change_value__numeric = \ on_change_value__bool = \ on_change_value__bool_null = on_change_value__text t = SqlMask(model.AllTypes, layout=lay, dbproxy=db, hooks=Hooks() ) for name in ('text','date', 'numbers', 'boolean'): t.set_frame_label(name, name) t.reload() t1 = SqlTable(model.AllTypes, dbproxy=db, hooks=Hooks() ) t1.reload() sqlkit-0.9.5/demo/sql/ex_07_date_relative.py0000644000175000017500000000116711714210425020303 0ustar sandrosandro"""base/filters: relative dates A filter may be expressed as a relative interval from now. read complete documentation on here: http://docs.argolinux.org/sqlkit/sqlkit/filters.html But for now you can play with symbolic math: y for year, m for month, d for day, w for week. the '>' sign sets a period. This allows to express dates in a very natural way. particularly when you need to save queries """ t = SqlTable('movie', dbproxy=db, geom=(600, 400)) t.add_filter(date_release__gte='y-1 +2m') t.reload() t2 = SqlTable('movie', dbproxy=db, geom=(600,400)) t2.add_filter(date_release__gte='y-5 > y-2') t2.reload() t = t2 sqlkit-0.9.5/demo/sql/ex_21_relations_select_fields.py0000644000175000017500000000070211714210425022346 0ustar sandrosandro"""relation/o2m: selecting fields Adding a relation is as easy as adding the relation name to the layout. Note that in this case the table name is not sufficient, we need to know the mapping so we pass a class_ attribute that is really a mapped class (in this example built with declarative layer) """ lay = """ first_name last_name nation m2m=movies::title,year - """ t = SqlMask(model.Director, dbproxy=db, layout=lay) t.reload() sqlkit-0.9.5/demo/sql/ex_55_signals.py0000644000175000017500000000052611714210425017134 0ustar sandrosandro"""signals/record-selected 'record-selected' is define both fr SqlTable and SqlMask """ t = SqlTable(model.Director, dbproxy=db, ) t2 = SqlTable(model.Movie, dbproxy=db, ) def show_movies(widget): if widget.current: t2.set_records(widget.current.movies) t.connect('record-selected', show_movies) t.reload() sqlkit-0.9.5/demo/sql/ex_90_genre.py0000644000175000017500000000033011714210425016564 0ustar sandrosandro"""base/base genre """ from sqlkit.widgets import SqlTable from sqlkit.db import proxy import model db = proxy.DbProxy(bind="sqlite:///%s" % model.DB_FILE) t = SqlTable(model.Genre, dbproxy=db ) t.reload() sqlkit-0.9.5/demo/sql/ex_23_relations_filter_join.py0000644000175000017500000000064511714210425022055 0ustar sandrosandro"""relation/o2m: reload filter panel Reloading from the filter panel is slightly different: it shows the result in a page of the filter panel. str(obj) is used to represent the records. """ lay = """ first_name last_name nation m2m=movies::title,year - """ t = SqlMask(model.Director, dbproxy=db, layout=lay) t.add_filter(movies__title__icontains='il') t.add_filter(first_name__icontains='a') t.reload() sqlkit-0.9.5/demo/sql/ex_78_printing-fk.py0000644000175000017500000000113711714210425017730 0ustar sandrosandro"""printing/foreign key How to cope with foreign keys. The template has '$director_id', that will be rendered as the last_name of the Director instead of the id that is the value of the foreign key attribute. This happens becouse the main object and the objects in 'Table1' are wrapped into ObjProxy, a proxy that follows the foreign key and retrieves the "correct" (well, best match) value. """ lay = """ title year date_release director_id """ t = SqlMask(model.Movie, layout=lay, dbproxy=db) t.printing.add_menu_entry('Print to pdf', 'movie-fk.odt', mode='pdf', accel='p') t.reload() sqlkit-0.9.5/demo/sql/ex_64b_hooks_warning.py0000644000175000017500000000132011714210425020477 0ustar sandrosandro"""hooks/warning - related Very similar to the precedent but with warning on a related table """ from sqlkit.exc import ValidationError, ValidationWarning from sqlkit.db.utils import get_differences class Hooks(object): def on_field_validation__genres(self, mask, field_name, field_value, field): for field_name, old, new in get_differences(mask.current): if field_name == 'genres': msg = "I'd really prefer you don't change genre!!!" raise ValidationWarning(msg) lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) t.reload() sqlkit-0.9.5/demo/sql/ex_51_related_layout.py0000644000175000017500000000065311714210425020506 0ustar sandrosandro"""layout/layout of a related treeview Layout can be also set for related table Try right-clicking on a film and open the movie with layout set by layout_movie """ lay_director = """ last_name m2o=movies - """ lay_movie = """ title year m2m=actors - """ t = SqlMask(model.Director, dbproxy=db, layout=lay_director, order_by='last_name') t.related.movies.layout = lay_movie t.reload() sqlkit-0.9.5/demo/sql/ex_33_count_movies_in_filter_output.py0000644000175000017500000000142211714210425023651 0ustar sandrosandro"""mapper & fields/filter: adding column The output is represented ina tab of the Filter Panel and can be customized """ from sqlkit import fields from sqlkit.widgets.table import columns from sqlkit.db.utils import DictLike class CountMovies(fields.IntegerField): """ A field that counts the movies """ @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the objct itselt is passed return len(value.movies) count_movies = CountMovies('n_movies', {'length' : 4}) t = SqlMask(model.Director, dbproxy=db) col = columns.VarcharColumn(t, 'n_movies', 'Movie Count', field=count_movies) t.gui_fields['n_movies'] = count_movies t.filter_panel.view.add_column(col) t.filter_panel.show() t.filter_panel.reload() sqlkit-0.9.5/demo/sql/ex_32_count_movies.py0000644000175000017500000000165311714210425020203 0ustar sandrosandro"""mapper & fields/adding count This is an alternative way to add a movie count field that adds a column When going this way you cannot filter on the "Movie Count" column """ from sqlkit import fields from sqlkit.widgets.table import columns from sqlkit.db.utils import DictLike class CountMovies(fields.IntegerField): """ A field that counts the movies """ @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the objct itselt is passed return len(value.movies) count_movies = CountMovies('n_movies') t = SqlTable(model.Director, dbproxy=db) ## add the fields to gui_fields t.gui_fields['n_movies'] = count_movies ## create a column col = columns.VarcharColumn(t, 'n_movies', 'Movie Count', field=count_movies) ## add it to the view t.views['main'].add_column(col, position=2) #t.field_list += ['n_movies'] t.totals.add_total('n_movies') t.reload() sqlkit-0.9.5/demo/sql/ex_12_opts_geom.py0000644000175000017500000000032511714210425017456 0ustar sandrosandro"""base/options: geom read-only set all columns as not updateable """ t = SqlMask('movie', dbproxy=db, order_by='director_id', geom = (1000, 500), ) t.reload() sqlkit-0.9.5/demo/sql/model/0000755000175000017500000000000011714210425015212 5ustar sandrosandrosqlkit-0.9.5/demo/sql/model/__init__.py0000644000175000017500000000006411714210425017323 0ustar sandrosandro#from movies_autoload import * from movies import * sqlkit-0.9.5/demo/sql/model/test.py0000755000175000017500000000132311714210425016545 0ustar sandrosandro#!/usr/bin/python from sqlkit import debug as dbg dbg.debug(True) dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|get)_value|[el]ne_cb|match_func' + \ '|cell_.*_cb|lookup_value|is_fkey|instance2|markup_clean|get_iter' dbg.trace_function(exclude=but) from sqlkit.widgets import * import gtk import movies field_list="integer, float numeric" field_list=None table = movies.AllTypes.__table__ table = movies.Movie.__table__ # t = SqlMask(table=table, dbproxy=movies.db, # field_list=field_list, single=True) t = SqlTable(table=table, dbproxy=movies.db, field_list=field_list, single=True) #t.reload() gtk.main() sqlkit-0.9.5/demo/sql/model/movies_autoload.py0000644000175000017500000000332411714210425020760 0ustar sandrosandro## DISCLAIMER: this file is not normally maintained import os from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Table, Column, String, Date, Integer, Float, \ Boolean, Numeric, ForeignKey, Unicode from sqlalchemy.orm import relation from sqlkit.db.utils import Descr from sqlkit.db import proxy from model import engine Base = declarative_base() db = proxy.DbProxy(engine=engine, metadata=Base.metadata) movie_casting = Table('movie_casting' , Base.metadata, autoload=True) movie_genre = Table('movie_genre' , Base.metadata, autoload=True ) class Director(Base): __table__ = Table('director', Base.metadata, autoload=True) def __repr__(self): return u'' % (self.last_name, self.nation) class Movie(Base): __table__ = Table('movie', Base.metadata, autoload=True) director = relation('Director', backref='movies') genres = relation('Genre', backref='movies', secondary=movie_genre) actors = relation('Actor', backref='movies', secondary=movie_casting) def __repr__(self): return u'' % self.title class Genre(Base): __table__ = Table('genre', Base.metadata, autoload=True) # name = Column(Unicode(15), primary_key=True) def __repr__(self): return u'' % self.name class Actor(Base): __table__ = Table('actor', Base.metadata, autoload=True) def __repr__(self): return u'' % self.last_name class Nation(Base): __table__ = Table('nation', Base.metadata, autoload=True) # cod = Column(String(4), primary_key=True) # nation = Column(String(20)) class AllTypes(Base): __table = Table('all_types', Base.metadata, autoload=True) sqlkit-0.9.5/demo/sql/model/movies_elixir.py0000644000175000017500000000321611714210425020444 0ustar sandrosandro# DISCLAIMER: this file is not mantained. I don't use elixir import elixir from sqlkit.db import proxy from model import engine db = proxy.DbProxy(engine=engine) __metadata__ = db.metadata #__session__ = db.get_session() class Director(elixir.Entity): last_name = elixir.Field(elixir.String(60)) first_name = elixir.Field(elixir.String(60)) nation = elixir.Field(elixir.String(6)) movies = elixir.OneToMany('Movie', inverse='director') elixir.using_options(tablename='director') elixir.using_options(metadata=__metadata__) def __repr__(self): return u'' % (self.last_name, self.nation) class Movie(elixir.Entity): title = elixir.Field(elixir.String(60)) description = elixir.Field(elixir.String(512)) year = elixir.Field(elixir.Integer()) date_release = elixir.Field(elixir.Date()) director = elixir.ManyToOne('Director', inverse='movies') actors = elixir.ManyToMany('Actor', inverse='movies', tablename='movie_casting') genres = elixir.ManyToMany('Genre', inverse='movies', tablename='movie_genre') elixir.using_options(tablename='movie') def __repr__(self): return u'' % self.title class Genre(elixir.Entity): name = elixir.Field(elixir.Unicode(15), primary_key=True) movies = elixir.ManyToMany('Movie') elixir.using_options(tablename='genre') def __repr__(self): return u'' % self.name class Actor(elixir.Entity): name = elixir.Field(elixir.String(60)) movies = elixir.ManyToMany('Movie', inverse='actors', tablename='movie_casting') elixir.using_options(tablename='actor') elixir.setup_all() sqlkit-0.9.5/demo/sql/model/film.py0000755000175000017500000000043611714210425016521 0ustar sandrosandro#!/usr/bin/python from sqlkit.widgets import * import gtk import movies lay = """ { first_name last_name} o2m=movies:title,date_release,description,year """ t = SqlMask(Class=movies.Director, dbproxy=movies.db, single=True, layout=lay) t.reload() gtk.main() sqlkit-0.9.5/demo/sql/model/movies.py0000644000175000017500000001026111714210425017066 0ustar sandrosandroimport os import tempfile from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Table, Column, ForeignKey from sqlalchemy.types import * from sqlalchemy.orm import relation, backref from sqlkit.db.utils import Descr from sqlkit.db import proxy Base = declarative_base() DB_FILE = os.path.join(tempfile.gettempdir(), 'db-%s.sqlite' % os.environ.get('USERNAME')) engine = "sqlite:///%s" % DB_FILE db = proxy.DbProxy(engine=engine, metadata=Base.metadata) movie_casting = Table( 'movie_casting' , Base.metadata, Column('movie_id', Integer, ForeignKey('movie.id'), primary_key=True), Column('actor_id', Integer, ForeignKey('actor.id'), primary_key=True), ) movie_genre = Table( 'movie_genre' , Base.metadata, Column('movie_id', Integer, ForeignKey('movie.id'), primary_key=True), Column('genre_name', Integer, ForeignKey('genre.name'), primary_key=True), ) SCORE_VALUES = ( (1, 'Nice'), (2, 'Beautifull'), (3, 'Great'), ) class Director(Base): __tablename__ = 'director' id = Column(Integer, primary_key=True) last_name = Column(String(60), nullable=False) first_name = Column(String(60), nullable=False) nation = Column(String(6)) movies = relation('Movie', backref='director', cascade='all, delete-orphan',) def __str__(self): return u'%s (%s)' % (self.last_name, self.nation) def __repr__(self): return u'' % (self.last_name, self.nation) class Movie(Base): __tablename__ = 'movie' id = Column(Integer, primary_key=True) title = Column(String(60), nullable=False) image = Column(String(250), info={'render' : 'image', 'base_dir' : './images', 'thumbnail_size' : (30,30)}) description = Column(String(512)) year = Column(Integer()) date_release = Column(Date()) director_id = Column(Integer, ForeignKey('director.id'), nullable=False, info={'attach_instance': 'director'}) score = Column(Integer, info={'render' :'enum', 'values' : SCORE_VALUES}) actors = relation('Actor', backref='movies', secondary=movie_casting) genres = relation('Genre', backref='movies', secondary=movie_genre) def __str__(self): return u'%s' % self.title def __repr__(self): return u'' % self.title class Genre(Base): __tablename__ = 'genre' name = Column(Unicode(55, assert_unicode=False), primary_key=True) def __repr__(self): return u'' % self.name class Actor(Base): __tablename__ = 'actor' id = Column(Integer, primary_key=True) first_name = Column(String(60), nullable=False) last_name = Column(String(60)) nation_cod = Column(String(4), ForeignKey('nation.cod')) nation = relation('Nation', backref='actors') def __repr__(self): return u'' % (self.first_name, self.last_name) class Nation(Base): __tablename__ = 'nation' cod = Column(String(4), primary_key=True) nation = Column(String(20)) class AllTypes(Base): __tablename__ = 'all_types' id = Column(Integer(), primary_key=True) varchar10 = Column(String(10), nullable=False) varchar200 = Column(String(200)) text = Column(Text()) uni = Column(Unicode(10, assert_unicode=False)) uni_text = Column(UnicodeText(assert_unicode=False), nullable=False) date = Column(Date()) datetime = Column(DateTime(timezone=False)) datetime_tz = Column(DateTime(timezone=True)) interval = Column(Interval()) time = Column(Time(timezone=False)) time_tz = Column(Time(timezone=True)) integer = Column(Integer()) float = Column(Float()) numeric = Column(Numeric(8,2)) bool = Column(Boolean, nullable=False) bool_null = Column(Boolean, nullable=True) # pickle = Column(PickleType()) sqlkit-0.9.5/demo/sql/model/schema.sql0000644000175000017500000001673511714210425017207 0ustar sandrosandro-- PRAGMA foreign_keys=OFF; BEGIN TRANSACTION; CREATE TABLE director ( id INTEGER NOT NULL, last_name VARCHAR(60) NOT NULL, first_name VARCHAR(60) NOT NULL, nation varchar(6), PRIMARY KEY (id) ); INSERT INTO "director" VALUES(2,'Ki Duck','Kim','KR'); INSERT INTO "director" VALUES(3,'Von Triars','Lars','DK'); INSERT INTO "director" VALUES(6,'Faenza','Roberto','IT'); INSERT INTO "director" VALUES(7,'Leconte','Patrice','FR'); INSERT INTO "director" VALUES(8,'Donnersmak','Florian','DE'); INSERT INTO "director" VALUES(11,'Kraus','Cris','DE'); INSERT INTO "director" VALUES(12,'Truffaut','Françoise','FR'); INSERT INTO "director" VALUES(13,'Olmi','Ermanno','IT'); INSERT INTO "director" VALUES(14,'Fellini','Federico','IT'); CREATE TABLE genre ( name VARCHAR(15) NOT NULL, PRIMARY KEY (name) ); INSERT INTO "genre" VALUES('drammatico'); INSERT INTO "genre" VALUES('storico'); INSERT INTO "genre" VALUES('fiabesco'); INSERT INTO "genre" VALUES('b&w'); CREATE TABLE nation ( cod VARCHAR(4) NOT NULL, nation VARCHAR(20), PRIMARY KEY (cod) ); INSERT INTO "nation" VALUES('IT','Italy'); INSERT INTO "nation" VALUES('FR','France'); INSERT INTO "nation" VALUES('DE','Germany'); INSERT INTO "nation" VALUES('DK','Denmark'); INSERT INTO "nation" VALUES('US','USA'); INSERT INTO "nation" VALUES('MX','Mexico'); INSERT INTO "nation" VALUES('KR','Korea'); CREATE TABLE actor ( id INTEGER NOT NULL, first_name VARCHAR(30) NOT NULL, last_name VARCHAR(30), nation_cod VARCHAR(4) REFERENCES "nation" ("cod"), PRIMARY KEY (id) ); INSERT INTO "actor" VALUES(1,'Marcello','Mastroianni','IT'); INSERT INTO "actor" VALUES(2,'Daniel','Autoil','FR'); INSERT INTO "actor" VALUES(3,'Vanessa','Paraise','FR'); INSERT INTO "actor" VALUES(4,'Giulietta','Masina','IT'); INSERT INTO "actor" VALUES(5,'Anthony','Quinn','US'); INSERT INTO "actor" VALUES(6,'Ulrich','Mühe','DE'); INSERT INTO "actor" VALUES(7,'Bud','Spencer','IT'); CREATE TABLE movie ( id INTEGER NOT NULL, title VARCHAR(60) NOT NULL, description VARCHAR(512), year INTEGER, date_release DATE, director_id INTEGER NOT NULL, image VARCHAR(255), score INTEGER, PRIMARY KEY (id), CONSTRAINT movie_director_id_fk FOREIGN KEY(director_id) REFERENCES director (id) ); INSERT INTO "movie" VALUES(3,'Sostiene pereira','Great Mastroianni!',1995,NULL,6,'sostiene-pereira.jpg',3); INSERT INTO "movie" VALUES(5,'La ragazza sul ponte.','Don''t miss this film',1999,'2008-04-07',7,'la-ragazza-sul-ponte.jpg',2); INSERT INTO "movie" VALUES(7,'Le vite degli altri','Probably the best film this year',2006,'2007-04-06',8,'le-vite-degli-altri.jpg',3); INSERT INTO "movie" VALUES(8,'4 minuti',NULL,2006,'2007-05-04',11,NULL,1); INSERT INTO "movie" VALUES(9,'Cantando dietro il paraventi','Really charming atmosphere...',2002,'2003-10-24',13,'cantando.jpeg',2); INSERT INTO "movie" VALUES(13,'Jim e Jules',NULL,1963,NULL,12,NULL,1); INSERT INTO "movie" VALUES(21,'100 chiodi',NULL,2007,'2007-03-30',13,NULL,NULL); INSERT INTO "movie" VALUES(22,'Dogville',NULL,NULL,'2003-11-07',3,NULL,NULL); INSERT INTO "movie" VALUES(23,'Soffio',NULL,2007,NULL,2,NULL,NULL); INSERT INTO "movie" VALUES(25,'Time',NULL,2006,NULL,2,NULL,NULL); INSERT INTO "movie" VALUES(26,'La Samaritana',NULL,2004,NULL,2,NULL,NULL); INSERT INTO "movie" VALUES(27,'Ferro 3','very few words indeed',2004,'2008-11-10',2,NULL,NULL); INSERT INTO "movie" VALUES(28,'Il Capo',NULL,NULL,'2007-01-05',3,NULL,NULL); INSERT INTO "movie" VALUES(29,'Le onde del destino',NULL,NULL,NULL,3,NULL,NULL); INSERT INTO "movie" VALUES(30,'L''Arco',NULL,2005,NULL,2,NULL,NULL); INSERT INTO "movie" VALUES(31,'La signora della porta accanto',NULL,1983,NULL,12,NULL,NULL); INSERT INTO "movie" VALUES(36,'Il marito della parrucchiera',NULL,1990,NULL,7,NULL,NULL); INSERT INTO "movie" VALUES(38,'Tango',NULL,1993,NULL,7,NULL,NULL); INSERT INTO "movie" VALUES(39,'La strada','unforgettable!',1954,NULL,14,NULL,3); INSERT INTO "movie" VALUES(40,'La leggenda del santo bevitore',NULL,1988,NULL,13,'leggenda.jpeg',NULL); CREATE INDEX ix_movie_director_id ON movie (director_id); CREATE TABLE movie_casting ( movie_id INTEGER NOT NULL, actor_id INTEGER NOT NULL, PRIMARY KEY (movie_id, actor_id), CONSTRAINT movie_actors_fk FOREIGN KEY(movie_id) REFERENCES movie (id), CONSTRAINT actor_movies_fk FOREIGN KEY(actor_id) REFERENCES actor (id) ); INSERT INTO "movie_casting" VALUES(3,1); INSERT INTO "movie_casting" VALUES(5,2); INSERT INTO "movie_casting" VALUES(5,3); INSERT INTO "movie_casting" VALUES(39,4); INSERT INTO "movie_casting" VALUES(39,5); INSERT INTO "movie_casting" VALUES(7,6); INSERT INTO "movie_casting" VALUES(9,7); CREATE TABLE movie_genre ( movie_id INTEGER NOT NULL, genre_name VARCHAR(15) NOT NULL, PRIMARY KEY (movie_id, genre_name), CONSTRAINT movie_genres_fk FOREIGN KEY(movie_id) REFERENCES movie (id), CONSTRAINT genre_movies_fk FOREIGN KEY(genre_name) REFERENCES genre (name) ); INSERT INTO "movie_genre" VALUES(8,'drammatico'); INSERT INTO "movie_genre" VALUES(3,'storico'); INSERT INTO "movie_genre" VALUES(5,'drammatico'); INSERT INTO "movie_genre" VALUES(7,'drammatico'); INSERT INTO "movie_genre" VALUES(13,'drammatico'); INSERT INTO "movie_genre" VALUES(21,'drammatico'); INSERT INTO "movie_genre" VALUES(3,'drammatico'); INSERT INTO "movie_genre" VALUES(9,'drammatico'); INSERT INTO "movie_genre" VALUES(9,'fiabesco'); INSERT INTO "movie_genre" VALUES(8,'storico'); INSERT INTO "movie_genre" VALUES(7,'storico'); INSERT INTO "movie_genre" VALUES(28,'storico'); INSERT INTO "movie_genre" VALUES(22,'storico'); INSERT INTO "movie_genre" VALUES(13,'b&w'); CREATE TABLE _sqlkit_table ( name VARCHAR(50) NOT NULL, search_field VARCHAR(50), format VARCHAR(150), PRIMARY KEY (name) ); INSERT INTO "_sqlkit_table" VALUES('actor','last_name','%(first_name)s %(last_name)s'); INSERT INTO "_sqlkit_table" VALUES('movie','title','%(title)s %(year)s'); INSERT INTO "_sqlkit_table" VALUES('nation','nation','%(nation)s'); CREATE TABLE _sqlkit_field ( table_name VARCHAR(20) NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(100), help_text VARCHAR(300), regexp VARCHAR(100), autostart INTEGER, "default" VARCHAR(200), PRIMARY KEY (table_name, name), FOREIGN KEY(table_name) REFERENCES _sqlkit_table (name) ); CREATE TABLE all_types ( id INTEGER NOT NULL, varchar10 VARCHAR(10) NOT NULL, varchar200 VARCHAR(200), text TEXT, uni VARCHAR(10), uni_text TEXT NOT NULL, date DATE, datetime TIMESTAMP, datetime_tz TIMESTAMP, interval TIMESTAMP, time TIME, time_tz TIME, integer INTEGER, float FLOAT, numeric NUMERIC(8, 2), bool BOOLEAN NOT NULL, bool_null BOOLEAN, PRIMARY KEY (id) ); INSERT INTO "all_types" VALUES(1,'a ','little','test to see how different type of data will be rendered by default','you','can chage these renderers and I''d be really happy if you hve better ones...','2009-03-12','2008-06-02 15:30:00.000000','2009-03-15 00:00:00.000000','1970-01-02 00:00:00.000000','12:05:00.000000','12:30:00.000000',1,1.2,1.2,0,0); INSERT INTO "all_types" VALUES(2,'well','I don''t create','Another record','you','couldn''t test how it is browsing these records. NOTE: time and time tz ae just the same, but sqlite doesn''t make a lot of difference as far as I know. test it on PostreSQL.','2009-03-13','2008-06-05 11:15:00.000000','2009-03-14 08:15:00.000000',NULL,NULL,NULL,123,4881.69,4881.69,0,1); INSERT INTO "all_types" VALUES(3,'thid','is','The last record I write here',NULL,'stop','2009-03-13',NULL,NULL,NULL,NULL,NULL,111,12.34,12.34,0,1); COMMIT; sqlkit-0.9.5/demo/sql/ex_71_uimanager_button_event.py0000644000175000017500000000115111714210425022231 0ustar sandrosandro"""uimanager/button-event A different way to change the popup menu. Here we use the 'button-press-event' signal of SqlTable """ import gtk def menu_item_cb(menu_item, movie): print "Movie %s has %s as director" % (movie, movie.director) def custom_action_cb(table, event, obj, field_name, menu, treeview): if not menu: return item = gtk.MenuItem("Show director %s" % obj.director) item.connect('activate', menu_item_cb, obj ) table.add_temporary_item(item, menu, separator=True) t = SqlTable(model.Movie, dbproxy=db) t.reload() t.connect('button-press-event', custom_action_cb) sqlkit-0.9.5/demo/sql/ex_34_count_movies_in_mask.py0000644000175000017500000000120211714210425021674 0ustar sandrosandro"""mapper & fields/adding field in mask A field can be added to a mask as well """ from sqlkit import fields from sqlkit.widgets.table import columns from sqlkit.db.utils import DictLike class CountMovies(fields.IntegerField): """ A field that counts the movies """ @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the objct itselt is passed return len(value.movies) LAYOUT = """ last_name n_movies """ t = SqlMask(model.Director, dbproxy=db, layout=LAYOUT, gui_field_mapping = {'n_movies' : CountMovies} ) t.reload() sqlkit-0.9.5/demo/sql/ex_42_completion_dinamic.py0000644000175000017500000000100411714210425021315 0ustar sandrosandro"""completion/dinamic filter A filter on completions may also be based on value of onother field in the moment the competion is invoked. Here the contrainst is on nation. The completion of director is constrained by a possible value entered in nation. """ lay = """ last_name nation """ import gobject t = SqlMask(model.Director, dbproxy=db, layout=lay) t.completions.last_name.filter(nation='$nation') gobject.idle_add(t.set_value,'nation', 'IT') t2 = SqlMask(model.Director, dbproxy=db, layout=lay) sqlkit-0.9.5/demo/sql/ex_06_table_filter.py0000644000175000017500000000050611714210425020122 0ustar sandrosandro"""base/filters vs. constraints a filter can be added with django_like syntax """ t = SqlTable('director', dbproxy=db, order_by='last_name', ) t.add_filter(last_name__icontains='a') t.reload() t2 = SqlTable('director', dbproxy=db, order_by='last_name', ) t2.add_constraint(last_name__icontains='a') t2.reload() t = t2 sqlkit-0.9.5/demo/sql/ex_40e_completion_m2m_fkey_relation.py0000644000175000017500000000054111714210425023467 0ustar sandrosandro"""completion/m2m fkey - relation In this case we added filter on a relation of the base record that is actor """ lay = """ title year director_id - - - m2m=actors:nation_cod,last_name,first_name - - - """ t = SqlMask(model.Movie, dbproxy=db , layout=lay) t.related.actors.completions.nation_cod.filter(nation__nation='France') sqlkit-0.9.5/demo/sql/ex_22_relations_filter_join.py0000644000175000017500000000075611714210425022057 0ustar sandrosandro"""relation/o2m: filtering Adding filters again: now filters span relationships. A '__' underscore separates the property from the operator and the property of one table to a related table one's. Iregexp is rendered with like in sqlite, with ~* in postgres """ lay = """ first_name last_name nation m2m=movies::title,year - """ t = SqlMask(model.Director, dbproxy=db, layout=lay) t.add_filter(movies__title__icontains='il') t.add_filter(first_name__icontains='a') t.reload() sqlkit-0.9.5/demo/sql/ex_79_printing-related-fk.py0000644000175000017500000000043211714210425021344 0ustar sandrosandro"""printing/related foreign key """ lay = """ name m2m=movies:title,year,director_id - - """ t = SqlMask(model.Genre, layout=lay, dbproxy=db, format={'movies.year' : '#'}) t.printing.add_menu_entry('Print to pdf', 'genres-fk.odt', mode='pdf', accel='p') t.reload() sqlkit-0.9.5/demo/sql/ex_66_hooks_on_init.py0000644000175000017500000000261011714210425020334 0ustar sandrosandro"""hooks/on_init with buttons In this example, on_init is used to configure callback on button 'b=ciao' that is only present in the mask that will pop up if you right click on a row Registering a hook ------------------ note that here we have registered the hook rather than passing it to the sqlwidget. The advantage is that it will be used by *any* widget that is created. Of course you should be carefull in using this feature as any contraints will be enforced in any sqlwidget of that table. When registering a hook or a layout you can mask it with a nick and use layout_nick when calling it. """ from sqlkit.db.utils import get_differences from sqlkit.db import defaults class Hooks(object): def on_init(self, sqlwidget): sqlwidget.completions.director_id.group_by = 'nation' sqlwidget.gui_fields.year.format = '#' if sqlwidget.is_mask(): button = sqlwidget.widgets['b=ciao'] button.connect('clicked', self.button_clicked_cb) def button_clicked_cb(self, widget): print "Ciao, mondo" LAYOUT = """ title year director_id b=ciao - """ defaults.register_hook('movie', Hooks) defaults.register_layout('movie', LAYOUT) t = SqlTable(model.Movie, dbproxy=db, order_by='title') t.reload() # clean up to prevent conflicts with following examplesx defaults.unregister_hook('movie', ) defaults.unregister_layout('movie',) sqlkit-0.9.5/demo/sql/ex_67_register_and_commit.py0000644000175000017500000000141611714210425021514 0ustar sandrosandro"""hooks/on_init in this example, a hook is registered that has hooks on commit and the table is used as an m2m. At this point you see that the hook is called 2 times. That's becouse the session extension is called 2 times inside t.commit(), since default sesion for sqlkit is autocommit=True and in t.commit() a t.session.begin() triggers the first commit, followed by a second one explicitely requited. """ from sqlkit.db.utils import get_differences from sqlkit.db import register_hook class MovieHooks(object): def on_after_commit(self, widget, obj, session): print "on_after_commit hook from %s" % widget, obj register_hook('movie', MovieHooks) lay = """ last_name o2m=movies - """ t = SqlMask(model.Director, dbproxy=db, layout=lay,) t.reload() sqlkit-0.9.5/demo/sql/ex_61_hooks_validate.py0000644000175000017500000000127611714210425020470 0ustar sandrosandro"""hooks/on_validation on_validation is triggered any time you save. Here you can implement a read only table... try changing something and then saving. """ from sqlkit.exc import ValidationError from sqlkit.db.utils import get_differences class Validation(object): def on_validation(self, mask): changed = False for field_name, old, new in get_differences(mask.current): print field_name, old, new changed = True if changed: raise ValidationError("I don't want you to change anything!!!") t = SqlMask(model.Movie, dbproxy=db, hooks=Validation()) t.gui_fields.description.blank = True #don't try to change '' in None t.reload() sqlkit-0.9.5/demo/sql/ex_01_table_movie.py0000644000175000017500000000153111714210425017746 0ustar sandrosandro"""base/base import these are the base import. All other examples skip the imports that are granted by the way execfile is done in 'demo.py'. The proxy is not strictly needed, it's just a way to pass metadata and session in a single argument Table ----- base editing mode: table view When the first argument is a string, it triggets the reflection of the table from the database. in this case the image is just rendered as string. Following examples show how to change this. NOT NULL fields are rendered with columns with italic font Right-click on the record to see what you can do: delete, add a new record or display the record in a Mask """ from sqlkit.widgets import SqlTable from sqlkit.db import proxy import model db = proxy.DbProxy(bind="sqlite:///%s" % model.DB_FILE) t = SqlTable('movie', dbproxy=db, order_by='title', ) t.reload() sqlkit-0.9.5/demo/sql/ex_13b_opts_format.py0000644000175000017500000000102011714210425020153 0ustar sandrosandro"""base/options: format (numbers) Numbers will be represented according to active locale (according to environment variables LC_NUMERIC, LC_ALL, LANG). It's possible to force a different representation setting 'format' and possibly 'locale' on the vfields. That can be done with set_format method or via 'format' argument. """ t = SqlTable('movie', dbproxy=db, order_by='director_id', rows = 15, format = {'year': '#'}, # don't use group separator ) t.reload() sqlkit-0.9.5/demo/sql/ex_40b_completion.py0000644000175000017500000000040111714210425017771 0ustar sandrosandro"""completion/filter you can enforce a constraint on the values returned by completion """ lay = """ title year director_id date_release """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.completions.title.filter(title__icontains='a') sqlkit-0.9.5/demo/sql/ex_65_hooks-after-flush.py0000644000175000017500000000162711714210425021041 0ustar sandrosandro"""hooks/on_after_flush This hook is completely similar to 'after-flush' signal, but is meant to be defined in a separate class so that it's easier to propagate validation hooks to a spawned child (RecordInMask) Open a mask right clicking on a record, change something and save. You'll see that on_after_flush is called eather. """ from sqlkit.db.utils import get_differences class Validation(object): def on_after_flush(self, table, obj, session): for o in session.new: print "New object: %s" % o for o in session.dirty: print "Updated objects: %s" % o for field_name, old, new in get_differences(o): print "%s: %s ==> %s" % (field_name, old, new) for o in session.deleted: print "Deleted objects: %s" % o t = SqlTable(model.Director, dbproxy=db, order_by='first_name', hooks=Validation(),) t.reload() sqlkit-0.9.5/demo/sql/ex_20b_adding_records.py0000644000175000017500000000112711714210425020573 0ustar sandrosandro"""relation/adding records Adding a relation is as easy as adding the relation name to the layout. Note that in this case the table name is not sufficient, we need to know the mapping so we pass a class_ attribute that is really a mapped class (in this example built with declarative layer) """ lay = """ first_name last_name nation m2m=movies - """ t = SqlMask(model.Director, dbproxy=db, layout=lay) t.add_filter(last_name='Faenza') t.filter_panel.reload() r = t.related.movies r.totals.add_total('year') f = r.mapper.class_() f.title = 'Alla luce del sole' r.add_record(f) sqlkit-0.9.5/demo/sql/ex_02b_mask_movie.py0000644000175000017500000000064311714210425017760 0ustar sandrosandro"""base/different layout a different layout with ReadOnly widgets and and exmple of == syntax that prevents generation of label creation: in this case label is created separatedly to force a different layout. """ lay = """ ro=title date_release ro=director_id L=description TXS==description - """ from sqlkit.fields import VarcharField t = SqlMask('movie', dbproxy=db, layout=lay) t.reload() sqlkit-0.9.5/demo/sql/ex_31_add_field.py0000644000175000017500000000236511714210425017364 0ustar sandrosandro"""mapper & fields/adding fields You can add fields to the table by: * creating a fields (sqlkit.widgets.common.Fields) * adding it to gui_fields dict * adding a column that uses that field It won't be editable as it does not have a correspondence with any database field and you wont be able to filter by that field, but you can sort on that field and it will be exported reglarly when the table is exported. """ from sqlkit import fields from sqlkit.widgets.table import columns from sqlkit.db.utils import DictLike class ObjField(fields.VarcharField): """ A field that presents the obj """ @fields.std_cleanup def clean_value(self, value): return "%(year)s %(title)s" % DictLike(value) t = SqlTable('movie', dbproxy=db, order_by='title', ) t.views['main'].add_column(field=ObjField('new_column', {'length' : 30}), position=0) # in sqlkit < 0.9.2 you would have splitted the operations as follows: # my_field = ObjField('new_column', {'type' : str, 'length' : 30}) # ## add the fields to gui_fields # t.gui_fields['new_column'] = my_field # ## create a column # col = columns.VarcharColumn(t, 'new_column', 'My New Column', field=my_field) # ## add it to the view # t.views['main'].add_column(col, 0) t.reload() sqlkit-0.9.5/demo/sql/ex_41c_completion_autostart.py0000644000175000017500000000067411714210425022115 0ustar sandrosandro"""completion/enum m2m Completion may be configured to act as enum, normally only if remote table has only a few elements. In that case the list of possibilities is shown complete regarless of what is written in the field. """ lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.related.genres.completions.name.autostart = 2 t.related.genres.completions.name.force_enum = True sqlkit-0.9.5/demo/sql/ex_50_fancy_layout.py0000644000175000017500000000171311714210425020163 0ustar sandrosandro"""layout/fancy layout A more complex layout, that uses notebook. At first you may be hit by the funny description language, but you'll soon realize that in *many* circumstancies it's enought for a database application and lets you test several different layout in minutes. You can also see how to add other gtk widgets and connect them """ lay = """ varchar10 b=Who_Am_I varchar200 - - - {N { %time {>>.general date interval datetime time } {>.hidden_boolean bool bool_null } } { %numbers integer float numeric } { %text text uni_text } } - - - """ t = SqlMask(model.AllTypes, dbproxy=db, layout=lay ) def close(widget, window): print "The End for me..." window.destroy() t.widgets['b=Who_Am_I'].connect('clicked', close, t.widgets['Window']) t.reload() sqlkit-0.9.5/demo/sql/ex_43_completion_group_by.py0000644000175000017500000000053111714210425021544 0ustar sandrosandro"""completion/group by the values shown in a completion may be grouped try completion on """ lay = """ title year director_id """ import gobject t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.completions.director_id.group_by = 'nation' gobject.idle_add(t.completions.director_id.show_possible_completion, None, 'start') sqlkit-0.9.5/demo/sql/ex_40c_completion.py0000644000175000017500000000042011714210425017773 0ustar sandrosandro"""completion/filter on fkey filters on foreign key start (and are constraint) in the referenced table """ lay = """ title year director_id date_release """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.completions.director_id.filter(nation='IT') sqlkit-0.9.5/demo/sql/ex_75_printing.py0000644000175000017500000000123411714210425017325 0ustar sandrosandro"""printing/pdf You can print to a file in both .pdf and .odt format if you have uno installed (it comes with openoffice) and can use an openoffice server. NOTE: This sample will *fail* if you cannot start an openoffice server or if you dont have the 'uno' module from openoffice. """ def debug_context(printtool, context, template_name, sqlwidget): print "CONTEXT:", context t = SqlTable(model.Movie, dbproxy=db) t.printing.add_menu_entry('Print to pdf', 'movies.odt', mode='pdf', accel="p", tip="Generate a pdf file with these movies") t.printing.connect('context-ready', debug_context) t.reload() sqlkit-0.9.5/demo/sql/ex_16_join.py0000644000175000017500000000047611714210425016434 0ustar sandrosandro"""base/join You can edit the join of 2 tables just using tables='table1 table2' note that in a join you need to filter/add constraint on a name that is a composition between table and field_name """ t = SqlTable("movie director", dbproxy=db ) t.add_filter(director_nation='IT') # NOTE director_nation t.reload() sqlkit-0.9.5/demo/sql/ex_41_completion_autostart.py0000644000175000017500000000042411714210425021743 0ustar sandrosandro"""completion/autostart Completion may start automaticaly after n digits entered Try writing 2 letter (eg.: 'fa') """ lay = """ title year director_id date_release """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.completions.director_id.autostart = 2 sqlkit-0.9.5/demo/sql/ex_26_relations_m2m_table.py0000644000175000017500000000101311714210425021404 0ustar sandrosandro"""relation/m2m - different packing Now the SqlTable for genres and actors are placed in a Table that needs to be made to expand. You may notice that the separator between genres and actor is no longer aligned with the end of the title antry. """ lay = """ title year director_id - - - {T.a m2m=genres:5 m2m=actors } - - - """ t = SqlMask(model.Movie, dbproxy=db , layout=lay) Tbl = t.widgets['T.a'] Tbl.get_parent().child_set_property(Tbl, 'y-options', gtk.EXPAND|gtk.FILL) sqlkit-0.9.5/demo/sql/ex_18_record_in_mask.py0000644000175000017500000000077311714210425020456 0ustar sandrosandro"""base/RecordInMask Each row can be opened singularly by right-click on the row and choosing "Show this record in a mask" or by using "record_in_mask" method of SqlTable. in the same way you can open records on any foreign key, by just clicking on the foreign key and selecting "edit foreign key" Here both of them have been requested programmatically """ t = SqlTable(model.Movie, dbproxy=db ) t.record_in_mask() t.fkey_record_in_mask(field_name='director_id') t.reload() t.select_path(2) sqlkit-0.9.5/demo/sql/ex_35_custom__obj__.py0000644000175000017500000000124611714210425020273 0ustar sandrosandro"""mapper & fields/filter: customize default A field can be added to a mask as well """ from sqlkit import fields from sqlkit.widgets.table import columns from sqlkit.db.utils import DictLike class Director(fields.VarcharField): """ A field that counts the movies """ length = 30 @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the objct itselt is passed return "Customized: %s" % value.last_name LAYOUT = """ first_name last_name """ t = SqlMask(model.Director, dbproxy=db, layout=LAYOUT, ) t.filter_panel.replace_column(Director) t.filter_panel.show() t.filter_panel.reload() sqlkit-0.9.5/demo/sql/ex_11_opts_mode.py0000644000175000017500000000067011714210425017455 0ustar sandrosandro"""base/options: mode you can set mode of the widget (UPDATE, DELETE, INSERT and BROWSE) can be set/revoked via read-only set all columns as not updateable """ t = SqlTable('movie', dbproxy=db, order_by='director_id', ) t.mode = '-iu' # revoke INSERT and UPDATE t.reload() t2 = SqlMask('movie', dbproxy=db, order_by='director_id', ) t2.mode = '-bu' # revoke BROWSE and UPDATE (but new object can still be inserted t2.reload() sqlkit-0.9.5/demo/sql/ex_27_relations_m2m_editable.py0000644000175000017500000000040511714210425022073 0ustar sandrosandro"""relation/m2m - editable Actors can now be added from here. """ lay = """ title year director_id - - - m2m=actors - - - """ t = SqlMask(model.Movie, dbproxy=db , layout=lay) t.related.actors.set_editable(True) sqlkit-0.9.5/demo/sql/ex_60_hooks_activate.py0000644000175000017500000000067711714210425020502 0ustar sandrosandro"""hooks/on_activate on_activate if run when you press Return in the field. """ class Validation(object): def on_activate__first_name(self, mask, field_name, field): print "he's name is %s and comes from %s" % ( field.get_value(), mask.get_value('nation')) on_activate__last_name = on_activate__first_name t = SqlMask(model.Director, dbproxy=db, order_by='last_name', hooks=Validation() ) t.reload() sqlkit-0.9.5/demo/sql/ex_10_opts_dev.py0000644000175000017500000000037111714210425017304 0ustar sandrosandro"""base/options: dev developer mode show id as well. hide/field_list prevail on this mode. default is False. """ t = SqlTable('movie', dbproxy=db, order_by='director_id', dev = True, ) t.reload() sqlkit-0.9.5/demo/sql/images/0000755000175000017500000000000011714210425015357 5ustar sandrosandrosqlkit-0.9.5/demo/sql/images/sostiene-pereira.jpg0000644000175000017500000005305611714210425021350 0ustar sandrosandroJFIFddDuckyP&Adobed >7ID>V,       @!" 01#A2$3pBP4%!"12AQaBR#qbr3ᒢC$4Sc% 0sÔ5!1A a"@Q0pb2B! 1AQa0q@P` +܇ز`‰zyB%#;еr-ƽ'cVLZ-wS?O>c_Y:$7u"ޖsY)A~79=/׋#N<:~vm Ŋ8v^2T9?چwE0c3#{}^>n}7)咝Izs.ߝU*庶&Ko[&O,V7jmryzUʂ|]TٿZ.stoA^ Nco]h鋯3-m" g\>x=FO]{d.Y3\p(å8 ̛*6F ^2];>{΅S|~=mkQٔa V ZybQS~קD+ܖG^/i/a_Hb\q3 U=)0mw*_JtkF;=.J}=tC@KK8.^1<&in=Ϡo_x_ɤe.rJno\-n 83/+&g0>U.=Teags~#/TRfݾ6"f a)tQ$0O% 8UȤPAdG>S4:"+gE jpwYYXWQ,hѕ=!/4'& :+GJ)t^&Ը„%&* Ò E<ӄqRԯS;½em A<8QJFӜRi!%V2 [: !qo )0Bph2 Hiia9y<$*#(D,"2/W  4ֿ}{0S8[b1Z&^iX8*ʰ^.e_ne+c⏕qݳ[{.Ey;Y:t@.Ar)L:sOF^ ׽b7B*6Uգ(z8xp:MN*9(xч،4 W|&8? ' QTp7X%p*!Z<{C˱69=Tܿ,6g` Ik,O!W=˹]N!V:,c6rp<^mw<`p;j.A'fbpo'?'Vc}9[Eigq'(@7615>skimg'}Yy  $"ۻ)S1WѨ G޸vl{43yfX@ϲׅӊl_ٱ>"&?O>e1h:V-[t̃ɍ㏳'zvf/N7bpGu.(5tzmX.A%Q j&iXk;\d7#q?f5=ՏFPz (U,4KTqDfI!3;7(k^seN8L@VvBҴa"X`ll(hGc``8ݫv^o;171pQzBSpad):;~l*}g]'ZJU,lT6=-\G}լYV57":ݙN~Jwo=Tǹ3n2VT z˺uwWtWu ^ox -AkgSS5cnxm']&hVR{g d@+ @Oh`-86+X|o `' v]2hҞ^KFWV.V2^ւՉ"|ޟ}zR{GIގſo?lh6mt-v#H[P̷̻22W9-컌յ/qN(Etٶ/Փ j?.}M@F}lԮM̟?:yn>KFa0Qy]j5k../4(M-6C&[4,F6v DUFOiYfL|.[=UtCsV^l )E!h u0 yWO7Yfl]G܂ChoՔlu:vLɾ7OZ|:o~Km7);|/2douZ,Vߒ)#tcxḑA8 )ZE~6`PКE-Z폙8>*M#2T^J3EVIH.ue$1GΎlᒴw I^?0'MT}-^i9Rmh4FGeuCa<,`';DŭJ3y us]CX ZqO j.n 8둔Ǭ$<{3>DEh V݌StE<<ؒ5$*idQ4kN%-)(#YS mĐ`جn9 7ٖA|fM''[ğMɗp~|l djŪ(SHS#Uz7sE?F6f߽Ē<(9eYIw0lΌImdx("%ƩD'(5 JrK$Z(Tҋɞe.qTW_ ʤ! ʘG>?s&&#fsnT,dاӌ 7tNNklX%gS'PT1X1kZ֢ؠZ/=8E[n\N[!wMfՎ?-lobXᥨHnfRG2bۅp׭KMAAto-xO[kt+G8J\fЕ:!T(c+8~k6xY-σp8]A0o ?lbx4E\q/)$ƵNdQinI3E6̺DmM2.Ly\ [>xx2.8NBუTЁWfXx՟w?8&emZ6_|uDwtm$D$74ڊϳ=NEK`̰+f2--йPu~\sֆWnJM"ԝR6C:-x5j']SZ9QHxg Zwpxl[Rh/\p"+\!pm_F.^x1|ۍX۷dp)U.-gW-(m<[3E&O/Hũ'j~՛2$a:QMWxy犙*kIi8u99**!Sm8޲)=Cċ} ×5h|8jvwm4s%Yme2}Mw\;kfĒC6VWwxw-,ib:uUT)Ʉq7z(X &ȆY"NU1PAdi||9s+(?ܪDU~4E>\]*j'H0pЏ)Ls }iҫn4D]P#Hõ4>W<܇3萈O !"Wv-EHHms e$Jg7$~`'oX}p$Q066- f4ӴNe/m[ně;0JmYǗ'mr2vgًFdz^XtftE y*K'*vb}5f˚*/cKװ5ZG)v2(xEC(E%EEݭ';D6w*b!~i&tw\fvG|WPrɫ+y]])ִ!BvST9*fw>fґig)x) >s@Ě:<7>nM*_$ˍ"BTاCB_ {q &m+ihbDF*F{4P}/d]0.SM%dž5[%ؖQ46ּZ/T]1tZ? 4BB਩*,]2xo\5WRϬYüӒ9b[n|ڵ'K=u8+ZSf; Trީ4E۔C6bl"^D4Ɲ9wH)o(T|]ܥvJ; ewZkz-CQE"!"lIw0 %D"#Q'5D3W|K0uTв:mH43/{-Qvˈ5o+ZѺ~)Il nU|q+*LPuѢ]p"$"[}P4ߺ!MN:E%F_*GTӼ]M5 G͖¨҉pG"OO2Ӯ܉jZ\Z٤b*uE%d%kMWB4یf l$`Eo-GsUz᧛hEڿo}v$(() n`ʊ\Q}9cKD]::UB 7'T\G8kT9HiU?|R9KeO4UK1!pzb^_ӴNF>3gScd$bg4 i8pѩ/T UH˖~Y`sdKd?&??bAZB64;} ZL\0U"ʐvf\+vĚU&qTE4Mr5[`JUIC,W*h{BEv2AQVKϵ"G1 E.W}S 86;}Rcj\8&%"U:E)Sht61U(rE5ӚUS Z̈(MI,y25|knGSHÞ;Q,#.+ᤜ|[dnsŹv]q *ާ ;^I#PGz*t{Qi"N"Ewz)o $=i`s>Ll n,] Ccmh:$TB=\%S֭yjrd1kIj@. t$B$U@;#z`E9jG;6_AR?/>D9ycD{J KxwpCVqX!ov)tEd-Ƨ=eZCTmwK\/Tv{CDEIS,TCp{{I9.[w%Ӷ5 1S/KHJ,"yEypBY,$lv8ZuA $RQn="acV;*}7T#N^Q}Q[m>DSj_,ep]֋{UUr% Cvh~ =^)ecG\6Al#4Db¦YdjrۡJr ,wGQ;-vJaXv /hݍADcDwF\"$Qp # {)QQKzޗS۹Jc[Kz.lլn])T[88$")tH0UlS#gjz-QDf3v'KKx~""p]J)l-ލRލލލދrG+t|-l nwdKB)}ٍQ?J>Un$=`G+e5A nZZ&o~GfJHG|ZZZ&m̾gt`YGvx:: FR|]OFoK:B$i9z.?v'dJ{kA{UI( <'$K?:R=՟l4nO6 j/)z,7j$5Tܾi T-^_T:#_Hwmh\iG>Y[6ч)p۫{L,l'%®wI鋁[ګEGPwdP<:-pa9 iPI唽HzĤY]&u&sˈ-7Jt"ɥ,hrڪs~вj@ȪVB.%{u.2D0tm"*_M/E!5..li&Jy9986@C]EzEkŧ6XzZcqR4kLHeF{vCLqX{Qjm& I!U>}kD\j֪ ES":DE ywgłFܸ[[ț!JmE*HL9 sjMj (=KS+ӷ6D$%_&Hu:֝EZҕzAors슚vD^"Gsj ő\ 84K=36ME2yg'!N _^UusUg:7(UdΛy#I{Vqhz/cK"NuT _rG,fѫ%LvMjCiwq`.] \v$ڱVO~ȩv3R5&@U7.h"-&kі>PC4qU1Wű>&2pESDUTN)~"tlD\UphR&=zb n!Yઞ抽v\S7yT%)hJ,3GlC\,d,եSci{ě"!o0 S$۱"'J܍gX*1ӳ!o]7MU lU"qQqfua_grPIg@VNy0g5qӦkFy:0s^kZHԒe}[]/5uS;iF@ Aݒq+B6K{9n腻mƈ[0Iϐjajׄ^9GT1.yA " %,yW@q EˍvZ{\"yEzЉ BU$nBuS;0em'.I WEغӲ"ҥw۪.(u, #hnD 8+yƞ^J4Y gIJo\%)KMӏ>ۚZ4WR\B5TUي <|J7H7UuHy)^xuW9Uی50pEډm3pnIWPa6`"l5ϦmT}j כYPS 8f&GLXp_6YkGHIMSܤH@ULȄ>S7ti_#|y{zU~8-:jm-P[kႣ}UOKde}~Hw:e?ޫ|:җMSJOpYO}oW>ߒӻ*:j*Y{gߌ;Ix5T4eN5lt)%pZ( jNgNTuS˯HKnYְ4;#?!.6}2zͮ3>iaB830GkSᅺVl-Lov%͎ҙϞ -m4oӿo൉j0K>MGDh2[8'YjHOt&zg彷ޟ|\[W%ϒftS];_Hy hEpYZSjE@'G>M1kOZz5dS%JgD9a}uOVEعb~&a>z1ɛ6޻;vջxP=ђx$ wk۬;ٿݳ8y};"Nr yx?!7zܨlt[uS/ I[-Ѭ&LxsY]щK7X)xIXg՗bQtn[,fqArer\hUBqs/w,㚎۳v}+l||chUxvgX_sl}r|v.ECAˏ-j~v[/Tҳ㋡46Og=׶̣7"Fַ?Z$Ot)=;ؔiW>7 ͊g6ܸtu\̍S-wI+J?Y>Cs\F=~(㳤B}w`M;Lsa6`x;>mxM0!ӼD`Ltl7}[s'Ƿs/h@S8NXeNٮ{ORug.(;O~?o pf:V?o|[6t֥CXzgu{o|veډn.|@HgPP_%, DF75ChZ PUglM>̄9D]J^f75vR3[fHRTս;)XH!f>edYWZ@1pXSYmp DY!i&=|݌W:g]4 t'Z#- %+"B9#94)YMbMwb~R3}_ UZj0L~ iyE:_Nﰶ|G{W$tF: % kH%Gy;B5kdPbw-N&LiJ<ݭ"#{tTgEW|k7n0}IJJYYoRtFHq*8tf=oNZOز~p >k5n!U6=Ȳmd]|7Lɝ{(NA(̜c.q{̹ANhúUuw ?qnu"ѝjպ b?!lZ$F*l`BnDdOLI dy!i(J,ʳE;JDU%dpJ Vb\AR"Hz_b]ԑkQQEH blj͒ɵU! B b,Ci1 맇݀E$.VHJH!&cJftc͈\eJg?!!E#+]$zNQA+jLJ;cD!u"VcאBDwZj+AB2$bE.MƆ A]( "ƆE]= ]; 1غ~AgBUN?pE~?!J_[!$wR܉X|~'Nuqݹ#5n|GM/҄A_TR1 wDl넎>ć5]3Gx9,Xj)L|+ uqOl=K!-&d)e{@|pHmϞ|-l2r2۟lo/1C\+*Y|)- LmԺܒ'BS!2SO=,Y>G+gÐDgby/>K F㣲_qK)\|.?Ӹ]$L6&7O.};}ywB BbY؋u( I7$ DFLѺ nd :#N!5clc ha; +D!tjh&2t"Ȧ+CXyCXEIXV;X]cJЌbvI[Ph!tP2}SQ$q^ $?B+ce$HJ+ca'c8! ŕa }cJa uayغ9]Ȭ!t+2uj7u.2X;9+j+4]'BBi&'I=9OVI&ɲJ:$ܛf3x;?  CUC9 X0a Xԁ6@Mh $šBW?5XyB*2& BUu6쇲&x fYJ o=W' 4*kdJ Z5~Twxa#i yZxm&ʱXm= = wʦRTbme1TD ]KKb:ii&mIPiZ@` @*MM-3mwrb$B_}XE2L]y-7jctv H?ѝlJ?C/)9> \[< ɱm&)U8C|`ulx:25ŒϝzZrPQ1=Z534ώ`Xv8mI{[_!gC!'Aff.V)ZfyYmNܜcG?,gs tQ3k=)uFhYs3C퓱s,LY [HpO2ϒb[4Z|KF&ܘӱBmE?/bqJS㴲2l )r{BUh2 6&fV:ld5;)'[Tزpw%ocSca EӧXqaLNn;5i{3z 4zGި /B#,5c)iG8V.hÍMRp޴Uon! "bS .HQM'9kN8Z@2HꆮҊҞ |ǿ(aMtz h* +AtU>fYx3w>:`$YuA;Ƞ`;hX$ڇ- ´_mV3Q*Te0Iqd`h8 Qr\XA0I'4_wWlK}#=^ D=fLAC}$v!ron7$y9J(l)|+(YINtaC,}t/f̚F.&rqv:\$Z6ENjU@u7G.~u,sKQ?OEZG4WA+S~4ū'x|+uuhL9BDY$pU!C-flv`k4xdhpG~W|)o2̮15P ٚޔnSXv2I"¤4ܛ7lS;8XW̴ʔʮ@P$5CBc[|CP|f?J%4[ qU@n Nskͼ(.*baQ[}o AC[-۬(@B-Ez.. )&UXWYQB?4Ajcaqk~ S8g_vA _M1WƨkqХgɆ| 1,VHQ㏆p os?%̉^UF4oaw9Q,f5ۿȗU={mEt?ILosqW}TcA WwbA%ab|HQ6aP瑛c2Ͽe1O1'a?!qNv p8꧑Ȣ+!l>Km~w$gj]c î3j cf)XhfLdv N1BTG`[tT#f/ OI ⬮,,Sڑ.P`>6Kǹ17L詙n7&˜LII0NO}+~/]<]D`v`Hw:F= __̯xzd+?~ nd uf74EA$~~` t#xzI)o?ibG*#<26ousu i^ q5 hJ7d9/= cT5+0{6?5G?IdN!fͳoPφÎ=wf.10Ab?mla?Mc|)2s? ogv4)%HEtbuY f{V/$7)-x9iǹvq~PhlTiivAG_td8UmةS87MY|\5,oѦkOjL`Y`B\#\etCJn~K8mƒM9nbwo%QtcsjJ} o`1fƛC+W2#xGR[h5&S}kK{}dダ"sMNpln~g=4|˾L;k1MHݾ7kKy"Kr;.W&Z:Oo!.C_V#!^S,v%f6X6%MwOsgɾăz&•JVĻrۍSlnY/F_iv,VKF|' SW#J<އبѳrhHg8/)$۪}R BA}!o C#G;3S&gj;ǑYƼ@۫8ra)˒Z7c?sױ.AM(.͜6P\^[xB5Duދu-3ّ,An"YׁZ[HQ ;cL; mj{ IMmsEw7w+҅ wk1a7~^(m#)[WB?j{\QPE lέA8'wS'#29¢;]wI&֜M|Li4̤!JSTfvoڵ} DٝS?vCLDžx؝y"^*>mǟpd3 "s7|w$ൽ\ۛ3ہuΒX R Rr2F=iQY]Rs\`).Z(]Mj]vJ B(EWn6C؞曂H?:+UMriZZD}Țҗn5cST*j+2 w' $ $**MkWor* n z;4PumUZ05 U`h"]D67sD"^vhvt&G5*SOkm!}\"e}6ZR4팳Z=/!yp'5B -&Z7hТ6{yRRʊ6j@*JΑ4zLYb.qK Dִjʋd7pbSm.5 IX K`B_oSof>!*IǹD33S6okԃQDšFg^ލ.10HGUr݌f:8I0Tܔp\ _2fn|pG_N׹OܝkG$j%J/|$56kxe+w]yc Ac~Jh@HL٤ƍ*@T,͐IdM4[uM)Ld{q =-45>jxԓgIƯܑWTzU~afFQ1:4&q x ?ecd;"5o'sܬy3E2CP;qunO>ʞp }Y˖IxwA{Ow]rop,kCvrUF#O59QÐX!cΦ:t#W= }͍rÐƆ`CjKV^oΧ.xMV(P|DYtBߛعpѢzE W)i-CGD3eKw"c]{5soyYdmHrqd =7nh֍A"qyZ=6YY{[!k8-?D-w;tSĝJyjtR-  qDtjב.ݐ1B[[sLN+XfJ}Yp4z& S5FvG6Idcl`:/l^[;64 DRz](s۴ ĒNH+NTwbeag"ۏ5Aegj6p ݴV;tD hVM57r\[M kQrM<bٟ(кI 4WŠ;>S؟}@ܹ,a]o}뽻]̧{6PB>ouq02Ip0jA >aQ]Í-܎qǡ&JV*\&D!o{pcAbcwOzKtXǏ>/.G ϶]s.AMۜY,#{uhYZnKn6{}7'Í!IF$V[["N3w.};Gy0Ah(AO6^.k)7Umkwyݻl"XZ\5ˉG ƿ1<6$ۜzhݓ-8D~6l 7 5zړoԅ5}wk.ボD ,2jFx%q[b˒F@4jSUwYL[Xs&|dK Ԑ~+]٣@N#敧ȪvΙ~{p0H"Eӹ=\\(rfw#<A>i"_I3j4(EhmعMBfž +\TÝS8kkT*z&i͔U6a&9BWs3c7ƋKdnCC]Æ>M)XW nύ2&1 Q&\hv= rwH1+t{bDBXecnl.{6v8Axn[ZxԹkoRְ᭗П٢"%[P*9R}GlNC ([G \RsK;;Yo52{e)$@k.-"ʽa'Y׫:UK{9suG~,"Ъ*ۊx\BE.x1Đ 2c߉DZOc J+ M#U%5ͽ xJ*7U{Q6mjL@G>O>+g";1к|gjA\X{)Υ$|nm6/M4W>%ݙy=&hd 6jB]˒(>˧Y3?-Ӟ|f[dv+k[bC gN&# 4.<o8+;TmwN+d u\|Ωn+mƏfIЏtL\A$m/IuSqruWjZc~Q30q1bbG+hesmv;$^KǗ-\Nax1eK: "\wc2c>0vgE#hPuG'~ &mIoٷ9MCvS|$?f;2f6 \VSGkw{V\˖J __64]a}YLhԀZu#Wԟg o|mpɆȡ+K1hiz1`|cs#4 cWo}+8^zk6msk]>I΁`7ljML Mn3oX e7fcݽAMxذlf<8X r>07,lM[k_5c{Gl}{M:dὼSe\j"=hBiT|c֛GL6@ v5;/7_w8s\ZkOoO;|?>{0G+q^d,!4Roa(ZVv_'^׻ʱc^FĕQvk#ָrknY>߈Lv%6Lgn{?N^7)8ȏ+WTUc;C^c&O$qiعsL'^5G"_q 2 (dĜovɼyu"t|L}ǖٳ D?r } 7iO%*ɭ =A>5o?}$z;KyZ>1 $VC!j?9sYӿ_pr!kv ^V#H% PyΝO/߲u7~[۳6uZX<vn12yxƏ[j]ħ+Swoi0)PNJw:yͣn;DD?:@oTwW?Oo.7ݔ{t=0@u9.T쏭߯uxyS垎pO^+-Qh+].MIRh… $8Z)e ?>l=n~CO6縮{waB۱6< SdmXd.?(:U'.@FV8䂠*/}iuf׊ ;K*;U\Uh?jС4Kdc1 QRKq =?{qBnbu3w|ݒb U3hVvCZVpy` 9 w?Z!5˙xLJ6^ViҔYy|UJTڕˑ v%@$i$ jGŎZT /8%c??S?_Q9p[?o(uKnOFTpR)RUn "W r*G"\.@ܟ2P8WX 5W">lX˅7HFٽ UKnWի^c(˜<ޖoNX zQBsKMyRO5ºTUuN[{:/rxPך Y ~ ]@j\E\GSTG: PC~\v Rx ')<+@cÞZN )BX8kcuRv6fVg?01B!ITU]1UDEYu <KRhV*M#kO H6ʤग़UI$UQ@RPHTmhJAFW)*ctGV n*QTs!AOWڟe.xq6I]($Ke6rBW .i /+kTÜ*h먵UtAMe6K-2ZrЩ*#ebURV'#x䎙A27PW]6V幑-ݐb;i"i*L8X%MMXKLG1ё#E_k& IOcv|f4Oҽ`n/QMk֖ȊqP3"LYr ȿOmcve\.#ʈa5 Ò=4>iٮ@Ype8SWT+L99s\?ARN{vC7,V4k\\j$)G$=^tz%JVu7s\W qڪ/UB'AwƢPxwִFuWŌ<:=_ sE(I$ ODm }]-E6+,UIVFa )yq56Y9YuK=4ۉKJLb\ yUQ> 1q&f3LrtR̉[BASPZqE,HbIT; .q8Ŧ:\9Yq卡U@8X3HXMF(B@u1^ApVS Zeęo6AGV\ַkJhNk+ꐍ6 'qdP~)2(h35&OcfrY%ao kڡOJhΩ#?%M>û <9cd*5r[F&~n߁$yL~ͮm*q;~qǐ!g/;h4`Xs|FGmcg` 2Ay8P\dgmI7ঌ)01S`Vޥ\dtFD$rőZ)Obi!ӓz۷Ҕ\랇?fyQܡ2!ؽfm&}X'h|yj Bd = GSoNZpslF>ԮD[XAET|!Η}ׯnѡp{0-#ȟFQ\]yY2{>-tW9lcĜ5\wMkap)F16To{φ( 3GTpuy#ΤF-?BB{_f8 kAV SAm{Vlf@֍keXy͒J=ը`ϓ=,H-^ 5M~I-EϒjQ,/Ӎ@%a ?3 K#]̪QPQP-+J1DBhtFRIU%u o꺅NU  Aߙr+4UR*dEgp~?tq)xi^l%1d3xAۂW Os04=TrK\X%'ΥNSA]BYG 31n@X"ӂ3R+EF:L)FOQy$.Oc LE]x%y&q*P8:3cprU\&Jaǻ:T|Wʪ)~A4JEB)i+jFSz&f<ȪMƦݮV13I4 :+?"0uDXI~cm(Δo3g\H縹Ƥ$5CYY RZ@!{qEFkq@dg`EhZג4b֋Ew64,G> jYlwF,D1/&.\*e NYjtwRAD.W *c]qsIW\/DTܼڄ;&xMyU3"q:?ڨ/$%us \UK+>Ph!]$cMPja t)F.xmbq/jhrgּ/9m.sK4^䇺pR"PjH]xiP!HK6<ЁQo V0@tZT7Os)A(oqP\<ؔYCⓞJ twQ_j.*;XJ=!hJRjMU#uR*Ud#^ a ͎@&ĊnEYp*WKB AAZ(PZ*HSd\<:2捇Z޾ =6`Y2w,\yfgnkeݰG2ZIS6!_HҮ< VT̐@f얀:h`~+q)tL+MZ+Jڪ5;P'Pݻyѵ^d:6AsZiC]*)6l kJԥݳ5@ZYZSUh4̒F̘ }[6@-^Ŏy!ŭsm{x(Z{ uzi"?/9Q'bn87WIl+oWv|izl|(*aƥa{Wc 8(1SUQWG_J̽l1w=΂Xn8{ e O +'JKÖ@ڍmcZNlVO#M<XYȐXyWtMvI.m)"vdPV])蚈5h$`:YhaK֧ /$y%?TnIy&'j)4II%x%M7**ABxSly[GB||X<UAQZ*P\H]ڮ\J*N:֦ATRﶉw fM;)@?0=ML#E!,> o04'{L6;N$;6﹘|vNMD\#Zp\,&k$ bC葑 oaǐadsgY}Z:qRjjTӫͽ2j$oc)Hi$ƸVF^zlCG>ē.HeDfkm2tsZne⚘E5\r:&!9VHnUbaiYj*,xDBEt h^ї#=7{4/!lw#Ҕ){jO L)2jx#;?' ?,{#u3=e}1l[p[aHyѡ׉\oTI$TGgFr5 ay$t%yc_%aZUꢜ8ncǁEfK@x$>GjPr"P&Rh!sDZ6Mf[M):]RMdq&$T@=ĢS뫫rtE}#'Z;*\=WH ~DE+B*^AE2 wVAj\Q.976AQKh_R*Eu (hvH@6qFPU89.sE%Ҧ(NcM+aI^h I 䨧VO>J.ck Mhzq`&GaRoD j-Eȡ"E!&Xu{/5*2_XQ'xWE6KWqB,@^kKM\HvA=|!WV柊 \I51=gtsE[zw(ERZͯyphȗ+? GNJx)1Ay䴡ł(^hgM%ҷ O Y+Asz}*ZUAu]H4#*9hUo*GЊW&CB鉮D4iJ$m0 pP}VV W- Q-+MZ5NQǂR*F!` S  ۂ+|2X}3 OQ0sg H 4U#e.pvZ^k-78i,Z+Ϊe7 1u]HݯuiX)e34KF!Ozˆsl$/D y\-m\$2jT]ad8 P \ Q >@ UQ Qc)$֠?).ntAZhfk6<4Jx0x5mji7 :ǒz 8v;v lj)V\P[<6 \ toᕥXYZ+}8oH2ޠJU yq9=ճt @9T@ SÂOݠAE3 `p>jQ*l%m>+58DEx&CvlX@`%S2iE²>ꅧUVUϺiQ /EVAA}^|nh~4s ~jnA W38$}`CORm{\[! mnsLƯj~7IVH5 >#ŌFzO.u͐Fjf cM5 7vPHcKE\4c,}$y-p4m(ugtO WKxL5Nуtr 7 U i/CQ:QQl( bŸzX`sd1Z zosORqgda䰽;]m% n#iK7x{װdIK\m^rwL$qe+VP6J{Jz^rFIsM–?q[Y ~]ar#vDAD9YP2M9!ɺay1<*W5}FI0~]\|i=~MtzڄlǍ~*U KOKS$= 7E,.mu\{H*2i-gI<cD5A~[COH)CqÏܖ s ;ġsU|RM"E!G"*G4wFwP]gUd\FA" `%3r D>)(CU87 (e{`H0-2$Ƕت.Ǧ͢Cꏊo-4(Q&luD 6FQ^ P#$UeWBI#bi֓\hG HpiZou{];~cJ[{v|ۘkdai9@P,% #C#&\<裊2֗᠙e+ͣޘge=C5 ,cK((i-&3^6-(Fc8axLnx-Ɖots\nc}Q7zuO}&y'6s >CF4v\;7h6CCx {W^SfwOkZxn2J5-Mܬ 6AgedqGDx-l#dGHޒAjrqu oWi5qIxjS$*CJcCSy/^6tըEiAx5xfidp]+JiSQ$id󪣏 iZQ ю62]KV.7U`^JCjx+m-3 8XrIh;kujN UYsIԦrlGe*5U a`T$]Bh Q޸ZۅmG)c<*Hik V1RȌݥ^6}G1U, G3]4jy V>VPtK+khyjcOH MW}4?Q49;œ 26c+f-cH E tL&D02) sb۲ xAJpִ빺ly4/~,\ds+Eh=),Ia4>)mTomv=Sڹ߯A*VT2u"e$EtA{>l] L"̥,Gnr7e!?6;P;TyqNJ)SO4 f>^EKo$m2S0MkVy.\O D*J֦S}*MV@-BGjtʈoҼ 5G:q&TJY 4j*y)\Q5Vh4PiV.hDTM#ei$`1n wN1W~j2d1?;'{?5HH7ÁéO ;x 4PwoE/E4&8" K-hqq|Ah (BY3@@Oz*?% GY^H.yvت]@]MUkOI*C ԝ+##ࢗ SEPYebaWG.ar5Vvnid7gAx+RJu= TjNeWHB$K!ucln~@jUTk*Ŏ۹DrGrad B>m\L3LN &eơ Ht'OrV^<>iYeH:V Ć Lq'A@~(,ހ*]~*q>,67#;\|*t[73@p6ĉ]3Aqȯ?ͭ\mZ* ]^.Tw(Q]T#]A]F]i|4m a{Uq(JtVo8<=l 6{-UqS@:/.ٗ-- D@H}MH!]{<яlK+]u5 XрD+nI 1ql4 hSsYϣb&l[f9Q\д_,{xA̓n.b:۰sKkH?)zئ 5M[K1rq.wMCZQt8mk۱&ycDӍ8 5=?_m_u?jPHA j) uUYyͯqP涄 ]sW REY<Gdv Pyxq,9'dީ4>ȕH馧5-.[]co:䳥v؞㫴A4/lbr$jkuǗlpкGθ~(!9+-)RMq$%&ϥ]Q[ 3(S,oqK"6:HѴ( ~&48Jz}QVkԈ^MjUngXZP&$=9)ÂVC~T6 '#MʩԴ,L<*3m͆}N@P8洤ə %Q 2Pnu^pY{l2abə#NCvD *aF\$J駰-\q3# c1kG0O,F^:TUJ>tGtxhƗ5uG汯!£2p:"MTzSL*STF][$Xk[ @ԠZ5Ѝ 1M-kWEvRUCNHJ!HK+0qtqݭn3KM$t2妪2:\H,>!3l:2(È>](ЂG87q_xz.;Zzy-V0Pk"5m<֨;EEPܐMPdu#[r sO$l6$^ata?RKӓx04h#ˊXx$`+@n/zh#naÏJ/a8PΠĦd&㎉9iC]TNss¼R2S!)U@:ޜՍU(˪t\rB%#JpqYe$%T)u=K$SQB.-Tĕو*鍸;>I33+Zr :W^(T>e}lOUnc7E7 UÑhЃ+M'>#ia#_Fs15<i I%G:S!FI5މ%Z{#f$ؤՒ[ Y}&^QX^F(v<ˁ,{{e=/8-NAUM~jgx19i_P#PC42m> :8!; Gk](t vNFu"664/~ AJZ@VtzW*&m5 軩)7ikcUtQ(GԚG95KHH<.Z@C4poPFԷ%}K(f׺>R8=Rpc9L]#q}jvNOymMs 6_ ^>I69GZ[c&]7?Kh@ULYxFFGQi8Uѐ\խ{TܛۺOڪ+1^ Aj+ MPv[2ۘ?;l6` GIc6%JS>5W*SƫG*pRITXymMuN˫E Ͳ9"UJkJ4BwVS4W#V c=haV7t#}iU"hnЕ+GBVQT^5*P)Et6@ (tfZqQSDɓJ]!n ]%4:eT2|RAڸISeCSGP.igI6JqDU/e*8W@BPZ*7i["զ܌>G27≲+Wen\}tB5ݕ$<u{͟aKUp K葲uP!U:(p0s (Aa- 29kZ,ݥU4 ~ΛK pwn`d hO/,qan缄JkbI hY3:1kC -^'; XO•lx$5͖KM}!54θ$CD?qpKOFj[R)VPNI*.I8.I"ʮPp> __>߁mڨuA$R4׫O2 d'5&ޯf>trR?%9V,pk\!rwp-tYx0Ӛ\G3^vWm|y&ds#pG3]PMIklm$m- NSMk)vHʮx£r#2c6KƵ ը&3!ͫcsHTdWoMչ9rs5MiD9& Z£s7U23$97G<~(Y,0[~" M<:p6RE9[ܭ rwL>ZDLbUr/;ruƅFX 9cn?Y^A"ĝV$3rUp 5jySSyW l<[YG?}_l=0&e]8ꛠ0ڑ5GdJa'iaEPڕ,O->*B.>ۑr.#d󭪁 ۚ)q,a]TmIǍWjWa3]]9% .ܙM5,kQI-WKk5uJo~3f?`(+N|u!G7GIBƴh2œt^+u[JeݣY @ JbYƚپ%wlsvfֺHÀk[B9`H/SIj4"̓wXƿ@"RF.l=9 c \FQdQ7;cިsɻZF?J&I5=('̺u^GiE;HVy}q2C& .v6(7?#i29cYh KH7k!W g{u qYoCuU46ThCAj'K4QZ oR$b O۵(乯AsC __%۸+v)¨sK nTI+䝹_׫>>WܻqaӚp3Ǎ̘<[ A)[0l ::y0M(|,,@֧moL4m_Ro$Z=-}A,vBP \o^ex&wS owat28o{KI.(/5W% 炬ڛa97VHD6>4K$*]&6Gۀ熌om(j=&R -H\AC 4s(f:;SsWS)Iێ++OZȟt(r Hܹr pUːCUG.J:\"U jʢTɿqW7r4|.Y_ ޫ.Usqlkit-0.9.5/demo/sql/images/le-vite-degli-altri.jpg0000644000175000017500000010761311714210425021631 0ustar sandrosandroJFIFddC    ""C  e _  !1"AQaq26Bt#Rber$3Us %5C&STu47cf(8DEv<!1A"Qaq235r#4BRb$%S ?ӗ&J9.qw󒹞{=T㈴KPbCEm_=8JaK ݅-/R,iya'n^62s5[֓kMU+y4aTSŵ_`nvB. mgWAGI?!ƌCpXBZVڊzT#EԬag8:3?( _$nžr&ݳTkTqlNERoý՟?6nGZ^zzÇS>|4HK(G-cgT>p8?98T\s7["1>o:TxeiSi[e…% BB!@\c47z%W%ÉLGQ ?eߓ5 z"r?}ߖy$Oh?J')ͱy`*1¤tYM} S(嗸ٛqi)H 4g/]fix v aW$>l. GF̭0.hU퐄e2}tܥYnX!PUER^=ӎ;hups+jkivu׻̭CDEb :X}UN; k./GeҠxud]KmaIY 6ăO=Vk-VS$DqjmqtcF'sB8xqez J@=ƣi8T~(*Ϳk݋!E6ӉV_og+qў,|-mQiFPS)Y*I irAċ W2)SDܕ 1Kh):cSnI]=ENRvu"5ڬpCC[ܷ B H)AU+=\ߛ) ȟ!} c~[e[[|'/d9+1uFo>)䩂:7Vvӽs)e~%1Pr$ب JT$(n, 4qnNoC]]L T <p=31_r<*b+ni-|Za{ʼP*$ZDHejKBJ(̈"86 )Mo0_ [ ƧSMAnkkZ]! 5x)T(e.LPY],*o%n-F :bU\,(QHgSHU!j,0^[K(ՈC rJe* x))4 %,3򚽕oG!>s|ᵖ:qc4Uk˲\U+&픱7ql1Եoz f+F:S^OH_o}-X{\?J"j5uJBu @o{h[FW*rfN NZ+be-,Vt=,\ٓDŽeQ :֦n:Ӎ ғfӧEΒ\G|2ܹҜen#Sm Uh+umiNȟg?%N+1ECnIۄX lqU:QuY^ӭu;Zyr icW+S9N\&[Rⴡ T{o3JVB 1d&3% Y8mZmwlj;x 5|QʹTu11Ћ=m(\SyL+yҪ2qAa×>t2~pa_}ɯ/B1`"DU?}8e9ޖ}[}HHxcd\+7**kZ   QdEۭJR\Bh6R6_u[9NDٵhªUrrZpDi:.Qb-&)9@SdFi)NB.[M|< Hmhq<ԛiblnpMc.U? WbӤP:ʓ*u+Z J:7(c|8qg/x:KN&z쇒-<.'M:U&xkP: aLy,i̶q_GV5[B)7#wǪLy R)jD䤩&RnZ{zkv1 *uM~m i+6]m/s~o1&đdD̍CȧP jPLxA6_S|tgΕ\U:t7йsC B\s03QsOQG.(K+}(aE-h %(ԕM#ԶP!q \lS0Y2<␽Td}3 ǃ1SHCqХX  TlIi.,5Upu@7PSJx%;wuop蹃1Βhq4T)J#&fM2CƵI\vz\y^d-0-ia\.i6:NZCmV Jcݞ"ː3knZzM%BVJR 8f ّ3TI0;OxliqX_~V+Gv/RTNqLjYNs%ߔ%+I7Nks*g9=Jp&Bˎ8Q7$IۉI([t˿'?Dk@JsM%j8K~gMy&6.BuO6[)>XHkC>/Ԓ/0K\JZY*b$\(ž(jYRd %pRiQF,絨x4fƘuo(]'PWh$(nNĨl%CG%)qHIJB ~%Bډ 7**[0Ni Tk:ź* l%"e=49y(틭! 7=Gk `4ͪ<*;%M\rBVXҔL|:o@.U ],/-`Υ+C~F L7q\s/3%QPczl()%Cȷٌ;B-Umcby|Gglb[ȑLldR\ZԐ=oቶ[KV=Q+yzY84$:~'-n+T\ȅtݽ9TKz(OrMM)M(l8֣5vhTY!>2f&S4nWZӄT9|6/bZYOx1IWfe HQYOƺEQ3QJ8q9r9Sڼ\r [XƓCOѥ:h/?41қ`#\BU{ī'xK>侤WS<9Ng'>4ZBR o U܂H%@7 wg/3S$OLոBLEKf )J5A#JFŸ&z#0J*#n0xdl6%0ZXJ%$Z)I]˹aQ'TZr 䡗VhzA&ɵ`eܦx O |k$ ؑ=SvJFs~AŴ)Z7)V$&:d;)1(jthGڦ! Ǻ RI*$'}${wubZR4kӥGXcfvwΣMK)?qbt$`ץ7 <:E2vizK*Re] 68:..d- ҕ:;I'ۀ@g4T̎&48z])%+IuIIP:) $irϔZn4ϦR"vZSQ[&RTl!`]?I#^R{5ֵOg )TP.FISN.'z&ɋ(p▢wo|k#4͕:q[KhٳïLd%^;HSY$"%RjbBBE#%(8XHKeְ#1S8o`gIo<7 cmPۥ}^O:&y}!vKE5K@[-=])#n_M7Ƃޕ*TMTk/wiֻ *axc}ѯb{@)X7lY{ě/"s}-Et=q` IuZRҰ<߀.p f>8$cm\\m@%J$ saݾՀC~F ݩJ{D ڥ$}ZpQ1n}߱[ 9:RmөhslSFj5!ZE-&iWqtb)<^tĖZ+Jb.ʣ7ĕBy9Kr`a%McRu$ޏ )njB.cOpx'Ma5-})hAƂ)^\Ӷ5[bZM N+l;il|=e|݂Qq5ջ.2erI|ceѯbMذ"d{ī"s-EwW\tg`X`X`X`0.p`-7Bxㆹyy5((\qh#AF1ڴ]KT#GcSv7Ͱ2S %$P7N:U XmKt*QWH'Ju3(-?fT"\.A7hUnMf8&JRF抳2 ,4Ĥ6Z_-`tx.Ĥ=8uiZ2)9Wvu%iRWRT$n,)s4f>)z+mLbJJ{{7-w7+;/=H1cJPK)̂YO[m{` WHm!o5%5t6۪}‰kv(L.3!p B TA . g"G̙-tAeښqYJs@+%V+ޫ;QTYq jY~7YqhXD(X!J.nf\s+$T-.pZceТ,URʕj2vpʓN˫)R!NSu`r5qNɒuuZnIp.p` S-߾y~x{Tf??J)ps]dt>xq[kJ>Ho7ɼC} T 6 {5Ūyx6;[Dq9CS"+[H}qݤ]H":2[kNp˫K(QIci^Ynǟ{;Np26Gf T@F@ޝc6 \W:24iAI!AteVѶ؏4 cLo `0_#j}8e9ޖ}_zFT261ؒ#Ҭtb#<0yGrӃ1Ty8^%@,Wk^n"fhYB4YPtWT cW*U<`,,6@( %)8[qM+BV#8G,_%E{HMY1*DjRBT:J] O:[(k%J';1Ui(Kpj2%JHeқnG&iZ(1mԤ[Aۥ-kt\S>s!׹(Q:ѩJ>p6>Lw><˚F+$1]~P[+ANJߩo{2d?[^=ZHj߯h0.p` _yn=ҊuS8W}<&~cNj)ZRT::Ik:2 m4#kPRז}|a8len][)FJ7$񐃗&zoo\Q:KKhX6S`Nzɿȟ@b;OL-uR\~Ge“sqͨPd[wjNF^*ú@c: 'گ#N%Y}NwtW^Fx` ,BI&,NAv34|X`XOYPcڱQfr?J*fo;?!25:6Z;ib3|䯨 "M"r#)&bL&(f7NN|\[ ݊2̋*I2̂裷cEMM׆) J 6R6>D[Vqz0&R HYP=փYnkq f.S|n wZ;w~ÍGF'Ώީ^>)XX 'گ#N%Y}NwtW^Fx` leZm [!)JEʉ6dr/~OXU6-W$'XY_rY:e^N*6#ej[]yHqi-`ܸ ەxla2ڣel0B_N EXrՒY|:QeLf)>3oii>'WO 3j2m>;ꌫԄuئsT~2yTU2\u  oB~7ՊJKXJЩS!Hr4bCDm$1J.:4gRRYLťEXO9P؛g_{\f??JԼ:/㤹l6׎>V ;lM1sv\Zoĝ 8Skɬ)R].?f Lǁ[|T5= ZLF[ _d*33M* }"^ oN&scFZΔ+k RXeqnHy> l0%ZulRG8!k9@,F?ߓ5 `r]rcq#NFe >|>yFeTݾ/+&= L`0_#j}8e9ޖ}_z['̥I.MLȘ7uhW~ÆX\"Ep1>QdPϕ [:w9m 6rzbMM,X_-zdɱ&Eo~Xf\ ni-`HfhmEeNɊL͚vKL$v+|I2Jl}6u FQ*X+ޓً )U{C.C=xBjKC痖? bIBg6vC{%G'զHTjnkb)(X`:˿'?DkGՏ1Z=X݇Ҏ|>vy[[-nO]89nofM;-Nh9kePAg MQ(Fxg>;q_ zeiVjС{q㯤ϟV߇-WFB-F,d@u3p.&Y5Rlmc06:|&_"`WO׈ua}BX/3^Ȯ)R$y(rR/ɚqoNz/O_GJxK>Q]S` u3=;44%RgrQP.'`_ w謲ud!g~&RքBaC`ƈY..Ch󷍻n5tlLϧ^ )p/!GhmCv+UA.'PH1VSl-s8ơHC)b6wKzgo RQT|M]+P7, n&\Ǭ Zq|h<ᘧqW g0TZґK-~o^^%B#_V/ u%)D.~2 ֚9zWݫ XN>\5jClJ@>>EI#i`,5}D:nX>kNHqb5A+\w; 6H) F0Q}&K\e9>eg̑TazUd>ÖƦOIq a !`]?I#^ M2K7s=҉NLG-?n5O;Hݵ/\BO CǪV_~AR-Aejmŭʒ }:<k1IVN<%qRJo^SYoȠJN:2բ1S bK%ԟqe]QqQ ׄ6:Җ -6>cͫݯZx=I¿XR*LF2S]lGc(K/CӭRnjaw>FAq*s,Ew=N:3 03cp}.<ȔNIS% i͵c$QӌZqf EԗkȬj-u\azǽ$ xbc Rrr'(rϭm~r"Ban/Omw 5Hp]PVnabSl.|jP?q–߿(.bFLӒQXi@@Q>] Ɋ f8K9œ}!crp4XxbJyk*R^[@OZt%{wmS{T=X|0El~LRRqJ$!QҞfԪ/OO( ̼,qQF/RPT֑"Җ'Vu>ZqV?8Rh1eTҜS[r?@O!iVNRpԻżp|~00.p`W6㷷tKw5S/TCڭ+9/[yΩr]@mjHǭTt΢8hzIC=nNzٻ֜ŤSKԮI|D_q6J8Y<20vi+^g{N(ԣєܠ7X[QH&ӍEkùZQ8鐧ª9E^U߄ 6;Lkoh;z񸆝{y&swtgyré.iW,Ge֏Pn1g8ˊ>D; B7V48CʣLIlԛbcSsi)V.d{7HGکTkΠI6SwIlfQI4R>|y?#e6ﭱ_JxĀcqmW';Ϻ+/TW~d)i5,]-Bu+*m  MtĘQu.!9y;VHOU*ʩBΔ|c~qr%+NkB򐐗՞4(uH YVWI"EE)(ϔ,:GQqm/fJqwH! ĸ" Y}IYxj7z7:,SR֭ RR6"JOljԍy(iu q$%yJ0v|f⒇{L )Lh.EzAQõb]jg,!T["')l5l&uw^72ʔ! :pObnTעB.%*@RQ~(z=XYW&5E9L͙7)K`-vTZ7ԑxb9_%"qM6?n# xO9,_qCGA?2+n{_^9˪W.K6/N%IGpخt*/~ڬS_O&Pxq(qݥ$uBJw5%N7̓i ]p-I6D3f5j ȒuFV'Dh#\ՒU/e-H#c~ӫF֜Ez2‹#,VzqХ@㺩sKj3ؗ-)hY+!ڽ/J .6vRO_U+OMǹ'-?ɜkgm)wg+u9Ԩ\8گF&M*&wc ~X $%&˃1*-̵_)/('vٍ~"|=UzYE%qўXÕ- tDQ*֧\~Xz1:7 eJ;_ cÈt_DjiN^ú?QiOMj 2̹<ٮn|d1hKLl+-f2(7}Yt#e"%hHRlAz/rm=qkDx:,HTc)6}|1XO{Fanʽ]&,H_qq#ӛ|r鎻"s`:>5N:"2k ]#*}8e5=,Ev=N:3 EB[! 1 }'&a+c+/IR|唻KHRF%hs=(׊F&8ꔵI8]h]DIAԽby  O__\O:PC,a.YV&^MG\H}d^8r-ى0pS)zXbr5dZz6b5Jp(XZK86wRX:xlX)! Ø?)b )(ςI7_ L:BqבmTQ<X~MnleU*SBjmJr RԬ*$vEt%A!5TP Nջѓ\qK땙(u[i/h6IC77l)R(kņAO7ej1QbxqXe,ˏː J HYDulX֩mm;ӫ'8}]čM+in\QnIqK"'1UKxWVju@& oӑH磬l>PVO^=a-N2r˨q<\)-#cJR_ ΅^ËKUlkInB/oqbU_i(Ty>i~Gl e#!mRڂO~2g+%0 poĨA݌cDk%EU*@Wv$NO 5~1`!1UR aKiþ.$'f9Z]JTVuq7<&I+/Mt'929ȩKmV89<LneJ<iBR!Ҕ߶TI6 ڋS_Eėo%ҳ]CaJh*T[?rUkI]A'p1(XO3P^ GIy[=z((( xi+.#ӴOoDu"tA>zc=2aAVHH6=rr Q$<ٶR}{|y@ɵ9 T÷8P֊XnDafI=GӍU풯5V:I~k 6֮kh\GDZSJRVˣӯ_3^kr]Ep!JiRfFa @P/(BrqW[>KT^"˹3IfFFPCg UҴ@M*yop{:<(+Vm-TNٮiy '6%*ʭ9]pq|HxxXUDhi%|T; c.U/%Jk?'~}nTeլj*.-qrZ7ͧy*Y뎴#s '.?̲3R,3pZCJ ;WQ;bB$|[pd0p3FErUV&b&F9I P9kMB^L.flك*@q &/RA m}IeYs6Q4n@ /xEQMJ=@!V,mQnJ/>)1Vco%Lŧ0e oa)OKyGa'ŸB&yvT^e%iĴx}KB-AߡyƣԷd_%+) L[q0ʚRm`-%*\*hƛY"q߈/q ?|)6/0J͆WqŪ1Z9=l.wJ/ye8>(cXOAL~+;6֗^9Y*z}NUJpF:(e̔)2fm Zt8ef޷T.jeR5\K$_LEpG+RO 97늘Y2bQaևUڿ&b("D-q *G짏l]5<66t} qW"ެio--K'S{J)5qt5E8<++W:%Mn-m޷o&x+}gQXW-·ޚ1`Skc]ZX?{qT^U8$OsmT91E3+DwSY-#bm'kz>:sSᗏ8]5ylW^qA={[UpMhKos~ah{ ҪЍYKLu#R4cVO 62̖#S*Y*Z$'Id)R4)A CWF,vOfNܒh苾㸶Bj)RT,RAx5rBVS 7ek4S[?&ÚX=ψiq4}=X殔$M㷡)o"YNvJ*('Wtk-?Œ[TxźRW#Hم_Ċ L V'FZ)$ ZP\^Đn-N3.Bjy^3j"ZyzHlAO<l *z~ ibR$BӖ+%V3XM{-߄-j1Gkl׆6f~ `vPu$2-7Q gkRktf' zڸвysi6R{i$MCuGdea KJ{j%]=X(ԥ̎ͰjRA=-Ucu#8FXSrH$l_31ˮejJ^yIP}1zFGYNV \P,ow_k1k)@9}췑uwm;%*=&ɹHa%Y^&'cztz^sGPE줓\YQ^T*Lpqn.@zjv0Փ-1fs)uV4yd4#J].㷓$Cic;Q+f;ZI o QD_XDU0.p` w)J'-QߛgM~zMkXcԖ:9G|[$6;;ퟑ5.3` th&p=Z]  *TfT&DZʕ}(H;\Mk[Wp޸HmlzQj;ӗNody7pUenh Kie2V)tڔYZOCtgOY(8a%lιn^Du8kҺvɬɧ,j\.- %șH+e(g[֕ 1i/k*o6I'܁}TH+.BwӋ#ZR}ȏń>tT2DNjJҔ[{1y%fIwJNѫ1"tzaZEQۨ\ج&ԲRPM`ÜsY^)iyR "Ċ^"ރ| . eǪ6ZTMXSn:ڊTV$,}ǬbZ첤ԩo."S}Aegy )Z MMuJNā^]$=mDkTR 2xc\n1f*hKjyE#cqqi$l̍ODmԫH?*k2;>OS-}!BqvRtDv+[viFъ)gF^鸽Z9kJMv;z`jFԎHQ&Jқ]κ[($lboBZrs0.7Z' t)9" M+Z6@"UoM LKrA:\Rz[@R/HOOXq'4% %!V*';{qgV\"gJGR\YT_u]H`=cEjkMO~Y<Gs􈡜ԧKi#Q#ȐJ:ZT?ߓ5 +.SN9z߫>z>\Ř$o |p-E\NS7/;6?z1˧3kۦF6Wa3[/ѓ/#.fob.eOk/Ԓy7*q6Lޅ WK_obʶî6Gfn6Rh haը<HQwAƻh.hc-e܎8g-Q8I-N g0ZiҒ]RrSm1iS_{Ic,;EEW-h (s&:6Pװ#{,^#;q7:q'Qr-f*(#7uDTJ V :횭6\H#,;dJ8p=:MEẉ^qj F#Q8NU̵ %F+76>l-M]H}CLkvk}]}=1#9"gr]FtP#c=~28 r;tF2IK~i `UӋ\cB'Zr4)BuF-L4HTej1FB7GA$+I]zsyO#]Y KE8fKy4@z|g~"DYn 0Sy S_[#*Ie.ũrܖai֬BA^sG1řc8xǙj氭L$# ~_ ):Mp"8pok[)mn~1M.c!(MC!=qd2yA.pbnv?(ӎrk>Zy/Dy Y?X.`3 0:<^+:c|0:aJAVO?^ M 9DQ.jiBbuzLS/(kO@1l)حMEXQQ x#nURR% N¤jEedZ,i)=/RS!A" SPXƭ-ph-vԫJmy4lxĨyB:Q[QfMaCiq]iGPmhI6TMz fQ3KJv ,Gy;ԡ%$Z&#Д!kK;%;ca K^Ris#̵J Ae}+Yŏvk٧8|f3lELm)U}-u*;f ^ocpVg'R=PdK9PW^:#`~Jyߔk6vyM'^s$ 7'RZ0ny_̍ypXv~t0/ұm_r^Fk5kQ6b*5)i*ˁa@~|c(}{~s&de35u=ϚA\V* d/CQx*tr_IKI;WkU&|SI{+L}ȁ[􀲞@_U٧pӽְ̟q +4?PUKvU|?xEQf>DjCX,-_bqs>q6T|pVq*YzhXT~R9w!f@JGb)Dt2HSCxeY M2 7$NA%byHE5>Q=bgJ\4A!dtI z+cV9ZQEj%De&"o6[*$zH|4- 3 :K4()^ $ ;0yCx+tM؟`OKypQw'{q?$竿qVqFKCŸ3u ·px`u|0:{:B0JyaB|lRc2M͵(Hlq" 4}x^Lfj22^@9.{ħ`kk ϙV ӉZe_r^F{/i%O3Oa u^F-iX$rM@8L8P#'WΎŶyRTH"=vʑrM$"/qg,d0fʄ e7 "3IS!IB{)6 >HV.T#9x=o9 367;51E tNe:dHez)Q;`uNZ1N?q9ލy!jyC$)Hĕ0o@?[}2 dZ:.*ha Wv#M ӾyiPMo3=K\yۧ-ԅ` 'Q+S"m1CZЗPKi&dJI x}CO?)>Z0b("{Ăuƒd,H7~ wFQ~j'afXOLdǴGWQF/֋!N_ 7GZ/⻣#M֛R\:UImخܖmlCpA-{0Qc)k?֟ᙤ*z(3tim7MO.ˬgOۘsm̰0fzT*K!D)J;FX ]߂.ƸN4XS 8PRS{Z[5en&go3,8Pe, ;m&i uʥEHJ&cu”$ەa3+S]lྦ I)р33n\׳Mf{dŻsn.v; džrqtuO<)%y[nJMil._ĦRaOq od"q8UQVPP:^uHt8MʂNK]~^̋.s6$tcb`pLJkT3bj":#"m- k`m26QQJz$%ae i`)$t U5RW3zL|\md0.p`RM3/8}S8x[2PxձBVǵ G[/98ul{PxՏjzp)$\xA< K *RJ:A\ⱆP^SbŐy K(NڑrF7ɺeq\d嗓+Dj{N K )6pB@)%!@߫qf:eW4URSm7!( hStYOp|GFJ(ߜH,y̭MO8!Iy4Sd-!"cN 6eDzd͐PRTM5/ldYed}n%(Saό]!I%_Rȵq{^A(˞n_iu֐ۍB'w$mpz^u$dHt$,VQM8pe3BW~) 'qpZzSǦ!(( 7(N%Do@p%A*\}ڈKT)- ))yv;xוôm5ϲu0U#q ,F?ߓ5 ʵVp}xd]48׊ue/:Տkzǵνxuc^:b^Xyׯ{[(]Kb,%v9Q oFǻ %0xTT)k,\aJS +WjH"spApbrMn0(JӣHya:P6p R/LLnLgbAAԣA6ҟ 7BGnVXW#eO4T!*u@w.XnD/ki[jw$I$ .K)< ~٧5RQrZRTNߪC@{K:*c$S\Se+ib%EgJG[ծ- udGgZ:,EN9q:'(R&dlCh6e&%Gt,VojsM)\v` kbR*չP0S\k, \GwVAMPs#U$&+PIJ#!Zm@#%WQ4)aR))D`RiI&!`&䓀=3Ɯi()CBIl E=8,W.3OpbLn!$Imnlk`cTƦT%Ҟ 7; w`\ ,Qw'{?y׹À7# pSB aSDZNf449r^} ҁnMC<3XtW`V*m9=YvPЗu(e))$jGRn;FH"i ob^Ԧ)0#N'֡2G2lfCi@R]W U)u|B_Kg3W%6sJG2k.Ӡծ'}dݝ9wr{ ;e8fC.%Imӭ*)4)&-'4G \|J4[3G Z7d>FND3tRғrMTWzo%C>nMoaq!fBwXqjheZ[[yK?)mJARkNR۽;>=X;g֍`(4{6`RK0BO7( BCf fhmp47~G:^mAzPKJH##'ddZAJT`J;m4:C&U6 mI8aaONiRJIX`]?I#^^{p +913Obu'-!HM{\\F̫Tʤ8.tGw1ȬW&Geyp) 7p x8YHu-溙  ƕ$gvIEYJBI $nyְ9Fΰlb+EN\ Nn|dBR]>Dc & A%D6L]VXRE hRIߦm?TF#ŨCIK*msd@N%q]ZĄ &Cn)6>K*@T0W4 ol[>5:Hiڅu)'YˊgU@;:yi늹LnC IoЏ&iTs#2CIQ`Vtn;"m 4{Za6):Tu ҟfk@m-k[=kB$OBԶLjX*d&XHʜgkJy)2%*ѡ$ \ ;)RXfB-@h%"J)mo6.}Q׈̋B\\bְ(z.pezuj s:@n,Zs0cfҐTmݥҼUZ RZfNv~SͰ܊ QӍdEk@77ۨl9ˋ`)E!'m>fn"He-UHa4aJ $-ǴbomU6:k<6Yj! $ 0ځ!Kqɬ뺁P {S -9W&P2j*CB`[ΆQFHXz| _uA>mKke!i7IFmqa:% BBZUzT[+I ͒G$u 6+ٞI,)~r@J6x^KYNh]K􄸄!C{\uzpK4GKҝDH!GQ&縃Ӹ#\)T<V[VjQU x F[:dP缰Q.T˲HRGrΫeW⺈ ,>QdemNYm)9jJP->zNJt`tG)9NRYԃQX}?8~qG` yŀ?/_{?,QX?{?,_{?,QX}?8~qG` yŀ?/?cyŀ?/?cyŀ?/_{q,ke5&LO 4?Xsqlkit-0.9.5/demo/sql/ex_40_completion.py0000644000175000017500000000160011714210425017631 0ustar sandrosandro"""completion/basic Completion is the way you can save typing. When completion is invoked entry completion is used to show possible completions. If the field is a foreign key, completion is done against the value of the foreign key (ie: not the id). The value is searched for via the attribute that better represent the record or declared as search field for the table or the first char field. You can invoke completion via Control-Return in any field and clicking on the down arrow for foreign key. Completion works both in Table view and Mask view. Completion on a field that is not a foreign key return a selection of values of the field Completion has 2 flavours: start and regexp: see docs You don't need to do anything particular to start a plain/basic completion """ lay = """ title year director_id date_release """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) sqlkit-0.9.5/demo/sql/ex_64c_hooks_validate.py0000644000175000017500000000213011714210425020624 0ustar sandrosandro"""hooks/on_validation - related Hore we change the related hook just to see that both hooks get called. """ from sqlkit.exc import ValidationError from sqlkit.db.utils import get_differences class HooksDirector(object): def on_validation__movies(self, table): print "called validation on *MAIN* (relationship_leader) table movies", table def on_change_value__movies__title(self, table, field_name, field_value, field): print "called on_change_value__movies__title on *MAIN* (relationship_leader) table movies", table class HooksMovies(object): def on_validation(self, table): print "called validation on movies DIRECTLY", table def on_change_value__title(self, table, field_name, field_value, field): print "called on_change_value__title on movies *DIRECTLY* table movies", table lay = """ first_name last_name o2m=movies - - - """ t = SqlMask(model.Director, layout=lay, dbproxy=db, hooks=HooksDirector()) ## you can change hooks any time just assigning it t.related.movies.hooks = HooksMovies() t.reload() sqlkit-0.9.5/demo/sql/ex_09_table_hide.py0000644000175000017500000000034111714210425017546 0ustar sandrosandro"""base/options: hide year and description will not be visible """ t = SqlTable('movie', dbproxy=db, order_by='director_id', hide_fields='year, description', ) t.reload() sqlkit-0.9.5/demo/sql/ex_62_hooks_completion.py0000644000175000017500000000060611714210425021045 0ustar sandrosandro"""hooks/on_completion on_completion is triggered any time you choose from a completion. Argument obj can be accessed both as dict or as obj. """ class Hook(object): def on_completion__director_id(self, mask, field_name, obj): print vars(obj) print "Was this film directed by %s %s?" % (obj.last_name) t = SqlMask(model.Movie, dbproxy=db, hooks=Hook()) t.reload() sqlkit-0.9.5/demo/sql/ex_70_uimanager_custom.py0000644000175000017500000000114311714210425021027 0ustar sandrosandro"""uimanager/handling popup menu You can add entries to the popup. This is just normal gtk. you just need to know that there exists an action_group names self.actiongroup_general and a gtk.UIManager called self.ui_manager. """ CUSTOM=''' ''' def custom_action_cb(widget): print "ok!" t = SqlTable(model.Movie, dbproxy=db) view = t.views['main'] view.actiongroup_view.add_actions([ ('CustomAction', None, 'Tell me sir...', None, None, custom_action_cb), ]) view.ui_manager.add_ui_from_string(CUSTOM) sqlkit-0.9.5/demo/sql/ex_14_default.py0000644000175000017500000000125111714210425017107 0ustar sandrosandro"""base/defaults Deaults are set on the database table not on the SqlMask/SqlTable When setting the default for a foreig key, you can use method fk that sets directly via the name. In that case you need to say which field you want to use for SELECT """ from sqlkit.db import defaults def_movie = defaults.Defaults('movie', metadata=db.metadata) def_movie.set_defaults( {'date_release' : def_movie.today, 'title' : 'A nice title...', } ) def_movie.fk('director_id', 'last_name', 'Fellini') #t = SqlMask('movie', dbproxy=db, ) t = SqlTable('movie', dbproxy=db, ) ## unregister to prevet conflicts in following examples #del defaults.tables['movie'] sqlkit-0.9.5/demo/sql/ex_02_mask_movie.py0000644000175000017500000000202511714210425017612 0ustar sandrosandro"""base/movie mask base editing mode: mask view Note how simple it is to define the layout. You just write the name of the field and a label + entry will be displayed. Each database type has a different widget that is determined by SqlMask by introspection. You can force it as for 'image' and 'description' in the second example if you want a different renderer (eg.: the Text instead of the Entry) or if you want to get rid of the label (via the double '=='). Curly braces can be used to group a set of fields. Double click on the search icon in director_id field to mimic an 'enum' field, i.e. all choices will be shown. """ ## Very simple, no layout definition -> a default layout is created t1 = SqlMask(model.Movie, dbproxy=db) t1.reload() ## Let's define a layout with the image on the left lay = """ img==image:140.200 {| title date_release TXS=description director_id } """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.gui_fields.image.default_size = 250,250 t.reload() sqlkit-0.9.5/demo/sql/ex_77_printing-related.py0000644000175000017500000000112211714210425020741 0ustar sandrosandro"""printing/related You can also loop on related rows """ def prepare_context(printtool, context, template_name, sqlwidget): """ substitue director's films as table objects """ context['Table1'] = (sqlwidget.related.movies.records,) lay = """ first_name last_name nation o2m=movies - - - """ t = SqlMask(model.Director, layout=lay, dbproxy=db) t.printing.add_menu_entry('Print to pdf', 'director-movies.odt', mode='pdf') t.printing.add_menu_entry('Print to odt', 'director-movies.odt', mode='odt') t.printing.connect('context-ready', prepare_context) t.reload() sqlkit-0.9.5/demo/sql/ex_72_uimanager_change_entry.py0000644000175000017500000000223211714210425022165 0ustar sandrosandro"""uimanager/substituting an action (entry) you can substitute an entry uding stantard GTK. I add the example here just becouse may not be obvious if you're not experienced GTK developer... Th idea is that you add a group of actions (ActionGroup) to the manager of the User Interface (UIManager). If that is inserted *before* the others (position 0) it will be picked up instead of the other. You can click on 'File' and see that Export has been changed into "I'm not the original Export" and you can right click on the table and you will notice that "I'm not the other one" has appeard. """ def test(item): print "Here I am!" t = SqlTable('movie', dbproxy=db, order_by='title', ) t.actiongroup_mag = gtk.ActionGroup('Demo') t.actiongroup_mag.add_actions([ ('MaskView', gtk.STOCK_LEAVE_FULLSCREEN, "I'm not the other one!", 'm', None, test), ('Export', gtk.STOCK_LEAVE_FULLSCREEN, "I'm not the original Export!", None, None, test), ]) # a change in the main UIManager t.ui_manager.insert_action_group(t.actiongroup_mag, 0) # a change in the View UIManager t.views['main'].ui_manager.insert_action_group(t.actiongroup_mag, 0) t.reload() sqlkit-0.9.5/demo/sql/ex_17_hard_way.py0000644000175000017500000000103111714210425017260 0ustar sandrosandro"""base/join - explicit you can pass a mapper that already has the join as you prefere It you explicitely define mapping for attribute 'id', it will prevent SA from complaining about 2 columns with same name """ from sqlalchemy.orm import join, mapper class Join(object): pass m = mapper(Join, model.Movie.__table__.join(model.Director.__table__), properties={ 'movie_id' : model.Movie.__table__.c.id }) t = SqlTable(m, dbproxy=db ) t.add_filter(director_nation='IT') t.reload() sqlkit-0.9.5/demo/sql/ex_03_table_totals.py0000644000175000017500000000101111714210425020130 0ustar sandrosandro"""base/totals A silly example to show the total on an integer. Here images are rendered as icons as the argument to SqlTable is a model class rather that a simple string. No reflection is done, the model is read from file and the model has more info to explain how to display it as well. """ t = SqlTable(model.Movie, dbproxy=db, order_by='director_id', rows=30, geom=(800,600) ) t.totals.add_total('year') t.totals.add_break('director_id') t.reload() sqlkit-0.9.5/demo/sql/ex_05_constraints.py0000644000175000017500000000155711714210425020043 0ustar sandrosandro"""base/constraints A table/mask can be constrained to only browse a subset of the real data. This means that additional filters (possibly spanning joined relations) are applied to the query when issuing. Commented out is the SQLAlchemy syntax for the same filter """ ## Example 1: movies older tha 2000 t = SqlTable(model.Movie, dbproxy=db) t.add_constraint(year__lt=1996) #t.query = t.query.filter(t.mapper.c['year'].op('<')(1996)) t.reload() ## Example 2: constraints on related director as well t1 = SqlTable(model.Movie, dbproxy=db) t1.add_constraint(director__nation='IT', year__lte=2000, OR=True) # from sqlalchemy.sql import and_, or_ # nation = model.Director.__mapper__.c['nation'] # filter_condition = or_(nation.op('=')('IT'), t1.mapper.c['year'].op('<=')(2000)) # t1.query = t1.query.join('director').reset_joinpoint().filter(filter_condition) t1.reload() sqlkit-0.9.5/demo/sql/ex_99_sqledit.py0000644000175000017500000000047211714210425017151 0ustar sandrosandro"""sqledit/the script sqledit is the script that let you groser which table are present in the database and open Mask/Tables. It's normally called directly from the command line with the name 'sqledit' """ from sqlkit.misc.table_browser import TableBrowser t=TableBrowser(db, title="Sqledit: the script") sqlkit-0.9.5/demo/sql/ex_36_filter_panel_tree.py0000644000175000017500000000076211714210425021160 0ustar sandrosandro"""mapper & fields/filter: grouping The output view of a filter panel can be grouped """ from sqlkit.widgets.table import Header, ModelProxy class MyModel(ModelProxy): def make_header_obj(self, field_value): return Header(self.master, 'director_id', field_value) t = SqlMask(model.Movie, dbproxy=model.db, ) t.modelproxy = t.modelproxy.copy(MyModel) t.modelproxy.tree_field_name = 'director_id' t.filter_panel.add_column('year') t.filter_panel.show() t.filter_panel.reload() sqlkit-0.9.5/demo/sql/ex_76_printing_translate.py0000644000175000017500000000162711714210425021411 0ustar sandrosandro"""printing/preparing Context You can customize the context you pass to the PrintTool eather subclassing and overriding prepare_context or connecting to signal 'context-ready' """ from sqlkit.misc.printing import PrintTool class MyPrinter(PrintTool): def prepare_context(self, context): """ substitue director's films as table objects """ context.translate['d'] = 'director' context.translate['nation'] = 'director.nation' return context t = SqlTable(model.Movie, dbproxy=db) t.printing = MyPrinter(t) t.printing.add_menu_entry('Print to pdf', 'movies-translate.odt', mode='pdf', accel="p", tip="Generate a pdf file with these movies") t.printing.add_menu_entry('Print to odt', 'movies-translate.odt', mode='odt', tip="Generate an odt file with these movies") t.reload() sqlkit-0.9.5/demo/sql/templates/0000755000175000017500000000000011714210425016110 5ustar sandrosandrosqlkit-0.9.5/demo/sql/templates/movies-save.odt0000644000175000017500000002117311714210425021062 0ustar sandrosandroPKNI;^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKNI;Configurations2/statusbar/PKNI;'Configurations2/accelerator/current.xmlPKPKNI;Configurations2/floater/PKNI;Configurations2/popupmenu/PKNI;Configurations2/progressbar/PKNI;Configurations2/menubar/PKNI;Configurations2/toolbar/PKNI;Configurations2/images/Bitmaps/PKNI; content.xmlYn6}WjЇ.d؋\P(} hrH-_!u,K6{-<(&߼_Y`! gcf ߞ~onxBc̔pLֱ;lđ$rPH#`Vx+H\Vxl5vM{6w(Pj묱 j=KIqXR/J%#OӴ:\p8MkI8(q\P S;~ l姱UJlO)UYWb"Mumfz}za7F%'#4u-ض/ݐ*$fsK!n= : OQXTNxhP*&.Ӳšg%X1x1Z~GTEs DQDx22;WV;),/ ,^8rrMd}ѧ1W0 pqL($4ǣ=7!O7ze%2SQ\LNI49Ud,>u!Ubk"GQьx &N |ޫd|xwnNT:{޿k kb4|y'pgax=5I` G=>,n<>^4> M}.!Nي;[Ӛ4Hhɻ׆[Ͱg~3XyH!l9KGRrsWьށqlhAM<8P)k? BI/"s0njS:z/K-9KʔTH$L<1i/aee=n{Bפㅵ\cR,n;( B$K'W1;B IYurtˆUzcvK2D吠$h1"TH8.` FdeJ}bDJ#4oK?Z žDw/PH&i@8OAy)&ðu}JRBAՋ<d_i4+Hnb^JQ prS6>&ngE1+)xAy%; cmdp뺃O2xd$h4"f#UVE*cp bhuyY ,ph28g l r"724krXg&>\҉ڂ嘠9aErKّRYɴdw;2e剹@#K2RJdsvN~ P  g"eDJ Ӧϱ?)I4ͳAta,ɾֹ5\VbَRx߀jnn$ےgz# [8#^0zL; ŋoA)(E!̥Rjz 8-Uy"%ǔ}ݩp\˰VC'ű}cNKQ0y uuM{t"\+JmXFhVEG6ǝ* ~{F=5gxZۨnsC5P\WBq\(W u{+ =CJŚm*,F]j͹}WUePrɔ߹9yCڳqG"ZA2׼+|>y6z!kUX Yk!B[k?3 dq`аh=DhSߣ9/jɖ%xNf#VRIj>yub"Ap`+5p_ݣE3N:5w3!`]<ߠjIKkx#mM`'|:Q萵hs%6 7?O}P}J)?c<62j 'ѬV/x.斢;֧sPK+ $PKNI;#5 meta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2009-10-09T11:47:275PT2M23SPKNI;Thumbnails/thumbnail.pngYScځaR, `E$KID@A`B ?` lQ$0H1,A6I ; I !@?#4?WAȃ2`}w`SBO#kx/n9KCrc^֨=LHKrp46lQ jEaY}|VNP$'ty*7'8<PLe/SucohOKXc N&4Ýld`PsI$6@϶^d'bs=+t؁%]ms[xel V:MvvN}T~s樕uVXmKj,ZzCv"-NqBR˕M3Brgp᛻j]ќ=i_TNqD2N # B5OVЕ4øRmݜԠ/$ΛnʺaLK\2~qR' {1`LfVA8o?8iV(:QPKBtǬPKNI; settings.xmlY[s:~?"w6LBǐCC7a/Y+dR.)-M߷+_]F(oi;`{w*_q:4B h0.u#{}%J6iE&h` rvu#U=Y .o1qZ],STjꪚ]/ PNPU귪Uݐ*;.InԽ:k׹ D66'ckڍG*/QvsOZ+`c k֮ڃ%U9O<4]/JZ/|ݵzC)Š=grzCQ^sʄbJpC8J !,!KPia!j({G/kRˢB5pQ*cWv*tB>Bc0S}/dB$i3樊 -^+]=AJmY{W*P Q}00ƌRa"J .l1p = BDP8SB[ږ8dfsDEY ;U$3 :\iCn@Pt._EU Q@۾ؐnD\xGA Q X I6Pɧ8ןN (\*2,BCMtM-LçL&(M=V?1l#q '!vO17:F˰%|ttm&D*})ѤBAx! Bb+я iA8mmI-GN ?Qe_|Ƕ. n^gvCїm8嶒'L+q2&;:o_s& L }gwd0)~:mc_5 !|RTu*%Wql'J1٤hG` k4mDbݗw.ښF'e)!Ĕư4O}Iattєj0Y?AKtMDʶ_6t3q7#C :b7> 1k16U42vQeSBSJwj)|TMm&8O*:b21V|LG l'nL&Lg7`cv=r)֭b?d>0tk} ?'((kS^py_U̠łD;vcwwnX:g N^.)o i%q7PKePKNI;META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPKNI;^2 ''mimetypePKNI;MConfigurations2/statusbar/PKNI;'Configurations2/accelerator/current.xmlPKNI;Configurations2/floater/PKNI;Configurations2/popupmenu/PKNI;JConfigurations2/progressbar/PKNI;Configurations2/menubar/PKNI;Configurations2/toolbar/PKNI;Configurations2/images/Bitmaps/PKNI;iF^ -content.xmlPKNI;+ $ styles.xmlPKNI;#5 meta.xmlPKNI;BtǬThumbnails/thumbnail.pngPKNI;e settings.xmlPKNI;5b9>JMETA-INF/manifest.xmlPKwsqlkit-0.9.5/demo/sql/templates/movies-translate.odt0000644000175000017500000002623211714210425022122 0ustar sandrosandroPKLeI;^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKLeI;Configurations2/statusbar/PKLeI;'Configurations2/accelerator/current.xmlPKPKLeI;Configurations2/floater/PKLeI;Configurations2/popupmenu/PKLeI;Configurations2/progressbar/PKLeI;Configurations2/menubar/PKLeI;Configurations2/toolbar/PKLeI;Configurations2/images/Bitmaps/PKLeI; content.xmlZo6~_AhVMEbf֬[@K͎"5;,ے-v>8񻻏wG(2ahAy^C"|vp+☆d0K~(;/|,jqBXc^j$JXgu kk] vCO{v$qU`!uXtU^*X-KF?w\tyQOY0 hE8pi&EEa@1T0 3:%%S";kUuŬ%4εaE; \0h?kA&]}FBIt躾jtUk|/Y2 B( 7\UjO9IL}ʕ|VL5"dIM0Ɉ(*k\f <&6RJv8$~DB&n9Ub ;#d3 = .< &p*/@'ІqA7 )!,.Iݨ -@I72Z`I͎~MI,L5} jv*Jj% B$ElLV]j $9)h[gi[;rgL*sۇx}SE*Љsh]@]v`9˲9?Ĥ ,K%'eV ׽0b* 7pLMznMx<Ӈ3o~7xͅ臄m 8őT{FNL2SH F#c;B}FbƲt6Z©t;w2K⏝M3y9s}~-ӪuಸԮ|옢ؠjo[Ňl 4%ي0bZKCSOު<L 5S+*HT?CJYN\O.6gnv68UT;" '^ CnBhy _stsoj'UZb#x%Ld;[O}5СȝXO l:F6hSqrk_+oFf+DVQ2Eayn؅7గ_p$cO-4+&7^۰7G^oq=߼;6nT]+.q *6pPXi1yUYzM5# cw)ɫS!xb|(KiB^qe[6u/XkNnc:<2&OsB!% 5bϐZq(/^wQ q &5􄬗E){Ju;Y}QNPH0ڻ YR0rU_d/59t2&rȠ$.<SBg}[VmBA-`?PKJA PKLeI; styles.xmlZێ6}W*7ْw7]h(m^Z-&(w%Zlp I7<K?G',n7w߽5"qBR3*=\ȅ.J N$T.T%-줅KmltVtNF݃d5~eNَSwYA(AXeJtNW.6h>O7ze%2SQ\LNI49Ud,>u!Ubk"GQьx &N |ޫd|xwnNT:{޿k kb4|y'pgax=5I` G=>,n<>^4> M}.!Nي;[Ӛ4Hhɻ׆[Ͱg~3XyH!l9KGRrsWьށqlhAM<8P)k? BI/"s0njS:z/K-9KʔTH$L<1i/aee=n{Bפㅵ\cR,n;( B$K'W1;B IYurtˆUzcvK2D吠$h1"TH8.` FdeJ}bDJ#4oK?Z žDw/PH&i@8OAy)&ðu}JRBAՋ<d_i4+Hnb^JQ prS6>&ngE1+)xAy%; cmdp뺃O2xd$h4"f#UVE*cp bhuyY ,ph28g l r"724krXg&>\҉ڂ嘠9aErKّRYɴdw;2e剹@#K2RJdsvN~ P  g"eDJ Ӧϱ?)I4ͳAta,ɾֹ5\VbَRx߀jnn$ےgz# [8#^0zL; ŋoA)(E!̥Rjz 8-Uy"%ǔ}ݩp\˰VC'ű}cNKQ0y uuM{t"\+JmXFhVEG6ǝ* ~{F=5gxZۨnsC5P\WBq\(W u{+ =CJŚm*,F]j͹}WUePrɔ߹9yCڳqG"ZA2׼+|>y6z!kUX Yk!B[k?3 dq`аh=DhSߣ9/jɖ%xNf#VRIj>yub"Ap`+5p_ݣE3N:5w3!`]<ߠjIKkx#mM`'|:Q萵hs%6 7?O}P}J)?c<62j 'ѬV/x.斢;֧sPK+ $PKLeI;i8Tymeta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2009-10-09T14:42:2024PT17M55SPKLeI;Thumbnails/thumbnail.pngWWTqX20tб0P! b(Bӑ24A%4H $ZBȀ"I@ !T0v׺rZYgwMGkI"""[KN9$r8-"򓙭5F'7lg=o˝j 歸-tGv%';X٭狦Q&cj[R7 "WV<4)y@;]s4@jFsws?N~DuI6JCEH[vxSL{D0K0(*`F+EOW'+SyW+dpc0f-7-gl-ܚ^"z%4u0VDam Ue#uAbEFa^qPM-X6r-QŰ8&\H( @a>b1>0\*OPrYaaW?lj a]0ibE"+2urƬק|S1dD8ؓo<3o`_d+ۗKD#qD($bjEEq"}&l,ҌV?IE,_`6i#5qkamJz~ADl I}U&Z42@;o~ōQι+p$8BZ }\g7e]h/$ǻ)snaڦd BKRJx}: 9ߗG8a4_zaf&`,:is6ѳ90|jV)[Sexl?ϭCו\d,qiYc:#zRN%3CmϦR'*ٰ~qmլONA9s[r:9So\\*7{.]fc"M%>8W9gфGsDJƕQOf 96%t1;~hjsNk܄'@h9`s cܟ>枿RyD0@1蛼amnF$κ F8{€T]šDڴ3Rj] W_OlS|(0f׷s `|3({ΗXYhVEٞdyW!/ɝ8FuB ]66?k[ wbϧ;AIpHOu߾I]r&-[&bg}QKP<ݣ鑊iw$]e݊LPXHb**Tyic5*w1Rr: bNTPC95=oeC݅4xER#:h@Dsiؕ{`@I R{R -%V]Ђ 32~qoc@.QC&1tݖݛrpnda[J@Ɨ 6h* ׻6]%w{:?0FEAV0}hC KC&42)O s=*:{Ӥt=.fqظK)͵α=rJ3ÚpD۹XӝkbtVE+NWŧVdwOձvuJEEeefw$"yT%J(o)@L1yIe!IfrX{lM{<a:θk]BWrȭ`@1'7fbĽV:X"+@nHWu1ORoIkL< ӊ-FoOǸ@_Ka+Rn\bwDwCF2"et]Ppm,W,%;znW&*^V? @n]33xA0y*ͦ˂2蘒DiJYG>*)TLb,*Gt=`NakKχ8 YA`wRI7'6={?*K ̸@z!EcBP1ف5 φM1|#CcZ5%۵3U~YOk뢤FWl4=9ymPƖj+FkR7hn~釳%T\px\Jy\U2oȳw.m[6؛H&§ܓ\Xj獹eeR:@-lp_=VilJ FH3S͒&j 67[6> v|\\R5A+$9CǑe}O4Bhy1%:F׆JwWҒ?&l/W4౻=~z\ d~p"Y)xBi>e 6P6pg"2@YIs(REŪoSwe)Aeh ~ /sNDls$\PK/ vPKLeI; settings.xmlYSH~;"zH)[=W p۷!i d:53g,yB_=+Hjg Hnq4 b tEִDv/LE)X && ۚW7ɒGƛk4bq8?E9֮zibU~  aE5;55u Db㛓9ڍG*,޼}OZK`cLJM$:nK_jHe0QE뵿\&fc/rGs\ ! Μ DLx) )m QWbT"mgC!W.8ҒkTܫ]_!; Ѹ,+TEJ=va[ÝR?o&ZcKb<&I6GYFheƗUF!v%=(QWy/ Nٟd83tI6f vدeZ&/zra|ToKZ*|eՅ;&SlaM6Jlџ°[TE[?.[$RE!1,dI_[)]4 <-:bଟWcx"72fL~HSv|NčM$@1ZF.: U6IT ̮'?qau$a"~c&‚GteF=GzDx[{qjO1f+rl*)-3 M֗sي<5g:\W3he&1;/凱isݨ9K~D0?}7WVw}!nPK+V cPKLeI;META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPKLeI;^2 ''mimetypePKLeI;MConfigurations2/statusbar/PKLeI;'Configurations2/accelerator/current.xmlPKLeI;Configurations2/floater/PKLeI;Configurations2/popupmenu/PKLeI;JConfigurations2/progressbar/PKLeI;Configurations2/menubar/PKLeI;Configurations2/toolbar/PKLeI;Configurations2/images/Bitmaps/PKLeI;JA -content.xmlPKLeI;+ $ }styles.xmlPKLeI;i8Tymeta.xmlPKLeI;/ vThumbnails/thumbnail.pngPKLeI;+V c !settings.xmlPKLeI;5b9>J'META-INF/manifest.xmlPK(sqlkit-0.9.5/demo/sql/templates/movie-fk.odt0000644000175000017500000002400111714210425020332 0ustar sandrosandroPKE;^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKE;Configurations2/statusbar/PKE;'Configurations2/accelerator/current.xmlPKPKE;Configurations2/floater/PKE;Configurations2/popupmenu/PKE;Configurations2/progressbar/PKE;Configurations2/menubar/PKE;Configurations2/toolbar/PKE;Configurations2/images/Bitmaps/PKE; content.xmlYn6}WB7YsWc/E $"),E$m"ɺXm/4<3s oR.߭b, 4dG}7M8 b"*Q:ʛCD À%@sL.krM:p[Jvu؊/tle*QSy%7eJ8AXF\$4M{Q?hA8,pɂBd~A4L. ]YH<7 ޣ{{o伥'4?w㮹4"UqҹL.3 .PCwNwS%<  YM4 RObk!DзXD!F00э2S $0EY/`Ǻ (Mt1,J*3sig|-~a؛B"Ɨv9f>k*#Я ¹cZ@96d=rC ׁ:Uթ>,N+P-)^A}͹fSsʫB*˸D{_ἇԹg*dkjM˖78rBooPKkY(8PKE; styles.xmlZY6~To-oq( )&i Zm6(ί %hy`7}-rE29+w__-*Y]R'J9Uje븖JԪ$U+DEK/ Wf+bM7̡=UyOdfΆ9%i #/4ߊœH2QTDOxuϛ5W3!wfnYWՒ`_Z5Ll @&xC]6 mtKjzlIRY{^;$ j$Ma/fW9@<g݆&"d/$ ̷hFv l3-0Oe"$dOHn`9jO~cFRYihue7'(`eNh኶a-*07eGNS+ ^(1fLp!7),M6kv!u%͇\`^^V O|8xS)(RlQ=-u@mK3;םΔ|j}_ gD '9X6@ ֞`"tm6@ G >$g%nΖ/.1NY?xȍǢ zfO~*p 1&iAX4끶vD9UŎ=^L08Ђ=LNd>N a9+%y`s'æ!X{{#YwH) 狥iFh:tHǦސ~Mr˙ef.*sP%' ɏ z9CqUNIOR+%gbu3X>PPL-dv %W=!8` [S09~o`F>D#'vns:1fRs7eߥ?l{L 6p;9ӵiq2&y,|듵ǵ.!qQh? /tufƱ'ڵ|v]=S^Syv}L~vƚGPƶJ|UF}s4h dz΂mFe޿nn7!8%8jGce&X`M m<|,K<6;69BtOHb( ٍ='h}sn Q0'7[F'-JgG73 4x>:E+zc1[&> ^ ?tlad!9N6v]B0tX Ca< OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2009-12-23T09:45:1811PT18M13SPKE;Thumbnails/thumbnail.png7Q1)*mJgPJjVJ4X۱T$RBU$c_R5v"x|OvϹ>c?ptSm˰7_1d%ȍtHDP jܭ Z7 ͇O}:E: `|Q7gɕĢT1kn&=a3)cp@63,`⺶pk9"O.672WEG\7Y[Gbbxf#s5F{)7gąA;oȓ]i -miUv.Ϝ.k?QLj> B p#{߁4Ʉz*ͅĀiQM>Sq0 TxO'vPTOvܥqdC "}'*h\R}XlAON8ڳOysup65TKnUP?qjyDͱ"֡`fxv?z[^fZA97Dاy>w¹+duYb Gw7 lGМwMja< O3GB^[>,䦲rHn1bO;Epyѻ\f,yfx2Eˎ5)+,0p :I~TWY]yE& Zi#5h֒vcNkH:T&Yldf붝?}d6.rm}e&Gq_}r>sV#_}k*=u^$͗XsDя-kw-rFU ;V`5թGy+̕$hȂqG0@"/.-ݍb-D鹒[5{0@~Jw ]_Z2,Kyt--~%E-bαಖȼ6i̠4J=䌬!;[Xzݩs#սشTrƮ%a fkIEū"7&획cںb5fcC/{<&Dg)vQYkί֖ޜ>/% V 9LNmu%GmNf^&Ii̅m ѸA<% p3qJ2MXdsHl'8>Yovc_1%JM{)x֙>__6|%X/9 Y9wk- gSv<=+Z9&yUS+rtnl6uQ={=+%60b5Yt*cfs;9^,\{W0uC;C'2wCXj[PX)%tR$ X2x̙~Mْ.ŕqu!ilRn9F,m!a_,r;$.z"aVa\*"㍈=+"C;<x:h~F*"ֱ wMt' Jɥ0J= @xv4|RPdUz,t)n.$"q~CH?>*Щ9  [`R{C@U@P%M:g$Uأ -ZQzFg7{#:g*3+,-Qf"I4%A8sRa\iל};yFu 6$܏wpifx\ d.- H&wYk5zxMDF6X 疞+6:1mRj؎mMv%1J@zWV\H< 'QFH]L~Ue @h973qI|tZpruܽ;&{R_o$\~9#гoұ3w|]Ƒ%-,` OdΪn.aJ$jݤٖ$x=!f^4eޯۥz4'a%gz1x3&⹆t[&udB'|>IYƯC2%-яq \#뎊!V6$sB޸ 7LCnrs); +P:3pAa v=T™ ;^1"#L~1%FMvG˒A?rxGApQ XN7P[O.|e0(n QlS>jS SpYoEɕW=7?h&jRQZ x;Q¨|8/nxrKհ [Y8[~D0M@cu"ڤے-EN ?IIc_|Ǵ. n^gvIm8嶒'+q2'{"&t }%Hc2@}xtJPKE;^2 ''mimetypePKE;MConfigurations2/statusbar/PKE;'Configurations2/accelerator/current.xmlPKE;Configurations2/floater/PKE;Configurations2/popupmenu/PKE;JConfigurations2/progressbar/PKE;Configurations2/menubar/PKE;Configurations2/toolbar/PKE;Configurations2/images/Bitmaps/PKE;kY(8 -content.xmlPKE;]i& styles.xmlPKE;4%/meta.xmlPKE;x u @Thumbnails/thumbnail.pngPKE;gh esettings.xmlPKE;5b9>J|"META-INF/manifest.xmlPK#sqlkit-0.9.5/demo/sql/templates/movies.odt0000644000175000017500000002134411714210425020126 0ustar sandrosandroPK bI;^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK bI;Configurations2/statusbar/PK bI;'Configurations2/accelerator/current.xmlPKPK bI;Configurations2/floater/PK bI;Configurations2/popupmenu/PK bI;Configurations2/progressbar/PK bI;Configurations2/menubar/PK bI;Configurations2/toolbar/PK bI;Configurations2/images/Bitmaps/PK bI; content.xmlYێ6}WjЇ|$kw`A٢hADL)R%iwHJ- yp333Cj{BΦw=ݺg?("K{I.34obY-&2 9ыa. U<dƥycؖP ؋PTv*Ď}TG}Y9#gu`ؘ %\RZ93X9!lVߍ8ҠQ#5DH=A٤;N?3Wͼ*oA`P.o-S"9?%(v`Lf!;gS,oBi+qve&G+W$b.EC\7|{`x;8}szJDe$(W~?6YᜋPO -HNI7N'8AH,Y,8eI]8JY2KN~w\Qk (Y ˫yJ|VWV3gzӰ1:Iݠ(jlXjE_u(tS@y,+gR̭Z|]IE-- @ #$ҿ@rYm9 P6>W7yqU{G*d  P!L"% }{nqJFJu-6:WۂD&}aPz@2wf>g=EgRU[xbm?3ߤ%wX:FPJQ0 hkLavr7J) >gYΞky^tu~C7ioΚ쾻ɨ?LR+niN5xV< aX)> (= 0 S7FDWS̡kt:%6e=5lլEW4k,p4Apŧ`rU]Wy0|QU8rgB"pEDH5NHQ._KIˤ9RwSYr /H))?S;?PKLu/gaPK bI; styles.xmlZێ6}W*7ْw7]h(m^Z-&(w%Zlp I7<K?G',n7w߽5"qBR3*=\ȅ.J N$T.T%-줅KmltVtNF݃d5~eNَSwYA(AXeJtNW.6h>O7ze%2SQ\LNI49Ud,>u!Ubk"GQьx &N |ޫd|xwnNT:{޿k kb4|y'pgax=5I` G=>,n<>^4> M}.!Nي;[Ӛ4Hhɻ׆[Ͱg~3XyH!l9KGRrsWьށqlhAM<8P)k? BI/"s0njS:z/K-9KʔTH$L<1i/aee=n{Bפㅵ\cR,n;( B$K'W1;B IYurtˆUzcvK2D吠$h1"TH8.` FdeJ}bDJ#4oK?Z žDw/PH&i@8OAy)&ðu}JRBAՋ<d_i4+Hnb^JQ prS6>&ngE1+)xAy%; cmdp뺃O2xd$h4"f#UVE*cp bhuyY ,ph28g l r"724krXg&>\҉ڂ嘠9aErKّRYɴdw;2e剹@#K2RJdsvN~ P  g"eDJ Ӧϱ?)I4ͳAta,ɾֹ5\VbَRx߀jnn$ےgz# [8#^0zL; ŋoA)(E!̥Rjz 8-Uy"%ǔ}ݩp\˰VC'ű}cNKQ0y uuM{t"\+JmXFhVEG6ǝ* ~{F=5gxZۨnsC5P\WBq\(W u{+ =CJŚm*,F]j͹}WUePrɔ߹9yCڳqG"ZA2׼+|>y6z!kUX Yk!B[k?3 dq`аh=DhSߣ9/jɖ%xNf#VRIj>yub"Ap`+5p_ݣE3N:5w3!`]<ߠjIKkx#mM`'|:Q萵hs%6 7?O}P}J)?c<62j 'ѬV/x.斢;֧sPK+ $PK bI;eBmeta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2009-10-09T14:16:2320PT12M38SPK bI;Thumbnails/thumbnail.png[TvfhyM-:So^D4sVb % eZBxAF`(:=챧|~߯4qvp@ ؃?8]ȆeE zl#FPafj1#|1Y c-D~{Gnbnpy@1d0 )fzDӑ+~)-](}N;vA7.pwWIQzYmXJ@?81g^ш %(ʂQu7\SwR[Qד9YmYvKȎ/S8Y86V=5|eN0M&~&d^SC2%I }!ʡhd;G[:4$OlNJY }*aym!SWa[6]p *ȵ@^z_l{a!ɽ[1kD qX.ֺgilQ "#M5l@]xqhrW8k >jFJ"eMh:샽  |aͫه6ҀX=|bfO~^^^u~|>imI'O/e'+GFFVAJ%w;BIՀȩy9mkv -jz`nVva(\W|d.`Qw/?;ׇTcyLN7}j-UESMg,ɽc=Y/3(Rl%F" 6ZP:0W8D-ՃaaVbBIc V27s5T7xS*2\82 TX?c -E r7/ Jٝb(3lq)6` EoTZ%/qa{Xot:1{hkSlB< Bsib]aXWڐpK (ͭ."_EE DM9OCLE`StYIWP;F>|)󶮫lS>bU p~EP=7?h *LRSZ6 xPQ¨C7/nD G>Ӱ [Yk~DM@tu#ڤ-hE^0IQa^|˶> ^)Ȧ@}r]C&nx;Ԝn*#*PLGjG)x귶&6ϊX8rf&FTEkX; >iĤ nu뎴Uz84>'{ !<5yV,Hr+TCGĹ@Gs Xlo@S eݟIߏӡj7ɁDhV(TB/ʦ :ٗղG!ΜDLF ZزLLh(`/~8B0zc0S-[?哣~Ų}[`j~B0[RQL[b rޡ|?vm3X`BrNy/PHC_OWgPK@bPK bI;META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPK bI;^2 ''mimetypePK bI;MConfigurations2/statusbar/PK bI;'Configurations2/accelerator/current.xmlPK bI;Configurations2/floater/PK bI;Configurations2/popupmenu/PK bI;JConfigurations2/progressbar/PK bI;Configurations2/menubar/PK bI;Configurations2/toolbar/PK bI;Configurations2/images/Bitmaps/PK bI;Lu/ga -content.xmlPK bI;+ $ styles.xmlPK bI;eBmeta.xmlPK bI;22 ?Thumbnails/thumbnail.pngPK bI;@b Gsettings.xmlPK bI;5b9>J_META-INF/manifest.xmlPKsqlkit-0.9.5/demo/sql/templates/director-movies.odt0000644000175000017500000002501011714210425021731 0ustar sandrosandroPKH;^2 ''mimetypeapplication/vnd.oasis.opendocument.textPKH;Configurations2/statusbar/PKH;'Configurations2/accelerator/current.xmlPKPKH;Configurations2/floater/PKH;Configurations2/popupmenu/PKH;Configurations2/progressbar/PKH;Configurations2/menubar/PKH;Configurations2/toolbar/PKH;Configurations2/images/Bitmaps/PKH; content.xmlZ]o6}߯b+$Y$^"mPl@Rm(-E$,KeKeXD{yEѹzHG m$! Oo +(ᄔےބО12#> | M!Yy 聎e,\,qcw .z M #kp;b`YaE6u^pT@ FО K///==s\:cXЃ`][a(@S~ [Df6&+aR#M8qmhp9ݨyzQ7bZ Nꏻu-i,-I26M.SJs,PM7tz.;sdxdh{Uy+!xC̣کNa`  keb`r)dH`r )M Lf2BgѪ r/7IJ;1ѕYN2׊о_3# ܺږ\@+lrhRնJ+g;F$E"K!F nj>aku[˫B*R{_{nsSֆv-ax >4#Ώ ]H~i_r}`&'uYΫ2eC^y+'K2dzy"վB<F1Z{|nYB*|qVf؝[["ON^g0k@DH#Dfq;*I8,RB lg;wfkR&)іɣK vʀ?fzjpv<ߜg^tˠ۞飭 G-= 0&S-Yд 6qہx4?_G^cj~@Stub/7zԹ=Tj/3.PtR>&wNn2ں GA?H6F1ő]ɑl}Xլ+0h_ o^1\4 Cy7*B__{O<=O j&eE^V׮1UĨ>H-5:%oOϛ|x~{bړl`Le~h4~A>4Z!btL`D?>@,v6\LƧDqىQ'yƶQ]r^ku%^UFΎ@t2T-`1.,u陪DWB? 3{􂎿by/#o`O-xniZO Sr-jIhi/mj~mS[55mS7>7GuA |{h{= 0G쩚9J:Ilְ3#ՐVMJpFPKv>t)&PKH; styles.xmlZY6~To-&]=P4@Rɦ} hH@R>;Ceɫ=898 ^,R!/a<-b?݄o{W+yʓ*:dT \ȹ!.JsN$TU2%-̊V6T\3ҊPa=%;kf_:d7Ty,Z(yIkYXen*nF\l6kjmpR4WiFq39GT!oRQK*(rU]FvdC`lh^{9Q܌Qm y\V>9絩(`T;Lǽ;˾LQ'g%y4-4 l V޺PV$QJL޾2ت}?U2x &Ǜ$%?j)GhM *:)KTX=My>7/9)vquew|ݷ]m[|!G eIoȃT4ϦqH4[ׂJ9)`b I.h' ZD3nC#Yz]}Iӛ[4#9e6T kN αcjAAIcHJqR + qKA 4f Q:iXa&"{gTKiMP[ӴuG3$j_gA -`6EZBA 9O0[eU$2 w@ G>(en/&1Y?x :zOg~.p 1&hNX46=a*+i[&9frp=}H~#%g?X)ɞOVAE.{o'[O5k(dpE8#v w>O[f3Fi}gLx-(h9]j|g{mT=K1f`8}i{2eIqh@# 7d޻:;moSNn!<{}hď9okz,">hF8ƽ `w70+-y#QxD#ݲ zk-$IwƮ5}n;;G8Wyd*"݊3!/\O'"}kzv]]]j׋ Ӆusv.Ԯx ;&\Q =Xu%RP"6W+exl)`K NPF%L|1җ1V>O4BZ}nzHcA6Uf;VIㅌ"=Dk|$·.ɒ܄hDh +8iM4c)~NF3sGرf: OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2009-10-08T21:51:058PT8M45SPKH;Thumbnails/thumbnail.pngWSuW `..HՅ{3T) )D*\ j%t*P0!}3wOws>>!s3#{`m;c}A{8O17[FT:l%js"5jO'0|_p=HYФ b*]RߠvվNM}So]]WqȗX ٹw-pvBOc-G&JZ=4&O6$@;mXB˂%Rlۋȸv6] fkmQ_KZ3s7FN$4I: {оБGm&t+T7(p4\7g 'XDCI"z?lBsCmc]Bi nGC)ei,*Gr^GP&pOEՔ4!>(Y4+޾;>7PE80ׯN>1݊){h'6ub.#.\#Qanfiq(rz{I׸?+_Of JHe Q9==R1FP]Xj 41ml/|[tp`*5?;.)1JKəYp;Djn|:d>Pg K[ J?06ؒ\~w̏^o^?ro+f +4>Uwϒdt=%E=>. L_8K{iH^/>pbP pc~|Ia=C팿^WphMm3u+ RwMWKђCճ[*aVfN'ZU]Uxz#9 YbΉU QzjyJ21sS9m,g ӅG~~oֺ>ZK[lѿq#v؝.9oTL4| :W+k9L:&`6}S/3Ak@I_AIfc0tL &BꗩSŴuG$/RJXݍɩvpQ%VM?OeYe_Z T0"#%5AA;[Icտ` olx% hDȽ{׿F~N]06q;cˇ \;`?-v?0,Q%=|#JWͦ$~R 60+ j͸3Oe^Rh\3$憶fą1G7113-aEIgY++(%6p\{~ʹBOV'2@򦣯|χ߭U#ZCE P0Fr. rz7$x!^Z8Q.m)W3U-k_?/ƋZkt5񂳿V3Cv]@:آ@Bv/M z/4"] ?Ӳ߄)В kMjd-gۄH\PGp YXUY^un3zuݢnȰ7Ru~r$& K͋HZ/Rzh?h;ߘgXW{X9>EWl>X_ M ᾲT <_s> #Z;VuNA<_"B'YY*,v2dTJF2z幼!'wO{c-@ns8rRXQFgāt.@r;?zK1@p</PUġ[?[ɱSfNHj*Uˉ%%R0y%2YWXfub\IМԀ2qK:~]e Yo||rjɣ+w'>I~4# pH wIjD@d@~5ʃ^KfY8JGyLU!oz՘4%0m{ZFxs|`XOX% 0Ot b-okYOscQPKm4s PKH; settings.xmlY[s:~?"wB2 CJO ͜ {Z$]&rIHlio]"G UƫxG  #1Fʥ5N&Q4+ %ꈶ ^x d*R bP 40xaeO</7LQcjj߮(&t_UMِcONNQnԼ*+׹iMlƴT6^#E۶=?i/0Vo27^人)a]mb+rP϶ɮ_Nl_꧗g8@H9SPk ƈSr'Z 0,D"ڞavՆ\pT%4\/~vrqV"vE~YT|Bj~vQ/go xẄ_$ eXhŞc5#AR}]z+3Q L}03F2a@L eF.!j]1 ?p.5.OI6SV"PD/*~wS:T܀;J@pJ*Zm t^!9]}vIqoGug Hg:]CxB%oq7P0,;DMMtu-Li+L.&[t`onh:jRQZ x[EL( A6A-=Jx_Vn^(o]dXo$a49A&]t܂.y/[wL~fw=3^n*qm~b'sUD?N]ϣjL?6_5!|TvҒ8S)LR`4CLe5D^#6Kt*Vyo ̷2=& nxX(%RE!,dIOPX)]4 z<=21p+Wc1H l3A<}=Ԡ>ho.-qЬ`\󵖦N*$zfWSfTKs[4;?F똉pX]Th4e#={`"e%5ӧ CL9jܔO)Yl[klEB[b -L%bޡ6ލci ^ 0:{ H!/'}su[huWoPKKY_}gPKH;META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPKH;^2 ''mimetypePKH;MConfigurations2/statusbar/PKH;'Configurations2/accelerator/current.xmlPKH;Configurations2/floater/PKH;Configurations2/popupmenu/PKH;JConfigurations2/progressbar/PKH;Configurations2/menubar/PKH;Configurations2/toolbar/PKH;Configurations2/images/Bitmaps/PKH;v>t)& -content.xmlPKH;J5$ 5styles.xmlPKH;%I5meta.xmlPKH;m4s Thumbnails/thumbnail.pngPKH;KY_}g lsettings.xmlPKH;5b9>J$META-INF/manifest.xmlPK&sqlkit-0.9.5/demo/sql/templates/genres-fk.odt0000644000175000017500000003571611714210425020515 0ustar sandrosandroPK<^2 ''mimetypeapplication/vnd.oasis.opendocument.textPK<Configurations2/statusbar/PK<'Configurations2/accelerator/current.xmlPKPK<Configurations2/floater/PK<Configurations2/popupmenu/PK<Configurations2/progressbar/PK<Configurations2/menubar/PK<Configurations2/toolbar/PK<Configurations2/images/Bitmaps/PK< content.xmlYm6_q2d% G2yu2=i?1X,9xJ 3>zv쮴Zq7o)CK"|uDL|>{oG?܈ٌFdhHp ѡ| jqJ@G^h 聝Iް\d*lMOlUXU[eVgZ1& i5afz WU Ў-$8 #f2vnX`Sq[~[֡eU-Wr(ڰzz齊)ICN- ڏ۵ Ӷsl-TYk7/(A-^2t(|%&#̢2"=4uC@diiM TB/t%Xō|%$[0S4(ał)sAԤ3qF30٠be&WT7*ʰK K ʱ?c15q۩#n ;& !:*lS 7U'8M̨`|, .)s!%8S/F{InjE:VBM7穎kKx-朼;Z6j$Sش[s9^ho~RS͏F43i OU jOGvFכ FaeP8FcY$ag_TH 9Q6t?/3hVo. ҊNqUrbn HBMk| A۟\rhAy#j5vzEuGJDX#Q3\ia[!,!TBf$fE +`v<+c4&,4#XSfp/ =G{ȳGNy=x_ܨM& ۠%M{ڋgܱ+ 9c[Mq f>l@DXn3[}Ӟպ$paA ^_>BSnͤ%)P=-L'b0\EJ.Z:ៃPK*m]PK< styles.xmlZݏ6_!оɶIo˦h{84@S.۾Dl)Q 3(Qd˻`g~AjpxDb"" h廇Dy'[u*2>rRkASdTu͝^Q6U0ҚTaȒ /JRMF^/SG[%"+f=+={|^Uլ ǫjnIW4SNq35gfT!oR^f*'hrU݈k=aK.e3#1-d6u/*|L !SQ&1wXwuLS'g“"rs4 ╭8|jV@}ڒF)MzbYoCIm?vjy Sz@w_]OetٲM/o' >\E.F3kͩdLT73-mbJ=۴GIF"#x|쒵d4.# Э?%S0%TStTflekn g{J46$), o;*$ZI*+C%`#B4R$ƣ N8.2?)+ZOXtnp}d#-K8w%!{DJ-p @K{+69kGa yΏY  W YhQ(ɸ ;}rRQBA5' Y{# kb_xYhI+nQZ>p*H2GENOSP64u]'uS,h=c9]"8IqD"UyK2 pte9D)W&l6n ٦30Ene֡[H47x:Éǘay(վd0w>2q^ 7B"XP% Qҍ#)¿(-"-vT΅YvicfS"pq Ӧϩ_(IU sB4lJ3]}3rp:^^I+%2K,gߊ@o(ٻxYi +Yv%5uLxy V09~`F>0z;&E)p9Nߤr3O>н2ܻؤ8oiy!4٧lA딱ay眷7n]=29aQ'hvge=Jq$hyXE߈t`䯇gϝC|;W5[{lnJ@ >[?K6vZh/eq<2bقOsGʋuNLA|]NA/4Llxhv}w|]fa(^k~m>,fwq=o>Ѯ++럯ԮWj/a]om.4UB-ەҼ !DxVՋNPEPLoݗcȣ>W x<3 ԣGZ GBvZS+iV4' D~\ɒ\h#D3Y7CX\V Ǻj#֛ ԱFGy-NemF (#4xo? NbM\E|j0/1|8h[ u eMXp7Pw=Ip1O-*jw hvQO -D=χPKaʫ(PK<^meta.xml OpenOffice.org/2.4$Linux OpenOffice.org_project/680m17$Build-9310Sandro Dentella2010-07-26T19:39:3823PT1H16M55SPK<Thumbnails/thumbnail.pngUTASS:ci$FZR@vaIIYzܧ;|s7_4?(aSb#!!)0U7L"+HHt2}sO0?. eg"&GRbbLti OlLqšpuFfA0ObY#ks+H+ q;q|h$qfmȨ[/)]X͂ l =3a5Yvva1Fqo*cp)&*/XCDB|z:'j{I~ePf&+*>i` L^pHj&璏=s@|BМ Ka=9c,Ze \vR!l&P}=@ "2r1st`0S:f!(JQK#\ ;7ET%b19S>ҰzhCO቟UE=!GB'" mlW+c9h15B[1l;9D!c_cDZ2\(n A0yUЙdNvRToۺ=7:ӐPuh<5TX5q "P+ jInVѹ7nr~NSp |O3⾏32cq=1x^t̝ΓC;Ȇ3WE,%?/mN7"AAGI Ebgo#_6(I6ks:zp@ N>4Zr5} lǏyB56鐡r/e9fΨu}s{ΤVmuQ4$2"&ozażLA.W>%JƯ-X+7f>65V8{:l~.ǿ4J N T8W4@*b14DnZz<̌g9Mr1|R&܃rJTxӱhJok\jM'"$w` NKtW9ȡVVq:EWWJ-P`l4~>J[?&=6h^56gw:ǦJ4|M*gG_AC#)Or$e&mR7GkXI,}RXK,Rlq{lK2*c 8ޞisGfL-[.In<  G; 'uHaZwE_E#8r|DLI^m|Ӏru#LU?6#Q("~F/fdg͎݅tOp$ :1=w0.Ģ$kS%/OJ4\˟m)|~oc edmW}jUf<іMñPӜiVc@m A>?ťEx!ւi6$ bM-A-X;T'/AtWFMb:EtXcf)I_67P"FJ2|V*@$!XLIAKOQ· /pcKH_HW?)~QC.fNu:Na1\iMfQ3v'>I8\"=^x&LUj -KLcV!Dv]xv%fJ+Д8pL9gMNG!( $ 0&lJF/T-&/~%q$7g2D|dÓ0'(d%zye Ґ[.WuT @|^Be%9<"+:/Ƞ {[gѯآ#V/u0%xH]ag# 7h7qNцRj4rˇglW*)NXIԃtStv=S[e/⴦}/jva 'Tz:􂎕r.!ڲ'g.Q\1>62gt6+(7FB_Jl:Q&B2jq8).OD[b>Ȁx"t.QYn_zظN߂duc)ti_d0+6 KEsx\XZ1 zڴzB0r3)}YNWG ==$k_D,@}7$+~ʩ& DM ] (dVn6R~p7m1"jM zgpHǟcZP K4?%(Y5qv@v%x.TcL:}H]B@7u2N%f !N ')&"{j^b"nm3t_L=}a#K(8YjbɊV<(1AgicOSԾ+>#JkKg{n1?)?$ey 4Mjm^<5&" &wI?Ԋ}l*{LZg&NT:g?gRvʄjoo♄?Dbx XE6οr/we7GYeQn՚ pz6-/gLi@Bm]-`zzeA4Ԅm}'1B-pI;]sL^Sֻx Avqf,k a.;wNkOpѩ&UJ GYFɄ.F3[S@'xf]/7O ]y<"AM&x@U}t6a~eH u?Af1`\t ]!' Fpqiǻ+2Mi|Pv2S7^5GM[7ԤwiW8_ǹ| TܵU̙w,$< xMT$2x*V!''?|hǗ@,Y~hw -1}V2BkxDAz>qLw* o%+$_$Ws1#"Je%%B8tI>SE (4,,w`.0M8kf ZjNQ}Lȸ5U'E>j 6.}%u2. W!X+KpuGdaFy.Hq&OtZ;!e7+"k/T;lCCtNRٲ^o fvtt,P|"X ׁ/P@VфRJ !O׀G&k,TM@+B#grVR:^/#bTmM"ݝH Q;=WVAKoeTxM}4f$^Ohp1MNi7FRNBU}kQZ™ F i a=;҆Q !߾~A)S Wb`HkT 7%]ޗb˪k֒ZB I*ɷske7^ɐvנ=2&c{9 L$լs8|dkNʲH94IPgЩL+HO`8Y^T}z4Ȃ  [O̾G*JӮcӴ`*=)*talCΒ[Ç(u'{7qn0qQK3oh%a+&1+7gh;4.01!;ObT,aq{iCY?sP3Qʄ>T D6/g=Ut.&2 yy.7y094^g_.Tl$ӮHӳkO!p̣z3c37U@U^kB K+b^h%tnwmUd+k{U٧N[zo:qUvvDhjNoT8_Z?^Y@ݛThaBV[#tq L_{1< i"H&ׯ|4=|5R,IIyMy,B/$&iD["|އ+KeK̹CLVZf(I hk55 ;Ԧװ\ )tyh{B5:].x!髅gtƧf|cXChtm=ٛ m*'~2n{;u#2q.B~xw54 [Ungh9bpj\޴uo+B%0=6~/5$VCF4w;{ld@6'@~VL7x[C&ҼBhD.d]WR_vUVrp=ig[_$t.4 iS@M5|V ýx55V֖_3h٪U&ɿU>ʱ$OO{]*8&>W>w/᫃fͰɦL/ aVG.xְY}!2"yYt`-m_r_/g_U>W6Qh4Dɵ8àޅ ,qeDCOl(qU :ܮג.ݭ EL0uw#/v3_V̱Xx!BCvOU"t-$:Cw7Ԯ9S0˥2nBbUu0䱢fֿ}>7cVieh@!|K>9mQ#똚.c0΋qt79|?zF:} RFЉIɜRi'jKǷM'vn]GDs=\4lLMZc1 y{[j -sKy,಼EXx~3/,XQΙ\ܬp=d겈0fZT`ի` Lzv~Bn{<j9e vk~LQS{8IՎ/~D򤸦 _6!KۡL?=IY:1gg֛h9P>Z # ~&ky=*1f_cU$Y5Y/7q<Tf0oZW =.jT}L<TgUpIt^ɾ7!dQxsE# Ę/97vmsĞ@@}Gdme@oouŻϗgCd+n&uxoB^\6.$TUQ-A+U_%i#]GY'ӕCs^1.,>iFcE!G(j6y* rmT_BBZ{`V',5܀K}||fCoB^/mN{\OZoݟ+G7o}xh ҝiyW}YPH~ϕi4ϣc51瑶̣jZVF]]wg]zLO+IS!pl@rxv߼ळg;# e?e PK԰`PK< settings.xmlY[s:~?"wBm!H3o^'#}Wɤ\Rb[O$7~߮|u*@q/3X8^5-8+ %ꌶ J_8-d*P-BP-0z(Ky oQZ]V9EvuuUMnz(TU귪Uِ(_\4YfԜ6[יhMlβƴT^XF9=?i+M1ro&7N⺺/tCb+ZCb`Yj >tL,@(!r`iWOI_t$ܣ,‡5:a 9-}T%6\ Me_s9#Rk\^^敪+ՒH.E,foX 1d[ #<ثrj< ~O҃|<: XNA,&b'?bMe$b#kDA]ş2xk !g qu.!RV PHoF@ @*Mn@P辰."`IE a$A֫thH7!8 OO' 8mNF,iL@ǻTR'T s J0 k)n$QlS>fW S z Z73rg*0]$.Cn%:$t)FǨ\w8ϊn˸m%+Dq^ od%z9`A&]t܂ydpo[Y{ŰYp?KrT̗Js1~ݯʠ]01c[e`!(y'=5O˃Um+m0uk{$HPccS^y]U,üX+;ˋisQ;s, ޳F+t/֔4De~}ur\Z=PKYVhPK<META-INF/manifest.xmlKj0@=VU1q-&fW6X; F#h[S0Oͣ)k7vc^aaӠHѵHS"Z^%ۯɴ|.Ax.25| h;7GWsh,.dLB%Mync Y'@,`(Uq:bbqW`<0RO G?Fr7=^ ޛbpmaD-*긓_PrS4I7ZOHNzbK|0Hc-2xd7!ɧa87|"sϩ]PK5b9>JPK<^2 ''mimetypePK<MConfigurations2/statusbar/PK<'Configurations2/accelerator/current.xmlPK<Configurations2/floater/PK<Configurations2/popupmenu/PK<JConfigurations2/progressbar/PK<Configurations2/menubar/PK<Configurations2/toolbar/PK<Configurations2/images/Bitmaps/PK<*m] -content.xmlPKJI6META-INF/manifest.xmlPK7sqlkit-0.9.5/demo/sql/ex_41b_completion_autostart.py0000644000175000017500000000043611714210425022110 0ustar sandrosandro"""completion/autostart m2m Completion may start automaticaly after n digits entered Try writing 2 letter (eg.: 'fa') """ lay = """ title year director_id m2m=genres - """ t = SqlMask(model.Movie, dbproxy=db, layout=lay) t.related.genres.completions.name.autostart = 2 sqlkit-0.9.5/demo/sql/ex_40d_completion_m2m_fkey.py0000644000175000017500000000116311714210425021572 0ustar sandrosandro"""completion/m2m fkey Let's see an m2m relation with one field that is a ForeignKey (nation_cod), and a filter added to completion for 'nation_cod'. The filter acts on the select of the m2m record, so that 'nation_cod'=IT becomes a filter for the whole record selected. A .filter() on an m2m completion, acts on *all* completions as it just determines the constraints on the whole record. """ lay = """ title year director_id - - - m2m=actors:nation_cod,last_name,first_name - - - """ t = SqlMask(model.Movie, dbproxy=db , layout=lay) t.related.actors.completions.nation_cod.filter(nation_cod='IT') sqlkit-0.9.5/demo/sql/ex_15b_base_all_types.py0000644000175000017500000000202111714210425020610 0ustar sandrosandro"""base/table Views Views in this context have nothing to do with SQL database views! SqlTable has a 'main' view in which default TreeView is placed. Further views can be added to represent different columns or the same columns in different layouts. There is some magic in here in the sense that layout documentation is somehow missing. Each SqlTable has a default layout that has a TreeView named TV=tree, so that you can find it in t.widgets['TV=tree'] (Note that the extra 'S' you see in the layout is the ScrolledView that is added to scroll the TreeView). """ lay = """ {N { %text TVS=tree } { %numbers TVS=numbers } { %dates TVS=dates } } """ t = SqlTable(model.AllTypes, rows=5, dbproxy=db, layout=lay, field_list='varchar10,varchar200,text') t.create_view(name='numbers', treeview=t.widgets['TV=numbers'], field_list='integer,varchar10,float,numeric') t.create_view(name='dates', treeview=t.widgets['TV=dates'], field_list='date,datetime,interval') t.reload() sqlkit-0.9.5/setup.cfg0000644000175000017500000000033511714210425014211 0ustar sandrosandro[extract_messages] output_file = sqlkit/locale/sqlkit.pot [update_catalog] input_file = sqlkit/locale/sqlkit.pot domain = sqlkit output_dir = sqlkit/locale/ [compile_catalog] domain = sqlkit directory = sqlkit/locale/ sqlkit-0.9.5/doc/0000755000175000017500000000000011714210425013134 5ustar sandrosandrosqlkit-0.9.5/doc/static/0000755000175000017500000000000011714210425014423 5ustar sandrosandrosqlkit-0.9.5/doc/static/sqlkit.png0000644000175000017500000002350411714210425016444 0ustar sandrosandroPNG  IHDRjnsRGBbKGDC pHYs  tIME%zV IDATxy\_3`aaYbEd $ 43q +˛\~-ZfHfzl1MRjJ.!(!8̀ 1A <g>s>yfB( E@PP(*8 BBPP(h{Z-r9 1at:X,VT*͞ ЊP `hjj2y{{cΜ9eeex7qGGG|gpuu}`ϑ-[@*|||dӇV4Bt:R).]|h4rP(D~0 ;7fGzs=FضmN> Vۭ477ȑ#8r2220`k2e \.zٳ$M(bpwwDkܸq|Q[[{2h 22&Msu̙3$-""Tpt Bz YYYPշ=|><Q*˷(*8wDVc…L(ap=Æ C@@lll6;! %%%8 8;;4OHp!,MJJ ^u1< III7o$ ;G"??׮]KHMMEjj*-ʃ۷dgyk׮E@@]ݜf_~2e ^x~;XF EEE&遁xZln !!hoo- *8׮]իWҟ}Yܳ q8p8Z3UpZ-jkkP(Lҹ\. ;;^p:MMMJhooF0rxpvv#F&h4hnn6Kwttbjq;wgΜkL'''#<<F\\||| #kjjpar릧Zq2 NBeeٔwwwZU"//d)P(ѣhv|}}=>l\{%̎ijj޽{q;#G%Hhmm%'L/BzR|$::ٙ?6MCFIoƉ'a䯦Ky8v4Whh(9vX?}4a&绸HtJ֭[Gm۶ZmoxzzI-㱷>~kZ9r$ijj"G.p8-p ^1TYYE_E[[7 E]]^J̜9ۭ NիWcӦM&+ 88˗/Gjj*lll:Aכíkt|gz3lll  fM!GAjj6Rür ,XCYl&h4(JhZB dd2=VbضmeeeD\E}cmm ___䷽{"11ӦMKy~Ç CTTbbb Y\\\|hmmŶmحŨ^˗/cŊطo0 T,[ BuMMM8x1 w^xx_֦(BСC-> /^$Zڄ:ddd[[[zwHqq1fT*rypBd]pt:#F2?!'Ob;wwww넇wgϞ5+Èri̠>'xϟ7ۀI,cժUFBBOO.4ۜo6^~e#*DGGXpq H^^EEE&0m4̟?_YJ6͹yWpq:ulxUTEEEضmx< p`ccsOBi&;w.^{.M"xP__5kl(tr֮]SfCVVL&C]]N8a && c=nf5k]XGzz:y~hii7|>Ȭg֯_?HOOk(@\\>^'N8hZ۷o@ СC$DEE,}Μ9Q&LfUFgafb~'O+80|pxyy!++ ;wDii]ϙJ8p>`=:lN;wl6ڭ°A\\Q\\ܫ+ ~G , ?8{=1 Wѭ G $$׿o>ddd ::wyARŋO1i$\pK;655A,[ nHXXXWojkk1k,ܹDll6~z9)SN`̝;eeeFrQQ !ˡVo+$J;Ґ1cֱ\YYiA_!esApY,ZǏ7Ur8bÆ t`ʣ)8&BHHBBB0sL466׮]õkP^^HJwjUTT`ժU?iɦ& (()`kk۫Gq8q">#VMyV\\\!CߺJ\zΝÁ,ctl7q3dյי#r1_nӧcܹTl(;888 22'N{gݻwW_zݻmM[{ vvvwܫέb 9s& Kc GGGDFF"33˗/mnn?ujz'sOpelۛBp lڴ 5S@tYQvhp8fNeZ}OPTnk(ɐ+W-P܂;z)k׮u:$V*dYBu{X[[#-- ˖-CPPj|Xj ;1`C:Lb[Jmmm=d xtD7́d2rss-::zy5O?޸_[KD"߿z!/&uL\x1JKK5ڇJCzڵ ȀH$2ҳ;~~k6*dee{{{g5K?{,6lpWyؾ};;U"08p ۇ!C1jYYY={6 &i())镣^dR b?~<֭[\TWW!7nŋ-ΒABB5uTe2 |d]e}|hc!X,/@JJ%~ ŋXdfR:tO~r7@wNpXƢxk1.!(++C~~>9b/3g΄yaɒ%&Ȫ*\UUUxhgSRR]vaƼWTu&:Xb.]ɓ'[oaŊx\|>111سgIرc>;;;3~~~n] N% / B...prrP(0P(hllDuu5 H J-v0yd3K+Ip9dee\| :A!** nnn ?PRRbwTٳ硌fիW`0ǏǢE|r <  .rN< .8ּ 4WϧoBWbh4IX,mcuքClll0w<ё .ǺҥKg!VVVmccC@ kkkcG}DadV Z Z Fsx<DFF222|W_^60]iii5 d2R)R)rjs,Zsν=,_}^z%zΝӑ{_V֘1c/^໊ATl6FB||<.\SN娬<B||<пm  6`ؽ{7;a1p@̜9iiin[ҷo_,]\.}ɐիW1o<[--ΝH>|Ũ\.F1x<ڰH7j`0bظX,D"Ass3 kkk {{{xyy! @HH{j.\q \rhmm˅P(4]bb"`|N<ɓ'C,\K캺:^MOBW~FPz lZ BCP(Tp(  BCPP(*8 cF#T6.Ǹ Bo7f͚6bgQ(qORQ(JZ BCP(Tp(  BCPP(*8 BBPP(*8 BC zPޞB$᫯Byy1 ӦMChh}laa!֭[Z WWW̚5ݫC8=B( lܸSNvhAYYV\ H''',Xqqq>?* w/ Œ%Kp 8t@p2kkk!C jpq1TTT ^&x{/bhii3TIII(//GMM }N߾}1{lT*\|+Vx !hmmT*`=A^ƐcӦMhnnkfC[[R)P:R2 vvvpqq13.QZZX8::ikk3>L&C{{;`pppd2!GKK d2dVvJ=T*z=\]]aejS*8u._-[ ,, fi4@P@ @ ]WTP(pvv\.GKK \.BaE:hnn\.04+r1|p$$$`ǎ=o.@`Lr9p`kkk.K^ NQRRPbގ'O_~T*^;Nw'gggyůzz^7'J3g@*bܸq5jQ aVqcܸq łRĮ]piBӧ4LV~l6vvvCuu5<<}`]C!;;eeehnnƶm`mmgyƍ0 @" ;;EEEhkk+0d8}49;wĵkЧO,_K&fyVVP[[ Nc"==[UUUhnn-^___OF[[1~x<&BqE޽ * xٓ'O";; 0 ٳ@^T/X,FDDƞ?NNN8q"^|E$%%ɩTDׯ^KNNVX@{ —_~={ݻw#""/"|>VXϛC8;;C\\nj(G),,$111fd2oɓ'DB !`;H$2p@m[YYILL$k׮% BJ"SL!F"ZBÇIhh(߈F!"IDDYjz}>|89z(6my饗ȟi<ӦM#Dכn:B #aaa&ϲ~Db1ڵk.2n8gNgV%9III!gΜ!:zCHNNXJE&OLɹsB wNU*Q*Bz=ٵk%ϟfddDtNG~G2tPrAj` O&qqqdǎ:ۼy36l {n2x`rJKKիMZzz:y'{ezé@{{ů q)̟?GUUt:]`f~@V_7F\p8RłVƍdOOOh45Ϝ9XyA⫯BHHH>" 0!H`mmX,jX, j5 jkkqv 43UJ%;5 `l6nnn7ޗH$|8x|7t8g;+̙///477c=Q[[ cUWW7n <$$$ֶ[-**SNTB& b`޼yhHjqtCB,7"##1k,lذWԡY\\ +++RT6`0@$h4(++ UjIDAT>}` `0Ln~I1Z6agg_~%9K"$$|>SNPD"AKKEVE[[Qӡ , rGtt41p@Lzׯ_G~~>|M,i܌*ёZ.\b b,ZLcǎBp8puu5֬Y#Fkee% 駟믿V ]]]aֆr.]22 6ml H$DŸ]&LgBP<=Nz 555Gkk+b1-[Ph֭[⧟~BXX]L&H$BRRE۷/x |(//0طoF_~h~td{ {\.aoon#Ԡk֬A`` f͚e|<ؾ}; ?Lt<0JXf 6l`y{{cǎ RRRpq̙3ƶ'o@ 33gw31cƌwyǢB}vZ [nSO=P tҥws\.ŋ ///8::"%%E~:JKKQZZV$&&w߅O3 '''oK ׯ_D"Att4/^`cwb!)) à ƍâEL*_WFLL ЧO3`@cc#̞Q;LvcP(F+W Jӧ_DDD=8t|7y5QVV^3f`&=WFRAǧGNQ`0@Vcܸqx  ;Ʈ8;L& <ؘODEEPT4i̙cZBlL:ӦMË/ooo駟6{pp0 QUU,Zaaafe6bcc1hРN{* jcƌ2fXhii@ ĉ-Oaqʽ-[ɷ~KJJJJh!g$ 6lr_?B!4x~j۷ .\@kk+fϞmф:JʂRDss3 @3,J{ #&YYYP(Dxx8x x{{r9sGcc#'xnnn=PP(BCPP(  BBPP(*8 BPP(Tp( BCP?0CKIENDB`sqlkit-0.9.5/doc/static/print.css0000644000175000017500000000031711714210425016272 0ustar sandrosandro#content { font-size: 90% text-align: justify; } #header, #main_menu, .sphinxsidebar {display: none;} #wrap, .document, #container, #content, .bodywrapper {width: 100%; margin: 0 0px; float: none;} sqlkit-0.9.5/doc/static/tour.css0000644000175000017500000000070511714210425016130 0ustar sandrosandroh3 { -moz-border-radius-topleft:4px; -moz-border-radius-topright:4px; background:none repeat scroll 0 0 #DAD7D3; margin:0; padding:8px 1px 8px 8px; color: #333; font-size: 24px; font-weight: normal !important; line-height: 20px; margin: 0 0 8px; } img { border: 0; padding: 0 10px } #preview{ position: absolute; border: 1px solid #ccc; background: #f4821f ; padding: 10px; display: none; color: #fff; } sqlkit-0.9.5/doc/static/arrow_right.png0000644000175000017500000000034311714210425017460 0ustar sandrosandroPNG  IHDR vWtEXtSoftwareAdobe ImageReadyqe<IDATxb?²F.8 d3&(Tg3`@ ( 4n gð2qF0A\A 0yꤙ !+1?B~aX@ahħ  *[jIENDB`sqlkit-0.9.5/doc/static/gplv3.png0000644000175000017500000000565211714210425016174 0ustar sandrosandroPNG  IHDRXc sRGBbKGD pHYsҼtIME 6K%d *IDATh{uZEр7cE1&t4MCM'5ILqlNMjH@x婢oD#9}prAљ3pok_O9'p&>8&2* JrT9 ^P[[jtL9`R&qW>$c2yZUǓnZo@c?q{pd+F&atuc:/v8lEj[5Pˁ"]iq}Nx,|Mh?+2zK]iy%hJ`]Otp0.| $-n~,ٟmXz;C#G4':>G:־؆m \;[ږ0؎^k!=<<ºeIh#5}rFL:)܂CbbW~QM @,2]0X2U㚡/ӟ̑sE|hPZENUcR]p~l)OjkB(LbۗGwvE93mО2ϛھ+rGs+<5ATxk#plHosr^J|LLh,C #{x!e&꬜I}錻snɶFTf3\ Y)_'+76Kê ̪&uzcwޙbsJf1gmPθ"YXcӗm+T`(*" /Rx?{&;a9)u`q~Np|n|[Ɖ" +<45аͿ[Z_g1p$cJjGderdr_8>d(g*f'Qʯ[f^`Sk. N((ͦ%FSSϠkN=r2!э2_˘Ҹ/RG>:sZg&%IN,wcpQ֐TEWG[5% 9!Y[Uj!ɬ#"WWBZS^YOlmǖܒ]gu'n㼴nM!ax!tTpXtvG#&DqC0l iFM6܆ 7vԹ{Ґ/#_k9˒T.δSG+7Tj'KePq\`cduLn 71;*:2K'g֭.|MHsqښ5RdȺD2<G.y!{5qsNswtCø+Τ-N+gFSIM[9߇2KҰXg^qSH,\U{NȘzssz~HgyB F| j4j>/rw4ycH"ܣ,>3pT!.Gg2~$ՋQ7E6iqzbŰ!%FŤ0N̡क़?0:kȬP<ոĘ ӻoYcHޞGmLK%瘛x9"Ok _%=*39iM͋|-/fBz=!ast!1L,gP.9RӵxLH!jLH˃?&&plqCq;rE%༻Hr^W{1nCuj.~8<29rvH!un澙J[wb"]tqh9%3r^w7I"J@m8]zieOu/!IENDB`sqlkit-0.9.5/doc/static/sfondo-box.png0000644000175000017500000010064411714210425017214 0ustar sandrosandroPNG  IHDR/Q)sRGBbKGD pHYs  tIME S IDATxv8- RrR?as[XEW[[QwO8#1s4,")T;!"ײMp "p88sJɆIU0s]z?D4nV*uj9A.R1123eRR9P|K  41y=(EDDLl9s6φC1Ɣ%܃+*m&2ȅx\ o̪R꺮`'-"9..?i}ە]29(4[`_Ƶ}'2F'j`>? Heӗ&0"~Zq/U3soK|{hXk|.4o3K"13z1m @r6R&9ºIkG&*޸p!ѣDQٕ˝޹n7ԒyعlorUR!,cTFR)GHcV݅bP B*2t@r>UUT4m4q0{Ki), n\`e9]>ձS3MC{ibfJA/>w9k7V H K9s///m I!gMnQ1:ҧ P/]&CG Ri"*@M>v-r ݡ& Rhڨ&&6RrdWM&!/ dИ`MIHRffK&>e[+겯qa7M\}y`K`4@ )K֦ mM(p^#+..ϲg|l^$_09 HZs\?jZU"z*#y mshRJ)e4M1kJS̑RUyTF"%biSf.lvxA7$8kOnAi>2ROYYǥRpeP<: UP62s 'l:'ӟ}L6*/Z˓t(@Ѹm^rҤB}i%w ` )0>!0Tn1/zmƲ<%Lj8(TT4g37m"D=9VW(d|\%>ӖqpE#zeȮL.>w>Mb m?xyy1Q9AdUxowHE_yYuo٘Vo{{+Qg_vU(X_1:mb p+xxjC&.?jr[pax~ƅΩMςQbJ|sdGV(0OQr9:8PLl8d5Z!wri~׏lގph !ȁ,q麪'!j?48K=O=/29>WlۺϿsZYB9}h_[_}8߮f>V '"@!XqC0s۶9gCffUHkx|bJ~e׵#dnW |RL f<~ƻqYg(1Fof &ש=Y?A{\^s"Rˢ_e;,U24dM.+W䥍kl?peA ɋk/<|@LSkIJf4MC]@κxǀ7C_6kNZ Vm"@H@b)1jďc9lM':`h3&L!.kR2RK.3tn<ϸR#3hmuw O|-P?[eolғg}bZ0r 9_tRϢeY4yG7@RꛦIɰ577\,c(FlWݥxA{2V~&%W'U !0s4""$`{dr.~ Bӡzn^6_&MW*?5|$?QKaR49D$Iu',̈́PA3N:.[E!_ĚX'uߟC dkVZ/KeAdRޡiBua9}rT&KU/_J4{{4 .\ +h!}N0+/ѪZ+0—rF/ç;&d^k4]sw ;3S:3[JRJJjVu.1.k;v^[,?@ƫEBD!` sexem7}FňMzs,"]QlyWFn?Bzo\>Y󂋰d=iS5\$ښ1m>nJ=K~K[L _skWy6jjrC%o6ଵ"#N_}???WR;ֽo?^3z#gf@-rCvdL, j8̭ATZދJ}ZըUb %s !&R`"͚2C,>ύQK=FW!T `=%f^~/^y] |oa։.\y} FC<]bgՖQLCcD2fTؽ3L\6"–Q ؂,0 1ƨV>/VϬUUEs91XdUYPGM>|*7ϭ믠, agq=4LBM2qYURJR\"s^՝*phc aWzvWc$gLt^Cg78J^+Mż.BTl!^ 'O\kGm7qI+Jת9>i=wض RJ% E_d!v<3ן<+Ϸ>u7%ж k5۟+ϥ+`Iu]۶gRj6_N6:xU=F+<zj8gP9-]..Qo.[c$Ӝ:?ǡ&?492{uBJ > %]\gL l"%7$c܇eV%fr k @IQX`<~oqk?+w{Aea|c/niRbJU?>/<_8bcZAa]1J֡wmi+V,<4qdoCSqa.&2 fe#tBl9gQ*m7dOGM\ϝI| _5'-Ffv /2jU&b;jBN'mim SS?y<4ZDƕƿ.-;M/&SoT54 |'-ڙm&kˇ5Q\`WI SAeg7,]% 9sS"!5\p2˖hTn_#LImxfg.+˟ /O;@$H4+y20{97f(/ҟT}=7zD"[nZUN("mӤ|̩ LTRсwĺmX,}AځY^s|Q ͍ BR9'Ѧ8|c?ex6ZjFO۪[GM bCGVC꨸4ۉrhg:!u]A1 +Ņq.xjh } ULW^M"Gp%5Ruұ 8wxsL7}\^0Gº,(1S:wn0OH-[@d\ҴMr߿O|;FOs|_p~7}[ ?3!""i.2>J4qqϩ_`gc9bk6xގcq>$K@/[uzI惇Ǯn|Wԟp!RJg1| / ]wSFeISػZ'눨9/E(VUZ..%6LǸh.J 3ei<7ݭ_?cl۶_wqqm  ne֨un{X~z;Fn_"G&-:wtTy@fÉ꺹~:k>2*g>[mxD΍,tPh "ґT*/H %"Q?fy@~FTS YrL겗c$HV"1RzO,->JkҶkwrVRЫp+쌢L<:a?#2)MAC܉y! 8ynI,rZ9C1 m:fCN;b~ύ[Kz>q.Jxv+fx bODLu-!>y :]qQe[E%sy<5G hamd 1IhAsNUI{")fVEw[&OpQ3wk3q4.V{lLrN9\-j,[&FWR'/#*)QL~qdϦI) 7Mi.n]-guya mISE]~u=P}wf1p뺎:ɹD$!B!tX̝,K U?Ҧ yJ.>+= ѵ{81Ɣĥ7R~qG̓Y2[ ?;y{{p8vh-Ond2~UYY(* ɃT "TjA'7޺q?o5[&DZ"Ӝ~VjyM_1ƦiTrΪj8yge/4(P94M1Me=NV9 19K 5WyZnfK9rJqݜZv:? v5_HXb%U%`o9.޳rg yVs m"Lzr'u&7m[Ȥ"" gg.?5yaRxhc]7L}ɵG> k,fjBDWD,? GM~$jxy2+S@Wm2p1R|>᪞)q{bpN rЪEzwY^-_jigj9TUDKYHclڶ m(ҾΕZdSFt>2*fzTm,{DAP>-,m'&jJ R:Dl%tDJ T}/ܿj=S큂th"T]S@ӏCTd⏙6^!y;S U ŞӰ3/PT3/ZJ-{BjP$P IDATn7㭙ͥəs~yy1F+sCD >lAYŭ7BX"S>]N.o3ne~$c$@T ߯vLU6Zk(? 9s/ܸ,3^q+wyOuk絉B)D&P&DdyjxF#騥Q{yAFW@]3+zߒaHGCͫpqY5qp o=߄{è؉I)Q8{3rt6Ld iⲎi;-m癹iLClL9w=QgO=#nnL&|v` *iV\7GM#F|j RңdM21x4wyZr %1Oa'I1M(ʻed2lKY[Yyt..zȰ% QOy"VBUQ>{X{{FM>녇}}`0XFN6^R(Vwo~.rT:" //m!s&:J@{.Ok8EUi,acX>%|\BM 'Q xZzFD eq(&5X"dK4Y9 vO!vo$") 5MEBAD$=!Pxո/2ErYw>Jc !䜻k5d4K.<ߊ>&_k z)DV #&aVs]{}9jGx$3Jb֒qٹ>(|uO-%R2ĐBg\)?UP"I}b>A&"@glյbڶ) =G#F?Q3/rs.V 'ׯ?dRrRzeۚ&.KO@*JF ,1R~L)::.-iqLZ㲡c_4!!q4|U0 Ic4L"=R㨉`v7;͹3 <}>6?f>v]BG{!#I FD2iRCi.vq٭q EynGc]Y[mr7Mjϋ/^$2PUe{O!6M`&H lMwrv#/XK YI"Ix!4-cB!c2 _`D\V| 6}<@&66+BIqz~QWWhX2PLD̆hw" -*IE1"JDBD"s7SU`"J "lya=U_ 6W[N]ltDb"5N'{!dU!e !ugt/_\׳$̍RKa\'499WchFСƍ O`}a,u 5{=/ 1  ԶC;K+DD4MΪ@R5Z{k}g9SN}0`X=-ll n}*l"=d Tj1@$Lu1x4n..&.m(;XKu16̰~s..?4'V{GsJI:tȿ?=[ski,F9B$z>9O4q 7 =ju|z_[[}80cK9POm\~iv9} "3l@{ jXw25H/7xVcG̬LjĘsvdRמ4qj )e ڦi~]1 qJ"ǪQoc|q):iѫ&.U*hTS@looo4l.n| ,4ݺ?Tk(X[Ι t*ڰU9o+.3yz>9<ۘ_%9^_msWXX_2m<K#Kⳃ6I)mD[U۶ @b][{ W܏Tgwlj_^^]9OZ!c/ ]X廮 V4}PBJ3^b ![uJ/0MLa~#%.Z@9 Vⲭiոbke^e+ž`kXkN eݑApҚ/>' ᪔Njib1YϵUe Ֆ&4UWORdU>?yh|a]Nw PBv,$9C8SJGQ0(e@ M9RnC+EOq%j쮳fNjϒ]OUVK}/j7M Hes0wDor_1<̑%BDUrffu!G_ ۪jb=Do0;[^EM^ڜо_?&0?Sz#L*_>X@tUߞ Rz@^po+WZ`ڿS ~òf=#zWt,^;JB -)*1SDTUxb0qYXL߮ȐK`bWȞ ^ݎA f^0 1++)eqlQ,R.] M4GUHI 3Lt(-a_.xpL 4qY[Kw|rqeB| oQ$gbP m9yv2qJڪa&˷WMX_ȣu$cr iⲖir* f憛PU.$B+e75\}ЗtrugbFIV !QcM6⦉C5Ӭ*1Fl*D"o-ˡy&qi2ԭ&-5!c:&H DylVhڼ)i R!n(M!d'D hRr?gʅir=lrQoq^'YQ1ζm_^^#a]ITerQBgڟtĘǧ9..JcvX9|{篯f^k1F3P@JYBQ6('| K)B؂5Vq~x^{ɛz&+g*65يaߵ}ߟ(<%mbf'"ܾ^xm-r*LA8 vdj&4{1lɏJSqqsKfF`usHZ-D!DtOKkyvDk3ʳXykg#qQϼ.PItD~m^ߏ)u)e" .KV* 0{ae"21d+Nh5Vǔ˖NJrneaɇasXYA`+j(e /m&(Qv"?s!?FX^|4ξNd!|!VY(! Um9"{kp~uY jy€24.Ӿ sYėSA&p: !ƶ[lkf-l<[^7M\>)='23$ EMo?n?|v ߭<ZExɣl`))7ǹL\xR>7oQ)M{!Pe 79<.S ۆguTqq^|'Uuˁkӆ0y0Ḅ[\\5qY ic4wYD(hE4м.ϓmm*=EdEMzϿYuVFMxxy?{cDV/,2gRXf;!GP^\U^6 rt3& GM\^#"fC 9뺮 '|{)x{8U#>rNJ&k.b!Ar폱qRJ)cJҴF}?p,*RQ?.+8S|'<a_W/O̫~Tuff+%Nvx<O,7j9g"E`f)fN)[4Miߑϫxˢ)=Pd&&Q5y+';%hA`s9F'oSlk7Y&. c?̲69vc5w4>sI ?qt18JAsIC&..K&I50D5HG"@iCl6($D?XNgdXZb2t\Ƹqd r "Vs.E?KŠuZ >5IsKF@,x=+Me~PJJ !$YZ>uO{`VIV&]TļB~ p@ގӸ|~-J~f#Z6x~++@A zM̧_`ݝz\{U:ӇUw'J! D0Bl#lJ,a'f30i@{թPvNM$zނ&X-5VMs\?0tE6nt>OvM{g;2x@eYNh&af2B^O}Ӗe, HYA b:O08F2N-N!3πHDdܾwrZcynvZ4qyԪcU`C!h;lvrp!j)Ik?3gUXf֜eO퍷BMD1'ȀX>M6<.On)N?{֓~:.B(r91rJ"jɭcMasmƋ(`iC#e.MgO5FXQ\ #h<&1?1PtgxHx%6^5Y* GM\vx@ ! fy{{!,4-}H3oKyן{Z4e*ģUde湤F?ݞkŝ)ܯ !HZ4nqk:7hݝ`WYfjYϔi'e͈YEU#)r S~O)`W~9* MKk[?zf0DZϵvNZҞ·HOkvJb] D\`J@Á;˾_Vrh[e%!a  2B0B9@*'Ifhe)Ao7V AFkB*[?gATP ~ 5X ~M+ˆPU$"1Ʀi< fS@!X2du9gUy<#pSv#iEnH_3g,&E*# "IUC&3Scyr>C #y3b{Ԁ:[}qML.)OFl;Wgi d弘6;VG8ntMs; 3D,uT2 RCdF#R ]ץ$CΖ;7Tut@6YɁuLWosR/6&Va'S.{cNRbm,Mi ˽/&#B{1 Ũ9 IDAT}^BWه)Qq-lT<&%ӛ75S]Q2BH)k5V[mm ` Q "KBEo%:Z~"zn>wqal6Sج 0H5CSDFUIΙ1ᣈl8;WrDdZ_m&:eQf%6PNvp6aDDm%CkŪs P\KJkQ/?vf?[BFtd( UORuC@i0EFT%S%6/ N\/̜UDb<2$ey\FDXg誈BI$#p4ĶmC6XrfK-);xrrA]3cXϿ:ڛ5g:AY#s4!z}ޮO!m9^gU>" 0&.O##LDʥÙ1B*LVH^_EDsN)JX,у*t8Yu gjdeFgcQr<^83PBY4q;dR""Y$ !,xwT^5YG{fv,i%B!ĦQ~gQ}a.1?% /v)Əgli82{~,"Bퟙ&9ߓ3Vb\UBhw9j(u%1߿4)m7'=mmiۍڽw,Ѝx޺l#&6h2ՅV}LSNm>K5RRFQ(XavIzr^l'9P&ԗ4'Ϛ e "!y uCU+|.N9Lb?뀻H>ز/|I>U˜˕4ەmCKUyĂuA;+(A'HN6۝|!rs8ƈJJIUcیU˥ߕ^2WZ}/Òذ?U%F slHDEr'@p@.q8Y@ /AF鴋XbF'RmehD)HISljߢ@Ô4L= jUׯ}vtfVN+gEez?c]Db`'l;ả 4v \ѝӳ_4g.qYŏ'&%Q(QlЄ*F,_V \?mޟAG4n4 7mΚE׆zmy˙ŔF(9zKL),=Yy^T"*O& >yf9*f&F.7uEBR&R[ͫ?[fsfjm[ w]<[QKӾxcs*VBzJΛ5kOgJ=Ncpd!ˤ*reeDޠ@M ˣЎwfb Ȉrש@.T i̬uY^T(`J_ f5]Psn4McG9) s5qq9 wRU1t-Ms.ą0tdi~Bː<̼Jds+Fxx|D*P,=A`0s\f|c S8hƱA'kһe7^71_S, =V=+*6gqY :3B8EU%1bJJx..~ - :Pjr ƐspN ˇO&iRq3S̔ ! q0Zd,ߺFCMV~Qi ]u]'r2M\\v*4ڰ(; InlZJ_SjqA'Zb^3`a;/R|߂T^CJ ЩER9gI?gUUL}X[1{D̀vSGaM"+TpzP?+p}bC.zdx ҟ}Csr"%("t Q@+%. U[CS1yQ4Xixew D-qͥ.R&rB)gISQT`*m&LwD~KZ9cX~3on[ YUmͤEP\K(" F' D e)}cPP$qoqn{PL@ hRJ)%U=ɽkJjlH&ݳۡ~۶]~z KhډҎ5)=ʕ4يHYdo.}e%aU57 @D ,u,4u,w o ܳ ~}2J(4 @$.aO5qqYK_DvlwɜsU5jZ累2]흋2eG'd=3>ұX/"d2xDٞO oDٳDf;uMpC-GM8oo`_7x6"zSݨ,CMt%i?.!&n'(;4pl}-nYq'suC)cF}RL pɳwRY /+:wve-Ӥ %fĈ''d/|wskyirglAQuJFir2<0X{2@ $V q_A&k碗YtGP Zx6nVX sWJ<=atY$ f4GD# NfǠr}?"_JmhJ O۔㨉.+);=+R(ROF*Q˖8K iPQ1l4)%|T9LQILߡ {*93MFIFgD*sygF|,il_r;UK 3@>cJ:_{S/o&p:; wp^N8Y4Mc+߂dKXٲ`]"grd&<4.YɥJiyt5yz^gAM?oP3My}}\>{4QLj~iCiN5Y50)S8j(H) )1BS3@zu3N5M\vˉ`澦g=&5vihWY @DIc`n%u] %.._k}I &c`fb|p8K]b3[pK2R+q.o=-J5  {;O_XP"}χ[JlD eP`IN)S̷|x{պںiN3g5Q7.Z[.Oq=2c|r>1tfps1D(kc03z)UCPwwLBj<%_#rttR3&LYG\9P۶YH%VpۂNøJ ]i@= ԗ[uz1.<7^KUVj C-=w&p믿z/R0XhNK~Ύ|?~>Ǹ.}ݖ踸|eL 7 Y$vēRʚ"dѵˆV23 v}WExƇvQ  I+Nxjg_=y` 0rA( ʸib(A)AJ*=z.8p :ƣL6%7//6 g1 )Ge(L4|CYVy>cs.(V~ J S23"gBDK@yVq8!9w=~XyVS +_5Lt]'JYu"Х25qqy]ʲ3H(>9 z1{em9Х$Ù{_qOK6fnۖS:1a.?S@- PKE cl+ՆrUafVHR|>sc3&..O))%9蜌m۶m-4a|qچ:n<s!1t+7A+3n[6I3?;kz<3 !C@Z$=tyRuYږz#N͛oCHY]72nu_{\~=[9/mt]+"A$4MhCphnįDMT h֥%(vgRb33ӽRsKsȝ_f '[;΂S峢^嚤GI5peLI5MZv7V^WY'@ EkC!ՓrA}(Ο1v; 2h%;a<1zuoj!H `;i8I*5{ f#BC#gzB`6> >'W1(9r:5AK\+ 7j8i/!bNԽ=' >{ IEMJycɆzLReB8tg/9wIV *8|nm}>5Y7s^eXc/b!4f9MS5w]JßTRe$bf@W^ѳRs^,p3\*p򕫻&1tx&]IEQI*iq GPc XN$lbR&sWִTrfűBpr!ln{zzzyyC=.͠'ٲc'Uf T'k(9"vCċ5x?y~Ŕ{f&Jz.16ƶoym")$"Nm[r;z-OϵlE%O f& MyN׷_ _)|+BXfI]kj+b}>kf=p76jc4\5Ʌ pt#] !L2]~?lɨ>夙dhvWY{3y??mGzV*L\띨fNvaw5 A !QUiʪ)W!a|ir!mK/ |Z|.cB~̱ / )]nF#~#6(nⱉ,RJҬPʇE^T}0^:&n 2%~i|G\T$ x~8YgCqƎ,Nw6S5U Hivm&yQVYzF_5Dɦq64%TR#xI` ^*a dF"c-kf A er!rNN_lԟBzU5HbcFCOη7L0UJۉCR2"Ǽk#(4RWAS9 fbG$SmUHa McCZd˰8[ed> SUX""C} ~l oWn\Wέz&LhQn൘]uڃz  yIIsN:2LUa l&4m:6ڜr`dZ@v"|P\W}72ʆhGʧlv/? ei|/>x~DzfkgT؞L؜n4M2=^[mM ڌqX*Q}*^f5"% %=E! f!cjUշ 5jY"A4Q#PMGTb>>?+v1hIPwt_*\M*U~ zYǥUrv IDATdT9`EvP!jJ)Y Yfq_#/6b @)cVyZD9_x-Qe)g&,6c33#y+S&UޑGl%+RB䌁bI*5Q@:!NxDme 8Q*Z)AQ@v; 䐊ub)Y6u.䖖WB1M' flQQ 4Yڝ BR: `0Ӕ)g*ԯ,pbļ"DB59g↑ʬٲL(>(u^4~&S'ol{qoh٠ F iG*jRJҹ=JUjU#xo(4W- LJ㲲3{QB(.{Kyuڇ&MujCҁwRJ=o|oߠa0U"!b9A,hF{pm_͇I?c:,ŠH5qJ4(Ȑsr? ~4Ҳ*B"'PЯT>M y'v7 ~A5MTryq!55#| (2RLifrd >}ˁ,vw6E`Lkbf5 ުR|W=~+jRZ&K_*o2q"\g,V" d3~~zzJ)mS!b$zBI/w M.'hA(L1a+~x7֡!39vk&KlTTI?Klu'!L5OHv84ch[m .VC3.D:pl bOOOj),(D *r!nkTOثI*Uzر& nCAkRufgU4B(ew@^irESguQhzW1DbgtL ['.C/;lU!^O\xi v+ U_Ox|]{= |7{Ec|>:]gbA)^x}^u>6{%'B!ÑbT %ñW4yyyĕ`'>`H{8G |q:Sєjjݎ!$î1˪TA ܊6"__osۭGQcc9SDb_nBl5*jR$IQ_|XڜH D6l$rv^y  /W#*%8H㘫TItY9C_XJa^#Kj ?\5r^SMF%@(`Bhd|H)B۹c6|ґ c"_0% %6sY0j3Qu:.%Wl3v9F8'nu5.Ym7Th 0{5MnڶeVڐ)Vd~9lDq",;1JxϵB!RRU:$OЩR IۥP>? 77WXc$I a7NZ<9$?嚮i_'Bh&M:u,꬯N?V/.6BM,Zqgul ?2/2DN6`!6CIp")Nmz'”<A*H/$!M9X] 5U~͏ro;rN)u&KADJ BMjRc+ :`׈)h{\u;V#>@܅bj. M^rY |A"u۔!OJ))FiI*kp6X)Yw<ؠ==rQK [cLyrū缍Gڵ}_wn@}9rqR|ts3aC!"j#>$yXm`,1F-fvz530J5[:妆) bs5',;>b:~2."I QrΆ uj"pnd(@ XӊA-(-TVjxuJ~lta}[%tpA60[ I-ܣ~CWg$Yt|D1Ii+WnNjSIB0 N~:mC>`NJطCjqy{( [' VjkU~2AË'A_fR*S)Q/mzgd(%<-=RےA„uR]:Ki'6mJncn#*/m}+!.Z/齅C}/U^Qe b1khkAr3m!COo pYĦv?!xF ҁoYa"B# VtC&o1nτ :bxY/A?fPuT!( 1'Kj{=դeŃ8D *U,8nݩ]r%{pkI/xiy^!%/rfMjfgla!t%_b! z{4,Ys&JWJmsfN?Dirw*̇aN_>?@yUp-_{/f$9C˜4e1Ք3,! $: |9朕.0 FlV#&ja#(Ѻ!,\I[zK)6)RviR7rirί ,&N<@2T- l5EH 45AIiEĂY= c?{4Yem_;QP@7{^W;"p}P #".3Pzu'ú{OER̀£,IgSM)ڤYS6ChC&@ }DbrCˣC/L5ݷ3_f.)eC6釫&vL:42?)XʩƗ8R%;*pŕ<"!@MaYUA͈'!B1if̯ɲG$3*@lcBіh\ %h␽(j|wBtj s~]UԤJ`5ꊟ9K _I9_,IK Sk"3'0Mg {W6mI ffȄ ѝ]Sf3 !^__SJ""9V?\gqk^u x|/oke[B/%6ws(ҕ.- gvhmc ?ͻ|yo : MJ oN.f'뻶m{85ߙgAܶh~?wU:bb=k*nKǿ}{kd]it*cmOJI@#A.YN/RTȨFE6ԜUm6ҘXdD@4ɒjֲSĈlP0P԰"% gWy9< "H3>3_ٶ{[_pU٪TY9c%nD:X`4ovOj2|HM} )"FuQH"q ̆:V/Sc-˪*i+YH;Uz!>~ R~xq!ߝKWOt|fEvL^n\c@V$@~'xRSHSvפt @g21*,gU "1ضm2#·bybEN*UciRO'KN˩̶v;|] ^h"@i_# $ij SaW>:;I IGO>w4(n7;,%U 4Čv#( #jRUmQHzd6q_t|wyIFη꘰K΁j H$i~s"i"Dr>7ةT$ ?G ;v8'ɺ#"['&>ƨ z K mPƫcv^KKTy5%v9@r w4gK,qNA%[b !rV989 i0Gɯ vτJc9 7urs%EM9`C<z&Di0L />ڰ}%\Vy[iSR5.7KV"3X4hFs{RqTvYd9"#,1{*UDpo#y.uc}>e#ZM~53?[MHHdP՜,h7C$R" zhGJ]\bz^»oyO7Edpgfjհd- <fOF'K_m6'=\G_`(6pۦ~&jdٌTTsD ԺobR>8>MWqJXU+_@tP-`=0O!!lQ}3`4C  ϵIyjwǓsDHF9{>x$sQjXG%ٶ+Uj<%Y> hΔm 5QibOLv9#\T&|!/'aM[[8@]]ݵs{cX|S:)'oI*k+— |%8.NqKAHN8ֆ֝%(Z730tE[$PUOYaȏzmxhE~e @0EfzƏC:-z2U3 8{4 ^X0Y/`bzF}X)1x_sk~ئY!Οo%H4"H)9-=ΆNvuKʋ>9 WV&4X61.K7a`4̗>"TќQg:qS=}n`@t4ݤ84&]( ݹou'zƥbm;DM84O5YwmMWjvh܈CM)AʇPĚlQu0S|%mwJT г3UHr6q)LI= 4"I:֓]{8z}|1/||L. r|7g~WMZp8$lƽ\ĉ-_\yi<.UU @GTΉ>nr#~U8r]~3ebMu};4ꌏW/U>RufPـ^v}[khKm ar"A-2*%4o$ so"Q Kɑb}azphp$BκynYE[/9^kR#(Vx`Kc_eV_x 8[&%7Ed|@;Ѕpge>xW;-?s3&_ $Ě 4d89'%_({޻-A-.Ex#.>\~~H-ź:џoɑϴkEVO~"Z~~ %;a їo'M9Y7/_AWJn!.kGvHG^P^Rө6Y6׏b>CG ˶%-@ZS ({%ݷ8mjŐ4zAAk$ ]j7ړ\؊WM&mI:y2V F?c>v s,^CNe^zJ\Nlt-f:`9}~^Pj!4ݞm-yo0U Z1>a6<.^ j4B͹@Kk?iu_ BM:W^iDYQs}IKd.k !5S{zz, jf\u}FFySESH|ON~SmIDAT0/e0yޗd-$ieưlV(sBawH1ϱ!5݃]rDM e]8a2 v/}g!FIENDB`sqlkit-0.9.5/doc/static/images/0000755000175000017500000000000011714210425015670 5ustar sandrosandrosqlkit-0.9.5/doc/static/images/totals.png0000644000175000017500000015542611714210425017721 0ustar sandrosandroPNG  IHDRG$t"sRGBbKGD pHYs  tIME !~ IDATxw|Ṱ[{@IEAl vTXT,X *VHo"""w !$!fy$}~*}Wܝ;s|93N|&&&&&fbbbbbcx^1E;39N8N1I( c^ gG(?W[MLLL0F#C7{,V2]A a'#F>㚏O(?rQ}WVONb01FUFOPj94 P?A!` [3+tj{pa"a-مW&&&&l7sӓwg?pimC,ƽ : ,A7yЯ3?oۅJ+~=Ut1K( T6w7OP}=K/ܒ(\l0J{f=xgoLٜjiS8ϲyG=s|aI_^~sxF+k,ft{2#vlv"߹!y)k1c ms.Q75 g=ܵ')]xO+pdž9݈}y}nc|<rی#;k\()n|&y?vϼlq5OHo~gܽEjo&&&I.Sy̙+>|#o 3k 50x|Jnl~=1]GL]`yﵖe !Mbd 7OJ}}`@}ׯMgGK$1֦cZη?SQ7&{^йdƢ s5O"R[RH'_Zxwȍx qo}3]mn a#sٻ_݆~Ɲ?< t|ИǽʳgNPʍz}0-ygZ&56j{S;y8UALF$y/v +bSdY/yfRM7AǗ:.@R&7yH[5.:wB]`bxm全G: l+7 2]@]hy 4M1Xb=զ7%ķXil`k*"nvxs>GXL 1Y %73DfZq:y'thѽXߟp{ ?P]gэ⬼ź&a$Du?jŨ RvƋF:[WjLP-n{ǟ<vYs)P!atHfk'Їqzn1'Xw3oDxW/m95荤_W-tN_JZ}@sq RooV{JVjaFBS\oSXs+B56ԣk x* !D vXA>Ԭmq#35S=Lq{V2f6111Vjh`nQiزÜIYn>-kGՇ>-t]c{pC z-'t۶RC(@he0!F6qZyzN^5G1b԰fvЧ]p(7G\`(H "Vݚ rGĄX7;V_'(j8^qK) O9ѷ&l~h[nڲ{`$ g (eJ2=ڨaXp刵7t)6^]қRXjVl9b%MK?aö (8TVYMLLj@!8>$!ۢ%iW^a/U5\ T CEHS'(:!:D"6 C" E4(.Bq~56*g2.CEJ}44D{QBbh SR be-:!c4.B|<2&CR^( ppDdLD52Yg(-pjQףE0̴Wt(Šp3WQq[Â\((1#Z&aMGea1}~Gi$ 08 K,Qѡ2?FSņeť^_?B|ᤄzQ2b<&&&&QU311111gcebbbbbj&&&&&&8/(.!B IcQJ#.) \;S,XhtqK&&&1  q:aY>@Cm\yNgXXWWx D_|X)TMcO BĜ}$9U/1>4$~10._Կq]hvb@0%J=OrGz F3eMCCAYs:/h)!//Hx}/Xi0׍522{'J BBmicɩ `pTzaAaN~10Byݺr8 8p޽{7ƛn9yhrrMLLD[-*<Q2( YcU$^T=Gl  ϶O 3cYj1)@7tJ饶%If:}sk2e㺊Z7wܮTx!<q6=葲+"AUn= M\xm̭y4M6^w|អk[e9Hxמ мi$[A/"Pz'n#(c p8d#c?O>6iNH?Vb%Q..,Pu`tCŘx [>UsZ>hZxNp;:dJ PmjIDqZu}wѵHV-h7󳲳{ѣF*>c 4YfLl|fS411sm5B)4oۇmװyc=7y|>?ƨ!;B,w'dtרNMwx}qwozk 5rtVבlq?4lP~xQfNA!q9$KxùgGXV5rv̊zrUH],iqYRZT4+3TdFZi&8g>+h9'u$"5 5ߒ%(%3X ]Չakڵ^K,vB t/jټ!EQ\{q/qUV3Bt]hbbj1>vmw"Dz"bֱז*jJ 7@~hPؾ{[3 T] +`wu{b(ZgjPJIaFX[b(.oqoMJ_:1>1T^}GXm& 2fNV<+U]3K2"Ġ̣uwӘgMp(S U(Ta@@|a~܅ջyfr0ކa߸tF/Xc_iXT7Nݩܵ !9kEc|MLL9BCTM\.rm~+~ut4tL"|vv6(RH$$$$%uv41ǧTx; k;1z@UjU5:F$* 6Yas._/qrZ ɯ~訰/WqB麭{wLs9t,[Rt:UDD㗖)ЎU{TO;4hegE)Wiuitu4O݂qfc+c Zё1vɓN'p[\^pHL zRGf"gc~߃xLS,XKMLv9Ɖz q˽cnS ]pf0PT;JFXmƩ/~YPxh큤N<ھNU]Bᡒ/ϖH퐬{ %ξi߶+џdYd sc/Zxт 挚_`9Nb%]ןp;Ս.LZs28='CM)7kV5O+eA(JEȔ"u}{d,ؤ='~y|W|!.C%52B gXɒG볏Q X٪Fd$YLbFz*,‡? < QXL&wM &Ge"(,W}!<W2B _nQmYAYei`Iry7׵5Br"^`y0IM _5#iѢGr4..s0ɟjg"""eYEQbkBrsjrUu]g HZKUXPP0y3F3ʪʀeS*Y%$ y.& i!;;uٸ+2>qYv_#8\΋m}w |ءAw ʉaJ9 9px2nѸA!ֵooe%>Ι7${#8_F^ˍ%iM uD~4[#GBÃL0^\$1&IҌ/>iȭ)xKS{踻 CRp*x<>߮(Y#R%aqLmz٠ {mjX_SO7ggU)Rʼý" k麮뺦:%WjToآ_ B]e2C'<WM>|%uE,5-*]e1 Q51ߣE/Y;W3"I҈7A3+"*cuSN۲uϲ![ڴu<_QQ(j~~aBaEQ!B]7yщ yyymڴx<9!TUUM;$-h!өJnnq<)#9%:MT\\[Q1snf-+ctq׹ czE9:L&AB?e 0V8FC *xHÔVDs%()]1%tpNHj. BBx 6Bp`zh~dW(6veL\BZZco>_en7;~ mԨa&477RV$ fٔ**jN4e{\LBRa0 UQ]Q(r]Z:>wD!$''oO??~"<W1P,ٷcSqRԨIv؈/}EcNZשs/`kOB[6kC uݱcwhCЛ@aV?V{<}Vl%mڤ,]IlUyED_lަ/iٮjWW1yC% Io{ I4 IDAToyZr YNkSJN1@,cQʚ֣Գ{@`WPg YZD=ظqOT\ج&ŗvUtW!Hɘ>3^!p4bK1B)otmf_BCC{W _*USӵǧi\b#x#ݑC}ё@ ˗1xēOEmNq]>yl﮽?=PH43KW jo6ȝw{ wOy{Kv:28xȉ:= N,[v'l!)wMhGN@ߘھS=nrf;1jL x74rGaTyx"C!牛G:w}ϜHC_c}z}c!>_w?CŅ{罯v흳do=w>8_ۑ''EC3Oo-6^Ĩrc^ctP+ tYq|~ƛ~_!cƸMQO͘>eʴ7;FPHoMbet0>_M%?Fܾ~%of|sBܱ^tۚNQ='~q>祵ℒC-nmeh%` .]}-[NX4u1s_n|xm"PrB5o4ȡ{qxٍ>^YF> xq&!&WMPQ׸?o0f@syn|y/{񌔑~̞u냃>p"îq ~^0΂O}ST@xV9 g<|Y5m"=h֛GgmO JI/ 8_]&w(;uﺑi`u uZla­1*Uͳ 䶁i[;wjDZpo}!UdiߥF}oLFi]#0d~*J*tWMh[njog8)`P@|&ٮ;@\޹gO8p1fTFr qᾂL@ѡkhw*)quXJ%j2>Uv2f.>WX94|Xsr 7 il(bT}_' : I<+vg~Pϝ{\=>Hb{t`>K/"b:DY#Vm ũ[8p.:WA͡;X}'r [IҳUƣ. FC"جx|75SJ0x _tgI2cc/4i,_/`v߾mbo!Ӭoݣ6lۄMMp~ Af 9a{N!q=X8N3h', c~&)"Po6*Y8:)eg;㻄CT riD@EAyhi"rTF-O?eՄOZ3 Zd]bE0.ܾtF803ę\]V/@(5VTYEU$-krBݞ CUET٠+,v(PDD(ʲbTc6 srb[1_SIi-X@e]ի(ڂ -gn}=g<+'jhmƌ +ͪP52a»zjzHxp&?מ6G t%?-Q4>Q~yd;Zksvmv?Sؼ }2KꔅI4]'} {qsƌZ=(eZ[7>:ןuB+poKFٴQv[ĀZd-<0XktG+[]n} z+H]Ytz]'ˊ5kwWo3m K R${XU_3jץIvׂ8uAR/PQBX:}RٖM\~o->YCcŇFs(G8s'N=` J@lVR2܆I$\NG NNČ! C0l*  qv[ȕdQ{;ޞmEE9%^14&9!~G;J>AJTC޹im6M z^vq$KRR]@΂2 rTogG@ JFX\LBw: !(%'0Ykfd;bA⠢('%^=uH e`EL'ԓQ`ʹ3>IHGU334"!)&Xqv4a b1gd%.i#մUp.!`I[ӠqLYg ]rX|zQ,3+_(8219>z60jnR]ѠA"(ؚ$|\?2{_TYV9N1a l\3MV1(,ʹkI+ ٺ`wy>S"U31O_V5{StVTWSI0BUZLbeã/7%6͎WҪ P?qɅHDTJ+LL^U,FDT^%5q4ͰہR AoraT} 9OV>MZݹqu[KhVDd? V{ȁ=륤F^9 H;oY<!q}`_\sF}c}ˇ1K/|kҒH&^b? UK+=zxfXG&8/~J *O6BrOB?1~_}{-4qco>ڛ3C?>-V^6Nޡ'u/+5Қ_^^>zӧwi̘1D1WR㣼جm(/181ACqfٹ2=(:$5/ 6k^z2} ""mDwHFPu@H?Qa9`#0t ,♷2tii%ѣ{鼨ȸp%%N!C9f9L$216ڊY"{* *(/K0ovZ^}9 # GYN"!~M@["ꅰ&~-׸oYYnhj5Ʀ'/>7aԪUSQf]:듇+]2ף4C+:UĒb%8!,5VN9E ODSE()S|G%>"\)-5!AGڱ~p0 [0<\`1+8SB%#C L.ìY4i2j(JiӦMKKK?wy53'>0ZV:셣r|+V3oi/&-(YhRɂ7<5oGYNHټpxTXlthąEFy;sõ{6j/{_o.>t|ڃJ\cof+vٝ]s,ܳo%gwqtK /_߂#ey_:OE4ߖE%e 佭>AMyxʜPGW80 !s>9'Zԝd&\h>$vuڽK^]+#y~kqvc/?o4NwgJ*/3O6K+lvh tMo0)7 ǀ\wL?2iF ^s'=pnF@Kw %Az7qlsF8j- Oiy F}mQZ_o#0[Ս;r &JuYW \ffFFF6JibbbFFE +9A6`A4WC`(OλV^5o?&7Ӥ E"51z1 i,FP=ptT2ܤcX`1,Ѥ݃6V{fq#㊊MKL=:9?0zrrTR^ UNhʩ(<s3RZARD$ٙ i F0/Tw |W{<KR'⸽W( ̗IRKJ䗱_LOjh/z4OiH~눀trːB]3n](VWk- ?j}kh4g)7M}S7U}ժ5:j#>P?%aԨ6Z [gX/ g,㏴mׂ+sfңwhMICݓGQ[~_$(?|/=^Hc`ï'lxdԎIA\+G\[|We۶mU r'`1-tHlZۊx F Dfr14B-%Me{`r.ʠ?b$u4 Z7ͽ=& sc_wO}ӝ:; (-/MvN[z6~-B;w3νHhk( #RfIq'tLJ5LPrlDp)S[0;f.%FqPH b{DuF1cNr}MF7S?骢c;5 1٥cA I2lm M>"DV.POeq(o[1ߧ88Wڂe DbRqVԹ!*F ~mRk|۷oᇣHKK w ;E(i:aGNwB?,D|#*>(DNH.9ա!tb w2vdrP:1uT˲[. z}彾};/@R(ZZI/Ì:Yo{C`_|.kir1RZu؃dE<7bݫ_r<|P&Ἷء>zŧdQv0F7Y]n罊fv5Wn< O2BY+[ѡ0J'nHdr pԠ-9;]Ͱ_q+F]S/sc@69lJeaM6mэ7 tyU_S-j=kW ^G#bWP i:?=;FI8a:#O7  XW;3̪Rw>)8k_/rUY3C#Aa09xdRu{-1܌A=Vj ({1i]}yvhf}gAQOM#o[| w1G4k߈73ʅnJu[O;r ev7uz7c^G~roܿcxqiiUԨ\lF 4mܹƍ Xl"`x8UcH`@MD(Qt""~"e}bUU Q"eXi(Yd׸C3\BdE1(Vkl ZO < `H` ly_%9`P@O0w|q7+[bJ8`a( TկQ,2ƌhgXr>?7X`8$c"1_gZի I"J!իb`$bCW Â,"Ux9 ^ >U#Pu;^EFAzfڷ4ܼZdqѥc8i۳ XDD|~fKDQ5MĪTW`4CżteMuMa, M$~@, j" CbXEF z5ztjUBuzaQŏ IDATu B`(zUi@ kW͑J@S!P!@T'~ʤ5.7ͯ@l4BzD| bFB`=#UAcfY v-R5P5C,_B\SU~ @oE@ UEF}W.oѴ@X siAR姧` &h>-0\ֈ _`t+ r c(3{{5]Bz=" *Ů("«OQ]DP@+H^rwۦ\&?ಷ;g6>OBU@ ^A߼g Vd"SU0 d"SO|B|&>6.1NEӦMvZENHp& n#4Mg#` B%xz-g$ .goe}t^w9њӺeP8=O<ZD؂MPpM37TL}]ծ={L>.D.^kҙ21ٵ܃>XL |EGtM<ǕT YˈdfQBШ\ "Dw]fEvW(,E%^S:rrvUnj.q_6,VdYZMԼ̔ЈXGngyfڑ&]/DG =55?+U2C]4TK s kAEQ  CQX4+hxThFiYi%/j3$)Zd4Ysgy6X#ˋrΨ.J B4HuUūp΁]82Hk՚H@TƑARb0Hq) +2&}"?H/<-bu8A])*tyν\P@z?H$Y|' J%QRɜ2Q Ǝq6Uk9ժl5JrcڳۯEgV7[4Й{KFb;A vҁ$I5 sآ[,vqs<$Lޟ> b1XADo)2UF# 4MLL"' M%Yk>yl,H&, `DSutb 8­xA$A@u[($*YWX S'OjjDҜN5m1;t 5\ g4چu>j:*v>g9)U*N;x73~>^p?ci rғ)ATki*9ءovQ \Ę~}=ijw4`/wd00?ΡF?=ǃ|[n;OoAS#u3uY]<ʫvA,yu}ǽHƎCZpZ+ ȋy}z-Ҵ"rm1 .+05W|87aYɔr< >_ ٘]fKR`fœ`זdU~m҉?7%%Y$v9u[tAݚ]tP{||GJ1(vȀv6GStXv"R!FEɻH)A%7&Ą?вpn߶{&!JIbfT'>{251Fzq ;>λ{dwj3~3' 17</MS] p};(dzj/O9*[cGƱ'% /ZAgP ƀ7h;矱g[_#i?mrcf.-)1hD F?6'u@^g`@zR-[jհVoHg˚M.ܾO&0@ڮͻK,Mtݵ;k"ֶ4OQpС-{hVnMiӳs,£;717NFP wيRˍ&m >%-_ρ96bKv4;Ś\/ wo֡භ:|pEGNQ`΃z4rUOAiKPkey Vy<ŀvaAOlZ|wшCW 2;z}Lv}fZuUۊ2=.}64w_6?/_V4>w+ ?>>z“_5\e׊31B%94ul {EKѯ_|ڧԵ~$YZńڻ^(Ћ &ל~ZT`ԱwC che3>eWXt\zJ4ܔgЬ#:=U$ֿp=Yg l*4{b~6Fjp-_wʹmTc7,ԳKH~ ! uM 6lG^~;w͢ͽoaI_.ݼuˀ+1Uv p,'vTRk^\`bؠ|;CYhw$ǻ/Dn©׼}{c97oO9XRy!wf_ֵKÕQӲk_!~u er/tLѬ~ گax[o~l/)FZiC5$޵^Pc9Ce%wz?I]uڶV;2M"\,hiO&FܯQ "Я?TF\8X@`5G.ĴܲMG #{rPݽŭ#wR kadAJ~EmRFQ94.!<@"sd4}?|$t}x5kP3;Y.#BzIlgȄM#*;Dw-Ob9,Y$$T)ܳbbY*p02}6?:R!v ׬SsKnh$5ܻ/Զ}#D (C^k \@ ҔW Y<')Y׶ro ضHcSCy90BL:0hϱU`=޸ens(%W.0[ڟa\}WEPeAۜzЦ6}K)6=%J= ̨7ɰh=AbȘm{42{pDz=}q[!#SJcj N 3!`" ~s &\`[&<ɫV`@^0.Gic0Vx|&rcM]}n]蚢ڵ2.4S3f|3iI# ,1ϓ~vt?;H 0cQo4Nmie%}~sn3PB)`2b:G竝3jΚQ]2*m0 A{cǙ}q'g8[pH/PUS)"72*6$ZRh.\z LN-2ﻔ.9gNk<Ώe18>a&%좳'`2n,֢Yƪx%8 \qG fTJQYN $A˵?Qi{/"0w)H9 [L7(\P*y Zϗǹ%ӂ&K8*_9e]rM I'9 o뾄@K!3oLIJǻ?sx+o>50[Zqw#A }`T/B}y/Qlq}l5AI省(_[ܲyNsN5slSޞYqپí͓y[[܍]qqy{DTN%v5c *~ōEB@Pw $Q>P74c͎Jk4 ?ݺ)CU.Δ5pʰ guد_^./q,UM9%Xu{+xS`UBTٍV<JoWqٹ ޻ݾZlSحsIná¦nǢ/_wdt ۼKWR_YP$DVdEZGhnB<׋5aM.S3ҹs]vOQh׵G%=vmz,}B>lYj{nnɞ鷿lIk,:&d+~cJ _HHbxtl\YC#C$K@Ud F6lmDW?&( $22ql olC~z~B5QqqqcCme%8߳1Y;:^܀ k*:)Fu1rѿC}ͼK李40Y,;41ܴW?|ڤC.ɤ6Oi^& *>~Om OKc,hT-/8.iaAD8 M1ң:ۿE _wtCc;vͯI.Ņo7$=;9ֶk8ձ}ӓ$7iT?gNiѺ?I']Fp$hޮC!ݹXD+[j5Z3JV?Y5eWˁFO~/fU0LMuJk&g 4UyqvkTsE00\>Ƶ LZhwɕ_h0qUy{UkL!OmB-oy]d^["5w0j:r_/\ŶKxӦj ] R]O[)580GםYZR}xoψAwp^p:'PB5Kd "̑Q ٙ)>QQi 1gee6Lίnu0+q`KJs~ ,;]y~L@ݎ܌\3>a]@ QDT#0!Fo02L<={kQx\UgZ^!A2 RS]AH4Q>J.]h#C"+,A$:ya~#ZA:g,p XwHq`HxēskJ~It5Gȁ1j؂"9cz 9W<EyA8=4b蚚n0Hkpׇd"D9)%H)k0 "WH&shx%?QVV6#e1q7,eEƚ->4A Ӯ]-͈o.=ȿ 0zq:SJ"m~AKD(]̅Yf  x_ »X`0^JV?yAה8TUʾΒ*{Lk]9q%VDMb.x3n#ur)BqFZz]ZSEJ VՒqW߽Tt!./8Ĵҹӟ+4 WԜ}/?c/$FS2i3Ce5XeO=kYs65gިe./WGa <6gfj N~+"\*_RJ>\~\ٺlNA@aGs^hNυ BL/i'>Kʰr{h3U_~%\ѫzEkoc_Q\ î27"#0"7`cDT@Kr@e2(+s-8*ɥEl"Rn#5jZUW4c'(Ү:e$#Hڿ:֏ (-AaAVL PK[hp d8:w:z/D<_usO'={/XHkGN~)!0,2Ѐ$ d!4*:0Q9 @8r142ψBh%L򋶇Tgq~iL({ SQ4؉L~Q"gH11 hXƄ6f4ʼi߭R8 `Sj?>w[:iOMi?7 x Bu㈱c-"\%ԀE EVw(T{XLG,7[Ίzq/r*[pThp(/ϥ1@% av5L#ƈ3ʮ <3GOf+z~>o~N;gXbŧE&9)ui< ,*&V Vg#..&pcPLU1ڔ2{>\?=2=eeLأf b|LXi^Se~!0?#\yEö4С`%*:ytUUp? 5#O'`FXN#@bͻk67NdA W 5KMPVvꡃ XԭL4#o_DfYHD{g9폯gn8YNL|խdz٘^z9/>3}ˉ_{nׇ`X=wrMȉ[}DM~pn xܨcڄ멞y2?0^!]VU7 r࣯+Oc견p&:f__?mE)ͣ3OZ6g4穭^ /u)*M'2?P .b2s^-sp{dDȺUɽx\'ܶ_.]V%+mQp_w?y%++m@O}TxM35+yƓ% $}aV ~7EセI\W 2Z%?ڨc=bC{~H?wCq4j(6<:2g-RHLtllVLbAF5 72ccC<.47FG, whܽ)Cӿ[jv٠a-^_2V!(}Kyc9_H+Vos+woލIOq*/N q(/Ct )&#o8Hp﨧EA?L⸿y󑷣tOp'a'-CLB ohqBJ9'!  P"GV b<﫝g}7,LX…Kd­w d` zCOqW:PZҾl-Wi xMA F\z68'V9 8D-w%o%!a!đhj*p|їbnR͜WIP]n ȇG ?@=̭[Cm)b rFŰLaEahۣ,ePiq8"y\BUhQfB(#Œo~qSD܄qE-&`UUfjL! '.f7]*qŌ3Jءe>ͅ4@je(&,""Ankd^/ߝvy֖>Si|3{$936Z᫘i9¦Fkf{6 n׿Q4' hPp՝#{JU<ð3F k "p. NBk%aaaя͜bDF1<( &*ci=B6_8y\-0BP?N#e^x/8"<.  kn' 51t?KșwDpFinG)nw}XDwu7+Z n:l 4sF#LezhB!"<"g( <.W։l ?"qsvEG7~1 -#F2N)!9&[0a[& qXl_!!QT/Ć1aa~葱̄\}4TmJS֐`.Wx+#,ߘJ9++sTclR_(#iwH&#[6}Lf~A7]ҸɅb][2IcQl-,-!rtÙ:t8]BQ%F5MQU4CQ]֭ǩ\rcbaC7}AemlzFU<1O~#wmAG/Ҹؚn;k)r ٥ل$&OvUP]2~נ*(%]lj3 '9tvu957[|oJVcJHNaSRpS]M#b.ݛ^15gt)M+<a^(%Z"aX Hfǎr"6 SQި׈/:zqS[?n4@<=9_=(9B\!=iLG:'3n߹ogcn0Y qi?Au_e.?ѽ80d7 h3Ծj= PPzgv&;YXϿv_gTH!"XAzHs ϟᤇ4TTba HڹU3 g ~irqxi~1}³'VLT$pW+g'_xB!7-ʟ8%*&ndˊXL\|֧ZBV]2ESXt]%^/M*Nl07 }hBuq0kzo[ C#&BJفçȿJj zԟ-7=-Olmt|ϻ?Mc[~=|MGLE4o6eq+w%$yoԈXQĐw4.e-FJB>֛GęN^X:di[_! ڌV_)K:u~XkV4~|sl\de?>ƻ<2y|P{_}Ʒ Tk 1Ɍ$Ni{8g>oGm*]Sr07x<% bU֐N>w-m}Q^=DT#z}}fLVIX]J ]tOS>Al3܆З&t|AO)gKK_/ {6<(NtpF4ǙNtɯ^mm|Nۦ} gf_8nBЊ#GѰq (I7 w}]T'ai@=da^M? ?Il(֞_/O E=}ᣞx2.0]# VCUeС=򿓮8{DgРb'XXo^o>_w4gSg>vߠK-m!):) t|+dEHT 7[Sp1]n.e]5ƏN^p-cV2 <z}{^  TnOBbF><S^_+#9v40M% G~gc pڼR>x2Mās`D ΨFI>}-ÆoxE&{1l8Qv|[GMZ<~ %h2,͉ԘVO1j'~zI#8pxG޿~'=^%jgU7.ˬ\0*:j/w졌#Q2(j=9 l2@UE8B&/!uUV9&Q ȪVR"#"}'t'l b`0R2$+nU2[0@UE &eȊb?0sn0ʌ]N ±b@\(:@"`,. >dXD C&!#`[%`Z(0Y`2J4YFFʲα(15&MQG+:`4 DȔd2CrEtTF-:\`dpx@xP8BTW 8Ӓ @wCNf,\We:-@؝1} ^}W@/* HX%zng^^́(3EB@ugI뿊3E},貧z#Te޻gM/LqvK9>TQַA/'*H >hV Ձatog&p^7r]uY P]UkG_ܹBEgV딨5*D( e'^J,7MuHFsu:-8]ly\:&!@UTU04R]7Y]QT(Y#d+4P\dU&Q< !%/\#J]Q~iin80T]Z(| 9/֥dzձ ,VjMU )!وAZYp(!ŅgR\(BB¯upJew%d 5s}jJ@H ;΁R:Ҫ_ u$gPjsApEr >{МGE2(2 A0\v'LC_zjLd뻎Wu%s]׮ãPB|X]E,8ށ1Ju=L g;b>| tMs*Q"$҅`A8)#z-5J9,z/*A0m#G>F׮ҜeE(lʏhL%b8O)kVhW)O^%`l0`x0 IDAT]}*G>|Oc&'aA4X)ˮJ M&oyk0 \a  d2J4`4L&QL773JfֽwSɀ%G>|OXPBG%J%`1lZw0dS ܒzF-1;lR# 3|H!w,^Òcډ9Sg9q;}l['G>ej1ƈdaUU@ F+s#,Mh>8}O*~xu;4.LZiVk7.zۂw>aJks?+=MSuYw]roBo|#GDab?4!5- 맪meJݓ^{uɛ3*Z[5}hK+5ri|ZM+] ~O2WR4?rp{{̬\(z/R~ŀ_Nͅx$k H#})">|˪ը.MUedTݥ.7-Z8M~Q1}Ϗlƫ *s`Z=2u0)T'GMigbRf8ϿȻU|/G>T[HƨYUFQd.fz(nѩu=?$UǦv##9аssgPB>~&QAĕk76n5h˶v'GJ|׸|ו1%k֬0??^G>eT7W8JZMQD (8Q%ٌM:ugk|Qy ѵtsCh>Kj&cT8ztFwQ_^MCBBjzHGATPĆ`]콣R) H{/IRH~;~\]!'v} 9Ÿ!D(=#$$ɓZ7wvV-MJƀRq܆ ԩd(P0$I111S>U:G )ϱʲ _xq( VS_e5Q%IBF?q'.*,RzFfA)]YO1(+2:5P9\E~F"f97eSe* #ae^NBK%jp)S{O͛ w*Vn=_&1Ji߾}C oC=tĻRz KhL!0ZM|K&K~=L*w2Vy$O(zЭXLXesyor6H>\RM]ƄRxRV1 ¼!%j}&/dC#8pZ###!PYv^⹷|nޞ e7;T *o,:J`+H&m)*D2VJ5vt3r'5sY$a~*H<? L>!Jk߮k2De^VD/Wx PaJz/};6z{xZ8DJoRy<-P_+%z/^8Tveĝr}6_}mRJr#1JdzvQy p.2joP%RطNȈ'00zԆ0d,!~pxxS;zIx1OA?^q"O՚jInAJ mzs5Z)#23JҺ9D)@Fq5D*9 p ED5ϻF:u& W9ѧV-V%ѭ~Trbeȅ$ZP-Tͱ24v&Ԝ_6DQZIuf!Ue~TӯDڗIMg]¯PIzg?$(OiѢ,Iu=G;U\Ͽ!SB{}D߸G&DѮ=ֆ$7¡̌5Eό.cOUJ^@*k09' N|;$wma' ̇`j"|T3 DɷWGAX3D}0 Df~֍$Z_518+TOm/ǔ;P=O F*&ri2DX6V 2㺞(۠T|_V֟vw=g%=<Z;IBNkTĕy3)[:{?d\1/Xfn <F.vx<cӝe , QpM()x+%BVI* ՙ|m,]i~N﫝Un3^6UEB8wP%9vU5aar"xh4rxJpŜQ#N A=G#ȓ(\'W]?fvӮ})ڒE\HQ*62>0X 7Hɬ_ba!@g7[EHH1(@%gK (Jd V*s]o-7sEncC*b29@,N{8UpAŁh3W$©BB NdW:*LDcPf.*wVek$N'2,#o 5j9bt!jBa6tj*]PyD/2*\iDUॄˌPeԕb/rVuUoRGN!ĕ>Ix㮔Hcqaa^5w3ڭRR*6V͉`0<֏}k 웩2s.3M1DrC&'@cH^EYyyHA` N&}QϡTaqT&*XUZj"*TY2|0#Y-NN sp(nѝPmhpX&$hF0/lAF]` `  hN2$UƮ~2o D$T`>}B_5eu2^2@$J!/iyrN  @ӷ5p4oo7 *lyȄV8jnےY';O,[{cr3x=Yf閞,Q% D Y=ng>pN2oV.NU|I{tXg"õŅ1}bLEoD劬]f=%UO@5MwOq}멾0G?/\[Es]㖆 ={Wm kNIƢϗڷfjڡgҧYdeSVTh!G_g`엿1mrP˧_b:|KlqȒ{r+$A-$4и^Z&3Gȷ+[r:YBmY0s/Q=c֦[˵ٟO21fpDeo`(&ۥO v?G6:es.K l@[z#9I Rs;؜l)/)fjb5gCnmJ!GTxF^(eϔK/?n-љ=$fݗ0VI%j]& :cΩ 62JE O¿mP|2+_bO0zH@Y Ҏ;o69o\ݡZ}[ټq Cۜ\7o;,/ka8̹?ucFFE>nJe ĘV잯]v1;lȽS+ֿwb߆A%%O<ԴdO?kOJzvE8k~]j'ǎЛ2,q84ʃKBIrɓmYW1lAf?b kWl+-i#jyy۾-Ǐ 2ro':cߎM㌤\ *IВ[MQ&œӄS6gj=?iOAk$\zα]9d}b]Ez>m~m}*SA%n,PvEuW>%c<'\Uؠ~ r#?O$QΨyu=-9 08mOZuhdxC =\i>ϙ4T <ϻfѺ67X̵fcvOflIrKV/&ԫCg65|XfmpM<{zc5s!3N,7 }CL61N䊼,/8G~\B8G$4L1DTPJ]sBdpڲ32("NksH2Aul QU`EN]-F]޼< n;m%ZzhڼeHNߑkvcQ:z0;*uJE>\p@}NfY͉SDH G0J kt>:S)"2&Y˨:," ATDQDy 9e RWADG2#+xΨS[l|6@Щ҂2Q&a@3'=mUZvy mwB!^ۑ&#L) 級"#)b(˂$1L4j#,3n(1Ψs$ʴ**RNUFp*}լEֽyNr 2ʘj%Q"Ĩ'NY輼KQ='SJ2κ)2g$22@XZJL;>(UZGyjs5`D@THܲ˸au9SЊTY1\vmy2û5>gR5/ "W% <$PrI ፅ؇;孷hKmNIsaD*#.ګBP@uKm4"$hOzYێE'S,F PбQO+l4a` 7s^lt Qac:1d IJOO|plɔ0Jqabr~3+>f2ƝFsFܡ3]fB ]{5 m@EIa1ҫ%y#)Odg:&H{N-sSΜzMzjD$QH%5!ZcSy=b fmCgZ &D^>KGC8a !!&S, +l8|X=}hbE9pZt伞qFK &srǺw =b ϜI A#h5BT2^cmE$Pe+Y1 )m٩3T׉ձg._CpEm,|m St ]R]Nxѿ->PF]uI1"ʠ9e3h-_"8ziXXKV xxR݇7%nk~zL[JuHv8+Ky&ˌE|v׼6lP"ol22,L8o wڥvzZOU!1'dY2LUl9UxQbd 9s@wxNQʈLDAU?hB6(TWDW# W7=?*&M(0 mTTW(4]СCJ~Z6=OU?cHsM56a%K֚i\Ǿz4|z;҄'92$2;u|[eiAY$JeFeF%Ykӡʵo~Pft˜?<5dܨ1,^pBc10EyI^`&Ci4A8;$$DVsQy/;Rx줠Q 8 NQ 1dAA!r+DVq0It2PT s9DBDTJ۝P*QiNmvv jE*{.&UTvN [Y4I^FbKx^3U Q*ؒy߃]^UD<s^vu&06*R;u^\s<Hqcv/WjzDz(8da-: k'TP\mfj-[S IDAT!SF5MAHk-mTVtVX1j2UplZj jUL?EJevpe*N]~m\z"5Z}m=&ksffe7(y/Խ~b5)-+ Vvn5hKtڮXk'V(,LeסּpWDLSۇNvI5KTu@TB+VS+YFDѻ_dz58꿽n؈ SkS#f$!lO}sTY"Beg_a!O8dW&y?B>y'v}b4rHC38Ծ &b5B)h 8h=<+m5yֻʬ=?:l[|ӻ:lؐAM6B6͞.gӗSGJZs?zaC_r}BBBs=rȠuYvbL)7g#G  !bֻG z˽}m`<薑O).^-BK2Ŀ:ygۊ VrÙlϺP &VK떤y5\Z]AOBwYv|tºٳu7lOl_,!:g3\}~5]E=|[߾۔Fq?4-vSiЁB$DZ0}r>n2@o}}oe;ΌMksSBSV_yl$s{#0drmP$!J%>sn,|kqG,xa[:4J-נ_AkH^S{wϫmB.ux|ÛcZ~QG{>k!< 1f@>eBs1'0ql#1vM>tng={[s̒m~@SZQTRE7Y~[-{7å_j/,T?_~yscG %N,W~zeYS;eXZ޷iՒx8cɟ&F},"|ۮ]k:gpoOk5_y}k}Dbw׀79@Oo~bj?J"rZOV" 20-Шy2}T'$Nx꼞l1GMRi갸a/8Љ@RI(SN93Gk2ظXً #|x %g?<wW{J MO5]d}O4 3|եSI_lRw;á\<-N!ӉX|8i+)XKH}i++ED\{|X6Lϓu I=j84k]E9cb @NcG^ KK{O⷏ku¨qY3g.։z^]4qmX*D "+fr*5ѩ׫9Om-AΙ8_pe8q'[i}Y'o]۷.1.p֙~ߤ_۶}0<:>@8L"0*i+>i XDV覎|5^P9M H1N1ϗ<%n}8f6fweJ+rV89AO!ᙇ/g|+w Qs_ мBYz>PyΎn>Cuœbsy~ڙ 'yg^5|i]KG&Ȱi~zV4hӖfl;߷OY'fdȎ?>j~ݫ7fb?۔~pٞ|>ѽ;$%%<3[. 9?Z %IrOyFWtJ mX,f\+˟+Z2x!e(Z3g~оfotT廰 PĒտܿmuQG}3Cd| jVw??~Zrۧ*'бyl+ $`(:xfM.X7N4xY0Y?e`ՂzwEXr~ON#M<5k+={&˖={{{cpI߫ig_ū9Am%Z9+lY̱e߬sPbw5ol cH,B-h %YYYōڵ  l̟XDDh۳_Mgo;9Gdr0~HK== ki iN̍oԠ㈠C{3d}=XxP\\q{+BvFFfzax\&nQ+cz{[7L2^ B!vnm=Zp@iiihh( ";gڷ2gx'~֎aMcEqpϵ AVQP^oohTZ l^P5h<Duh1=mߖci٩[lX;4ں;6&ypњ GtzLUB. TJ6'me'/V䧤uqLMMm֬YmFNvWv5g#'''..5W^^n0l)wZQ;q|O^KX;vμoκ Ӿ/ݸ9 (/wŬkTF4i=C.sXPtyAa]c/N137#QԠi|NhqB{Q!fќ{4Fiq04>IP:D͇845y[}tҭc= ͡ Rͧr5JiII 򲲲Z-eeeJ6 .Ǖo *$?-L!Z*X)ăZ)uZnݮLϨH(7I <OG* $e]0 Qk]&$#"D%(""2E(P@ai 4oϗ8.Ac vw]FE?3@UNQ&5en;q#Ʋ9 ЇQI n8?+K4 C~`0v,HoU?#٘mGMQ_ϭ"FҲ_2͚ZVN&(\ (PXjHeUۑpBbc Z=HAmF3O.;x3KUC"5B bV1f`[" (Pڍ!5BPzץAq^rσf>zE!:v34 GՒ#sF8}T&AqђKh(k (Pp8@,?n환A݌nqtҕr0 [e4r:(DlŤIj *NOsgH$NI(P@aBP >/m7;owԪ]VtoK[UDA2Bh5$ o1:q, 0VA lYYVafuXR/PWǣ{KBc4@t}5Ghx胧V}+/t2~SMm2X oi sJx)N.GΜ>Tm9}ָG4*O8C{sw^G$_ӑ;Ƃ_wXAF?VH=w-ѰݴchwÃl1tڴEy5'l:xY1ګ19:swbY#;XSњמpڬ Sp׫<Њ/~%Ԁ@@Ѭiw-N8;?+t]I(3/sү~:>H'gLfĵ&Q nb 4}Zg~ww8$9"]P)73Bڷ@Ў$/Cڪu:O<6鎴Q5s4(:Mzڌ>D\k~:acTt?WQv.&GdS"ʔ6W煍&)3%lOmx4 >!u"@=gǸ3|7+ÆmwOh%Y;tW6kԙ'='* `a~ogmػntS_S:BJ>-@QQn|GKBLE0T^\ȱ7S% UAXeN;f|(2yULPc~) ?۷yNx |e(Vٷo߽Z:]9Op,Z =yuYj [6.4w/-T?=Fڇ,}*s>FK@&r|~v!s,ViYw|(Cˣޘ?j͎3]& b'3AcXDu[Y7ch,\3ÿ(p)-!@P\{nB(SƉAnߣy&YkڪuC5ZD9;"Xb_3)uObE:nqn5y\ωma*!FLfH1M:B3ң&ݻɖ3)goҲGd,=-#Y=%c=Ke/ysHפu@SO!i=_u@jjjfͮO 3)g"4neY ~1buH 8B ==QFV)  eOy7=WA7kkQ晼0FlzL1ЗfO -KNp *uP0-I`fI3yLܴEC X^^n0xW~p>tLBSODb~vL?rX\\(;Ҳ˛6S14ifMZ)YNۖ'SKm;&jYO90"rPw&o֤ops[NIDATgSϘ%<>T»] ]sRX:,<2o6דVSp5_|%mϿGau zM׊v Y/56j j9]BpKa (=P~×^tr-掌PFD.^Ö5~\#V_@ܪ&$.!I/ݶs_ZRK nZ$6x-OjҒ.(eeev#ʼ\h[&IڼMiiBl:UZ-SV%S_glkܺLϨH(7I Ƈjm``.Sc:QYEI.ntQ*eJ$(1t}&(Ea޳uʒ(J|$I%(K('.2e. }zCfIKu\Rε5^擩m]ZRI.'K2SÚ$6]gD ԦCfg>&;9iA bƹq3oï6Tggi|ig tii{vP g~{wᡬ.<\u .zwɃ|}}G16g[7);)ڜ5OYf/q4㹩o;t)?v`_K VIHJ4HxN)3A׫Djd@@=TNtZ9Ǔ[}̌ZvUf/b78y)Etצ7h[x\ TzG&۬vZ_N\p?}gO_E郩|ٚ" &~Xy}Q>wό{&ၯ6^GCb;+;\+bAlt䗿ĵc&!|oݱ{W$yoN"%ٕ댿|Ǵ68#X,W^t-ɏdOXD8dD ,i>o =eqqdP{!q @%!"p 󸶶UIMউ .R(*6d $"8֪aLk_.?4@dTBYrJ~n{Dv^ػ 9*_7e'QDN,yg 'Y*YdȒ)r2C&NTksH8՝OLw?WX},'VL O=EoN޶P[p@E ( HB $Nz^o[N>}}ݫ랪St9D# Y c\RRҽ{(rU0%DnZ )&n"-f >WĔdq,??I`\YY \?XwRҌ"V'$Jz-LiWM%=c~^ulir:N W0% 7KjZӓ׹(gJO58uAMLJYkmr3HB;ԐnHI )f$],LTB N8HS2[#[{E!nJMSQ%bbvv2fDMĔĘS1' FK1+3:mABfI əMMsïoYHjϗҩIiY78ێt,SmRRYvLYh!̧XMIfk9 陆Ȇ5=-DyiL4DJ̼@;O&bHz F4r̛^4kbBul&^%XsV0ÿ/D`t./;3DII+L4_g"OO)bRk_aꑷ8KNN&&&&%u`$¤ hga8l6sP|BHrr2$4ltf1JcU`k B4\3m"j!f j뽡֔˯86#DV[k\ 0jSo9vAg}]mmBVqj>EHrlno(rN﮵Ի 7 h!nLVr&^}m/7B{@TiplŠՊ^_Y3o]Pw˚߰\kwjG6x|glxkK6,yu-}7rWx⎱OWC<C )1{g ]o};2_'6/vE5F4LU8cȮ[}ۤ*~o(oxu.GM}{vLc+|sܖ_|OeQB4W{#!RzjG_%-->,pyi'))6oOji# 3颔ҷ{BQJi'?)rwJ)w R\\L[sp/Gr?5J' eᥔ~e?l3bIJi潱sߌ\SZZJNAEEEP1c[xKo_tOAo.vA)`аQmSG/65CgtxLM=.+Z{hv(u(@4 *NH(`0&ԋ2F>3]|iPJ%` DyGѱsʇ-HŀB~zYT}b< 7z\?4uky0EcS} ,>{YUо*ǀxDyu4P05I:eGor`k~kyf cђ>S Ta߷-olzƋ01.A6%.޾vnٴQ0tngMF|jYQa3f>*\u^z@I3g/\)Vn?8Ž|YQ0Yս^޵i ؙܼ"ʅˇOvYoR~+wML`/<^;[7zi+N]Wt u#s/O2q)ph /f;qk\m;O]rP]>9CJ<7'ƺKovNK׮6^Eޖωʊ`HH럇v@鞀_Qj2O]ybWl׏x PrL4a>*P45a̗wjf  D(8PH%(/[ a%6&Jz $3~ٳgQA!D)tz`J(UBA *N9C8A' u֍IPYY >5xV(LQ8CJ0(^¡JgdYwU 8CxJ(%DP( t5nL43tQ5x.PMק$hNhUpѨQ!/x%('tlQp;[B3/z¼!r @ Iz} 1 sI@Q/4d;>h0sQ}.JfDƂ`0:# AcF])O %ʠVc0xϛT_~1.))޽{˃ZoD4 aP GRa(h=qBc\VVϤq1it,L-B)u:VF|Ccb DYl5h}(HG@B@|@| >@2\nXXXuu5ǃր `[n]lYYY$HrA]zu͚5 B!Cw߿lhh(aD"@ه`B bcci4ژ1cdee!A >@L& !rرI&h#F f D$ň#D]\\tϜ ZYh<X[[3/SGս!'1@~əJzAwv~n~ÖlI&ƹ!'~2.YM}q5/R/udz`0 #884888==47V{?zbڍ Ѿ9L)*&G.a°Ȫ4/)R.jL:8ORTtUgAP+7IzuGAAѣGy<^@@ޟm۶Xg'i^6pKXu{{;Q\J9+Uڞ"90ItHrO77q`Ō9b@p8booQFZL&;:: M) xF6y?,PGr5ˊ0%4n!a|gwݰa=z9 ^w,B_v0L݊~f?uCKkkkkk+S-DU14Vr//oal48(21o9)/Zʣ)9|C7% =^ȭRwXK91\Yta>>#+ fֳS#}#}<-^8:ksFGN8!E썅GNz݊侉dH y'#}dXWEGBI;qrlJ+qR"&0LwМӹ&B_㬨 hFQn5BVJ0 qޘQ+dP  Mg3/RXxBU ]*ycb ZާByyyG677޽{DDJ 9y$,}=tt4w}+寇fᮒ~j燉WQA1,}5I#(d2SWpx5EZ@(3ؙ76թf]TaZ ;^qǭ*!Z>\v_yolw&?{ɩc6>i(쫸nN=-WN#5Ix5iۯ~7ba'c|ʌC҅umL]ӎN\!Kd)vy^ԋm=foc?ri%mʌC 2÷N?ueƩ+^Ϊ=|9"ȑcUG/KJgS/ã$~,\O};}H^ŝxIمn8/7!֕D_qfAbc"QnXC/U­JWMtQ3l/D09)a#BǏqqq)))[la2s}ف9S#ьy)3ćR؜Fxy?6#6ֵp;YU|~z 3L -G-["ь'Z?̶ݸsw5Y~ܣ}E_2oa- \ݱWUf.k)Uð5=Wp6S45) HUbnFw﫭Z9,1v]4=0؈[p#5`8{Q> vGOk>ߨ3ȫr]Uw]zI<,by?ب0F!^؄uT#dM(zҞ_t۰uhvnu;m8/,^8a{)+i[ ^^AKg5uECFJMzuj׬s7ɘh#O*:735oo-#dP'J" b㜟 =HPN-|´ͺOⷂ|+D"/"Ad!m)8N1h_A>Pރ b3ܨ{ ,*RUƮL%{ YxjC$\sӐ#n2SUjv +MC& #ӢGh8v)Wt7 F{5 $.>"TRڦ$+Tgj:R|ulY;g.-jCC@OJơ,,aDܮF& n#n?ۗ)l㗛ݳkV У`II %ySmXэ)H+mmJJ2ܞċՈ_]|6^r_w-C!3evܮ"i:"6f1>]x]dk+\a|]uSIg~ȿ w Z['Տ33u ]J;(ɸq_z_qEpӫ\8G ium"M(x;(;+j{}6Eڼ;oҠ4 IqX`׋Sl{ zlD]Waج'MXjS0 F#ENعwn~O~Z&/u|tgE4ɫY_{]7Ŀuҿby"!õa1P6@ jD[zrS̫VhiT ٹ?YpuAkBivaڨ{h mM=P 4(Dd9v ¯kIsoE>NJ:s+J7/?B>UҟmB6w2t aLHIĩUvKn|BnEa_mdh [gl^Wp81?4Ug׍T!\σ{*"@bE C^>*up?;w_A2mH!h %GQMy O6@ d*ƠY*bWWLͻ{_S/IYv ɵXI+p4}S[G3y˙eov<=Dژۂip=!tA観IDksFxTq-g|E@W??͏zIH ^gZE5W$ޗ\v':X]UEU"GB_σ8z==.uE 3t6t9dBc4@ׄ?E^WPAW ж.M& 4?OI+)Y";D-,G:bMV;^[tA-,-|Oַ$Oӊ6Fj2BB%Z {/Z\__Tuo>y_j4{um𚋯k½DkZҶv%-Wzr=rg,|͏dS"_$br>1 /yU&;^H <=^EvE`iTU?V0z酄KZ^W_DLlecmV|Ѫ~I6I(mk 56ƞ5Z[;͊@H<>+z;D0Y_9} p"{bNoJҦS+sRA] z!}f++#redMTB(>xe{k%">h#lǷ: |#hzΡ|j$SV_4LGho-SҶ_J?SJ0iNO4x߳e4:?{FqO݈;gƦW#89ƭm08V/jp\OT{\iG!hfjG.H:ZVڹB7|_7AKmDG_S$BʹI$ \<:qǯ[7R/_a#׌5Xe˨O%>^O'tx hfڬi QGͰ-"%礨[|` "8ؒPTݪG0hX\ UL+BHŜR0W㍳ht.]436yZꂤ!DGDr _87AP%~$ʰc [xV94Hp-ӛ9[&+?O97BPX#iR[=r_ Ck+%;Am;ǎIK3Co.HZZm*DGDT2ә?mor{m|9s x>Gd9jq+G˄[Լ7m!:*PC2j:jru=zz$̴M\$=- gQM(x5*Ue #4?SiYNJڬ#ӆ/:v!DP&rKf .>NXk/g"mǿT`/,IyQ&oy:#邤GjZM&M6D}>͏ĺ v2^T~(`ۚ,~b& ux:yf4$q@рu*\oSy~Sh#Քg'\S=WOiGtN^ϵW" I?n0@Z3j= |.HfF`:8@Ň H?i0n!.a\?O#du;:Eёڹ aߴÖ[v7nc1pcنCc`0s;0zFvϱbލW[) oZ+!/0V{Y\:H|40 S oYsJsJnZ:į8)mwY2ڵš4DlQ#knԣSg޽*JA@DJq&l[ zqm}N:թ#[qOE`{VFU]#R@i񶄔vC)tiҺFAĴm9WXvO)\RPcY(v\asy(uܶ2SZ:2RS\5c=qާY[y$%#S6y!]4ufTf?vq2r SՕx-nz򍘚Ν "=-1{쌴$)^@6F$ϻ~`Vfo]>?NPaq yS z'(ūK6kXx:>HoQ4;sN:>9oAO< 59]#[4y01k.>>ΥN=oC:MJqCQhFa@'P*<%i @J{b-WRʃM` REw6o. [,#|_RJv;wB5)P]f !Dz"uM׬....-bkoR׬....溸򿠹k "6~*?EhRDQPC]ܛP57TZg}8yֲ-U/2DWF~3`VPBy&d_ꌉ{)V䨿|X}?g eD%R:O)7>>e㦀lh ld;@ 61 &e뺟...{LD` .#@2.)'/Rs딸ITلϷ}:OƠ9 %.q)ZOY-ӡSN$slZ G6~oavh w-=%s}%\.$j1qi.?N"Y6廍57zGq+WEHRJn_VU+'OrOIkv`9I1,)ܲz-w=;aneץ7:v˾5g}zA"BVf?f$V`NeٱG%3.X۾}nJa/[C0A!cTf8a,WG$BEZj {u޹v䛋. 4/(й}jWbׁȋyzh[CF 9-9(řgt9cF{)M7Pɫ_xCU.{6{?n䈄"Qovs^oϸ >`S5t+I1L.$S̬hnXwirv|goUPW6+[gfpi}\vΓWWsQy9Om l+ NxoJ7o@r~fט[zǶvju$濫z|b;\pش vc[e3/:mPMUs/r{˪ w5-k:V}ϼSxM"˴iH,{.#ݏˋk{3cEl;Uv4ӓ!D8lCx:s ~Rto1"9BMQPRq) \mhΚ=xiv8z3ܩCͮ۸9wMe܆hm--HMlM++V|/>9 =xgكr/?hƳضik6UN(cL3*vF*l{c1޿;Ҍ`73^wÇ :mdqI >Di6ou gGfJť{4qħY(%3c7?nۦ*J.EJ@2S{~>uqukL}Q2Č$>{ʴ֒aulLnϟN0'nݗӓz\B¾VE/_~̘3ZgnY0Rd&2bZ\2oL%뀠eF#bbjFslʐdg&HDo\r:tk勉k;hy-=C #4+#vGȈ7SZ߳[jJrd]8$j U7[pqqq9Ws]\\\:(Rr .... \+A~.pikÈD_1'1R. `wV^B`d)Qsqq5WP41l[ ݎ/${fD@RU46k:iͭUQ%Шs ! кUĄPfl?GJ"y@RVk_W!~kQT@!;Hu"-=,EёLUK5yvTjqJcJ(wRJt 4R~ff.9] ֌1 )FMr@20;vɃ=ޝ:@|eIIIMm..<F`Gp İνbF^(-g UFw[ 7ՅlWOPkJ]?/y X@:!1.涷^~P_sn(D@ ot.Tr#?`]IIq_fD,ںY s۲_~R}vM'܈iBSyU$::!~‘]9"*L^![+7T8>kkkO8ᄢ-[۴>08"R~ӵ19h%\Sx^~e?dё|v6ƴxGMT}.+'^yujvV|t\:AAtOߍ2׽jS1(qɀD""D+;Pضqo9bD]n[y]R[[[nݺ+HII!0lݺz...[sLIN G'%q`w%'?agI(wekU)-}游~xLӢmZ|jXEc-[ N]7-9ȚBMjty*l,b(]N)ܴRzTiJgwL Qz; fځg1߉7_w=CKN[3od#7@D!)kSJž(>|wɽzx~҄O>wީiZ$y'~i˲^xW..G}`0/Ќ~)Y`!&%Y +`X_Ws+j*jZ%uMTtb-9Աc֭s=OB˗ծ^}Yaf~e'{̺57._ZT-괕Uq>}ӎh]jaG!*\R>ǔ3B&Ƶu*S|-Ժ?u0QDC9agtMi] xΘϖh"`S0IT]9KBbb⺂U*J8ٶ}"0ƚ<;OжmNvQ״a!\YUfX0lhpG;-jDԯuU`w/H-+:DL5&M%',)،=#Z1$Rj DT3v~_u7V:UD}Y>c3OK<͹m{?Qku&#R%őC (%RH)ğM8fBTVV)x CG^?QlmBíyL]]TWg!vCC#~B~Z_C´i_Eӫ{h%'`j*rrRO>]ԓTؘq4DL+%`ngKk[jͣ+R)LTǼ(oMS2@!=iSOEJ6i^{;筷izgy<7+HgjmTtث{#FԜq^ xhuwq9K)5-+))b93lqdn!bJJJn?mc Û&z?ы+uS_ ,`3|pMa|=e=E^݊uG[8Q))Vl̨9ڐjDaνf; u? +%G)$s!DCCs~F-QA9FZjPM#&h/~!ol3)))ճGjju|>}`  A1M7ڵkWX\?B̈Ii!$660|nE"*˲lǡ6wN[ƚ_EVIm[\.'/|=|i~֜'_f'_qfnSrѺiMgG2pY#0?[\a;\~JHLV5Ҽ zJL ARiYcn*1i>m=#viBJ ;oY9>32&qĜtK}x؎3tsGRq7߽]:wj/@ P]]z]"bC(TY]Pگ\H$'+z^xZMEdZ'5D I#ivYբ|Qt{ʖ: W~3|n>9CU#.y'(]:mE#Ψhi=Wl_p8p3]7]!b\Ͼ\ }gYߨ=CkiY@PdMYBJ Czo?p߯;lٯ<9sG5]+Vd'&thA"= 3lZLLHHP%D"۶ٶmpwBDC7N޹S۫mZ_ZuTmb~ÆKg_M!›M%Ġe@aDF|Oi߳;s '.*H!(cQV~q[DZ,˶PYI#hʘ(i몪6ڵaF -R u$ELg>߂۞rXA) !-h G`|,$ "^mE\3hnc#^@'<39A@7=9qh!BU՝Zx-V(bF5DK%ǟPPQ@R D Br i.G[X5l* 4U:4S#5 ۊ0LKŵNۺO 9np?6ɱbD HW.#A]P~d4 q*+5 2b 2H5/p,'Ot]L#%w}_~®)\fps1tj.}WG1,nFmhUcBSUʥJQJ&-j QI5%C : ۹&ghʚ*J^.2)957+ 8{b w-}pz}O ##ReB1Lq B0Eu-!m@]MM3Fp9̟im)(GB@!,] '䭬#"Œ 7-g׎J!b&ea kfi !cBS-]55E%X159 E8Dh*ma: D0 q_F.⫈R~7@Qݱ͍F!Pqb%uVDj$Q('T2)Td6ccP\2*GfsAPeT__(,bgDm} }۵x+"_YƏ?{KnrRm믜=[D ܾgV]Q IDAT ?fl\ȤC&WpC0+A.)Q*(u(H5rej`HEsn GAI@ 2ZV, "-8G'v$$W't%Wn6YΗތ5Etď‰M&!혦doAUPIHXe!RÆxP"I RJ( !6CH%PQrJP__KH q\JMܹf,=[HػGwŏ[7"%juJHXAH!uA@mAqO=& J" QWcfTCNodUydE;PIS8I 0B"h &o}~vӉH!yLzQ^ov9 DZb߳&WvSpq1Ca3Ŧ1`H%@PJ "ژF&z=dKN8tTtl70 9UL:a9 T@JD A$1zIb;OP箯|wi#]; K٫@t Ș$Z\&ұ´ #!n8$dr2*N2Tdʘ1qzBkjk4)9H ..[GTt{6$ \_r{k0UV ,GH'H@$}?]c*1#!($HADǃuvՕqPD% EBPR)Y(AWp>Eܱ\;/!f$9wMq~.'-ADU],lK2ݒ6##(0Ic!Qm. 9swj_y๾"C5P]LiJ^nB 0 %ES֖lJI^&z|9NJ֫ <sF%-+B*9G5I2DGfQk[ThBjznH QIn {I3"M4d7S@2]Xn=-n[mq;ֱq=bEBk~r@ɹms6$ؖYTJqt,U)\GdB ҦQP+Ht΄J,nԋ{Lr4RDP:y^5(wy]Qm))$t"1Q.]uɊݎsrɌ9v/%zjG^N9زb6)GVy.@ !-6ć2# VX[\ 0$:(Z8i2BrJ'_|tGZ ayMMz2]z *AB A dH;vE:)0tLiΌ1E9v5(`1gCTB);ZO=!1F;t=lmhDȇ]J,[]6Oc~Ш TP"ý ngD [ pDV$1Hm$X.ƢL9D!xLZya\Ry0/i{0ߟQ)v0/0GiD0 *֮]W5߰w2}Ő|SgPm EjIZx~;GzﶘR v-Y23{! 03tq= z[F) U 7af´K?^?<͡H, -*F|KWA$͒=3*Bx֮Up$ k @Cu0XmW-Q@. Aty*Gٵd2*17 2]1 j +ZգSDz$$0U196+nYi_xgã3DLDB ?vDorz") UUCWk5Ħ@gxjhʿpWNݩ3 Z7<^CcL Axj^/'&m TF*`2EhZ"P^Ƈ`1BG{D7`9FS X^ejզ}Fl_py;{> ]u [wjybWMcF7D2gAD Ogt篼7`YRءuT?D"ɉe6ϮuQ~ ;U`yn[@D0* [LaRϨ1[gߙAȆLwm]tsڀMǭUq|_6eK]|Zb`ϝ\۵`/k]e<)?^Z[jt _8{JYlM2N rϖ|jꂅO26|h+~/ظ|&8ᅗ~6;.Lx?G@}qjխ*Xe}(L}UjUc0WiMImwҹCs)i1_~ 5vˏ_y z5hպ;q_W;JGLѪEFmy[߼'r8!k?ڹW/|<|k<{܀ѧ%6jϧB޼)d`4"$gEӃ/)?ҁK(dau6H:>w3+~k} :u_v՟0#]ZJɓ=ιk}3xgڞWWom2ڶi}縁WpgVSf1Ʀ}=WֿA$w\xϰ}6aM=bG:[﹫`w{t;jl' QbV}h)qY}eJ0tXLz?rϽKfVX$%cۍ땛~N}{b>zu2ՠWS]j\|JxWL@y*9u[]̋w-j2TY=fŝٚrsڏvJkE=w;jk%JΞ.͗COv-BMZs]ޗD y^5ˣd!DFpr:B 6?X:Ueym-^Ue $CBD SH}ˆ }{`;Vqx{{Qi~rw v4 "H!`K.bAEs[װNQ"{CcS;!T `cIo\;DO%B:lxr3W }{p~ڦ}zdzG\F ̜kΰ,{h^9* oT*%@(՝?7R~7!dkqDFޘV/1կ, ¶þv'Vi&Ž>$M ecȉzp=q !q#So~حcȉZ3+yU%v،;o9.z77il>v"e 4#獹oUF\`dˎDB0bZ]Ch{՗f%ݏm)}e./!4-qNC̬388]B,?*c77cԳp}yh'+Kfe0M wo,һ58 /={s7gĞv'z%0-Dؖō,r >˧^TlYYx!Wm{ZeV68nq9tqƽMg^NL{zұ=?nFX'pbiqSUAV풂0%qηFp)Da*ǡ9)x5NĔ)hRa[V];}vxp":DahᶤQiPu]UmK*[P&7<:eDX6' ǖ@vJ"ASaY(ƀ;a7m= b}CڛprW|YQAc>? "xqǜ )aYУ䣔,5wmEBumܸLu9RZU %H(*"8 A9eM۾8OhY&1|]t"ڶu̍'%zz+! w_u5B|?+W8cX!3iX4 Y()Z,%<0T8GUMzfOQU;V"gJS{u=vSm b5UٹTIɩǴ=憂ssu)e(WUUӴ]僇zkѕp="sbRvx8J ]POP\,fq$u5NpML.eKj]?v @]MM3FZ2t(pMP]K<)lǣ3%Z587Ͼaͤg䑆hO+fP +J"gt_Bp*͛2~a j( iK'TU1/ *r恃"J)jw7! PZHr8FDJmSݓ£jCQy4WqmnNn~G_B|LR214?z/?BޅRsLՉ5H 㒡ԀYH3}j^}>/٧ ao_?飊^ Փ>5mI *xsګQٺlM{o&~Qal׆/2 ͛ޚA0gߔٴa>7f8T!~4צ=\Y.)$t҅ lӯͬS[t]6Y}ި }y R]^N.E]8Aٝ>>G>^hYq7>hhO{u~cO#;3L^Ӳ.ݻW8熮RxKW^ d U1rk7PJ%*ƙ?_SO'@2a$??fs}.Iʧk<'G [pOrj^$U|H~i̬DikFmv};xoԩu[Q<<$;'7?~v]:“y7ϨSsW'Ŏ/ssw0J(3[zHJɸ`yit_GbӲ͟Z_fVJ(ʇˤc,y9jB~|ZIHˈ׍h񬙫uϡ Wgp96wݝfܞt^9/*ZfbZ2inT&|e׬]|[s+M]SFϞ<}/}_l[H=&Ž廗SZEl([_ȤYL̙=`r;>:d=?mg-+ڥG7wJsr;7XY7M}VYxɦm3fө{xâlxktֻji/U7ց(̅W0 *Xwl޽:,~E߭ &g2ڧƪYvi'F*-.կޣhi&]=j@ɍҵ-:wJOdDc -5mDvoܩR|e^&n7,E*Ey?k?-j쭽}zOO<}ҹE.Cm5KkyKW,CHQN0og u+P/:>c;շgw9j]=s2?x~WnUneX8ys@+?ytۆ$q7p:׳:e/wê֮YY?=aCj]]9d1Ko ;Է9+ik3ʗ覔ZzazCђn|ԯw4nO_|y7j~mq}~cg\u +əHsFx9Td1}Ɠo7BC iE}lۥܿ0>TttMVE=L,(umg(q 0IO1qUj;kx ( umbuQ c[ڼb)71rm! vUvr?aWMI89GɺsjҬS:R5m_r&'r*?aha:o;N"W3.,9IΈDEL#fs:sΧgDzz"H9,9/K؜) g>oޛ IDATסU}t~e|Qؾ_ EA.I}fM\kOa. mÄZ5&NL8Cr:YfLTn_Mv.))ܞ޵~xі-{_yy: ZUl* %}fOy䨱;ukh4nնOu3Wn*8h%O^§ \s&m)(+\| {E`Vt'}.4#߱kէǿ^؜6BtYu=4C^37TLtѥt{]v+̮ڶ)tfo"%%S:a`G&Lʅ+ao=-^^}OM)Wx᥎%Ʋ937Iyƒ;t @N f$T>/oRmbCN˫:1a@@Վ(:$[s=gl&{(eE`ޱʪlkg} +*Ss׮ֻdL+YQ{P^\QWs-Lm1|Ў5|#;7$gذ՘f ~]e`\8ӻY~ٝwN>kkYz͡ϥ|E-%Sl*?xÇVQ[gU?䡻.gSLnG:JXd4IZm2U4KKKF| ѪIDlܺc zBNN!mԷ?^Mr/X_L{uN1q毋qpSAjj]JϞ 'DBÃ%M{83=`y3\4Mр*ql俤{jCUU M,K!QHAPt pוRq\rgbU3 );۶n)D:ޡsM7w*o1eVTo,))I{n5o__ODӍ(3%5 \(\PDCQMq]f@D""%>qs zp%{F0 s|C.*^mJjU|AABG"z^hӏI.B۳#IK T'okvR(+bOAMQJ(pH)U$Q TPE*:SLhH HJNDZSSu.Xi UEQ4Zq8OБwk(؀+pwR44!(TQ-{,a@% )gRiIE BRʤR()-;mB_g?m`W3T7ƹ`B}>8 8MiDNK S`l*RF]Bp:f OHвJ$>a9"2 Bn,؈0oԧc!LDaRtt*A|~?tHl2+_̲LTσmKgjcw "~?%Xͷ;I (@JR@J%Rjh>d &A$m7R6JIٷgmBcf"bSg-?kHOM7p7qY.k% 'rIABA2f> @@5hϱ,35ޯ;[V䪬^?0hXfاԶ3XqOg׏ɟތ]Ξ6q3}+#+Pp g-T1!>o%wt8" QUײt[ 0sދm}A/nqS i?ct*>6umBO^d/jNS{kCv6䶳6?ufWUbriV+]a3( T]wy쪪id fA(峕Cڴm.G(dZ[8ų$B[xz\IǁȩnF̈I1hw B,H7iYEEX1ߖ⪴k]y>ʦ7rƈnc{ٗv:x;9}O ~iwMx14wWw|(?|wk@}ǫSy9ڠ)ȶU>iz.׼⬇GqDk/]-SM9[,g6nu)H7 GfBW⍍;*X:_*,Oayѭ'_Mm,nMeS'lWוϿ5=뺫;T ~)7_?G"y oibV9p3+_ymѳk3s/>d=:?l;5{\Ќ^K{_F:@F^zF٢Lo07v:_즾uϹi'X;Nݹk}I8ae.፧Wkz'بexxOo=/vS_?|wV?.k_z.~~ӈ;6R/[*^Ebd'jJlb2XREdE ~aF(WHh R"R8}IDѿƾ= %vBĶm&4{׎5s>CNJL*Y6^[y=|V׿1{}?6M2uFL.Ôm'<)%@6۫?U8n[[2PU)<76+;JRQ!XVQgTUUUUm;OAFTU=ڡP^z=׶wZ_|/H׼ΛӖrJ_y/ {k񦬜6o?pmy;uͩg&J-rǘ_lv^>.0ťEUxw~rՙZ|;\?j?'}6~steȳ `C|:ɐ.RH%wl]M %"J) p҇d -Ƕ…@,^8qOfȖesDB T^|絷$r9p/{qwZqgo1nLa}t~쇉cQM|Jw]ek҇^y> Wͷ\sr(!g؎qB/Юk _y]gf%r}G]_h7y;'"\f |z|#emg>4us_vmb/,63|[ʇ\=zm75y7hmE_=nko|_Q660n&`(TEQ%9b[iڹnI0ejr3_lמla( %SN]h3u]LKKkܸ|~] >@˕>pE߇̖TW e!!{W^~c̛  G)0>rsV-øAc4@u k܇ o#]<rbۯ`X}m?._ﰛM ]+ݭj1rgdiĢǂ<n*q%esmK3l2}e9h:dݗ/i @B1|nnDsh"W U44t߱jLyKޑTJuNB>H׶XW@wcﮭY6tU 8gR=e3駪۠TUV9zNœ,̶4Ӱ,GQY񭎁2~B kyeK;j;$4@zjC.XdK5;Kw,ڱv]Ѯ Si.NӒ4 RbB(AO}tyqq1\QĄQ!O@ ف: @z̮v~7` Rl@nv!ı=ǁpqycӚ@ Q.,)t4:qc\ . F# !c!̩sJo{M<T[gPͤ?D~a|;r\*hƂ.4]kP渖F6;mLsrtU΋*(MԄ$,Xxͦio߾ 0jt#zi]7GDuPJ`t);A#`BU򿍠ZAB#clրs\ HJ@)]Z<)j܌2"( ( E~}{ 8! B(MD':V?B6T ѧxP~A,1bڵNp!׸]!X{^iE5PT?>#ִ [ȑq)(D QIaPZmL2Z1˓FFMPU fqk M_D nu3޴nyqQ!A LkѢmet%i&vmx@B\.ڛP3(#"ٳ,ƑHC#Ad !Hs䷈1r,[s]!@(mO4KG͹&$B" P!r FJ;,WR^Z-[wiLʸB9jFPJ@T)ҨCiqSo2,5Y}piRz3:e}K1!n !K"eط07&AG6W"|cĐ3gD`FsxӂaCRH)!% E 3%kWorHls &Rfq]JUkduD(,ά-{o+N7" A'f*ȭ{3(A8$!$8*hykzq s;n'"u\!ڴm.%hˈIjL@v8j /.\-f;\Dt\0ep/Rr4K:RXh.рR@"" @ PN]*5ԉIJI!*TQUPwf~]eo^"=ynPw~_+_F@[Q/ݍ\ܼ[Moe񊍗?v~7~%~37ւ@OaBWFɱ1]A{ye(*|XrCtӽ+kqm?]y?9u]ߍJ}^7ߏq>ar<}?_R'q=yԗ\\tQO^ߞn7:fe '~v7Q Wr JJEizˬ@ikZ~VҞ$:iQuw/r[6yk3m,hl_lJPy k|+#`UI!TwIq8_K@|-cRJ!s9gqιU$>9\U( U")(jWc>)\`屢U,ʊKDum3\Ymve帜sqЫtea@9imӯENn8Mgq򤅔Qon6O k5}lĴik:XvUXyv٤][#Xm8jˣxau-9"Я_ց Zc}_ ,1-#wn{<))s͸/」{\yOs&FȔ@( <,|# B*k2oQ:V]"拷WrwRk](a"g.ŤL{bD 9 G 9H.%%Cɐ7/-ñm)}QrP)[PT%ppKʲ1t{EA1+JA!.MINuAtSj$KUc`NUUB+x̍jdsh[|{u.(E^u~9We?@WLt̂ܟaZpvyUn|_ xC[B2) C7P0{ xLÔRJɤ8Z hՔӁ30,{/8wض֑r<'}6fv,g&%wb~yc2:{wĽ$-iک?=wŵ`H#Ƅ˹rq8S$rgmvO,~8Cjt5KAfRnB a9Bs.p!O4u\;RHkg !j*!y-⽿s}Occ[TT|xXE˩4Υ$,6`&4$X<>B<]o݂U4tLh-(G _Q1|J50:ntmA4S Xvϰl & ײ]] s^-3gSs'\ )7uێn<,ǖCym WÉ"837zkct, (r<$`U7ÊMC32zshri@J$15b3 s+4 8jT i1iv5J&!GPk?n/jՊsQcADDkw!ò ]PB P TUu?4>F,>͗TnSV H%I@3M%P3F5u𛱦STU5iPRJ=K)P7UNd$\_v^aPm Jb׆< 6 Oe~{'{&\Gela?r޵`IǶ_!z,CQ.""E^XBHᅰp,U=ylY" Z Ry`xb t] 8UU"U(}3)n9  h Q+LIP4UQ *UP*JЊMUU9 uUBDޠBmU (*PUJicG{6g洉4F)JQ[Hb(TrgMD)JQҟӁgH)5]C)d/*~m)g)šRaM(Yis RT sҙ- D R$럺s_ou>B]UEL6۴BOWHMWU0 nጤ놏 S7LOSf8Ln[3wmPnǷ[Q4pǮMIL^M:cDO7Tbyl9'ڢf[r1Og/v^dtg|cZq灁ӸG/aS^Fz#m?x`~?h|zf)<ǧiesk9GLBdMJeR,[3 vijE'P"(NE[~%K?t)]Sf$%W;isǍ9mG ܤ)Y%KU^ַ)%MӀI{S򪳠 zzcOiQurMXc= ձ=Iɹ_N\cWlD?Vj쁛<{?dynϾҋ/N\^s!Ʒ>DdQR?RRQ D֮-w dd Pb/KY]"wHL @M]rI)-PzgJ5pE# Lq>Qw_Hi?OzկbVa\qa:(JQ%@s6׭؏YU݆îz&ɗ>iD)xvܸ>zt}bض]aYށꪪp]Hוgϵ-3- O0+ .qs5\dFl}'j:Z޹G:拧"o NJLtn?ʘAOǰ.72(E)J es;2:D +kLr_( Yճ'K\%Qġ4a[xzL\B=4k|*um8 tT)0e\NQnPfM[Emn[CzZ1'h"~,x҈'Cm~ap}{ö<ܱ١ ]FwCD)JQ:6xZ<1>WTHҷG;~Vxڭ(El.Tg,%:u#]7F-o Fbquc{хQRՌGXUQG)JQ:hkc#'3Lw+p`*R_+rʮmUk$Q #`h-=-hxYTj?\X}F*J ՞U V6!j<"3KD$zaJ I)nŏP9߽b}5^)5ňG?T)^HOFERI)2~ y%Pu番x_BbjHf"8&0R<{ ADB(jZ<(Axi"KT!. += &zV3< VQޱ'uZMuR5:-%XZw$զT! i{jcNjOVЫĚ.%Pe 8#oP#L\"'@EZnؖ5ja$D@0ϰ5 H!Eif^'*%p j\4R[.3 v%[a'@^딜H+hԉpu1U4 +"-$`/9 @T]`/9. tmrgv{k8"\W[P! A TJ.Hču*E/;UTE2A5E2GQU9Gv" oPRHF6% 1%4*B 9 !DJzk&6_Ed$RȼWS@am.sB+$6 & 2x Ý]݆i$؝UkU aWbe ikDQ1 @PH F,_]uQC? Ek^B\צT  !1h<رB^LhAMw_tE{5U K1"XM| >+\G?>f) lWVwT^ nBumDK_ޝ0 o__ԺM*΄QW$9fQ0Ԋ_ jN#AMzP&ErU8FT !A{f鰁cÛ03Zlu85E<B'Ѧ.Ч{3䁙sDBBR ױ$5 GHD&:4i߭nq^S746($.XOvy3'nlr,"BU!AU?-{*<#` {UR ׵GRLuraFo5'jWfORaz-ݱ8UC2F)؞ ]B\hL.8!$턐!!Fhtu".&.ӠPXdCĚfwot2OpąI˶/Jj2.л{3AfkUH C+{ NE1Ν=]馻ᅮYzW]aOU+ fY9Ə9ms͜bc^JzLE骼iۑ" vw׎5g[TVKmݣE˹\.8kfs.s&K!RpRH)wnθLKMܼb4.efm.$JL$_Kl[bcI<&)GB!D%|6ʞBr. " ɤ+R )CBT+k4kPԹ$&3ؼrOLUVϟ=ǩ+WoZJhkYmشEMΊ -9b߶g1Ib`5V/X]?M]1tbcіuv잷puZvVնw*wR̀xjaUWiRNT.ݶf2vE6٦?~ɍS.ܴq3o*lڴp.@)QHQm{/M l_&oU[$?MS f?Yq˶2p톍ltSCjPsSq|ISf(>vZAaUM _ص~m~y+SL->s¦90TKk\_U%ڻW}B1.\vs4uΫ/$qEPR0Ʌ<\흿(75+jܼr.l^i2v(XY,\Y+̜jgfL(4efҖ9)ܼz Ib4yQo xlRJ)rʅrJjY+6%fdaIivZaQUKc&MҐ3F6:r명?997g x/-1Ii~ptUht64MGK<Nܖ9I )güWɞ׳7:{~6(&eŲT"snnpFPE*(TVu%gݸu[`{VelS%%ܼ|o V?99VY2NBkۮc; P8->Nw_z} ˞mk}{bu: ߛO+ݞ;±+ 2S -y+i3V1//-"|>$؁X&2oԧ>!˷NZ8Vv `[۲cY}lǶ]8;̵c:1k\Rq SģݲYv7`p~Ѻ go+J}:c>ҵ;PeG IDATWXw Z]mUVSBNuC^7}&E>Z2e͞^!fVS#Nf9iHRu{[q}μ$+z郇?vO]_Q68ץU(ܒd'\0G\}zΩF>;)FG~_X~Ѧ`rf2.9wUYbyvE߲of,q[:6sm I;7,މ7W$sލry7K^ZӃrUpgvp쟿jۥԬF關ջ|VuKM0;wc/ծ˹IzgL\?oiC'/H83r֢}zDe?'ѯ_1  :m#۪?0XqL2o+ra}ݲyv{T}>s> f%"PVl>> JԾ/\:4Y>u[boڻ𪊦?{MwA4ib);*R^"H^HGzPPBB ԛ[OILPK wٜ;;3;gϞy$ZLSvGk[wWc_6 l9DhӖ|tdb!ۘ?&(yn Bh.Ớ ?nZժVLV{ohҲi^;Ͽ}vAJs>ɤ{AlU-KZk;m@YnX3K( (JRi1V s=[ԩ79׭=.=O{ρ4 qJ<?"d&߭kDQo0ƢZbG%ԯ}7%AR)3(" b@Nh\$VgoЧtޫa, &mԿU% (I`ר[!bR  gJ Ҡa!r,{ |jP356`K7Pr%"}c^+{ޫ9 O(JwSb.l;t1Ql;z]xʬ x-Z_9O8_0#ẕ;!OTvbfN* /OY1HӛjURf2`i~.[)n߾c,`k 3$v"fDQtfA4wr-|PtRfʽ"}ɚͷ5 sNQY% K"sEo-+x'lR+1-9IEdQ{pIY.3Rܵ`OɅlR6m,ܯ^/,,pseS#@NNz?0$Hf,&ؤt{ N)gd0# BD#?Y.e9!\0`wUJ6K GzRz*Фy&5P j>W%> c@0N8Xٺ32BEl5r(QT8AiIsrb0+U]]P7gLD$u"-cm P_CiyǢh}DjeƵOcI|4@06y߾ Qq!F?K% D?},߉N)9{IWެ^i%vt$kP|2"Scã$tzo*‚JN򴲍Ba{t 0)l( t/V B>Lld0WTBSW)9E߿ aQUαvSFf~XMC|1덠qd iJ/wdr 8Tt),s]19%ڪAn bƭ6Ϣsq~)HBy_W 7WuڿtJrw:H\]$ƥAؙU4"c Zv=ꁨ qspuVK &b&ԓ>t:78٥q5׳箥hnR򝨻>2 :Hp&]vNZꭻ񴫏'j~2*DP.MuFUs%uhאi4^ŭ(ِO*Ha+ ~d*&Z%U27ce[VN2 [#c0߽?GpRvjꭻ%q̡8P/(k*<RkX4*"uqϒ Kf;7׺hiQYUځ9y^~>>Z ٺlV qs̙/(k{9>MP~*rJ '[UQnf.r6WJu+kNVѺ1ߍo<[\VT1tqvuĘ~tJ c $ʦM(IbsC-4޽amBCcÁc7MBªb\2 eOCԣ{wRm[TJR%u4ոdm!!DTLFH:QPXoT֥#ݾVOPa-g߂H9qbKc7 C>kT)Qjڶ]?Dc.ðwfX&/qL*U*}~>RfX.X'brei"AXlӛRiFP49KUĜ4 ^yVei"W넥du$ bxjYbw( bIĶ r[lplyFFXr)X9兜Kʉ ť*E6V_(#',E&?هT-)xS(?__;Qn+Y Dt:W/ UTfNvY[J0b$IauI]=--.]eKZط"n3iM٨7ER8³v=m'/2VYy;^{ByƳjz7u`1Ewtкxj]<]ёV>o~[ș' Go9|oĄS:2cpbӹ#oG{$p,SrQ?緟1`u{^lؤ̥mh3݉{"{~ӅnET#Wnͺ#m+ډ"_nr`EvN<~tOs~eɏsW 'd]T[hN{y d/[4oCoռEظ~' _;ysI~5N\`=&آU4g~QX9gM:w=qشͨ\xu_~Wa9!OoH8Bh!!]г9)'1$10,˰,rX2&~}d[ױ6zG$5ŒfV=ߠ… Ү Z\:r[#{?7{AG}ގܮ+z։$t`֨vCj4ts};>8*XAE?sQAjA$jgoÇlf̘9m9WOQ=˗3ȋ=z؉!u&v꣪} n>i2ko9]/1 7$m[4mnĞ}Z5doͿ}`fL]vMj_݇n nQ"e4QRX~?ڑqAvΞɘl_y,o{updtMպ;Lvqssp8"%kOvѦT/Vu|9hW+9u;ܯ[TNĤl7mrs('O7y~5s&CSJlm挷|ܩO5uo6W2dݫk19FbQbjΧ[JO$htJވnM"K-x;~DJ-[pߘUv@I뷨 eI;s/OJNk귬 bcG;Wz BmAS#@H~p1S/ > /:M2B$Ԑ #.Ŧj# \$+lW~mFwx۞^h7G =Tcha;כ&b=lh=R8k\.1!jV>A_|m.CQk5:Wt)!"RW] m@WOď~gս9C1j;k%9 ub|9K5:۾&"-Ǐ8w^%˜|/y=?e?nGk5f}O~cC* ~_w׍}\>ʸQgOo_kEV'CPɴzUtsFxka:I|+ǰ4jhƍ:{T?%(L#;Z=~O1lR5>akȏѳpuԻwk_v2$%;r(NXޟД1*{x̋DwmQi,YZ#EHpH?}nĚMĜܰЕ=O\@[=G^}oJvyǹ@3oXe'?4R2Jk7۷R~éVߓ5RFD(~pˎmYɌ:QۭRu?_N{oT1l(hwVBmz}9)mۼe/.s%Gc:vޡa>U:wܧfD"v,]'u|fi8:AhS~/5$ >d87I;=?JUPlX=!!*YzC]\Tݎ nA"Ű4x;S3N4EB(Dھ?~%W1v?z# -8(1("IFؤi'HĹ빯nUf5mhU`UzUYm {/-|$XN9a{o/{>t>d1j~wm/;O>h^5yf +L`3ZݮVbɆ~RCg9UՔNؕkLfK]7mϘ@ vl ;Ҥ?dԐrkR3WmhyDqrZD@׆6`4 }!ce xBXZ LJfAh%KLҨ@ь荒BhJy1(T V27Z q;hP0rk̻:g2D l4+% /Vs`Q* Hf-(*--ݽƹ2BȤgJ h Q)X0-B񜄢KQh.;eA}`2@dhLd)b41b5*!@D$R&QlzGt6vKir,ǹA4 0/@) F!ěE06Y(Ci7uhr˓u|l^o /FDJ0 Ed~xI&"&3, sRս THm Do܂-lfo;٩\B ś\.5 Vv/qV+TSY9k۶sb.xbL&dvʉлHm l~к='P}d;.;9Ǎ?4z±T!'t:f W?!oPIDAT?_zS2 Ƕ,7oUKj.i{OZ "$v([ʔk k0sBmw[oqhc~iN:8uM̌\'Ɂ?͙, @3mf\#| ñu=kǏ?35ɰz3w3s,N8{MGnn9I@)r^d^8wKX1[%JؾtvT/^ɘ a.Jg?8f1-݈ʰ$A'Za-~ΆH?ZA+, :~,IEy(~So;IXq^l6g궴ص{\mHyȠnǽ{??w׼s&"W\$7)ұNB+bM'55K*CFhyLhPBH4BȘA#!#dW d)|wH5YX sڐVޖ>;rhr7M;eB0&8m cԪq<̾[{l>ryBȨ?B.񺎈OZ*ۆ0Ƅ}?1K[ỲQC0rIJc8K{g?Emh!Dq$/zcfNTEǖӽiWrnҟ('޳rV=(֪Wu&c?b!P '_q.,<[$+f5MKP7o:/f~>}1Z4r\"s pEvwn5r0Ieh,޸M})ڂy;6t՞ZC@f +fodMA+p;ũFE(5uүDb9]HaU='lx~]x8\;ֺ]_KeZcZ9H2QLa3%,\Q@ B=@ѻw+>AuDSA" \9hX4RSֳocN_\0H* D2'jc(o{:$W`@jJVtI RV>^uO1JVآHNRHT [n~^M:'7Uֺ8x E3~n4{p=i]1 9QidrBd ё1Bމ{w2;P !̆3-_}W͜!z;4]朡XژwN[:U/JJYE ZR[ͪTV'{BrJEr 2!)\"('\LV[B+qJ)*nwrV*B _&?jėO\Xڶ?Aݜp~& l_7 =| 1KKBwB{Y?uI_?6XXv뜱5ij>8|Ĩy4g3?5(2rΜG  M*lL[0d}M\:qgEd4 %% S4pw}XZ>YcR+'>IfRVt(.`C[}qMf}+Hi}!ŧk:5NkYQuu>H_n{Á;9f>V&E?6n&~D7RrG]WjOФABZոi㭿rT+wcZy,W?xqO+4=\w!lG5:S֠͆oKǞs+.籙E?oNo51[cEtaٯ"-44<.}!gۙҺz׮ݼ%30{,WJ>\ڽˁv=MU+]7_j,O\hd[JqAՂVKKY;?/e'沒eAvb[ͱzouPK?t!^c1Y)Ǖ;tJ-%tࡪ{.EtWX/OG/qqѾOt[>낢qI,"hXn ̠pқgִ67ǧ&v'3xB a5˜C=޼c)@Tjb_z& s;i3o)Cm'8 O8).rSHG`՝ hUX`)VoMS4ժs@Mz}١Z9^N.+nXƏVq.BlwPwLYl\HPN7VO6ǖU:ϘԿ&fǹc~cHѾ&6jtdw-GL ,!@iף0imw7k1ՍacGÒX"{l+" rUҼ@(-?yq6i $0,C`q헇jqԻ{>ln)R!Н{`Q+*s5 OfYcOO Ewĸ1 ],SkoZd%I@∄qαq{^"/ 5+!E!ݫ5aٿ,8,@A2%p2Yl /(HuGPXZ ׭pEHLt4L{ MLGM9R_ͅ6]&izߎrVu[5,4ƚ$:}Dv;Ŵ1cBӖ6RIڮt}f|,KG?trdf"bZ3B")}뼭X3fj+|‏Lf `Ϡ 9v{soIF1]_"HPsi̛Ll 9}'Q^<g0d oz}ض]ǏM+Y\9յ=N=y~2]D_ۿn˩>q|&úv9bgs`vt\ޣY7k vXJBGKXՏ}iI2g.w4jq/ߋ!R Da RkX0XhE F*e0 g62'Ad)Q)hQ"(Zb zҰ4 HEcKe2 ,I`E$^қ$(%Ҙ4,|Xb Bш, 's,-F3O1R.âh d2 BYā)h"y J%ZFK"]@!LQRJR!'D4rd2#3d*k0Ys({P:S:RzlNpBulZB1f2`d2a0 E'ʂaer9)_-(Y!L|bT F@Ӵm֟sfI1 @L'2 M;;oZ(kQ~:!  O-Us?@hVF kNv*7z~Ѓ҃7gmDw"B]a XZsV1ØshI^5^=~;#BF|(|5 !r^M\Pp#(Û)hyuM#sX2,13y= Bn'6ij԰; ݫ)=i߯],iuuUUվVݴAv?{k,[`^}' `)!7othPΙ&RHZ_UF4Ύȉi&yo硽qeo,!b{̵ӛe9O*;:eqn^_NWM1&mfW|skQ.mt7okrm=<:y ?=yqf>>]K̯{~$e_3i'MLǜʋ? >`c!74;1$sv7t`kD0i;vKXN8/򪐥4^LftJX,\=f|S\H$ϫrL.`ڷU2 `L&u>T5n},bZ\ Qch*۱+ok>`';N sIyR@*'rIZط"nci+܂U Vq-#^#VqnھB-J/ʖTe+x LN|d';َ})(IENDB`sqlkit-0.9.5/doc/static/images/filter.png0000644000175000017500000006214211714210425017670 0ustar sandrosandroPNG  IHDR[sBITOtEXtSoftwaregnome-screenshot> IDATxwSUǟsKzf2ޙa *(*Wl^vW\]u׆+k^ւ"(ҋ^2)&dӀ1I~9i!U>`{$$\4$`0]+CtDsղO1ǟD TMk~t eu6`04[֭{LA@UZ`0䷥ :d`0NO8TUy\~$@Y 1#y#zhƚ پyd0.LUj! HH@cBJ,˲ 8<P/sfԶA`4ҙG5w`0N]pU5w@҆+{Ƚ;{]iI۝뮋r-ۥKD:Px0\ "8B'yX\Rϫ{Ȳ٢<^@EbՄ#[SNXO>29x  TeT=H*TuMDH(Te8=e "d" `񨬜=HNQs"DE(s&3<5@)O1[drGͶyEm͚Z{tuF->[3R7o>nptrA$$UWHS'{5`8K$BݟJ3k ㆗3nͯV.t`;f'/9ۿ; Yg,]Gyʿ$2ilIU<<6FulW:drĜoy.ꝈR}f\.~%4ɬW!ꦦM *c~EEq N4|5k9`kE>.!Hˈ6I㼈(+jBV+n*t G*%45pyn64UUXzSNչ@׋8,Wѭ5E@"`ϘjYbOK,qXL. 5?6-wy\â/A "@U 6cI8MiY_e; ˢ߱t颯i)P[?{袄l*]/( /PE2YX^׽{@mmI,vdVV Z Cy׭د_nܵgᱱyeEx\[YT7[&KmUOmebr:ZJ,.mb}q}U<M iDU~y/3,6@ylZÿdǷ?iv׌)>Oc+ܒʒFyҿ^a<[J|5՛6_:rGz6?bŶϟ$d=/IFEsX|ℱ.`4Z(/f0(e4a\}`=+~j6+ fG 8BP!C>/C4lnDZ&[.{`x>'l6dEAb^g1]0M5|,z k ^I~ Qt4A%v`$CUhf;'RRӷo4N06N8Q H%gԙS,*/ϯ~my(x{K${Έs&dsގ6j.㼻/͍dk_6#UVʨdqT&ԯ1BETEnR~^=Ʈ^:7::Z4G9&̖.K.쨪Xo-#݊g9'ԏ:F~[IFp u+/ EJ~"}sZP"1] 5U%#1 ^ Z AqQQ)Mm%qxAoR(&Ѡ\ia;`qZ@3ZӐG$)-5)raQN5]QvKV(oۺDf !TS_$)5Zu:Te@mMh+`4YSHIYyAAQLd6zx:_ӓ@HiRUu:˴w#"Y3bTUd !h2!њG{}ҲrrjFEY#hy :(:Zu:4`0zŲΡ7ٲN -Iq*5|LPl2""RY=T a`0NT&ꂴ`0L4  0d06b-9d`0K`0m&`& h2 F``0m&`& h2 F`ۻ.*2U :#TyUdE?e@n?>ݑז3Uw)P{ .X#F{,7zgĈϽ۶5NiW_?xW^zz F;ih|_?V+ ޟ\%G*/,3[py]~-y$SrIo S$;tR9FLHQSf͚5k8jj"R O둣F$$1y[!<pai#Gxoч.]ZiwË/-8v2&$kjO-\9G'ǜhشU':Ҽa9v#Fqs(+PX嵙3{W:]IUw(t>xl|']]b\G9'C񎁨yH,Y,66f-YleZmO>]RZ:tЪ*;xƍk֬}wvlaÇ~wUU^3W`f>i?2"t&};R* !toY!>Pc9ҋfMb_S8.(&GjZ\׸eYVC4R/k]=/^p]_MTK /Kَ/톌^i@2jk+W߳NEb^W^^VWW111 x>^zB6qi+țb3MϪ'X>ϲm㍏)| _rČ^z÷?|:RSrW3-ɕxs̀l\y{ލ/ Q~UI@]wL7.^e~࣒^6c7#{|\KVQOO|îzr'\;~['ym"Ek#Nΐ;%'Y7uˊ) @|^V} V{Kw(*ʌIU{e_ni?8[gw}omZ?#@y+o!eMin :2WA$xO=\A^AtL_UUm?ߢ>BaÇ\ZzJiޑN:j?1:6 +Ի,9{Ԃu?.uf-eǠ[g/mUL.~#nL2?,᥯"@;>~vTWZxu SŸ7GE= ~W_ #nmʵ_=ᒾ.&m[+f{희PmY(^I(K]7MLi,t?$WV$]P /{] ؁#=6H=5}|dN7Ft Hóͣg}#66ZQa#p:ZbJiMu1g2dtMy'x8gͥ :Yvߔ1)M;wV1 8xQ"32ǟ/7XJFe-Hn՚}Kvr6!GO ko{d͏gܐ `6f i~_~Tbus}Rm,q^wajdI!"/u̞AO}wGIr{D:7.|usEn%ja-ݢG!*% \Np H$H -0:AuUν+=Ѣ~r++k= E O1!oyhO^2h=+ ~"ȣLiXKx&7g2- ] ,v5)Jq M') 4X8;nz:B n)&K ]؄=- *~ʱZSX$Mlq*<4PT"ؚk; .U;GQxeHK4t1]v^YNM/BJRPk.qyCOq6eKiUVm[_b;$YvVf?UJU*UV&IS<#y!$-=5-=퉧6:,>Fz\uz_4Ұcї_l**-9z藯(iIxRiKe75y{өDVmO|Czoea␳2 J>qZGmTkZt;FQ+ I xh!5NYXϯɲƕ,/\~=ToϞ={fqW:z`_ݝ|fw**UU5LZS{'+*R% ^_*,(sݏ<:k9`H:>gƸ ?hCr}Wԏ/h;SKb^wEbϘ4>mg̽|o?3HVe5W:?Y ox54'"_ǯ|1icf_2-wcHnϫ +T'9|Ǒr_d[ [ NhRcݞ%<^ckc|bʅϼZOb_oxGA6shȣs=^obbBii$1,Q@⸸ꪚGdjTC~[IFdEI՚dG+|gĐ(򜤪^EI0DNq 6-h76m|YH,q/3o:z:e_ rq\O<|cZ9 @`5gL*M?\.y/I111G"$\nX-:]qfs9] [>K\ޱ=b*Gz{fA4l6w1r FK^}ittgXliIF)EDD3sɺbTih0)R (i6汱RL/...))qM7tCjJ@%Yի5뮾) IDAT)DZEq{=EQJ[ <ۊ|5URP?0%-Fɥ -,trEY+GO8/)[b/li)>yKs 7p=$YVdEQTER$/ DQ؎ꌓUKꄝCH_BsRc.EԆyk!$򂐘9tĭkWTWt}3{ڿQٽrch;<`DOCvǴ̻dthv2z'٩9]g[G,"mA1fZ/k)j[Jײ8shL4  0d06D`0MhL4  0d06D`0MheY 턐&30 Ɵd06D`0Mhl7ㅪ*A</qA%W~w9 6Μ@UNoIbD T)eubPU15#Խ!"A3 ` A) $ %2 @c@` ]&+MaV̾[]|ᶒz"Uݎ򂃫9N4Ǥq'T$_IQ޾][5U8pB{NL4;EQLMn1[) 6ǠPHt bS^CE& Ӥ4 d!ˍ$ jb!1?@堍(dgPQ;oŦY|tޚ;ᢩREE-k q 6ѹPVo߼hOJC zņ4N_gĀxaDDIuo,0 #>6@Й > M2j]9w AD !AU@D1&YvR? jb@PS+` ]؆U:X8Ú D2hiP Ae0MP/) X@/@exVpHv;HAQIzjrGE@]$Г"e pay@@DJ@1Z V 兢SH뽸` &jiB6eZE@hH51"V6jO(-3o $T pp>3j@REi.\E8±Aʌ٢ \c'zw߲x¼g" #!U䑠4>Kdу`l4@, pG֫K}0ݧ@]C "DK%KWμ]B5߽7RZY.rd?jóY=n2 Wn[6eOW/U< D0QM%2&Cxf(o+cH)?m&<(*֧_ Ҏ&m~ #mO'NM /LL=E0Hy߾yDL[1y6>zU͛g `{OWyni- ?#*㯚%ºc7Y~@_O _|8K+s4Bԡ^4=ʡozQYF&#DPfX뾾:B-㈐f-a7.>8Ѿ1@JO3t8ޕk@IyFB |2U8cU,IPW EiծٷU'MjWZ?GUs?sa\Tc^}Q aѤ+qs^messҢm_w{{~Sx󜺶 G#>2%9ER Y1gz]1湶,hӕ-n;oh*w#TH~.b q/#{Y3&􍊮;(>?RSͿ33o7)"6vN<۰vF§ "Epec@UTWӠb羟nU cO4+ wkFZ4_qCU g͹oP;?hkܢ;&0$p5CKsD Soy__[C^Қ~_ Sx3WIS i̵W xߦ;U7i^'?gJ,nBL7w[55!:sGdՕUZ_g!<0AfO 6P /.I6Q ǍE>Q7p#BH`Ac0@PUrR/=s2GY*׮^1gCMzͧ Z'#:gD>&ѕ^|;w>9IGES_Υ  C2@k}9YwFթ +JwI%{o㰃ɥ.M>Gy;Ocs6e.myݖt ?>lIs4#6aڏg66l߆^YoaRy[֦ *My Z ᚟i3VpBm+.fTZxaPUڹ,"gu(ߟosg MlF?]^e8(}usU+ ACfN8Z>q\OK6w0Q_y"qMiֳls[3ycOk4m!m-zzbm}n$"8pnX.̳FxDPw}vnK-@a)"bd64 hʌU TUۿo9)[?ߧ.z3%un6Bj;u0_}рEN'+7ȭX̱J~| 7%ț`,g̚C/?^7 k '_7=ᳺ1\sySfvіzD)B5g$z<(@}1[':gF|nVSfssTXD؋HC}4gV'{IQfCpl©Է 7mQ~[y*:w㟪{\қxwʘruGV.z˪_܃~q}њ0 lرcn5sT|y uE =_r4g~Q :Һo3g=vfpDzSL@6ڡӑT>ڍ?4'chjS Q%m>d 5vo|Kug{J}ߍgFᙵ~m; =~QߪQjUǕ۠4Jx ӝc[S^[3fb7m~k_?GUҬn\Y:LΘxBHL°~k(0Ǥt`dey]߁÷h6%vc^P]rɈ1gv-D!=pM:ʱAlIkL+/əCGMܺvEMuT.?d䤄 g>&{RN3XVPZamBoI1[|B'gfd Դv)&0<^pb|Ơ1ONF<Rڎ %L4Dg-zyN~&nCtaأ?"qA%W~w9(X9 d?ӛccRc2$y8&@TsWSl."LUQS [: ·&|9=w [&=|ޓA$8oK@Fu+I`L mx,!pĂm="L"7E: ъٷ{ˀ;[DnFnjTu; 8Mckԁ#7a|:p$uڼh*(o߮-*tCmopJz/Ghv>^b j)FcPE  K7 #RCCz@Cko/Vc0YTj\FMH"Be:` }H&#k "P9h,3 [8۴[lzGIfDT-SGj;REE-k †PVo߼hOJkǦL4;Dq2T:}D-_$Ț&׵dH$B[UX}0A@֛(aKndzο{L4 HU@lφ{`akӇBBH-tp$¹ O٘Pa1"G[aG"oDgpYMx_/`ТMI? Ś66]Ԛ1< ""*h G 51"-ɐ@PS+` ]؆U:X8Ú D2hiP Ae0MP/ޅ-<ڣhC洁ւ)x^\ӈafDTh B۔i E"!ƈXA0ڨ=0"ouMA0@mQH@i.̼g':G=~[l&\E8I8 ll}lsg_oY+hW@4PwoͧXS )#V'BLuconR7|]:hms J,A*  ^q[촵M<,j *&B\*2b.=?b@ ڄ9^Q©6_3.vt,y݂/{ˀh)柏&\ԠYw je*\TѬI""U{eAdߣ/,H˺rVnXAqn]%/\y~GQvkom}{ |T@LyT/zە {>4;-!&AkJsrwiAI 5è(y΃@B@RV~-dx1f.GOFP;T ~"A&fYMuOk.;E meL)tgNT Z;]Ujkj%' >{G2GJYڕq I>{6֜iHl ˅~犑 ]W{a a{,g̼[9^ƻK([6ǻ o'47!q"|>M7Lퟞ=;fnkR_sbtNJBJΐIUF~n9ZJYsV7} 0֛>U9U*#*9w$ր>ˎR{UˊPeTqNi2WU22gThSHb?U$HTk#UMgm=Fs{r%T(1Z*XVQ vT0خe .Mo,] Y IDATfK@ePe ҆ץ4C/P%>TzQ'a6 !T&pԀwE[+V MO`3Cm5U).B2ׅW=qόY)u|͵<騧)W/gf!=TH*{%N.uyc9r2zIIK#ўUt FT*TTFP"hy2* 9?ޝ=1-_D IgX;(WPsT2bdk% jnG۶~KntizmHO3p,l`!ǭ#*u;{-j@bxN_wvPxQFv0ة@|;W`ty:#b B3EI{n="ogG 5F3Chs Vhl1lÕ2M՟}7 !òmg bQ)-TY[Y/NBC[* =zI>1W.^Q{,IK718Z)`d64 Z=XV fjP$|DQ1zj/KzI{uJeq+bjuobC՞ºߗr 4 ބ S}ZO}u{ oSh_+:ntE:.~3,kw޲y%yV곟q9;^x Ϲp:g~Q :gv;BLnH2p.c|mѸ9.ֽALxChjLd'Fj}@m}YY tDRu>\T즠k[:~ƩCSqAX$zuׇD #{\^22>e%q`||Ue6azsp=fjQ % Vx ֒rWNjE[)r#4#Z诳RGkSG_wFw_}'y =[[1qB~[68ꪭOy\'ŦS_dLJ|rBvo|Kug{J}ߍgFᙵ~m; =~Qon .}jG "{'s,q/3oۻ^'Ky|Eǟꞙ̕>!w8D"⮲" ^! W8$$Nf霛|SUO=ϲF>=J\inuM "G@FX% uɺG\d&[naց]^QXȪ/O"n qjMM) \,S>O]$TOqy@Tuy ;O(Xwfݨ JӴi K\>@T#.Z(G )许n+6L8 f=Z!k,#i6$DKέ cUh=2׾Ƀ(t5&B uYYS@}_ WW.yT h^b5˳.JRi@0VjU1_'vb m^4vW.ߵuCYSQNw|) L hd K0jQDqEo_ȹV29۷NܢkBm7v}{hmqѬ{ ic#?^IM6?tBh?HVrCw ebpxThD w k"KjE#K@@*E,s؄#ެ T^[oh[X-ds$\4 BR]"(Vq7I zArd]V|L` `lH,ޡ:}ĨDD*9uTi\֏ͺQŌSFĘc pPq׹t&lMK&Q *Q9 g8`yAjjڔ ׷Cu\f m+qjDF r3RS;L%/]8y@,>!DkӾKHxE$)аhŊ(S,( ]rԸ5 8CjRkr'KB]/˂$UVگWTc̡QL4ъUWC4^=vs2tєˇNra^H?5:Y tcDEGYOQ/t:Okg5Y"Y$ K},MΥ}4 "TZ␿j w AD&!GV^3Pu8jXy:'6|o/zdHڟZ`.@yShJ0GM'2GyqD͉XRf1j\ʔDyaxS 2|KeA7+^^ Y~ܪ^ʨQrkR*DwN"aNN@ݧVl5wNM7H-UeDЌ]ǨzDsJv<W0K\ UwD֔oIt9<4XQL7\"D{M AkqY "cveQsdXSG]X.+NyQcoVV4JU@SfcuQ~`\MATrQ!@MܼYo˞VU!b5cTpA#[sipѬ*bC*F.FNQv: =PIs>*R 1fVIcK6F5 255?c ) h"M[`æ# Tۥ&&սk_sHD(J6F ɩ\4JZ }5s}lpd_&KTl⬂bI#rsmv0_ D;"?Mi9 (!@q_j=f|h |:eŚ_'W#Дu[fz0Ot}uGRw5;":9u 01bZ2;WvD]G(B9k3S#QuSN_/w/ws_wߐ;"6ji"c!PRT.923{gzwZp8P%~]?N0=az@[C z|zS>ts`2h_o'L.'n:v]K?[UX[> :7"%:DSYh"uH9{?v`ԪU($'N@LZ\8l7{Xp"IvF _qTpƶ 7wn+x'XThp3F[}pY1>f{%l >A)#TBDl&geʯg9h:CRƷF<5˴IG_AޖD[ *ZIK[42c|H.$Rf/9xwBX`ۋ۳l@*Ay87cmf)C`Ph^ڔ;8CH)+#f;T9"2և[8Tr`3^N;rR.nw7靕WSELý +5/*Ӓc*.ʠ'RdT{29W(i@GrMvnB&{yˁ@at _+岼Fii4nҴ]gnmB:Q}sΜɏ2?E;Ox.{edgSχ SQN}to|{zaNZҎ}lCM v>in'?umbbrKs46r?E?n:$R.q3$/؝~--x|M͈߱0@Z?gYy~/bbG^|h}㙶*洸BM&oPČ WcΑyfmޜtŨs(@ +,.lŬ2)(0P f΍e\>Ez&cXۤSw_Zjw:)3s>g @iSGDܻN={ןu| J?TY;]#1:_ۼ zKFwsiMs<:ǖyT-q n?riŲOkFt&+Q{lԴlM' aly8kcZ/6= W\)Z0Du(D\pn#~g;j>D])' FLr=1+CŹu 6}˔dD7H;[_o\q?V).-+hGH5jgzyXܴ'cIGt5:WYE̔2JD]ID_TURT񬗏_u4-5)ThJw[mEPSȑ)VDp39MȐR,ì>x硴;yuߩ(,Adտo[[_fZE>0v7+"vJQ~_wfݨ JӴY刋9CJ*+&ǺĊ g$YP _KeO'!+?6i0Fk>E!IDQ^ IkLX2@ij7 uN=y咷o@ >\4b /CA}WTECNd1y/;+ںGVh=DEu@Od`@WPE&&CVXiWZ' Bp85&.Shr8N p85@9 ù{U+iyt^p8[v'ndin1Mé\49hQbWGo͛Sܱ{O^&TX%B -]}t,7黹ߗn|3vSBݪ*,: Vz?op8܎DσTc^ޞ@A}G2\?48eΛ-w-={!䳧[++GƷ^^t)^M-4NL4}{N\-ѿ=w_# l™].4Ek җ:W|SN]YQ4w|֑Q3w}(3Z=o+#@ OxU#_ˮ ;d6/|R??Mé4Chqķꋅ>ubN+ş~3 ۵1gv^ÄS(8Ӎ`rZn9L{%ۅ-J"d-!#t_V-q8N%T&Ƙ 0`%{_)<|*1KGB{ ץJURIaW|uᝬe]*ZPQh\*L/I<㙸ޜ򜽮i0yPNUd݌Py$0)I TXѻ[XX|vw~ x1,jj$" =Юb[ɿr~Vnu>NR:M}d2 'TsPŏo3{`wwXE{Jݼ3>_ӇF f_w(6+` *)S/<IDAT 2$r/:jv>z0Ɠs*a+U,ҵsaƇ{Bs](X7`3_2Y3G(M"Ty~P)6PIޭ^,UqLxrM3fu+wnT,:@!Y`͋9_˰3))gSs`˸p ݺnӁ2#} OzیWϞNI9}&A?CyJ=w`%>Rikd/nݝs|/~)@ %RJ C F@>n =0pBxx"NUM pJɢ܅J^OZ`iv0Lo@xAXN)]Ѽ40D(àa*j0 .ڷ/ 7{`n@iE-B}7#p Ws,5MR rԍi ܣR~C ,^B.A G u=zb144c?bܓ0RW' IIiEؔe[XB>bRob`e6\֓MC-n^Η?uCnh#ҕC{}]ѼM>߈R(fҡ5dS?fkV@O;U쀇'B/dy=ob۾_dc}R$b]nd 9j׭`@ )mBsX CEZ(mK1R.ܷ2 /?X o fc;f pc#TOˈrvKza\YٝŎ2L&U~_z~ǜ.:>ݺÊVp3}V)p2H%"l ]| .#8By p4F1QðKqB~'6D%[ep Ȓ$ vqHBB>W x> 0"De\/vs>irB) `E糨]ő2`YLBKv_ zS2mBiZQtLU/FQ| UHisA!k*-SgvK{a۾@ierK8c12ۇ6PaC>zBMAWG75e{R@j ,}%b5uUL7],]G,#<2q"QݵFVǾ\~Ys6/J4׏N㚋?qs|ov">w4~oZ|YTqg?ĤPJ?t-2P()1LQB+Y?lRI\:,AkbLDJIM]3l!$bh!dJTϸH%^O7M!Dzk4 }A](Eʽ}dsG+狱H%;룬dqN4?*,3s "=?E8ps9wO03" bFdBv^alW6SkaT(PuoyN>|XNSD)tqB*J%ɲ.!?%Jc!¦~§OtIiLdY«71e|6~i>p5җh!\Oo_ .L~:Stw }A[gqKxq3467Q \vtm8m tp/|uB 'G2'1`ͣ AwO??D"Y)xX ĈdWAo3mLZeB,O&0R?x7\ ,>ߨ,yTW>bmZdy*K<%YBtm}*ʪU?$/x'WnSB,<"^\ӎG5_L K^Xm c M޷ <ӟY-:Eaس%@_u!/o+9z|ƗInL3㾶ޜ͘jk{iO1EyI  u}x&H´),+cȦ2;ȪB!&.9nmc֥WpI\ߐY`-O7k/e+^]x-af2n/޻COebB : +_ Bə-ŀSer>vSKIIH>EP2NKS_dK`Z)rvBs`+V8)wG #tc))DcfzʉE0 P$gNe|qZdy7\uǬy;+2l Xwg^``D%%a_o Ljq ,y@J;f K&EIeۀ Av"%Hŭ˒mL24u\83 / cZ vs|!*+ʨKwmKw2ry?#1 N#q&8,ul`Xxc<`RV/WNBG2gW1mKOCGQI|:^9cΨ80GVi ijYSSY0 ҭD9/|/|y (?}75̋[_v1,shfEo۶e54Ţd k,>s)Qč>JZNfNE7(a jզdTm#F\xٜNeΘjj*\՟ v/ :7/g T1{Tkx-4v2 ~/W׶m(}lŧ@UE "!|4XTr2Ca,vBaeis|Z0EI+N b²m"(v$F?өÌsSGժ"(xu8%ض H=WP[4oK2 ‘°a`0j뇒ɢQrdț؇.%?IK_=EiN mL8;LrY .>{u}8cT-ײrCy/?" |`,߾6 TM3ˑLo\{,w=gѹzSḓ窯L8A̱sy|3)r%~ޕ%{g@dȺvtd@.%pJ;t7b"4X{OC\d*cʌzx#=N23e<Ï=֎pw?̓MN'ɶf*9 JjLG\zs&cRJ1w0nu͜yD7:amNI4Ʋ|uwP^Ve+))H$i Z5p$tPQQi٘C3O!j6rĉXP& ֓ńe(?OOA(wа[ih(oREw0mBP(>I|j(Juh9C@ ..J#H AL!J<0mАmI 14P"GlTzjW;"/@RxRQGP0&qTf 2BCQ(AB*J0 K`%RdHil @Jt12GH$2QT RK8q1?ɄL ,6iDe)Bn>?5piFv}a%M 24 ˴a!RI6n꠴Qջ IGGMuX)+/jr0!XUN #Q2g;I: S@vp#=8 !hmgg -X7.R]sl[PCvmG8R0PB %0 c<ӢFb<&9a( RtH]iL#Ad P}بB&㨡:b.@RAl eٌqg]L R[OK޼riD,Y7i )?Pfb??A`~#J! w?o][r<pۺcvW;=b;~#\*r9<9d]pնE9RP#߽b[zF 'gx9g+1q Ȣmte *BrY ?])sȮYs~+_`8-]}d}SSrpbK%>v>As k7$mҩ$"2eŵ>Ү US :xDJ˩ > 8ihB ]h'&g +Dr" VUFWb.'njʺlw7<6֊(KH0 f QXHz{D!E *)#Kk kʖ{vں bt8H ,j(ut& tfCJw$d+){Xd70+<T9c(%kFURR.iJK351j(kCj4Xp╌i, ͲYv.ל״p[Wr-PPC8ŪL#ԱT`.P)G6 Ngڙy꡴.P %m1ZF ֬Kqü1!~% .地C; ˨yb>2a~̭)&ܗ3|Y6u$ F9cbߣ@0*wPϔbSwqsS"g䂯NKL}lba96B*F͝gW3c'FY䡤X]/s)eqN"²+Z1,.b!T3Qwd1Q'ڙE4F9l@ ?yE-DLP,ޑx||-̝7SL9Ŧп S2bC;dc @%&l߸}OΈ]j>8oYż/|w(5oq϶OŲ *SFca`JEߴx(`%1uZ:Wr㷗2 0C)P(=v4؎MLI3âx/sՋ께P]:f7Lԙ5XSuY|QE(=(ߥ/s7ȫ@!sZɌi,*%7>TK14=GGPf!Oeh k3;quԕZaջ !5p(auǍQ_j!Tidqq1AF}@ 8ik^HV1YY2:%DGf:$`Y| >_}xVъK IDAT8%;@@jB`#D&2HTGQ4t?[ kg< ajhܭaVnڞVA GXD'װWYL1hşM'6@/B;mH!ph4o[,%Ci${(31knW:jʃ'Hf>ēЊEf6s t_bJhQfo7X Ș戬rffke%\1?o`$Wsoc99P1sxG~*QB!LeKllϏ#L:.XF[{qe(o"bGVeXt-[k}?hjY 'Ҷiwߺ5c@ 2U }Ho|g 33O⼳Ns16*y#b̜b B -!lʠsV͜_^f&h4o[o}[ppeJJJ";(–(JUv d]8 ̩Hq_澧WaFڧ@\XJ"0)K -+x㮧6S;w.U6ж)PM[5PWja(gJc5fcÓqC/)cڬY3)_pr ZW+!*RVi FUܣxdTƙ2gt 1Th17L|"&nNG~XےL2%֎bTz165)>B7F%Ɩ 7={ !LmeEOϬc􌙌6p_7c)$m 4Y56JlX2}Z5[Ϙ9i l8a8BPbR3N(nt'L4B/ˈq4,Mergp1,{axb ǜYGB}[d2O㸩1k5;[ih(/Pr%| ^1i' FR%e 2-BJ쥆bl=|Yt%l&m" !v(H,F˱ Bxa`(y>muiC*ۄ-(ϹxA  Pb9eɷ,(8:ypI`&#wOe(#0_7XHW ]R{GK~xn -4XFhh4FKh4F ,Fh4::Kh4^r] ,>0LIACh4#4M:^ fo $!|F9e! B`ThנFhFh4F ,Fh4-4Fhh4FKh4FODi%NSp]PPF) |(++=rlZB]Ƚ$Eu5S|o,B!Bc a` D @'G9z Ӏ(0l(%OFh6͛DPJuB@*erޜo"QJf5PF֎Ӱ_QC,Rq Rlݺ:]d'SH2{ܱ#}~0 6n\O<* xSB HM Jim`TRj=C&I7P,p-7PM u?/GYF ,}TUWhoo琖)[  k/d7555i:;Ѡ/{/"i mL8sww٦tl: OHJKK}˲Xb9&MbÆ $SƏkzS"KI$G|j ֜(FuW>iKox=Mݯc: ] RQQlݺ7FcE-YD(!bPq=I`U%׮*Ut9e%ɴ;r{㥼_/a翀P=WzۑnsϤsY; :a@hC(01Jae,7z,Zn+g;y' I>aN)E yf*B gM;<s>;yӧA( |EdzRIg>~M0?}?1Q(cio:1+oT5X,S)$[MSyT8kv%=#'VzT &R@f8Fһ&F ]xܐʑKqƢ=Hp^a;C<ȯv y~ a;:]F'B~o s2%'G!2<\ b\"&,-[E Rj*9|E叏?ACe ?!W9rfmX;G%G.mݸԜ|26ѵ,g2CyLʏ=+ipkTB\ 06YQ1J̦2@QY,e#ןP;kTZ˼p97R|>O8fpp @E٢A!S16t.frzD(DbT-M_*[td͵70 FIEyt /B@W%jH}"IOчUX(xysC>>d;cv ."߾(si;B%@2\d6Cl[/Y,oFhX!ǡu1 lf3yxUeTP?ٍuS¬4IR_`8Ce \X"ͯ de&)%555#Mr.B$}N=$<}dj#oHͻ8L#=b駑\7¸q.yW}a~\@/T{OJ%I0 ˱mjLӤ-q˩dfn$BΩ [$f=ޢQ_,ּlZN]iefӒG |:Odxu,ju !(2CnM_*\_s%ڲlϓ-AqȒ‚SY˄~ݸףD '3X?~ ܴv/CKRhK ,H҄a|'a&UUUs$c YcYdrDv&&X֗}qV#D1^hpp83H\+>͒? )aB4P<7}b#ߺc]%1 R)\:]ʢ&O,2L /$ʎ8MQ'0pɬkgU`&a`#s?.SJY1K - V`ж 됣iozj4XŲm,YwɨQhhhrJJJFIaA@.c6.}4/{weU}k9׹{aAf TAł`AEHbƞhbLDc DQTt L]Z3E3{{.﷞W^y%gfΜ95rh+cN2=~ܶM?a栝5?o`\G֭,s | zCM[Y[QX\W"-&r/~ Q5q']0;цִ'p HkmjTJiz,( O\cqM\Ga$tpu]T'{LBF_8{ {]0U>k$`2!C 53gnLR TCM0 IA|9dr0 8 /kZ[Li+!G566CIv`rRc҉y]+yK?/ b2B+W+ROH֔JeI%36]q G^pb50¶7%ϛ)}ͮO2^=U#Z`Mly68| m'IR.YI>EDu/$ شj$V$$[C7}c_Fulv^=2dȐ!#XU$I8/y)o# CWI=~]c 'D.[7 C>7%'\RIKϏ~y XInTTc#+Z<>s]\s=Z?6&r미7Myp:4/מ¼|\.Pq NHEk62o(o<"1˖9aI+-9!|R2uR8 @i9Y"ՔN)eCQz*mI)і](4a}` 5em/C ;~šcOC?FZyT*Jj H ɁyeW$448jKJ R.Wu>S Θ</POT2R'\iY p$V|B@bVSV'k-K*L=$ڂq}:m&@ j%-f]SѶA4`JElMcrtʐ!<=^'uF`exʤE8qڞ63OfaǸejsV}[O먷L-?M!]OM%B,%Omݬ 2dx srϐ!C 2dV 2dȐ!CF2dȐ!C 2!C 2dȐ!#X2dȐ!C ʐ!C 2dV 2dȐ!C`eȐ!C 2!>`%c@Px֎1.,Q.nʹm;*x3u}~/k> O!<>o*Rdŋ3dȐ!CF?ض}+] O!=ߕI -+jR( M4R%Z*q,_v(mtwuĮ`llj0cccKEʥ2QqBTJYQNR1 > i0UhCXJ؈y瞎79d*XBBVi{20 d4R\go_$I]2dȐ!#X()^@p A* AU! DWW]39k\*l۶}E$%kBr.n#̖AtqȚ՘G4#\t^BSqVvJXTK+R(Vt2GO瓽e Vgd`%OLmBt JbؾkB"}\.tvu{'Ƅa8#D7rjU'̘1;{w 6yaBytѽc ߽{399w?usDZom-(v(1h#GrJCĕ bgrl /7pb^ &o䰗{=PyŶ[t͛u5r :Z Lׁ-d筷\.T@v>WfᅡZee2ṡWT[SU=]k8}QcEc$@i)LNLR)aœôw388[8^qZ[[QJ166Fx0_~9BB)\[ QۘInZzzL9 MUxJ;iߵ,k è_(FG 7:}{yyW2g(>}k̘ыlB7{؂vDB`ajEm|ag\ !݌>Ai G7}oj:$#X%MZh>ו%k-\9}3JF3Y)aiqV<|$w&t)U(BkpR?ErJ~$#0̺D *&AC+P$'qOz-oik"*!7s9g#^z$J[nǘ1D䌓|1w|л9ZMb1&pYzh?8Gx:7 UPhi foogd &6ii6?go:پa;Ze? T&nݲO JeCtF2hk4>Q1&iXds2ܾl \7+OZ[r g} [Qdk)$===188HgGR ?_;vchCD"̜1^8X,6m ay 3g$"( Sq 6xXAJu}N!rMo\=W,BNTٿq+7 m˟+!p\eMx~u#]}]xKՑl߹ܶ=,`w)>/$S] Xr=IMm-8A׆TšƑ=Ird~8 % ZT̶7/3N9<}-q-' F|cтٻ|mtww1lذ\P`ƌ3::ݻsɴ`\':ר''IҨէKQT$ODevʻUZQ|x=11wW %5:h+9,b7Hd V ñDۋ-FHbVX(+ݏ W("a1JCbw}'0:lش8XȄF:yBBTS,Q5Dz>>|=|#0xr  F  hY[Bهͷig|'xNFSퟌ0uC r4KZU=K)ETbrrJd=܃266{>cô BsNv~~>3f̠kabb0 0ca߰:PE)c$" RI$I&T\M$,^Z.`1ѷPzW-gqG}f8&Җ*Qh!V8Z$ʀ0[+ VX]i2F11n-&q.4b 4ĩ&/be|! hu(R0巼 DCђG>~C{5rPuZ'u`R$uqE@6Qq3?ԌѾڳ<-냎EIVeAJ(L0G8($J0JXpo&@\+NcWmdq̫/_'N}%ɶ߹gig/⊏;ox',X嵼o~'p]o^.腜52_߿}Ïo>t2 2?׹s1J7؛9b/W>)_Wӗ~ȯ|dI2d3z)-(r`=QTRPa`>G4oAgWBZvl8N=AHzۙ;o}R)ih]Ue|ߣRy@299VHF!BZ@cxXdHh?#|aak8moK?# w~z:"E5wsϡ-۶o~Shkwt0oڂ<y|7w7ou-G׼Rr_|6Pƺ+8dhf JJ*U9!V}2N-|:">'~|#wkd|sD+=Gơ|QfVRڬ!׽O| |ꋗ3;9nΔ&fww/dtvʟƿ^Gp;?M|Ǔ|& |8K,N=׿W_p?‹9-ଋ6' /sp)#ǞW/o|/wO^,!C"|~4%;Œ>&&&طF`rfZuTU|ޡ+3w <4l5=C߬.[99sgZDGG6#~4/]r,>2w]]vTG3&ŇsEv2={-9؞bMR 1aD\.e/:tp~JV^D݉ L%B(N љ$W!Q\ Wv'a7pH++{ٝN:^ZVw{Ce= sB.8a:ִ/c~0 GnsNyK8zn h7y8]w{!?ĖH"8|[$#yȕu) ',fۇ[އO <ټ=?mMF?I¢s^܋󦻸6+Y;|fm-w7XmXX$t{.$$Lc{;?\RS'a | ҉hӰDhmk#B8dR1l޼\@MtO{[+ .D' G=ՔeT*cR!°:D6!4HtBX k|12qZ< tajRpQkٲ~#=-T+1_r ^\ж1$1=,9t|;۶(Ȏ}O%/-yy{{=Lus~[w=z#~qoyǟ 5F^rMMء!&w*%Cw{Q9QM-"ط>| KO8D*Oq*8d)[$ s_WaZug )ABOl{5;#6\d-?Wyc1E-DX/_z*f)&5H8dۏǯi.=fqM<|;|uYf^_~q+y.F5%4A5sb-9:Xmx V13o 9咏𢭗ό?83Z=`1aɇHo~F7l9,RV#d!IiF!9#ҥ'4̗ˏ5]3ر?~Neu|=J(v4G>oVu}\΢zgڗ_Umn3D6Js{vfj!څ:(~a\WաE}cA!.JV8Dn_Ba-;wYʑ^Ň2>n =ՐP^ZXCX Ǫ+v, W?Mɋ IDATgyXz-:}!?<=;CF2S,!q-AX L{"NyI4751<8X+x 'ccS &1J4 !ky\˖21щR$(KhD_X`ppa,YLXdllr%-cY/`mlp? u8vDH J:TVG4(I-m-97$H+ں;AIuS8|Ä$FK l IZGZkkC6^3񱢊/}]kNx'Xo?[E Av.} ^U糢0L~a{&H$Ao!D.yYMW>%1e*ٔ!7s?o`HWr䚧l"^>ϧ%%9Ob,41(?w9\/;ʴ4ss/})7xǭWzwcm}q;f} ^4ldG:c 8崣 cns`4{:jmV7:s@/Wis4M9ƒcEJ$I(8CTJ}a`~ϟOP '|Ev"#=u7w܇EIݽ"  )-p9]tq~({X D]?B&qjR$e1`!eSO'(k -d15*סsrf̝I5ɂŝogk?)Zg}`\B A[/ zRuhA称*3waQYS͚r1-sݽ#,[CAl\Τs}}t<^=oXhig'Ǭy̤ѽm A[V1sƔMmHfϠpS-܅l٥wv>kFΐ Bb&+XKIZueǎq̒%Kx^aYN-3<3IB}M5HӗSE}[bz鬳z; 9Sf]Lwl(~vTg*g %|!Uk ,Nzbzn Èѱ1:ۙ?>Zi$DJ DD*U<+k-J9Ɗv$il|FX+㕯8U4ć!I$<V#rҠDt_˾͌^K%Do*a ֬wV;Ωo4&2kq\W πMtƪRy\q]&qW)p:{d+ó.^!E>S %5JtFӮ;9쌗A5NKniv #)ԩ=1h;lԗEFkQHkՈD'J 1r9>88Sz_KwbhNbK~2AHN}$_SXSά:Atwhٶe=B\*ł5p}:P(LL e)8^>Kΐ!#X/H)ec1P#X)uq$,]4}Z0&i(4Q=}YaASl4b&$ Q1qԒM1BQhnR.S  y&&LYsP1ƦCؑ$.H$BZkRjZ5ȒH)j\,RuRH5VSJf]ʶ-Xs)w{{fWs++  f56%JSB٢v]םDAirɝmY^[ zHXPR`trj8J)$aڵaT GQZ,JfS}r BB(xn1jȻ<%MGhij*0Y8DOXD4!K%Ka@`%()p\';Z TS18K.'iU]Qq*IL*]ZH7N#m(&ii8p^`pxRTF76PՊ@ E㦯i`=@lJL خM!I82d֟ +X}]RDEpB8F 9XS󈬵?-LI3 Jki BL#Z)ftt4UESS{wMDKkS#KԼ4-#sȃ`M wշ:j{UKw_)H诫ruկQq ɪoRólm-8cby‘6r#Pk0bP[uw6uly&_ŋy, 57}s1ԨͣF服c<2[igt}TN8LI%|H>q:{6ۤ~h` sC9x2G ~gw#URԴcCWQGEFT%>}CWt+MH=!(ĥnJCҍУH\|2Tw}< NWWW Sr)K5(MͳTRUW\m|}U'zZYEh7TG$vzX!)~ AN-Qi i\8(J{*ʦ} 9{Qѷv!"fݕQǟgy\Uܭ޻Y;I "*8ofPeqEܕqAd_CX$=魪k9ǽU8#R|Bu>0{\+`}[=y)?ǭZCO3?= n6 Q/;lԬY|cW#xRXbDh8 cF_bOnf>)h]Gk]mZ $]RZxVl~?m'i+3m5HAi6CCXmh4mFFRz= ϫ5CAq2_&K%,)I1E~.mFujt '1]VJJTێ+v]Z%Ai1:(y~פNm}o\CGT[ާn'+83ljv'}ez&qjn+>}mBTXa;_p'Z{򺓖a=8#V-u# 8Ok&kzɚ`sACr3Xt.#&ciky06K*%+u[1u _/n<bOxՑo^6P_-oy}J. Oռ{;oͼ̏q?>LӏL; u_"&}ӇY,|膯31GEҸ68lL{>O^M.W`˖M,X'״mǯM# YkBb"WA$p"d ǡP(Ny??.@))DS!,`H("J Hk"c0A mDe5cOf"PzѶp=bJH;mI,"\ː\\7M۟1޶^m^ H#Z`"!:@X2fTi#mi dQ[©\ Yb k+l1}6/f>3C?CV QǼ #5#i^{l]+dܟmd`Rڶsν14Z˵pч/73d)͗O^LѶ^wKp7>3q;K{|{.a{zE=ߜ/2줐ѵ=H[ūD- 2\ EdzG2kp^v:wS84!3W/r5Xpy1v}g;UCIjF܂8}\3L׌nDfmlv?{v(|TT+ ]\$sNrO ZURSPQ5? Z,-"X/>FԋXB0c >(,Ys4bAoa5;mH+a }tc0'/;Eso l:,3gfvo~{8rA=tN¶.;W:+8# eqKK}\C}q::s#SnF~}GŭSO86NwW6y<@1u/C4-(?UpɃLM7` cV컖\~WgFƒۃZXR⥳̺aSF&%J8]J PoVh[h=Z-I ?8sYv ٳ>Z鄀(l[V &r_Tn!tojVU,ڠU̗>=*~:3d_a爫1t߻ADTDJhA`"ɭ4"$eM)i[1"}K̖kMY.@DN_?ǹҲ_2>`~w]Fz+>b$k",!4H(. 7;^{_0aiG9?[E_2|zo欃soj9o)SZԧ[5NfU坫<‡|r|hU HmL܊{QDm>l'z ZVgZ-i-"X/ASx j ,}2 z=v6׊,b/,)"&a -UAs*/&O飥!"HV݄Q!C! %Fk$4E!h34}Jܞ1o`m[R)qlmtv166NOOQ݋Қl6x|m:sgaejQDqP"Hy399J'e4^kJC{8Z،=~-;^JNMn!nu ۥIzxmLP'POrKZYAWTcwqH3' 2OwXcw0ƶc+K籢Q\|6{Y&<Y8t[V͈k we-#%PfYso# ױ'/_F7}zdIX_ez{?Y4bTgkntV8c0m,~L6ݷރƼ4 (u0\BbԛO"F!/$:&YkZ/ g5ʍ/P xDaYlJ;*!$1XqQ"T$(  nCy\rL|LҊTQbB"A>uO4]=%&o~zB՘jrW~x:'?G70|&mms9q4[Z3ewy̙5YW%N;S8CyI'|rV\gYgL12!CGQv'>ͷ8r2͉>Mٗn_~߹kf5S,彯?~}s\f5zF6/\9Xco{k03O]=Ws88H-A~V-f9tl@aGWO5GU9.^o[/2ro^kk-L-10N fC0c'9U3}{Vr>̏~{ָ 87rwrA|VĪa=d,I.zn h,;::1B0mb7~ZZx孹qeXT3'[XuıxPZ zǟ2c<0*U/*)|>KwOI{{;ŭ9i:^\6M2 OzYjxO}JLRa4N^T,>DX#R,RT+I+EM)HQTضm[StQ. r i:NbT<ץZ"<:;;$Qר>iiOmZzN{{;J/`jjή.H" CN98>&U¸RIGzO^X+̘5M :!)Kht8YлMQ$0LRwib?BD\Liaf0@D8 r*SrxڙٌF&Sĥ_u y}̮eTy_]Gv"ms6mW-&qqAy&:{kӼ 2۶ ->ZslH1\^ڹ(WL#HvvW>GǜW2LјDٖM>߆@-Z](m0:Z D-"X/{8S0,A055I'H0 qh2G[[Ǖlٲ;n0Ccrm%ɑrS(;kZ>X^& Fƍ%a3YE &2e<ˎU$Ny'Z(t: XeJKJk+,[H?8MZ"2d ]Yn/욐0뾁Қ߂6HacrSp綛Aiкn?Gza -"X$K%rٺ};+W"R!k[nT E4TXVY-JSl8Z"ݝ=wtEow¤CRHI(\\.3>6EJ6qc"ϣF!ʼnIj $4yͻ>=(ɕesv Fa징XA4"}RIKFb²mbI75YIH^!fIIh h0i[ۻ&L A_1| pQz_LI,Z*RhoAدLmB`1(Yi0je$㒲)m;6sǝ739Qa|_G)Iuhoog޼y ,Hb\*O\E0}J^ex| uFG,TJ)ɵ룳3lC;㒲JjTbX@xyiƦBĖze˖r]R)j٧x„\c#׍ۑ JDM*$ B0)@ mYLFqx)nƕWh3f{of j2pu a0Vn]Ir=iZ<ZqNB.B@-OwW6mF+={ケۙ3gk׮;ٹs fa8, TJArLRT*199IXjV qB0U.7RJ\q&i}ĵ85X {CdX-\|*F2mz@iWK@Z1 t:EXXKShѝ#qX-܂8؎%g) .䦛~{r1 4aP,cxxIL3}sqmFZ%6 G8d2RTӿʵ-\jFvH)1QL6ʦic\.jNr)'x|+;w.w~;o?or_Wx_ϕW9 %`eK/+\V^͊+ <:[nK./`nvN8~Q)c;-_s9{2gh]o2 x :aGijJSO=Ŷm( J%"mUkKvx+Y~*Zwei8-}TG3p\fV Qh0MV9;v\qmЂY0?G:Ny9<E\7 ,`˦L2R̙30F255Ɍ Y0Yqt21>Hc>) 8ǩ/?[n9sr5Wʤpl3g>[GF+1N8nӍ="R]%No?FGG ÐP)&&':vaࡇزq3J)οaU ]=PչI֚3gR.Ul۲c T|n MmmۨdJԉm@-ZĂ VkHDxi4L '՘F 6˲3xUstY)[ЃjclF+/.t ?[孷w~;3_]n8]G淖{ji꿰"|p,۶V wE|Q!;9S83o^MYR,]9sT""ɑNeHS8%%~dfXE:R.3};۷meْ%hT 8# @;::d``>zzzRuFGPJ!$1ou_ ϷNܗK5mCOsf by!*r`lkTd ϛCsĤ`͏>M۩_[=X22>J-2•ZhUmB ÎjiAmY YܚÜ=(NUr+E ~TTBEmQȷtх mG"цt:M.kV.\8Zϓ#dt0lݺ5kP.,2w\fΜIOOt:6Mb PQAN)CH{+\AE #$ϳ}&AR*Jܵ T66Mx-xF7+qӏl-ׅ :˿RAm7=l{ J\EܱHߒc8e;v?w\Ǹ7Wσΐrxˏ"'9!_T}k~{+yN52\Kx׿.8~󋟲fK}>¥Z>g=e,co Qȵ3dl`)㉿t:M&Re-%$b;vt= ;w.tuuF:n"ӄl3Xp/&DQDPRDZm# !di:ّR6ctďM2ͥ)vw]q.j_}'d{Ix>zL$t0ʥ>p!k7~{?{ \wߴ/r.N:,p1'sAty锽o,tY~Y@3y\Cq?ίĔq# Vw2G,v;^Qs{ث9gs쒐>Ur7?.]~W3e]n gF>-Ъ`=A+-B,ij5y}%hPZ!'qvdMֆJ((*D+zRxdY2:43 6׉"tu/fsXZa"gٙ7wlf͚EPpS)FFFb6$2GÆ1$dX6ZƄk&T|!ҶcwEQؼoUo+7{?:;m_xۍfe}?d>VЯ9lyUrv 襧}{/]oc¢l8\ĶR0k-Gmdz1oLu%9{zlIv}8O&_c҂}͛77tw˲vi#SKHTB:C[kdRijO.C(Z0 cbՈBJTqunz]Cǥv >'kE/.[IrZgɽ~; hi Z;jiXP«.NkY{wq5 q,6}͗}r/t+}l馴[|^V-S7N(Rs387ŕWbh5F%Oiogrx7]3_Ѫy5⛆ k8\>MmgX>z6uϙxbȷ.XhcRb ;jC<,jؖ/F2!M`%cHY8:h GTl߾1tB[Tl@{{;vp RZ4b7*pMbPV`j5(²lt:tI6mh$i:LRͿ\Dg9vpĵ^LÑRJVKR):;:h@+jAclV6 {& WIgPQؚ2|ުQhJl>xɧs#y+/s;A[O[M登x)rH纘`${3/GmY,\"`hi[`)f2="{#eϘYXΓvWOu4,PZx1s+%Q9zfQaY6v5+?"|@ I{>I#muPBc t[Y-'|iWhpJH4 ֚ѱѽ'kFZPhmp\l7R  RnHy.mAxHd}zLPj5T>02΍STuDR-j{ڶɤic;6aY#k(֠WL|M>#_ȣ1瀜yD:Û$1LTbԢ}, q5IUuPzF vN<6?&sk?$/1w?g??C -"X/D%B\%@XClq⩍O0Y@J7e"ZmEkT: "*LMM144˲0ZZl`\.MnJK&! 븮C5Hy5h0 (iHۢZ*STbfYuBܡY8m۸iI+)nBx"' 𩙈0sd'&xǞ@kS ~bTb̝֬9 yhݣ-jB -B`MsX$4#qL# J,AEBXgX,266m8VQA6Bj5ZS,NR,X')p]ZDZ891TT:0kHO\*˻ݴyyxnN6 eY$RZFLVhxb㓄G^_15U"1,'h,a,ɣ^I*Z3V-B - ` ;V3FyZ![hth ^/.38(EZkjZTB09Y۶ۉA4N͈L&T\UhHi0FQ*Հ>~PGzFT ˜CIN6f 8- ULlnd_F˶mO4o*<Ж_N0'6R3Q.QU)OR V*&bJaB -B` %(Rc0(vM#99cdK |)t8}9>E*-[6rwGoo$B vngaocEEi4B;mmm8 &gȧVUTG%SF jOb2 X1 TŒvih8DD@]Zm) |jzc{H&PUIgҋ:l^CPľdBdU -B -‚1*"TU`$Q"¶"4L#8ÐPLAēo=۶d2 q,l*[>EZRq,{֯_φ ( !?|@:6(&׫2STVT*BAP"[JJI0*BXYӇbMTs]D~8( z0Q-ӑm\b#Q} 1Jcа[b -B - _̄-u0¶\Ե1DQm9 Cʕ QadQԌBF*l%F%n l䱆w4ȗR:TԪUtۆ0bԎeR |$c-B -"X/, [XMʲqtsr.&nst'mN% ¦"hdMWu)%044QRl6>.RIww7J<,W4MEe"i(ZכBl+ry*viG"m~"&3A! Y2 WӉU5K#a~6Ym1o|J"l;Ӗ!l aIJ8pvp\(Ś5bq}Zvr-B - _{r]ҙLlivB.~=b9d2U9LOc18RB&fx;fƌx)XDmlE) ;|ߧVVT@ZFzk WGh5'b{ܺ펳H0ĎQXX?dA,+v""Ogo CsN?|\q c,?2BM7!ޞ֮]Y}| aIZ>X-B - `Ex^Ƕ $}l²6BZJbS.+dRh&NcY˜)*Sq(O%AaR}*)U# CޛGKzgy~{V%Yd--3`LN !d<`d!x3c'c` &ޭŋRZrתzg[ZdО{N[uK]_~5u]OL&Ӗa@BpB}EkLi#1{M}Cbۉ -!Um(_܅ y2yY`<.1Oa=ln9Qn|9z{Z_uԩS`]IrS5xDF(Ut,Iȝc'O<ʥKI6%BHl Z'(BJ[ggϞg<)_;wLʖmxjRN&xQ!jϳp8رc2A)~n>)(C.թSN:uLƨuM:' >hHI0&ԩ=zIӴoNy*˩k܈>E>CFA/q-c{a2Ȟ{R2L{8e^UuB0˧Wi9jcb&a٬xUwQUJԪaQI)QZ闭 Ӭc Z0'~@Y$ L&666H 42ޅɡ :uԩ3XWdYU&AqTKRGl*)g5Ӧ5`<'`}c /pQM̌()yM^'_aiiUIcj4aP}ެ+B4R͔IgIO1ΐicfj6nC="ʲl h `:Lpޒ)qc%/rzYcLvMʪ:QmJ[d1s:uԩSg I) ^6ZPB(Oƌ|ꓟٱ O&rʥS&'4:HR ("׭xoA:ǎ#MSȲݳ5quu]S8ׂH}cP8Ӭ1-uYaږi29d2iAHA67O`i-QMt{Йw-a0cB Hn/pZGUVg8('J;uԩSg:`}d0xPWuM/I)yQaS>- |Rj /1??/~ }CkMKpSOQ5% 51ؽ{ "-ʊ f+ eTTU cj>R*'Io֊8<$YNڈ8)JEIcl;Ji0r}&c ┥^җۿ9%XZ^9uB궮SN:u c R,t~ՔDE乡( ,%J~nFE"$RDKs 71c`M&{.^4'S"8xv$RrW#yU`t:mTUiPiq,yp1$IWt[eι`E$qα=c8n ,p>[͞_x-"\3(˲a]y¶uHC(.27jE Ht}}8s46a?1 ӽQ;uԩSg04Q(!1eIUʔYsiN927ZZP*,$MS,--sq~1UY` v%E>MIkCQTeIYV8[#6 FjvOk r!Ym_a'0ּY3363Mi̔3}|vɘ{W= Dle}~|45[xGLqe)*B]F{KeA7ԩ_5^0qQR5Ӡ*G4Tu20tR`pCJOԵen@?`qqU17c*\ŞݻY]^f2K ޱ̞;ٹ<[Ǜx*/4DFD:jle ծ!šN-Nav_A(b7?wׄg"I,ˈY]:)˒N={BF sوͩ8feAd[$IiRެg8(gvit3+&ߝs8S*CFdXi`h 2"uOӒ$a2b<]g2fssڭG8HqΣc (\YLc3b{{ÊS!e}`lYzdt* ð*Ä/|xhI!R5haXjmHXE`(LpɸAQLoo*2A5ylQA*|g~1֞ u}3f+wп^O|T`ϝusS?sMu~{^{Cl{<o~Ǿ%|OUU}Gyo? l~?8Lt?_!/51ͷлWHC]$HwnSN,OSXK%_9v o x?cHcz7?o~OOY?FS|{9y1X}~>kK5)F♜9m~l=Q~x{Oǻ'oǸwE}## 'cL>?{4ɇ/yK_I^u~?:;uNϺڀp.ʢh͎1< 9!A!.{gΞrv<$/*֬mooS% I(S TUݰL{"oB|;^šf:~selmm1O(˲2V s!^p,;Ww/Z,/ؽ{ۍQs|!F)haY^Yak{T-޸{ȧ>|;Տ8?f77Ae;˿6l>Iy%_w d4gS񏾃k+{ã%/5^:V7xʻ~fєԩSg:=7,yLehBeتFyx)zj$.sEbޚŅe$ino-[[Q5i2n՞ڛ*S6n{*ܣ(jMی?3CZmLe(,.9ȧS,DzL 'ZP&>9p4˜'N`c,K!,--I(2cqnzJ&<{6i4J=G)9G{R(1%[&ck-{xHc~|#(ͧyPYGy]NzбMs?: W,cJ̧a=S`QX%9hL~V:u VdZ^Ht74SqTjڠj%yaGi\Ʊǎ*qcu8XxBOߌ]5CuMQԵ1VEQRUY㸝Hͦ`޹(-C]@i1f0R$q<!@xrhBQYUUEm*>ϐ;6lmmSSCxM+MO1zH/X1 g sa[)yN+>ySrlRx!WKqdQErvs ~ٺCvn?i)X0\rN=g%qE:'>gw=|J OٷJͰ:uNJ )uUYQ9ml|q1UspH$:uN>ιKGae [AT BN*sʲl׈yY5yQ=Jl 5(ͦL>t:Kع)2f’YxR2V('S(")h]L'dYw.,JRT%Z0#z=(jWxz(L&LӖ)VWW9|hMy^^[7.@â7F؇!-Ba 'Dk`LXIJs@hukjPM)i6;Oη-qƺ(F(b֌:MG)He 80PHɩ'u`tڅSDQB'`{<+OOُr+{ V_طg/|Jm-RJDQ`01h/uIE0d=eO B:b كcΜ>h)EeCRYOU`UUxq;Jv:6 όlTaJd5uL&1^hHeqڮC,볹澤$~g gfkF\srL]!_h$y\U ,e TuK &P̊"S cff")OcJG$?Z=ŸyRG:n!4IީS`uz4Qd:# $( X,..rwbɔHGxV4fa)-Nb:SR;C9 vH%Ij[`0$IR1T &\~@f9^/c0P5.-5Zk z=&iB)%څV}pRPm<R4>tik$amm S[d81(B\ytZ;^?`{^D`(%gpւoVU*څ'YƯ5RJΞ9\OYPWQՆ}kĜ9qWǎ<6G;3EG\ԩS`]뚋Z)@`Ѥi%8ġeenbq䡇ٵ<7DqY{(OʺJIP] 5XJ)5`RaAB21eKwoW=Ν;L$H PH/:Kt-}KTzҹ9(,^됫X+Jj:q?d<Secj~qq47s\\[CckXWm˟{tSN:2@X%"K3vŞط KQE '\[5{vq<4aU-~8ZSu]RZNcr(:,ˠ@03j3|UUMk>Y1i3cp~BsYtA,S%X*!yQEx4>Jkm g8|H(Nc]Zcni˫|sgq4:ǖLݽQ;uԩSg 󮽑nE,I q uBp'9yInv(%:@OhWu{(l-<[,B\ XAB4JiQfJ=mff+cqk `(B D|+!8AZB D+㝳"c4Ԛq]S/p`DdX&oހ ,&}ܠާ:uԩ3XW!Nh V,cx2[oأGcEUUiqeAvyӀuNbwVyO$*Ac*" $i9W &:3OGYfBs.|Td`4Z (dr3l[\XDx4h9G%Y8dinFNxA]IР)yʺF%/VztoN:u+MJp&yw)!12wkꦃ0(ˢy,xp@UY^!"䣼qx Dwdcd<(Mց^pW[Liٽc7 T&di[H!QZ1R*Dv^KQD1 臙S* URHI<QFG,|'yϻgs/9ӒM]HHjqJԦ["[fZekY=NC̈́( BO`jgn##!cxh0VF*1iz5|^0PRT&J"U E؇5cj=^, $DԩSNLV8mrq#>ɓOhnȇ?\su ` /K)g{{;}s Zx0 R I>hMZKmkfjxY 5> .]yJb /k1Γ iCEf=}bQJP!_6+.˒|2e:YvLhh3 iD(0#X1^PJ#Pl8\Р':uԩS`]a$L!}wΞ;s'xyޡXWiv[3#{u]2hhqߘY4ucfDQΝG| _@Jp8dmc2ӌ01.йWTEA> ^/ce&U$ i"cp ES և,>\ȏ5N:uԩ3XWePHiMUxgΞ}ĉ(G{9xp.`mne7BxʢFˌKkpY1k'53nUQDQ$p $A:fOmJ 571$Yduq>?b~4_rN>lj%yQ ISle-JSWʢ|Y)DS3C4DQDkZ !YTƃO@IkKUz=t ]SN:u늒o8LQIB[  [o}E3Oy5Rd,Bx9rIs..zpAX3 AYjc0Z$ I1cmmGrرg0"P={(MͩXZZ!kLՌ1T" JE=Ά,l 7ˀ@5S~̒7qv/z=>ڵ>[qZ1MSj]P),DqL9:uԩ3XW5m:5'2ٳgkkkr-c>r]'$3H%۪!U{BOk4REy3Dkle1zg~~,Ky'9 3,Xky_?qVvkkka2mj| Ӧvc}Ml),ֺ=i8QJEDBZkhQy Np'$Muuʚ6֩SN:uʙa#N*n=XN׽oĉy#T1h177MŚHP$I2d/}3g1NKa!<{VVYt~~M &M8WT%[[0) xלN4ڑ%1$Z$HQ$qޠl{>iz$q=AV*`2aĩ'LrSL&iJUUhTUDtv]UU,kZPu*LS-Zt:NJH8tъ$NI׃i t0Z%qHuQzC%ZuSN:uTUL(8#G6zի8Q_2y,KnX)~[G)^W裏bd ;VVVX__'Q :YXXhOeIQH%%"+UCFTɧWm+NQ*Rg?ֆm%RFDQM=@I}. @ԩS`uR(y켷儷sE:˗%vd}q<䓼^sȣlmٵ,y/۾G}"pAo&s`8dc}؜L3\uUcf:N&xY\\`4=Y1r W_}5> Ip)*kx+^>03{YFiN eeQI%:j;s}(HӔ~O/{ٚ*P2A!RF 'LŔQXh{ES5[[[,//N:u 1X(#dTsq޽{y߈{ /Ν;+^rGΕŔA~wg>O/s'O_$,ry.llqƴ4Px^hgaaC⻾;%+Kύe)++߿gw|_$MSַ9e]O|#QDE0 Nܤ 1HHI H!@D )ZHH!K#z!I2D6Xp0Bǃj(tL bI$R]Ȁͦl4 Ͱz))Ϭ ԩSN_aL4$~Ԧ[nٳ,,3Ưcz9x2p}{ޞ[׼|~>?y8y1yɋ0A1Yɭ/?>Y$k?,caapHUUp>͙3677)+C^NfծTUNklmQL'5qIӸ Giୣ*,)NP"&0-kbq1ZʲD*$aLsEcLgqIXUcWHҴ}IVN:u_P·G>Oďso{Ç{عc=Nٹc7we:Y[1%ۛ$꫟h!4 vG'?$ш^9 z\{<}}nf`mmz6weȧ|s_EY9UUj3Ql9B(Vh. <42^F$D:ngh3,e]S{K,N2bs F@:po癟LVN:uCsݷ:}{뮻;|h[^t;^MQ\4d gO G#Ξ2Jjhg/vLc!G(!:wMaE+}szqLUU8N<ēlnl]%yޚ5,:jPҬP2R|Sׁ2U(]VCKGm0|;I?S?MY0y:uԩ3X| R,0)/z-xxы^c >{du ב(! F/,=,-fqa]v:$gqY&BT)A(I: d1EI$I Q`nh}}=LWdna yc8nO Iё?ckk4MI`@%QX) 8M5hnp)# ٰ(qOZ%GE8 EQQGmm(oIY ֚͌HP*#Vٱ3.]\'yXkMgnn;vpױw"Vd4z9Xg<,Z$iZ+7)%{BѳEZfk~76(Sʢ c& և a٨X]]a8YΌ[53#{밗eXk }fln6!0`cZ?sy51\etMY,..feƫZZZj_=k+8(˒pdJ#s677 p`D1"`:7RUd&iIpA>ZFmh~}cLKx"8ω,:x":[y:&W@x8lHO|lll׽7L'-onWd{lCZEt VgIY/6;)X%LH%c_ sdipsZ%=(P˽zVwPW9gΜauu5O5E^STEw4ӫ8`P{ٺpcchĎ; δDhֈӆB`q~WFR0a<DVgEQ;dii)JRv}˾*?k]f.K` +6`z1B#@ $rI yr}c\zb\–eɒN^3 {}G93}}s-E *Q2㊜s)aAV0pqq%/sY|&~PU֭[b9gYv3U#Tň]*I+rTo*2V)$CfDUx0AW=$I"MSǙXEh%c]4M( KKg~~M6%*H THkF§jz(Nm۶h4(ʂV3.Jm5R~x!c|l a=)c]w ݼ5S3tۨVN0hCVhSqsN>$2jH0yDf9Xv2:RT)k&K;^xq/99^7. + dr ]Y_ig>u#fm++ X[` 0LZJ\) YA%$mt:#,.4efڵXkv(2116 fff1e CF$ EBwJ QۿWR ]ʒ)FؾcGJFGv:{]VyS$%`pسoF$NF;wD~Qfd= 'GiAc$ޓúuH7)>.:;LQFT!+gBpM7g?\s7O~ũFw]ܴh84wnߠ12Q+j*z([R(k;K4Ji4C48=BUU;{ƙYhƙbll9碿//~ <~ς洇<G)Tp LOOV"B=۷}43===y' 333^=ˮ]fs7~GFeHΠ8O} ϚH%GaTYbX\x.8#ޅT\OOOw_ȓDfffx..erbb56+Rþq69_NܫsĎ/Y% bIJqD IkMyQ`E6B@$҇>-Hwt;]:g|kHS46n<gA k< v-"h]St@yk(v?QKY3?ȳbff*W \uܳ'6Z[񅚔W|-?2LRYԓq$MIDP?oGQ-zihEsq$[v+mOd21TWyȖ-LZ1laۙbjz$tmK6ɕb۶9IOA%)R)uA!BCYe ^~OYd貏3TH&F4vKY9V%ZWL^EvZPx!Br8$KT`u|'S9S^ʠ"uZ; UB.+v\sc˖d[6&A%2 hVi"pW>BPTXrI3nQ{767\ZiHָP;(%Razx)$ٻv˚Jįw1&M>O_8׼>oW=qgEVBfBD H/&X 1PWI)~#feeRtmٲy3o,5cjrQ/.!ea]1$9#i="SkٲxJ%kذy+kLCH9f+cx}sYνeHcǺ!w6IqU/ 6ep5*k;*N>xjN;t. ggY=뮿v{_ xBE# \w_CkkW'cH>"6U]^BnGa$+yX->\b>>&WNG=թS_~-g>"-/ϣ7)T _|هxy,]{sǝ0s}wq0OO>Y w`$,/}3y?I;|+C\\VBߴxk_sN• {,r ^3+17n ǞO~~m/N8F^ytzG{Xj5 ӫYznsT;#Q`E~dYKL:YF2iPHx.k[ .ma֯_GQʲ5RAhK5FcF&JecBc5Y>WHRI9QLMd)kgc⼧, eQh49Sh6Fits.MQ*VMv%}qٷ9֊Wv^ws=R VV:X cCJҕx+?ۼ$gap:x< sӑ&zټEU{ןLw7km[\rܤdXfq{ O|_㒃k~v?3y}CQq~OR2~<$U υz%y/mJI[ z<7ª~}bn/7N$9醋>w9{CFOw]o^~j۾r)o/~hcN8to 2^CëOX[ZZ9uGyW$oΉwogӺL0z+M6nDYTx{sooxZ^Ȣv\rWTTIM7~4m2s߿@l]w8xp[/&j7-pҵ|K/Kv|{o{?x+y>~'0Of=ofe3?ln ?q D(!(+͹η|o ~~&|v7-yEb+rߣ:=OB) &*Aie###TЫBh`[+lY86Fe,.. YjUӌC ~Q^{):eQ=JQ]<ÙUL0uyk ]ֽ^ 14 ʫ3uhzؿAc׮;nۆS%_ذaf1[|]w{Vβn$kP.V/!,ny'y9@>{ϳ9c$y&QةTHLvd2w/ktf:%ɃW/f8]/<‚?ٜ|.x8X?8ɗ_;$Yz(U)$iec|owl_<79ck{}}2+ȏ7G]+J4("5Je<c59D󡳉Ц.:Gii8KUT6i#9Je8mpօWUV0",asn8Y) ,Bc ֚Z)~0T8˕9PA,ˉ:M#HAYHd Z&΁hR>'OE O%Ko4XWIDAT.M0!';$O D"?¨ǯ~F=V>_ؠ6`e, :ëXC 0ţ <28l]$F#jRi#D(jWRuEYJY5@ LMCz?C5 RRJ c" uQ) 갼!K0PY+MUT JJuJ2B@R+ . }TbH>5뷰o,|#S$/(ɍ0y HY:>eIҌ;( WW0<˲Hc:)T($JehoA:1)n뽴P?HO"jp>X_ +K3,P%,CTy 3S,aiy$I-ʪ ] hJJڝ6ceIۥxkhd9JΓ,eld)Uc},ZHwz,FkO`B!KSʪ6Η{4F T15w7=<꫿#հD9g]LTH  ܁S̓uiA)!>t(Xe=TcυhUJQ SwPD)yNi5,,)4X-5-OxPLULs:Il)EO3Qƚ-rP 2kqΣ$xiY:4+LN2??LZ#ٽkq̮9$ZT,o?_zcb6:ƝeWQ`EU!U:L)(be/;HuplHڑ +($FI|Z2'c-1&B*6ax/I *Lk-^-JJ#4IWx)ZS+jPV0[D7,>$(Kd(BTJ)9t6NH@ځ<(q0ZM/Obs%b-8&!>@I!U}zzY^^MNP26ĪHVY2992Cu`%~~UQZ Ɛ& m-a34 -)"h0j c>YJbP$Cb0 όʐ2C#80%0FXA;?LgB(0SF/ߖ+0]k-/h Y 2aIp069"dt=.,-!`y_ŝws‰'07w *$^p-6c,҃& S3i|"bPu|4oaCdNV{p"Z΅Y?dXu؝ EmJI`8*96;uƻ0wV᳨}9 ǓKD"iq"Ptݧ+@$-r$ AݜK${{ h̬t]cH$8!&uyoawmڵiۑœ i9PQ}>]A"3m/|OuwpIbmMKM -Ol$i9\2a,82SMMfRY[I۴ATOW@:%IHb3Fn%{r"BM͍MFD֫O.ӟ,XUzn횹aL3dkjnc9x &LS@fVaLˮu;v+/WIƏ}ѹ&Q|)*޳dvClF.2$@}y\+owIcހk^њI T:GXrIczûbAƴ_<+oLnHt|ɚ/뎖Ϭ?anHZiQ|CGd<''l[nϼ3V4nh狟= P ^w;_:>JmfFk_fRa>uˆCjQ[/iKP=3ƙoV?o]QI? H|p @J $1>uowYbc_ O/{eroWWu,7kqG5" ->[UvP\Xyv?{P}}ٟN+[|bx4FKfTݧ\[#2+9g ZQwwɎ1h#q }a ]ƭҗloƜZs<*J p!mתjVӡۦV3*>@řWHnѢ٭79p1|wO4fΛgm-DHu |誱aѓԮ|43NM+ \<5%W{X~3Z8cjç'N]<- %to ;nn?x L&Aiƅ1qm=$ <wǂdTNhRC@Ug*la >znV -EO=l=o׭? Gײ!T܅8HF}-l)H rfT*kcTcfѷw  6}'@YD ,.:u7^{nV9\`Z2~ά o\yDvcБl^]") &tƒ*`cHih?siYޓ=ɳk4Ŧ\wEKBV]_qxGܑvcXqsѬW= C96}lEz֒~)peMOٙ2ag3} K V=~sՖd[3GLHi7֝JHmԎJ:~ג<{xD}L[g"kR(^q84MGX}w$l2橳/HPuL'>㮏OoXа5Ǵ cgl @4dUI ]=]p{N@"Ywq3+G1@f.rk?{ʰR|7]\ ePefŧzwXg͸z̩쑀3PQx6k T7_\}Q kQNִ8,L)ͬl/?opm^%m+:3ϼNƻ' zÈ%'+tdVB>%IJL=1;I ;:)7pI[ L:+u_HWV2m B5S)@hZ$Z4鑀 Ie?SaOnY*fs9{$iaHdR-#~mEJ%U)@Q!Oe$;29:2Ih21β4AINZ HdS# PB]SiU7NT^LЃޝ-LӣZG.boGcH(ֈDr"Bu W~FBgD_-\fl߾/^%֘g@8|BX^,f*J늾H@ApD3$\TJ($bp癀`ΓKo; t{OrXbX6R|T*]S]}Ks,XʑN(RkYox N~I1#UW-ʞ85zGHa8^nIV6T "$0~|cLwf6i^ 2-M,ˊb' Q!@0 COR8-$Ri "iO>D 41%J56ԝC=` 3yh9 AMo +5*;0miCMdyWܔ>U~]DXPe ̚&c0 : n9` HpiJt]OpN|XR^zbTuÆ # 䓾XsYu8g|+f[T'cƑew{>A}v DD ǧDdo!( EQ2cœY6|U_2xv[0ɪ?sST:kfL3mͿ㑿]G+R5 #Hs9 Q.Fpѷr*dwoN{[IGȘآE~HoV&65AL=Sª~CzRP@S SfO"8A4%mHw/&7M+Oʘf4tuTY2kk]<wcF1d~kힾOcrtolkg&-˨kN[]݁e/01-wf EirI{Kn]uq= Ște =xfA7g$5M 'iȋ^5t6kZtc˾k'UB&ɚW! !E#!]o|DgoƬџ\ܺ Kb;xĉ2 QUޔsJdH"[ "c;O4APjFm:=cЃ.h4h4:&%+wҰlZzٰFB3T*yKDąp &:{ٴ2F~q݇dҟ/m Bm0Tw92ip1+&Eu=N$'xwMzwU/0Գ|>8X2s/=rCX`9Yt]x2)*?J ޓKa;g>pT%&(>}7Ttݤ/ҊA0BiDb$pt%n/.a9$ntVftRSX zEl;\}sodL3upGΓID p%"!MeLeH|Ѥ#T]?ڶ {4iuuuH$kjk\eYD@ wI7z̉gSN̥|uIa-Wi;X?z7?ߡz"2}>[UUf#}²@AurC`k *Y~]9ؙʤ udCZEHmu1/]ɞɁh\&p|sሪFGmTTkC2JE7Gw[%祷e$zj乭!]uWC(_Ru}KDžw~<\,hZ͛eƌPWW"WUBp(81.1x;]@?7ۖ꾴nˊY~ uUDf-k};*@GtHcnnڱ/qvSs 912$ dc|͊:(jqUUU r}}}oZaYpHo( &~ŇHuu(Yln1DZLrlA2wu4⨰@mUcUd|>EQ\ttGJ $92[Of3B}ʴcpį <k0T%*@U5cڼ .l%nciPfмijD R(:C?އKd$}WEBKɕrBTjMrA[vq(3ݧe9TX0͆=htv2]cqd>)D?ȣ~'IKǵ:E!Rh/2/捳B;aK0XeyQ";W|>N䀘ػ6|w. z%:KЂCrF.U#[P:!*T|KiiE44q LZiÒ= )8FTLgڻ[\A, n*xŇs?y #WWLTnۜk}?9v[XplrGIR䗟KmTʕfrAܨJF"Y%-̽8]?tiT-Ds5S4]gթX{0 RMkY4J2R'4.pPiĶ4+Љ3mABJR9'`Ppj1 ǿlTt.PsQ/L~b}Dm}מε 8)v0V(\ؕd#Y[' vUl0Bz68yJq4U:<gE=[vjT>M8\JwBNXJ",neA!^֫oO :r8t%]߼Z[>X))XE>؃ U.S(ιq^ǘ F\؝bPI/#J\`@,CD%yE%%VE"*>ec8gIX2ZUtw!G+#~a;B@__&EUU5'-Θ~$8)4_*l/eCʰfDDR8C9s⪒y~>}1l|W 9b#ζx_mu}kSl:k4x1Qh]c+b>v|7qUѱo>>^7^/]𙗷nnrBTYl_R]p-W91zNi'OFɨlAy]$j%7_|uwJ iV: tN\c[1N[GWjڸ!%}`<i5B'q'z6?&7tI6f ) [r}`ߚ߮j~&2~tv%鴈G_>UFUhnu~4koqٵW^*4;δQ;v n(]cJ㒛{S{^G N ;nzc_ʽ M$~k5m2V7N;r0=OmX1Jc%*wmIOQ. K|޿iӯn]zw=fom\ufychn=Y%XwHκlճ[|Yus8_瓿YbͮUY|AC6}x QUôi D>oFWWY}pYWVCM%1].]ǯOuu ¡5\t⪥WUOeOv~u8^|u9ca C^QYm^r}M $; Dz֝ͳ>uz{OsxOgF&Rn'wm|88%ӣ:+R`ń/T̷ttAe6^sJ̬Ϭys;^1!HwS4p_~Eg(#~dϿ1mmLShXt֞J9HD<_?~^m)Do?=3OS˖1Һ  Zo45QqfeYlҵkܒ OٙrUM>{תg^vf-pP{z-na+g|rĠ'/"R"ꅣsՊi^99}+__EuCfsu}KGw?^9v^gJjtw Oz19gOvxUՏ:0`"Զ4g!9UMj bRH' U2Q#\=*DZ>GH:wrg(I AۖE"ݓڵsXP8"R_+鉟޽u߮zѽ^VksG$z4OW|Ԙ_|c^d;'~T̚5u܉uv7޹g HhSR$"RCڂH{4$r $C$!Y~dMvKSXٱC\HdH‘$P8KSq\DvL([ OFE0*hwvZJUUPO161k 7{OıjB,S2M'R\V,JD̊遾}k+]!vK±2/4cq (C}0B$<#+\cU7}G-.x6ў 7a1w "I$e~Hk7U==:)~_%?y2"pl#Ma@{COW n9&D:[I<8CT*zȈH!t,s봚\YO18HV2'Vr)/f|ιR{{Ў[cO}Cnyn/a7N$Xpe皵{>W ׏uT.ٽԺ^U|a-2_ܴu˧$w7Z2 4)gZCH٣Xu`Kk&5l3 hw(r-mIoe`ŵ;WnZF?R84k9FgN:+[g|ٱҨBʚ)_߶zŖG1E 4M9KGyc ĊokZu}$-Sѳ-NlX'dsj$Ȫ/rN3\?. 5O RHmBɄʖeO>og>=+}Tc(qpZs\+ʫoZ6R۶-._V@D%woێB(/tћo?U]vҬoh},qEnm"r;R;3=AX'O/k‘* qUUc_rUjǟt)jx՗ׇZ(cR2Q;*'r15tUWԇU۲޺|uT0y}n_(}BtX/b1/O>ecjhƢII$VV@UU[9˲ $Bd9s1$2Dr F-w$Tޟ|$3C p4LPquUDR )*-o!w>iM2Й[ -AR {"']bC-.+ԭtuhB9ED~qŐfoJ;ʻ0\ O?vu_qw}?L@,.JWQ]ط^?7)(@w{!"TP|Xuwh` PJjZp:U|r/H:#E]H$p64T$Kza IDAT Q,-|˭]Nڑ6}oo \rvo$%mY\%3> ]lŒGw4RJF] 2ulm߱H!ċs =$w{$B&dQrA(͞/yb cE:gE۾E9Br!{a#桓8&]vţr]͂L;ׯ9h,1iƬ*$ߛp艨rQd,BBNmxѼI50ݖ =Z_LqwL7Ϧ@E"X\r 8#^|qYY.,ASꚾdmo/[{  ۞v`(6MS1FRx6yH4HtRRuMmOW{  Rb"*8`k9ˑdro 6H$E d@xAeZ/&{iI @ 8Emٶ1~ Ͷv7lvlgm۱7-t,۶82Irl۶m:#msm!ciDDwQܲ5)+$$A(Rm v,(6Pl( bқz'Ho{̼?6tGoswܹSN3#_IlAv]Aԡ fS<{{ }ժj9sZ*+/ wBb6F\1oZBrdIU}FRB#̕U%%%XS|C1u'OJLLKRj4/uk(-+Uy VHNww{~(^5OFh<ǜjՆX*> U`dd޽ݧJAnQJC_WNサ1bbK !colYq~YIAey Q1UMܥ1T-TIR.j5z^xwns"9Sl6Zek-Ғ 䧷o-r)fs##nk/ /4ܦ.T*SDXxB>0`n` hU!kf9sƔ9v"sejFo_{^xqpA_?SݮjdF]DQyHN1 La!y']d-,/^xw Zp{``HyEp;q{gʰZL!VJwƍᦷ] @egYISsɎr7ݭ>[]ZaߙcשƥE;*/.q천u=z5Ͱ|J]Z1TY\RTicGrNwtt\B|-uZ^%jpB!`9BqO&smڝS"sopBC'[Kv>زM9ˏ4cIWgsAa+x}2šjtyP 'jM[ټY_|!_vB~-iph᲋Poұy;+ԑr-״wZ jįi;2ʇ/~4;m>]^~U۰0S U$!&YOyD)Lm篢[)~1ztcV}Ƭ*nҡ}Aٮ_VP€bBBߛvWUYeeY 7U-޶U8A^D`4:].vw9Z0/poHוɹ B/_02RcǑ +TGiR?,-On %/+w&=Bԫo`uX0%gǦ,f!μJcU/_1#_YQᗻun?xYNXr[WV݁9C/Θ4r#'ϘvOo_4k릙bB.qvxjݛiۨqV|ײ(g"a~VSYzyMNV?ݿčޣ?Ȯ<לeCg/>k !Vn҃eJs#]~ݳeF\6v urtϓFʃS2g;e4MΟ3Gthau5au!nw Ȉ^>텇@^LئNdysƙP/zePMu{8?Ҹ ؅ ̿crllCks(;?߭m&[K9 PMkѴͽ ;q^5d˹9<λLqr䇺d6JoNfO]1wfM[p !Jfyú6h{mooꑞf?\}~ aQ౗ q}1|r9Ւ'3d)#~;غ!&}cG LܰreUQ ~F#hT JZQi9_u2✅RĩFϯܾ.\; .9 ySN3"2G^uw.zRj>:덏"&eʜ }n=dߔA+.ڴ{]k9Aqe@V:\q}K{h-VΌ3uʢs)Y/G|Eg$y?Oֽ6gMy/% f;"c myO͛gIdAij1Yߴ/|7~>q][{wD->X5!9poYKKWqthܑcKZvG +G=(0POG^e/|sV+hm0qr|o޿O~>m%^'_\b`tYPm!]yrLXhf-;"M$LmQCC##eFnG4|1̕Z1tF8~}*~|o(2+:ᣝHo(/Xz6yy0il񤕏 ]s⋹'Aq얜G1C-%nm7QE;fD#&9i֩z< 4g *s9xj/7׸HEō$7Ni\_宪9]A*+ =Q=R@^+˜E(&}$K ʵƵ뤫L~qhiP Ұ^ҟmG=-ΊӇQ/ fr˺qrز)SL3×>e9GXn0Jqڵ٥[,F2Z^-DLh~/܂*)ڶk hJrKi(@nʘS lۧ?+\';i+: >&6r sEŐtz3@Õ&=[ljYa̋7M Oڿыo0o ha/F5f܇h .G{cP}7bzwx7KrjJԉ-0T."kECfNɥjբHr8nd nOo5 ń*UH85 <Ug>Z#dUʋzroǷ>g}a@N QaNb*:ڍL3ǁ7W1e<6&sϙ {Yvn?+5xs-ӭpz8zM|?E<iqṵ|)2/ ˡzp$Oپc 1nS-(6ܹsZ}7|v\C,_0>**[h`p).姩K2- o\;v{V-,h.).\4nK^PzUr]/1oc݋s~s,ECtRU;}##xYM/Y2K^gB8cdB ƯMH^TIBή[4{8?SQ4zPdO[wgB\?0C@v衣+w}̮a.,[v 0DviGA]ZGP] "X/cLVi#S;E@|.+;UmrAFmN 簨Q,#F%1joK 扼uso2!v.FF.D﯈);QZPPOjEgVc+_gg*@,Wص1@B^~e 34 ڣ(a3ilX #YW=[l@AHHvQZuQB-Y ~,WkȹOOMeB  ZG`;'7ϝuUÂW?tqOl{/4QJ/bOK׭N7&m!Pl'*q 1/=4Sۦ;/^{>7$SW-_DOZZ3n~\G//e4 J5k!&պӓܫNfWǶ%BBBc1Aظq$IaÆ*^84Hd2]mv$Nk`\pHurUߝ1ܖ\)ylsvSD^  FZ`F0D80*~8l/(l6Z ]d=sgu=&+**bgXF#L4RiE5k7oqJ5gΜ˗-\fku*)әs'NԫWJU+ k^a.*iM U!+)l~4cڥMZ%xC]Y)5vPRRb4/ E}5n tc.;~{V]u^b%[0u^vfd2nq_1G$l"ܠ=| ̙3QQQݦEj ˲Faa98NB8swk2er ]\8\_OD TBv:[&N|紉}cLo3B`9r s蟨qDtƀUm< ү?ozX| @y̵kn{_zh?߱]rpnZ݌䂼'%%i4vN700Pe]RJ)UTuq2`TALf|<>.jcLCGs,m_1LkHmf+ *\"UZ/Y"ǒ9R`Z~zC亍Dy!L5t4^?? r3~:wZI;Ox3DI8Uv1eX*/4;ygc<ԴLcW'5YrA0:vBBcJillСCeYvsQ1zNJkRYO+ >O0[~c~< #ح(WnI(El{v]ve!"S B IBA9FBnP*e`w|My!#D)!4UxJ%B/,HyL)0"AxQY& e^sI (rFRIR(C L  "*Ph;;ny9Q\~q<'r.=<.Xvܷن82$Y>3fEyBۯIQ+>cPI&X8!& SdA- 3!T<ݞGk‹\fcH7QBm޴XIs|6*we$?=Ѥr:+kCbY8@jce{wxz;A h(gwe/t\Z4:LX~vNGF6NϲGpݞ*SRɀv_? IDAT)I ˧ ϶,t 6|^.9){߲+РUưN!\EP@Zá{75l1 )عk2;HI'@J{_F̀9xKB/‹X}| h2FZNV),c/0Qg\"K2Ul޶1p87mv-Ɍ\¢e' j(gh8&jdS|lnV9GytjB{TRxl]x| PEfDR_eCq>mٷA ֧|hq?FQ9ߚef<3,Sȗ}{uBsu~>Uπfl2I]ʮᘭt5xޑk8Z$S)g)@݅% r邕?fw0/cS8Z$D{O\97˂r3)a~1-v=o`^R+IF R4^TD5.j̐ڄ艋( 0¢6} \wns}A/p(1J(.R( :^r)VA(+.ҷ־U 8.6:ls֏caTd7%U^ J hZɼ'YJcer/O](CR|0_ .B$J$FeC3ݺ}4onmiQOBc"_h4Yh."u2aLpBa1:U6i[1qB+Ā0%vśd%aRf'.ؑDzA>V2# ;O9QL{9091Bs=q{O [PjCjQF0jy(a9A.$eFh~B#0 XzV?f 1Jg򺒹K w͟ vS`d{Mk;?,!C& bbaT*/WSWWWj)#PۈN%f 38ѹpϧBj  F|'Ľ=,sWDPB!?©0A`Ja*FQZ1 @)ei|zD)JFM/RܻoHm{Ri1#@&}}Ȓ6aeo^TзcxfߎJ/J$ACT3_oж[ A.k]6s.tVn*&:tlOڬZaȭթRRb$|Mǎ )(ٰnOx)l[^̹wS3v#^}#{0ϣXƘfFoqz5&k J-$ Q}tbZWn>RBmȘ<*UT"%.TsJECZ@HyE!Js !Q B)%J"0Fd) RFWQbkS$""8/39KYv( 20&)sdbuc ] a^+"`LAP8yPB]! .t"r1X `F/2JKK/Z{Q̡EEExQ$''5ƹk=???<<#iu4:Ҩ 0N rF ?:08!Gyl @m@V2!yk8yW 3I.~w' (,Ղo;!9?^j~xlZdRoxI4qûN_P1WZdŋ;Rm!,*C@V|jĩ4U\D( *ZNK a^<J$%{u[Uz k%AiP%#}w;eK{rni{2cs{ww{ӈNiSZt0HiJ7)k{7=Q3Ş>g:"W%w4)!FmK?da+{y-5r1WůN_i."s r=_$Igz,5Xj;!SrSG |j.S Q640{[)b6g,?S|T4 \?;?t=fu2^w %!pV9*Qpm29-#_'6! 𺰠("Z9#qfO:u xc=G&;sGm Q <:g֬S2<.VLooOzqn— Ϟęo7̛x.Y_/Wg-R 31Oxc@FxE10 :OLTVIqUgm?np$~󒯷8.}qH{2ujctAkg6"3(+rѠh#'? A`T =NHÃ1%J_]h@ @V<$ Y-FM '\8 v쟵Uyk"cHW<+5k{Nڱ&/4/tq90EQ'dE_F~k(d*:l`v74Z|y{U-)h=/:IFiG*,Fj*^SʌDձf]SF`~Q<־_7KKz4vQ~r֫4>h4^F@ \zj߂J$) a^²ۖs*v1sƮI SUHv˴]b̹薉~ ī>_QEv W:3iu[IWŪVOhӂ}2:rn?wʨoXuw|]R*g{EjAnVgnts7}ќ_R#.MN/Z[vOʫ,y]dUQ%`zvor/}] ;)M1ǶwYXm㟟Lc;j0Fe.ŤKs?ol|ڵ^j]Dީ , <ף-:~~5ss+8Ya W[; eXf%ī z_Crou.6 VTݶ3 ,`;.|82"2AsEŹ9:jIHl.KD'oyÑ-tdy_.@ԘWL LO ~O^a BuVA9Fl+!8r7 L;wN7Vi7Q7N / IDATy0)202k?!Gn#v߰_G%}` J'7A1,>U|@Ɩ;6 IY?DwJ dG3O>իQ$\V 'JO;Cww"}F:v]QL䡑Hjc^ħQvqzC KvHi00ƀ1͋?GPzЏr^ۤc,;.JЩj)aY(ړ}jq۶HUK5Hkv~vmDZ/nʈ8^@s6`<Vn6**fڗzI) Sj5 ZA;v@g䨴 %[)ap,Ld=]5)4L||WڞUe#@8Y舉O5 xrWr'+ > xsSɱBdB8"9DN@Q-.rA#2@qZ{&<-TM5, F.a,1_*TBbۻT"cԾ"i.A`΋oo2'SY^CA%1a`W?WDMȺjܟk&<7\U٬ŔbHoߜb3;@e9jL܊7P^1f>cuYYYxx-++˝N*lULVtq1`o΀]( : R2s´v31 A)mHBdI xO?oBJĎ%?̱żVt5fLD*G+xAx_yqG1"IG,,U<_2cJqFD"3EmG![bw|'wQUM-7iB(;""( RD{DlH Hһtw| ɓr93gLl URZK CBeI늘cօ3#kq*/}X !۴\E/Nr5^#ņxp%%Z `I5ل'Fv{jyŘO郷/i-OJmRkƖ?CCܥ f]= h'N9j ):* 'O4NFQiUj*8([A \VdΠ)d[5kpp{`P`~}ؾtssZò̮2ĵ(@r W?Yy׎CB](D!߽g?GBB qhH)a[@xu[j}vl.#d " LF;V1"]⫡zf:¯E`cF>5kzfQB#"A.,A>jj!!Li5[b>ڛc:ŽU*JEP8xp2L*ը***W!T[[BQ9$6jg w܂(` #0!cGŠ7%0B`.EQaaaMarrSOjj#W詉%DB.3_}J7~Vkņ^ GU3ǻkXAQOk޾jzt QrsO3o\KTȥerƱ J(y£oNR7C84FhpfB ` 6H;O zS >Z*-z>&^wuumB ֕S[̎F۶=ukJ1 2 (^k*kw4C,onYq#O4H%Fe޹;7A{܄.'O^n̯+᭽ejOswwqEb6:uAx8jd͑7v`=N8=jj C3O7{u&X,6ΪSj本\!` -VN8N'Z=]C\}[^Bk8ΠKd4䤵N8ˆ]XP>'olٹ9yvBI#!ƬLFN8N/Zds|FDVJ87N Ue,y3.779!ɍHylAĿ;ϞM:<\~pm\\ݷr1=;'=035dj9 'YUUUU9W y@/Bٗt]879O!#yøG?G  ]OA̜ oIÜe#9dSS6tяvi^{}IB^;m+̍[?z;epB JR[p oۦMbɟm-⮗ppiq[IJC!ߏPĶ#ZeF!+{{֭?)__*B89wDLtdTW/B̛;-EH*88~[N̎ +k?/^b{nZk>:M6m$Ą7{c'E=zDUGv)Y lR<@doNGH=p߷{(ϨmlkU~5G_臱/R|45^޹D^^\$zFujc1wxdPdTZe݃4ʮ~ɔ[xҶ*crY!qsѹL's==r .U1]Bc4{Ӛ]慌6 0e'rNjגjVe uMywƾnͼt In-~xu+˞p9Tmŕcekɢ:ufZ2WV(>e@W f=MkUmS:9B*/-Wr qwTYӕWg9Uˆķp<;rI=cby2&=iNۆ`/Ϲp%(Pѱa^,sѕW$J&ֽ +R=wNPH9;RX~`> Ev1j!D+.\S*!06ey\{v= X|T' wފiYzygMBgzٻv{|{hpYF}#+kn|\٩Sٱ5NQ 9F gkϮVd\K(H-κv18Z]kV9CeQ),JS!{#[zf*: w))///wW:rwt 7/~*%/ =ǯmyks$cלvlo^WTP#CŜw^, tF?-Ջ{:mn2P|8~1_x2$}}ƶiYiIvY-RYQ^Um7􈭶UeI>QƗ7,=,t/n\6}n#O{`H^[Q^^K S[V={Yfn0dƦTqlg_HJpLV/ߴW՟ٗc[!N>HoC-/qd"6f{wO9XZQ\j]#O?Qzܼ`.&dABe9OAx~w{rҘ^MY/xPUZ`bc}L7uRV1dՉǭtz` Ɣ<*M?wG[*AgQr{*=^?YGOtء`p*6ޣ8>IJl+|eFaQzcj1-ymWTA5 k7m\ݻ ^y9 U%oeKE<_Heƶ?'^I Hb[~(VSٽM3xpԌa^nbhsV1o{>^T0y|1&<3I .8*5*EHTU}}}ܽnTqvBT=Fz(d{&vho}DŽM)x/Gw4{؏ƥX3Q]C947´R?߲X+:aȊ]ӳ+v۝hOgǀOO=gh!>c^9x޿U/L:y6Qش#VwYG~tGӼy?z5t"2_y݇<`IncJdn_8G)'{)@sL&%xZ]1# =p))↌ʰzN}I5eėδ|!׉7u;bi hOe1Aq_ׂ5# ԋ퇿6#sSr&bxG7^}!1Jx'Fm諴%oU_efyDM)%n2x,\U{K [^Y=mOw~E}S 9y)o|sm>㛀­U~u7t4[j>Yv #秬̱z:6W@@Gޫ~L v٩vZQ+% eVY[\lWW#Ud z{hxb+AQUŹo@QBn%{~n~U!ÃSGW$FWjָʼKө|P1De-.B:Ƙfl=:c"C@i6 @WCzpW) n\W%EfػV :m})7>x$[SuR(ThR8 +gW.`wj>X[ٓ'd4a={vh 5vțdTԘp2fVQ Uė~x1^vk'$g V\6Ko` _/R{i hfZNB:|>GSY?[o @6c@:QUV;3Z{zhT2CtdC:ńl+O[/$Aw@zQT!KsN^-X.~}F|tСbe` K1+p~oX@*[1b z5ew8ЬP'V"^o @ݶkĞ >|L$oWI,]7!Wϡi@hK v^gh`6NZ<ɆFo[AdtVVxG/mQe4Zk*M-~͘W~.XjM57D1 Jbnb IDAT左ٔaJvs@NjG /\Xr.YVM(\ٲ`ΊΥ':tYb)߾LfT 4= `hJh)uVUp'K۾j{eN#E?p ( Dcّ](;O)E%tf9o'm }Sy q7~ڄ'V؛Z!a,ɌbAQ۾PCk{vܾmt-\BYeBC{<2XLY -k,et(tJ]&fTr*rӖ:Prxɴ2`ؾ+B];x˒3;3zܜIm9 !<#5!/MmٜTwXS?Elw oeP ryLTsiwWz/u7}ji+z\tGq 2p@׾ n xU0 ub.Xx <ę9T_#Z49"8$@ejڸ Y%cX3g;'V9%I~Snm na}wط%}u-C fF~eˊ=N6M7OB!, !(ڛ=VY7EXLov U6ٯ^ RL.ifSzW K11Ad_-zC[nGƫp~*⋞3{緬>#CYdmBI8LxҰO_ ބe`6Cr]ΖʺN^>%Ӧtx+~P_"J,/>A >aZgX1LjsG" ϮxiXa3Y, Y6 ?~z_4N+ a#ޞv oX9ۉBq^oVJc,?Rl;dyf7$um&~66mx{hOZ[KDxb0HY0gR%2m6ԨGpܜ;N8V$ಔ_z 0nG:+zH-"HaG/E@$MKs;%Dz 5I+n}cdlg0Br/۸x?}" tQ:e`;cΝUڔ>y~7EC/MoۧfWb %={^oW/A+UaxYd((<$O\Փ&;,<1,@´eϾڈ_a}uRHV~ 7w%(T&>0ETAU*rl`hc庝oٳ?JZbh /.*ܶqc"##opsƎ]ɟ4yYԄk 7vgA} JBQa|5ufՖW zvźh/u}ߚ>HL7Ӧ0{Ab_Wդ"yN`̱IO݂:b2|lnsθ~:aT`onYs66 U/_T鞝1.^_nb ]'^]<6Q%JNk7(8/=82]vJeCVr71-1Mw+^?YG9gzCGz5u]d]AhtEf>N}_Lq[}&-;8)#ZXX.Vڡi]7^3MU|~Ҽyۄ~Z][\㎲[! 6 i^mAOQ!o?dVݞ/:5Z:vKg NOM)-zI#}7yǢM_[Cqau2[:eYVF$!-~=D?(^R&"EѤ0\ӣ 8یv 4E,F;So$SSRMD (Xn_QmykS&E@(RgE-7 uc\nßMϗ !\}=Zߚ sn]1;5~i;=m!?λ2HhLg޳GIIh+Ew#*͢;]QZRt5V6TBTzjvVfYyzVZZjZryOk¼j6b<*Kn&յӼ2 uPozLnf>A8Ss&g{ ?xmp,U]hs$S+7e:x0E,$W"Z ߳~ۗ+=ZJNK>މ,FOG;Ckp؈O KEa3^>-|TƄж?T~ZUWYJl9,rW*`uYyHqN;V^\ AU&|x<==y?tM./RǯS2nzV%da;{.rm@Lb+K|b k_{*I|je/0l%z >f~^xx߾>[8e^"k=ig{-i3GxE_{3F}xR_NN#co84C}#7㿉\w+| ǹ}w#cshLdYNhЮT= $;xt))/) | rӓ|"JU\]'Nh44M `ZvfۿO]WzBQ4g% ;}(#e Q c\7fdIƒ(J4O3%LBB@$A CS"c 04" "DѴ#c,I3c<tý hb_w_$_[(YiـXdr3M "Mе+v0㠠v#8+++ l6MfSt@k-":, IhH/HAw1L$Q~t=5/%KM,p 'ܣL rh\UеD2J<"BN.$[SQk&#X)'^BQ?$Imx׌(FM M'p‰G j]$VVګm4AR\mpvV!̈́y%XwwXsmF$I"D1?p '-HIHY42UY% Ea%FVZ;ۂw4Bcp+p '켍0WS-y1MӴ((_ jɉ; !ADQIM;D߿aȎE9Ȏ1fn}%&W(~ffWTV P DBVE,,X,ߘxLfd2OZi9q7Lb2eaN/J<EYlP2 (ʯY!tߩ gwZOw~W_4e_"c:M99ڵlJRTyzKd W7{yў,2_=c!09+1( 6029 ]Zd9\]y$J;nKy!#V3 #YBahQrħ/{nGTWZt6Ή̴L.CIRP pvN{SH#W*X `f0$p6h9Ɂ8rVi"6e{s+J1rP5%$Ө"\ۼr.EiBbdrcgL`i$6;'1=H^ۚVUk [9v=T}ђDDG]$IR*Uz^hM"(=722DYSW,\‚2.I:_MLyYGKĞS)BM rhwZ8YTS.y3|ɚϹДU+jO?wt\3Kv\S2I"^{eI!m;Iy<·;~ur@.,[l p`(†Ϫjټ׺M"w 8lXDy# D, |GڏJ 9Za9?|ןO|y*ZNnoNjYfLݰ{8Ӆ_<ɲS >+Ҧ/gXrhFW+&^ (8z\ZQ-ӹj kj͒݅AW#\ht!2<=X(~aia,hwpB`(C AyW:[\I ҌS\'G_߽iARΜ9cXӯ\heeŅEX=X)),exI>e \D+)3w06\9깞Kg/>2ݙ͓X 1;>q%eH4"`QfKivSN-ۭb^#)^|>8l76vE?e8,$l_}$ kMV{tErf]'IgY,8өYGu^\aovo'e">ѱ9quvy }oijvAcIiP:"&}sHm;=:lX6ۊ*jI0oZLֽ+(Id.}j/B0&`I$gZz}{-*{MaZ\0 ʸ]U4vK NXDIeCH<׆oֺspvl׻ksRXT|ȗ%ux^,]6wdeƑ->wjm=5sᗇϝBlefXp 0WgG5R eieEBv, ])7B0qcYgxO{f;V=[Qh{˔@/MLܷgoA~>ŠʊJ&feLN[mFG !jc# DɞNd  `ZnrH ؄%WCQ~]qSt C\n+4}+ɪ*& I7B(95btXp6g(u˨ K`){9QRff*&D8LVB1N%y,bKHu^@=!sޗk_̽2]S<췐/ʊN C:>?hSg6o)OEE,Uw^ssa$ ~8Kk̫FZoPRڈrk̴ #t0R>ԩVֲw@LQ WRk.rfIu$wVzpvEY¸XK? \ݹ|CPߞ|a'E3gOD+]toݥ˥EE3 vU v %b7d\_l"u97ĻZ \)|jU{OWPd&hA8(00TS[ZRTPP(յdj%S(JJ?|D>3&`bߌX{U`L_>hwxǫt [?eއEf͠Z;R*~ 2:AĨ),{ko=?@ͷ#;Ϡe!5 @y5dk(r{Inz$A@@bAQQP TH; !mf7 Hov99gΜlZ:wom6lS1S򖯶`ԦFQ( u|jo#3"Ez[pƌ -Aܥ'p&O{Q"vLk`,54aBc :JAc jd`X8kBhѬOPOIQ>;ෞ퓳o>顦Z9>3_gTrTd-k@yNaydЈ)KjTNu/<3sdͳ=๷6+2^ޞ?|8}ykƼRT|~(-'>ޞBpGz??i! S7VξD w tzIF#L|ݺѨ3}laANTdF ʍv6jYyURųv81,|Mݭ_k TN:ulqE\LE֏yaRɏT!݆voRA~ґs~:=;ߖG,R?ۣijtՉ `uyN+Y?}D%9i5D^`YR*I>W' 1V˥C!*KZ{~ʇ}#XQZLܤ3NBFW/gIJ F]o@eW)'<3fgјSo"w4~$~2}HO?f#ޛbș/=]) T$Bwu]i7]N{l®oz 7']!za2ٖS ~~wظ_EruWLϵ{3ZMY+7>x"\8e3/=OEMzD !"s$IeEd0XFv{iq i|o~wq!Ф gxc'/jѤm{% I~eƷ콇}vnK*piͼLPCY9|9 bQ>>JQqIV]U'Տ/O B1at Ak/d.1^%\)7v5%|;G &k3&u^LLڧ]|ݺQ$A [!&ll=l6LهVd͞s.RJOP ѯheҦtq\cW1v!D^-S 3ߔf͹-ENV&mnH/KQZ?:ym@`t%3sv9i䢃?t2h]jW.l]C0):`1o>^LZܗ:(?'2%֧qn_i'/ڻ1ywoOíHG7ytz_ ƤiwXcSA/,,s|{b]j5ߘ+kb_n:) m]DoGA/v؞bXhh1!}WI+ѧ舖}^ٚJ (>}5~:pRR&Z.^i5xꖇ>ژ6d`CB~t&_A B# ѝO(!F$FQ %b6ogWdX )W iv's :M~?'{WcSV1͙z2F#wto52`;}bC;栀\e0r+,11bIcgбླྀBE_ZS Y7wZaj]?nY:*૱"[i;u͘q:QkN̛$]?Eޢ$(|ݯei+6Wﭥ/0 o?m@VlY1&⻱cۆ,߼ujf l5m6oY॔$9KV蛭۷m_3F,Ψ+9?o'b9HT7~۾*M8Mwn_7.I)/5}IoٺekM]KWlK<(U:IȚڊ#LAm.n4DcwrE|J&~Qv_gy;ȩf}: _ M E=y`Iq187;+97؋8Ӱ}k(]hk[߬Kvb?6Af2mչe EF B#@~i55kg[Q sD^Hx`&=a}h}V '-H@sI=Cu Ķdќ!mYޟ4Nӳg>XQ 9:֜_mHL-F׼KЏ Rl(%UT̔pmS!/>[/g?tjs_2̯EHHM+l5`bߦ,2n{3}c5gC@8M3X7?unGFE*ܑ,(n=aҦ'F*r>@#U0W͌^!O$( Ll>%ޜx>]!B <&Х6{%b1ONp| pcg c7X2ރQ^n29o!* h5sBcώZ/q[|*(}0ױbDeJd221# "cꘝiƐ Y1` LE0,- N(f0%!_A!2vN!$?qXg ǺK SLIgB*,VV*8wflid뭂)),xmOVle"{h @$m9z 7 M٧]8|jXeF̆;qZ> bLzb/vX\7oo1`pKXxOw|3VtMHaA$c  }}}9 :dQ7o܈1@7{rS&Y܅k=׭]/GX';;q>JI҅)a"zlk:ʖI\4kX2D?؇hDI; F0;v]0J@_>ƪe)QP@fpc1 EET)idéSiG>:$;c 4QWQv # *.fx,ɈaL[捆LjM_tty\!sù {`$|OR'mּ/oeaX\ $+Qd ohX|b9hBxIگ@R}YH&ӂ%M%0v|RKSrA,[{юӛ#ߪs^0򝊁U7:,VkŋAA`t @H%0TQIXtD)E -P;/((4ԞzED^.^(@tIwkq_qa%*nQNTGy--WT>2 rxʼ_<{ʤ?e|tpZ>4b`~\άڜS;P@%^q )3|5eAeѵsǿs8z#?(:2N6oq7s/14"")V\Up_J$-Hen Ip{ħC~9aP̷h%codesІ[GMl┽M^r9fTv9˟Zv;\^?[PcF“ ,I 2yOA*l3NQ3l*Ӊ^4rNȝƿ߄n@)bsߌ'DZG@/nv! uS>u!ݹ|-~HWNks~mf;b:}GSs7|^Fy}$v uxl@M_̘#.$ǏM~6fj}}5wK}돟N-=5#怗o3rTѳFH.iz iBh'Ÿ~O ~ud浡 dhNn@`&.Α MOogvmgJ3SBzNjsp(YZo++}9k^Lq: Zj +,ͱ;J=*ɏ pX~6jժUKO k6cq$ZˆrNdf6 bX "p6΄tFmrb[頥œL&:؊U:Rj5lŢӕ8SO') M˅hc.e#mFRv&)[` !!lS?R܆0ⱧfFD9]:ujRhʶ2e$]r+H_pTO%#xgjRRSљ| qslD|.} &Ս5bP p&aut4fAȔ 5|0xRϞ"m'F=_]I92o3dgvbB]2m\ÙtȅgǙ1' uk@J2Ο,&!fL9I3K=Ma1dϴ8{֌ Pəz!c "/^Q̂=3)9PU#w@#@ ;SKј#":cY8Vœq:WNRz:թ~)w _? J2@JY+3vSMNLɗysd\ .JNP$Ps`TpKK9F<ɥ66̙3uԩȥY'O cڵ.$^̓8SDl\ۗ_=SG/ BXbceBRv,}1skF6Q @Ŝ .CĞ疁Ck6υ """I߭nܜpiѢlL=2%(8]ݺ ?.᧒gHWfH6m ,x'SYZ<*s(j:5k҇C?xmt*էT5J1N+jZs_ ~x{*MszpUֽo{{4URTz+eզ+D\n23?F{ZKreꆋ*%/-dnZW|kK6GGGj|TUMzDsV̞С~~DM&^V٨2:[|Ξ-ߨ|O* tU>͆~7VzGՎpbc^zie~uv x巫 JE{⢏{؈6#؃+?[uv_EpZs+IkɬgT] da5WϢVv^*_Sö́I=[loݦ=7GH͙ܲ 6ば{MJJjpS6Rm4oǶki T3^n}m|5,[ŬBjDG O 8VxAd0r;]R8\Z̨HkY^USߕosqN׷xeǎG jk zәe˖|AO %*TPMڂZ#i߯X٪Yu 2 cIW()$q}B *+ۭfצenA#?0-+}˶vD; DUPB=‚R I1FLIQ#NGzF #xdN*V{ckx&PdA\*TPq/8B}Fe~*JB ׬OHLdqF @UPq7BϽ^y(\dr1|f- X,C[m;vLmʳ6TO !`Dxl BH Sʢ`Ӳ׫7 F.K ba8saj=c^!}ZFaNHHn:ky {If]H0{μ5/~dgᑝےJ)j}hQ'Ԃ}?Ķqn~"fQ?tK2A@WkGw9QprڠXuF;^*Vw :n9x_f]hHHIXe{qju@ɞˊ YnhД&9s\b4zVou,J)b8X~sz8%zR .7Zx Ž4vT/,r *T'`'8")d1zkpb3o=^$oh~\*!11Lu `chN[F RrumB>xñ,]TQz0+<͏=+9 {ѥ+RӚY8Y7$k3i.$OnXK#X!G&k8x r<GB c0uCf!5hp>i1Gp:L}/6&!~hJٴz_:v";4J֟nZ@?D&#Sڵ  e)f!\|jyz}ךzSۂq %R w Y`u>g<%>G2?q rWRyKZ`wz{]qWtTrzQ卟>"J* Z&?H?cZ9,̦w>kp Qsʢ/=#%.ӋY~NƳ-69 AtAe|Óu'~ : >,u^vrD2=9Z׳tٳ QS#O)'7n{wmnQc>%f/|0&NrˈUPqWSb/`atP8NռAf2~E?t09AoN_1u61p`Ï{GPQt?E내X'Md#6L H>2 *Eq `X,oJZ7Oq[UPr,\C > ^G~}7G]ebJiHH𫯌`k֭[,+ڳn]x30ð,??rՊ% lx6-E2?Dkײ:>&ɓ"r ċ|ZP!9ju вEX9C%_ƒ޸Kgݾ̞2mؒ:}X+%U~LÊ M ]}-7b닷uU.E΍^yDAѲKkH>`{V~Zs([eR`ε~ٰ1\"r͚5Ek/AjeIJO=la_=ж(iʇkĚ<6n;a#jaڼ0"s볟f34uf+l]V׬M=PYjU_&{%T lAJ!gr`/ߣW!V@x$Z9EB3#}ޓA]d)~s,Pdu~c?|YE0a8x!-f\kR/i[zk)!{|xL`Dؾ]ۧzmaa!@eI,$+ԛ*e'$Y!@*.H(rbB!*KRZX־,W *|֩xSČBo9@A}j/_ƒzϵ׏|af_հUza>pٶ c9ȊI1h{3o 2$Zm .gSkl{tCB4wFd>ڴ/f_2Mw\6>;FhzY} Hire_G0ƢRB8 G+ȸ^ 땳kc݅NR31ϛ#f͜;rTk%:X >dA$ nEit mI1NLLzR7sf=v;D嵲dP #_ H(n,14a :$ud XkJ!ؠ.L(p:^5nO TMƢ*sfHɱX,ZVk ix6Su|EA=6tٛ팢DYNaU[ ,jXB9BaϞ=[v{ݞ0 {> X*pR%+ ! qx5dA.zb,kHn=#BAYp U 4yeY@vU"U$S*)A\},Xu0PBŽJ_@ saC_>\Wi*n<&S9sf*b{EL *DH/QEeGtIooZKW \%[J>SU^`?\}Y^~3!PPĨR~c2wGzPX*c@ `+"E A¯+);$k|\J2Lvѭ`GT22D%NEO\WI=(]}!WDlJRyrB**FixPq_#(*0#ZB F%i)ht FPt)JD649y{] XJD++K> ѩ=DY} D\lբScD﷡Ih-fAXzU*-hR.X"f-]dg\̼$Msa0 P^cHukLż ' j| gmPzgy51<ػ֤PE!TAs߀0Q6.&0E [TuK- IDATdeJ}B.TA[ˑy17zMB)< ˝=ccR(^r_c^C\Nu‘?I- zcT[YIBnp0p it1/ϱDboj\Z v^]:$卍T/HE*ZѠ1˃+ڡ=_yG|Et8stOrP9T܁FVos,-)/b0[~sIYp갫:/_%>kJFf3Fw?k\4EL@OُFIihI)ˠ?pP"KNaPY @Idf~j)eU$QRj>~}l(A R`J0TQ@uPcYf,ʸȲFg,v$ Q0fDvtUVFr׮R)⅋1}zN /(޷(mT* j˜ 9Qxjٳ>vC wK=&-tXEW6#^e ZeuI QY%(v TSd0| u F?0?0,=, Y>T0Q5֫؄9A~6bE=䍛l!ߝlb0(-NˮsS9|HipL)jWfy7Ko7 e@̎:3p 7LF6g ~DHn{; 7mV*6RBU$!fEBJ$ >+7] BBaQ@QzEQ"c>'@-Y^*04~kE30=v KKR8KL=WdQ.C)BR)΂"őO8D)0\DcF%%-]4]SF> P7wHKRDt ~RYâŪz *n¢Cb4"oBohPFJtSpdǣcE!*_ [7:${Z>$+N:?}4T1 Ƙu]+7@{ךt zr@) dgAJ1BxSקyK$K&TPKeXPa^sjonf`eD!gg1,pM=2~V׫tMϽ&'%ubwG( /Dsh{g1tC |&x!ns8`Tޤy~w=9;Eg5rUѢWP`ǓoYo0QJf8$"r.iQ>/4@S bܷW&鰶M5(-9+3r<_.֘(TJ6t Ѯ׸񽚛X_s|iM)cxhs:iV3NTp{#>4AqmfxKDs>mhW&Y4cXǟsHr{_7E[A%$"ANR+^RZM6훑y.01jRJ(õy*),+gr`kq:_Khur" (SJ) ̰J *ȈDr,@d(peYIt)U$;o$8+\Xt+T%%kGn3,Z䤟*gA`wqi▽~ܒp}XukEMu`PXM+T]`U{D\%hVgE)!Fq:-BHdr: KrNկLy+Ġܷ yj?PqX' \E#nNPqmPJj?Pq?R{IENDB`sqlkit-0.9.5/doc/static/images/rim-bi.png0000644000175000017500000021647311714210425017572 0ustar sandrosandroPNG  IHDR=ŁassRGBbKGD pHYs  tIME; a IDATxwŕϩxG(KH $$ LN&`.qm>]{Y1k6  l@d!@PBi&xc:w$FHKG3wvׯΩS0H@@ +.XA&@ CuY " # c DJ@>tcKXH" 2ynW{Po8@C8)PJ"c q\"!cp"Э"E$'>sSJIS3d:") `!sO8e)_p* MðZvĶCӦeEtFxt( B0Iw5G2_]Ӽt&lY# )lm1aΦ*;8Ḓ-Trp((+L˘8tw(kͺc{;J#=vչ)fMx孷YGrOш=E"]WR\FÈTv4q΃8)E֕Ei"2 )1^>0bHw MהVkNҕJrH$7cJJ=%dP!HJS+ `fp4=7b0}AN 4H )[5HT ]+kV![[^/ " #0ɪeq3'ҙ4)l;7'sa'aBX,8:0Č uuÈC!t+׭ͯMQW0MM31@BqCGxH")|' 8ky TFU 8tyu 8 8@>Z'4A@@@@@[n| "LScY0" @@MlH.s K@mUVbn LiW[-M'az^(Bs{[E箶bse]oiho÷o_Y֣fVHÃ;wn5ʃ҂`q{k㎶nml5zokw[:idX>Z K<^nb@[Tz+9O/Hm!!v ,m 1m{_=k\#nt""d<!ô,agt\Ҍ_䵿2βvZ!o!qO{mS[KKuU0BV84N6t6~?eWGO#a65% fG"`ᐥso*U8rSgO eeC:f@L>Qrw⢟*Lh[[N; ԳvAduu/o_R擛JO[[yZr*JE@;|vV~sÎe;Ϲ,No]?b^zOT _u>ʋΟ[$vFS]G;2SN,gM5;g$rwZbO}3iujg1TĘ1`2Ҳlvѹ\rl+{L疿xGK@[ rkՏnݪ< fH' eۿZe;˦e0ONUMm~iS]yj=˵o{/Nw Zn!0]4,:Gsymko{㌙yO}ڴ\}\yȠof,?̳O<íwE/-_)rZn#Uq݂a$WRz%]vۿD,{ۻG_Ml=TL5?~|/>71'?C" 8pk+)^rWc|޲l983psxگ\:Sl@R~޴.%7:׎N#DݼK7ߖšqZ^ {V*CW<<ަuuNK !)}IJד%5˷fNץ3`Qжo{EvE}IH3cUK kZ܅D^n|@e ĤytY܈-6(o|s'/<^;zO_9 v4{Fs̼$Z[^"K ߷gVV@ b(չX(Z5[s5Mf) ֐dx^]_~Y ydJq#AѣSb$}t|Y _B5ot%DY!)ȷESQu8C? Fq"aYIm)IeT/TO-̚;kk?\1SYuZ疭ik.9pU.)O H'c%GOּnISTDE/a5tlAwS~%LW׀C`)Ůw_ˏfE vG+"c ڧ?{ ! y@ѿ^ǎq% ޟ-;#Bj,{*89p#-Fط={Yb {W4'lC`~+GqĖ1Wܻ}~}Kݵ+;'J{au1x%_ܯBnMT{t,Ь 8X{P=k]!kV@GS#=f1 HV@=qk@>s* +-VGE ! ` t+ ЭA\FK6cc a$ў?޿J)R, &"]38`3Jқx1C M t+"*Y>L* _!|!?.(CX6>q}pƄOHyלBzڸux92\Ke2D8'Qt+27oD9|=MӲ?>s#RUߞXfeHBN4~kc qя'_ t rr++uw7m(+;>ۉK!RT2]91"B/,,L +K^Lf`OM3RɄyR@>nTGtr8h *)J)"b1۶}y"!r"kzNNٶ=08hzs>#Mb7߭[}%9R D*&qv:m =b``*tww٦U\\8*d2ܼ|uTv׸i7n2m*״јT:۟KR*D44bt`E+?p``} -"*XN@_ˇ% _ti "CA ڴ}<ʊDN$KT AJ&TȌU MV2_T{Ι8P\RjxUkK3KCJ #1u;;**?V^Ad jȈEa忽'W "B I'_5Moq6wLҺqSxfƇRI`(|iaۧʧH9N8pq`p!SR!tƩ(5tBRJs"Alok5M1=/nbUFEx BH4\QYt5nnBdd''']*4dDRj3!")AJۋDWӏ:ޯڄ` HQ-kZ,wUuΎ#O+G±\DLRD>7|;h}K.[y㥤át&3tKBX%,M>wy' JE/o: =kɩ0%PS=kjهŸ=u1ph'}ĝ\qQw]O:>.vE=;<|'klX웮}!JiVZZ* "^߿q̩8R8N4MRaٕC)i8m);cƍJ_xDDc#+&-1N^q{~%t#ZYU{~*0m{=)4~A""R HcŅʌ!j'Mjij}?t (RY00<4XS7)'M6o۲<8'_~c9hcҺ+.Ь׻v6n [Vm.T`Osgϯر/zއ)g5/3upduv]{HckҚQ`$v6oMJz$wzC o+iں s[|iŬB"Lj붖aX%!𶼽u(VXfҴ2~wG#I&Oȳj-yN+YinԚs̪];;" Ӫ~|,4Ck׶ΨuKJ/ϏUnh^_}mOXW^s{faM=hq|3UPq]|?u6E3}9+^x&n͛giUߛӱ=(гvYPuuglٹOOpt3.wxap/G~g(th㉾x<9)((7}"'kr͛7O2e``0p8|~?+"!d29888utthO,R3޽̣&S8s(5xלz=κHcV4Dl=w.M[&|QVV6{GDym݌1"{3ߗRJ۲tu'r}O#beUUoo<m+tsq@D""G;ϓRJY][9 ާ5Ա8@u\d8H8E$յ\9 ʜuw޵'i}Kwu_{AGyje]X1VAL( ltӞ}ƛOO?xض;‹[;&Q.|]P+\qϿE#kٺGDD-Ӽ+?6oۼOaG[?ʫZ| ӢPN+ej%O\)΢soT ۝"Ο_~¶+[^\Y]m/g;3G6tiguJ}'_v:Kf@Pӏ ?܆O?jr͆vlN#4iQ%%x3LMz6S@bBdW%kiZYYy㌧[XCCJ)!DoooOOOf@D]cխ4a\!ľyv %HԸ &^qUT\Ð ڡcl) epY8'd{veZBDs1ҙ p84n[aI2HJҚg4?q>ꆙIg*3$s}~$X86 R8aˑB?%x }+M;|v'Aas?v},C;?7 _椚}b$Spkl?̛=3 He9pm&ϛ FB1y31RR!|Go741{JЙ?ngLD錔Ԉ/^*4mAd}o ƌU}slasO{3 ڽs^PZM};o(.ŧF E'חJڕS Qr9b֖?/^ ْJ1Ϙ߳bi3j=gO!O/B%D:"t*J 7ΠzWg.9O]0yÎ}kxB"3/}6\~1={/pDOuF_דh0@Y‰>1H|88r?B)holklNZA g͜9%lfvl/8]ͻv5P6crRZόhmmm3֋Sv7#Ĥ \d'rr-t2y$ϋbap9?ƘR4t:Lg\וDdv$dBvv`ٺe[$5fӎs6:/wϑ5H$[-رM"n))|'!7{\FfDwQKca{.nEpw|Y9A Q^B"MtI , H gHJL93=Ÿn"&{aPfD8 Yrz=Aza/"iV5RP<E@ y!f/$2B2Sr8c|'V'lYO=k3ÊHϤ79߼v늏R񬊚#󁁁d2u1V\\i[tw{("0Mx<ZN&i'3.٥ǺfDs y{|x.mg"!beo>ʊhu nЭ+ Vy~tCǿ<7ȟ)9{\>p_˪]iOUKϴc9q!{aNOܿbP{د *-kVWO&FBL[Ϭ1|$漕"W*'O0?.rb_)}U7~`A|uY6&c N#F.G1ݮA|z{qDs)ŐH^lwqTKfHgNi7&q7:EkwW2gtQ ;5ieh@R'3"ÐmYϋ@^q9:Vu "ȚDDmOasEETx ic\<2K|j\C}T< xu? ‡5L'xkkK&yO w.מnwqLfЏ>{{c-#fJ^kՅQ)Y'NG6v O=ל}G7\e+h[/8KPq׮==T/k{Cfݷ "R斯ubćs%6'Nٴ[4w !yJ,2TrAN,Uys6)+;Sokg@ (WP 8eZ!s=O?*p*D5V7u晗^iCQOoJM>2@ MyT!J% rJdfF*_#r҉Q V}tC ˴mx]_{aK?}1)cu(XNM-;jܴDc9]؊&9vu1R2;qSMJbiۍKrp߳SΜO\g_Η<99yя>GT~5-)odͷWWXolҬΘRʋw>k=:SWb^ӶnKcQG0h,+rapl޲ -N79s^|U28w~'(~13f#ٝʕ}V0e.X +6nx ~k7|M7%'0dH1FL GͳrsA\!"S}/Hmwܓ8so?SkWWVK%;PQGxg羼j }^TН^ r&py7޷~% ]bdPlXrchׯ=y턱[c *"Dž| 4Mr]";񎟧ίK˻4R9Ő+ۖٲ#atQJ"[T2e!GDkTD9um /os̹_|DF—pL'i+I!ܔR1d uit; 'H@Į{ul=/>#Qu1kc?ؓnkOv5nYx) N9ضk;.:sF~ٌO: CR},kV흕]h+0Fn_u%j:l3x6!$_YsїϜZTtu˝moSSXP]9*il$+baXڸ-wx g3=Kxxλxq 46-+x~K\6^~ _rR,{f5D6\2Ƃ$ICy{|~UiXq)ɏ;UճflncR[pTDaAy!jD) u,҈yHJe{!*+Eo_|a~5CK@I#("%8}/xc$' zXCF>栗NBOv:ɿ=7}LSvI_.$!\~'8 3cHȏ_X|ݯ>|!Mr-Ҹ3N -DRVdLilclE`8\=or]rg.23؟3gR G~q>Q=HL;3fwޛde34+jkOd!S:/| }ϝuy_+t##@THl.G8! y0!=}tRrTFJRj⇒pe_兇[Z޼!fd"`)5NuS$ Rd)`s]P#'Wǝ͚wrpl@KW6ܰݴzn,މjǶر-ZR_?.9+_ ߬T{ɑ%Q}/)$+^߷-}xnreS!9|j ΛʡpyrJ~m=L{pE+w OsO8 |}6LVNkS0ttF _?'ª) zQM8U' dJ(k3Di+o\e=C 3κ͛/;w ' CGƸEP2d,RR)W TSuz* ‒)B$BBDBpdW7|֥aӎwec8!)R #eUS8)hr‘jJ UDf6+&)$l キ I }E>_ ~?򫯹:m'ȇ < C%茭|_^ ? yWO4Yܹ}KۯXXYhq"hF1ໄ EZ.qBH$5`"֝vz˸iӳ#l-Pm)Oi '#1 \j]iZܑm[i!e5ð J:O!e0$) 5JEʑ#u[epr]O(Bٶ|ux `-ih8"`\,x a 3 :zoqML!I!|WQ3y]C.7 (WOErcمH >FM] ^PpM_Eדr F ,Ң 9.J'N?3/h|_mt#p-JsW{~d×}%]w[~=qfT, N"e2H t{푛qQn;Ұ gKK%8ΖȽsZH~Z$3i9^=G-뻣k7P@q'hGg:(uUwϜ9sT$(HV1#*Fb DEEň`΂@ *AtcvʮOٞ*GDp-%Uq2@cَeGcXXg*@6bI1`6q"me+s Dk%85h;pݨ]<8r+ݳgOm$Qݎi1 w9)G IDAT{\ƥS~v[^E=oޙ2Ho,~ݺx^b(۟5,ZԵC4RLo]c3 7U?ij< ~uˆ)/)жF4c,L1F]2H he#DqEIB-)3KVmNaZ/opB${;"( k#G{Z:~fH%H c;]={~sNu };#}go/mYO3}3#:hѪ w'Y~PVw8Mա7}[^pɌg^d0eZB-˖ӷW̉5nB05;篂6' g 20tF0(H)/˟1ЊFE\DoזYɎٍt?9J&Cn!x z6Z[q`f&!!ZW|4g<{UhqRR\D+PRo|5Œ2SR֧狏?=lzvMmZv3F<~-&$+u:omK0xЌҹ<{iNx[RMI )^1ӎ~WoOJLk 㶝""(32)=*FT!xJ&&b=ۧa.ϲ\ѕ˾? m;qXp;DQe`e0i3Dt9J dښ=ĆGBJ)J"JcMhق%a\E%mG:2-;3+J%7̉4Lcu-Z4ڴܼ~Azʍǝt0Խi}D{3d;afܐb$,3ԡ[m[kCI@~]PX?i ,4LH|P2 ƈ1BB~k]1Mp'5YNNZLZw4-/# J͂V,d$HeS^cDsϻ|ܸn݆_V-HХM4QZzlγ?ա{G fç^Z4gaAIO˗w=5s>~T#]%͟7G}mٗ3lɏp>EXxw?\pu}\rϽP mvS]&]x'ٵ|C~zo8_96~iP"X o{^}Q<*b|}IOӾXL89 /ی`h֤|m˞]&>#S޽+KK) H _{5j^)Vgrf?xv_pGc𘱍؟I 1nH9difޓgg:M>%cxLJw"i;Z&4` A=wr1[><On[ S$HnXr%&CD.ILk-SJ|~ҨLT|K֕)Y.s3 Y> 7o8I>cF7wiy~zݺ[M5N=%?oas4ʪ()啦?ѦU =VA]ݺCM,xp֝;./ݹ}Cֽ ǘn>[ɡ-?-[6sr䡠OC>`_s' !@5/m 04M@)+d>rʹd-.k~州nu$d @hc;ញrK}\0ʎ; ` m ` "/cuz}\\KG\D Z&l ϯ}p۰#3'L t@vW7 iSZq=&bcDB L؎"rI4|H`lM@Mخ`"O$d0A[&) qGn~W70^$) !٪^T 4HAQsdJ#I$$ m+S2 I.haY޾(` oA X1k׳.ddskRu,4C `-oYo7@-9HL2#T+P`b2|L4i.fc]3}7> ˞/gvns㷜^t:ux/}l s{Nz-1|c̴Y֪ǙWW|v a^vh:a2qG C& '< kfqG\@mB)eSuT)DFM@Bi(u4IG(!Kk)P1,!ZB@q`*0"FJDW@3L)( ApQʑ}oKxifd^vYohY~Ȋ~um3f|9 [e%{_`]5NNT)ij;Zyj@W߼&v]=t{ n/!C{[R/SfEmu 6Lý:7og]=Bw?,Ɩr٨_~/Y9soonմY{~+&<7J~}q{f|kst*/ܛ^|dw)oӠ=KF<~Γ&4│S߽8e>lu"(h22 .]B#FE C )nI3 $ &%?07Nx-Abb597PDPPQOhy%IFeHB3asJD)ByKHJ꠫2{<2UM)+ZZɍU1##@psgY?JFA`T)O=6Ҡ 3e^ .YκBϚ>͒dZB:x"I˲nd/mzwid:pWdIg{^;!*˶n%OxqQ_p⅀ĭ^# N"aE )]Sڽjaջ=r r/d.n|mypb euzEXnjtm|vBe7JXeѿXy^/wL]s|pQ+:(j6WJ؍'nJǢqEwnn@@B4CN(2"IP%^ĐKiRAjZ윋.xa[?/8מǦ*ɕ#~ @!Lilܐ#p@B}0BE 9 $j͘Ј%H$ ) !ۭ5&_v B%#Su {_k`AёwӰ'6=njU*K"2#u_~}]~zqTW)K!r"P=2PWR3O 3p*>*@x+ U4~?Љ q8k?ѐ?zۋ^a`(_[{_i֍WvYzݒfѴS߻ C<<)b ȼPI:ap;Hkdx;4id IADWylOg}ۯV M֌*=iRֺxR^t%Y"rF\!/M3'Hff} p g|S7g`O OFeU7[[@7'\e=F2m龢¯|%X0=b[{j3o\x{%48οdZn;*7}/VϏ_XQ׹UfQi/F r0 Azo) ՛Z6SԞmnl4ҫ%;U(k";fOW~fA"=-X~ќťՆrfzϲl" ٝZqN禮-)(%k@#aGbZX,NN5ԕ)_IPRRI.4H~}&PN&Tb:~1C/3O4V<2-ADL!rSph1 9J"Zk̈́@k5h+a!*EBM^=+/+{[N}I?z`Z!Z'#"FDF&2e3@$ B8y/8,7ol'y,=n=5jE_ss "m='7~NgUaJMSVӼQ"[]1`7/͸!YƓö_m/OM8~4j ??+;b;ᗮµu[vC&M~Dm۶|fM?`Ll;2cEDddtE#0"9*NH4Cx’Ȧ2_pjR̆ s[w6`H΄C2۳Q6՟Ii!vR RqT6Cƈ;qX=-N͘|w7oV%@^?Rn|ATZ3 hied9D D\K$×l `ZckhyLoODQKW1ΤTL+JyE4i%pR%7|׹KscWH gJi"G)%0C0DF9&b!P) "ƙ`Eഔ 9i-σG^WZ#\K %qWyއ.HLp NnS4VJ)Mq(qU7X#* IWi 8]E(8SJ!#yjZv}K |XrzѦQc@~ pNN8hSHwhkegr;"@ޞ#] B7 cLi͛B}ʟ*U '߱"%EE"w_?kS>q0>JZb񖊊2kѡQRd[ O2N@ќEs _:0سyۗӌy]Dd N&#L#jЊ@2]"DWdB24CɁ9a#̙_LdZ?#2H 1LNJ҄|zBrKXTࠕ)EFi數& @iYX\zB\RJJVf)R-5w'RF%̄7^N֮q9%s aqAɪگF%UY~5ꋑRdTυ 'Wz DqֲzQu##ٹ`P*sޢEv̱ 8"wLMMap$';q&iGf -U4Y皴0z)(t"95P")eu^cu6m۲m{FS^PJ(Ϝ 6jyI5\T}4ͬ϶9DZUoV].|` bh13kҨPbݴ_M4 Ӆ@L3T! 3\ < BC]ΘF]Û!*OM)()o]#`҈7 .P󲄕 1r2Qm<EI֕C 2B`(v(HĀ5)uO>*C"Hp)TQSO{tGTݒ2g8dHU1BaP?iii{_=p%џ`ӛ_|}ܠeb6(%igIJjoʕl߾= {dd!;7|,I^2{e6m=w}EJZ(&o_"w=s_j4긅&29,s֒~GY~FK+wSeu>2cENU<0}L2)IB7{ρ. #s#)$ "^Y07h jDL!?ݑǹs̏޿;vlݗ_FLμjGu;Vne2*%kv\w)'/u@Hq3V|ն۶ ßdAjWF\BJw |XRGH$]nYŪv8 L)lKiB{ȁ-a/[Qr2rn>YVVּYӺ!R^z9|%X [ 0*vJm"1F9_;䆽'81cGʕGv̘\]jFiĉX2/B>1oFM"-r׽/T"mYpѮ2;xÁ`6?j YGwVv-XGǢc[5vR_Gi yĚw$if!iCuO|rEڥ9+.1uJvi3n̔w&N~IDZo7z1_uY_䚳.y% )/s9[tiلw%fNeߟ}J 4'kz-] 1֞6&wm[[N8ֺ`OΒC{"?ф=c܊pT"0,d IDATMߌjpi,HtKe6ݥ0TTTسDD  !5~;B̞#n߾74=y O_wyy?tDJjيvK#Nr@ 4K]3޳UVa((3@Dp'.oqy`9eZ8P{Jvԥ훹:^*=㈦}Ż?x="lQ#Z&D>cZ[ Ӎנ @h`nvm%wp *秵G4LKjԬIU(띉oͳd\rF'<o!7CppigyLy|۠C9˕9Y']×.裩IZK tjq޲]6G}h?k[!W%Kѫq19&PnZ.v#QH$tpq0 6t;$f?M.+nYe+%­NnwVIi1F8JW*~iֲNGfy3M Fbnw=~UkoOijXn橧n())9С-T_iԑeF=7Md[/ٛ5" J]7f}ZsO;2D}Q8*sTl{ 9]ƗǃIFq(4 6|<&@dHFιԺ݇ª{9l.rVP\$ A۴A.>+)YwGr5f5mۥevu'DtuKO>v܈GڠK:u>Cd<$?xD'ㄈhţŢ,@rjZak'7vh5D ՞Ix^-8a<WRHs;ӻ6YyӆK=CyyaX,na۶e>vGJZ:oؼMWJ)qQ) G-DƙD5=MrFPz534^tٵ'|=}"5 &p,RP. YI ܲg 7M$QeL|&nn^܂u,S"h_]T,D^ 0 ]zS"?5S<^@Ƶv2WIHdN"!i?_,$ 35?<;v==k?[`3uofZp$oS?ػ @ר d6m'D" dոc,Ƕ/ӠIVM-Ɯw:u$¥d6>Wj[G@c]tg/Fహv+|K?m_QͲWn}aڶ@ 23ػ>{~RXv|9`8RN2A֜s0\Wftk }{v+%j%{h7۶I o[/&ծtuvr$gHDK3;fdUo7 vq#[DZ!;Vv x64$ ȔDE,IsI(PFL25b`HD5CNJV\> -פE˻_xb)6lWߍ.ظj]fRJI( ?, &4idOꅿ\v=xU\A|i׍7>*Sq $9zZOHV"IJlTˀ5\tD`uZVca@ JN 5yA>_()dUMjuP*+/G FMҏp/yR*%%qzU.d.rssD$Q޽93۹rH,'9OIO,KQř!w^3 c_7堸iRa# p4{_a +/}=[)HPG8A_슺}eny\fdYJpSbBS$ZHdnZޝo}?URo/~w]aM0狣פ߸i?^8w^؎>Y9_q]ML#Ӥ-$-F Mpk`0jЋjHWVVY9BkSSSիHO3cH`V$:aq.BP8NOO.;Ac JKKc^$uee|y_ GŹ9?=+?c`VosI FrR\rVbݴKgZQG лu]yAk?>8A4-.BIFv2Bl6}ݍ Iq|A"5I cQH VG1˦_J|IkS\`u 4[#!zbZc =i`䖋s7ry{ֻm^0#eҒ30c;t9/RL߫#ꁻy O@#bXwtsԟ" 뽥vMT$B)pBkVViYYNNs΅BƢRJq\וRz*m`c²@'_)to!=AV*M12~""IǍtL1U"h|3GQKIӖT+k $Bcڳtc^K;CBDMZ+V` =گ[ H "bP-O CkRJ(iMpϥJS+N|HQ/@b(UIGc_ ; &''s+@fRcv̇dBjLK_J9s[zy@:u5#`F͝]gsvfyě.M1i\Gbմ{F[y̨_0ms*uCa/|g_r>yA/[>?m}yS\? I g5P42=%M1p$--VauPv1gϾzꗕUXͷz !l...9  LBp=wisN0ܷ/vzN^oxp)I@!Ej̒1+VۖڸaƩY/yģ?Gr&{K\Q ۿOfۖq\EwxԴ>=K&'cPuK#v?^+WSZ07n7-dfrYRoB-r9,][;ּpc{A9ZK%ЉGʢ[+=nRP[!bԊ %^ߢfȘ"8!'dȴF\xRxUk0jY1$I&E}D0툴J"CθtPjoڲ,˲Q"̌c{/c I)ѨRJ)ovRRRj+,~ DFh"bԨmjRJ E&~~j0f Pل6N#m~|7Dz{7~L`|kQyjah MACO8QXF@HMpq45k3#O:G&Re?0V$}N'DQHk@mJ8wPG ]a+R G5 &Z\Ee"TK&H(O5{ͽEJJSk3MAjDR(b+8̛֖{o*:_rSX\k]sifѧtU01~P9{~i~+ؒ KnK]u\sKxògs5xpYC~D >yѺ]8G 8Ԑ']}bkhx]t;iPwxޜ ]Zz+YYQXK{P:gKf?Vſ֬+b{g7l g4:wf>η[)eFƉsnCq!P0s cJJ1v8`m7t×3D]}2DLNNi԰PJvM?ܲl+{>㜵h<778l|۶MpM<IG=$DLNNnjyn2&un8r!V},v9Kr!Z c]iZkN;f- FcHD䁱BOkܞү?b~eP#H!SriIDK~JHxˉ35ը8Psa (h1 K2iٶN &;-(jKj&@X#e)d9 2 Z#W HFוFn|'#Kල ҡR -̛ؐI5uNcVgOdme{EyεBwď==ƿ9أ}_2FAZOkm-0}mw\ud>7Ve:`& y;>Fίax7S3 RSQkiKeNUd;`]̗rzwQDeJ$Q1M#b!x˓{׈;cAfqI1V* O oWm(Wtiْ&Ox"vYv/ٗ75o]cmvdet}j܁G|G5Hb 'xa8 /|^ #_[b>;c[zY}w͗?Kyz$D4i\jivdҎw&qUIdԲ|pVxOy߸FרjMnCIyY *0tDB+,i}&<zLJT8'29?dJZ5J0gǸBR44A`qDb42 U:bLBh82X zVU9$I[9P9\`<`$bT(5~p)-%qe[U J 8ػ0)}Ω²$bB1"*A3W1EĀ5d@QDD$J;gv$^?Utγ>3=SuBF, C;2IȎmkDaF,n$†eV( ! CH KVh$ak.#I $ la$aғud?F\ǞP{2w y@eIbDlLQae\"/Ӛ۾|ZyLȮm{B'Ptse @tl߮jݛ;#Ck]ӛ;U^m;PWJM6N2r"DRA3 zeWLAd2ID.2jT`肮%ϝgZ!$Zk" BvaӫլGsiRh:z~!iP 3$帕jU!{v( >%f P{w1e[fDߗGa(KwD͐G:eB$aXtB}O$LK#8/HHL J-'$@%ox h*QǥC1S1fV2%ȕD~( 5223030)Ϻ{3mFДc?}굪'f‚Zw7_)V}cۺ1^2S~{dsy@9/?/~Uz}v=h%SV6 wݳd7fEj$ M@hv501(\,*`Ǒ2js`^]˟w@^?x`_f Z9.:.:ߎ@ L2xovjXu"DJ$!+}`Q) 藭:aZ&)ìs vO1>G%{//.*ڵu[IV:^|v˗t[1 /}Xp8V:.Rjw>p%gtbK6n}'xmk7dTtӀ|d3KM>{]UoCM[Pk.KCxԽ @ #.%[>4рPV;ܴ|3:^b99fXZێiҪews@mHNHaV满*TH B$UIPb.i$䄠}>.m˖-B1szzG?Kde];vO)m6q-V@9ٵ?m~?=[j{0{XHJQPc KR dڵ'zta0$'$a۪` g'̶B&@o>1AHK@ ksc, $&wYSE5+2 :ԿMDӟVSzG\PZq+nE HFE pȖ(k. OMH "GE4'iغ=zyT"%ROZTLٴu${ۤRX)m|]۫5kq7g3__`[{/O[ \Vs FC^uuYݻѨM  (" (E ʵI!!0Ze"_+Wd2j-~ۀgB][J<{-UR n1_S~&>p\2: XÎxVz퇳 I('OݲnSx^3}֝[%$nrڤ~re͛7IBe,oղm?jP/vŚ?  ?Y|("a)kvPaja;3y2fY߹7;w91k uY(P H$BKìݰU Zֵڻ{C8lbC(³;_yZj6b,Z3 <&(S*i9B) [=?q\ՊS}.k6`qh}\{S;v;p@ON,8rS^CIiPOW j m`- R7qĘw7Tqs F~dMڒ^NwY lfA?~}V.25/A3F dbNHfBКAHfv o6ۯuni8,{ 5G#$ccʚs_ifJhRSes_z>“]b7Cشf!Gi PkEj7dBbBeYȰ 9p]Z0˓L#a:lEc!1 j"`dY}Q{L O.Tcƌs9a.ܹK;>o@U^18ξۮ?uVr'#3uiעYLR{mλ"~-۵i e^7oqդE#Gz5o}d.zܶͯ6 0uO=gF\s^ێW1KHz-[=ek?ҺMg,yك'M9]놽[˛:: Yuڶ8v hX{՝Zjs ߺ3;\zǏ}<=ߙ-uaBDg?^񯃡7]{F/( :9n1BX>`B} 5i5)Eƌ.j`kr#!G)( ,EB 5Fֈpp !6\6FM$FؙYj`fBi#" G"SA(]͈H+X VLS$$ih @ P 8ų>d ҭ}Ot_\6~kȴ] R 5kG/}gNQt],^tOXYҶٟ}CF߿d/^io-mhuw-[t}r٬?{Ҧ>[]̎eOf=tT^W޼BV*VϭO7w4+_bCtPNp/:_جOpO_Kӗ~vc?cԌYӊT;=q˜.CކYhI~VFܾ߯Z7g'3y}׶Ok\zEwqB&TOrsݪ7u fZ?S# ( 8ÀqS̊$-.0<$ր5!wϡ0aV cZ#DPG7qb^)J*UMf ppbMtפDqR4hК 5Q^B`IkN2`AОZZK @pl_S׾<ݳe  ᷇nҙ~W/~# Q|~T}-J:kFi&@izy8R[piek {8nN^`;uzߚ ٮfξ λsvI_]ݯTTp˂5ӣZx~˴/55?d;G*_:aUsVg4muFvu/m> 6owe\ӹǝ{^=uc׽RѰ\ Zl_ϲ"t<|5 ŵ&m @hM[W<o13 eNsvr)a5 RKR"kפE͈(D(8J3IC B`帊5Țv!ZR b֮$F&wiCmuo{啫\`A]e2 b=y:! fQAB0{Rn(D) `Wū300֊aTtxҋv7/}5gM^,_ "!n\8JH'n<ځۺߧwEُIxJT(v5ㅽ]ʤv9iMm%R7NPd,erj\tڅ?q9=-P m@Ҏ@\!3pܓ:N;]>黏 J^ׯg {pֲ^_G5qEHJfwiWs࿷+C%MѪn}=l+B9?kƽme%C=9zJO =h;Έĥ?~i#-D\FBr4"@O8Z"iS a&I5 y?h·/>+2yŸ澳xƔUrYH>x;H}יFϹ5@~:~w{ f}.n2VK@k7_-'KW&mTqs?$a{wk8C̐_pmzc[W,خ_y]zVb;#+yڌuv{֝9k߸̝lC#{㶏c_tt؄~GS_Q߾z}WA y?}]E7=4Q+OCKz.dwݼ|YϦjkmts[ڀڛL\ +£9$@hdFvlD2r2"fDs#W 2JB:Ό2b$,B$Z#UUt3;}zQݺ_?iqv=#~}4OȨ^ |I5*&{?:kdz.ޕOkzk|ǵ=zfUjxj :G{$y.hi*V^NFsRU+Rf"RO4AX*/bT3rZ<8mO/q6l.uTsʴ[ft5t~!S=w˧_V^ !5#=`3TO |t[Foy醝k/niػAIߞo>doUUu*|g/*xϕɀfj:p}mFk{Ǧ6*8~~gmܚ?q  vd˩SRU@mXCZrNJW<K@;%-|s۪:G=:\Cn{n녓fmrmf. λgʼn~?pؒH2#]D౻=zwW7>GǁwRzNFuKGxNF |DJIrla *榔RA"!ĉACq=}h QhU%KtBe&Ba*t RTFf BnoHssFG; ~ns]VԘlzi쨬zbX%j 麛jc+<%'Y%[4w;eIGIǖq̭k+VhXX53{V+fOx(ٗEr>|ԫ'vI:z+ɔ&0b'^-ӑpcwW-?sίk1kF?Qf L6Y+FBr;{z}X0azQGJd@J̬etǭ?l| R 5C.0/6g(A0i@/4M$"2#XD1 XVN0|R!*@o+VxlȯCzqepdo}]Y#pMy]LēVv;V8u>Q(LBJJ?{mUΏ9iQ]"Dr (Qo/8qv!5ѻ"^e_Ӗ8߮uv􄓼s]u^ u*}.9yA} zS^_Z_EzWF J)\t%tQfB|\ RxCM..G:LQRxӨbL(; hvYs,1{~g.%3CKF.ddw0ѩᄤB4fDa^UG?2Gg^Hzrgxi&kO[NCo&h_E=B@WՂ$iHlg`g=(Yڶ"E_g5TјlG2?! tщ+j@!$M>Oo0# $e4(%g!O}]Ө\@w&iA@D9 XHX3CYzl`/t R3@",R ;_)+eG#֚SRQL3GG\?]QϺ|'o1Y"?`E2XVWɉmQ B>'g:ᰖnVPX}f$րҴ C0;J_30Rf3% }7U(0a]6-S dQ`8">DB.# A[}2v XG,)#.}Z/Պ2Ra'jG({$FĈŀ IDATA@Ԡ_TIP9lsIbBRBI'%Tw^}?2"bR~Dcy_D37Xt8D'za.* ud D# ڱT8HtQNdn$tұ&":PA "![Ʈ@'b;e#nt a;xÑ?74;4k!L53z2\. ,<߄s8 B*0IE'R @$`HyM%'<%eI0Ilj!9b QdO#9Z#|t,%E8߅]v@&+D@QV$B`, Fu<2GӿnN?+(*Q|PQ3{ &LtћH5 F& bB" BY@DD $1I# 121 ) bip0_9WO ` QkV#@ԯXJ-nqT5}/Y>S4tB `@Ŋ=(oQ` VB qHDr)p8 $Qb=]J45j#Ndm0YOzdv2h""\ *TJFDDE,d`FL@G= X 4xHlccG製YLkٗ2˦0ƒ2{Xתĸ$}_6~#3qu0YxXXΈ8nuv8TTpX*~SOۧu Ġ|#K6Ku59 _PQ.}h}N*lM<Իキq !dBbҢKkԨmF$" -\/  5+LJ+w޹aC-@VV.ta4"EdGq܊[NqCH8˓eㄧnAR<X3#{Gfv3#7D|% rSO{0k`$|/ԓC'L`ho6o~]p4W >5 5ƘZBLB)QAG}\@V{xH*u>?)/?zKa>xJڠ ze9]c1qQqԷ=֬ߠR8)fZi@\-%jKӢZ3)ogϚ}*O&$SDgf(m է 5k~]w5%d@`XVбoVT1)5P־ukVLMJ+EGU־}Y%!,{1}C;lSX(XjmN~DXE/XBL!Ԯ/?&P(!TZ*nҪp݈^@P!vfexff-qg>dkfϮ٠'vZ:Ã#mkn1`?S\X8l5k0RFMR+VӲQD/vYU,d OM5\ٿOOQjV׉VHO*(Ͼ/_.K 2v) r殝*f+^jzj>/8fjNݯ@um7Z8v8,,O[:IǢ[i hͩ?KmG8aØR%)L D@tޕ]s\vRjV]KXᕑcHG/鯼Tj/jUu6mszu̝s3/ֵi#0Qtw T(Wk}ڲm΂4tB~>""%p]"u6-.(Ww=pf@ʴ˯/Mk֡]},g6?C-v]7jם{WyWo (ݙo0.xvFݚW37ʧT\rU*bl#r<-op ?<9 _-$޳t_BR#AfW<qF8ㆎ;aº5nK te:EbxqCٚYz+>Ù#{lƭimspc}qM4hcCɇ|lQ#wn)R֭[gٮowl{ɜϜ[1-ǯ6P FJk`~7λ$U$:krb#>Ú9{5k-m&=מzls%9kVN+ Eg7.O8J3"I!R~_3ᾛdhszG}-|ߐJ~Į/=w e=47۳+Ilg|pJ޹8E_-$aQVa TXTk=# A.~p乾y-xYY ifP)63PB$G :6c@.ŪYC敓\j\FAl$NPJ"[ `*b#H;BB\vРv 4;o6"\@`$ bB;H_6of! !/ $dɆ`THYf"N!"Z$`nD"[`h Lڅ|&# 0IAbP(0Y] 1'dĢ~ڼ| 1!x׺[%Uעaubg׆U;CFBz֍$޴v>G&6lؽ.+P1sҲc\c -nbmv|0.:7fg#Ȧ J5|NܑO[,LqU<~yO]<}v NmiO?6w&J(y$Jfk2Ygj/<̓%q\j a ARQ"z *d0|dؓ{W  $IDa0H$ DaH@a`g iI' d0AH" aD7!$QH i4HZBBZ(,! !,aBBF@4|BZR- ÔY#h 0W%z2.#E{F=6a%l|֊[H2煏V%+ڝBn`j$ʢБ nyΚG-W/hѰ_wWKdujH5ڄ_4g?0 _>tP0k{[*y\+ic\qV4phujXի$ƊdU/hajmNK[Q>:\~r,a}߮)8 <7量WW]yZ6Nh\ā UCο7v_2xg?n R@ݩZpK DaԚ!?;-ahT~]x1IcqJ2CzQ'QXQIO)fFDM$%y# % =Ŗ?"~ųs,"ǽyT$jݾ݋~|VvjFJ6o_+%Y;sfR+E%;\:RKlG1tAOۼ;Wvz8GrjHY{n?_h}o ԰=Tj>T>מhc_&vzK$^׊8dcW$`r%`w#NK %<#Mj̆0Jt>(eQJgY+Q\'ؾ cq1J쏐=+|4cGd﫜VgT3Pss/AnDVh@ÚiUNrMܰ/aAEiҵQKTN`]:` i5 Dr#L5 fˌ*]'!)=G[Dʱ픖_Z) qN 0z$L,Z/UOKZSvkҗQ?JchP@CZi5R}ohEn:ж}OVZ#ĈCS7vVPر:;>|}WBK qԊƭ"z̚=l׍GC0qY7 K J%PqW[-W ^QbθO<;޷3o /xuhAج@^>?<1hC}`@fQoϾÍ|jRJ3+Oqz]pm Cï}|E>i޶8V.*RҷoCsV܎ qUtܩ!@a2h66nd?0^lxlt0[ѠGuƮB.㛄KIsdiG1ȉ5WcHuEE2vtw **8Y'9Bp% g*ײJXׯۼuco[i1 ԄqW `cTb9CEq)҈"bH$^9WfW  T]wUW >%B)$Q -Ym h ɜ0;/NBj*5|1!ZZjV*i%ֺwvtiƭ)MUB83c33׃*2H*{nɾ0;Ϩ;^.$܍_:8&ƛ.Lwy? *$V@XtdlcB8'>'ea!h*q^7Y_GF(({pEE:9qOXiϕMC_)w-^4@(Cq@hj-s8i"geVVt\#iyxg΁PVsQj(&QĝMM- bM%KL TU\zJʵr U+ebL b\$83rI->WSZ |59*+>5֜4HV w!vN4nM9+"\T6O;+E|R-w^=| rTL'ž*8!Lk"'Kq'3x2aMf& a?@'bLƸ=Z-X-o a88:FuFݐ'/*溸kة/?dQ =4IedWz롧ؑ.ULC$hzCgs46ے0+Q.)Ϊ.Wõ갸J|eiX)v#Y UbcѠj"6E` !ss+Wd5ͳ$X,I-J0&EXDlS]-RFD7* 3Z|Cϙ(FrVD"k IB6 kALǷm÷9Ov+/oܶeSki=1pWˇ~!/ާ̧}w޵j.q> N{ƨ ~-T ,{1)h+W`NB=+N"[11(Ȁ'v'^g3!T.7꒣)8Ae\ twJ8DA+QF$%n&#kl<̐nrw7>pσ؞4\-J>7"ƃb=u.l[(.E6,bD9|)ŊLjrpبÕԍ-  yv>uH\b0c֬BK81AR.osIX-E&T1fZUej͘VlhHlbfCXhAMk,+kڦqkj?@m^;O#(-?wUn ڶ_ָFs `XȣOn|';$8iV;6TtWOcfJ^bߣL$h:Jt?p V%ӵw޵sƂUiⳗws=i^rLOM-Y~‘{ͣ=}%-O=ev=rݫ+ l}^_= =߿eDy6qeVƮzA9 S {\⦵{==\1idbGVm_߷-#fԂILfH1jlإX,q9Ɍ˵L#skc{ZPV ]Ȋ0*!dJ-Z"MM-+rdl胋Tl`@l hl 6$d w]# WEP|E`Ce׶};ϺzND-Dc ,KY%(ލؒ'6(*#}8-W{gn/4 RR4*I!ZS!uw<54bãX:4/9G\IZj-Ա}Fk[G!6mZ5ZK\B4*"P* "IiԨx,8T" % r%j!7g| (45ڑk~mMiឍ;O\4o̶ $4[m,DA.ݹgۮ]!["+u%ii7nlh ΁Z5:/l'_{z͠- T+kwl]2MG-['7"`^$kc౮R$8{!U u?U_qkV쳴ẊN+)6vN';rč%_bc/ñDyoGv~Ge4Y+ZcDw^%(`P"]ܦe %o\_%XўJܐlߴ1Wh;ϞZ:vS.1!_B='50'q+!\d#P6KV. ;v$.B4Z R 3c48kA׼"'U;LnܰC49/ʶX2snm56D4o LDCkꇴf6!+6>mӸ5^X >N1l/ue{B/DtoْrMyW-8=Qs FtFF(H]HEF ׆VJ,Ӥ[ձ04t}jɋZ7*Υ ?{IsX(=-}'%@ǟY=zv68X7H%2KdC[GxhcڹV|+c7ܺ;9%\A,N22,YvXVf,Wыx`ݵkmMb1@DTiƭ)CVFA@yGx!,+Q-}uO~ ܢfb_{ֶ̘s04jɽ; U|Bw~i|>s]4j.<緿ݥ7&i9gWCڛ>u;d^f5m~y/7}sg ~=͑#Ⱦe7YgRL,78\/q'`!䂄2Jb"׵eŋ~zƥGGpƎ݋>D(0MbȐUfNS@!c,!bdP-*h1^1h0Nl"o-c)dmmz j8YΛ1 Η0* ̇lx64T#JHUHl& C45 Ց`)JY4O .hȃ)5`DʐԴ Y ||Vp$Zmզo}~u:PqV X؜W`wqFÄ?? ߃p.2`c&ާPwu{FxZ؛Mj l!\ XT`?LJ3րovQs.(BN A%Ɛ~`l@m1bmd r`\D 6A" 9V%,U QO"PbբI(֠1`6QFJ!lY`0lČ!pFQ$9Ml͚5`ȃ dz 8m~©*"EjV"S8 8/|}lYb%| 4@[9ob$ aV\.&B ,@l Y ! JHh1`qư(j< (2!1P`!JTZba!F1d:Fj\BOuAluNeL4nM9t&2 {Y"x8qmz*!R n?t_rkHFQI@8 QF (E9GD 0 GU$c4XT2h T!Ea`ՐM(2^ $2Q4*\EF AV !b`6Xl"ޥdJ9ݾ{' Piƭ};T}س6ҴŚgs4B1鷾qȡKfd!@JD""UP%"R5 IU]H 1F, "!p AcPPTB9(&"BP`cIE!f%`_wZ,Uų WV]8Pbi9&)66%v+Un6PPqzQcEVfhd?Yee6D$c2h € AEA$f V-"  !"\s"!B@UME&2zb`AɊML#@E=j*ĞO:zϫ7d ({0Q2h zɇ9Hj\hQEC25hRl#VRz]UeETT@ LPYZ 漊CYrU#BR ]8iƭ}H6W[DLqN*w;guzw('Q>5MɷǷdEQI8c/}gV5 ų1 " IUpL8({,F)u!!Dh#jeR%Vuii@9ũy4ct晊"if>8\*55+z 'hՉLSiƭӻ΀!‹(g9}/)0%Ix?|Crq6b @DPA%T `m_~od(3)aEdeX+,cK!dbb >uDW Bq(EFSJj.FۨPlL@j*Qbz>1 (u߲skր^i_6g[&2׃May|h0]9Ku4YsѢh].ZD'¼sJ-SN#K>0t?D:g*zi?|+5R~rW,p`g2_/yG|gd__[yyő}v[Е\5{ddoy/Ѽ[2Q|2XYa @kSK]wQTMe2ڐJ|^J9up;o}/?߾rZ>F# NE UU@$Db'?~1jTU B@PAL8<墜JICC3MLnLP(QbmR!e\d$ilhhIJFRg/"PalDz^t%]Gy𮡁=:K<xСgU<@k=7;~uo;dklhť;]Fj>kf~zݦuWҁw 8*6j }[w|vƬxx}^|4'BU2*W907gN{twWp67{v{Ho`+h7q~ֈӍ;ysjn/As֌ n9+mYf57uEAT)R N$,R,jŋoض9 &6ٱsjŽU!qOjshl{?\'%;w9[h;ɠ i-c!{DEk:J(@j@|^7(*(j UE`*qIH*(΍ f0q<" 1$I*F B.AmtRV]@$A0huQETv`Hl\vFdAFUGչ Zn:hAӓҒE׭ݸoO[뗞EwW\sr_Gm탏׼M.<FmڙM659+_87`Ϳexr]sV%Ypg.1ӗe^ nO.x.OŶ{{?۱j6|0ًf ܶ;[ηm>`߽E o>\eYb*# C߻O>em?(.77 t>ꉶSCkL{/8snwdmyd!y't=?.y^]Ym7[?{w1?o3tŧf=Y}6Կn~?pTIP΀rI7ܸoEVP!2ϗm-`-85Y͍![YU+n8uo{amߒxt冃^7U X@[`[hfrAG3TAu2(>od_}TjT0RPRp<%DDC(k|K#*SP,@;k,ѪD`ZJ!Xc@TI ID*%KlTRfPeQ`A *88 ^|&>l >@0c?78U akvQ u]KZ_nv}ȏ|[Ճ\߸:}cg&<3:iggx$(2ꄟ߻)IP8ֳIUTD؇j-e|R.77Hd ?HPvA\ΐfsy/N~'kK[?GvY/g/[Puʠ+E%9ok6J(FUpDIAM?;\ɻ*s@5C ` (lmلD Ldl$DH z'`ec*^8vmZcppp+ݹmL@ԁ8Qq*4Ng*XWn]SZrBV/ Ko{آѐ U3}n!94 !2N9 {N4,@gl ~~➦6O~np+شY osŒh~}?N;m$FQR19gI+Mn6wKUrLU+[߿TnFgk^yߝ}agc),9Vn[D|ֳg)\aV@D3:Ugv~FP R߉"#ڼ5i[umMaOq"Jq2'W]#;t&w֎|JhiIs"D *8q=6Y΍Y~fQUU0"Ƈ6Zʵ]OWtSwڤR" Pټ,<]<՟*;<_;˦C3r' FDc Ro]AEyUXj@4zmRq`Y[a2Rlj@ҵ2jTc !2Z M鿻䇫$.wǁE)V m~РZѽLmB;ZQ)A蘈јA4G|}[oyŅbdA@3BaDeV6VD|UPlB+"*3DQ%WTRѢ%82 1 Rő$I$"e@ĚP??XF)"(h >kOXx{o:}adb3Ym2jD*ĵ3_~zYsD &(t)hM?^}'Nnu7a̻ܿ]+vZi$#+7]K̟}o\]p, (aq_ؐ@Z +?V#kIPK=<+=_{!?MFmE?CE\-9qG d]W3cWs?Ԏ[j9$LqG?;6nM- !M3p'd[{XhwymW|g-8ĘCҸGԥ?kr~r".M3@BZ˄yo)X3/>ٿy3_uwȱpHc.?k?Պg ^tYG~槳mЫ|eY9Iifʵ/8XW2@y[*0 ̝;[+qH ,Ps*aoV˄.\_pŷ>zÌcp_o8iV B {cL@AD'M U7\qw J-C19! RWc*e+qP Fj\dQ,U 39bq΄ Db ʼ !KYFe@DDd:T{D" jP$S0U@ڰ+^?xF5(f4n@M=?vSn~[ηX|g|x_t==?Cy~o/%g.}Kg_۲Vq{E9!" gn[qC G1q~"Z+>sggyo^Ϗ23uykNOaǝ|&hFK~::eoo\q%={O»yWO_eX=c2h[O ¢H #36hPNT_̞pBŶ7t4N FPdPGz B\7-II/\KkD'yQEPqSE9)ˆTաl,ڈK٨AIxa3pb Y1& ^3p-E /a\p#C7:RL=3湴(j#tn٦'^G>G~껟=uw~xn:㌛/[6x#?(7t?zwi^S5g_9yÏ/l*eCӵ(s{x ^ux^a? _}1`Y[*= TЍRMznBn}[u_sItИ۔ W~ŗ^y(7w=|]‚! >9q6oF߬ʲ^s y4L-AHAų I,"ZFuIt;uvvՋP$IHQ |BV.|j~`I;t<">)=&0Aǵ]KG,]BjZ4"18aMYlU$K$R)>4gțDc#uX!S&3WZc{&VYɐ'cd<1 /Xp팾Ŋo|Rȑ1Ȉkq"1`C "D3>?uDd%5x]Ummu}usb!Ip#!xDqLK˲$s|Gc>15ֹq|´HVVѬ8ǐd(7& ruDI$&c\r?=I)GEUݢjW۳g0W_&W<ÍYm%dUCY~)/M! g+Q)(E ҍK=9Uko̼wU^/s{`+]:Dޭ]9r%E 0hSY03k퇲{ D 3H A"|?]:ܜ\4"YVWW`l-fF߰G@z(m6Ӿk&j0~0BƩ Q߸lG6drZ` !TC2, fZTmY, >MA@B.qDʵ4 2p( HztD:a[D(Mom:IKɌs"@ q:$jH@ 7]'.~!o3AR\'ᴛFx>]fz9ᄿg&l+$ F|Xϓzw{攟yq?>ҫEǼJ[!GOH~cN+<틅h|k\y̧z֯Rxt7\׷:'ꁗr @2A s}A$e TsC#DNv'T3Nc95>yՆ_F,sCiϊXPK(Dnت!Y@]K@ge6EDdt=u'bTCf[: 'L-R"t\W궮1Yb8Y@FHgȀ|'MV(5ABn٘Nˣ=A`:G$yri,pś22IhaX݀Ͱ%N@h[Z 01覅%&K} 'D݃Xv_x ]ADRzޒ@ mm6v:O~δiL̐V]_W &do};L-aZI]G| $> CZ.2 /YtƟoFBxd<T:\ɳF=0픁K{DH#twxemUrmI\B,I`ЅfH# |p2-h (1"d2AAJIuq"y } QQ5I08~c2fһ:>NR "2OM> JyA~:LK9jv2nsJ[݃`{4*2]$ ˉŖ1ι]ߝ:%%u:!e%ἰ^QNl5#h+AP BfrtҶmM,DȾ3>%@jU_[4|C ka2G K:z5PôxNvQ;lu;k~"!8ٚSL׸ISa,$t]tig($iG6|5M^HS]]70l4.o,D"2MO]&$liKB[IDATECuy~<+ե5k4 4 @BSIuy4TP!ayI)M`7it1lBh3k0$$(5kl~z eЅvĔ7H4\J8b 70ȕt`iYz8iYaHΥ^/pՊ\L4<F;f:LJI.J&Hf&$_dLJd Rft7k aLwM{YdqȘ[#pju=h6*~2f4lݳ:t#t$0dX`d d WHHf4I XMe^pn8bpXg^Xgn]@aD&@B|6#V|"vLJH;/-1st+dj djE˾\-$$"d $C$~\2`HH2$ADFCQQ[T|D+Wn?u`P^lG ǜijٜUOhC`F|9lndIF$l ~tfIIt =S,[N23iXHq5>;nXt)"uNeC^nN87 4` M#(pӎ`L L$ch5/&ػ$=D2a3>n /t|[nͿ59 Kh߱"~٧L)ȑARAà 24%fH17%$Y"`[ [C$d,-]3v}ѽK B7tD,<'-e.Ք{k! t al CL8: @4݅[b=G 8cMcD$d PkBBIlvU{iad%Z;r8V8R~`۴[-o(~Pp'@@YGD!'-ѲyN?<#|}+UO3_K|Z![dچ0!4H< O]Mf &{Ro'0`@vVEuQ3+/qe1Ap1 $'2!yK.^jLAͪJ2 Pظ @fѭ[00QY.+[IE瞜\C"QgWTά?LaVqˋS=;h~&qwL3L˲L@@35 ݲ,SP:z3p\A gw-2 ݎD!*&@fO ?!=dX7IߕB!p@P)n篼4bpmvvYv9V~*EPN(pȰÖ2t4m V>`$"⚊D2$5sA@h%v HzA w1)Kg.1䰻\oNvÅ|uO;gK;f H|׭G4%b\2(XLpdBEΝאtnIP0F$GD !JLKW@Fʼnfk+߯B֞=<94wU;^u92" r\Ȓܗɱyտ#|ųx/ =z_&V\(6T+]ߏnR_0@nFDdG:'P( [#YJ;}44 ֗-sT(msquUܳC""k]Cۗ0Y/ٖ+'2>^Vа~שxQG/==g{脡!/^]nɆbя&]tc}>eX/(1!I/N: 5^yUB?Olgº5sFNIƊUc=sW9\qy[U+<9_[_y8tEMz!vuڶCi356 >䙫C݆^utg\kκ7_Oym V/hoz} X5T=0vĎ|?PVS~DP(,5?YyogɯtɕcKJ_s翻%_>y쏮.n`ӗ/m~n OY~~ww݀? _ucˊr?gu 7"vԪ?8rMYoqū^@~`zs_K'^_ܩ|5g-W/St՟.rSׇO\Ԇ'7\;eO+7,ZzWTƲ-}ɓ{+6nېtR(-EDѽf7mCt澷}#8w6N^ʭY6룯pݳ7[:$0n>{utFMLW5K; KM=Gw+͜GSs~Wӯ1Q_9hьwVQJo–l[u9|Ψ(@GњYgoh-kݦ }kǪ9³վ+}:7=~>‡ԥ?`+Bs'ާ *\4USl*q1Yaʹg|}qQ8ݹ]n'\>1Ï;]Nz7 š=Nb` e7Ȗy%_ty `…۴bdD:d"D;WOyW\zY?kpaMp 裸z^",#q_.-jPaf+ʮ#%ĩWW}_6v-&^L{㉛o8wo{veuc6h~nKFA ӱl"28fD5)}܄G*}k];{el6:de7=ϗsdAAvEg3W !z;}t*ISԭիĶ-vI  šoo뺮SU۠G6V7{BG=3-(̝bSk B]GW^PԆ/&-Uc%9UibVǡyf,s`pWv0uk{GwmmU[6 *|鲦r;+꺟|/>{s,ߙ}Er޲Y+krk>6d2n'̛5!ĚݫtW[歫Y1Įk xP]SK=o3>oڽ}K[҃T#vBjVGy'A{-Ɯ+d:K&"|slgig^8ug'OdSƝ uDc zoN9ǿLśg8x㟞BTR?>|ȉY<:C~y̝#GyѯJ@ Di,L8'3`-4ˋ>l7+OԛN9~qqs{EgYϞ>rȨ3.{ u ]P)_ S??NKn; w:xw-*ӭh(jo}]֍%T2ZPx%v(TS]WW_-atAvų>9fg}/))巵eRmFAi eBHxIIg{\-/5j]/J%.ZPQөolkcBP0 ^kc:vrD~ATW_s]}4M!BQdTB!B!$` !BHB!B!$` !BHB!B!$` !BHB!B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!K!BHB!%B!K!BHB!%B!K!BHB!%B!K!BHB!%B!K!BB!%B!K!BB!%B!K!BB!%B!K!BB!%BQ, D^i29d#@jB!$` !BHB!%B!trJM>)B!.trą&eC*(l !B(*Ȱ Bq Kd>XB!B!$` !BP74. ݅aȯC(.KHvfp8];t4dQ )AHW.' l}իWtrB XB˦.پZ5É$D!9:g&I'%C!$` !,d4 !,!B XBc,ܕ㾒\7#G]ouBHGX0+O,H=*qCVkcΣ;pԻyAnor6'_Iz|$}9g{بؠ6+9n/q9WB˩?vI-3Y\K}y>n H;ߎYÇzK 6pjU` 1!,!nLg4z0q:|[G,n9)<9Gf<̙̥p4|#} +Z^wVPni ߼7YHBH& yYtl$T/"rh;Loi9 sڲwx)><ΪY `^2/ Q]y'[Np+V^lW8k~?6lG]Sl* =CZb~Y/o|1;ײdʜ,ri$?g `Feq$ކlM^|%čʇ/cCL'ҏ^l4dИǹ}+o8ڔ5HX0~'QJ*(cMg9U3@ʕV9=q$yy MiG哿ꤝD yWB;}2*~!eqխLֺ&7m4^SguN7{/fȹޏ;t-BW`ɫ~G''&m3.<}PAqT'n6$Kns|{m6aC,Ty/BXZ= [&wiJmϚ+f A?/&,2wH%uBFF>Ni[+Q41Ad) Tezڕ-K 4*}9ؖc;|FgЩV9ªԤ'׉~$ZińV֟(ZNzo>ZcsV9BBBi֫+}gd+.zEޢ)ì;*gjroABx -'iv Ħv12ʐw^uj.E-MdlN\|&,!sag;u 9Niʶx -biV":O~67yQNvCLP:u\io<&Lhע aޅk<'Q||sF4JWÖ $d?w}zR'qyXTOh9{p$aeg.k]/ozm dON\Kߕ;1]qO5"m}GI+!$` qӼ .ON$s Q4ok~:ڽfMe`pwyzg9FfF?G9_nP&=0aw$w3^FlX#Ug-S'9~8;~~?Q5Q859Y֢[pI™߲ u/˜LXCǎoWNwU2w`)BȚɸ1I?>M` MB\1_ة ~ Pר~ ~Q2;JS%-D,;q_Y 4G31{0!wƢB{xR(_ =;7i8~#>J `-]ҳШ`,_94_Wo>9K-IS `ZL eMQ ];SR1<VKߕfĐi7сpOPkb|ETRO%s_\i=n! T?u.dīl[s%@!BB!D&}?k9hʖB\%R% @QT C~/ETReʑ|*]6BHBMQ#>;>đ#&K!ɭ a?ibǎd $L4%d Q)A],JQP4(*T .W4Mܙ ÐJkqL fb{\Ez mMPQ!o%č@oWG1t(:P5P\M۠WFxonS%lĝ~y\Qx[`M!K.\cq\ѡzdhݍ?w 1"RrQWBQUEɩn07Q<f{mv̌3[IjE @U3%$` qcwt>kbtQ)Tڹ3vzhe#+Z3'Z)Y<{41s"Vv(R[&S!Ki*HV- `&heanOʩRSZUn?F+t'~ިb䙦 0``(gE$<ϐ;8ifkb`z:+Bf'x}Y2r-245Mϔ<ɛ ]-h^J؋*_Yȏ? kTeĶ<ԁI?ciwp;Lx#JUgŮ;iJ`+xDNq{mޚҌN /!gH³},#Ns=XjeTTkLfMryr@7Q>)_њ+; a&(jV@RINs1n?ŋW' *hf_ !K\3|^[tz o29ewI>OgOxlk&.lMP7~YB]!_gi9FMhݴ~üd6<Ć!Xͳ2dm#ks2cܗ:]|V,Em4aFEyҼ4KoȔ Z2}t=A4l5uBtt4˖H۶mm<|}$d"Xyj<=h;Ee)}U )AF?{BHפC PK3F|nBK!n^Y2Sll07zeT\qkf@Gj3%T-[!4.ݙ-ÕJ{bx`v4 J 86z,Q4<5XY)++`١NogR6‰d7uc3$5{8B%PUlyA VЬJnX2ML5Os Ϭo_Il(@AS w=<$wOJcjdVQv焬lmڴaQ gʁKQ2 0sn>^P]ԩ⅏9ϙx `3O!$`k%*CZ1mL31ӓ^<*muHf}LBϛ%)?؉rNPY&Ͻ-;nf`k1yӲ:6_燐nnwաCOpJWDɺix.ln`f5h^ӻ Ⱥ̎{3Mm&%K2^ڟգ͗p*EU缛 xPM-^*{=z3Fg-<[;jL+iTuy0 μcC6HBb|q#gqIf|"7ꤸ퀕f~ w}ddd0'>YZnkLczw_dٳ4dQ^b)##-SF{zG~%k~kQl8s&8pݞslDGGJ1ԫ{3&o(\W1cp:gv-AҞ_C;xj@x~7AxEB\ϝ;v-ciz,3*!!uҿ>2uUUۭV\.*uv}kPɭ2Yù{jdaUpZF׋/`YvHn]INNs<裌3UUiV(բjK3@/]AKa7deHB7TkW7z]5`eՓ[jTI?&MK/ēOi۪^&%h]efu2'}$a XBA+0 tEEw:qMX]|ݣ{wՉwСC:t(&mymv>)UUQܰk/XnLUI&*YUC a8C1]F4y.jřMIjO+U fP&Ő/Htt47UQU,*+ d`f}-K5Y%=l)cv# ?yp6ax$$` q]*ϕJZ}ٺ5zX YWidGecB\qGxbH֬Y͎;h 5]7CVv2UQP4 EUAjVRj)%7`nQ[~Þl( !5:L|*V(򛦉P8AdR% 0 "/WyUbwa]q4Swcdu~4s׀nXTf2:YwUKS/ ~>4(/}5+\id*S(ͅalڭznUr{ҒǷ*F']5 $` XY篎B7 Lĝ<#Q_Lr&Wu E1|gD*ik\bɾfbe5 7)sQύ,M΂V+ V ];X-X-V, bi*h5tݍP䵣fXBbvX,@҉cd8Oc-nĩ<̕ xlY W<,8O#W%dL(Dae+.^Uղ.z/:Wry_oۜ;]&CCm!KqCa掇%ĥp;PՀ)o3_+`AKQr>tN=bʯ,!/m+cx3[Jfc,~ ̔{CٵX%k?3 G(|J4Ù{TvgQ) 8'c.Cwe]&Д!K":GX0d ڼ{Bdǽ`_@2.Wi!x ~q=<>vyE@`>nюcvKݗLӝ۠g*BR;)9-|,;b.L|5XBH$rz}^FhWpr`G:y//aZ`ԫ]-=ãɮKUlY@]`zjLZմѦU$iL:AeQ$|||3BQT|vk0.O_xTkd!K|5~"s?˯ w<' p=&.GAJ^<5Nxiat폟/s0s߈&t8=ػŠ'Y!v'd>faCaye^h=03[>w?ݧ P} o؋0]2hDFET422mnRڠ;^D[`Tv -k k 'cMg_Tk~E @jb\z) #3{:pׁKx9YUOGb ~?@)w>KK5ffi{!Chs6w5:xwTiXNYe:|1(0+ IDAT-DfsQA:0MDZGQL gT֎(x!"ԎpH#_iJ,QTUJW'Yh>>S1k\gf9=Om1c Q;ʬC?H E,ijR2o@13{D5Ifoj6t~ kx;ǥø2/]ۓ7̜3@TlܼͫP^r̭V`MLᩉ9k(Tc?_ܸq:a3Uʯʯ¢qD@ԋ-ׄ\8vCtn\!!TۅgmB/?/BT)ESM/ZZr$aN(Yvdur7\yn:`ٱX,dnOx sJ̌T}Ym돦px( 7O4PhD(A9q7pGx<KEڜSۿaoٰ?nl8:3YυIEtgAUz^@R.t giԫ߈V-jj}ʶx -㳲:#\4Y:UZ77_Iߪ>9=KP4KTs heuK v g3eŧF4kTy X9j{xfW X08i6˚rO{KN_2[(S,9ݹTJٝNϭ1lgw|qDk/Ȫ:qۅEP8]~(әE0 22QϻuLS% VÚ Q`:6/HI矟i`*U6mڔ.|e~쒖iZpT;xF6l̆O^ӹ7y 5.s}6/*Uo‡V:uyw;GdJMF0fQ K/GA}4zWԣb-#면 Sf 3h&qVgfƋ=# /',00MBggDIͱS94 ~Q2;JS%-Ay\j jpr2(ilu>]íX/R׉Xv"bتK08C}c,u%+!R<_9Q`,=yg,ww$#=∨X UUI8OAɤjjUcZi &UxORkX-~C BQg튒یGni™3g8}:]9u* EQq:$BQ22I<(xf] !MBBFv'w3:珿?)T GS5|pr4ahƉ'g'm !K!č!o'X%Bzz{Ăj\.'[nCQΜ9MX230sBR%$` !^yFj(DnYoe䭯ej8XBBUÔ*S.ktut[2MUj,!9rx?|Of<+ j'28*2%[36+v Պfj`hh=EQ)U: o\HE (B3Xբi әD\*GHBQ⵺㾬qqqL4•gݛ|ϩ`QMNx){aBB]FWT@* VC)Z&ShB\;N;D;vʚU{u܅GTz,!B*tٗB'?B76Lܜvy |ճf.Iuv.vZ]O!I pd?r;#x`>nю\ŋwѲoџ)^js=%=ã>Jaڥ0p4 uU/ܗz!DM Z@ժWzx@AMc}[ɾC:(&V}!yg|]yOCo&1z>;,>V_ǽ*5G2\pz7\+3~(zT;ghKѿ'U}rEST OŨU`u嬫*.S,9ݻTJٝN5%!dKXSfswsJ@հM>K}(h Wʺ'\^[Dɨl!ĕ֡"{X%޶5)o#ڝG,+Cm•XVzN,P1܉ٖOHK", !$` !ſHZ3/Gg?K'nZMzch[ѧ^_OZ̾ 33'BN_2cwX9yfɡ#صa ߬;ܒn̛,ݫp썸սt]PQ$ ˟f|7y﵇4`ƝுZ;M$~h50]'Pe37#zej(j~Hcد+X ߝw^j"^W!S.K.HK T"AQ9Ee&vq\8.223HO'--`BdCJ%(4+Vȵ(NB!B!$` !BHB!B!$` !BHB!B!$` !BHB!B!$` !ĥg#͎]‹jl'~L 1a'}cOuB"@Q(f2z< }7hG *EdhYŷH"ͤE8+ n/l$h &,ȭ| "ɹxG(^+3g7t}K_ XBHxeg[i"lg?$Wi0%uno蹟r`Ǥ~ۆ2*)im&i &DW0DӘJ'ĵ])@q)UN͕3;3'Mbs]b$ulNLL 1{J3=3rydnkCxøefd\ְ]_ƭ8;!&1Ld)n"4ٻp4}~K 8gւ2|tlYN&c~TՈ wVN*^ѓS3m#_s:wixyk^z~Fϲ1]~cbb2+ {/3-cq{]NLØbZtُ%Flfߘ'S=/Lc%̛6jS -4/LVn\gX8V0Hf:K2gtwn61D~8lboX8uڥ}ɰٕynoZdXxU6(lXh.['1|Ըm7Jm?Zn֟͛ټy35'y8B}x>>4aD=-7of1U&MBKy0-brW|b_=x筇h`l޾|Cܜ.<V}:+~(}c`*E#ymݫI oп? @H8=+欳pӵ/ ;_|<˷RѰq-˩P.ʞcNOѱI.H-WNpWK:Ϳ ,r_jێK !KqKū\,5*۽ټ>~.kMB~ޒ:YT)KNO#ՇRv'M2qЌGusB_Iߪ>9Y]*bl:ҭ׌ nkNf]½2㷰?ߘg-',>7A'۠(f; !$` !i}|>P,jN1~╧dl]OaTQΎO&fW% 9nY?yzLԩ]x6 L&e4(pꙘfxXBɨl!DqpnWujJd)RMoMRω[~~ݓ7Wv%S5ү/6͟ʍ;7׈I;3֡"{X% 7E%[OHQtPPU4dBZg)snL-iI r\CGkYw3%4uݘ7Y=ûWq/=ۃ;s\76'1 XwUZQZ3/Gg?K'nZMzcWl!4߯gOfj./ ͗r 7IN),!5R=/؜h~[yWjkVřbtЙ/}ɮ E yLוN=G~yQJ[I}#wхǧWӷT??=XCt#]˂8_B ]qv̥oƴ"o}^JnNj-Ïzξ7ug[Zwz?d!bR/rez*Ш2M]qݸ\.NdNdx0AQJ&YTF7%Ŋ+ooop8l6V+M 7'8H6D3t Ňț 6;SB⿢(uQhC&͚be$4qJ2 4MCUs.!.`-m+c[^^1c=ײqEAKAU#..ဠ97 VBHR-iU~Ă0u(p*J! +{AAKq'BRj_2U+M,i"B^M?W;hpd 3_La?8 M>p]k-ʻS7KMx~!F|:h0=ɪQg׷8A_CL7_ zUr%$*ij<ܗm0m3 oԛ}=y,>s#u:S}hde w8F0e v> WaxVb|\⢲b((dτ5N|߂g'KMuޝt+TLy{2hZžy +x{fs1Ro'>9^}Kޤn3w:]EWb^RiG spj6/n) |c;k~~gS57S&ë#Lԇʪgoh/x&Mv+V aP}ŒhVZy:ս2*)MpdVI\g]4i8=y."R.*Cx7{ub^/fh 5Cߪ>PI' a}=5gn.E?дuņVʎL7W[+8\Ie,9?^;wi$|(C皳!ŖY}`پ=37dQ>~4_ҮƑ#^KYДo?P{ؑɥZώdh5iml̔1K +{skownߵ/fYx?ؑo˺-sgWB!Ruɧvϕ+u>GVmp= X\-xygOytݷ"s3z[-[@_oN؜YgJ1tp_ș$9Yhd 4.T*ظY+.YZVJ_ϜN>u2׷wTOxgTOv.oX|s켽Vc p t\:3X?T*)\6,D{RTh4].zQ%&fXՋ +Ho z{h'a Xo_ *, X @` @`, X @` @`,X @` @`,X  @`,X   e]IENDB`sqlkit-0.9.5/doc/static/images/join.png0000644000175000017500000007347111714210425017351 0ustar sandrosandroPNG  IHDR!4ֆsRGBbKGD pHYs  tIME :VQd IDATxw|l~;n[%wl+6.M1C(  5$@@`zi)W{mY;]-3?wɒ18DZ̼f޼7r P( PP(  BPCP( 1 B>BP(P( B} BPNt|]N;!1VW3\{cvߟ6O{R lݩ-"   l?_(1vCS3 -'RDւ:ˡO_C鿿~|xM qK|hrp1E9y7 k]^|xG\ 何+cR~ѰOdpK&vT\1K9z&u΍M{/r)ǨMY(G}Θ!us|| M4Nܶcƌ;k|LZAaUǷ#k[v{H ƕ[3epG^gv:P[=nӐ-Ig[϶U,Ku'M(ǰܼLcLoFLGbϾ`mU̒s/37!2,ܽs9jmㆠue>tKї]1y^hg{;$cƔFH:7>|a[8[7/29"o^咵M!`^x(CPyȐ?fuףko)3[.II_ٺ͟q(sO[z]?S٢iE@fzPzw1, KӆTl^UNqM_6_vǽug׿!c飯l{՟va{7Λ]M`-7MlfQj]+?5vN.}\1F<pD{/1{\4l}jEݴwꊮk`]w?}_Cy7.zIp@חqbaݿK~ߏoصPk7^؝kꂍG!e&uco0/?~igiʝ>\MUCfw $6X3 |e4zpݧr %f{oSB%~.g^>ܙl)_^'.%ңpzճ ?|۟.d\\e>ܸq{K΄1rV-,gՅ]^Hک)n[o}[yhrx+? SRRG>\{wL \Dvl]2G&wF=̄+gO-LI-^p͔m+%Y>nȷaI@z]>,fGӆX9>ipf#)pĥ{ՋG 'Xvll{+=@M+Lbne.ba[ZڲeN9Q #lF'e4 y~yYy9-h={CΛ-v ՠm9E& a7Uye_љEvVW4ٻ堏۝ hcfRkM,4Z^KsF[y13$3Q&;[QlAu;\RN%X A}27SvSl^DRg%°l7 (~`xpCo߱jc)=[0n+60և&>s7u`ա#57~/+gw%GSv&]kYK4!MBIGeҙyU?B57][bdty#,0|V WS('ZBP ]=1Ic.^q}`ۋ:8rQ/܏[ƞ3GnM7 OzAmVv؊յ4!);ƻŹpp9sJ?|ϗWUr71Ebբ(o hIzң9Mso@?BëtO4wrK+]y3_YxkYٳDQS']+QG=feݍOEsb}tn#P('~kBP('NL BPCP(c( B>BP(P(  BPP( 1 B9>7BPhCP(c( B>BP(P(  BPP( 1 B>BP(c( B} BP~p9G RSR&O:bB{4B sZP(^læ-&nc1>o.{g,1 .sysT:쨧ڵňԔ" q7ZTKN SOĘ\( 81$|Qw9`$x?ŝyNj eYNOO̬ B:Rܛ|>!CXmljx|)1| JM%cX[o~X/̶ $莗:B*=;Z_*`㣧|>o,NAt$E7"Crj`@r%;01Hn[tj7䊻n/|ubcwEK/O/N#>IiiO@jrRY37j=sHI`R(SeAAE"JbgN'Ix<>U.Y#7ok[>Z23`.׷vl]gSmz^ޖO?MYp~_姾]`t9z:gDp@RڶȟQL͔B> zaDI!1 (|vf-At:۷8w> v~ Pk9cG=>˛xP(=Lg@6d嗚;BN$)آ稙R()Gs'Iҁ.sO9w!)运Zpq%y67}ΕcB}1m߯lݞinh <(/~1撒~ n իVWZJ2ԵD2xIkZ<$VȞ,V,cHdhCPN8&rwlc\‰1i8FY_4"Lf6}>_Ñެ8/&"ƭjדږT2aS\0z~꬙}>Ug4~"u֬Cn]~9 *`5pti9y='> ˡRMPTR(S3Aʤ$sKs-^AͲCb0$J#˖k7<{tل0(}\ٱ1}1eN/ҫͫv^}/(wxxupp p.$XV2Gwʏ:}uާWT͵"S$c()B?B&]<69j$-7(JۇӍ0aRCZڈ|m,i6a7p}yK}vdP kSqm>)G|!Y -PLcǖUj c%ovl}C_2{-w! @ff&ByK2}Z[~녾1 $:ʫdzfLuWyޘYtԷ_ z  a9kRq(c!DTLh 0'BsNj[,AV^+7.'#6o؝50v]9B˲nwz HrŽJ\b@jYkϨPM gݝaYY 8 Ɋ2QT;ĮgֶBN$)tGB9ϧ1cQՕ$QeYeBaF}͌(NS3 p£v> /zq7)(wO>gQ T~GTWg5d[s D! .sgIУy,!$ G~Nd}9PGEsppkb-z΢gzVg~(ʩc^l޷o_QQ˲>Fd69cF kdYmmmGMvɲp8Z[Zczs9?D?著/eA9'm|¤<^ʐp|^~sBtͶHC!cEBJΙ$ Zy'igY~-&yM6q=T r*בfԃG}C 0,˪eYV}CCcY }޽_8Cz_o4 uZݩ+n{% 1 :ed":0XhȈ;n }+<˪ dV@z Ip)2+6k7<2LP(S|~Nt&W7+<0zOty{[VYߟ.҆g^˝6?`>Rk8! ,k4 (h1IY^R.cF>6cXuzPRHS81<)B95}̻?Z*===55pl6l4z tHl_b(~ګ}sTxV9`H`_l-;+O{ᡏ/M/9#bP9e<0 ~%`<+<۷5q&X&r1bcޯ,a \6.n]P(q8lneՇ]p,kȎP($IՑчqhwR_xȥ7^{}/K!`0$r=-.y d@BXlw"Ǹ^g4QEbPX ft. eEY8>A$Xz rԴ&뮳Z$|>AI~5pQ(уgafq!3#~}r҄g~_WKAA `EAHD3u1HYB!44K4G"a( VεsNͶRKP(5sǶUUy3gvoΔ$I 0Ɵ.W`[E0b($IRנ{M& B :0榅ΪYMΪPtIfK~1dOA't:A<{!{eYf99b%Y}49d2LcRא{ԍsx%gHE:uR(SP( rl) B} BPP(  BPCP(c( B>BP(P(  BPP( 1 B>BP(c( B} BPNUs]eK{.!BB  @Dy !z؛?tV a%j3#Đ2; ؅.7 z0\ο$zqXD#lDBK'P䏱’~HEUie$jf3vx3(ȠnI;!#ڞqJ_DM R²F]Gs"1#f ƗicmP#su_$Bi_C"S΁ vxX5bwAbP$F1fq"a%͐|ig( MV#> v'1XR0aF6Y:AE LPTO&gSuC9μa&6F, ˝sM4͚"B4=X@]C| #s. Ć=H눴F4y]4NzQ<BED&M9 IDAT⋌sVQd@%<7.FAyZ4lԛtf QaUy  xJ!U$Ѽ^4$s5Fܩ)5LS$D:E0O W1Q H۳5d;EG@#PT&JD=d[ 1yP{k)H1QX"M:1=zܨWV;UNK!Q>:{XI<A 8XNkܧHi"kus㫤Mc&]cmkR?&8wWHEgmsvlb, !"R4#Cxl' ҝ1lt#)tk^%#Ѩ,VY$^;4A7H mWD;I1F*9ҭAHff"-Iu'/G:,Ѯj>ț(IBa'̭꺞s]*X>ve]QUO9zzJ5F $$vYk-vm/&܉!ЍOAMPbf)>ڕ$xԔ~g #D}(ިl5 bq[\\W%>it!'!d s*3l$b}xC$^$?Q#SM` X߷V 6E@ly.B|ZI|ĉf5PhE& եFf3?G)ԣ8v5.}"}(Q@'Me܍4 BG7xcŝIAl2,,8E@zEm]fuɿf!1ĜCpozՖШ!"/<$&P7f cb']TбXBUf5hzkה(uHʘV5sY'[kmj uˋ^^ \k'P+{uqwBE̤Hޖ-26ƈYa=hcOHŘ \?@›=eQdE{ږeAY̫1{WC-@l; ObdCpϢizBy"]^4~Vncڝ+13!ҝ>,:!v';6‰uΊQlȲ:ڣfSL1$ӤGyX$VJ$WbPb6\!773D3Fb}%]$x|iCALH]ь&^N基r˚k:Eki;@j^++8&_b@ @`kOR!GnfahO1 ?@ Ҥ5L=s/ф1nWh4# !iK`[Y!|krˊ=Ò >22Hr " EM4`wk[Gz4t; ?ƀNGI4}*f6&NW\,wK)jFL’]%v& 1l,eE4ڝuU5%kĪO,v,$퐣Vnkn^İ,r܂DXC]3H#*@1 8Fպ"ˈּxuɂ?Id׊7?:hri㶯xKιsŖm9{5畖aۚwTUe@AM/!/X#SNX\ټҫsy:]Ά6f5=iD, 7rg[Rʿq-@N+e!H}wwBu;,Pˡ۴PG7 IOP/ĻVz@wAMbc(5nһOعB(%eYq[xÁ&WdLƬ,0/֬?5d3wa]w_/1#PBG>@+iްuwsxŹw7F 0qά& tdCAʌKl";\`@K7ոe/?=M~<2q3g=T(I܄ B9mO? B޿o+h`珼^Ǵs 7j~35*9g,!bѽk~ZU1uYB|▍v7Y{yϙ+}:,{xс$8q=-x WkT0!5C~q f<{?:~5c X&E`Bl?}o'0_{af._ƺ?OKՁ"IϾԮ'^UI>3M}}ˆCmY_D,b7lλhB3߷%:k!DgmڜOGddYG-P}J%s-AЧ,/ ՏUTCt P}_3h ?rCzuWrLJz$D`~;Ɩ_]JrFn'663o?XG_5!WKMkc]JgQUC۷mJ18~bа;pJF^'ַ6x竌;dZo>Zިlϭ yՆx} 7պ`LGZ+[|$]~vw0sI$/z:+n :<agDWX\+szg띫UԮ hT׬໥#*8يAC+5{b^]q%U7_U u  ?Q1c0XQ]R~O!5rﶽNё2Cgw1;jفU5̞RreSy;Q,$~pJ_`kՔ6e>]ɗMjzV ^?PʗvM|rӓ__م:'2lh[@ڤ7OL/:{~笴dbj~q]-[B2V˟ǻ{شՏ4ش cf5 I N>ei_{W\/%}5Cy9rU]xV  )?~bY'(ŒcY'乌Wlj ]`!ؿfaBun]x9&+$Tǩ-|Xt8P'J _,:u63$1cM Ƒ!BӍ"VNmWIT(#}!E`ihP R̈xN^pT#]NiY%@2D^o*8q>&X!@5ǥ@"$X!8<[ +jg*vzg-& c̜cgCt!kݯ\16]OF2p;D-!hދmKx;>ƪk]s{95iTM-+g:(cFvl05 sWӋ(&N- ˿Gv{-/9F:afӮ0njwDV}($\3m:OW] :q*:XRl<(t{֜C.f_Y,Gv0_{Ɩ@7`긒6p25(ig)&e.='P :ka3&0!'G1<^kr{juD)rUEV+`NO y[eg$%/u4lBVMI u|>;j沂W̒ gn=>_,.?mY689I\ 5?*yJyw`s՞0k8Sḕp$"P~q1?$9n1 O DEV}1,6Q9i!@ݧxk!r9';qGmxdJX| lL68C氛/IpyLbحOɉ6?ɡI`53dX|L9SmA+rI\-;Ol'8CFv?u|c1 ¾~? LmPP󣒧>r|l8BGyBECP(cF˩Ѽ4B͏J?sjjjjkjLO-%nGs C3ַ5lY';$ˠIz( ћ{%)iVA >jGJ `yxR;܍m-MW\s B9V77V6׷T6Շ@18.'BPh= xܾ^ohmiOMMwedUlܴeAg0XL~1  ,BPIH[K#1lebՕ5cF>|r~jey HIKE,#)?+ \:EU?336 U`%xN`GA(8hjjw8~ppIUW]F+ ~1:A疬`,Y%Ϲ_*u\z{EoS ?Lt|}Gh~R#k3.O7MNO.v>jXeY LFrȑw~rb9NpK?[v)a4D91oLֻOK} 6=q/flco}`I_,IL_qI{FG#_"$?O8R/ysΜNG_ cx!`0F`@ 9pjg^N{GW\ !EdQ ')͛WW[f{),8bkV س?]:t[Q O1c^pUo *qSM:j~gNo?m-=z8K~?xsM7~5]/ ~^:fkRise񗔕w5s'qw0lR~0%F,O$ؒKcz(ysV GL7g#;D_.79cKKGӍ?c~cB0ƄJǎ{޼dLDIxh1DV+ϰBt 19=[g-+~ʒOLwrYO{lm_/-S2ydwt֭[hDؽ9W-^;Nwr۽7^C>R5~qGw~ߵ"Wov}rH칬w /~w.m]\K˖-o<ۧws8aܭhKGAegiE1nngse :~Xo\=>|b~ٽӭCR1y)Eb107LIK6rd^C&M 999EM=Ӄn>aƅ7ȋnk Ҹuܜrȯ ?:؜q榥fΘ79.nt?zܑpu;>Цڠ}W&sH7qՕbz56):=^k~ǍwED/1% 9o kVT38>H39j,xB}qw- 4|ׇ717EK~̤yw,]礓?)ދFe8ZTNg0r(=rsc-IBA٠G Q0njify. ,KJF sUCyDǀO,Sg;_~p\S,28WfϽÇ=)N8}̱ƞzVݵp«9nX.0%1&N ql氜p$ (T;k{(M;Jq%}=ΪCp;l״s=1es W[ w:|b-Tz*5y+w?Xu8cS&{LU)C\etDf%,{@f"1,z^E`2ut2~xEQ***$R(Yl6hjSO9_]EW\2O?VLݴr^?Ǜz𫘠!~ܔX*!mIx~*r4#Vb9\%!.Ƌ^+z=Gm[ sPcFv/m?^DU#yKNa@ ,~"gO~}YзoeuCNNF `Y :,ᔔY^%ge671R@g9zhϤ˂3rNkQ? `yXsaH7.kdjQXuo]IŹ^]*ܸ4g3Hq:A #8{uDxLU`hʜu W>ONm>tJ۞ݮw!Bc~ IDATzt+|$Y-Ԍ`@ äz'^t)'Lܴ'mڇ13Uu/d!T3Z^RGXsV۰qkysVI~\yU U{.~ m5_{#_Pbݵ}pQw[e !YIH >*x瓇Oy (&T \e 2yl?lߗiᵿp.n=Bseǎf;tft`yavp:v=333 t:=r1nĘV]4.m߾ 9f>o<}3\n,ց[V>}|ڈy}<7_/Xye-:ᏋJu/.d¼[lbWߐ h%I/xWJLڬ̛9)sEMfy7aUSu߼IݳK*0 /bЛϾ~G_$\u}Xx;Xb/vx8􀯛\o[55Yfذa!B` Ç4I{TǓeEdnE: *_]x%Y,~VЯSSN`#IR0ed21 ò, <:33377eY^qg2cEq\T7 Oܭ}>Ne"!a`&33r1 >qi0d  ,nP($|IJ,˲~h4B~Q~ff,˂ Hc (h4ZʱoʁBPfYV$Ij&9NE B ~Bu:!XQQaX C[[ǀjUE$n( B!uu=?//`0G,P(c ANS-j~*y0bi jXAlw@/475cd րC;z( g͉ݾr$IPe[V12e&j5L&幐$z> BqLOnKZxiutt(bX,bEo2&`)`@/)Q( 1G1 rZZZCCjݲeL<-f^_ǥD} BPst&c#vĈӪKGqy܇9t\!`S! Q$cRSDQP(cz;+۝EQ>l)LyEe{{; 1Âå I BPo0Feٛ[h4JTPPi:{>dQ:gDQ>BPNN2F3wzA !Vf!It`$YgaBuCP(.^xvtbGGG(EQtb²hRR S;xxI ͉Ei9׽W#SQP(L}(999K.뮻^{M6ڵ뛯VܱX"x`ߞÕGT(/?!"U10+/͏jA#(s.e}䘄gD KN𚿠۷رh1G2F-=KrP:aI6F&xzɹxF˨kEyYׯ_[n-))ٹm{III3HJJB[W__vP}cCKS3B ,bo*}{Y""""hs+mܦ4́#q/9ٲr׹4/py,dԣ-wA ̘/XP :wRw#޷"O_/}7SqtѮYxMo.|{'tO6 ກyvdϟ=}ji{ HKLS*Irqq8am Kh`)?2{1hp33gv WvcUaKN.uC+%[Ю549kߑO+Wv’0Y:t)>d)+?L)feu?}͖'-}VyGW}ŧI|mъ}ʼn &._٦.0H-x֐|Ʒi|Ngl FeXC$nrKA׃{)3P /!!y8pt>)ѹ}sqGWjXMݹ1F&lTNGDvvvnn.ϷleVA8Pjdj4 eÂ7I ^`T' C yHH}Do%Wg& $wW&ʃv116mx]P~E$0vX#.>>dCrՔ>B/8d?ФӠu.YH+O ?&7G P6̇kroA:ɼꏧ^>0ſC-}ڱJaڄ?SUM}#yYcPWz{7}P(a{VkPPPQQQAAAAAAi#p@@Xm6\xEԧܳzo2TԌyzQ5*&T|ēiO$gus6%Auk$Fߦq$v}3̫b ?X(+SЦ5n-Z iyF6VH)TN;84:;ɪn? D *4ET𛳦It7wUSx;0xW ֩E^Utqk4- *)=X=# TJmTY%4d7%:IrZaC-YZQJ&@Y9uǾ o0̗_~9cƌWSHI(e~ӧMիp6j-v h;I] "_Vsɽ,:dȠv~20FL|kŊV؄=E__au$&5|UfGI/VXZnѭLCY "LV|ۡW+n+ހhRD4OѲR$C)Z 9ӍOz'‹6m&@kJBQwUq n\rܹ:e1VQ$Ă{gj4jeza0T,| fN+)(V*9%3$H޹[*,9wdV,K*`E.ݜQ<ê|Μ}cCvnY\u-V3m_ť.ql):tꡅ,y6/7=x$es| zĽqbڣ2/ljko!ZpJV fw{-UYh5f:bJbb%Kzaƹ{eqq&oE%vMg4upEܥfQB؉ k; WrDoD?ֲA=̘z"GqhQpkđjgqƼ%/_gn`5->d% o.6@9o?2aL|s\Y=JqnQZ5' ْ$g}]bÔG7gT0]m?5//k6f.k =mx1ݜ~zK (goߪ*%JM]hE(u#zSB1*ԑ ٳ|>b| Kb{9;VϞNbYd2jTV tqX6D$N%HKKk֬|xѹ;~putyy}d29sxY'7d(")RL CʗD"]oC@xV 0(@ $M8EQ埍۵k׉'c Ti!Y&6RS]LY$PiLq^ 0AՈ,==100L$=\#EQݺusb1i_)W$K,4zJܥY?I/qsiF^'^~޾~)ڻ/CGeVvw-<GOܻˀ !Am$O.r ߯\~=iAUk9i׍~YqRcXϦl*8Cㅿ8nt?۸?n()+oWN p<kWwax ȑ3Vu|·Ko5hayƄM߸pd>Do9uo=N.O5[6wt9oy߉[=vhϲyܱ)+v_6lqw͔j9}/?̋+ֺ>u´!o_ҿ3j5^PS1/;`4:$TnA%BLz"̵֬ߔt=mZI_ژ9 [\dWAG!+\AQbVq}Z49uvWP.Nڙ,Ŕ}CM59}^[c quLԱr}/]2Oo/>EvhbP=*.=vʯۈlxI::IؖA'n,(ykSDԪQM׮+ْCyjQ'"l__MSt7=|*`j5^PSYcÉ SB8[кm@xgE)uj/,4br//tzmz񼹤 odo剃M x): YH֛]C^YҺSjʻEh`B'?0.8^CYK"$m˪IDATH{0\۶dٮ3ePL&[P08Į[F lm`[{٠B^`)_[TqzÞ&opȯ ګqzH.D"Rfe%H<~iɞ/˱ׂ(꼠9T2c^ "Bخ]@[FgC[AINJԧb[5Vs՟c9$|m}њNBo=9K7bWbģPbTMYˍ7mcوk'Yo=\G0Dm! tupkǫ3<:6yx竧Ϟx 6 ٯHZl hӁwʯxC)\^POU2W((O^F`a*;+/-!_1࡬lyɽ% Ljfs\>{$ A=7Zl9qƥߒAplݫ% NJ*PbrV$ݶt] Q|+5C E S _ڵ3BH*"AC׭2IHu=& ̯Y]v|e&k7u÷?jGЅ|ĩk Q'$FkJ:r\e64tq`_:w ri&ɜr>xDj~gMSTs0gA~70BTNJ=+3K4ZZk˨e>q#k7UMYLeD%a[)cukd\ӫ(Wy_b~쬬ד~[?_1;Ӂ>%L!u0<-uתmǮgTywNg9EZ4Љcfv?[->uֶen_Y< bт#쟸>zDE@gVzreGw^AȼcPIe1y+ ,;gZ„Yɺ]G={̝}EoP$j;4ˍ l-C8=ߍ Ҧ$֗-Y2a^1o/]26ys穌"Te P`K͖OC-8/Tcx'7iT§U+Dh,l#g-/ ֤tIgΜ{#c_mzә?]}0hBW!Nc t4Mm\C^)-ֱxK:5GF4KzѤQ曨3ATgwdYh0˭Ea:a@ /Ɣ"0Y6vk_b'V /kB^_XXH;lP7UﴘS[x7j}H֡."[v {xzjCTw';_g^pvyP(B,7MP7UHbfx|&i1&CWjc6YN޼HJPտá,)#"8ǰPԦTJɗ/1ZnYfE:T$κ@ FD2՛Y2 ؖ.F,9ÄMqpO}@ 0T]gi1X@L'J0PQ$@P[f;O (:S=7X>n6ef'$RA`ÌXN+:l&I¨B@ 1b' ǓH$2GHEf3)| 0 ,gsbq`@ cE) 2@e(YR .3H%6GYǨu 'wqq!I@`*$`B"0 B,0ځgq$.QbX0f@ clAB 1OSVV^ό%$4k0`0H\]n.Zh4jtZH( fZ Fpl9`=6T4hj( űMP(8!5$$$IeH 0"eYqLyLHR!ΟkإKq{..9y&Q! U*h8qx_SKVqF۶mM6J0LVCɌa@eeei&(AX~@`JԥbARVc4/?b\"Qʽ]6_SF8A(iii8zFiٲ%˲zT@&mcb"Dy7A0L]Id B A@ cE&edd0,+ % 0 &&FTZlVWWWVV1q,@ 0a (JuePצfKRNbVS&PcYGQap~@Jl%xn7y cX$.2H!M`"8aX8ORX=^6Rԙe5xEt;݁m=0H-x[r.J@!nץ{P,zg>cӮ:H30k8ޫto c|Ek l׷jbN( ˱j,XV,+kϟL0o¬q^|4jo[q+~OJmޛ˳4 ur+/"=}:as^˷/8D:eÌ ¦Mĥyyȱ_Ã1.aVxZu<$ Yip P ÄA0 ‘ m\AT1X ZGϚ^ۈ^Rtlٖc6/@@ఖ>{/g-3nyl_p&,k9 mhtL$Y33|nZ|sQ$Pgo݉ '%@QhjZVg0V3Xv Ù-6Ap8aYqWmFT=.vu,ݶse )x#a+km@ɓ'|,`KO/C~vfdKy(MZ2غ}Ζ߿>tP]JfKc';?[tnʻwzukm\Ir˯q{G}ZUwzr6VD-,sYБO9mټbbh9s>_u7lͫgT+ /i$t'n24MQVU <<=EH$ 82EgcnXz=aR)]Rg&.Ӭ\ߪ 1I[á8B54(Kgř &S=?=+us)USDw_B@ޡV.x ฑӆ{=x]S~0*ƓD1p֟caRq!fXkңnNzNC Oyu]=a/TA%#^[dyF7v hct.C8b]G ݺ/bxR'\Q$ݭ1t:L[Y%1^̬! q!Ird2 a]l=v1K[6ĔGa%еb ]daq*E  vUJ1ڴbZlK@ueĦg+?'z+lyzQ\kuߙ PXQ`f[NJQ0)^M7|2'!$v-=jMwҭ0rB!Z6„nBĢ`0pUHIë_dzxy(cE!GDBc-ZE-MᙑPl)H 7Ƣ3h ZjeZ/^o?ndF z.2V{۹;tm&gdˣ٨{88npsXiɞoO]xT'*xYoX=7Ӟ_Zwzabu2=-pR`ܺ ݂|3ًQ3 >R '4_A1Ub;1^8Fo3 GLOAܙ3ׯ]Cq0v=B7vͩà1Ke!CPe=4֒J!( Wxg'W(5)s3LVdZqS_ZOB)"hLjoyaѭLC^tzIYZ') LA3$ kۻIwlnm{4co>${4Hsp5Sd~~H*QRcL e$) 0Oa2b5͓?^}݌{W`ܤKSw8KNY00+~;[U3s<; i;xly'/Cj8l{yE5۷߀~]V^}[NS\9ilSf5pSPsm9>} &%P}Bk'c1Ä5nmYܫfo:~#-;ޅL{>=!Bng3*޹:3||D|ɠ'Il0M* +mcY3P c Y@Ѵ ?xķܲkעWīoNzУxB<߈E[[3n{8u⯖~4p.Gy9oVl V9ɫ绬ٲr&(=1r:^g{ܢjN%#ItPNCnfYv_S`릻onPl߰{~ֳn?%V,~G+%ۿœmqIENDB`sqlkit-0.9.5/doc/static/images/director-model.png0000644000175000017500000006066311714210425021322 0ustar sandrosandroPNG  IHDRdsRGBbKGD pHYs  tIME p{ IDATxw\qX84GTDĆшł=!E(j 5PL ADT@:Rr p(Fo̾v͛7P(@ # @N@ O4Sa*L@ꗋS cɿT2zd/hFr@ A{疬bmbmbe7_hQ_5վw.(*~K`BUӌX z̘xM7! VuhZV,B!I200-#_I1waRIԦSsVGU&툝c'j3&+9,G S__m۶իW#q&$ͤ. 0CI+HtCPa$*Qs ^GESa*?& T-W6pbWuPcb_Glkks2H,>iلö!ֽO<",|%/kC/Z)W;>!!êIqiY[ظ:^/e1oAV76117~J) k5R%P2t I(o*6ҡfjA|^cFa%1AӜz PZu1U ^IWy/skH W/o@ >#.P92Il%j+p İ4s"!(N85lVb!;CM 6\w{VR07Y̫ m'nzoy6=oNR &$D~UWW[s1r_)(JJ2qIf2DJǽ4~7˿ܩ$ 5=܌Y@@du)n3">у? y/YNyg9JO?u?-QQWF R6T9褔OO)e>b&v%Y 7=Sí[Ο?uVP_Fb@Ucn \LzAq3 faO9n/Rn6 dՀo*Gw𼢼eʰX, 'Հ[Srz9|h[y2l} C!}q0вVlC':;f`LpZqlݱm$^<)h(0,,w*ZG;90zu/}tc:wEa}dAKCǬcwoϜ_\e驪1=Ϋzq-6e־zr-x隯,79+T[tج,jI}wqs8g]Y Q4i-ruK,{s\qB|YbSSׯr8___yyy7 89Dt9OwðWM=:sYk0p/ƓxlUA(*(2hGӪ/-WkTuA8WEU h\0ph֦+{=o),u]wyFщ+.1nxj 34fLh+!Bּ:O< $I++?sРAl6{ԩ< Ŀ5  .Gˡt|AY1 hQ!-YRo(5;2̌XE>#`w eu ` h`׿(.jh|>,=Z(9cUIw [2< ZvP<6fu1ߎkT=0T}(EVAZҀ" b._~ӧOWWW 't7FtA]M2ӛIlUAI6][$<t .0*]C!MR>(~p#ek}lgX{τϋn])c<:pe^G;OjZH35uXqϓ k 0id'r 1A)lI[F:COg~ֵIJ7' J?~r+IMD͌j{;W+l>G>ϼwZ k=9 ǥsMR?~-GilQo~›3,@ !_T$FٳϟA~861XԽ @Ͷ.kOCc}!} hH\O\2$LQ,(C`م)qBꝩ8\VAl\p*ЙAM';h :!acbŏk){h .w诣!%a 8z-3kC0aQoE??l:v7 JC=W;sޚg;Nuћ&\rʧ)E7߿]0Hw P2[(H(N6GBt嬅H O4UV 2DWWWQQaÆ!!(А@|d^wu p λfL_#U %CWד䫛? @J&keIMs'Bk:K|'(V߹gs$[㫧8Q }md@ts _IK, >T]מ!]I6.J23d܈6⧻Nߍ Av%5I{yLvplkfgמ!<SHs. 0V&%q!<âJhۿ5"dg7>׋,ܹsMLLqqw7660`ERB LqBa6 uBP(dեΈG$[fycBVFI7.)W;'O9\J"J8|TPߢRS<=mƽ'"җ7Eߪ)5#M w8*i8TQW=r\u56u<6q畣(Oq׾ȔUQ2ZVۮ =l6̕YgxP ~܎cUr~_};IHKW}!0 z#vss[bȑ#L&Qҧ'F_]o] wnՀ k6;oHH,gN/.~ ԙ8Ǝ7p8.~B:0l̋ |u'+h [uEF\eJCaȒ~!4$ȩu!ʝ:̸8(Lψ\nc@[(goy~ylhY k'®{_K V-P&W* Ԙ~8XJ/61 k$@ ]RYtlVJ'5Rkjr^t4ݴ+2zrW6S_Ԑ82Ջ@ 4H&!{yMa3N 0^)9.Tcͤm.m~CKÚj) ~w Ts=N|E'`௣;3̶ҕM6/N׋@ 4` jGWIw [2,dǿ~\*yZ5CpĢ٘o5ؾR(;{OOԍYJL PWRZ^(֤>TxzVmdk_l_LV^[L{R-Œ*K30U=9,@|< 8O@8R\n`((߹QMYzoc/xyٞ[O\e٪޾^:M6[ќzgy" p\:$5+#1YYOY[%sZo{:Y~ga* lbe܍Q8&mh?GyVtM99W{?{Y(}"bwG v ܒċqį]nr)w^ՈI8ʃygn_Z2~d@H9Z?iʊf>A[8BA=ن:.4;4U=l;i0럛ˑ,$lMpbdMhRoH*s plz|ډOQW1 @\qac9Mg:.Mm2_O;Ei`p[- '4?0Ec͞?{g46l~ÍckϜ&o0vh*@ @ r@tbM(5$ $}:Dkmĕk@ (К@ "{@B*ӚLR}gnXX:iz$>YCm_>hHuUНu˕%{|kp43tpUL{.+@ , P8ucɻqrC‚H叜*~K`BUӌr[MP}sOO.|Θ'kǷxSaddwa- QGn |EюO]3 1>#Ᶎs_m͞HO#VI?uپZǔDm:UԜ׉{Í[+D&[m%8%nAݻ6{1Ez3&+m2Sg$ '/5lߘ Q4Rѿ:+]maf:a原D 6Q&BA}IBK|Kw⛬HUi8t$sm')\WP~Ƀ1zC[E;l tY[oe \^F`! ,_4d;d~"Y] p9G%1AӜz PZu1bA˼ ,v[%;]>^ MuqE-~&ՔG*"_jQn,gq5cA($SOzQU#bЂw7*roI)qg)6zSJ-eQWpHvS"u'o |/875l<^TH5_=/R9 s{q"g՟q˼,%7rV7z#o.r?4d$ȍaNkӏqsȻXN|/sM*=UFL&J Z}!4wt*;%] z&N/lxtMF(uvvy󶽛ҽdv%iM`x (z񤌣&$ӑ3Uz\`sh/;lO?:3=OK-o8fUx7%߹``:eu{Oft| rv3zSt/}tc:w駶Se.tCNŹ-1HϖJﮭl{PctyUA1P ofv~NcrV7Rn+a HiJ 499KCFTJWebi6@J6 Q #:JXr,eɰ, gʱ]lyV6E2躪z`ʱ$Ӟ*D ꖊSש 030y#M,O}`nhA&aэ+9:Y9U,x:6yJ@ɢwnW>>g;Au8m ̘ ma,#U geWL1TTB?uοU@0eA]e t"w {TdtЁ!?6xu܉`w$|"D׃iiju=5_oQv58WEUhAʑIm߸NDzo7M$O1F;S~GrI{vSXdWRۏ)zelyoZwƉ}F;ErNmH00)v8YK8yU 0HWSר W_OߙΤA ޭH{n3 77{K#"ct,<( "=H ૾~T_ }v8ڰ  Ј7i1b+cUޫ%[ DH/.sID>H1ZH[#%9P>cXrГ@|SN޺qL#@mƙ(̽ uNs2:~Œ>ė7pjˋ_&uވ2_\3+ k<Ew$3yGj[w2>z[=f0I]g0r'RCBR;Ōm0q|G%D Ӏ@ 4*@ 9; wf6mC~%cʟ cYXy}Zd;_4l0_/3yNk0&nÇ)@ :d >];7Z/V%%L>R BݏkK*~K`BUӌ?'Ks3V!P1p$E둩<~& P;m E=W]e:AJCezdW'J]g'5.,$-ƽz IP4dnkDzJReOǔDm:Uܜ%G}lorі)ʵeb{ߜ>w|v]lPY ƌ⬣Sor=Ԭ>㔍o0k̓ȵ1YmsXΐL95o}^/> q5cA($7j3O/jF5vK!3؁ &½A<);_k8VG = ު_= Ǒam$3KJȒiN=AYx] FCF0qrOK($V%[ ~ɜN|"r$V „SwA|;v,GQOSЅS ؔZU1ڍqsC bA˼ ,v[^۵&8ãפ J gwa^&V^x_rMel/OWsHIX/]|n4A(x^17oaT 'Q޶5ZTX2v?Prr!T5[@ >S.mvFħ_ )Y]c>DѹpC1ܞQᣂ*Gwvi7v=+G^5ia1U^c~_Ѿpw/ӊ\2rѵ_@VFI7.)W;'O9\Сtnp İ4s"+TDty=a2#C{'|po*S"R]MO^ߺZ>1y VAuZc*q_A0vqy:`kf^dm&0t;]YFo=f_uB94}UWtzwJ6YTk{M/ ^2؄%]1tV@H' O8Z: -ݺ|79kq];''c4m W9xhwVmßO⣵k'))U= <0Q} Fa)PUxutMs+MrԍJ+nJϗ5/lwK,[x#XXR@~-iGfž r@\ ԿJ ـ,Uc0"866ƺR/sckn<ռuE- 535Vу\88yN+~Yyq^w .9P[QKI0 ddY2l. Ùr,B1@qZK\.[DECFcmdd(ۨ7D6C9ђ @USպY,I=Ws3b 7VyK wT[ ũ&4tN6Nwpܤx4U!][K^˂ʺXs'Qkjմٹ6\MΫq eM3utN4D=݁)7{r/Lr*JU\@|v5 s+ K}U!,c7qrL\HdΓ ۍ=aXW-^ k_4fl 7Mk@뵒¼GC_ҋ}VFHbdNa]~|a@h|FF|hʈiʰ~Xa'W6,ޟg;SU>Hc;?XtPГF^:cg/xyٞ[O\e٪޾^H/_wn1y 钿AL]O7baFXEdӵ%@RaT9<eC%R9Y A02L3&o \s5ۆY4}߿S>0HWSר M ٜ߸Zxmx|aIGj :Gv%,$!pe!d}%Vsâ{9ooQ+.Th)vNl80,炇*L)?ޛnc7#rb28ՇGo7cmnO޽]Fb`/nؐdO5*6rlw  h@ka`k' g@FwP&g}s$ ogoT\YV {t=vن:XvNyމd$E,c3txz (q_ .o{WܩbESׇ^fLqStU@t~ 6B|DY=q|g;sGX Izh尢+!WmH " <2.7}3f9p;~P4vE3Z^FGWگXy g" DLj֍#e5p肭]UH,4RCBR;H6r @  4F %8 d+(AhNnsPy}Z ]JYM *uɼ1N[|]g'N2@/]6[Mj0&nÇezA qg&?N_K w؅o4^JJ|dARǩKu%D(ŀU[zf ?Yzo A%/__]b'.+1vAzN 5?]e:AJCeNGzdW^*I]߮MN?aq{⇧zA |ڴE,ぽ7޾i3|2}S5{"=X%)WxegSTGssVq)^sF?E[,+^A}buk'ˆL՜Kh@,o@,::u!C ϟ3NN\6 wDˆUAzA HC% F_6{鉪_xr(')ZAlJ*roI)qg)6zSJmgG]XJjb4ƚsѾpw/ӊ\2rѵڄJJ[c=܌_7 c*qӮ/|Gz]t,jX#n@k'TҖ;(kTG?A @ fQ\b8.f2iWU,.@={K90Sn#ۡtnp İ4s"+TDty=a2#C{'|po*S"R]':W`Ь*C.Ea}dAKCǬM`x (^g] w`kf^dm&0t;]YFo=f_uB94}Ug :%] z&N/lxtMFt: KH O8Z: -ݺ|79kq];''c4u^ӣ;ygrm}i2SghI s EULp-$q_m0fFkqp0YW\gsRYtlVJ$`,9Ȳd\3XP_SQ(cⴖr^\ECFJ{6l|Q2mR Ftђ@USպY,I=Ws3b 7V;鱡8y>7ZI41bt|*dUkkcqYPWYk$ʳ+hԪit9=mW; 9-$mN˚g)h9zSn7S9B_F^'U POoU&P. 4l. t˿N<9pQUyUP=⼰W=v9W k_4fl 7Ms- PjZIa^E>+?$KT%oGrkTWEU]JP0jB.ywN d0&aPSFԕp4[zuyBPRhqɚWS}3.Q;8o]m5 9K'6!>:s;c|6d#ݍ'1d[1fW@|#1~sN0@2.%޿$_%-6n>ڲP)n6۱y c'@t?~k(MȫD et̚Ɠ18LZ  UMc[04ø=ȫ 6'txZ\M0B_j.J|\NŁzGKm'QSP =xP[+p\ aKdkME DSWt}YaQ FiRҁ;>.՞E Lq95Q-Vv.n;#JJ@50" Z>hڶ>PXHWb'&_dkMtXgԕhe].&-,Jk9YmX<6Qt. P>㫤˳&="FaLE5n}ڵozjRT{sSÖ+.=tYb?F$$5=:EuvBUjx**m~Fv8.f2iWmt@ 8/rSg`xêzkWOҫ ;OM/ -løߖ^ewŔ2lLt[n%iK6YTcuJ`koSM`ǥ{]vvӆIUxRQhy̾1FږncZf+0hVS130y#VuN6Nwr~`FVyui032CB;2l!o+}m--b8\M0q=k(?֑-L(/3z;}c Yq: d@do6n辖-|ýiZ3L)͔giޏU޼ ݈! Y}=C1Ka NsZ9o@7d.F~lѣ wt (h w1H# VoG /UO`7oguڤ7=4ց~J:o>G Ti@ (`VO 뷵sO?_U-J۹zנAVOQYjƬy6G Ti@ >+xlO|U l6j? $#Y% @ =@ % @ PҀ@ % @ PҀ@ % @ PҀ@ @ (i@ @ (i@ @ (i@ @ 4 @I@ 4 @I@ 4 @I 7>@Wk5 hMm72wonl{Xٹܞ*Z_Q Qri#kHg?R˯fQM&qX==uI֪xyǗq\d=1@Cvk7?:4} P8gc]Mbx)q- ._:Z qio懼/$Լeq7pXZ=~&㸺ٰȴj1 / Ǚ3Dk8C@P};PC58鯤*0hrۿ-IM<1_¯OW2@)Ϩ{{ޑ~o\y }46)[{b v;㲹ev#.NQŭ_RquK+%bթ}l㸖WOՙ`kM.COUpϠkc#NE&pEͥaBAlWw[4!ҏ9uqz@}?ŽcvbXXt?ww%d;ҏ} dݷŋ8|툫S#&;8sSBI[i}?w6*=sQO+ǩeG[ő+{rţiwM'ّ~NL e_|iGBcFx~P`y52])=!wx"̚NZukL>qD'΍LMɺlz7ܠD['4.zuMKĽGW%@JC˩үjwj ézݞ_p8zd _znq8/r^嫛!&ǿ߾(r͂ڦJ m-(%9YE8 E=r̄/ɑPXCu9wnm⊗y~_i*(q?xivZcGF뉆!|Wv_ eG3gc+v|H|:yOBF+T}8Eg*{RژiO:+)>8TMAq)F&_rʛL{;g#'Vk+a+r-OX,6=Ӝ)U5bKE aGmq|{`D~ oŗ ^D%kG\^߮}P,c>; s01B#'3vi 3"Oyx^2fZ M?9#d@NNe_Om{az_n2Ԛ}Gҭ'Lwe,nV̡G \GY!Ǘo=ačަ>c-V܈:7;RM;n;x& tV#,cY_*;r"Z5R{m h =8`G'1ͧF/!Ep{|A8iSI`;k%7@\ouW-=T 9QУ1":Wӷ?0jހہ;wzz^DuC2=8Eקr{sRt\9_kڃFXa,*XB,Iy1aHO[hcL8CЦ[ )To>ݚAթrlk߾6@5@j_EIןY~=-c\`5^KV~cR+om Kft oGֺcfχ _Hg^Q"3N/$<^>}o@+ɵCG00u>P='ZIx/HtWxޑ;gZv`:$ MF(4J4hs +;JVm-Tn2vٺ3yK-GW34Nz b}ScT)b1ɕ7HnYibJ02b&n}԰vt)h~~PDq )2H$:帚*LE4{ [%ߋhk)_'3BEW+.&#eⰨ>G ! +;ƼEة'\yP\ox,WثIGRub˖/1mUPP)ij`CX=J8`Rn @Ho'bK#(on$T-2Ly^鷳JOAA+yi/xȏWcto =\DU2Vd t=5yNcL1R@$* MwxyfР[/V kG"i(b2vCgBR2ռ2ښjeZp)wݸ Wb5 qawy--IJ]c FPڧ AZSO? p4P!qC֓Hdk96uCUն{i6AChjZ躦 Rn~YviF[%I{F31>f?^ w:C H猪?Jڣ}O q|rK`)w?Ɉ$z\1U{iOW~$]u>$g81FPR"1Di 4),ied^7*gR @52ս3t{+ܾS,pR&,,sG[N:Ιnѭ ؞Gt*y/ȼ[$й"IbI(*SG}ѕG EFfF i"Tz (bne^iDϲՋN'R5 e {3B$ UeuJj<( S'zx&.&r #Z0sr =[>$PB P07UZR'kpyyVOlD?1Eb?t(nw|K6/UD"'qPxEK2.w|Ŝ7ٕJ--(P_ZF,4 E_q!Ԕd3"_1N[W#|:7^j"6\R"2Ww%5W}4 a;}bϼ-&YSW&@5W֦{(lwY:њˢs \swjZ}Ⓖ{gbS}}K,tPʖKo꣨ůK+yb Y'ʭގK?!K7Wm }%{Gyc~+8p͝CI)"QN U ܬ N>6ҷ40ל͟DuB`ڌ;uFTSVHgAbSN p`J/(._:(C|0zuBUm'~lgD@Fj7o0ik?7xݾE3xm,vB۴SvZ6V۠||"eđe~cokw,/yEl Z;ر^(FÍWtWo* ߞ{e"@盯0Mp'Őm XcmvIȭD=Ê1LU1dL ԟ7auQuEߔu-B1#SpP;\TC <'o LYH-JJQ'0_L Lr(jo}q)М5ν,?ȏ/($yIv0[N1&15A^9]w^rsӎp;Gi 39IDATg4:bWC[A_2ϗ@1[k%]_OZe~~s4r`8ˈn`7og}~K[fvK8bŁ۵ԗq&zJzJx/>`kYTryCwV)U(`VO > ^%$pXV/.~@(/zX5_G ٍ,*KM՘1φ0A@ * % @ ڞ@ .xm@IENDB`sqlkit-0.9.5/doc/static/images/sqledit.png0000644000175000017500000007137211714210425020055 0ustar sandrosandroPNG  IHDRRm>sRGBbKGD pHYs  tIME:ڔ IDATx}wU;ܲn}eH* P5&J4~5)XbĒ+ؐ"MAiҤ)eil[<+=33> R@@@@@@I@@@@@@О=A{ ' hO@@@@@;@{J}Ⱦb}s(i͟e=VO{qSL6֝?NIv|;˪+f}<@ܔ_qcW)8x֕Kkzt8 ~suγ!sЫ] ~SN{◃D @@@B{X4hjoz=G~y`,Afqﶇyvh.y9^ô.l;߽s򗕟z]%dH}_1BdBVj<zր+{5jm |hN)#}ȁ2'^;,/?|7/ lAH𲕟\95qzE.Zgݮu8o$ 8 =oIt<^ L Uu6)IxQ ~oѽu׭%?tG떗,wۯGʖtOqMD/gRswn[wbbB'oٿ}TtW)WQ,E.!Ո&*>7[ŊG^\y78r\F5L=ovVJbNѠkey0nsrθ2~ݺ@UJ''3).-`iNc2\tҵ}+p|bQ?߽,{Tb,+E2{]>wB^Վ% k8K钔oεO"ĢiEmo۳=xhѩ^YxT߯?oޟj?]hB8^ '\Qаn]hWqo6helLgOu2:+v󴜨;3Ld@р=-3(JG%vΔ$&op+|P0(Q2#NuLH?Z@y63ۭ('Wvw^g٧1ͥ,Eλbǧ__I{_i&dvK.I19yz\Ѵ۴Z+rJz i9sL3\}&;wI_x;s}?zνw.޲=pQ@$?ӏ&tCW1.Z5rOa8y$h`iCmuۏ%w&ǼzM/6frJЮwt" 58G;F% mFh~uLk8QJ.\o)1^f: kVk2ߑqy˞ܾf\}Ń^ֻuԙ5[}A^Ñu{tPKL^f+9;jJcy ɞ2ڝkfuZ^QZwi!c~6i#dѮ{ڛ*+j*ڹ/@ӆ&HrBF:v*Z''wZh>+ݳeey,|}:P%)#cxѶƊc?~~ڙ9zN|+9qW֝zASNuaS@ mzo[uʂ_Y6ii(ϘftZȬxkKk\nEʄqY] |kz EE߭/WO{˵ǟ~;ތK)|QO9V(;mLڎw~3 c'zw׭c \ͳk_Y_q&;-׬26/znX,E~<;mlm6Eւ_1 El!Pު_n{.y'&%ɢ ;P5 kom:'6@ģΞWo#o9 y| ? ؛=A{ |. ' hO@@@@@@О=o =9Q3Ddp}Z"UjWٙ|5==.H,;8-A{a[W왟&Q\%@r%*&ťTsr.tK@@@@^(Sj,'J$; QA@BTտChK鎓޲=9M   w8N29Frz% @>ͨC-4HL%(0.1v \%K@@@@^4pD"D$xTR!tB23MS9 54B0}s0BGΐQ" )H"h_A?~!@~˴ô[[!8a#S~re 8jI4QY&t[QQbcsIeYjx'qY"1t 829qko۵>=CP@u#HL1G̹&C$l;iEoimA%豣mmXo.# TS@@@d_u]˝)9D@@RʃT\aLa\ax k^~Dc=8EgQGЌ6D;]DsPaX\rxnm5yF2Bz3BZqxRbRFf,;R?_AA%PV~bN/S@@@=%HnРb\)D"r~GC RN{9cHe$*hz\`"86/NӖ8CꓶWSipdMU͈,))W_rmY97Ͻ_⣄>q)Cr9eKvSF<Nᙿ# %:!`L`z#!9'cE 'QCj[xjl١l)P 66G8ypUxY!1ۿ(c>CzA@Ԗ/DL+ 5z8QV԰e˖`PNIMKMI-(KKK?rP=M`׮cG * 8#(ȒZQTNbb;6XSc < DH9AR}.l^o@sRg{qطۉGˬOE# hdZW'D`}p>g!ZZ@Xům޴e ZZ9 q -MU]?x=o# "W(q8b&^5?S=xud4j{AsET 9AQUTg} AZD|5XMJgN>]v([:8$B8Gs7ԓ\ZpG[s(I,˒$w)B)}yɎ2pV8c㛚3D$`zѥנGʺMMNwX gBМ}&gi랾l's.v;䍉`0Cȃz6JZahZj(1noLL.y!^*JEU9b7 ":9o r䐱2k4uʜ֭z2o!7} li MMn JIYSk$ .a'QfR ۋ_U//;.KN(;0Ƒӎӊ̌g]SLճ+oaiiiivYsssg_pтKgHK* pNir$p~ Xw G8g76)c`39A]@+ ֵ/( KQG ḇitGUU_QJ9!p8yRТ11_K?|بݷ73c99uS'O+//k,|k333 Ko P"` waw)=;x`L :Y#GwA6JJB90ȩz]a[@SD"(%h HCɥr.'$N16K i!GCBZ[}͍~м!z ՙF{qZ#;3k~|m5U陫֬/~аbn sЁ9W_ӧ]#U@@@y{@}~֯(0mzF%s00N25j]ޔN:dD%葃gKfS%aV i@jKeBHjj#ePx\6)c:Nz=r0)%)555666666+#sUO?/Y -^s/%U@@@z{ P"o]3'K|.^QGK=;x{{ p ;ɩx&!=zdG Ubkxcݥ%RZ.K$BHNN^g!U_[qtE]YSiIvt:U{>4.N,f8״(!@kK.{0{\]j.?H]Ͼ2cpѿVV99H'҂qN!>#No^9E9̨nbB !E)uy<JWZd>ߡZ;A B8d*[q4;\dLeqdsT#ĞDi>i*}qqaCϻkkKJKۃqqRSnD|5w1RZ,9b1.!G/.T@@@4)WP&(HJ^rkfN- T~dFAi+ vT$`e8G/$:LPHڸ8>>61>u chmUWU7&:|M͍ j`9N`Oo{#%qެ84+HA< ڧOW"CAAP8*?YSuЈ8jcL]-`˱9xIɖ gXZq_{0=9{|% $␈-y<'ko yQn=%,I%%%zBEvED3XQ?i̇*Q N.Wh YhK]?NOY:#WHd!iɄ${ FO?HA(r,$9%Ǹ1nGq9d iHZR11piV}f } QTHTSN}ʘ=BXO KI1/]xSm%"wH:c"=ȢE `B\teeCG'ƆA,%/y y0_(◃ FHZHMMKII߯,111>nR{DuN|DIHH瓇qQ^.옍39#ufr\.Au:h5AAԏոOH|Q 5Ѩc{!ia؞"$@7w&^u')ufaKK@@@@@^'W\6o ىC蝕AUX8 !\%o#f( AR/Kj[{\4ϕup/RT#&GZBA [Dlh$#WQ 3f>g{6v`rF4΂o}xsݡ- ?#@ ޞWl=8:/( pn=; m9;~5x|<% :X"`{o}cͣ=vsbv<ט"/*J6.ɨm;PcEk@'=mo?{^ARo/ۇq#ZSrvxi") p`z82\=[hQ ,hxO_-h뫂%4c6V1D^U}1S%.3WeCrg]wZV~qPsuc&H5*na7kaaaaaa$7/9oPn [S!CΑsd3 gL/RZ+n<ln<6A]EsE]{yIWU2dh>cxRqe@ѣ/}TQ߶kݡU1ź,"Q?Q/}J;?N/A$_;HKVMۏ.y"-D߾{qV6`0 y_}1"wR~GI=qGpR?ٵ(*u\=Uo?YML:.'K{*荝re˲i"rlH@*y|U?Sp4[O|8NDƅp5eҎ;qrV$0+5Ъ3S!RkzL3U% _yI)iz>./.-,~t3Rňk>2 'GU~u.9%II.x苯'F -{rgգuU}sFd@✂D~@B$P[)D@@@{ʲ @6'I̥C=a#&1H,cǞXoVnWIK~^eٱ떍{\Ғ^sKN5ą '`2lԤ6 S DJNI:rrFvgT rƧ|}WJH#[D&86* "E f|_\3 r$gH3~.; $t6 hhl^v$a n "hE{o-nGY=)#iB3'zj*5 !Fkѳ.@869ؚ. b1DbvLaE3 ~U>КvSwqjiK2rkWhFyLw0{):tQ7 *CZ 0tX ;pǨB+ 1!X\; FjP) i7[̖`4EBحCw-1{S`$nɹfVfdnV#O_hJ(nGK=AU"ZSnf-vn֥aP1-^p\ULa1NuH}Z Mւ݇M2SߠuO[.}uݭ*Mًd7X,4r,ARze[+B=vRš{RTc)F+h&omj2ZlEh!RB n6 5kK7(V*1b4bRoNXOI{b fǢ1 ]zwfdeV%L&EhFnL!c62=X=4K"<-c'Լ2E%X=?C0TրYBp0nS;^A z&4:ѣwJD5 h-NG(]A@no5K,қ0cIpՀ-#-l]NĦVV ggv ǐj7Սk[ՂCkHK(xZYtUCKҞ#?$l΄j\3mfuh0 ¢zQ;Qk,v"lvTigօV@S8Z+FkX*冨`9Z#%UecHF5WOjmNeݢ2FAҭݲCi7Zf]9hw)ӎEQ4v5 fm9u bV 3"0vD,1]s4CQgЮSrrnD5o~.haD6cP`L Zڋ@DQAK`4eYh莾qFl="덈=$Ti#I,'!2uCMb!n!ͫ[yK%5"8fgԺj'O #ўE矘heh_`6gר2V6#!|pal!Vѵ9Ń% 4= ^dZ5IKHC{gi6z5fRW>Pe\]oՆA#%ܔEcJ1_6p",y-BTS7䦵EO< S>"55O;ik` i+Qbh6>3jח`\hwh0VU~mx\I1:kUB3`ÎYRU* bke\Ww7̞QdeӧG%o!GkڻJ)P%&FrHl=*bIح %<]UM'fu$ :)"X"йx4bQbG>D e=d~un#Tgx-+͑0t kn,!m,6&Z#lVSj?zcdu k\waB(u0btv u BsꩵvhxYt"GX{C؇"UqSnZhihF@GqFaNKZ8CBeխ%fA,#ȼslUawUmUc֙)5g~/yVtD-7Іu|jП?9" +fsZAb fhZ-`6bN- 6T:SHY0J{5CkwIf-@K'6[u:&`X>nȼ9Bn:%#vsb>6"Ln?6.4UlmĦ?/k\Ŀ w4Ҹ M4֬3;Ʋ~~39p?F JmCt 9WQ?4G-rgBjҦj|帣5n/ |/^^cr9U4W|!|H ta}X2?2-Fs<Q&+f, JC2o < /z2)Z s5nx,mӚڭVxnY]Yƴ0 k|f;~96a%@cҰagt@rC"օZ@@le)#>g蔋/B(Бѻ )Z:;^𧛻{_sSnBdDŽ*a|чlTgBDaMyU5cK̐x-mu,mRF3hfǢ1v>e<iNhAA&6$ ;o&ʵ*%kP)c0a TTm,\<pE2pDYvDA/>##-IU[VF'wٗ u#_ܴH f?Y" paךK6(9yƴ ]=uz!IؐQri hr P>(􋥻6$A# 2Nu@hݚg^{|[JZ'|(eSGJ!.?Q}%?gZ7Ck^?JmWWڊ 3z{ՐtG`:QejQ g|Fz_7IAl?╷-j[;z+,۱-9cƬUsVtkI93/#}l2eo T RS=֭GQ<]=/aךU<]`OVf-xFj<|һ@{zo5y#c\isUqXܯ[KԹ|3N=oW{jڳ]>^zر%i|oˀMig^bE69MQWUvyÜ=sf ;}v ”]Wu:cڄ1 ^{lCW_Wtnow*|-Q$ffy8r㇫ViGJZWvףtǖIs1Fjͼk7 e d;+j̑S6~>ۯkYwگb ~Wș]>0a[ڠ\;}*A4Zj6Sn㻺7؆~>9ϩz_R'?sZ&̔dw_3k/}ۮɣ*PGDFQM eY†Opo3fOt03T(>D}F(LE*?=RjJJSO<;-7~LwzRjæ%k|{qO0dTQ>pֵ xu ^[w+xR_ث0Cip; %oί( yOweC$|NTe9gĐ7l+i_>\+0S3a?wҗ1<ٳ#a?\+5}S_(krƹVDis@:dto c\b# ]=9^wHtɸayIf̬۽1qOQ QA.lkhwMMR (H dB=nhʜ:)cz ݴ _Uc.387!!!9!UQ\Sc$&%ɛ|A=qBBQK<2CI5}jrhe(E41T5Z" Zlg`kMۛ$Cy%@Ce+7sXvfj搱}}UGuMI>qdJђ`XS5#pTLaLQT4Z-̓~~`vF9m9٫_l57zyGz&șxh}43N92nr?H huShg#fO:>(=: { (j/Fr;ĄdUn[7ަήܢ)uŷi?Ѯ)c X8\0z&MhyO5 ZD^y+r{\nOeٱmͽ 9gz|kvk:eHR ]-pdJ W~0jiS}byJ>m^y*m\]Ι|~ʿo={4bY܈RbVP+HҘ4CkiN\,5#)m/.oF5uI zOӯm+*(m`T9 "U8,}/0ptK{T'c\-WvC+3=o+?=9*@ڴ( LmՄma9Ӟ3Fy9k:F]7<{tEׯіq=f́3M8N羺ZENLrAT4FW;KpimݹَΊIyco"oBB 6,iXp@z7t/B)~O^\*$xTaFэ$ wBzfZrdot[L?5_zs6T [+ɕ\3O(V8M77'UlANpQh7UVC< X[ufJj ݓI#!+҇ U˙&Iٸp>uEGBf6W?Ykeϝ┐d'ݱ-+c_|u%Jlj;kqS2wr׶+Y)]\#z:n){J׵= 39TڇJv rt`H6Bd>E}76-!*P@П *F+s I8+4Ad8u}:Iť'aцPVˢr`:3O(sBB=VG GδxH[(mW#gJ6/x'kjLr22xL*^r5eǶ~5A39Gdz:LjG3{GR>ظ%&mЮ3]wl{i}}ՑnN=2]B丬WӞ9I+Ll..iq"rܵrՍ ' /rٵ9C.e-ݾ!yXXYx閥;jmڄ\gPs:Q,\[SWUUy[Ҏ`đ<ؑ#YQ湪8P|p^ӎ1i.pL̸,Wש3nxٛխDw3,`6bJ?L֎!2U Z#@bd=G5LK~]*SDH^XׯX,[w)WƮud]bwݥT)4>uvw'C!+7&#MhFkLg=J2U͋~(w\wd`#}# g~vU/ݾJ99{ wӺK %Bf=(SN__g8Yf3kywc` TR%ԋC[`j1Pq2Ve,/B5v{/PK$[L* 'f*yZOsVaA!vnθp }W: ȟz#}Ya`I|:Bd^H͆ h' * ka@^) `WfrхJ~!lf$ BаlGGy'&?Ě/7R39%&>:yzJuy rQz*p脤6pBH',kũ-t_48/4EztP7?/ [>Ĺce%Z=oź1}>\2O!hxI 505_)&a[{(1ȍF%თ\%#~RB!Tz{!=BB!a!°BaC!~cFT,2Ӆ Ĭ|ΰ'hKj(>45tb1+0*ӟ빱 CKdnעt"? Ĭ| O,0+P= {@K]X7sAI f[MQS"3X]|'Y<i  cԟbV`k^ Iӥnz.5@T'"CA.3m$V'o۫|L!CKetPSу.IA333͛@PV䫫EPYVFO\q{{{~&B`eJPR`z{KYCsӰuN?k FVgasݽ=VmxpqFg[4i@4R\+>]ZHN# 2al[QU局lFmC m/P JINLT0Q[I]}ř897W^3zEXU{12AP-3nl'Pz{A:ux1կ`o󭌰tTo1cl좭!.[xBnn440 t8;Ys^ҷ-{Uwn(ikGCw2׉^ n1#~߶m<y6"xŀ8|=8p`AA[7wʌ|LՙCWLg 蒮=PU ^RO a|۫KOOrss;vl`` vEYY p~Є/g͸sS/%% 'MRߴpgmfܴW՟lXe^P6Yrdq-TEo^7<=^7s) ban>}Æ ϟ8qbxg^dM]!&Qg;"|73D>gܭ6kjԷ]s]M45DF uzA0xn]O j9Ju2kB8 lz3oʔ)SLYvulEJ=;q|锦:hnΌ>XT||btzh3wBW <*.|RjN$xsyU~ y?zaIW U:ROuu1|X֭xIPYq EAXvNZZ97FϾ|=LU36H߈}@=n.s/R?d4?}~dM0xB#S%aJ&m4zS݋s9UqO׷ =͵)t\:͵-g .$$@6:9d*F3zAe!*!=N!1Q&vM9K%j'\|/^6y2^f⻛KT-ԳЭJl! E*.=j,6բa}8Ř]m!*.i6jՈIzZX{[t-ës2?0 b/\ Iϯ6j 7_)-K~!lf$6BUEc5:} ' Fp:8&j/}QS3 {n5y4H^-~7p @49 m;8|{%'=JglydLL„,-I!}M w{i>"Wk$_ݵ)4  woB:aCh)inx) n3ϥ zF\TqkZFyMtmݺk`C 'B}(x뱨lE\O"hRǼ<[f&_^aꓤ”gN'5 {U {}?E rGP+|#waffvt0PUANqҦn TV\B.%*)qzj$YLGXZˏuVøP '6FDD&^{ALmKG;C?pǍdЬ[ںABpІ÷ @`8z Hi|砳N:?q'eS+gHC=2߉80{űP%R2b4uԢz wEYڮ]FͷYP9vzmuQ*6̗lۆ>']䘱}i4b ֍ÿj{PqNr t߽C8B!TMaJ{5uO_r74he]=.cDD)z)Śli5;J9)cmE\t}kً#B?9 S>gyp$^$mhe`k4T(!%9ɵ +w!Pq7,Y8~ϯ}v.Rh;'lKF jaW*w\o#BHu!'ցRMÄ#~)zZ2G!31ONajmƒvM,nB:ۣ\)ݰտyᓮBU%!0c!j,!=BB!a!°P&M wɲ xל]:+_^iPMw TJX>&j5mmiB0/͙xבzd9:l FJxsbC y ]vj$Я\R)p8!YLR]FB"HIX{{[z&'`AD,L8qqпt2DYs謇j`w%Wwm =0Mv[ J5ibHQϏ}^kh쨯ig]p¿&x$ag$KYSOOqT14n7\avZ]8SR>)ЈuZ5~xJ90#,O?jBKIC Awn߲lm w>\;"Sm+eŕ|7sofw߾Dz{,"đ`7obg5y@ĩS&;\ikBwn^R 8uwUmyxMw;{97wc{k\ݰ aثD}oU< ߤπL3N\7ϣ%mF6h9iQSqCIƭWx36520H Nk!Y܂;{ʷ7!pKR3ۚj뚴[;c;T͈۬S;5>}QciyG56طU[ V T{ANRo^fǢmrm>Q#FRǼ∌ޢ0+69V/Z0IRa3}VCF8V3(se͟5Q-@i4BϣbaR-e'9 +M`odLU=i|s|It{Nn1jj+5'.zE6puM[Јx{Gx=G&r̒,:>{?ʐ 6\cs lATA/jM`x?Z5KܭffvtA%%Y$E ~j+t̅(=is,Oؐ!{{[mhx)Sdeўf@*rOSn^'^̈́F]kc5jc$_H-je(?URR%3 h(L{ FÛ(N!h[g?ϡ-ulퟧc[?|YjQ2$^$mheŒDt`i?uàUZm\8.5H Z  NأRϲD5[k?#I悯chhlX8*g\=}_q@OY5kџݨvh=_e6a/džc= 0.a\Ʉ^gbJ67sC=~ {"~},[6j 7_}> CH@Dpwضm_/ӵl ÛBՒ9(=htBҋK ym8L "57vۨ~Z7>(!%9ɵ +b)"**oxsƸGàUKǚ>grqCAmTJ[Vh;'lKF jaWRD!TNĕ=aFT333LW%khլKNxnl6 q'Q0 0 CHJ&)E(JFd|e+B=BBaC!T]yEuJrs=T/:u^s<3 'BzB! {!=BB!T;4=IENDB`sqlkit-0.9.5/doc/static/images/o2m.png0000644000175000017500000014573611714210425017113 0ustar sandrosandroPNG  IHDRlssRGBbKGD pHYs  tIME :V` IDATxwEf$^ i >"A~**UA@X!$Cn?3?&K}Lϙ\\3#Ry B\<6߈iQoB;М I7($JؘVfLahƦ8:+PvǶdr4 BM=rS]UeLѫw 躎 < ;*$$$$BR PRvCq}TCAѕ@hbTl7ma'ynW!۟ߕPr(P Mh!۴67I9i꨺s)AE| $kV$BqQ*k1H!%JQ.eU]4|Fĺ]QJ!=9E%8F!x:Җ!di}40 6pC)l:~nZ >jR$ۛٶy-=طhE]J+jPJjofuI+H|ؔG;79Sm$۩@(r% E쓻|n5tCR?0WʻCKђUs)saQG|i1_wiۣcSX-,a'ǧ^?vl)'>s&GW=wtJK͟>hbMu˙`SOgbD7MV5ǟq@xN~ܼo}}\ol)gK4̸V4݋n@x.ל:Mp 3_3L+Jm]#ei:o+aq=Xo0[7pK!M*AM]#JIt]^֍vK nkc f1L_>$$< %{Ξ)"ܞ};;dij q ]$VAYy44S]Ug݌Ο71z,&^WrgKo2iv:f~In}$'\`O%0qd 7iDo2erqO(FHɼg6$=+P* O5T 2 ëw})S"ش;"2GlG;nݹt'׺79ŔF"sRjkji;wВrŎ_=H&^À8>ի۹pҝ41Τ} {9_6<^L[OrjVnea+бSIZZ[x(k$S_{4/>$cF\ʤZof+؏w-i0;^ٜlxMk^H+Qcĉc0w_߆vW8yљd#ÛxA׺36V䷯w2byL+ъ#"Xb&.ȴZg֝#N77hcСX?ϯl~zH)A#L;ZPǦdG}|\:ƞ%V^P >\SqnCGY4h؁#μir^δR4 dH} ]%l2kQGk-}oґpOjS#.(/xn~?mGx\3.۟I,D+14Eye1K uV=dp*8F:ggˆ*B, (%,w/=5?Ezk#K&BͥU)C%CU<BN{]Imɑ@^d <߳ V'q&=W6 e'E$Q)Qd'gXݧnV`3R@He{iҙ,\3>6r>~ bUpf+C.B7 fΘƻ'B#&1abF"xV/+62vD M\0Z+&O&  D"TViuvEז!4H$~EURbqL8Nұ cR6m^cgǧ F+XI)OODÈx͕?W-!*8uz%ib_WB(JX#"_VϙKMDܶd,RZeҾ}3)9to=~PJfsdZZ~7kߥb0y~vMY8!zawB;IU"nlG$)zM>@{o!60iX<(6XN'ʑkZ6u>+}43Q^Ȑ"ӶOEyy*;|(fl kO=o"%Cz-cćD EQЎ Oa3#t%JRTTOExn!3N; tvcEXVC&$QT 07fA>d +b5Mthfی~%9Ο48e XEehI'J) ]#!4Ɔ(mgZ]+k$Vg?u_o\νk0b`{g1'79=~CL%WyR̅EjX1ֿp'<gF._y;{z>}8;/@y=31;%B2[_{r.WRZxU ;hin懌oҜk+57'g誕n!M*B$Ȓ1j=a2 m+;NO>I>t|PM6b:_}!J9 httNqq#<.;8ԣ.²z5/:;~~%qƗ.fxcKXl@FZQYU'Uji4Op[,NM٪IGr$ CBBBP.EEE]å5Da ꪂLs\TTDKk ;iKkHv"7!ħG.:e @ÛqE%(?;`4PJ(\WJ!@&V+Z@)' fWM9_+PbHoa6qCI"1G_h;("f恈wM\ xA=>P"JƉ!<%7MbNPu'j~!E2*(E$ư<>'mJ@w \L³"R@hx~|P?&|q#]iQ# <%Ijۇx1>u?#PAGG@ǩ?3٬"<31$$` MJcg3TЄTN SU]MeEln%AHy;UTuw/#Fz%AgxM<ǡ A. EWBeKO8\ݙI۰|B\,+k6GU)]4hOP!nﵜ GPD_4О6v)=&ae,]AKaW~Uyb{|4ægB V,EG4:1{ʄ^(CyBds1!Oi$(=Ӵ].,Qs~S0Mm)AXm@ژz#lB$X$յT#`հ{u e@ E=$$-v!*7PߟWm,A"A٬MG2ǰaC,/i@4OF*:B26c@hn(93C}ߛׄ@|`2^lՍ.‹4ֲeo-,?H&ҥ-5\!=۾v}|ګ?^g)al,K2Oɩ,L1F/r IzTsҙ *\P! 8Tgу8sXmxCBB.q4΍TP>q\*0 TzwN2 AEMGc~VWq œ]ӈ%g[g\`U+ %0B2P۫<Au(T!.NP+rY?ڧ0Q!vnG5P/mzHH?b51(z{}:%C6;d,7ͮC`ngTno!Tݗ =mN!EO ~b%Qbt98 ֭6g}!!!! {HHHHHHH(!!!!!!!`a 'g[\Mro{d__]->}($$$_#J3-bn?:kyue.Ho^;Ѵ(OUؓG"".1P`F,bY}UϷu8Bh+U/46XŽv)t+et߁[Mb)PAo/V0 LXDLiRPi4MѺb)aLeĢ̼nƓ#@*7 ·eW_'|ytdoͣ \?hE[B[+n{p0iWwsե#!fF|/8B \SEMx Ξ0*Q!V;dgkdD$BqQLpK\?$$$vvY~4w$ϳgEhn^B$;6sChQ&#]4?R#„ecYꦍ<`/p-TTZM'F6;G[)pG#ghI}G ->>-ƳʇiAN}x"ksC˘pX~u2l]-gvh6ޒӸZo^ ʶFg/=E?i^)H_^j;$=n]D5W~ua4oy9c37w-+C&;)tW|l(" CBBBaݤeR y ;R΍YĬ޾w|ޝwls@L+px'`r2П[{|?>߼ -y;^\ĴF:f~ 0|Q!rΌE1ؙ|1fqdGSX&o Nox.&+գ$Oq f>hV E_]˚5ǏgXԢ񬳙YCS^%))wHYXZ׳υJФ" n\u蚏2ǐS}p1< TjEӏ}i WX{GQ!=4z o纯.ಛD\(*Gu]R;CDJ)FנrV'MN RmY;\ⳅ6"C*)5!"&X @)>Zg6SY fM垸m큔H/GS'=]^3iVou8BBBBBa5+D9u] Jګ gJt]'gȸg{2 ?ϣ2jh"sdB`r8蘱8tp@ GԢ ,?KhHa}_rїab Mr}O0_g V$8{`5=xcP!t4X*/NV{ RAi\кnYQcTYFbKCTdE_LB݆ ɦ^Eho㽗^&s/Qeb7t$} MVs/y<(iD@qI)ަ- }+KnEe⵶HcF̽&/|1]쾃п2:$bW`bE5v-HҬ(Af]5uL8Oe5q( ZBC9:ϏsU?^3hL.?w,8[GcL},AwD5j _o;׾YE_>K8f>eTsF UG}G#<~S8cU?$Q;qG Vt,=b xGAOsgg'氟?/{}DqzWE?9[nǬN9Q F4[3 "JK<AP6||G2UG1$ݖFF￘^s3 fqFuIYFM UH_-ԩ <@QU0^yVC8 Ӏxm~3ޕgs(?Fdx롌u kv҄񻥔RyQ Bu%t 4 *$97@3͞A-I>}\}K,98~shI}WΑ r~}X\7P(t¶%а".@J!@y ;"{iaMQ϶qB[ Ez8( 4Vw .rO+Пu}Љ, pBpmrf$/JbHw ꆁ% A,jڅQ9B'b=運eY=iH\u[<%t{7imJO>[p]Ji@a^w`m\#ޟ}/p^x人veO]#y{w]]!С[ug$2CBN BBBUdY53? q,LrPR,/YW_(!!8CMM fϞE,aάRV{H'*r{! f3=짐>:b[zkNI{'$$ˆ+,v4u#Grwrs}`2CBnhڇ9V4$$$new[1[n+/z 0x.$$_LR__)† 1|8SLǟq0%%%agPCBBH)ٺu+z*/"s\|1 0 h4vTHH }[!!!2tMg۶̙3)'q73|phim_~TWWcYV {HHȡa\{Wq-`Xj~:/p?{HHWa;v,7&N=~~?Fȑot]tj=pmO sq'H)%7~s'KSO=%g2| UA@ %A 3Qn2 #N% Y2yDx 줸xGp/ Uio$`~]сeYTWRQ^wuamXzAGHVZ9d8֨PC@nߓJ@*j^8T)fxPzT+nkaSPN qOٳ 'Ӆ0$+;[(-/G&=Ku}+n1 n~N6^C*Ayy9\k+vvl㮤uGM{^3%wP D"Nkk Cijj"L1lh# J)40bAXeeC#ر7mk? =YWVt\ei4VE9EخDWQ^d2I t{:B)r*' {6 Ϲ#N'H('gw'yBBIaJHI"aL0UԔtfPuigR %i1'禑R~·I<W#4T*Eee%u6\*p%;s'UGv (h@CS/Pr ~Ǔn;DY{@btMng)QK'nJљui˸$":Tн_!W#-(YKr|S@ɏꛔ=a@,9ccHm`//cտ%QR VIKr+_y` ű**y@4VƕL$d2Q}=N)xh=;X e24\ Ci>|8Y'3~CW<|`D}|,C'r)*&Ofҕd|Hĉj>Kv$Ç2?sy&xrq3A%/iItʢ9'#xJ\E$;^=;u$2fkczF܎Ύ0W"we$x-+4Ԣ[~pC+̢r {Ж]Vmgg'ɶ<mm1njAsFeʂ⤇#"*Uš$IZ;'#fƸܸ[Y-\|^Gs0_=x_qׄ ܕ y>NgLq%YA-YBF3FtH#1|8_JwP\ց|b~SMmyL]PМa8Җyfq3HX:-iO}9S  ۞+<{L%JL/ 񱷬O5?! 'HQ%5Ft;BNTK˄=K.e萡Lr&~n} l&}ţ 1C΢M6a UAX|9C cZ}B !U)&5;>^|b"L'ByeesD#.s}1Ͼg5Fg]{U\\LIJd\5pԘK{ .ݾ /GecG|(s =Pq'u=/JVJU:~ىA_3g޸aH;Ch[ „G"4yh_Hm#K]TZt]'wy2Kh3ٸ1_~4$>ⓩAMM ay.'8k|l sqq1BS߾Y셣rr "zh)eۋR2SI1mj IksYß3[p(sL]:;FSyS/(llkbz8NU A)dK(q r>d`=[,JX4/Zbտ$>xTxLiHHHhR)b񂀹K4_~xy|eί oH ,lOz4ɤSW.+f @z.+L.k;8;zg%= jAEaŊW" ^! =4!$@BHo'n2<sΜdK|^f̙};s j2Zvc_ ?_C͒TϜAcOs.mNkxx:6XJ~qV~4ZFq{O./n$(Ws6g!jI [X}_̦&bu-: ЋM2!TնC-|>"Nօv8XZ""QŁU;9Gko!B类9.w^I&ql7nqhoogݤR^u]٬Ewc?L6H)bEmb1"шW9ҡ-L.=kUZkF9v݄6?k2hy94z2y:+^?%yF;Ǯwv.^Dv͛)Eq$kN^QomU!pƖ 3|$ckhMktKt[ 6wMo3_-5 6Ag3Xطq7&pMfMriZM9 !) K5{ HǮ z!BD:aDa0u[νL&6ư}.St`;ZkRTm׳cz71ZuӴK{:S^0zM#عp)e->#+6P=z(gllNIn%.61G9 R7 CSk)EBjMԲ0ЖBqbg i& HKDžץ|8}ǩPUcCs3MtoqD &޳o8g-HmV(G k?AݔS6 e"{^(7; #+rMG^zѽ{|[D۶G쐛Cݭr\ $bbVtњS~mzGim֔*زM;Y3 aݭ8Z%®ˇuy0 PYUmwmr)#\9_of_D--%w&I'0th4e#B)Q._{o%ObHgRXRhH$rm~g3glU1d2m1Q"DǥbWˌO555d2;z.4 ~R4\nSO9_&jjIGV:WK[fYXd H{8ik\0qaݷG rӾg܋цx}=fLd9Sh18eeeEYvq}+L_kL6ΐ*bànNTͰ j+H+ҵ;< IDAT="-~rgh[ED٨hW} E?7$`*r۲m&?Ůk@V!Bkҳs0lriJ)iڿ͘#νz'tiKY)u[Iv\L&Vw cDTT&]KqA] K oC!h-n/!%Ž 2ֶ#$uW ,2!B8`Y6?fw*CuM 5GhNwxCuu55]إhKJIMM;VR`HR2R KJH_5 X;V4+D!5Q\2XN_[Q4G*1%㴭!BDX"D!B=D!B*!B"DC"Db"D!B"D!B{!B"T!B"DP_9O!B""<:I8#>9!B8E!O`)*kWEz{!_^}رc/K^'ϸl&NJPF;[60i,+,O⽄yiXV(6C񖴓1$G"9!B"{cw6aaM[{J=?ʧgAh<+MMއ'IT$$9V |z! ]L _k|dطucR0DcKW'Fh=SProҕ=At?Q|_ "&>)59z9C"DA%ػ L<q0v7tgӨFcLVABfXeG0uL"dټl!˷6ad&Ƞ2Tߠyť{+<\s?d41Uv멬|a_GO?b%Ěo}[(ݹ1D}>|d&4o3(Oocړh궉s}Ap8s~<%H"T%q#m(j!t%Ft&[˴kNÙ5,>/27 P"D؏B# 堲 w)u }8c8ix/9U  &f[]ۺ[~q#˛cT$,g+$b6; 6X(n'jZlf[޼cpdSiTC83ᵅP|$@IT'(KF*`1AlkU60za+$R/C(1,-_]'JUԥ8ckS3n?!B*>4|;?b}lz}F|R D ,2QZ6<*w G/<}m_V+G]v\ChgBZTTS[7m>]&U/B޽󯻊V W6`I"*\7#85v{ lp)(ƨC8;BvK(d}m.r`d .G=lxmv52(Uɴ좩-˶Wࡧv!(UCL>ͷ]1M{BzX<.kWm`kYdKhFS&UXy;7u9TYϝ߹Wnᮟ`"D~HE FОqD+1~B Q!0ZCcsü^/&HNjV>~b0_DbٞZ;DsY˯H.UED*;~n垳k`$1T9_ s4 $=N:bGVzYj`I\FƟ8߉' +QLFcP՗Oo_ͷQ2"1ʸapN>aG?oʞ[%DnTjY#J3"Dcc^zvRyxli\\e’\ؠ\ۖR(WM'cXD)aI$W,F wh6p? 4 W{ϊ/-%-0mPEݣ\_3D<ȲmdNkelv5^ ma\y"j(қ'@kuaXF(/oG,bί+#lo-0|Z=DF4jq gA5*XO0ocQ. g&Q ]H]w[:\χ8R*w5cd ָZ#wG*:?SEsู?C{׍R~_A Z)*!BsCK7=?b DD!B~|tCLCxOJ mmi*BGxivɸɧr≃FBNg?5Nl6NHP/RЫ ,+k֠Tk8)!B:NY9  ;N\K>I #Z\pR|Yw|TC2@6eذzJ9Ą^CGgIX}\xN&ŚKX5\7ntݏqNH \֮~7Y¶pRBxq\7':r"=ǣbe3f_"_7ֱv&L,._̬sGHD i-D")Sq5n1؅ZٗdYOŦ+B9{xk{a]7eY&6c>7~n:N!-!uVdȻ}}^ 4U+1;:&7E ۲m(X"aϊUߒF,[%,ߣۄ#Tc$EB{mŧ]#c:vH'8-[/} .$v4#?&m'>D1hOajU$H˓_mgӺ M8e5#N+shOi_fYBJdMe ׄ׆8JY_j>ki_o+ =؃7;TIŏIrnMl&0O1?x ?~%e'~^(4~Jq./|*FUgx?٭YT:C╳H._1ÆsvU'i̛u[w׸z`t6ß c\gΧ&&_ƈi]k ۼ<Ie~{\Wn3"'z=d8ᒋ.:OqV?l1PqASkAbto+_{[-WBV~Xkek&j@9W7iM;jBs:0nc™8߾e [ObZVeL1(7|;1bC|t`ޘ]oAD㎫^;꽁$1eRuzZ[Z,@c?DȺ n#t˼`X2:PϿx6?7}no&f~7$q;d!e.j&%獪⥻Ë1N[\ _W^I+ɹ߾m}$1ŇO~y5|8/A \/2ⷸoå|a;>2n6]ex^dx'kEk]*ü;9KH)C:SKS&oXY˹Ղ2 lt(IN8kd_[t)7޷vedjPֳ7C{bGu3@j6{]śV[ f})'qBڅ~ 0H6hk;c$z1RΨQD,ɠUd5}B 8@[7z7:ʪZweh>|?c>-!T))+:Sܹmc'̤:fa'rRxbU3tC8*%]\ȏ*=aߚ;oE= `"8_}.ϳ,9q:ƀT侅Mˆ9ܵ,DkS$Eެ |v\[AJ*cF,]]iE"{汫UѳNE C2zSj 18𶌐D!F7$y7Wj=oX=#fHi*\(KeIԑȲaQXeUW'hY4O>-o<1.9}o%Ne$19l%F!$i4=.KFM7G٭y=ءWEn8Gf珱t[ MʫC#SFG'ѮР=ፋWP_[Y,VH}-)JuayuJcY_%c+pAdKV>8"TGMҒ`jN_-aTNnU\p|T(?s)ym6 ̼.%Aհ?\'գ! :W*s\wy?nG5c\$ӺcG4!Š?v+OՃgR\)e_`5*LX2|-DcZ{ h m|sw㈢qUpއo`o,myϔ|_*(zGJ_nv'gsN%<Ic^zvRzE<%JcDI? Lx7E]t G#.ٶvx&MQXqI9h<%e41)Ʊt@FH$lm)+#Ӟ(N{!RF+̾D" $bG-,錋6H"NDgh"87C{VHVDqZR8d$NviO)b d:E OCx7!| 9"R,;JTjRYh,;.hv3d\D'D bر8;ht&EHmDIl;)mH+0v׈b^ ` |3Sσ,`ibKNsA6ɍYْ \׽{:MlNg|ͦqsi\nɐv2E7PH9n:pS>I7C"ٶT!B8ּuEHY9(4I)U*Feb͒v 8)E(dYC]0"DM+$PDbl!BwͺD": ;yRW,WEQ <+9ġDpRBx qxi Ɲ8'lԸ,3κG6${P#8)2|kV(7NJD"qFO 0-ē3SqpCӝMcG!ӑ֔.ajSt2omZ.7->|[GCcvL';lJ[:H[o@ˏ+p-x`tG{%rC6-^KV:[5EG RaZu.;?_B?M T`97tҦ 8`ܱe= h臁]SvY`2-O?%g@$h&i33?r4.7~e" )>3+R 1vkt"r+Uk9tG\B_ڟ .紘gM%r%Ӧ= ZRH:"Yvy' J\Mys%0+Y7<&M){*R0ܔ`J68i{BJ C||dL1?+Df٠ L[^~Jm6*!ϼ]5d2 B }&&ww:bA;KA /`MQEjk5uAFEAɟ^DN{$q7 UA#Zh_# )h8ȳH X`|:mƠ];E 9(sbY7E2)s_/xu1Fq^ǕDA)s p D*?aBx}0c8}ĀM^a #9烙 3(Ѡ\UdHaȏ i3@q?F/もY:W9aY$t\y IDAT%1%^܁[Hq^)Yssss@k *SgׇyϯcgU>ꂒ$R"ЪR֧܃_WF4s]:?%` #Q c{dA{FA@7QA U08讖("ܛ^SBk#\nΘ`(l(XCBSb`JK\J| z-Nd&A()Lƚ(' S"9g`h\JHqU:.P[a rL2kmqQ*FLIL^!7Olit)UŴkH.("|Co%/Ce :MўDt[GcWgJiT@3C^7@$RYҎ $lP%sޑxM׳d; ,X"q4׎)>+:xWT9sXtچ b1uq0 wsY'갃-: {JM+>qb$ ) C%ޚ,o@4/ 5TLﱋbBx<3)`CycY(:3S02؉7M5Etcrƈ0 1GV3hѮ6(m ``dDL+p;}NX`3JaxŝOyh?TFN ;+ rWb9#R!N +;;M DBBw*$sAv7A<#\J EGf`\0K\vsa?fѾ,lXsϟ#y6?8yC+y;=y>beq~KeW3姖L@L45pR.d>kr(7=zh ǵjQdnKBp ;[уe॔žTEIh@jC٦O刯Ԍ)}0r MDF/=yO4b_}8^صA޼~2 A$fxa̯qդ8i"@ u e.8Fx 8P`D0'؊E}E4E5 WX:$&nOaTA*R#W[ [9/rZv:dS#w1hp).W!/`N\щnR4s$3xXiXW1*Ta=ǐcb/Z/; 5"rT`L'sIo i%5 I2)cYF%@ % *_ؖq\-Ҡ]xl hCX\->mˣO(qQFb^hr=l!\rڷ|-`A Tb`W(cH޸jh}T~V3Ɛ07Ei!Xp9[ΌbHRNtnJg 5y04r&Ct zԦ侣5 "^dʊ`[PFv[X5J)"6hH,Wx$W&Pc#4='}~liPSv/x[kIc-;*k"8 p4w󎋋jh*2ض;^Jz| VTxF'GҟKA) ϯmaK/l% Um{E=b=:ǫ~{ZQPN~_; 5oҚI2ApY:izI|x 5FQ,,/ddSN JS7n=S UhGYXG}=去hFtH~4H9c-6<*ѸE^ޛǠ֥kk"]0 aسE=s4w`s{WvƟ>>,&3,yb.Ϯ܍]7s?~ 嘥cw|}KŽKxUԧ4F9(вm ]Į38sT#Mgxq~VqSq0gXX}.8w ~5=D 1e-Ѻ42<{45mzT .==>^j؝+>\qlذƞݰWWٯL84NTӼg^@5-v#N'o':CmJwe ޚmے2Z撤b?Uq)c©1ZDB"ҕ[H%gBtfGSo⑇籵]0t [+8Hhځ'E1O Ò'2wu '5m^̓Ͻ.U'P޴yvӶ^ɧaēs"9SW^!XO*׼̪(>pV=ϭڍ]7.UR.;l[0u7 /fK"T<̪gxa]3 HC6-ٍ1N>Ƙ sٷW^X#38ɺ.J)R%[qu; ~߷\=qKVBv:~i;O'ӝkǻ*ON9G<!*gh\2=9-}L&6qxũgcľ6;Wo!9bƹL`||H6=6v*LΛ{vOGk- 7qJޏ-iֆop=/3sPSϼJrxFv<4#kYSBr\iqɅ2GGX#d$z]D<vν}cpOY; A|S_̴&]?ƥ3#w Ѳn^A69*ˣ<&9ckھKڸGPtO㦨 ]A:W jƻ@_Lhe-)aϛm |"#{S練s Aa0N9ZWI=ĖggMUCM6Kė9exՌ~xl#ˇfLVF/8uܳ}=ZG73lBZn"1l<3tgy}S{7~.Mg(VM'?ۘ5{&jsm6iҟ+?C#kt2]r! ,*_2q$-N9ns?p&W9[?y0% bO<|ol;6g=C&!'4eRJvl]OނN8,w 㧜Ç:;mE6Ζt0g?I{r_ *t+ YٟژMx DGde<2^[sN/1b 2h)2 huPNgrޘDhf^ƀAio2y$G!qo8+W<9+'-y폫m#.qyA{l4f_]ivm]ELSAČ`U0WRVOyf[@Zx~>Vĥ3< /lN@e%aht *Ơ XG !\z?Ek44.#{' #&9ejn/X=3}` ^USpux|[q/M"^y Tԝ7mzvnbpR!k3eh#ƅlwjp5C1m&sϕzS- OCpr6f^Xy>xk,N8s"/,yUT KٴUQӿ3x3ǫa.RF,Q]-۠n5ٰ{*k2Z\'KriLkY2Zvo Kº7|e,qqA]2/v[O q'[$F5jJ'Fmd+>~v,1cQ;RK U2v5f10JȲ:zsj/Qotg1dD+>;]6:BkD9BMmխ.t#дxc0P؟Ӻ "_#薰X!qj\UZrO& %B:8` t߽Ig`7b{=_ gSYiqHuohDn^1QeS͊c7 iPSlyn^?R.t6_'Wi*E<%j[}V~$>6tIT`+.Ir:_RV9Bc}F^EV WhЮ\rОF+qP oH¼ez4tc1Qطq+"b\t@<2?:MSJ2i$.}GR-PG k77P{(ÐͻQ6n*#?{ggU9ĝ"Ρ%ETnDQ10GPTP;sx0 {aX{Z{-vV0RCt\00N+nڷEUT*5;7bqSzpٴq#bR-R/!I$Ul/pI ^oߖ~U &vT@PX<[ٴVo1{ho?:'* >E5rEjJ=,Z ;3"UQ|{944L |h%QS&(֯ى1D֮\YUǵv IKKAi||hEuSѭOծMdp=B4T76D:(2+Fsɠ&|cկ/܉;6$-YaVJC'K+/+h(+:p CPGDd;HE.BM!΅;6FԙΝu2hXt{ZF*ddͿ.!5CǦ⡢ZsIӤS4M-^CnbͲwUW%UxUEdWHN&H/& OEJCvkD41aFi+܉`""*)i*aTW٨*+X I{I1pR ά ,%P {1UfRCɫDLD驮HL G/*S씈⻹lEEVP"j؜lvu6Ȟr &,:!, >R>-Ga)QTt#1I>pH Ɠ?--DN da0X!$'Da4drjMQYj'*&>g9x!:؀sSP$&&(B %9TO`|+5c"+܈ B@ga 11u(H;(H x!4*?\R_HA%܊nǜD +#"„ݦ$&?rDkSYXD͍* 6‚t-R]8aڸ(B +Ս`aM !:3F 8̫W/$%QZPB݋*鈌#6D*)805c ":s<\vW#L\B,&󂭨Jd$&!(@ui1NB$pVQB0V_M!aEiN3To$I51KG2 OQU kX Q8ô$ ,La5hh\ N|0ȑW5JC`DS5dwN˳&Juuz<7[aNON*]VdpqȲ>PETQcڕl %wɨߩ RxBDY7BWcPd%&k j6|{;)Wzن$ZGINI4F{ hw= H36ߊT_j k?Kf~*] _o75]`K~t;.ıV\5?LOg+)=Z)\SΖ6#G)~ V _&&E|VHYMp\ =QU;n۟bnށ 85CCnki 5oig^&`>hB%233iӹo2iFhDıT̟ IDAT(5N>u2 5w˔ `++;% ~V@$Lfܚׇj68lntzÙ좤#1y`8mWMLkW$rP4xJm3 ƀa QU2?JU4$II PYYN#88rMnϷe?鍅%UeNGJr%eDGG!uқ6 /%1.b^B4MCо U08NJ:5hߡ=eѶ]{dJRR)`w (驪I:Af* (*-v-سo?ٹhfCaZ|] kx#Jl̼r_S*f^9چQ/a wR}FK3idWggPk}C?˜NIZ44jmlm{k3kk?Awg.~‹-CL$͓&-Qwx,Z~/XȷcCOuNF'":zWS}9;PnvIދ[5N@B#Sp1ѱ=<5^Au3є?IDƸHni<+INHLb0 ,l`6 cp80Y x5)j]IS12lt&^89m1kuT;֭hMtR:k NkFaEK'nKCOrPpB 7к˯ߩ F<4D}1 ,y ^[Ah2v0VwˇFP{Uh^#Yc$[sOޚ;Gǝ^b/?*ymp"dj eH4PY^7] xcpזw3{%﷑&4A/y~/2t"}= h-49o#PrfA}ve׳ϑc=ѽt.~Wh*}7} IOE>zb֧iZΗ\Cn2.탛%1~xG(ݽ/wVzI= ĵGV[D$|6iBn{aO5o=nk+r?}`חX՛5kw?|5MUQ48= gȲLIe9ee?rQCʄbG;#s˄9u0qw\3=Qk`9 ]r-#fۨ 4 Wxf}w::os~)b;*y4o1}e~[%;w{# ϜGeMEW7SD*7YJ7 'X|ڢz Pժ[͆Xs'3G ξ=h Hk*<,:(޹WޚKUh[ٴ\-T>{od}$fëHnL4_J>8/v ?f)+-G_ђ}ü?|n-%|%st>6mWIJB[&Qubhn_!9)9o%Mo3J:3شQS,AϠ{' >URO^k+qoT38, 15Uź]y,o2H੼xv~!X[gڌ\֪)3%xmċdwX#ne~fBd x{Pr4w x B*k;_<7[w׉1=Z? ޟ*#;&v ۋl*Ic6ʸ<>֮@fV6gC~~%eF'IzUcKt%wEFa1:Myx'ss,f@e+&cwl:7 \͗/E߇+y|Ag2ʘQR&_ّ_~Jcc8eP,{-\YOa_(BɩI>מfm㸾CrǙtYs>x)t(d09̕H;!'_ݷp x{bCl}&&SV9_2E*hfgj^82g:>3% aGDzHr] t6 DA@cvcFbPi7!s:q1 nP~Ÿ9I^Brj>{ Tw2+i։{_uߓ:G!IƐҍ_KsloBLF]ISoE}bjn(cr/;5˘[YY$M?;2  磷7KhI:.!Qaw3w$ SKis^'l̥h9o]r (x;HUlo5NC0$An4w[D&sv4Ӂd$*:hBV=W3|(BXX-w?v=.{Tm [;+Џ̝Htxd"'Cwx\4puJVӷYt},u'%ӹ2<444tl`[]t-RguqGKC!}:7o[Gٺ5 Bޗ3~5 c鼖zg4g]f5?PjW hI.blOtuojd KزPJa+DQw Geq% Hqj˄;S|%*!# W>-ǏYDpjoFO^ Od|`Ì|TBG!QV r@_'Hbcsg+x@|B>U&:1rDr&V(vqU۱2vENVi}Z5pUL2t~D K%V1gJQI~2^%Eb)Cxڌ54U$@SE":?*!4dxf$` 6c">U[GPHSn˃ uNYx#HxvrIDi^Av٨Pi}V"B=:zw!J,y4K<;"^g< *ثJ0 ^lv7L[=;yoo;{tu.F P%c1M8G%\2ưcV/\ʢ4fM<zvD9 S">*hm\Ep]G6*rA5uh u$OgxzO^[X"bHY }Pu?o$mPٽilv]ύ#:KH~^Sʍop߆-Taܜ8o$H@8f̐LZ76L뱳%Qβ7),/A؏ : Ʉ[ȶ"i>Vkע1: J0Cu|z;bMùooO`YPOcc|6ZC}WbW3W'jHv3w%SՋaW:}i"p2`kMUi.Nxc~WI1Kx+3bi}MGz-ጋF)A(1qf)tC Bab"ڥaT3(OQUS_07ΈN=Xmܽ#sDB^y-9AriB*b*?% m̆7'2qy+~=ē9j$o_ueE뛭{]'J.м9QP>j^kFPŦC"#+SǶ}E%LfӡrڏIUz8"DvyxQLY)ړЕm 1s~l4}+٧qL"G̭kẫAݾ7Nc`,A jj6:^|y=xh٥)yzu{W| gb#{QVׅ7| Gǧ/~64-%k}WǨƙ y \:d6~ .]ڶʊ RԑiN5?gEj,7֝9-€a(>_Hxl W#j7gWVj( ^r"/+BlyzD`iO\+˭o.T@SwbWk3{{ (rb 3춁L%<;P͍Ѵ pxg&TIT| [(>zf}Ph0yY$'&;08 рd˅itgcW^5ť3UO֔4>m1t4罟HJ'Eӽp4O{|).kWDԚLsf>0:੬$+Q_Krp3k9?#y8F^h{@|:s+P5Pd*2^h뼛ߌSh9 ~#c}k_PrOȟϢ*Yvn@E2EK>} 횴!j>y|x2>bk|/n~p"#O1~!ٛS[#:9ʛ4n.%jLgmI1J+ιL5xg/O'q,xJN;jqx;L|s?̲?3hU~~yNG7>Vf`"}NO7~Vy{09e;,\>=.a 53-қjztjJ=`4Xz#͛^(`޾R_1.mz3 ~юW{Ep5 =hCZRHJ{o$2%GNbښ]ECV(#_}0~ˠ:6˿d洩 @+ݙc+Y5ilǧq~+^zhiK4M[{2z EQn%1ivlʑ*dLB6kTɪlFA@>m(ۼt.vHh~$Jټj#F2#ʱ[Mlnf}YЦ?M{ iѾ-ll]BQmvZkYpk:݄%k Gh mRpnE5:x //n*G֭Dh֓(1=g' 8{6H`Y4 :g+wYhǫ*-:3 IDATCal݇$Ʒ@?+B@Мٰ2k<:ܾtNvBd>ĺrRZZ)ֻ5śVP؋qzˁ5k1GJv=ڤSؽkDܝKm1ؓOm}0Mw&=6==AvЁhDdZ:Nطi=GJP@PJwԊMVaa:@fۦ=fda:}ncEx k`Ta4#`4U=ѝކﱟ[Иs_~ۍO~kxwa&)C\4t`oPh$sPkd4W%pA1 2z$Y'' 3?u*~O7HuH[gCߎna6vZ5 Ah 5<'pIع;#GRACSU|nh*fЄX,AJ%rMuu_5v"48 <"d]O ψ4 ߩۨk| wSCPRB0)?)&SuCD}jprAo=Q1fFs`, "_iR/yiI$2aXw[\4x#g*x222(2,v QeKw>f9:x/m!Hf]>N:h@BQL ?8x:u -kѻm-d:kk(":Ita4pTWqf6l؄ϫCAS9ד5 ANl\YYGlٺӉhb tRPPXgJ0XLxe90<G` WXd9~ȰChպ5EGEU5,JJGU` @\Ah Ft<^-YʆM ҫ»~lr{ P@̢8tj ^7Jέ\.  I8.&3v OMհ ϭrӭ4Fm-ԹKmcէӉ@Z:sM?p`2k$A;)Ԧ @Ȳm4VzAz=3BKz'x^|>_|0u';] 3 @ju#6uD@0[j)G ǫi Lڟ@@2N[=(I>5&OQD&KU'"xʷ3eTT-0)TxB},~yor%"8wtZ'"5+秒 /?_v?AoC{8~~6 53'{gcꞠ_Z^ [&8^v-Nd td\\3cf]T.c{MQ( GAS*)aGmkNϥ*2! ֖K@;4K'+je|ic)IuW`7 >t0`g2]H!73(OU7293d`'3_x4Y#L}$:7k|!#X$ƬK.G{{o}nAFzPte{yXY#<4 cG]k&/~ 2u3ޥL2 ^r立xc@$[bx:WLxQb:vږk3S*-w y6xT]/GGKĶxhp+qCq1|EIӴ$,znǮ݇I6.\дſ`&C2D%% Y{!"Ǿ#y8&t!VdTA}?FqRm/@پa >ٙͫӟC:w㗗e TWo<!}vvth6S @4A 4:͓K$, Js9K^0aiz#/^ 7ajVzG01F#g/ rxh!&6 pUPF4m#fEjםQ& Msl2|кm#0M0CKhp0FAH=rx&CcߎҢ bR9+sAfH^RgNco8W&jD4Db-j8C*41_YbM6ť4m#`/ I=t ܑϲJٟkB8tAw%׍\˗s-&'|҉0 ]@$D=]b<Ԍ1k2iYEHf3'jom)j˫ l<;pNtA|Sfh"qn#8\h|Vɯ;}\x{2Ǐ=tssRQqգ6pɸa C@@tDYTgPbtR Rj'&PR11J:>rӱBM#nmfIEUD YjH 2Kh$QEUUB©VdB/J5@^#Mft>à AWV;-c 5cbw+BL%'VЩUc6i r~>yI6~̬xjޏpBMJs٧&?V75' *NA e{OKfnD*`/q4_N!\~,#qthp|>ӂ] ԓB/iMLd)`v=RhEL|: S/X#uDP?t(ui;rGTslky=h*ŅDDŽ`i^/!8WUvcL]ՌIZ|;66=ٸJ6g:i=$*fD͙vtODR4$–?Gi,MÄ1Z]7`N$٪]%&. lHJ5۲h-#7iJi8DmUꚄh N 9\ GyAbr*Q:&x4DJZvMD'kHm}JF8#G+˶D4/UZǞKxAYMuN7;(C3yqmC]2'Ȳ|LW|5WdJJ79 D:ⓛi9ˊz̾.)(Ӟ`E"4'62=ۢw%tF,  ]|'nqz Uj`1 z L-HLrb,IIjR" !D'D]0ztH E,WI-^"yqHJ @ii)QQQTNPPP l ǃ( ?]W̤iIDd$iS,F¢HKCokNX6԰\~(R$Tk:m(R{ViOF(ܨjO)9o`PjuhCzwuMߜng (w^Zj[ IR`8-l6t:AAA^ ZRO&U?J@ YV:_xz'Ꙁx:Atuk?5muvp8GI4LF]M0:ZcExwU)99%=9I!= %qY.Ŋ * *mEPqQtB*)wG"H0\1@_5 3 acPyaC8+&e7lءgWUBJӅèGRs :;hĥl) iRHnbA]QF|p#̸[؟fgl W\/ɣLjl(JK*T6c'#)E =Q$X-j/RHEPw7h\U85ffԒOQoe>`4R/ǧ8±*͕l+kj##% ϧ`EZb8Ә1vbN=M,z~ }IDٱoNd t[ԉDp/?^YƒLf+T+|΅̸鯄_ OpYy9bV/g-FbgA]MSDRP[U(ͼ3}Ydؼ {>.(_4œe".7:xxkבXKKLy,m 'zE9sÒ{ǰ<&nNx'&_ ut8 34Ό͖j:iM 0U}ϼN bφuw>Uرi:e0wj#*2[5x7P*ŌұprBiT@SU#$<_VFف_5A`]…1uEoMOBfR 'P+h{b& [xrAk`ԄڭΊFbRMy<k 9߲OĪҰEџfN"Z i4>euU O0p`,_A՗1c28NOErQ|V³<@||6֮}>T4.Y1+{1I49vԯwS>Ώa쇰 8L[+22:+oWT[3vQ+*~L5r?c3wMֆr{e`F3l,i&dZoqtEW_wsp@,F#S=B/ Tmzɟ}'e"X=l.b*Y}ٓGqQ1nz)ɽheE%5htRKN)%}Bំ_MSQL9~.:ţcO2)㲡Cy $"WK93tG:TW(L۹w{FFA*!l$-;D #q}hq5=<ظLZbeCD\O)" sf.qT IDAT\1E ԖO|԰GxwSZjt(|D@zbipm71ڭvLIQc?3w'޸A\q5`b[4XFw( ȤȶӇ`Ը(7˸˷e B+/kQo?7Fk !G׸|vçK {{;'Xf5ش 16ڄǣl23b`^nJWg%:6Xxi'.{gD%GAGkRf;_>)g1a`O)؏· =ﰮy s>OV~'9xgSɊL; NG 0}p .{S{o>$X0>Ӹ)Uskݍ(&\>)L^'FN1] 凴Bq7*zg܇9|*9zG" L|cUۼ'{Ϝw1ir.wC;1mڬ gBu-vKzn.n'^} bgyD!p.uࣤ0?\4 Uҟ1uLJ'VOsBCq+n6b-58z*J]$j{'V#B>TЄpq<:j,ٿh_yiV,<|՜>)b,Y 'x*+)(-ԋNBRAޯGJ2}ji.+ }A/ebVpxPJe,/ dT-auTװs"rwn&{0_Mtl.q,^K(w+Gb^XLLi_ .EG`9>&Tհkr)B)8sB~]d,z'9PMvIߥ/t"pZ^ɣ8ӽ>NMfC8Zٹ˨/Ft%aK u]wFUU2b;i벏h@!}c2.A-25(~w.fLy2g{ 0t`PjQ*444QPd%#7.*VPKN!e\ }umeרTBV|1$]=gϠ7X])wk ݈$lT/yeߖRg%WpΈ$YRc>"Ekьx\?sjhM*֘ߦLc 0GXy_󯭘qAxIHMt|XٷXja#Go%j(rn8po3{S"{sEYYbQ)Zii>{ no Dhuz&GYU]GLl 'X\SȊ+ RS em㨬 (HnBZW8'OJN>NK6Uĕr#F6WL\TrPVY,'bb]WrHȹ眍|VrЋ<./PYD _yz% >_rQ"FJ!!\FxH(<%(H`HN&Z[[عm LTl l޲5kWnqPWfWEVD"]"9HON!.V'+k x<U~@uu6o!($^6@wH$RK$'gџq݊jSh^KRrl6QU~N_D"D}x|jΡ~~Um?ѾBi?g뺯9q%\4qS]J.U;J~ztN+>>^ iKaaau`41DhZ9NN"4o-}RS)%D""D"H$RK$D"]"H$D"H`H$D vD"H$RK$D"9 fGIENDB`sqlkit-0.9.5/doc/static/images/printing.png0000644000175000017500000010407011714210425020232 0ustar sandrosandroPNG  IHDR{rsRGBbKGD pHYs tIME [;f IDATxw\W9sWuιAe[΀ !o1f[%`f.0sg %d[ɶ[nuթ:TWux㨎 dg^:uRkk~gt:q$IB Kݯ)2NCQ$It:L]]olRX=6X/z<+`0X~=L B|>*++qo$ 2 Niihm=Eww7>(+WCtwwӟ||Sz!2>t:Mcc g.*]/eu҂hsnOOO>$wq l`0N蒞GT"K84vilBGGvn:Fez=z^r9rz\.`@דNQɤ.;Y^$ILLLߑfr9$I`0^y>o["{vAe BQ&''ٷo+VЮd2($IZ٦ؾ};EEE\{lF9rnE"޻FFPc0]jF'HvillG__@ @}}=twwH$ꪫlD"N:߯:+**hhh`0O_&֭[Gyy9%%% ZSQQ58VbPRRB6%( xߏ($ H&H&lTUVa4ٷw7oVZE&1TVTR^Q~WLo|'x'N`۹;ʡH9u:z@mm-6l JQRRB*۷FbHt)DQv;ׯ2#Xj<䓌`6ikk{t-h:J&SOӃ^GehY5^~7$ ݻIA8fI{9ك`:^ɤ97mK/D"`ddz={L$/Ze˖B~|n@={p-xം055`@eػw/Ӂi, шj#ͲuVv؁$I$ vАf"MWg.mX(++cڵ|{#<ƒ>8r (tvv/}f^Gf5„%  pp8LAAڌ`0#o￟j-z+ ˲?fٳg?g2$IԩSLNNbZ1 l:%I^n7d'Ozq:z-PpǏgn#I=#?NyH$1 XVlV+@ xxx<۷zoP(#@𞥼{w￟ =r _״NcǎEkk+G?>:EMM FICCovZ֭[G2nI3 UVV} ժ9/zzzرc^W}Ġgvvm۶iinnaL&pBnV)))kfFx<zvt:˖-P(Dyy9˖/j288Nk֬}122hc^DhkkM 'addf3罝}!"t:7GFFXt)3339rN***سg466N&PQQ^'ى(|>Z455r|LNN9Eב?of2~a vZǞd&Lb62@};h4vFHDII XIf3̀PRRBUUVp8^/D .M>癚bݺu-Q0=uz1Œw"| @SS`MMM_4"s"2΍vz"@ "! o.\(.D&)ZȦE>a*g ze'wͷ"_O?_W{Nݬm m |$#pj pa|f៯w_Ta*:w|ZN8<T;/8~W-T;{:3ykAAZv?cjV  Ixw`Mg}0֥vc(m~ }UѾo|=a\Y Ӄ|aWo-V:=PX{>_A|,9};?z; >1W&_<a_`WajoRT˷9@rbdIGN1Uzzz7)SdDQXWQ+c(ٴcy~qQTݞUe_nV.uoh@Q>_Qr3=(?Ѕ˹{\XtZQn=(ʏoULtQ~CVWр|wܙح(?c_P;Sf~KQv|Cl+UW=9M-S@V·R?@u P@>B"u/z3 k< ݰ_₍ #?D?L{uy:z=^)t~:[rWuf{]-T;PGՋg:K+NH94:]ON sxs~/AmSvmjc%Mjz_~-ת+1+Fy(Tdlq.;IRm՟~B5[]waUHޒ2C4pHGUow[ 6, /nVq2)Xaޜ5pϨNK!B1拪Ϩ&>/:Lڥ7SzyWe_p: 1Zb ;걼Xzjc Yϵ'H uVf%P>ɰM/f:ekLAujgGc^%-!QgT#^QG[שɓп^p_ugp^ORpl/:}~35x@C/~t7img|k=: ~kSB&{wRM ٔHt'=USj`/:STLyל_ΙQuTrQVl曼Q:R;[U;g ~Ϊ.EQJ|NR`R˴gߡjyz~{@63K5-S < #`m_}fevezYt{x\3֥((WzjRt̎tExڮ67roy 2 >$̻ri4/h~GT~|ר֨m߭nzlc#r]YTm^Qmy1@Y@ ojQ@gN~)z$Id2"(^ ,E!C<'˩H$ AHRL&QM$ z=EQ0L8N1u8΋f\yt:2*  )8EEE "@E D@ xmE9:'rۋ?&@DI&abb18%%%477Iy0Fł$IDQXM Io[o3WFB@ d(c߾}A2 NƇ?ajkk}tR6lO? {kR[7?ˎ;76D5kP[[拈$ڀ@_oÁ?!?7I6Ee-l6k,ˤi2 Qѿ˘f:;;qtuua6I_(0Yd2f^ynʇ>!Css3hlr9r1d0H$PI0z o`Putr94<`ݻۍ,\s5a2`׮];v%Kp]wK__XG_~Y䯻:VZ̀=Jgg'^իٻw/~z,SSS :{1z=VXj?@VS__bᗿ%Ge˖Q__'?I(;vࡇ NH^ݻ L&*AޏL9_$a0d2\s5=ƓO>륤8FsA"jW_}٬yW)fggq:z\.nd24>qt9%Kd{.}""8CCC<8V\!XnL&ٿ?LOOlx<V^ͼyhooiBc6addz=hߏh4NىjCu]G"`jj ɄҜg/;::ڵkX,A6m{>ψV@ x)))Ǐ q8t:8Gd>_|D"Ayy9033Nbbbe˖y{~TWW~lʕ+),,QPP@*ZƆK9t^I6nHKK 9X,Ɓx׸T1;;K,"^)tXHC'\Wt$Iɤȉ.^ss_(GlB@e!IcccxwT1Qrwlذ7-ߛd'tCCCTVVҡ-A@%ٵk[ne||EQ8uO?t'Np fɳ7)qݴXd ;wnGFFB!frrb/H(bddd2jMK 555,缤$LRXX,ˤR)Fb1\.iz{{v5a PPP-8;;%Q^^NUUFQ4! _0멯WbʕDQ طoF `޼yl6Ӽ꫌NI&v뙞رcfJ>;ԛ^Mtq\LLLpA$I"zjV+ZtAcc# Յ,x<:;;7o&kj@ $imm% ###,ZEQD"tuuv---,^yo hmmeʕ444M&p8LGGb1a;FSSD",TafggI&''Y|رcr9O$aŊFʪUX,LLLNEC3@pvZ[[Q^/;v젱Ӊ(8N/^bppt:jt:ݜG^VZj%HL&9y$lq, K.155FFF,YH6\.dn7;w0118˗/Ge4֒d!NcX(,,I0L!@ x#8NϟOss3 mۦ-p8ep:8q={zgvI=zG222sF<J,#`X`2fXV JEg IDATq8  'Oȑ#9rD"AAAzKb2ؽ{7r9jjjhjj"+0::n"f FnrGyl6k׮2==MWWD˗%222ݻqݔc9p&"h)IsV>;`ЎI4,lr֭[x<088ɓ'aʕ,]wNՙS1E$ѣ8qB"aZYh֭tG s^wwiNCk~yH>a,ԩS .=_PP@yy9tvvrrȲnk('N JQ[[˺up9rC D ΢FǾ}~r9Ѩv~.gNI,[ A2;d޼y8pX,n7. ĢEعs'Q-UB|GӑN&ˑJd2ZY֭[ǫʷ-/sw I>-[NIRZÇٿ?lP(ĺup8!Y 455aZ|  hhhӟvn`ppp8Laa!hll`0?A0 ,]X,hʩS0L\.TTT>ˉD"t:q:ڻydѢEaZZZ`Ŋl6X,̛7z(,,t:rU"mmm6y RY FQ|*oiZwxb".J%XJA8@&c k#dItjuqSN@pӊdZ"# $N8jtB!r)N"$d2St::ۅD"@aPFii霷:#B89ov_ WU1-p&l䜝tڌ`l6{ebb$IɤZճEQP,x{aEwN P[[.SE;6F2JX/AA@Ij#)^  6PQQq i~?;v$<;Kg֬]NDr^@0a=:tI9ps|7fz9ra '9@@[;8qEEEL&())%MOOS\\O i ^l233  f3󘙙!`x91hhh`hpŰX,rM7a6k.&'q:vz<ݻ!6+WRQ^@IvAuU#DQwtpM71=5#Gq=^V~qF=J:*O255ISSk֬:]T4^! ;˪Uعs'>,hݎN0==M__k׮pm6dYSq5o:ڶm555,\H$t:ƍlۋ xڑj)\KKKY}NJ˘7o'8wՊ򤧒ITD<θG_?eetN#DdHvlΝwI0"tRJKKq9ϤL$۶os 3lV+믾N?ICCx^w-c16oB_?7x#d}}'b)--e'OC'hB@w2V^,vbX֮[ @Iq @qI1rF*4/_Wqlkkkq nfz=p˅#bZikkcKLMMsh4РFayGf֭444`2t d4zdYnH}}=ޞzz{q\M&V+Ͽqz:-^%e*VZMQQ! ۷IyȞ^U#Ja4H$2RdEcq-[$>40'Ofa٘z4tpB'"3HDuu5W_}5hd2CV1MkZ}Q.]JGGzsUUUڶ磧p8Cez=z9f,lV-[p9d22mm߿m۶SZVJmM iN$yzi NGYi)gr.cNq89;:(xSJU3T.acZ爞N#Bd64Kss1|>VJJJ QYYyMMM|##Gh)VFMR޽BO!K,~r0}bjn0L1|Y$YFoގall1:DSSLF۰Ɍ͗l9eTPI<ZOx0YтLPXXHΛG.r8ˌ&"#;vI,X-H$X,9(( ^{-&C(//f$QWWG6LIGZ_Zf3]~(zt:Y EQd9|0%ȲL.cjjҲ#\Y!c>LFu8NV\I0d۶LNNr:q\TWWcZI 񼶝bPXqK:;1 R)$^/ d2),JfAg+W@βd=Ν; ,^٤FٳH$Bii) ?N2d|^  fq:TUUaZ<L&Ѩj?i~_dd…畹v:v@f>Y5ꪸZ~_x A3<4D6% 2dyq6ҩT$ ǜّ*t~r95jE$q n}B,N7Ǥ`4atX6f3eeed9NvJKD"xbͼTWWǒ%K(,,$pm1'Ցt:jjj(++C$Į'LbZ1TFӱb ~?& ˅hjDL&CKKiFʊ &@Nqzz|>p\w FB\ǃnG(a҂Nc޼V$tV=X-9~`ez ~`0᤮ÎhOstmnކjd2QVV,瘚tR[ShIU"mmm6(,DpP^^NCe-l4tI0L)ɐ_U[ӑL&d2t:,6O[y3Y$a޽r]wZj9Ȑh\.ǸO6L+(d9RzN5vJ(dY9(F!( T\.Njh (9@As9tZI"ɐf10$S),flWr~GNREO^%ɄW|SΊ@NTiSLqd2x^8X,K`0h\\z/!%IFAU%&$I:S_}8~ =+$iN ID^ @4USSZ455]4l ""wh`4R\l~@$P \o~T年ബ]L~ FHxƢ+"lV@ FtBR)+^D_x?71oD+{QS \TǏW8b".ny˖-C+1(tww~eeeazzzD@ \Q?NCCeeeR).]ʞ={hoogvv^n7@ zHDyy9uuu9ze}>X,)++Ce9v466b49qL@ -XVV` 044D(6mq8f )--UGFFPYYɓ'1͌!2Wޑɗo``H$dٲeF ^W[xbbB bhp8h4200@4v-388H"tRWW<;K(bvvކzV+lqFFF0 466RZzy9߅KjRPP@*" a6q\lܸ)~i֯_,E4d21==M<]I8uǎL&C0dffl6K" JŴLMMH$F¾}U(hL6i2 $ ֬YC,Q4D˗SVVF:frr1&&&HӸn9B"V>D8~8NZꪫڵE0<<XL[!9 j+b 4778|v|ttYϟGri*F0^)RlV@ xkx

>Nee%K.%28NM&Dp\,XRvލ([Iشi}}}477PPPyZ i3L&C<gƍ(³>믧m۶1::Jaa!8NbiK o**$I1<<ÇinnXZgq:444`6[y7PWWGKK NMww7X,R^t:Mii)NiBp R^^N ВjUVVRXXH,crrX,uccc(BMM l߯("'N@Q8qD"AKK k61迤+Wj۲,c٨ٌ(,\r$ID"=gGGhT{X,F*:ǤLV+nyaXp\nxxH$EQ D 5L&jjjh4x}ݧ)ɏ󾄉 fggx<̛7O[ >,˸nMX 6 ͆,˚Y*?x\u:&t3^QdYfvvVFeIl6X~ƐOr(++ y˻;Ǐsql6VeYd2aٴy1ozr3 5 ZNj#TZfD"A͂)D iaq\Z6z!F+Wlx<9ti:$ YghhHsk& ]zQPP@KK eeetwwke_/2,ϹFPɋ/8Gf<|>vw}޹\ns{ϋG.ԩSgf.tYYa{{2\"W \dI$i^/~X,Fcc#N>-)ozK&LLL0=}f]Պt\Ӊgzz5X ׋,˴pB8ORZ&qREuel6 ,pWKVՊd2aXt_b".  ());:,FrI҃h"E.y* N'T͛7( PhNz5,S\\c߾}X,- P(ċ/H  i㡸aì_~Ny3At:311j%HPWW, O>$ӹ<<}:t:ͮ]0LZعuuYUᠲa^|EM.6PDwxzgvv7j>L&C4cbq,FQ{  RPPE=bvMO$88frA8GTs|gYbDݎ `20[=oͦ]"Fv^8֮@pX,}JWK/DCCDD \HDee%G4Q&"+"v}\||u>4L FJV]IJ_NCe5+WEtRRTD6W,[/~K8?ۿKA{ȦK;?>V ^{W_Ү%w{v~au=? x!E"Ȧ`߯Ց\Ǎp'όžaphQM ?{}{Onʽjh 2XX~Nwq.~Uǂb) ܛz~P2z?/nNg}g^ \p}Q"n߻ZTݰ}? uɗìA={.xjϽLw  x% S:\ETJƢ 6> b=v.*6 ]om/|/ѪȜ.ZrA9솹wˎ_vG7б*AlhLEgAa/ u"C6wRvHNq]ϻp99Lr=xj(P|'30DZ]~/\L8}ih })ʃԫ##Q< Q$R}Uj-Qo~׈rD >f<>?|[\ IDAT~^;?}Ӗ;ޅ)vswBhH Jis+4w7ZZl? oD$lκQgA}詃Ok,bE}<|(߂N |.5wwPi-W+vU$ƩWGZ_+ݳOe}sD:˄Om_Gq{aŁ z/,M+>Sw1 [! B '}SmGuAhe Wɢry'}%q$/G+9?!!CYb ̿_T ~ T B-d1\uH\9V¥ QUV|>"scLL.s͔#>_Si}ysDK+:IW;yG?wm /׉6a&b} ## :|F=*'Et(5oԂ׳E$-Sx$#0ԏDmXb˙-h2Th>AȶBʏ`AD,>"5> y{߃CnLH$Tbawѧ~: kh!6ҝ$>`ۣvMp7nxA9ܣ],5!EsQp ,*>ɗſ>89)r:5V1~t7Z[e1׋'H}筟1S 2^q:gEe)1u+~9g :D+*+x$)xūh0qyY9C\8qG-S;O,GysD: ]wZEYI=u̺ν]Gsv)ZE߃8yYL HP:.C5ic-.ncW_X1bH.'a \(Nׇ멃WUtubLŀ{/N Ϟ%N9?*Z;ohӐ vqƝ+Kmq*1eUx&]A^ 7 ΟDJr)yF *addd ObRDrRFD)OLF >0u~s5L&%d 5و)^F@Q(PAE{=PjedsppRw|IMvR/ OVVwDp!Je=p8줨#M__ZvNƶ }9J/+%&ZN#Az !,6>[hՐ*#1/.Jl6#ooot2k֬"$ WnsYgE$J k׮!3[[n;I lڴ YguT Cls*fLM0߄A< B7ع^akbQ!1F Ȏ;X,ȃ yyy\ve'ܨP(D[[,^8EpZ̛7@@{q%|'FVV_~inL]}')^7.B!Zڹ{_+^Xî=ut[|w%/>k(J>3_%#^z"8H N[@wTc:_R$G/f[YO< +}rc |iÁE|G<2,<@*"=cS@xDٙ·ݶd9w$?6/Ê`P(\.?m'x<>bz~`g4Q tA^^]qV,Ŷ/֡dqycFd)DCCQ1d2&Ml6駟RXXHFFa$t:YnYYYP("B=p8ؼy3dffT}LZ 8كjEbcc0aF0qd2~?;v젧.h )R=p:=f'MUU477sjO,bc +xٷS4 /,=z7~vH$ |ytR}Y8pJmm-{կ~!3\ nݺ%K曹;IKKmmmٳTxx'Yh/RQQ /3<% s'H0d֭444p7}݇fcΜ9XVjjjΦ>;J?r\g͚5׋ZHݴ{NUU6m/ ڵk5k%%ezî?\T* 2 \JR >oTߐdHR*wroȠko~ʏ~|#ҜrssQ(J,YE]fϞ=n:jkk&11шjEӑT*j2i$|Urj6o0yl߾^zG̘1͛7Or뭷rJ6n܈&***0+ RWW~;)))yf^uzzzXd '!!:cv}l߾3uT'2e ]w׿x衇(++7d\s5v~ml6W^y%QQQ\={VkKYv-( OLL ˖-zjN'O= ,_3g׿5{Abbbp8۷o[Z-*o/9sz_Xr Ǎ7ވba޼y477SRRW^t;< ׳m66m555[?ŋr0ٳA"`fZuמVV.Q5VlٲDˑJX,8@__V^Ovv6oD>c;<͛7 ;CGGoxhmm 識sma}]n>s, *J*++Z߿|>ʕ+yBPPSSCoo/˖->vT*6Gy$&lقZ+ ĉ),,@b[UF/994{9z= , ;;L~?+Ww!\uUΝ;úדB`8d2博j6NյL}̶J#ԏNjdg磯NO~͛Gqq1?8<l6ҰX,<(JVX/H `׮]ﷱ 6ϔ)Sjn\.q jRXXHBXz- z+1c===( .By, tvvD*f23318o2k;?~HKGM]׍bp:X,Z-FBrrrhmmbAmm-͸\.x $ x<[JN [.^Z[[h4MCC r9L>'vZ3g?iiiqL&OkN\\%%%p8p:x^=\n喰8rp:$''c4qH$j5SXX^4p\zIIIATRUUŁbΝL&f3w&66˗s-n:T*Jш 0m4<uuu(Jh4$&&RRR„ $---lq)0f7cʔ)L>>\Nkk+F~?DGG3}tL&-?H|||O/r-tuu?"))Zj? 6'KOO\pEEE466nawrEEkKss3MMMMx:v(Bד`{'yu$3hwݻw裏b۹ 477R\B`۶m޽LZvj5j{rJJJHOOp`hI"3S"6yyyݻٸq# .faHJJBד͛5kpF [tttdx<455k.ڰnXv-l߾Al۶ ڵk9  jrss"33M6#O__]tzt:g眙̙3yWy1lٲ7bZ曙={6 Ne䥗^t}ll,mmm_p\.(fxikk [mmmlݺ\NQQ---s9t::::[VK^^hZ6mĂ 8"LJATih좸0Oee%n۷ ɄD"᭷ޢ5lY%%%cgݼ444z=G =>E[vfB@n000ɓGbATCTe &M Mf3\s5ꫬXP(ĹK~~~D`죥>~cΝۋd2gdd"YȠUV+9\b!!!Lbbb馛x饗xgP(\}\xᅸnV+qqq$%%!h&$$p饗bZY~=L&Jex p?'' ֮]KNN񤤤`6PZZj4dx<1b-ÁVwb0n8F9pW|bbbC.`R 3յl+bWjMIIsΡj7o^x@zYgw^lق^g֬YfR)QQQ$%%]tǓUz=F*2/7%B _7[SB3h4L0lhoo'77ɄT*eٲedffb2~@ZZ( -Z4jJ_?t:/Bd2:bz<f(n&3g^p|⮻bh4&MDvv6?Jń ;IJJBp 7DKK jK.<:::0 dee"(..ncʔ)466W?x{沈xrrrDדhg?S#-[FVVV[Kh4dffs s=0w\t:wuǓn':u*g} /dtkٰi/u{F &##S̙ŋIGR/{/vVɯhr]!Gjc-B>P+$0ur1㔻948\iHAd/_ܹs8q"III'}+fe ?*cG_שHXLqӖܽ Hrb,x1d8̰9Z>,V᭬$ͥRZ- 0.YB- 1 wml檫:e2OӭyT׶=d|" BT 9mõmߏt3xT߬b {{ IDATY飼 MWW߹ommfCmma}gv(kYr㟸~lf;9hn>N\mn3p|cT* y嗅oLC!dQp@\N\NTJT H (I i @BF#! GprNVd19hTo#*~ ؏~; \!MxP+FO os[A(4ʲõ̙?jhjBnULJҟ twޡ8&3ZЂ" /_;vO?;deeq.USޖOb(v\=6}VM{س1LlfS4wܲKN0JKKDz'tr׃?UDfoތWF_qW\{.-[[KnnR|G^p\ǫ>9Y<؝Ss/z:''$LG^N2yI&c<~Em}'5uTh@U(kl_:X$5xtR) Ŕ) s5%%{9hn&%K"_fDD\V<˾&J7|ɦ χkv:6ʠ`eeТ׫z ] ]l6 LTp֌7s'4q #ޣdd[/3gϛGe Ѳ|9tfH r$# DL,dbq&? lǖU4Y``)!hS*dg%R=šƊwX@cH?vh>m]'R2|ݹ—_RA,!KB0H `?>uq 9f,!?zGPb?>mmb*w$C8rxwP'55wHzhU#  aX0LDGGZuכ >$z}hh즫ۆpyp}r:zVMQOvV"驦Q3'dQn-n'&<&]ݻwsWݘ﹇+8sxAVsլ  Mܝwb߸#[ 6PUUË  --L>޺fƍ\s5%D) dlcPV%ppdǎ2::;gMz {ݾ ~?fSi;8f_K?65cK`au 455gn1fo ԄB 33H;wKի9p`?Ǐ'&&AVV6Eh::;;s\.wBaa!h9ٓ!B@}|V=;x'z##GO:? G&E__MMMZfQ?_bmm-`„ ]Alܸŋ#I5Nz| VK[[;hJBJINN^Ö-[8sH$ƢjH@P |>/#HӡT*#drcTFF*Itt4QQB^I0`0fCV؈sHeRjkk$55FC9L\&ۇDƘ*5RVvt: hF>X,h4Z<^~0*FN*`Gggg@~?x>JOKD!WݻB`64(pc ][ʔ)SXx1F4J~ ˅ZpڒASVE"<7.NnwY0L>NNj PĄunK>VT#J%-@Xjk֬AR$ ٳ)ܱcTcHgg'l6@BK.M$ᡇhxZ2o<ؼy3[,ϘARb"v.TZͅ. Tnr1UÖ-[9s |~?|,^z=ᠩ:v;L4F(,Gqq1PU^JX,֮]VrQVںZPɓPPPVjRYYIss `q )..Fs(^tv;ɓ0qK< $J%Rɓ=ط}! ZEVV6Iۻ}}\RRRƒ;v`&(AM6x/v COOj #tmwtԒR^^ɓZWpE Ym<0^f"J/{vIsSS8 SngSP]]}+Z4F#&LxsP.GhqQP`0RR>l2q hnnfWRILL $ eee|@rr2nK澅Yf>r dK̜9s0jlL>oY8sٳgPF[[FNYlM&* ZûCMM ŨTJK HzZ@}{rqqL6Tq%/brٳh4ƛԐ˹GT!Bqq QW088HRR"iiMJʙ;w.111щZ"9%`0IP]]֏?GyŒ(:Q" K9NILLo2!P1v;;\p!iHf 7;w$//̌p&N.l7쉗H$x=+fY\tBbbb ͦz|Y2e fJŇ~HNNr O~8chllV:m#jGKK L uVʐH$ىng׮]q=P(شi:ڻwo YhۺARSRjeVď; [%Gz m[?Ho83.7?o L<A->s32(.*BP088HGGV TJGGG8AA 0yTJߏd dRS`0x8YDE &&I&oj>\._K20DEt:Ym(pJRj' ʾ}{kɎB&R8NL&yyf\.jnZ4j5˖-G*{ J%$$$`29p`?VkueL&Qky<, 9DE M&ӐNSWWl}F*|pzlذO?gܰN(SSSD[ :TJFFFt\.G233JbBCPVl='?LFuu &c4mZ1>V\l"3#Ci2OxN_/*g5*++TWUh`0HJJ R HFXGt:HF0e6D{p A t PƱw^AjEFFF<  :ǃh4e8HQ6 ΦM%;+븉)7Jv;yHFX, Vk(**&&&NGuu5--ٳKA@@$Dr\/dHC!PRB!!, jRSSȤRLJZj*=n?ɺu^; ??_,ggPGOK3gs! qFR)uy¿].,_|!Fdu44鈊2GKss\.'\lA ( J4-i餦rW<̄D"V ^PPfpT* P\\D]]V-ψÁ㡶ł Rp`Zo#" (ILLDQ__OzzY_Chh@?zOj6INNr)ʵٌfcMi)1 ::: *J\1ʦ>&˗w\.ٳ1f.r z=MMM(*qGJJ ZbAoBFFikm%77zyH&փtzrsrc=$&$@__4Bfyڵ\gyӴ^_@J655%7Y0\Pok .㡷O6m"::I&Qc֯'+3 ''n8LYg < J|l6Ѹ=Ə DpH_oFc45~f34Z eeeTUW180BWΞ=ubQVT*!##֮-%6.K.8<eTʐJeő_PZZOȀ4 ٳg7VRmSziӦ300H}]YUԡh<$''"zֲh"ܬ,~+Ggg'ƍCoᠲ ECC=6.\!}ct-uj̙CJJ*^PeCJ,fϞMbb"j}RSS" b4IIMOmm-mmdg A&NN#66#LFaa!*YBד8n>LʴL&:(tvtPN0LN_<*^(((,C;w^,XA*h455ގNc"f̘VAPA[[$%%Nbb"&N^0 au223s9GNSsQ6ddffGGG'ih$n 7HԩH`TVVq7A\l,gC1V/@9h~&SNETj ..V$F" 㦭i@JIQ4X'XrѾ\ D-^L{b?鸾aR9t2HdrVGUUߓW$?@RLLJJy뭷+?V8<~o+yyGΈ`%?// >ZZmLBn;n*T}v=}{{6o"%9?6#kFwy|Lt{-+u9W1Bj֎!T"%[ $xW@6(đ\TTsJǷPXz"}HFn}"Ng"cdrlC~?OM!++C{ N7=U}Ƽpcٚrj[6ޘK9KK1|{Xt{z ahNԢEX>999bE|p>&H(7./"륄3IqU)L>.vĉ8٧)GӦ~0cIxGA?d|iR&P( ,BtW`LNE"Vwy2YĪ*vZ$H;>*I{vVׇ\*200@mm1 񢋸d^d~?իƒEll,F*)!rb!-_#_w!P2}!;lW~<%S Q y?}jM&V= (NE $… r9<0A^#KȊH$|S",_ {07.6-~@ !3@d2Juu_ى p "Ɉy0l6nӦ o$ljSҸ!Ds{-/4"8@yy9nj|EGGD&>0qw%Q0H]wQ?>nSD#"A t5ݿDn哚>f͢~6?9c 2_H52z âEA v] 9Th87dp8tI_U].* (K >Jt+g<+UAۘC?w.s⭭PƍTOTC=q"ٳQdd!kh܌ߏ0 f$7l@~H" *={g~$C( 燒w4yit kpbDz,Y^-‹]zɖlW$[ql$'^l.c$A0 T"- c"Bi\ с<ޗ<o P*z B!jkkQ [nT"5͛7io;G"IdR_LJvFý+W9td?,ϝs::gSAx!ZM$abb@ l᥎L&`џ?b099N#1<<̻ヒ pex<&I188`vrUF# KLwSq*  3'NpTjşWh4h02CPrJ)“)77۷{Q}ܸqvZ{:\.JKKIR|XV&''`ـK/J,x@]\bjhIDATO>B88uj3a6X,b1y׷~ngnnٌN/1nܸA6tbXH$yصkFz T*E<~^;fӦMR/C8&<\Vk3 3c0jTWW3;;륶J4---HQT=zD"Aee%XVlBnn<®]'sزeʯxH hnn>jqD?| y9L&D"L&aƍ@, vC Eޝ^۷o_pqt>͆bb䀳ԧiϞ=@4%`ZZ[6{eaaAT$# B__=N^8$JH9s#G0??hrى #S\\L}}=+'Ɖ'L&#аF>\xd2I:&,;ɓ,333@@AL&X[KTR+ ID"\xNAAܽu ٹs'tSWWG~~>h4T,f*555455o>Ξ=K(",swa&Y?t:y0LdY~}}%b6['Fuu5_}~֭[fd2rѡ$IܼyZ易D"W{^gjj VK<t:(CCCsrlh4O]ycXVv`mUWWc1LLLLD'+e\"e4˓>c>SѨx ccc;vy sa4 & Jhbyu+ѣGIRܺu ׋Rdffi)-- ZnA***%Jq%:::hhh@r!RVVFuu5555;v VB Nj1eʹDsqmyHz^$IvaT*7n`0H$d2QTTD]]:l6Kyy96l_xUit:ٽ{7%%%\~,[II &FGGQ(~|>i f9l6Jgy A\.mH|> P8jjjBRQPPpO{"c.;V7Xf k,oSO+vVpq,h)||e׳lr܉[%@AF۷"Q ߄B`fOT0A?R_}F)D%K8{YfN @qXu FW^uNbuF j1pKe_lLH!H34?!C?Bn7!h4BenvNYxEvuK;},TVUU1 z1mWcL/ T 48)Ub  ^AU򲴂So/ݲ-xB!Cll㕪.]Ej"1YLQ?Za׺҂4b1 F%`AeȐqyq1̈́Nw ?ngGAtޟw݀)x<cRp8 EBIa"BCLP'$t? Kkwj/}w>7GEHt+(8/NpxDLZY=Yqɚ@WfeȐ?$}7J8QYȟp[w]lXdfSJ !R00*n'B,0ƀ .t*_?|Ҧ$%m!} H0x.:G1x, - 'pdȐ'}q$%%aY%~~_Җc F?ӽSJB.+99!鼠=PyYI 9msEi>}6p=eP3,&aNj:U©6'g0^PuJ!D)ń2ܭ|$ 2Zlv!D+ R|<>’XMκYѶooobT@oEs̃ۈ2F@6LDlj@Ŧ{AԜGxE±LI۲dycq&kʐ!B^7^WEQ&aaxW(c" H9 %sx᫛QѼp 9RQQ_~SyLSf}#ŸztJEUj\\ёS3SLKouA&^dW,J)"<6.3-Vk5׺;%kZV*Vb# 2~ ҷZѣ,:өhZN8a)Et644l6)?#bMMMLLL}]~rw'}pyN[W^^ \ b(۫y_+oźLՕ3^]6X7X-}T ;1Rǟl6`eY鸤(Y([P$$$  ϛw#҇]Wk]{ک jw9Wzɟ6XP?zcMcl6גLXPũ/3ԙZ7ϲLK( y8Sna5kD$L2d;NR)u?)ʖ׵Zt:]mWȦcSgY!pq#QәϷw^ YiPRY))%MԤ/s SZqZ$E]ZJEӚfo j`DXAeȐ ҥvظyɤ4JR( K:!ct:+=yIm{cz9kvh6r.`4 zDP^$,rщ]7*̳g'U0wp;[j 2aSB8 nhVʐ!"SseY= x< p455$']w0z3nYt3c^k:NAo>폯XL J@@H$ $bh$K0cT*F ɤ} }0Qr\>$@Om8_ WN>jΝk0Ap8ҁ}[)6rn>.ln]/\ʀJZYUF"@kc'}Wb#X ` Q)ݠ.¾J!PAc8h-aA(SffO57>*C_G07=[vqy3ft[J x!_M?z< (VU( 7T(Ujuu6lw[Vy4ɝFk7l BT*T*m7ttUEa8KWUW5y.sp[Sf SkgVJK+8T oٴl ^pr >66V׷p8Qh{9^hڋ6!'I\9́@2d 2dA~S 2dҗ!C 2ː!C eȐ!CL2dȐ!C&}2dȐ! 2dȐI_ 2dȤ/C 2dҗ!C 2ː!C eȐ!C&}2dȐ! 2d#g:j+J!D)E("J!D"@ }@?0L !2IY>aAiƀ@)ȧ@  T( ~N!JAj( 4@T,2ɢT,~m P=>O'f!PYgPPÆ@HEu $MG#@!X"Q7<1Y|Kɉh> [wԷ%~CCnbADC",BCMPH{C!7uPC[dǻ/ šAb^B(BPec;[7%'" " XǼAQ鏄R"B toFNw(ٟ _#D :!}\d<TGf/+AB@( P?X|Bو=XO 4#͋! > +>C#sv F# S_1_郄 @4E"LJ4hAäFeMA?4Uw$P^%R;Ԃc2hO|bm[}MB7Nj$},`HD>hBQ nE%p&<|%TX/nG່I6=>I.Qh>0`\wPX@Vo$"ls@p@ kWT? 1hF./@g]ASP 0 Pxv4 ,jF`ǟ[=4gR*w@?#Di0@p]PH#"#\1B:6\[Cm>ri#t-AiW(#l(m2Z6)}GqH [΢v τ̷B-aFo Yk=G3B 9>wtQR-rM5DM3E,2 xBDt˴L˔HH'Lv"I_E2" d[xK3ld%4h(CTr=(5H(l @" #:49zFG#_ʚK롡NHT L H0!;(2?“CBIؤBaEKmm/blE=t.m;yNi+.#Kb- ""G(8+hiD~'$#;Ux-6rGT Ҽ[z\Hwcz:;s?^\dQȴ4mכ/)Wȥ5`ۏ _3_jiEzhFOʪD (V .h}~Q4aK[(Rd LH%q\'WPnRHLOysP},eęJ:n{\7m{䰈[~#"q>!B/@0?A-C 401R$/q iPFvadLzL##b1k+ED[(Q\2&poWZ[9e# TOyFRw_ XġM4JVX($ۢ}u'<=*402"!:E ̙fF$zjZD\'?!F]6Ѕ]oQa 9FG!Q8z6r#4;H?T,bSEvq,!"e'?p*X"(Tn;5mmck~v@i#!Dh39rLg#jK`9e6 ]`>bSQҶR8%M_PdՉ}wX$"kj~޸lr;Fa)=9 k{%;qR>90|ߏ6u7?rw96bƷ>ؗ;8wܸ U>W5yoT}dPor#fn:'p19=&L\`@lX;\ӎ"ߍnj~ ׂE1,8 "@ EzΪœݼ돿8jv9zEϿ_>c )Sbդt˪ 5?:6EEl{}y]=ӡv'<˖UgO9$ۀl^j{0(R1׼u䧏KbQMuP+( MMk8Z2hS;h_ &!5֗ԕn] &q݋UN7&[ vAE6ɤaؘYZ= PQx7*̑SoG{@?# ZAbQ 8|InfÃw LAf.3y:o9¯JM\2#6v)caLم{b ;m}r*ơ_jƱԣ{t.öSGv';Nn+Qݿ 1QEGAw PC]i{nڀ G(F]%{T71h?΢-LLaB< Vs &ke&U_H(vxv6둣Í>UNI=wzL CS525lsDpWҜdfD(YizЦ/{FO"ZQy-5/Z4Q^0Qf^wu5&9$bmT3J.pSі3l[vMU~"?p#C.u)gƙ hqォ7 NI Z(2, -&#B(Ài `hwxGTS8JC)PLDBDtrKmfavJ;+zܜ @ܡkuqd(jqN?TB !Pʘu,8Ѧ1HpEL:'>Y\SBS188XQ/Z!Xϡq/ cbcPc%z F4$U"}J~4( IDAT}x58;jDrx-/x;x,(Ippk FQJBPL**Ns9>bsM=Mj τ~X 4hhF dp>Sѫr `װBK]s8ΝEͧ[T k{]p:flѢe Jz 3sU8}ˬbZ(RM+lL쐐aЀS.ںz@91Wy(# 0g}L:;*TBz!-o &Zz5C ^]T Ð2,*uŔ`Jp?RS PdB'ip7Q._ڏ_cTaV@`ɸӞ]홢rڹoXSSuۭvRb**oJR%X`HӇՁܤ?U$5[]\1!}=ه'ze<|(Gt2ڰvaC{[Ҩ/7nHm c?sbULq-PIӒ5뷌S{k*qJ0 F~\zD{e ǘxF|65pJѤ 2R4ZsMLF>{+0 (FT:R+ R,mRڬGT}|TzK/Rǎlvۗʘze4 "sčS56_j: UGbiĞS _,XwytkDG1+)L<`*͝gL&m=zr(9a(E1gDn9zh* :?8tSI#v[ICS~8|e󰆤^z1T5Џ(yΦ]䢻53BdPSq 0ї=:t;wfU̮]2JQh t`1nҶV$6,k8M`0ߧh]TkA,Li_|l= u7l5u2{u{O4%xeVAE P v(EW\ә`N }vqΜqzh>L~roj~./o2~*@ \QOdzg7ˢF1q uWb89kH_aHN*lFYȎwUGa?zuQժ[_to2~!xW-Z>#Qx2.3cLγWb'Qx6"sk>{W6 2d\+B-;ֱcGd3ʐO%⒎ ZN4T''Ɵ8v8lFs CAVW644k&yͱ1ƨX8X2dՐJ>R+*8,[MB^n~Ni7bbbXmfkuC]emͬ9֗!C_(himuIueeC]IM:h=h<Ԕ'gW75P_RSylIyuXVQ![_ 2~M~˞aojdV)Tqq)Igu0lPjHb(K5.!+?{dȐ!WCRftjoHMN,o8 q h~>qofd7z\55Z6Qcb̉eȐ!%w#=pR*n!>8CNa4<&|yMUAneȐ!M1vdг CD^Rj5*N(.[<'G!]ԳplBdyW\x?‚/~t>{zF?}O6t2珹'ii M~ܻ>#i.׬*;?OqqDz_*ug>Hg^w𺾞ovfW|w CTL+ %M_;eM'ѩ|kJ B)8W>'Nؽ{wII b.-V׹R a]K)U^&E>׈gϼ|K:(MG\ ׎*Yc>K<>kv O[=eܹjY3\ﺉ/}[~5Wdٌ!.ﺭw+@A;E-pis0t2˸zq(矓#8 bՃⰫi?n}ؐ'|Wh~ oL$"?l Ws:|nL CWŒ兩: xf骑/ް@W۞{o7GZnpR:݋E dwqaǥP)^Ͻ߷Zݮ];u{<+ȳc?}q~|{5gg>=jNEos6 ~ rg 3J9w=U /P?STRX_ϔ֞)]~wN?nD7 ;V4'.^tgw HT|G( ?~] c'M'IL6ox~][RLJ[qNɛ?;=dquH(Ulmo@AZ )9Ғ%غܘOC :鿟]"`̣+HqKWRյ#0@ }`2* [SJ1!5u,Ϲ^KJ31&Fg4RҶ3@j7n]_tdu^../<rsNKI*nXeZ5U[lٺmjtc꿽o5_d-#,adWKsQϿy_6ՙ 2,iX%RW)?8m[UZE_Y^P;n !M7(|+gƳB_c4s?{q_)8x5rR9.[wh~.}YpI5BR%sItt:,0(T*^)q{VܬƴNEqxtNIݱ;&l?8޵(LwN$o=p_kɬ$DtW3X ێY:= FzO?vϳ/-:th#7^_К /ٛ7oj[3/PV ~E;+;$e@H>S E*lxXnyy&{ߟֶ+s&K{-]úCn.Va{޴eێoۓ.{6ʌ?juaG0(SkCH_ihI+.YJxD nrj%5f:>\SYSXXu.7rZnTo!Rw"@ G?mm n*՟ 4s 2f"ٱ4LyBR1;`1xܐ(;vܸqСC)I C.GHrH(Pz^SdL}jˡ9X+-'j=v`V@ [5MD&aNuFn#2:>|ˠLS6lo71q1"pE,k01gg_sեH[wr~s6Uw)Puy|WP]~W)x[s^Bv;BRjЛ.`hnnf&!.nU*Rx<|RRsMScyԎ8z' hmݦ#jćkGx pE`x5W(ޤ50$sQJ(˲9(n./0u֍1]b?#FgkN^0yml.%s-'*svP]gwR?m{h9hԻO߭ ْ>д)uV}O647~w_k^\ ;vTh׵SwuN Gis+~ַN9O~H=U 6ojG*FLyO/-r֭YuOv`;vw{1lK zVòvAw44д{WzV߮t7֟= Ru8;RR퐟W|bCE;9 }3[K.'(>"%?݆{x+"Mݶ`{fnb_yri~Eo҇'v3?mAo?9C*ZqO BWS(ե$U?==JR .>cև&aA~kEĒ\5݀+n:#O#RG$z>gQ=Q !Zu<7뮹̤o2L&Rt<3 k6NgRRb1G:ޣTXCZԂ. w)c3Sg(/y/5u=GxZH,:A'fu>؊+?FXQBoP(,<߷o5߬=fLQJ}~_ol4>=rHjd |vO^,<[LcGŇVy76\|ҳʚ_pkܑZYh'xYQ z(-PޱK9-fxK̷]אַ=EE"Xw,杊v=2_zɞ(Bs~\=./?ؾ!"šԙb m<11m;NL74?IV~Ʒf&c:z{k݌񊦒oFR,RܪET @0v/Cqиsn#Tzgy3z /,PAʌa7^@?`½S?=tS^mi{-cvd3H})*$X6-|Jm3ܖpau}=7b[l>~W2l.=zjL.o0N' R"txZq\m6VPq0ǹ\m{fZtsӁ97?HL^eҋ:4oUIFUW`LJ$J*K˫>}6IJg@zqI9 #/'|S /EB7/Nca),$wo9j`s9SPUEU,Ex B:cJ_z @ө=%hdsU"bleLI؄$dJBw3KYW'3dD(P;>ٱd7(D\L] i=Sb3>({uל|ә&O[kڹp# \CZv=D&pўV_xI/EQ2 ò( t)//4999--eYJqRPB0MMM 0krIE/=lA:Q|vK;ҟDQ8N}1bĒzˌۺnJ4 ~OܚTĜG:P3zcGJbGÜg61bO @1T@ JR"!cT7r]ArJLVF@0P+鎫qBhEI)ޜv0Bc]c$T il)v-DgI%s2b <hV Tj.Jo^[Ֆ5"THyʉn7h /C=LSx`jj*ƘaZzY b(zl6oC 흺f˙O{o(񢳋?^]RZQqCz+ڢD ˲Rhܸqyw{=1E1={0cI&E]SuRc6w] T25z( D_) %0L?ɊGZoygw ?Q$EHT=ݧ0 OE(.(p!34%`3@l >!b|?Rz'BH.ZF}?tO[㣽u ʵ_Js؎J"!WQH-5s4.75MfGr!>OpRJדpaB!yFEK=!yzOfG>;Ph=}#vbLԉn^g˗~|lBָT!1%-ONUOgzvz}cy!PUUfڲw7r9|qfiS \rZ1w­q>v1W\Wz@UszA) I)!hzZnhh0 cAxzN*=0m7띲`顃E (EشT`vs0Pu`?x{0;7#T,o?uh /8y (P#3P)]>)u//n2^g="Z $b>Qx*>%59AlϺ<6頼oސ;J #JoEg<p\ckt!EyR1" QF#!$7C+:vXǎ8slζ^o&(t| }˓>ɢ"2曚ӧh4voTpkyd\(Dc׏h%^;y<N'=CT*QA Hg{ N&8W2 g>8P\as}RRa[fQ,X駟~z|V(CF6Hn{^Q܂y!$=Z.CVA@ɣ?V*1g&k#gLֻs]nCϽ>+ {3ːqGmp͑t!qۑNp'> vcPJneo'~Nyy -uͼwI Wo>Sݺusq* .UƵF"j50RnG%Ɨ{ X:Cq:[(e.a8䱻b60vOG:J OɸGp͑~y@/˛g@e Vӥݢgnaqw_߿{F^W˸FUo-Uj:"CD{AaF Ov.iQPԝvTJG8b4jʚm)VM(*,"i2ˮ*mvaԜ9-Mhc ezA NI>!9ҿH񪵩^)0Sc:t.+JJJ񊴪*..n{t*Hn,W" 2>o.Fz..1%6^d(\t1évޝUSSUPWox;(%wqĀ$Cpw/-N "-)" ._~$ Wnowٙ<쎏_=žl!(sRBD_S*\Ğc1dbVã)` -:EYXXX/7Ky\ǃ NF!ZcppuzzS!8C1BLSVf-}I98C&Obb/K~xl$e$ q4=3-AwIJU\f1ESz1`P 3AVJSYfwtps0 ﰰWbD!!!EyfstqnYiyVNqTe; ю DKOƖ> $ F ,;KhO7W;v2LJI):/4n[//+=_*r{k&OΉojeSȰ៌GowB:XtZsHH$aW ^A"5jź\N![6kz=cR5$;dg{IK6w k!ZuɽSULw?ʹw "؞Q%a1.>B<3l OWs끢| L<\K|h޽ҥ{J%uRG{8k$uO@.921,4*{T+8Ч]VQns~8M6<6z㶸$(Lm4gRd~oLFu ˎ4ncTL'.=r㈽~_;3 `ؿ:ʪ!Mԅn06%FU!W[" z68uj01;IwHARϗ=瑟W$9:~C +z%mb)/$MF5-8$o=5ub_XN^{3en_#79s {qfKX쥧JJ*-A/([tzYNa|roDSG*9:eіkqAV奭~=q O+Th6:6 "ٷ͉ͯFqJP:Zhϊ; _ammp+!2%wM\̈Ȕ')7zED0Uyy'fۏgdS`ӸZeZ0zd Yz;Fv;eHc^brJ9To+;ϟ&c3K*4|:NbbK糳Y'fZ 2!ޏSϟ3'vmZ'&ƒ׆O .n]_eҙ]U?VXwDuK,+KdgϞoѢɃG) e1oLy+ >tvRth\ؑN aZsK+T }Z WȋY5/.+j|w1CW7K: v&9Gp3{wUkγ=ܐ.ṣAЈQC_ ?B VҾ3 4<.V!pʾ6cSͼ[/BFM}نv5/=t-}' 7?l⩶ktF’6d5YSk;[_, U fW9W"G4]ygӺy-ds 3~rnbUqR QHkOe 9s=w5g6y^fzˡ7o )+vzmk'tN Lw䶪mUw׏AA>~YYYY9Z=AH(i۶۵+)Id40SWX_L)0VFW5V0X(ƄQ,v!\~fqR. I9v\o*>Vw Sci%IG 잿ƶ^F uwt0w540BVt# oe[{Gu. _tQaIk&Jw:oѓZjx3ȁQr FpK@_~h6s8?֝/h7Uzm-n.>Cg?q8B\ꞽFk|zIQڤ[~=<ׯKQ~Ǝܼ[mtWO'r<8¯M2iz.v^5r|Tٕf/N bU|@U}ttkMWl``=<?[7x|UY9`@Y)y< +WD_R[$DkKTj_ͣ'3xN0aUZE{36_l< Owx EQ6¼Ќ3߭I^׮Doñr:>ymHW/[mE lO&Yesor6kӰAV#\xEBe;|Lo 0P]?g=C~M<&hިhiB0$$5mvu X,V˥o510)7r`.Q<^>Ǿ(Rs%ufeU^gV);Iȿѳ][կ{Ox1C_34غ C@{Ƹ[$Թx4[05!a;h WU u>ˢVcZ]Pլ^\\a.m9?/ qpW oٯlR0c/p,w[o@hֽnƻo4m[ڱQwpڰQh Ā00zQVSQemڶjڼ\RTn2@c2kL&$7&2.2Jw&$?*_ff}ͥ>bɗqXvxUtx=8(ɍfpEL靝g# aLf 0l4Z}QU-&lAqu2#PBxW}v5iwzm^q@>CQT!rwn޾o~ݡӗkԸvd۷[X$~g.a p pc\ˮR94'|UAq=~60ւs,|m-ؾw(,-L{xbHi҃rVo> s6|,ʺmno`?S˼IXrmܙNȭǪ]lBltG6Kx]\R\Cy= g=aΚ몷{g?)򳑃|a 8(2XfpW*3(P':ȅDA.D/N]i݌gvU=z1/&O|ġqנW&;mU(EO^l浳 uk#?do;H <Є)HxY_X=e![ͺ5oCx繟O}70%l֌Z Vu`G(O$}:01b<91= TrR_2&Mǝ|'7@fgx?w鬕3֠5:2$cp`\<~b0!j$ 6 Aƌ3(55^b-uQg@+(muG4lϛ+tIA`AaQȜ|y$Ŝ8~/ ՠ'˵7,"ѣ\`=Ne04i糥Zh E8]B6Uo8h> زbaaEF@ȵ7SH+pPPH=?_'W~"AvV._(pxj^a0E|ɠjV@ HAuP\]======gaaayDhl9/A+bi[G]I4̏~~/=[ϴjI"1EާAxph&2(#0m0//rVgb/ {#E׋䒑b]!ZVKIBNCz]bP*"_> L޵g/kʵf( o\+M~l5__ag'B! \.@PyʂR 6oĻ^0zSHCȌI(T*UGH !2[ @ibKꓣhBy4[lo0..ŷP+:yJi*s%^NloGON+ʒa]^5\M9:oЁ}G0q=<2fbI)iI(ZTUAQ'\R &6:ukG e`0}QiNCiŏ+?A|Opvv !{L8/?QE?x5=ނr(psʒNť A MI{8Ã#gddPU?4ڵkl鿚X<xx80r ٷ:?bhIWjC'0)CuEgsƤwr∡EW=nw\OOU&&=Л-Yz YeyE.&=x[x܅Z]nv+IdK՘LfV,I5͖Gc|7w/zwe]wpUMK>teϐYklϱI=R1rgr*/m>;w2dO)`9bڨw&~&s:Ӝ IDAT6ڹ[|Z*/m;WN\]5ٿKIqpޛsƌuluI :::nCCb{l$*NN48ud95&Te6 _$u;PUkU͘4[3S޴șǵP60 ߻wCٛgvn\RR3)))vvv.,#3ᯊ]a//r[9̣ʥO\Cgҗo0 3~rnbUqRPo~[5C﬚"=f҂qD֙KY&0Ah*?a1sf {o7]*;Z^e_j20  xΠ# AX& MGH<ݹdj;o"vP“DzjR/fպS\.}G:8x;.rRK׆VvkvN>&W4?l9-}Pϛ)},z rsk'Fb3]IN7 vj㸽= K;aЫ}*!йrr,i38Yɸ OۻpҐ.w盛F]~VVsR;w>asMW6]|||veOl+kϘ>+/8a߳lG; lu E)͟'7nvĒ2vѵYttttڛbb^yF?$?mCҌͦ_Uq茗\Ad=&::w@ݻitttAߞ,L֚׊]DGGG;{]mz=1g?]-{؝cޞʁԨ~F[} 8EDxy`d5jPWgw'YY;O{A!a[ qO foe~-S/+_v͞*K~SOJm_ ;bG}rRVƸ- _qUI/Go9>oѨYItr*~"qn\ՠ.;ύaë;f^:T1P<"5{m#n|AQ(JnL;,&tDVە(mNSyg*)y?њ?,U3{yBC3mN;oC=ٵ?2IP Y;?|[3yC;ko=cƔJ]&3nmhߟ{ŧl$s/s/.{Us!S̕pQ*MDtϴ7n߾}aA$@fl]9׽ۖv_t1ny]ۨ7}Qu&p,<PSSٻs<2X!@lde-o0{CYE W'^+7vױSOCG2%`F*L%h4R`tɹ+]?rT1 ߢop\q6u( ?/aah~16zTǥ,3\3FM'p ;v!YOoy_&|`c#c3k4ƃ[lzrZɩTގ˛4'smܙ # |G9R @BC옻ߙQ&ޕDͨ.C\ܼr%^@E_1ؕS9|^C+q " jRrtVHHSW³vyzl;0j=CtSQ9W āO(͌Uy'ۚ_ܯUsSHx~I5&?е]:lѬq6ѧ >HTF A@Fb @7I]浿/P M|њ1W Ċbl(iaЁ! 7L[-2-zүd6nnqu"yʜ]|0bק>?/ACʵܙiֳ, qԀȌ=ݔ{((oc!0 `C3P$-oxjº6XX1g0S4;39n.LՋ9̚܆_t3O(2w0֫+=zmXܼSlp2J a lj )jȉW:i%[F>ܽӆ`i:<*ġ]_rܢKܵum=8g<^nUj<#X @Dn~.)\no$/ Lt<`4x_DдAM:-]ONO EaJuj<.}t_p^[1SGu ՉMW3|3ÀE`8ەI$9&<(뇒0g1''oy=c yIvڅ;99|0FQkڊpyyޞWpl9 ql^X >*W~R^QֵKs;Ph_>XBƜu_Ҫ^$܀^d޽@.H%*TWfBW֜zSytyOKfV}}$~ ZSv|v4OҞ7IrUKT`$M$#~=(h%)vAA9)))<nVAD]3& (yǷD%nF ֍jO;D^Gt:.FuVb7 s3ݾู} WB]w*9UioZ;yV&X>K07&S1E}}=1ދ֌a/o}[o}G\/G,q=<kR&j0al/Λlq6~[ pq&_N:^+=vj!ۼC19Qeϫl|nzweNOP61oڑ>xHsan^8Y˿2\"-s-_[`z7U+. 5cfXXX Ҥ-sI[s tvBf/z O?{7OP/Li -0,wrGa2IHB)Jr>~N*m1{Zc_<}PD?2!r/A.NI"%Zƕp!(lL}&>}w~n$Z'OזY) (͔;y H;0dqI EaTN+H_JlMcaaayD?ɬ1Yk)BQe?[ H(%o/Whʺ|ReHJTja` ]6 hn5k^Y EeefSf&6l\>."0jLy`"I^Ǡ iB4m#\\ Zdq̠հ>.W,K"j"\(FypŪ+iFaLgaaayD_&u㘺T_̵8_6q./= $CkC0*pgK}} I P!C80Mc$$d@1ᨈ/qEV+[,,,,ZUn&ޭv;EQG!wqvD-(7}ZZMmHyyٳg#""JJ *PѶqbTj3rzev 0')SEQTn=_ E ^JOByowa:Mh4AL&Պ H=<==b1MU W @ !j%lyEW^www.ZҬn|uA us-}p+ڋlj)JW  U[-f04n-E$in@[0[AiL$ q$I#o @3z 77Gl6UU2b! VkEz8pp>v֮]{Դ(̏?ۥjƹ#zuܭ%i5tC@߹;FU|>W8>|dxxDnnntTC^A1@(:$0L*AQX(llJc/Vc.k8wLWY7gEz̤ˉ3~La^+߆.k&W|x\]yo-%r8`Uhík۶mo朘?|[6f֢u$77G^v-ϿSN酅?Q E Ð4U\Xd6R) MRVn7rˡN&z"?ضmЄk43={]L qj=î/=P81ww3:IM+LQ]<0Lv` pc\˖ܢ܆K ]^>zq xEt.WapQ;Jum[u6Ⳬ7YmxåS}cv#8r|322JJJOJ eqwq0h];:S?Nxɕ}_6l^;k/ݰ Y6CO&Ƹs~MHc/ 2ԟg\Ң-Jyjڙ]]C$j䌨w.#R:K>rv8ѽ:>IW^4ab``;w3z}&M$ "JeJz#LZmF_)Gs瘀$oڊ֓Ysxх[s3 stBX[sѲwvCM}Z߻[V/P YyYoN_&3`ǹH=hWjlKH7J-;ԬhvUvj[pij*}H]ޤU׭dq+;6<9m3w>a& r{qi{)0:~g"sGM:@ǝYƌ=fVۯF|.ș=!$ 0g( Gn/QƜ}bOi =F;VFf Ɣ lkWڌF_w;:|ӦǑjsu}_t tuu',@ )q\ҲԔ"G]]e2M׽JD[?9gޖMG;}쌽N*y@!`pp98ֱPnjC{ ;swo3\f{jxR]^o9R읙w3VTqau}uhcfˌ㗑}9~H?@ :bҏ+-_]+k2R8T:`eqUέ IRjMIIjD9; IDATxVURiUr9C(ϧixUtfPnj6SOݷw՞ ܥ s 8v[T[k: =Mn'S^ʫ(БrK=xDnVrkM͒)ŨwJ03- Cpk+,d-E-c&M%}hcۻ^C9~)^Em)}qj誀B6>߆l(ONZ94Ѻwh +>zbUr| Gb|.˅8o?  IpS j*է* {| (0$ !ȵ—G pA&{dXihq]|2x؆1ڴrCn|}9T$EϛFi Ӈ d;vũ0̙3FiٌaH$2Y:A!CMfTp1.$dv\@7^6A!/7jr͘m>~( CUZ7ϸWir򵛷-K6Nk v#\|ZfQZ`B!n2j4bW?,x;@c%,l3C $C(R*s;q!Jyҷ^i53vHu"Z9nNIDDQэRn?04XXXc3>:?h_;9,Se8CǽS`^DD^ NzՆ/õ ,5n ImR5r-osF *U9MS:)Ah@|BdlFFZ&tRy{1Hk}?=i:)U]>CoI=}rƽ8Tmp+O]YB_6M>p .<酻Poyleako,Vmaz];LFm\bM9"W ^S$GJnn)Pp9B.$bb]T9}w;ڍ߸qH~foo5uZdpppCR !Da20.G rХߟmsbym'bh7+WL`Ġj6ʆm,!ٓf?xPyyh.T*F\$q$M-6نC^oű>z} -A]*{2'E˶:y 24MuFVKӴ@ D$IA(h g1[KKK٢gaaayDٳi).8RP @TRTq8~NFd2 l>a @ `{,aTb@01am1UҮP2Øg?0T `0k[11@@2𶠊 PY-Pnp6b@?4O=yQGCYXѿxjN^\.WTŞ^vvA0$%`._$(ه9ϋYXXkѧ(ɩm2,=- AGZp8 @4#R#AP 0jť%-zLo&RT999R1$(`0?|088B؊KJJK˻wZR2M ;}}\A\^,xRx{{ €`VPTfU2 #V+[,,,,bT,ɡ,6^_\T A` )cV+QTZbTflѳg_^^r|d6EA (QAPjZ`4h~,,,,,触RE$Qi9(&8(I0 b,VYXXX;d4|{I )54m4b1c& phT*"V+gaaayDl 0K/~6׽ѯ*:Sm׮bayoD#M:ꢷ =phXK*3ׯc9bІ#H1)C+0@鹘)O>*|?q]7 ll˴P.[,,uzbp80`avvv84B2LBiRZyǶ3nXueZ(vʄv5;rA8]6;lV]\ՆG#_-LǾ; w6`龣y9zcGNfC%^W(>ޭZ"(0$$N&SGB  aPq->}Z0.Mi'J~0aC|,w}V LJ 6t搛cN3%xۍ|oXPh4wiРj)--e @QTfvhB"j5i 0 p84OrUc*uhٸcVfl* Ε>:,i3bȵӅ킂|#](9l)|$a2X AHIJoZ2PZ*+`q8 _rLm{w\Gǟݻݫp݂{c MXbM4ƚX![D"(HR뷻X@A|`fvvv>;[F3gh~zgG !X2ˋ颢"WO\]]$~~-ZwtX$T aA`0 jwپ&\0t9KŦKQ@afg"n"fr"ӟ,T-P*tqp`8<[;@|offb1I5󳰰..mڷsuwfl.0 MNo,'-9OJzQׇvy{Vz60k{Y6Ͱ0?*rn]/ݱ@^Բb+V᝸b\A & 'urX,l% !_ (V&&?DzdKe%>ghr8(fkڳVlpneC=ڣUr XՖƴQYH./+ݼqoʀio*SlBC6FMjE!L!'͢hq) U($ijj JdAv wt}OOϚV(acФs,7͐z?-n{W>1 D HtyG >۵;G\8? vz껲@јal)ng)1`(`S {%,\!+.++DQGue-}e"#\$þrvM #oF~scN78tCԆ ztQz})x< mH$9 Velax@ MNS><_ժj>_]^ԩúZ[Q)o0kw?k#Dx[נ̌sƙ?JNm(nVm0Ap6y-_; EQUZZRcbbnݺuKqw###\rbXإףc${-,,j߱ϯ.lL>-%gzKw~n{iaKH+ojKn4_aKl:],wZ.\YQ#%݌|;KhޤESөze]XMRTm1r@;KR Р6}.x.˺nҠNRT?>Bf$J/;cxI ;~tzmGl?sRi~Sw+;T*[cNP2OJ߿Ե.RiQҩ(u#VM6ŐREʵ٤^gq6 xbRr\YVʕ(Fɕ HQMl6.wnc=~h٫B̺Wq]T/܁^N=Fܸ|s~2̶G 4yjwfsnvoiKҊ|OYҳdǷs<h#1x޶N6IiVmYv=;)A7M+M(?{ةc{4嗏"7t;]5>Wzroq&w{j,ig,ݎ-kքL]cjI؝oFuزËi7m{~%O2Vp*ڮZ :?:ӝpqfTTK[*תNp j|~H 2L 3m(Ơ3sFSDޯ.'?Lz6&+EY7f%|ɦ}l*hH юyX/.h~v1\EZI|~b(:R>-8[XY/ ÷:b`'ittC`{ |=?k{[ 9bxl @qm. .ud{_sX# jc{h%?ڹWҵG kZ\?ܽ0 ʭyTUD_.2smm 4E,sss.Ө FA<.W$)U*R6sjgQ>%-2m-pSTTg#o5wǜF$~Ֆ˾; sF,kڐK[K-n.q2i۲+D7{.Nil}!D6|CeUVbvo \mb![ܹ0ZiJ4SHd0{yxǮ ו8/iTUY-|(V3oFUPXhiklakkeffFjZ&iVQRRRrm :2Qϴd?;gڒT̼.+  O`&"Ghd&~К5>[cO3X%v}fU%VNf\m/i.s=l~;1Y` .T}0b$ ٫Pk]90L^Z $:RG| v 11\r.|!M8 8lvJi[)WoXG! FzO8yDZ[9A蒾XVT}c$.fU0YIٿP(Lu…|pl).w_٧+@ 쨛3ݽˎL305L"Cfgg EYYbȌ$b1Rf\oBa.@v #x>.#/7oXu(U϶l쀥GѤ\!%ɐQ[B3݆~"yqnc|v^~xԑk ]m*Pm`?%MK ߼X`Py֊%a݌J)*/dKvwǺiMYU?ޞӀ˄";_t[AN7YZ\ǝ?q?@X60MuJԦuS}qs/]|`dI;H6VZN}~~;N|M.a3c 4eҒDD"GGG]XXXV)T AF$ŕ%?㨎 S{'/1X l?kO S8/i#aŀ5AJ1X씴̌ R.Nl}&_{/ZAP._VlI:Z`C=,͚4q6ˌF=Tr $a"teB$\.Yj®_ GǎrQb*]jǀ7Qt}J._Sb$!+,J# u:JTza\. >>>..TD$=L:iz^קٷ)}[}S0/''+#34446*$I$q| >B ÌF#3 (*>_(+PTVQ([^jehА+Y]RZz:F>֛*89eùhR'RtttjEޞ(KKKEZBY^/**"BTEDDI!uj\ T  -6>wWm~Fyu OnL4kS'ߴq'vޅ^^%$4nX0"K쌬drtzJAr]AOBC C7_&R5i<'" ll Z]Lb=ecfanP$+NLJVk5IPzbh/';'$S:2^Yo$Ø3ɩVB6.;}sIx6u/%Ҁ %m鞷lt vL\%tpTr|sD1K^@4$WklS e٥.+TN]8rxNv% g:Fe]7KL8Z4oĂ @+7[v >V6ۺ1l()̑ڰ1.E {ccdғgNڞ9wDjBQ7h:_`&ap4z8:8eee-XΙrVs,(pD dtr-M9]̞W}sk)_pݫE }kG vws#I4jm|{^J]s~ǖ*VHܲ螂+S(;)_[Oet'vXxFՂ ,؊Ux'.X.Wj^ώK^,׿u1wbO>k4yua-'orl̘39LSV-0ߺkӬ*CkUlYľ=RV7DB[j)c6Ka"x8y>))q(Oa<}Nbꄄн[ *~@5r3mQmX|p2aT貱o%7 lm)̝\rZz:*X3?PZۆ.qvOmFUd`acgKۮrmAnnjF ss 99htߵ{CskCqFȹ_2ά&+/C1-'f >(Xtf~$pMM5ע}s.#78_1y۔`4u`aAy`J^J>&@E8<ؓ+Ғ|Vcڋ3}#saO aF9F\泥8(hR/}s/Ќ[Y5IZcPy)+t'\ $f>Nw{r@ dYpsj/ٺ2)UW2Ff2}Zr^HB[}36#G HƭqbIYi,*~b9qT+wtvSf^kE@ -N?u^s؊J/_XZ] {wt|G#):B  3k{䈱3q[br^|X@3 C#=뱶[_ kGΆ?nDs=,"sk7uQ&Fӎ#}oo/7e3I `0  y`o1%2`11Oc07UDmyy❃b4DP.@@ $hР Cò3GuQTAllm: &\Q $PJm}-`[YX$^.Sgc @ygLҧڌX+nG IZХ$g|7oY!碨Ga[,/H8ΈG/60jȷ.66m:p$b+"p*w_c(<7o֬M!st6CXo}a-DbzÄۺܜ/H:w,uӬz;R=LztYs̳Z U߼&ܜc(S#Îlw_G DPxo} ä?+5 MKEsc> ;ϳC=r3Abzz[4mԖwBAƍ7l0cǷ}u dm$pkӾsڶ58rټG::w4y*QK&nK`DŽ;Wՠ(ćHɿQmآ_wcX.^Vv\Pt;Uw ~{z75:#xϟ1qZZ\}{7R`hu4VӓϿ{0yhh+)=@i*$_}2 d$pmY _D]΍eLAfA [/!%ƝG+9*maU2)yFܳ9e *tW$C|>{ǠzՐL'f豫;:ɊB̋'OD߳TIa>y^;B,,Rv\x:߈O9@xK0էpb5) ~ u}_WsR};(ԨU6v ~9. 6B:3^;Xy6tݲS0'.0f6c6ٙY\JxvuU_7q!ׯSO34;Z$*Xդq⋭Xw2 HRXm24.#י4νcSK H.0Ne4pt ˀ.sLrI:2RUЌO`\~qWh/:~ro'g&)oݵiai!5FXɯ4uʘMC6_]5k ETDR&޽ &"s×vC?ԵO eDK֞䊋0}(p2)݊pVS$`6YE-j>4d=B"xf$Jf}DRbX6^:fU#gDwMk [_XXIENDB`sqlkit-0.9.5/doc/static/images/sqledit2.png0000644000175000017500000006341711714210425020140 0ustar sandrosandroPNG  IHDRX}-9sRGBbKGD pHYs  tIME$2 IDATxwŕ?3yW]("٘dM0ws:gclLY%v*nІ36HH }`3Uje1B>|h󕎣lCO9K9a1pn~ Aa߻AAD`  AA%  AA%  KAA%  KAA q<ŷv/߬L΋|8׺nkoOY]_FGО O|"a W?Re7cO>9ˈ s? =i3z |TL*nփ/夯ȕ#{*B{<Ëu}?m+5~\q :>}o΢i}O|&jϸ]އvuXpv 6WӭV9 -`h98 +=]DhvD1SΞ˔]XPSL1Q{&agrgP"LG(mD>>Y 6Pb(fLaG=/ MVz[Tfg.;Qqqmȯ#@43(}uT۹GYց[:/;$6W宿.emiOJ}Hlck'138h'=Rnpy ?on05S>Rw5O۱8@ ^O)w!n)A9>Il'|Ħ89"Zo(xK!G|dN+!4cɯwdO'0u,XAg1g/QZn|3?Eu/ץmAs8z$(w$7~xW?[ywӘ5/k;:/O.|DZ~ZSg2^X_:ek5K`ZE8{s/:{6~u6ݾǘ "}`>X!9u1y!Ù~ |*'`] ᇟ.fk]P`sm͟WS0Pv|'b{&pNcRKk_=F_1vғR<ǽ 7qeÈtJ^…Auw}1%8o}xF;Yt4S&^~wEe1S5yKƍ;C/՝>XaO"Ij>Һ%K?cSWUІ\~8}Do% lU0\8l; l"N&eHe)5CGrQ㩉XoE8ړ9!:Ljeݻ`(cy4ۗMsR r[{G^y:sj2b"_s<7s6nr}+OQԎeOK}ۖ9sf3QSsr»gyvidg-_,ouuѬ8{J9"Z0`jSjhFɱgͤO)u ;sOsy#N2=3ƗVP 4^~'gP(&b&+nDcL` <;*h:=/] tkȟ4uS$0vL;o'뉯}Ȑi0-~۴4#ÐB?1ͪ=@bv~ja[yJVfxiVU HJppƗX)F lj8>nucpz޾MGv&BmYkXi~[+9oXyU*9EA$**8TƘ}EQT~vQEAQE)8lMݾěaQ|4N*#gqxO+ 9t ,a_ݱxBԎuqEF0Ī_1]X'Cuw}O|WwǞɿ~tc0S;>:qՔB+")A5dǵ)az% gp~O5r{_RiƐ-s]!+ۆ&}W?[d_>l^^F{w_|Gmg@ & ;HPΫ8hUۺ&OK}Yhޒ Q IB؜fJ"6CE 2\u:>`ACj8ulaѪx Sw m@|!L.=ARc98gB)`P=մ56޵Rj8Cif]$RJ})q\JJIw6wBT2]MT3Wo}6z[Ӫw&o KؗU=lJsF-~~;~8]t [S86NJe͎NEGoesOH ?~O4Ͻϲ mog4e *'q,~mlh5{>!|]3<PD\:l~=aۗ[ʣ Wi Mx'ob!C) }މcLm>n0m+4~ O>v,f4'ƈaSSfrxE /ݖ;h6ɷ_3AسHPعy|B \ρF+?΍RZ5O'%2oKM:޵S0%pe3"ETm[k 1W]uaPa}*9=[V=GLaԎ?}> ,A9qe'ܾKga =PLߛ.MOO>H >U AXs͖OSW7 p1V(kIJ0B#_טԇB#RBb'}s߫"AD` Nb!־!PBH1V g2I11yD5s(yr/_.(i[>WAA " ,,.I!RoJ1"|a4^z  Kv1ŸNcX<:$d<Ѣ &[,OШh_  K;wC!xE ( -[63u477ʢEH$lꩪDiY)UlLMM-k֭d rʩ,^u zAXž R~QR4,B]!2~jJXh -[2 x.G/~Γqnd.*sH%C{7FD8=qo' g1_ClĀV7F]7q>ӻhutwi=[s>ܫF<eq v̦^]'J[{+;vtPVZ֚rvtiFF/A%$~T8L(8$u+ G<#eJzFKO ([x'HA69OT[vMoVNf|(CvMy`FM5͌R͠2_U>E3'Y*]q<2*FÄCŅ%I$ wVbt"0eV?z\FMu-mS-XK>}E8MpBb8%% K{,q6"&Sxјx{ʌ=q#+רӴ/Xj'lg';AkGUMmoRZZx)(ZrXި5kVǨe}sÆ eڶƲBXX8CkvZ[[4h |AXžXn,p1hnz,vюˎPA_H׏~v4n<>`kO)Vo 9nS Xݢ*J Xj 4j{H'SCd}7v Xj1lGc;g;c$\Ӧj*540PQVIkk+mF ôwS\\ʕG0s.2AD` {N`K&LȢp :_]O=wЮFg(=00?3\Ql*u֊]DQa!%E ĒB"YV?%Vyϟ>SOSPZRBEWW۶my={َjv\2x0s~N>&N=fBMM-55 6l7ou5]t =}kjeA% RK[* +14~F/*x@hō%t!3uʼ#ca[aͻ˱mB#*zyD\Y:L}{u= ٴi+V|8h p8qߌz'?Eyƙ': frk?Z{G<u]\ehPn'† y?3̹"; ,awcklj{Z%;eh%c!:xc#va,1aCdr[Ox,! :HphiCcD !{yF XAYyx~ ]|3׳y&kvtu3/<Ϙ1cy,_.>|?grCAA;q48ݱjeG)gй=R"4ghPw\ر8C^\JȀaqS1U##)@aYpSVXf BM@3{}P+NumPUXofcK(끇/r\K/(b0TVUR]]MII %%% 3/<ϯ~B!fH#4@$w5pJ-5Jv>p]׾JQQeQA nN]<+]HExY_Hh/TNJ#ޣgU=hb_yw! /Qr6-ݔbÆf9PlټƦFzzzIPZZWUӗb?=']%d)RP!pgȠ ӟwߩ%"l9hR /Q2/SNxh0Εkhyxm%걱C3=-1< yߩ@K`XXitee%Tb7=q:ټi3m;9S^6ZZSmF#}g'yy5ױfjTqpO{P8woTTTXЏccRJٴsoI~kr' ۉ@xp]75AZӭ+(K7(rBq5= j#\t` ƲaKQ(hi0!^zyQ7 +'A8dYgWꢲr%2w3/2Gy$E|+Byy,A%}\#lﱱ7BIYnJdr4(tC`@ c0dVr>1PR²9Kʮ>*'_}BJ1(jb xŶ]qBe(,S SR6/& IDAT\@IQEmz@q9(}Q;.cǎes1OLyy$ ,ANśK /Rr;Kuu5Mƒ?1G%{78'O楗^bΜ9}2Y K2hnjaPl7,,*j`uAyyy_ W2sknNM,(čX]Up}(3Ѓ@8LuueeTUUqԩB! {.p3fr)PX嚫ԿAX@yy9c&6PO8n˟>i@{{L}[DD5w^!>]"  D"vaX+q5wB(.Q\0|$L}  [EN}} q)p's}qWPrܳ('A u((Gw&#&g*BsX}}!P(,J:;3}sr)'SXX("KD` '*tQiEƀ)HƜzo +8_L,㦛o '!XV8LaϦq!{^ )e.BcVF>Wa# BXŌ3XfM7===|s7C3Ce1A%{*": 11hSbf_\mܸ rW~;>}jfh4JQaQKXtָh8[l /s=믿}{a<^{$!<Ka/yǴb vmp-pb0Q^^NQQ9f̘Μgi򗿤3g+#AؓWſ}뛜qi5JկpW x9XY \b;p] =y:gu] DTnU,cK2y2G}cƌA9K9rd8 mk޸gG"\TKIaT%8m c]E7Z qIĺSc8̚qŸv#yX"#"E` <&v4Ƞ1DƠ{Ο ӳ\ޫ ߨܟ^VBj`L~ۺ??WJOɪ__U3y۟6&$c6DE(U1 $bV~@+/`mڀҮ FAczPOƝ8m&K8ѴʪD"FPBIiBUrq<ɫȫTcW ]JgQ]R0tyddŤ0DTVi!ahPq@‡F˰=zEp HfsKa Rd6)6ZMzt,!f(v}ݼBLpݔ H17Bj2!x"k⪗#0΍cڦd<%ףNA(Y6%ף3.6%M O7dyR*& ~Ψ@ФœM%I$=p*Wea_*(҂/Lh6LzɌS ֆ&9N o W-K81d$$`tP "JitH-K@%tP@ҫLCӻJz~cQp*C? hB/WcG2~3(H2 r![2 N(,9"K:&J)+,#318T1T zZE] ++U{T\)U)r\2)"ͤmr ." ZF?}@ظ:qm1v+OnkV6:9@_uϜv\o脷^eϓX7}'؊Z?o۫K^^Vmĵ֧vmz6rgvd*:A` ,/7(/ercڬGߙFsXrp}vюv;y'OgʽHtO秂*?q'q?~vbvhݴ'`(3pUDh8.:#7+#KtZc  GѮ1D2(e(<}@۠_%kga/?hAX! :˃SO` ?cIݭ rR! Cǚ&zZRƄkmL(zw W!c(RŸvlck;Vܐm=8:g[[[y֏u(䃂 "C`e{V]99XƟk3_2Y`YAwn{?㨩ԗH|/˨UN(ީ2nL+#|X?m]?c~lJPaCzX:5r9/gzBgD[N)gYN} CN\!vsXy:]ىѦ2䕏b8PEƸ@cW  e0Z{/W]T0}y+_Ƣ\mU[tt:8;uBZ=*jb-nwy[.(Y߿],^6r!%Ts0`64ngV|o[Uo?79i&_Ơ$D(_!!BW=%䄵k^1L|`-(yxNyVY5m'sO)(c|79Ѵ<~y܀~pj[W1%tdΫS@s⋥kyɥ<F7 ڪX U12ͨ ~2"ᝇgpmlf"} y6Ɵ#ۦ'_;ђ= ~tkp^8Ǟve?GPa̘I3.F=0e=Jb,A$VS7s%1 " y>|&0r߭#1LJj&x) p'NÚ4C{+XPQ?ʡ3)Q7]Fk\$  Kؼa]^Ye0"6J8BaTQ3FMROσx܌RG5U^!u!n`+ezOۯۗ~f*39vm譯 в~5uڎJwSUzJ?v(ʺU53EL6OX6f'iL&I)R*u&e0osɷ*c`96zc2ƽ)6C8|gX>I}r3d&OeO;;oBCGIB%Zwe+Ё1LJyGwfr8(k睜ݗ%L@ʞVhN"ڸ),{.b߫NUJa}NL:-*IJiSe6`gnڪ,yr <ܶn{PȦI%>utu/hI[נSդE1i_0M1Y|c IぱTYǁg;(sXQIڣ~i$n AFB|, ,N* \f %`l|܎2Y$Rܨɼ<dFe+&wM x夼=<[yf28d{pϘr5ɜ$?>p+p5*PvRuSX zm uv "{BZ,/:K4Uz[۵],e0Je8kɔ,`oUᕓe SdXUVۃ:(6_ۛr%ۘg u}(-$I)Rq`V} MƂh& Ftksǃc K8.ԁ;eyVG+6X*˝>X/U8-._3SdzRaT/kB&):B1j hd z"cħ2KW&-U8⫤J erAI![kWaHlW@`_Owd@_)?|er#**iRL}cgHe ,Sc8( 6Q*jʺQvm^EnۃL!m3:۞6fL>O^e`O=粆@`%k{J@ځr4 r\8HU\Pa 6J{Y'ٽA3^Yᑌmy}iQ<;bMmJW։qWhI06&pw,#jqCOU.gƋhf-8=c~~02ePQM~`Ul9򄵒Sd|a21`>/3kݺo#w-e9po|Q&8hBՌfDeQOl\\mLJc!8c/߯¶,_!$%DZqTK t1xp æEqWf*bش *pXͼ߽ʂ5;9,ΙZ @/>So6jC 8d]:Uv0hc08uO?Ϟh~zz X4me;=2}}gA7M\? ;8Y(pw0k6=,/B7a᠊>t ##w{le7pUCzVqGalm#d XTWUPP[Ø Lw~YOe*gωsOQ~Žo@xE|l/ <|-8)(_y.xQ.{M>N'<3.;?UollZ :bLD` Br,/Lr2FW l⦊g䢚Bt{b> |P6M _SYEl_=SjV>ˏglKN<VhN=rw5N`tm86l~=&^v1SNtx|Xλ )4e1Wym;<h: 5Oplm4_{)W~}KV^+7O]2MqJMqp+~Uq IDATwͻ2RxCŐfc5{ӹrMܹ$uX q3ف'^w/g %j{dM`~eӶ1+]rgޜqQ ccUoޫ--VTec#C/ xKkɃF.kQw,f8㔑+W)zGq:*h8DNolg*^ ͬTW19_Vsd/+fb*Q=l"l'f0{tǏ.rZV`ŌeT֎䄳gȶ%,lZƖl}Nl+6Fq0khKh]Q ;$Z7(uL6rޱO {k#=E;,ĩ xVak(7yt`r+p_WWucڼ{ǀN]u]TaMnxmlI 6`Ȭ g<{2Zzl\S&qef-RPXDA{4$'` 9RxyZ /.aX:tCC.(Fh B7ms:4y^Uk7sn2]BdQS 1#ߗIGyB*r+%oЪ*9&Oh0#cl҃3quaZ!Y⤉*6tYAZKrk͆4é.P{>VcRu16EQͩ&z7dcQQ_J8e-cSgqB;}7?^&8f_?[[ڛ=Nrq<`jkzbݝuFK x1ns6k y-=.(%~1˗{ h1n8;W Ć [@]`ɲM8[ ;1H^q%׭cF)/ĕ˸Bk٧@Y5vQJsvmRd_$h~q~we22Zwoct?"49&_Ih?=p< Lr<]O[eD93ϵ{ױr"!B׫+5e1Znoa[Q(KmĴ KchmJ?siCrf;5R'.}y)cRW>~q5ly VGY߅ˇPiY9l;QAeZ^JEJ[gȭdbM(]2w1_3}CBP"HKEUSTѫmihVZziQ[Z;U^$Hl!!r~$bi$1GfΜ3s\͚ӻ#_>D{6nԭJl~~qjT|uہ2rn oi@jr叼gnl,xBf-g dipG<՝86̜ͫuCg}ntdeuٛRt؝v2ʍsYc8ma鮓x[XsƏjͫaŢUr-Y; NmZH_[֓W *:ٳbۏ|Q.t7c8X|JQ"sP6DEH9|KAfݬrHIIXN;(e}}ndԨlI.7<;C[X{ny)=sDbEL*F#Gs7O^M^T,FҮ$eqfgϺ$洭X(cb N\rڕ<9 gEcoKz.00+к)-^96eu; '&L\Q-ho쵅?,fS`T*D=}}AIg : oXqBOs") G}g#2AT}1]sp EByS-]׍ŏJfv+,8 p-DqNql)-YsZ̤Ɯ.X|KRE33gT2B{(U|MuAC?2?u0Nldܼ (D5 d8Ln9w{u!Q-Yg]ujP!vwVy+fL῿V"0 ?|y{?HZ~~EIMxpJ.ǖ+I~ 6D`/V' J52ҪK mww\9_gEJ} W9}2/kr h{,)K>^N\Of"MCN_B5$q#pN'Cm@ۋ(`IaԦOirq-Ez\,~N `p" v9[.n%0"%T@,YWSGm/*yތŅrqi<|Yvm]K:MQ)PJ",q˂Hᨏ0 @ UX5;v_W eDT,##]E(U y}Vq~b2SZ.EKpUP,):!44T(QZ_>5wHWfʔxХrDDDtɹ HI>*`-QOJNPۻ *`ȽC"qY:f(+NNԛ'X# EDK.9U{UOnFN[8; $m&"t<ɑ9jՊvs=iN42~odjRў!u;0Պ5b(;ovՊNJ̺*8u>-i`CciE½'urI[l؍iW("K=Xǜ.V|=·oyMa*0+ !-n7{ /[ȠgX1PޜuX4z1kv?Bvbg/ѸD+㊡/Of>N~?]-FD$ K/B-ݞW_[ǚͨI)RB }V-Nv000.l -ݎaǑ}sxxuRՊc1o_*NGDrJ "胴Ur,Q0˔Z1@"kԃ%y,#s"զqV/Bt{>Ҍl؁GlVodz7k4R~bÍyu} -i/ݻMգ~^:2z ihj fasdpzNS'N5}$sl oC7î$#" X9Ûp|"!{?d=zhBUr+߱uvGV]s^sjnLׇ곰7{f͆ |V0Q/3P&8Ss .Y oԗ,ɉ#;"Go 4-_9cۤ4 [l>iXU\!BctEջkX3r5-ߘn1g` +^lG>AM2E1M/ }bҝ_M%7D{rUNrRzT % |/% ؔ}ڴױz+~GཱXw(Շ#Z>wdޥ-V+IJ?XϜyP1ď #2NK8nZtل-Щ|Fn&iШ-K*χ9)?O_el3FEա/" X_UZ)O}ȼ֦\Qi+{I bsMϯ~s vuu?O':W̠u|?eMSgܷUJK!T13z Qbɵ}J yɞPz<ՐbM&Ց;al E7~.ͽ[;J v!q2)WgfϞ X0q;U~c>g4U,Vv~a3 Z_.ūPǏNc =v1}z\:X©9r<RҲ =8ŶOftz4Oicxb,>AtT/fmæ3q/ğJ ~G380X# 9s_r%t='}ٽ^Vs}:QN%kH2q)xm?=/qǴ.]Yu!_?#&&PB\ժiޮadm802p8v7ݎ/[/˛-CA%Ͱv!LdEfCz4,F/""; JaA8z^|\6"[g,-A]EDDDk{1{?7W}>ڼ]yvVUyED$iDDDDDKDDDDKDDDDKDDD}qFDDDDDDDDDDDDDKDD䮘t(`ɽ,m 6 r{8N1[}:͈ǮʈHn|"$D4NIfuON(Bs[:3V+VkCھ8g*J XrȢ7Nl6M A4[<gr|M_Yc|^}Kbm*(`I>缸i/ՊiF.;AfNMC8u>-i`CceghЊj%y8ѾQzd؁GlVCٙ}~MVpGTbQNA2_q$m25+P.O}+**]Y8]ae|r#M-/|1]63ىFfU l{;b0x⿰|i0ſDkT6zȯx,ć5h8n!ubaQ6kpaCMxs{c! Ƭ1=\13#;x]\irHf瞳k:\,^ĵ|كv U2j}9w<ق|ҝ&eBh'3?2ey&FOPJޚN n{u$勍.|g T_۾O/u>MԥyOYį)Sl|:L,}J y)3i/}WcPuoe!Zܳe$ .{3gMH!-aǍ<BPf^geפVǥ~{|#~©7>{P2=wⲈ(`I~ff .̈́|-,N SuAv¦;7f7ɇTK3\r&^zt%*U\!R)eĜLL/=0rfd^ QbRo3hl iӃi9fW}ϕ%JPԕ3?e~Z.," XYJ5Gd_L##ep4Zfmæ3q/ğJ ~G31?-X9i~D lY7[|,i9} )i m,1E9y0oOH'x2S+qr Z{a7h|r"yKr8Nψq-H=XCf0&K?U"4|k|G1qpw ū_u1Kj9|[1u9F2wGRqt|s~0Az ?ǗJ4}a{Yد0 Ce)\bbb U! q}VNy^aYN0 l v;NGiKq*lAOP DDDDDDD & /(`iNyX%"""%""KDDDDDDDDDDDDDDDDDKDn9OXVIQ=DDKOIٜ*\AҲ?Q2*(`I<|RyQӲqc_MC+Vo0sE8uGI24kvS$?1k>Ӓ:t<o-S 61u!" XR Ug, &9â,~Û%3,K^Xu߾sצ^۳3Y݋w)8Lz__,g8z/@e iN!)F1e;G MX˄ФO$v1o˹b-Z;?E=]yfy.<7,S L 33 \WDDF =%㔹<ϗy=6\ #aq'%i7[6!; M1LNXV{LԗQf>w+T+fкvGI fψՀeb}{˔B!OO o4Z-e3;!" XTc+Ǯ^ۈQX#-+c2%?ysҮ dc2p*##*-"ISRMyN*gՁNOF/4&ٿ}iL\ ߷?`ѻ;)pg]J+S2Xt%*âH.Rq qЧb ]~>:720at/f^ޖ~wy;)""La*/َ|Jnkp(䅘BCCUB\ժGg:0 a802p8v7ݎ/[/˛Ƭh}kIDAT-z$\ڷCgv0gtN W""%rWivh=g< jЃowZ(` ~u^U""R`["""(`(`(`ȭ׭>fcW5DDDK$< n8d$~-mՊ#t\D6 Xux,?ډHӥr|(J~CJlX9*O ~./_v/zwoҔWȺEd{*G%F3x{~8ϰ*pWE^ fǣis3dش=άbyՄe{![u߭s]'.`,/ fY"Va`Zb&Kz•+qR&J)2U0Qλ Z*h|G/ϳCB-yGE.Do'ĵ|х cX0A!ٟJ KW:êeGF*݊Q#a^y6)+>jm*X&&}"y[ΩpD3evwx:&Ns!3#\*"%J=ӋK{e6Y'd&%`zVrIb)KY3qUBh߫m ;8.LiZل㦕iA,-_ }TD3a6_|L&0p1_ 0;S,=ҏI 6烚pb3| ZP'n W釘s7ۓS"K$\)$o)?,*"]VAck3ٕG`_̀%1glv.{Qìܑ3g &Lz>vq&+kןnCk)" XRX"x#ưhw<'F1I)Of4KҨcUG]hA|ѷĕ*}X0թVf< ^{V:ybr.z!R}Gܡ|x5U?U"|1GGN&ݑT\)]+!>+7H#8ُ!3[| Fb%ݟm링ʸò-iLxi&L y+00Fx&=Бw'o`H3 'H!Chh Q6pZp:h }s:02p8v7ݎ/[/˛-o@%]"DGJ%TRNpH.S_!BQe"iPDDD$KDD/(`2 4D("""y'AIENDB`sqlkit-0.9.5/doc/static/images/sqledit_setup.png0000644000175000017500000012437211714210425021274 0ustar sandrosandroPNG  IHDRX}-9sRGBbKGD pHYs  tIME/ IDATxwx?3}7Z$JfRDQZPEQDĎD, ({&)?6Y@Ww>ϳav9yww$0 ̹3;?zIAa|pRTQDpّ\n6[őeSIJU"a@pYe"q%)y̴#Ծ}$Z^-*Kpy:DЊΡcXWMV ];"%| {v -݃W딄Cfne:Ȧ?g@Vd] $_>\6T1q ?HNicmB` .#WD][1I?Xi$a] ЃXv=_IP$vzICT ĊaKKLG^ھ89CQ֗]IΞ)+v&RY%uy~ C "/ MP}`d!)KjYC `hwrH8@W +f[F^Ml#)f; wzރK[~nnQ'|@Qd P+`(APKfN+pYCH̊w0!.lಒw碪1݀٭ 프a؊d2f j-NTwGK{aD|N*eT- |w@,TтQC¨kt !I%+& V"+?JGP޻™Y4j֚ioa#1. Wf#(<'};!#5/ Gxr2@S6 ߙ#2Ƿek*D$7B1?@qB]"j =7,=WoY p":Q!kPp|3$Ng'7JnQxtLHdXvg>sMӐ"6K#xwbb"I&|LTNx. Y*[${Jн8=VtM-2_~ΣٴnR} X6Kan5 ϐhuHhef>Հݢ׵*Ǘ nT@TJfSn:NnVf"aPN}8+v;B1%V!i7}n;_.n8;KJ26.q{ҋpo S`Qy4fZFfbvDb1[8mJT_v'yG7@ iڃqi+xuan.OuzuGO+uE @Se>7"sΈ1磱pp.flkKR Z0t=0r?’~msnᙁ2ynНj:2Xk۟3ofMho1fi{5jɌUI:$bu-faFDOGi*ӾY&*&rm1ۣe?1o'| ZG59vƌ_0[Vd#~ZbL$w#&/`]au<6$I"`uoaG AR9lg 8j*9Vlٮ {5r)g uG-X?،EVT? ejb<(jSe~A_haUJ.⇭Y2~{d~G V92 |9rsIj rl}'&esvUJ:~8v:bX rpLl>TF(vgl+󷧩t]{Rr;6pǃ#i5t:=oc2s}78_' B^ \ -@D0B]V u5G/f'Y4^y qv"hS+G6]1cI$C%1~~ +Oޯ78rxmI̕[bvGo9&{ 9vsYjγXlw4phʚY$νؾn%{N|| >a]LLd yQ9g!!|Eۡ,f:M%g=3ҹ6E҂6#)npM P=:dl.v(kOØB@⭱t߷w$#n[u$ZAW7`LF9h+'|(IVLUsI>3$3Z5,I P*&s[ԂA|^;It~t]BLf vye+>|7jqDK W2 |>>1I)-(!2,#c>I< Ν ^#"Sx<:D[*f*NMNjjb`N  i IMox 6b_DssɂjI) D ncf -RlMӈq(Ԏ YvI hbn= ϗD%tQԫ^EoвU 1|ؒ1;괬,]!4Y)s#KH#l:^ĉnN Y#X7?1i ԭZ8/ryV81~0 l&-hXL{$\|MPө_N&.SXTt!+eg#oP{C")Sq'yL_@YTYF:.YHJhwP`_rśP3Xʁg%5A O@&FS+ o/8UnZ^ӑԐ'z= }:^Pe= 2Th~kY7 Ŧ\!1\1 *~L2a[ "%2@>eeYŒedYWDeͨa,$FTHs鏦بXgNlQ< (2Rqc1⟨$wd)8iҩd%kW\K7 TVrPnLl<8?]V,dm@.9q"<_Idlf Y t[J/Y՛")f&2&#?O!p|-_ۑV>?,c xfW`K-.;'$ҲTQ"l3YYghԤ%!-o!md&٫ϣ;M]0o6S=N!Oҵ j(a!v~(TMG3ݿJTjgV4᪗wܮ/P2fj6Lt\dB>8I &rv7uiUhج5߰plGښh;G"d_%fPGwHX@/8g_ fMD;ؽc+&L}_q$d d@ ,&}$$ &aT|`\xg.L@DU.KȥVX60̘-ЀvY +YC/Y^`,ISPNf3|6s=kYTl5z?f9|'e03{Ar3P5 I29,By%d!-\O(&>xEΝ:fχciz?_7; &CrϗDfw8qNơ'8#QPGtD^32 wgBugs[>l j%9΃EVLL~}4YgN9‰z_QD CddB|Y8>xcD&3[ӕ_,b7/bSzws͍c%?чiQӆ'0oxkL:ɦ֭'/_̫_jD$쓬ؕ>6tc;7O>gmL?1hHP~N$(&i[x/:m96no-))8{ܼf1â b _(0Z-e}ea(l٢8R>YP#\.f̜EdLL[[,ff̜UNttErrk\\ץ%Ivlb| -dEud2Y,v;&HDL,VLDFGǟw,JS K |SWN8I0sz6ۻGTT*V!***ܨls b 27SQ3=vkq 7RNI|R%0zxh#x<2yۼe+&(T]wCJF<5Ya'QASnVNL`kf.?q]יTjCM)XVnNzf$4 &&UU4R6n=)** ߆$&&rdYbժ5x-)єOQ7 7jN9zyTl3"^#}yv&5q 7QN?ŤдiS2N&..0/gFRAS 6#2vFrmc6ٽguItrrslL|: i˫9kV+nv~?,N'w- G^]֮['4hʋ/)jE$^xe8Q&2wm~- ̂?2mlٺN٣DcsGRIZU۶`OKU>Cyʉ'p]4h DŽRi~\ڴuڽIX~ڴAӖ|T ĉxt )1qHĔOQ4 3of3S#S17tlEIאq'/ط?ժ` aIjݷb3rhR7]IO(oX_$IXVꤦ0?q[ҴY1ǐ-v""\P:tʲ<3yƏ˖V5 GOﻙDFD0ٵm+Te(2K񎈈ݷ`ǖAeqgÚphZ̳gY~=?-Wӧ*]596ov9JVV6qpp^/Ͻ"t#yjHPwengϒXVϝ͆5+o1,qQQۿY3Ḝ2ѧO?=ԨQ]7Pd ,ǑGOo>SnBʕB$rrs8W IDAT`%8eS>)#C㸜N:/q1^;1/="^|Oc疍$''cZydؓ ~A==❉坷`GSx<w>ڵmO>믽-1,["* YVdRuZlAV9~^iӿ0 oUZpQǎ1xodd"#x_Cسc ״k˂?rh._ӎsp5q`N&Ǟx^ˎ-2>n\k̝CtT&'N/zܽSd Ŝofij- Y1LDEEzRQ/O9xo1C%26dž Gn۵6믣^;vO=iׯGNY \BTT$mbK/Ȱ'p7@,/=ya˼ݷw7ueP2G:t@!p4nD QÆA֦UsxHƒ\Orr^bU%&ի#I*Vb eG*ɘf\.qq jL%|e>S C(rQ2hP⸽]=vX\HAoH<n4oEQp:H$Idegq%֬[xEٺu;#"ك@iԭB!?KFuBʕ+{>BԪӀM"uWԵ lIiFx$$$#0ӏR7n!LlݺG9Eٺm;_zzvtrAZ4kJs,-/YSN*L!5$6Q'5ƍС $$$нg/ @;줦z]@"TUL4J״"|>?hբ~ӧKp9DFFb6X,QTXD%Z5kx1 `6ZXtUU)Yɓ<1|qq\{Mݷ0qfqnZk:B@L$.rk°owz|>n޽1]Xaa\**VdŪl6oHZ5ɓil66n|ZMש\"AQ3]1}*YgҙgZN|۬VN9adee]{sPUOۍ|l6o\鴣MӰX,O$~q>Yg9{$ӧ}.KÁiOֹSd=[/#,j׮Ūk,_ ٌAL/AK~J;Cl6lDJJ-0Y?n,23X1 ʱq 4 5kTgݪdet1LXKv-G=zn5r8tmۉ U% ǎh VJ\$Wm|'4k{Dwޟȹt=)Bc?$4Ml6iUf-[wA~na\Vaq [@W#a"+;3,]WH:ZʱcQdI J{]XHHgӖ-fG*h&rt?.//T ɧÏ :MILHj*~N'FJr2UTw1 HJ*koY^zyi#nsM7q]=# ұC{lìi~/"Lt#oNAyPgy'7~<1l6+5kTfi*UVUmYd!a\jլAٳ VZ'NEu} E ߟQϿb6ѬIc,d <&&/<̳DEGqo^tء 8rrsI(cnjǞV.t]Efr{7IOOG72>XjU|?゙AJ~5Ao |'|B  Iٯ%.?u^ >_^tk;ѭgoڵmsώ`yԨ^_UV+ڴZ- {Qbbb{=!]TPSx%>/ے,ΰGW_q1Mt؁! 3Quy;[nnwN~}u:js +7\_#ƍ2Ĵ eW|5kYg:K2M!(JxƔinAEQBoӚ,˨ӥy"jJ&),XJu)( gs)\24 UUQdxF,A%cWJ\+IR@(ηeZbQ²ɲT0uJiHiV|iݷ$%R\&TC8%0L}MKl /:(2*Oټa_({MfM Tp%>PJf1Y ٽJ]ӴR!%KaYjt5An|wKU2clJG*J4%~p_꜒ƸLtaCksKu%IB/З42etyJ#tőbt: vM.Y$ [:풆>(5-0 KjYf-E*o/~ZJ}aJ](//..o]Wm[w:@~>oΎ3@,@ QU͛vM.!DI7Ӆёs$IȒ'Gt]'v-/E":,W'Z$1?ޚD$oMnu#!8x'M[/klmeRrb`ٰl=/uQV\$1o*0ltʗO>}~&@ 8"P%&xjԮM pHVX%JfDn.5Ӱdv_0 ٧zx%`EגAV.o۶ ~0pED1"r(**b;/䞪 &6GD O9YpW_n%ڸXV &)s$#G[^& EQؿ}{^Ed21fvD@ @p ݷ/,~m߁\ǴϦP1E8v8,3s7\Ӯ-M~%K~&##<ޙ>c_m̌fYZ$ޛիUiҼY34oďgSWÞ|[ͤKcsUIp|?խ˥V7) Wl6qaNpۭ7{N{`XpڶСt:ޟ6sKVV6YYLxMF=;AgWʌP]{-ӧ~‹/)jS;obe=v|L%|M7zߜ@wr3t8~=Փ3DDs=1٧8ل,lذ1Ɠ@ \.B%iӹe bcc _n]_GfMo%Sn*ʗrJ4iܨL\OK37_c萇ODhJU頖N'}?H0dYTokbٳ><4w[5R 5o֔Z RN?À+V"+2WEQd 2pwsgcVLk-#G@ d䈧(rh׶ -7C4խːp8 Lf1B6oJ5qbtZ}OUyz `),,]@DˏD]J4j؀O>N͘ 6%3DRhDXL\,|4lFZ5iX>^L*|tq***J9y4fLwfCdl-dlפnehU+S~=ꤦ/۶\-̤xZ Mh]hU_˫OSNꤦzxva5\uU+UJZ*6 "hҸQҢEsTUe/ػw$" omRzuJBBWUUZh~GBB<ȲLp+WLtTM4زm;7uB/@5kAج6,\D뫮>P~ EGRRjGdVIpu,y);v]=HHHvZTV9sb2gn֯W}rc?_]z c.bgeYVzݧKEKJh^bbI]~;o-,6VY˃/S<&K@}<3lܸqCcݬ_EQQ`Y˸\N22NQF O¤Xa,YD6W$IX^08s&hXf-S?Ά+U&aoKmӻ:˯0sDFFRjU~>fJ=򉍉]6\l6q7ǼLp9]$&$0tC(LLb HMAt,\y?OJ"\E>F??`P?:@cH󌏪huپɦ@PC DwT)*"6TDDEED"ATAw(zIOy?lI>:?H8{=3]\9~?T3gρqt2111(BbLU*W{t$ɧc0C`LB!W^\ٲN"ECDFD`6х %5D6T fy6UJ{sB >bx,f3pind2QD<KyixYJIdwMH[u V^C B|rx^ʗ+w]9JBAZ*y^*'$1B 4^I$)$6|2 ΚuΦQ^7 Ş={1,[ˡERzI IDAT(Bx%QLi O5Ȟ?q9ڴn׶|)k&~8mQtµ5 !gUķfܻyf .hD ,ɿ R|  `وO(ǂKILFH>+bNt*9i݊X#cYl92e~ܵr,[M~Q͚4V6 Ks[Ѱ1nG;ЪU TUorEt]g-tx?nsαmtϗODG3yL2U\MZZ}}#RR F#M)Y6\Z"W%ǯ񹜼7v4ի'Qr54'~D…1_xo?t]FjTZBq%x?p+TƿqPф'J'$PAcnׁ'K!@ ӥCt}؈~=sBM<#r>$E(ٱI(CH(U EQp\agN ul|>?`NҒ`0a*Yx>䶢8rp?و'kbxdggCNZPχ HZ%'ljƒINN-nǗ}Bx;?(DƐ8h4٬'x/X,X,Ӌ9x:r:( (RR$5**32P@lш.trrxޠ. f8.n7aj( O< W^/? X mV+( NFNvbLl,LfrqO.ӊf ߇(hFJj*QQQ(Bzz:1dgex0L#">X)$zJطGSp1}N8IFBߠO?L19sMre޻ P,.NiH%!OJVLn_BA2e㉨^vJ/^w1uVnS)$X-Ο% uD"H~ӗh:BȰwr`@4p,DGdP-H$R`I$/f39Vrv>NѦ:gˇ:}:@Qx)=Uh,f3>fl.H$XR09f),x H&Ahdʤ -RlaYV"#"q 5M#:&:)33.&33 ~}(VȈHҍքOE9DFF7 T)Pp?h&z^ ^p`XףZ,gL&c#{qr*H$)$2~?r)S'OEuF#׮#V]J3}O2)ǓO?lDEEQlEpp~_&dhԔ~އ(ڽ` e4ٸiՒa'+;m۷ӯsx<vŋiݲ9fPTYѐ~ؕN1m4J@՚uX~=F!dc'ӨZ6e*Tbu SRkdUdah94d(ABDf!oE\2~g#N:i ^:JNع|%D"_BfV ʻcFr~F#B:cOnpa\^w/dIKKđs!,( 6m?Pz7999yG@B`r4NF#W\%33ڵj( C5)Y c{&Lb޷3Yj9?^`pa֕^ ?Į[iԠ={2hd\K5]]ri6oX3)_#r2/;to}7)APD"K/l2TaGҮC'CUUN%'sU<4rSLiVXUU9{ʕ+NNhχq{<%3+ EU޽?xw,3NmVx^JG{bٰXdffbdn3S}ƩSHȐL"HnMobaݪ?q_ͤK8|Պ`$99xΜ=K)JpߑrJ|D :~Դ`>;%JPb"޻QOESgf`4pj3gR8u4F1\ xooeTPKdlSUQDP S 9b0hD"H%t=f,qqHIMn:(BR*t{3={Ν:r>?Xf VcQv-tȻbCyͷٳ6UOz<3w|zzo0d~W_য়qם|>x}0}/ ZRUVYAm ISHf-]B*rxx1J(d"%54ʕ- \n ,@bbbѣ)\9ʕdU 5%%Դ+hbSUɿ*G"-X@Q!&&, %K 222,Ȅ+[&|_eÿ.\… %66l6KN>C&X,hk`(_l8DL Xq411Mq]]56&ؘt]G4+^TTenSlpH$X/r֛ԩS릦+Qhތ<~ p:ҧD"H%x<<ɰ(Y^q:4Ng5D/Gi#+~ yG[>BZ҃oKblڲhn*K-+ԭ] MjIXEK( ii4kҘqE@Ç~ ZlNlL ^u6p9*/GuBuv=N4i܈%KѦu+ .\H"g55ks˗Q_S\9> \GUQnCq8"1LZRظy3-FȊqYnu+Eiw=~N8ID ٽg iFXVN'߭\MFf6$>8o?gϝ#:*@%e޻eu%KsJNiƨʌgR">CSt+Wb8NZ6oFB0 ?pmۿ'66{۶A89:㫙t@_D"H 䟊f1l23j;37(_ JV^y{~9+"##xޔ(Q%YX#4UL6>ڵjgO3v|glڲՒxcp.[t>Qc&9 3I*X,3MGZ5ٲu^?ĊYǏ$-=Kst2./fjaEQ ֭ot1jXbccQ5۶soj׬=f,H>:>H9zCEf|łQ:ojo2y{;;dffITZgޟPUN ''re2dffre ⨖TIԬQ#GưD(@=mԭSyx2<|~޷53s70 łnaD",?Ӆbef,]xԮU|45`i N*$U ry#}ԪY 0|8avnCS5|5f2֨^E;vN ~RRR)^,Ϧi(D4< (BDE96:|%˖gXtFE'{GOCi_~CzFF-[0 ,uiidffhФcǽfQ oG QL6ƽ[7d21zxkZB4D0}EUbziذAع鎝?A(u;qG1͸\.Ǝ^.s;{&gϝBxGt֕ ٧q:y;v;﬏bf0 ,;zxqEy䑇h֢ HjJ*4R}D"䟍@j*NzB DDD5awU l99n)oŌ'Ӄj8~ 99.lV.14 ߏiBҥK;XV?@Y+VpQƏ{5:7ldi ~j֬yYh1om9u4*Vęp #2.nk=Xaf-YJtTvpY,f<"tbx>v܍vѡ}; ~/cv?,>K9sGeMו{TTRp6+$ ]:3lH4Mߋ˰!t]bp!ҙ,~Ւpgg3lRh֢ 2yytf /5mњ#ǎѮ=2s74kن.bpB~HL[?Ւse10`kwgZjNxGjI[DHSr%|~?4{.t}?(StM&H=`ϒ_YӴK S&ܥ!3@[Fx<t]fjr-XWۿAETLۇ'}UUA.Q%tuTUt*&EUL&'L&#^]1xp}fݞqjPYC*ل54rrX,f\.w8ЉeL ]UnOH d!{躎h ,:'O&EQ( o?-K=|e -|} BP?}^E{=X7EA"dB|DZB:].TUv'UE l:59ЅΝϗF^ ?ޗ y]v5okN+_nny(wn!W4)87*yb[= dϗ|ں8oY}ڱ{^"HHD7|$<O8`މz¹dffɉY"HH$'?s^-ŕD"ˑ%KYʋsz1)?G_ s+my<.]_K}k}r.HHLl6*'tيN9px괰?(UJW^ oξ Ӯ{=/׮B$,-ψGTUgϮnSr.BUU*T?_"H%`1 XVV+a6lX,EjbXA{UUn( Պj =V͊`na4?g)a 9+(hft[nm6la.pqDFbXB`ұX,z筧!l3p=MӰh-O{X|mYeX,fTU '2i԰>҃;t*>(K(?!`[?$j;;ヌ=*&܇19s,jt5>Z91WLpC"0G_bj`6cfmP.{K$R`I$*ilF~&[P"'̓Ͽ@jhشo`0x2*VN:?Xb j+%ѹeKQv=Մ^bd]ԅ{<`W^%V]Ԩop`9Pe2q4nފ x={M[v;QS6mނA7lBu̘9 uR6jգCݪ5X,~Gvä `Z$B>c&L焟»w6TDVYd"5-fԬ{3gFANNxjӤykFy Tħ}{^0.X džEK美TZg{CLl,Y>O?ۗrGFUUlFuUj'F)\$^Lj5inFGP|e!F]1r4I5T._/x"R"H%i& YM1d[b?vnѣYd)Gٳlݶo)\S~پ}3;uDQ=ƫ0j[lٰڵkb2VLh<@O}N-l mAjI8N>%;m6ѡ]EvZڿ^ IDATDEE[osXl9.\$--^z?ǖkiۦe^Etf &3?_d\Oe.>M5 Z _'1߰n+Ȣy߲=<й#yƼ 9|0W`9$,1@ܶ/>FX, C|ƔOgٸi3w5iNbŊ<KPRel\KG‹/sy6[͈aCk\t))fӦLbl Mh233=<ڳsE,f3 J>׭b PmvKgw1r h&[,3sU$&V`m""#|Tbcbx?֮Z[q,[S%XI;﨏 /$UBDD` tҶMk*T(ϊ+a<`wEsz)-KPt233iެ)4m8Z.9svȃݻ!`;X+l}x^+wVqSty rN8Σ`FSPЃy?ԥKiެ)Nzu\J.uqX1ԩ|~tHɒ%=߶UKiؠ.3gQy RQpYPiۚHRjzi\J\4oՊ>|̜-cFgmyviFaǟ?H"&ԮEzz:NؘZ4oF(;ս|>qu' t:iڤ1ٴje P11، FT#e^pVO(W)l=ǎ?c/V Pz ^/(G8X,lؔ4}K D"Drkrx}Ы,^'LrT( ~uĠ/3lHN'QQ"""Xn=v 7b0hץ+`μ( ۶o'HQ%Ud22ugQv"##4aC_= : ?bi,;KNSXd6q[Y~ED2,YS8|PD<1"{UllڼB8F#O=݇'׷O#aml߼hz=J:vuVpiFJq:ul3iTyoG$V(vcU*WҥKx<^vڍj`0wٿw=/<𚥾ud2hu'Bn6V+6lnj͚sӖ- N:EjZjV~.\Ȩ1c:yW/ _ \7(YN7ʩrgyDAڝ%+BBD+'',PT@vpjժA…xTx}0nf|5[={`6)[ Ç eA;}Zj'V_kypO>KY:6m{p\deej}33_uddda0(_׫#axCn@b'::wƌdL3Z4oJzzzѩ {2<֫'ͽxWOoGƎ#w!!g/иQ|>O~fzv{Њc9x0 6Mrr2cƎԩDEEѯo\Y *C g‡{^`mV㋣jx/\h\FF& ݓó}ѰI |~oF…p:L4w$#=]שP?'v-%%W(ޢhBQ>Fχf]~h4b4C6n>ӱK7=Hyc~@vN q{`<@|PA={qqx-f̝ON*%(^j 2f!! c40hzʮ*f EU47͎;_?h6p{bۃcHC!ޡ٬z8Nx _?T t:>vf9ʥ(PrC"H DZ,!Q/0f1ΨGE't4gg6AQҩ#QgLFFy5187/]8; w; _wn|TC||{Bm }mtm۾:%m9s$Vm[c1NNNN8|e^w]9Bme2^0'OFt޸BsRUUmr~7*׵*HK"-X(;UUm\-ϝ#2ADWk+$.իeؘWY]F!-X?i#BPHE}y`62q~7&'z=i$+K`@"K"TF{MLTnWP+ =ivFiE6`#'O*hgIIQPU6z{a`⫯5t]AU~ܥ#IIuAxBnjtbdO*"Z+1F^ㄢktdW ("-dg+:=:MCIa vL󮁇1b>g4 @7#O*V-c> Wd: &N(]Zl/i BO?+Lbi^ P,G=2rϋ yU/fT:|1\D ,}{7_%+ 6uqqPJ*4nSNut)xȶ^LT3HHIUuZ4)NNBz:TS]wԪby}>Eu褥D׺ *u3Wj_hgt:}ج||9Cl T ؿ_OU:(B'}DG ~ODD~kӑ'X\s5LfX\e‡^&kв.IK$7rc9ހ ,FDBӖ&~GB-([Ffʣ= KՓnT*('*gJ)s'Z] l*% JپŃϙ'Wi[K`)O:^`䛙^TiA]RB@҂ʕy w'~.DDZ$eoW9sVFuAP l6?Z$^b 5~ާͷe?f|edOiR+&HNVغM Oy'GD ˖k|n6:_ЈCH+ b^1p ~ҏ%T_8pPhxW`gGL@vU* vP0`A5L&h9x m+ddD" :tlb7Թ3l|E" :L&h\g<]糖w,X|F'+#(Q4lޢpBN0DhZ'= [  fԩ'0˖ ,\aРMkLWyZPPRe0]2AP#IgRcU`!0^}u*ݻYRT)~t<Dt+(mUvÝh_"KGp^FK_I$29%W.U$ҢD;%'@wf H$ ߇Q/^3f%tRڶm+-̕SGH;*1|b3Ѭ)[Z$߁`Iv(B۶m{EQXnk׮Eٳ|2 iuYvnEQdʕ?3f),EQxnF38i:Ilb/&N0 yi.W+D ,ɭ񐙙INNdt:ĕ'X'++L|@ݍ7zݓDut]tvCdee_|9[^'-!999BMff&N(TƼˆB5Bp;fggz|~?… )V)))FQmGsvvv8-[ vPvm9oo+}^Jh9DB.J~f7<ʖm%N/O>c9uÇ;`Ӈx8q"ۗWbوYf8NzEϞ=iذ!SL#66+Wzj<?5|<<rFAz0`gϞfQ`Aƌ@dddX8lڴ!C( 'N 22חЏ?K/D||<Νcʔ)$$$fƍGILLdۜ={l.]ĸqXb8-HIMMĉ 2MpBLBll,p8ѣ& DϞ=7oW^{|(_СCڵ+ 11~ѣG *Drr2#G^z[8p͆`L,%K:uwaVY~0̐!'% " (1.ĴaUt5GUoW]" q޹a8/O?]]uT7n̊+Xjv;wfժU,YqQTTĬY iR\\aa}vGAAM6%///zGh֬/bܢ"&M?ζmhӦ >N:[DS믹;2e z+A;v$''t*˪Z_5\pdddФIٳ }݌3&-RҥKiѢEsX)z֬Y'LAݺuiԨgʔ)ԫW-[w UW]رc|L>=]رcz7@'6|X`9d6Ϟ֔1пxODi giر$p: 5G}`0do*h4Lcƌoq{x~}@ IDATz) `ԨQ/M61u1 #s\@ q0Ms2Vǚ}Ygń K11[+W[w84j$4̸|} h" ]ue^1_y/9Y7k'uhf:KƉpUN8&TX5݀vS)ukغu+'xbz*r(((NKϵSJ*˨́{c믿1[E\5Uj")Jlm6i)}("_5Zt@Һuk^j9T *H΃!y=na@q!5Z˞A++72K TʣߛE s?7%"abwc?ӪCG,&s߳ɦ(@""Zq7 S3g3hו:3sԨqx!M Kr9{0eƂ 4`cؖXBkm%j%_ЁEeY^Ӄ:%;R Sx }O%I4*, e3=L/ܧc3\gY]'^JN Z9^9u9Vٶ8vd]~/H`׾$1;qj_?xSB/xPdFwax_lW "ZR(=4cK?q.!ofO gCռ+>Og|b-xp(EY,̄Ex.e#ǟy@hH5הύs'2#g`~DɆm9 K.ajF"ʽ]H9Tup\jwln5qT_k0٩5 m\u|UU1s9G*ۤ;68nMtu,Nؚl褼e oaΪ4{nzݪke+˳SkF)Ҷ__rRxRx-*3^gݏ_Wx$_G7;Pu;Ǘ>f]4{mR2yNE"OfbD8t<1An0+ƻ>gMfrsQlrX |.3FʸKxbl(JĊsJ[|$^+`gpc=~xb\4ּ{Le֊Lr O.z4恏hp"ƥ]rXas֗m0 uC|y|+>KG(_9mSŶH[";9el +/Yq3 -߂.)Ms \kgwaYs3=ObS6+#.Ʌ˱ܲw+/ӟ՚Ϙna~+I޸gK|aV>wǏ1) " PhU!a+DkJy<}(OD~쎩 uw8͠o]Ogz(PssZ~9x u妕LϿsxrѫe䇲)8!2^h9;lo/C)ŵo?xRk-Z;+UkS-i#.'jY \&}9[ҩ~j^1m;'u_ө~ Niӛ'xmĝ|m։SfKq-paPw4&Y]9/EN%(;{ $]Ԫ' Km־;cZX1Mqn֔qJjkхGLsb| [bqۢ8>=%;%Vx;Ƕ잶: -͉;CG ӹ۩t((bm+\xFt9A+Zim) K8M@;cJEaFuYT$b0N8CN ҲN#bvz\bvڲ-_S08mLePRFYu Y:2Kw\2wy}قԍ<yn\( U hyMXMs 0A?DܶP$=Ahl .79?G1ːTx+2Rn׀DoR!uCل1,OfR]ا0tM׼ ]sfɦkWk>^7)]]Vu|+(5%t.l: ecbzt,UU: fRZZ͉ :6Ofb<=Z7۸4kh*ٍ۩Nm!XrQJQ'5pFc䟃Ba^iQO21d/^(hVkpK|ݺa醥3Ct8eSxAAfs[cᆥWkn]ؾ3kݼ<|eخCdjBP]+=l \ !qSay|+L#/gzbw+:_ ZvK P exo+(צKVL{dx,X >?>{sǣW!DT]@t.lo?$bӱ^J)m[s'\ý󦂙|E5>ӃlQ'U<l];7՚e[u䉼s|c#VIzy)eK f|W+yO8xA.<~F;cʠ^(Iy5'Ńgp3WQ[]Bi,=d+8mv8~{KIRDRv csr3(IWxH$"4/c Z5c*hߔSo&cFb6>õ\q%pT8IϠ4^A/DE"l%'T-zx vJ0"j'G"qt H8v:o],NWkI81; B^?RlcUڲ-z v=O{|MNӴ֘I0UL$N!n[fa|s>L_0*UAГo<֔04֔0wbF9d<>|G;Dx{Su.c7=d3(G*g'2{Dnjs|a{6q':Nuю<_+QX,A WBmݗ{8(\< p|:s~UQ[*-eMc )Jml85W+QZܥcR~XaO-~!dݪ f\6&TN v5F >G̹a{x3M6W'*V.`)Qm̝Kя}w`|s6fg]6C'mV6(Lm3p` rٱs'W^} yzʫHlIRquY?E\ˢȑds G9`:ѧ x۶称،8|/={#y(P06|>o'`~,rhc\"D` p:ࢫm7 g%͚,߹eXYlT/@)&yF"1z\1r֭UҵϘC>?^}l?7Mc1v<CyvMN%Сu[, H- Wk,\H=ҋ)(,{n<:a,az0ދӇ gmd d2ʫ(9o$[oE`A?n .Cpb8zs=bVַ"X n@ff]͠NGu]LH}G @0[׮|̘ SlimYt>p?`x8-ZOM`W,0O} OcgGyR 7j*S&NѦL43xˉlTӦ+-tm8d^_o7JK6heTe˖sHPԬr xfd/] ?4t=Ӎ~Ҹuɶ7l`e7e[ |ulQ luQZ-%[8D#d!yt>sn{ zP7\ςѿ_?-\8̘1YLƍ|Y<\exr堎0! c=yr2CWevX4Lz2cKyɴhq~L20h6j* {&wqXVRh.NI f^ޮCA*֯|vt׋ǵG(\Jwl' W[h{{Gfe更lh4F]HEڦUb] Åjؒy}>xanXV}G(|Ws'ڭ,O0D "̄3 QXXgfMlݸ= W/gwJ?@5kסfSI ` y㮩M4^5oxlsi[ q [Gaʭ-lbm"+eYZJay۬xǨ_8v@?|x<A֭_ϒ%_R]6tnV#^.ӶqЖ hw<ؼ9j}k(afgi)Zk6j͐'ٹ|A˖-cA vM&PTp$q\.x|-.{>GIEE*0 J0]Ν;9n(hPÈFc\}C~?:zRZZaʠ'>IDAT"A<@FRԬp5W8m6틨P?[,]DbV5Td:P&M0 뱒SIг{woE\ƶm;(jք+/M~~>7PVVNx\s [ ?x<nqtyёekRx;uB;Gt,ZCg%>oڵ8J)1|Zur x5À~,0)Uv,SOܑa&g6h5W]ŗaƍkז!ƨ+~sZְ giرD `qNʖM/KP6h6^a>{-oFѶml>|ee(t=n]Z͛ǺQC9xZ>,eVLc$ >˲)+jHq XߕR|> qzX]F׫/1٢*`%,xaSJXJ)\u*-lN)ψRX4r_4ܻwlvxьj(HT9m9(`)CjWfzH̤H`ĀD3?߃BQQG#UFc<$t; +m}cp'?C .+cEޠ5>RmXٮZk Z3C&<4P0X].CD.GVGgqT c!O1lFN'`(CU9{=!ڶm0ndʲyN~n^`(H^N޻skS{ƅQЧ7` ']ZZM%W%W F~!MݎS]5\_Z&a "A8J{ ƶwOG(m֜ne꿲&=hJ j\+\8*KК֭M~u7bFT`hp.4GꇼDJ_̓d(%X ibVLIaʤMaM0 ׁ>۩^7KiҺu]pDZ=.9mԩSW K2vQq 6+>d\MT)Ѣ@uLP-i$Q.6I_m 7`gp(MBD` o,!k_lQӼC5}}Av L+  KAApA ”lA@i-oi. F)-lLcQxJ6mذ~=uɂ Q (6mMvb/` y}ȼަuݻ .Ldlڴ#>}(,φ y쑇%M J\y5mڵaFb sxIENDB`sqlkit-0.9.5/doc/static/images/add_fields.png0000644000175000017500000005161611714210425020465 0ustar sandrosandroPNG  IHDR'sRGBbKGD pHYs  tIME  #B IDATxwևOٜ#,9,A2$*bFE1t*f+f *Iٜ =.y_STq@Afa  6At  A nAA ڵܪ*1xwIW@5י3z;[pxdQ_˪.ϣ{w]|;ǧߘ>N'jSE_&Bn?r>iIԊ]}F^C;l#CM3Z=~̾R]:XW^7ݼ Tuu+m!4Mɷo[ }}ih8ƴd^'ɏ5ձwQ@\nCCү IZt qvlZM%C&O=L  }-ꊭ\LzFzݔ?]WY X9Gɺ=;m^Nי ]ohlFYYrpY @ߔPc3>{7B/qM Ar*CdrJ/ӦUs7V5Q#33'6RdȆȖ>[h+Z3EˬGe嶁#xּes,>R+"hӦ_뎖~zAoa_6>g]KV,| *sɓn/YOtѧ8:tC/XsrՀPkTnQҼk؁suW=1lH @|lFGpyE~rBC^s[;pv׆uîܹY 8s =tOjWg5wŇjhxu ̙~g+$ joGbɫuѭ`j6vЍSwSLXfX{q'|j};ՠh)<{ձm"AvN6vv~攇GdIWW7o0\7[J'ܔ;6/l-[%3o^a;^[EzA)|9?,,0}3.;#}g>@k6C1d c E״8:%tM;=cڷ7T0{ΆS#C]K͡*٣%)!KoQg@LZ8gTfKҸ/N-]? ɞtx]ٲϗZ hmo;1RmA D#Wԧgrtifؠh/D6\BC|;|_oBc/{#?#***cϱ53Kz`Y:0{vo/U03bNsPRUyT}|Q~CE'wNjr~%s.;`A:q ]n!uh&,c@prhٵ9ݐPJS8$Sm\+}૥WбjjJ; 28-%!e]K #GoغQFǑʡUy޳d_-Z7nMJf`[Vva%5&</6kDƃ5w/Obŗ1vSfIz;U:嚌PΗ*HDIФ6!'UrQcn:AuG+Ta)(Zw|;% u^6R]ÎZ?V5VZ m{g͙'+ AF)"5k.U_*٠ i&r`%N5:.4 њb^v82pڔn߭9np+32xn#ȟ6m4!c$zA뱚=u.XS8QՌYlxTŭSˉٟn ʣɱ&Ԣ'?Vm;P0ٍ7g ,.,sXF.FeW-qod8Bw"gvF?CG$DCNEӄGBͩjwD)*X[K9_ _yu G܃{wO9vߡ=&.m7`sJ 9XNm҂^klUNzIDZ4=D;v#ّ{UfD'6\n ˚[w蛅 vT;ו:ulw6OaUamGw[]tUy_n(>gE\>[ڍf֪\7 ef¸᳗9Ljokͬf!nfۋ/u:fkېcp֐Qmۤ_XGeE<1tVy'E4*!wnkn[&ՑUVa#֬Đ."zN Ģj;ʾZjV[_oF}}=fyMsi-=S:ΞMtuSBx50Zòk:7V_|Ƿ8YbDu.kmvR0i[u6U?Z[ͫtꞰp;zA$ynFJE)|TTr 4jmQ$*Qz2ھw7i3N:pĕW~pI'bTg3Vmm֫ϔYH YBi3ʬsWw1jT(VA$aU*Fh4.D%oڮryj ȑbiQhɓo f׮-DZTA_WУ*GX#t#啕EMY=2"8)s{SVVq KvAb *auuNX܆jtǹ? ~t9N8ţj[m4mݱe_}ԫDbܶ~g}8 *U Y9>8V>rJoQ?,׫9@QjPWZV'qf:>TWcjlBY^$Q\4v? 9rnL)ɉ7>ӭNpn}rQ!:?VVZ.Za8I.mTߨ 6Xg)Xʊ+'r"2Z^{M)++ɧ+-"Ъ]\}B(0Z{RJó՛rTĚHmNQShA+*ڰ<$&&FDt$7Me6Di[$^mX*++M&jkiМ6JV0C "|U+q7+($C֙m; 1G ?G **VE궝 7kl%Y[©˽Cb -D*:=bE䊉6(Cw/44Rڻq 6oE(NNCl%~tь1X\\qjATmm?kubꌸzl2*'*p'wl{je7v $| !ZZ]͉S-nZ .S3OHl&Q\LVgm rDvyN)%67wo'v!vo5OI^;t:%IjVln-چP&NE>&{+3R> Q]Zj%͡a`mQuV!I:E2FU^lˌ7x7xm98WDvZjyEQr_ :,kQfށ['݇d29t-p6mp'Zt%KI@; _|-6&&%mȠGGNdD$(iQuNQ @b#M >pAnf<qqq$m?xDF@v1Bn'j-Zx?qK >؀r֞~0lpsgkM qqa-=1yYpR^xsh*H !12H~Qɀ#(QA./aB(!!!l6t:wHAOh$uzQ&G !<<\FDD0T*U]] nhn :I*cFF}M=N`&P8Y޿Y uE5bTWW[LmZTY;EI~ҔK;Wn/u5ܲʙW鬶 P!rEVU6(.K^ u\(cq'?wDjyZqR.gpZNem{TXoTʤSix|9 WۨMGiFRYVf0Fdy89EfUVVZ,9$biiiDDDEyy F36Ztof-[̊vp[UJTXL;B,UbyZXء:{ qJ%tuuw-'܋C.U*tF^4w>LN֟tUY4:+ W۰GDFȩ*O"?28J%_*lF_BZt_s6kUB"ͯHmwzhJқhmmz8^Zso>u⽜nQ\zsuy|o)OҧEΖ9*CD9BjM^2 :w pQcŒpu WPʦjOG76f7Yy&iѩ/ب?n~ߢ.hw#Ghfuv9k̯1Jf՝ɫyM cT*hb\ c/ǧH"e*49| ZWE*a#Q\1nضm[UUՆ bcc#""L&`tƛ<'$fum͏66l#1*Ԩ9~] 1:&]Q.*8忬.yL-DZ *d7'~JjU(:?Wq~3JIe-c\AV[[R0?ς!55 q-0{p#xgVtUhnvͬ1f}gfԕׄ1aLL#0BD !UTc,:`0xCB v1)¿YQNP撨(I{N JA+mDǜ:}h4L:l6 `Z8B^w8.KbҹSsb.z[!:45Ӕ{EV["hQP&4$B!c!?;[RNq!@$J&7N ywD"reÇzmOEEen`NV59[ r:)ܬ5l2 r:A𽶶I;n45ơj:}OeչzІBR_;_cxZjZFөw|!( ;'HO69xGG6,ħX̀a#_ɼQ=Ϛ4V_=}Km1 DFDHivjg%[!1&6as0?.0#b6fG(ɀy[|A&6l (͗~FuO7yA3xOESy !Qz@uc<ą ׼GARN[ 0#>1z &my 2w]XOEݚf.)+Lψ{A2ƀߤy7EY[o>Qf L$ ?E1J]!0ϔX(72F-Ѐo ?|6.yu c` 'GB<&[HEd^kh@gHzgL>tK)|RvglTa3>cAwݻg!!+DRN50&r {r;41Be 46$Q g}oF bGkXtS|#v6p)#*вoVcQXB1fA]edAH@t6hLiЫgO@K8gyibяgg 7$ D9AʼnD!K&)S yER1e@ޡ'~S!?i+R=6晻stTAuzo`xg.74=wQ%IÞg$x2qBDƘg^Am=3om$HsUDV;M5 IDATAK,y/<&1"O0< mmH[l~M@+|L̻җ!1rY$hP,t ~sK`pQorMBO\%pF?_)l/r; !S6eD|i152$M<܏%7y)yGԭ7okadE"S!f?+]9ܞS+wGcnxIs[Gvb̗ߜO|}N5ɽT/Q$dzsrϑ3OGIL F!bRɟzb!Bi88ۺM: Sxʉw1=N<zw;k,2mّ%3\ܓl">{|;Iι'E^C-ܸ7K+pQ.gr09)& A (S:ߌk L+'4i0' O~1 #]JQW r @SgN`D0 r*xߔ٬d, kI' 0KXwf³V o(Y@sy}"' F +7 :nҨX%6~_'QL%ӊ~AVd;Aw 0%8R ](}?KP,&XCT`lo 7*giu"|""G@Mf 'e?wP7 zjV<927&{Zš6u8 Zv|H k;rSd+@gXkf~bx'\H-Y=J OO|LW듵tR"_`f+ eAE SQ#^*X6h0eY+MbkS)NiRQ"qe{.U$"*:&rȀ7v蹫#yez8@ y4ngR{g"//>ˠr/Dy7BsG 9y7]N1<DI+_Ȏ[$6$1@״$ x-CD)DIb tԔ)? [ƚ7f{iFηbK x@ӈx4šaM=;i(Wj-mON;^x8D?WIjQ&8|K(%G[HYK%9' Cems̡ Ϝp>1T}dOێ)u;j yf_8!juY%vʺ}bzՆUKv0so%e׮ݱG#nGkJM,?pA"KRpxwIky!]ۑ='(H(ٺt՚6#һ߿c();[=$5f?M|KO WP}`ͪed^=vĠ4ߓ3?ˍ]W~ #S2HD{+W6vB<~~o~ٸopygi,k?Żqh{];nB'ݩog/.,е#z'_+ } xJv\TnDع c$i]9_k)e< @;|j/޲ĿfʵP^1XM߷!OLrbT%keRd\r/VWY=ufk7}R ،(dUNSJO=zmV{y\P>쬗k]u62[Ͼ[ `ILcs76:N?dɚ[6Pλzy 9U_5P'f9kj-ι<Z5D]@ݖTI>nU T;==huSʤ^*((;jƴy5?o;r׻u;w[Pq,w?YƢuZe(~t!y˩}CaTEXVHT^r\=}b3Ǭ޿wl<lg7&D&1` F'~?ooJK"C9%:˸}$/Uk޿E'Lۊs{&0QbD ALDQ8IQ]}`NjWJefwT"kPHZ.FI{4)JGk7.YG'O:呫T@ 0I\}?ڿ> DӨ6h#ŅJI st UBh)zfDzc^ioX POgd֝ kp2"uX/YV$]Z#jE^ntM?XWTwxSD(^po)@̀H?qѪё\Ny|$Sȕ/ȣ7Ή]~YVKH_aĜg7\nskjwb0VYm4b{&ukwx= t[{h AmG,8,`TjNnWu4r *Nm\!){䔿\T_ ,ެ sJʫH5VFF.}yκ̑tC"ͱa,Ts<6!B11[6*N-҆'iM7.wt7 Θ5ؖS޺kXJv+U7V5Q 9js1ƼQDz7'BkKJݓ@U:hH@uUܰ&݆2Д`5]~/"'YBLjj;zυ0+*|J$*OŦ1%fB[CsUO`TtV1'9XL*Bl'1;N O4([۰D*X]qzV_]Pa3G=F r* W.3Ā+l Ӄ+׭d09P21*O{ 5ZyR1QB }[yZijUqb4Ǩ+E.H-ukUF%ΐ:oZC{uO3k͹C*s3t&м[j U>{F&I-fv1#3-aAjjs ʲTf7MI#CX9q4ג UX u7uTކ'I%?.ENUR Sփ{g-ޙSSsR߭{7*IueeE5v4k#ayUu$GuM9Cy+bbUQ@HtZXLIϫ&Y_^&..R#ntxYujN޾K.Hc=C9@Bmi%)!L%1 TxsVwn/L&1&jU'dBY#G_yP&t}hI 6^\MH򾚴acn0l_fV'*sƣηN1o|pӒc@5OO?ҍ7g@b0`y(1uJnɋ͚?4}Ұfl5fX?Zrn+O:v2o{t@1rC@c6YjQ8'BÚ^`@MĀF U@O+o3'װ&۠6oúNS$AZƥ+o3'IO_c[ i&iq p劺ی %t\q}uAK6@AD#} IxYrŜ^(WA쓿m86 ? v _6 9رcڵ~> At  A nJD87/䊗t[gw_Ou)S^D.7 6e/t}ڀу3L8sK׾7ΫeۓyTuSF;SkY\yNlYG& c-W M|3/;;;{ WK=Ozﻷ7/ۯ陝sOro3_<>Ovvksrl?o=v7Nα{ Z} }=(υ2rQm&+=?OloΗjDe=;wЦ<.rӛSX慹(OZWlnWM1#%Cï^xF^(ͯsz ??]--^y(=wcvyߞmʰ[z6s4=X?kWۭUBbAɧ7:X;/NU+aLl_jR^ b֔x9\cۥ"K!Ev{|]=g̈rO9cUhBAw=,88:DPb'j@jm~zy̵ зQ{%Uqo5RThW;(Ъ]s^1ojES@œx |}U"ty^N&E[@Ҹx9\RF:#-lEΜ9}?-~gGrS ^~'GS'ND}`iK|PnqQ 339ϛ9gŞE֯;#FgD5wL$~]&]g'gA=Ǿm9ih'䏄?v,l ŧs*UaǏy9]jYIDAT]ֲ$ڔ>M|z=cE_youPקtΙ<7gFvzmtVqV]'>(c[ywr1#^2ntSh|;+c/Ͷ?{?3œ'-m3ăaxg?ig_6aoOͷq/EGl\1ABPGϛq  6At  A nAOSmTWWc!Wuuu(WA6At  A @%RFM/bW HhpY݆ǯ{=@kvz_57]]B Q/_Dߩ[#VsKjXVFeXq$7w {EmxG<5Dw3bcF*rWݔw(UnWxPDbg~xg3L}zΕ?MOWU ~Sݱ.?)8HL12/\NtԖڹrGO'&o#:w FV䢦Ehݟss+m:>ʥ 뭯߆|M</ S52& #K!?ɏ?!*sRzf~_ ݆P3fL{LhWb/{MH(Xjc#%T*ݳ,;4vd3w<޿zƨslj|crqH;W຿0:MJVnG)"W&Ii^u:?ږ a̙zǧJafMmocGඍsvj ԩJ :*. \,B ꑤo JrV)z!" mύ8tu߇h#gf実^wwUtxĎV'?o=S~Uj-M1ϸ^ճ'p\sM!W"rE[~kDUlZ2wN5~/bTpZ) Zf&1 C1[g8;FwL}wό\Ȍ(`Potbaڳ&㖷?Ky^Y\%; Yz(itu;|H<%[¼GxTN}z1ݢ2G>zAQ3{?6s蠿ٝĉ8'x;7 nAA @Am  6A6ñ+ٌr>hAA @Am   A" qWr9%l)xb znYo;,d˫u3m TH^m4Z}a jˌ]t p|qĝFA뉢Onwt1a` L:ܻskA6SiT'6A ٧ 2'RNUt AqW̞"{%?qOt HNvʝ ;!_.#r!VY|%hAYJ{86At  AA @Am  +M][]]\ա\qO0@Am  6At re t}qA.(CZf5A>G8Gw=4M+f5?6鵣A~dj.a}[1&BZ( _{7HǿyeƲg*vCn~聱Y&q"}FLyLhuCӚݳb͍ouW?ߚ6u0<eRoIHg?Fy|?f]?s&"Y. RHނR Z^`PB v#My[}o_ct?Q':jKN\9WocͰT탧^\e2[f>8 >Eʇk=x% \}3ͺuSŨAnڽ|-3F>GWDaJȔ̃%Uffݴ{sOUDmgݔ̃/3f_{wԙ aPP Dm(*#u@O GGѺahQKhO@pE :2NAD d> 9r>N.!}r}$艱Y\z7ÎՊh0Oyc]+"-dNĪy<쪋9cA#_TJH5wJĝ?qnʦ ۇ`e| ٧c|Fdq"jNI5 ߥ$~RKO/:s]xDVϝ!&iM Av}&[u<_&@6'v kʑ<7nM ߻8{L/ą]-dֻMw4}p!qY:vADv ]$""2lM,Ql_x:/$7&ԙa]ƿ\VmCľ#e单o^:xkG|'N@4j ۂ$}>dۑDD$i[^:pn􅢴{|N?aG Jt̕Ҫ"MΈ" >\Ć;w943\vf9xڏ=BCNmu[ʦSmʲrp 4WGͮkHɓX[~j3=MGXtC۳h:a1Eu&"BsR#<)pⴐNs'.kOskCե#YY+;Nhe'-Y؁3g; |^Gs[_w1纥&̧ʾy=ŸuƳ|~O鑯I LX>8pƨ }+L+m6-dxG+ $*)^f8V0'%g˫rqA}p_-r]k}ANq^N|zH4k8UwރcOw>_bcc-\o+ecdNm*<} h!3?:XdDj<1];vRϓS#g7Ŧq{~AJLD|O7x>i{+#Vzu_6Cay1=\'y;ɸKgX0;,=Unp'Fkmƹ[*u^qrLrJKc{OJ}yeBld;/fsxR%[v/_/ژD>_lqJ?_leL0a檏3w.m!Ց* "@A qW?J_2/M*whz:`Ds{]':}11%gG7&-dƒ//8> ,l1bwM̶EglfÇV/mI7``oNs޷K=<{zz؃o @lbmڢ@`r9{5hb"gY] :yqDoOh;ݭs7;XކGĴ URMmD:^}/dTszv -0@lo>|2M~)mFa=bۯ^ԖujG9 (6`Ԋ~\BD_PDBIJf‚8bDDı5e&"{aZ}( QZ{e&\@-HD$\@%JZѡӲD|lB2k,#6`3 0 +ai⽵ a˜zq\rr^7 6[lIHHhllDrb'O\fMSSkЧ"F#Hr9S @ N*r9VbY˫`̙b8:: ACC ,tDq@ ͝5khdC*9Z-լaQM0ڀG|>_(SNZXX,\P*b /EF͖IENDB`sqlkit-0.9.5/doc/static/images/completion_group_by.png0000644000175000017500000005145311714210425022465 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|TU;=@ {T,X;EXւYUQDMKD33dI2!!%s){|{$* = Ac;ʟ{y-ߦࡩX}HzN j8((zZK\/nSߍ4@P$`Așc<埮Zc`LE)&4>iO;nMΎ (@dNPhWQU9?C@Јp_| =;j;pDŽ.MΎX: *};ho>~ Ly^5,ꆪtkۂ#뼚oYQQdIQdt&=צ4U^5,hzO\h";p؊Ivi;PX"x;(ί0krЙ}jd_AQu V}DAPUY{Y̲(%P/(GNdDfy5..qPsA-W;Î$<*d)٧I_tϡwDج=jڨؽ+r(ɻT|*w[=sSJzjggn?9;v}Z;k%5RÕ5U;qc"tt:$IN'9:u$BUQ1>x$6a` ~o6otG;B-$)kޣ(3$*` E.̾dlz~7p䓩!u"s)sV <UQ11#fg9;_W4~w@S5#,mm6'O~vk)$I5>4EF"WF7fnǣϽIe8G>yjl!M,{S D ?W~ _ʛnPr>?5{~:"GOpa Xh+ow^}07nlEpDʁ-+X|^y 7>Cs JZ$z,DiJIrV_YEvJg5]*%US;żk̒wq+X((4W@\ʄ:SEUY\}o`%- ]m5)E.xa.nɼ"n n3a2/;a$./n dX=G\ /DHc)>6.?妜 jcMNQ]씊";o[?N*ayh`Rpf_ўr>\7tgPr"gSTXAgʉTNt⨪BQaQWqp&;.m 6Ytn@F$ l*; ΢S+?$ȰHv%m^mUF!d_;pYv=**' mePp(dۼ&p\d߳ë '2m::fA&bCyR_˚ͳQ]Q̺5U}GvVz..]\`Urh.Jnʇe܏._݃ȷ .gO 32?'dFD|6J~ޏ 3P#:<ʑt36U sPJwI꟪&_ܿN+*gہjrݖ-mT5꒜m0*A/H##ڝa$Ap3U"Dk`ى 2$jlާ빖75; Uմi A$.}/j{JFp^ZiSS~/QQ GqKI 7%Yqjbmn9^4,l6{e 6i* \umnp3:_+˯.tٗ>BAAA6<ዯ3r:wW}n)LD|5>|ؕn1'o = dmnΙG^8l$.ح,e/~ӻ`}+ԩSy}t3=0udff~TW0`cs8'퉝/͈1\+̫Gu2߫{=-8q.`L7O/v~~e,|x'9|X'vn>?7o1; Q=XixEKy%*2Շfϯ桍?<"XzݮfO+ÄǟVUU~U?f?+l2_f~Vq6ml+<449?̲Ϧ͛r׼*,'v{Ȉp||,>f8Xa^=UOՇW_Fq4\8x9S@h=Q9AA|,sdYvgfeۼ=V**'v=(WmVgpO(pxtxK_QiRQU] TiZ$BC| u ?9yVyxX('O%_=EttteNuK]=,}m0)**bp̙j񤌂 zok7iN:,>r?桍د cYKdffsϿĥǹ/t<Ͽ2YYYdff*-WUxIIM%7// +0MMFNbSW~TW6U,o&]6~}3vz^{XRV^whߎEJqqi9'$^RģwL޽zqmwң@f=8ᝲڬ%,4K&N撉g}]3F\2IW^K}*-WU;i+ɘqr-x UzMSѫGwyGueݳ]q #^JpPL{^3]zj/{X3jzu€or3WFV[Fq4ޣh6?4V\XqlK9w{C!+fuj >S,п"D|UU]IR]pxBO'_KLUˈ 2xXa{gÕ_N`?9y;͖[ҵ3\}-]wV@~~>iiiL/?}{6h)9﷦iP4:e_2aX7M#ņ'8x*b3r ^\s,y&tArr:|6% 7xd;D@ufWBeɫdk'0fl6>Gh=sYCP'44~4_rbAQJ՞ӧ3JWQ?5aNȏ+h{3ƫ {7_}H/ 11͛7$''s1N<~^ENa2J6АX,gC-8.A1BcW:NH^pl3~x (tuj4ZXEQUEQPhkZZ:a*q5eBV-]p80~ܓn멉H#;yՇڲ_ק'[#IdY!6PTXN'#3QXP@JJ V_C(*EF 0(UЕHLf231؈>N3+ E0^G4O o _MBS!5#If74t:S طa5s!44?*n'?2 N$@*`GPP\WtЮcЩ6T-[ԏd@UZ1;v6hfI'K^F $ R:Az=)-8il6ڴmw۾73bg{Յ/_~c<ѣGA$d;J^^>Ԕ6mΕ'zEQp2PلCVPIaIB%ZL:`]Y {uuTuϧ( ~ILfa(Frjl'ʒEf_+ѽc4w \ǎZܙ39_;}h}{ NyJ6 '88EUWp#Ы67'`h &&WrrrhfpJEUҜ h sr~H$}L:X2m0_1F?܊,5Kt5ڴ%9 εᒑyWC\}ݛdY&h?@FFȲfgai^EU紕sL ƌ#h8F @ o~ L@:3Ա,tv;Nլ 际߻Q]t 5kN)MϜ!:t~Jvf W&ʪUIVVW}p \: E!&#i2á"/0m6gV.Zbd(JE+t/%4pG;BK))k.(8+h@˖vN#p.G.Đr^|3rE}@`HZÎ"(l91c[5UA42QTل,+AmH>9Bb1t%iHImJT:5]mI*GCBR.C;b011dɦu/o=5RkW_M?…<8&Ee-\qg}Y9e trqHZII4{jq&ΠsB4نN5fd Ja! EUxgpM Hk>p~O[d6kCҼysBZ7fUFAs[Ys 09J$!Vv愳I*]ʋ]?sl5FJG2ҳI/YY0v{)me˖>r ob4Ѷ][ Sb2PUU$5 iZd:RBV_ߑ5rRAQ0~4 ]D[Ƣ<~gxn:,+HH*9E8P4Ҍhh4p4!qvZwa"gFף$$IB4$MCޱv-[2|B@==z4k֬CǎF^~~Gֺ':[6ZISPQȥ"#Q&,%H r? RŬTteפ#) IDAT`c<]j ^uڇRJE.,<\Nʉci߃nݺӲeKZ.®lr(pQر#X,zP2228s&ǙÁ`p~ v**k 3A/hyin7i}'Z ih9vQ ;GO吧 |W*9K׌|GPs\0Gx-E`xfz=z$4$UER$ 0ybbbh<EU^Ml6,&m{س}!\ͩTWCKJ檖]ZK+JN)p bwb*:\8F\J+#=‚b@ll<~@X,^ߤ*QTTvtdj@Pp`IMO&b+RRR -b咚\^n.v??)7TзA`F26 ]ACZ`0 8Uop,l:0xqn2mAg0nW6qҴPPz=8P\:bۑ$YF޻n'88p;^e0 ]6]_C~%%b 4"Y-6 +[ `2l4ڵmCHHȅ3P^jSSqkݎ |C)..lbA4UP"223ܮVRn9Q).NmΩ%uzBCCHQ3ˋ)}K=.=A3 ً"=P3NBsh4ѭM0vN']k%?ɘm1~٣k IGS% \9s:@9z"\CQ2O.8ݎw'Ŧ 0+!gG~Ry(}]Tk ֥UVlG1nqH"W\}5oX@h^C4nSlS\lCT=UUf#;)k&77ՊR&8%%/u9MQ-$$[x'93 )?'U8v|㜎vt#؊Aj=4b13zH{Dv~(>f z |W?tF,oDo2 fʨqX{в\]Zz_5`$]=:w$ J`` 6NOe-t7J B tҙYfK`` ]tMqRӰo)ے۰u]_NjUe0C8SHHkW8 v}ݎ"+;Ml=jAu!!$'`:+EUJ2@+\ڎ+}+utQ5Ԕ軌G DKw#jm6Od2<&1}z>}XBF$Wo@7IzlyUyLF\be 8v=77%t\r ǯvړNl"##/ ݝlmgc6=TkX:lR6 d3ΧHCޔ~k맙͊d;"}11t+>hޠl6ѭS>3Ə쐉VLNN[N2l0|}}HZ4i_ Ŀ_|0+E\jhM;ZN e9HEE{9WK8޹īz8Y{ Etb*tv.II<4Y+>AQ?AAu꫌`Ԩ/vJDNܙCd"/?p EIzf3 Uk*l۴ b~Vמ|Hbg AJY/,VT7Z7cM&$5Obe5tlߞ\T޽iFY$GnNm[DRА|Vtl/ v9lHFвN!-09GyԁS뼆U ܣ~r?~oDJ8 n(' GƶsDGGcX4lZHLDU5^2v0g0ͮdl`4ؿHX?@y qt2; mDϒgo1hu&!5 tz_U{3`2a0پk/w;˥_pF}ߟ oOx*Ck=ulttgЃVI筵s.-[ઉFu8m9zR!]nj:G2G mZ_ײ%0[n./ ѥsJ'Μ&; AyTЙdEϋA`` ;v7`6HC m̙]-[靵za ߏg#Yz,S\lszt:眷҉:sH飡EA$f Rtp%v95GmO!)sLde90j+Yl6[BxXAAX,z$&%%={f]66ǧR]#%I4 I'cډ@m4MCQgF \[sNhb1j-v5dZuT.)3  |}}ڝA @ hM!rI#DN 4i &MG{yM@ 3m:UB,@ \h@ hM!rI#u5Q,7A`0ܶ 2n="h F#ǦdUDbBAI߁Chզ5Q>~aĶD]*>Ⱥ+0-4mQ:!rMV3|dzЮ^YDDDrqGiC%=%Șf @u#=%B(&Ij,DN 4i5m>AcD\= DL h<# Z‘U 9@Ф"WJkim;_(<˯ҧ`:t3tmwv~҄#ܚ۷[7b6yi"C\x?oQQXVyx-Ne)G,j`RS3n۵^ "ݒDFF4+AD4WH@?ǎ':_7\csx$,sa[1 Q#nW\Maaa}gՅ5$>ƛN||sW;.\$#モwvUi}״Jm@ hM!rI#DQUy(2z"D & @uOq|!rMFe;|E .|E|g 6tb IYlK~Gc<ގ5Y FS @uda(&JQan." F8!rMH_>uEr7mGS9-lXԴ%zh I>!r^мDO}p BR_[v--Z{HZ=5\P>?ü[hЄF5;TKKػw/&Lhۂu(=j蓻(..n3M`h59]e}ki>㍷vm;s#$y]Q4zvm;|JtGv*4ʜyhS|[q78}763ѢǴh13ffSl|gǯ-p$^?~ɓ'ba„ ٖ@dF_`pHNN4En,bX֭i۷vd9.]}5G܂b`[\_xi;eٽFNvν)mRfּ|v6S ؾ:zII<UՖ~8M\'MČ3HKK#553k֬z/(}DEEKr-r-R"+ͣ&4z[na>D‚UZ\/>?+~~F?џ,M,xvPcTEMM6mģ>Ν;)((K^VTPmy5mZ3{|<(]Mh"C\Zx2F##}uŕDGVˉ<ڴ(p_7N$S#SՅkW^y@^^:<߯hիl˜y,&=lQvYkI8,?mwr_v<N. 'e8rlW^ފl"="h]嫯,EEEX,, LVA;j@9%(LK28FgߓǕˈiK/-{Dˣ]`XB>ӼyYs=977?%~It5X¢E5kV#G2xpAM_:hnfdSQ 'gƴa3 by,3nd+9O.7s=9;AdddqRNpON \4A,!r^<>"'}B/kžzAo0Ң=}o{ҵ!@˸xNu^wuD4dzFB('vĆvEp ;]N4mZ7X<ڊ!rM]4 Mݻ5 ,VFTk >]hرPd6+Z"D u)˩>Q#AF@ hM!rI#DN 4i &9@Ф"wc2Txdsj M2t+w%Tpǟ];}:~(?]zp]GQĒ2A@\ ~;(k]nbƘqoۑ7p[stΝ\sݍt֋ص'YnGDDԛ6;w0a¥n}n/[NG}>0uW~֭s, AAhт"9RojX_Cƌ+_y;wϻnb"#+{eİQPP@מ}ϗܹ NΝٹ*ڮjΙ GUVtޝ'O{:tP_EӡK 3v$uƐ%0 ^wAtݏ} oۑE#hծZ>бkOyl-JZ>M)v4_.fȈѴԍK'_T۶mc-؊ [Z,INs˯ᅲNt*vÏxKE &F^[YY_}Ǧ[yϏ1+gm|Kvoʸ0{޵C]Jy/5v#!00gyUVy-AӠш %<,清/^?ϛCTT$VG/}3idge q}t)%**y-ﹳ%4$RJ 剹ee?ys~ُnq~ "##aSٷqMեU7oaa2JFFWm.|eddۼEls4ƌj8̲o 8rowg_y""*~6o^/Rff&-bcʕ'((b ˲_  ˚gߗ9p [q!hMF#raaJJr Ɩ k]MÄ#ܚݻuaM<5.y-SՄ$7&4Խ\'O%REBݺuRXXӧ~5J_\\bbp gz u.qhD./EFf&O?[n#IdY?AW}3c82[q3ϒJnnOFf&)t֋ jIDATg>%vwh~^W^r=ptZj̈́I`ب1ȷ>{1~d Mppf vmLJgƈ1zQ<4kF7$y7W>NE{ζ[(,Cy*Ӝ}>y$OQa>ǎf=ũ.{t1đ۸3)Ǿhocg= >U|b6_#<{ #ӹyUIM:g6@ BAF\5\hMU@9@pA(2zBER £<_=#DN \(̱Ⱥ+0-> r_O[E=,Y&~ŇquBF@xgf͉d%\{VZH\6%3AOYl)^V^#[Ƒ:OINylZݵ\[q$'/DN zJg*✽_*w#&_dߘ>DFE^Ն74!C{ Y&V $r“n/kÖ-[߯?$e9 188⢂s6El+ݻ Z=QزŹc [vC Eshg0w!}7{&W4zk5bVgMn@E")rÇ;'#; Ǝ'o~%A-sZ449IiOQмfyQRQKYfxɻ""fٷN `OW(21#֬>{?{?5g> tGqq1 ܽCʑ\>R'`ll̚5}rGqENtOzJXxDʤ}xѩ/ĄT+GqV+)++ctٛ^z3ϯ;u&"rcuȕdX } ./K+[t5O.]вeK,99,%R|gNNN 5O3``,j 4Zu[z-ٷo{˩qT&z쯧-pp`QyV+ ݸ}{3'Q(,^͹s=\+? &zv~n]1Te>n5\P_s1/ T*U{{;O 5?`ooW#pr??'`cmMk|תrѡWϺǥR~#9}P(Xz%kWDPNP;z |V6<3OKgYt9@aaVʭ rulmmr6lj-o`073sп߶>R--<7ӟFrc!s'ڧZ*`{<Б/ ܭNXƳ*$I>37'bffF~~>]Qg6vvJ\Y9^PT*cb hՙ-u%)j+/>nݵwppIuD]zsozW5E!Kɾ}[,\X"/..XH 5-H-[sZyRK̘U!WVߙ\rR%**.c`@#Y~̙ ̌I^, E!K>;;['MrI^== sSS'cfƌ$33er6 ~cSJǎϲrŲA􉁫K/Uܺ}舄sڷbn}9k~4k|McFPE 0eLJFZ ''40uO,P' 2e%iּRvx-f(Q$Z?aiRPuIKM޾^hcB89PV }8}(KJQ,)~=]\iajڀ7ԇD9~8޷/[6cۺ5PlOܺ{6M6n!~n-˾\Nzz:]tfsx8N5 ڮ h99AejGR13s=-d R˗,]ٳz&dLLL;oDb%3#Ç3G-tgCz:m nmCwGfV'&:tPsZPP@'繞 s秃ptt #3ݜIMMV뽙~VmZQW#̙3ÿRڮJ奙b$4SR\T_ɱ e:tpOѯZ`0ʚ[87nRW'ug@fVDo055ETJ.\׏ݻ%NmAB89A000m[#ݻw+i1LJRU !IlooORUs,.*̬,FMo^M ZѮ];\zsww5rblؘ$H{{Ofi\v R U4-df>o_>c'^2zVZN (!K>ΓQXrn'&Mƻ>@T- ž<}|qsG$&%K#kl())aW3ISػ M7Gp1I=u;UhB/lMaa ʚo ~ƥ ²{u̯xMw:bM Y'f](IOtZ{\Ȋeٺy#FFF|̓E֛Cֈ0ڵm˖_K_|'z;'%5_.!l:s%%}fzp ޞ ;vFSPPȦ Xr&aTG8N:͑Ǚ9A]XK6mXǝ=E7Liedo‚{\ri>>rE߸Qow4>ƾF3Rqr󰱱fTo#G4kV0n;qqV-K/bdd)ƼϹs1-bb5kVhaDNU++ =kIkl=yJ]ǢeK<'Tyf$jDov0Unj9'5_ [ĝ;9m#SekYWѪ=NԌn#+ٸ|WBΆ1 gְ!W L/ܽ73ش9>HaQ]z>M M6Mj:8p >YkK_tyIGz-D)\Ǻ`֯w/lg %^lm[3m,JJKqޭQgϞ%X4-j Q뵺*aC0l׍*8^t VWk9$AS.!lZ@(K)\5=g0!Miq߀`bOWW '@ EgCCGb F.]( EvurБi7Q,l͟2B0n~ 7 @ࠚե Z 4:bO= s:9IENDB`sqlkit-0.9.5/doc/static/imgpreview.js0000644000175000017500000000270311714210425017141 0ustar sandrosandro/* * imgPreview jQuery plugin * Copyright (c) 2009 James Padolsey * j@qd9.co.uk | http://james.padolsey.com * Dual licensed under MIT and GPL. * Updated: 09/02/09 * @author James Padolsey * @version 0.22 */ (function(c){c.expr[':'].linkingToImage=function(a,g,e){return!!(c(a).attr(e[3])&&c(a).attr(e[3]).match(/\.(gif|jpe?g|png|bmp)$/i))};c.fn.imgPreview=function(j){var b=c.extend({imgCSS:{},distanceFromCursor:{top:10,left:10},preloadImages:true,onShow:function(){},onHide:function(){},onLoad:function(){},containerID:'imgPreviewContainer',containerLoadingClass:'loading',thumbPrefix:'',srcAttr:'href'},j),d=c('

').attr('id',b.containerID).append('').hide().css('position','absolute').appendTo('body'),f=c('img',d).css(b.imgCSS),h=this.filter(':linkingToImage('+b.srcAttr+')');function i(a){return a.replace(/(\/?)([^\/]+)$/,'$1'+b.thumbPrefix+'$2')}if(b.preloadImages){(function(a){var g=new Image(),e=arguments.callee;g.src=i(c(h[a]).attr(b.srcAttr));g.onload=function(){h[a+1]&&e(a+1)}})(0)}h.mousemove(function(a){d.css({top:a.pageY+b.distanceFromCursor.top+'px',left:a.pageX+b.distanceFromCursor.left+'px'})}).hover(function(){var a=this;d.addClass(b.containerLoadingClass).show();f.load(function(){d.removeClass(b.containerLoadingClass);f.show();b.onLoad.call(f[0],a)}).attr('src',i(c(a).attr(b.srcAttr)));b.onShow.call(d[0],a)},function(){d.hide();f.unbind('load').attr('src','').hide();b.onHide.call(d[0],this)});return this}})(jQuery);sqlkit-0.9.5/doc/static/jMenu.jquery.js0000644000175000017500000001417511714210425017365 0ustar sandrosandro/************************************************************************ ************************************************************************* @Name : jMenu - jQuery Plugin @Revison : 1.6 @Date : 12/2010 @Author: Surrel Mickael (www.myjqueryplugins.com - www.msconcept.fr) - Croissance Net (www.croissance-net.com) @Support: FF, IE7, IE8, MAC Firefox, MAC Safari @License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php ************************************************************************** *************************************************************************/ /** @ IsHovered Plugin @ Thanks to Chad Smith fr his isHovered Plugin @ source : http://mktgdept.com/jquery-ishovered **/ ;(function(b,c){b('*').hover(function(){b(this).data(c,1)},function(){b(this).data(c,0)}).data(c,0);b[c]=function(a){return b(a)[c]()};b.fn[c]=function(a){a=0;b(this).each(function(){a+=b(this).data(c)});return a>0}})(jQuery,'isHovered'); /** jMenu Plugin **/ (function($){ $.jMenu = { /**************/ /** OPTIONS **/ /**************/ defaults: { ulWidth : 'auto', absoluteTop : 30, absoluteLeft : 0, effects : { effectSpeedOpen : 350, effectSpeedClose : 350, effectTypeOpen : 'slide', effectTypeClose : 'slide', effectOpen : 'linear', effectClose : 'linear' }, TimeBeforeOpening : 200, TimeBeforeClosing : 200, animatedText : false, paddingLeft: 7 }, /*****************/ /** Init Method **/ /*****************/ init:function(options){ /* vars **/ opts = $.extend({}, $.jMenu.defaults, options); $("#jMenu a:not(.fNiv)").each(function(){ var $thisChild = $(this); /* Add css - arrow right */ if($.jMenu._IsParent($thisChild)) $thisChild.addClass('isParent'); /* Add the animation on hover **/ if(opts.animatedText) $.jMenu._animateText($thisChild); /* Actions on hover */ $thisChild.bind({ mouseover:function(){ $.jMenu._hide($thisChild); $.jMenu._showNextChild($thisChild); } }); }); /* Actions on parents links */ $('#jMenu li a.fNiv').bind({ mouseover:function(){ var $this = $(this); var $child = $this.next(); ULWidth = $.jMenu._returnUlWidth($this); $.jMenu._closeList($("#jMenu ul")); if($child.is(':hidden')) $.jMenu._showFirstChild($this); } }); /* Close all when mouse leaves */ $('#jMenu').bind({ mouseleave : function(){ setTimeout(function(){$.jMenu._closeAll();},opts.TimeBeforeClosing); } }); }, /**************************** ***************************** jMenu Methods Below ***************************** ****************************/ /** Show the First Child Lists **/ _showFirstChild:function(el){ if($.jMenu._IsParent(el)) { var SecondList = el.next(); if(SecondList.is(":hidden")) { var position = el.position(); SecondList .css({ top : position.top + opts.absoluteTop, left : position.left + opts.absoluteLeft, width : ULWidth }) .children().css({ width: ULWidth }); $.jMenu._show(SecondList); } } else return false; }, /** Show all others Child lists except the first list **/ _showNextChild:function(el){ if($.jMenu._IsParent(el)) { var ChildList = el.next(); if(ChildList.is(":hidden")) { var position = el.position(); ChildList .css({ top : position.top, left : position.left + ULWidth, width : ULWidth }) .children().css({ width:ULWidth }); $.jMenu._show(ChildList); } } else return false; }, /**************************************/ /** Short Methods - Generals actions **/ /**************************************/ _hide:function(el){ if($.jMenu._IsParent(el) && !el.next().is(':hidden')) $.jMenu._closeList(el.next()); else if(($.jMenu._IsParent(el) && el.next().is(':hidden')) || !$.jMenu._IsParent(el)) $.jMenu._closeList(el.parent().parent().find('ul')); else return false; }, _show:function(el) { switch(opts.effects.effectTypeOpen) { case 'slide': el.stop(true, true).delay(opts.TimeBeforeOpening).slideDown(opts.effects.effectSpeedOpen, opts.effects.effectOpen); break; case 'fade': el.stop(true, true).delay(opts.TimeBeforeOpening).fadeIn(opts.effects.effectSpeedOpen, opts.effects.effectOpen); break; default : el.stop(true, true).delay(opts.TimeBeforeOpening).show(opts.effects.effectSpeedOpen, opts.effects.effectOpen); } }, _closeList:function(el) { switch(opts.effects.effectTypeClose) { case 'slide': el.slideUp(opts.effects.effectSpeedClose, opts.effects.effectClose); break; case 'fade': el.fadeOut(opts.effects.effectSpeedClose, opts.effects.effectClose); break; default : el.hide(opts.effects.effectSpeedClose, opts.effects.effectClose); } }, _closeAll:function(){ if(!$('#jMenu').isHovered()) { $('#jMenu ul').each(function(){ $.jMenu._closeList($(this)); }); } }, _IsParent:function(el) { if(el.next().is('ul')) return true; else return false; }, _returnUlWidth:function(el) { switch(opts.ulWidth) { case "auto" : ULWidth = parseInt(el.parent().outerWidth()); break; default : ULWidth = parseInt(opts.ulWidth); } return ULWidth; }, _animateText:function(el) { var paddingInit = parseInt(el.css('padding-left')); el.hover(function(){ $(this) .stop(true,true) .animate({ paddingLeft: paddingInit + opts.paddingLeft }, 100); }, function(){ $(this) .stop(true,true) .animate({ paddingLeft:paddingInit }, 100); }); }, _isReadable:function(){ if($("a.fNiv").length > 0) return true; else return false; }, _error:function(){ alert('Please, check you have the \'.fNiv\' class on your first level links.'); } }; jQuery.fn.jMenu = function(options){ if($.jMenu._isReadable()) $.jMenu.init(options); else $.jMenu._error(); }; })(jQuery); sqlkit-0.9.5/doc/static/sqlkit.css0000755000175000017500000003512111714210425016451 0ustar sandrosandro/** * Alternate Sphinx design * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl. * modified by Sandro Dentella per ReteIsi */ html, body { height: 98% } #wrap { background: none repeat scroll 0 0 #FFFFFF; margin: 0px auto; max-width: 1024px; min-width: 800px; position: relative; text-align: left; min-height: 100%; margin-top: -10px } /* #wrap { */ /* min-height: 100% */ /* } */ #document { overflow: auto; padding-bottom: 50px } /* must be same height as the footer */ #footer { position: relative; margin-top: -50px; /* negative value of footer height */ height: 50px; clear: both } /*Opera Fix*/ body:before { content: ""; height: 100%; float: left; width: 0; margin-top: -32767px } body { font-family: "Lucida Grande", "Lucida Sans Unicode", "Geneva", "Verdana", sans-serif; font-size: 13px; letter-spacing: -0.01em; line-height: 130%; text-align: center; /*background-color: #AFC1C4; */ /* background-color: #e9d56c; */ background-color: #fff; color: #333333; padding: 0; /* border: 1px solid #aaa; */ /* margin: 0px 80px 0px 80px; */ /* min-width: 740px */ } .logo { margin: 10px 10px 20px 10px } section h1 { background-color: #FDFAF3; font-size: 180%; font-weight: bold } .section h2, h2 { font-size: 150%; font-weight: normal } .section h1, .section h2, h1, h2 { font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; border-color: #E2DCC8; border-style: solid; border-width: 0pt 0pt 1px } .section h1, .section h2, .section h3, h1, h2, h3 { color: #940000 ; font-weight: bold; } a { color: #ca7900; text-decoration: none } a:hover { color: #2491CF } pre { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 0.95em; letter-spacing: 0.015em; padding: 0.5em; border: 1px solid #ccc; background-color: #f8f8f8 } td.linenos pre { padding: 0; border: 0; background-color: transparent; color: #aaa } table.highlighttable { margin-left: 0.5em } table.highlighttable td { padding: 0 0.5em 0 0.5em } cite, code, tt { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 1.05em; letter-spacing: 0.01em; font-style: normal } hr { border: 1px solid #abc; margin: 2em } tt { /* background-color: #f2f2f2; */ /* border-bottom: 1px solid #ddd; */ color: #222; font-weight: bold } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; color: #5454d8; border: 0 } tt.descclassname { background-color: transparent; border: 0 } tt.xref { background-color: transparent; font-weight: bold; border: 0 } a tt { background-color: transparent; font-weight: bold; border: 0; color: #ca7900 } a tt:hover { color: #2491CF } .field-list ul { margin: 0; padding-left: 1em } .field-list p { margin: 0 } dl { margin-bottom: 15px } dd p { margin-top: 0px } dd ul, dd table { margin-bottom: 10px } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px } .refcount { color: #060 } dt:target, .highlight { background-color: #fbe54e } dl.glossary dt { font-weight: bold; font-size: 1.1em } pre { line-height: 120% } pre a { /* color: inherit; */ text-decoration: underline } .first { margin-top: 0 !important } div.document { background-color: white; text-align: left; background-image: url(contents.png); background-repeat: repeat-x } /* div.documentwrapper { width: 100%; } */ div.clearer { clear: both } div.related h3 { display: none } div.related ul { background-image: url(navigation.png); height: 2em; list-style: none; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 0; padding-left: 10px } div.related ul li { margin: 0; padding: 0; height: 2em; float: left } div.related ul li.right { float: right; margin-right: 5px } div.related ul li a { margin: 0; padding: 0 5px 0 5px; line-height: 1.75em; color: #ca7900 } div.related ul li a:hover { color: #3CA8E7 } div.body { margin: 0; padding: 0.5em 20px 20px 3px } div.bodywrapper { margin: 0 240px 0 0; border-right: 1px solid #ccc } div.body a { /* text-decoration: underline */ } div.sphinxsidebar { margin: 0; padding: 0.5em 15px 15px 30px; width: 210px; float: right; text-align: left; /* margin-left: -100%; */ } div.sphinxsidebar h4, div.sphinxsidebar h3 { margin: 1em 0 0.5em 0; font-size: 0.9em; padding: 0.1em 0 0.1em 0.5em; color: #940000; border: 1px solid #940000; background-color: #f0ae4a } div.sphinxsidebar ul ul { padding-left: 1.5em; margin-top: 7px; list-style: none; padding: 0; line-height: 130% } div.sphinxsidebar ul ul { list-style: square; margin-left: 20px } p { margin: 0.8em 0 0.5em 0 } p.rubric { font-weight: bold } h1 { margin: 0; padding: 0.7em 0 0.3em 0; font-size: 1.5em; /* color: #11557C */ } h2 { margin: 1.3em 0 0.2em 0; font-size: 1.35em; padding: 0 } h3 { margin: 1em 0 -0.3em 0; font-size: 1.2em } h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { color: #940000; text-decoration: none } h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { display: none; margin: 0 0 0 0.3em; padding: 0 0.2em 0 0.2em; color: #aaa !important } h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { display: inline } h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, h5 a.anchor:hover, h6 a.anchor:hover { color: #777; background-color: #eee } table { border-collapse: collapse; /* margin: 0 -0.5em 0 -0.5em */ } table td, table th { padding: 0.2em 0.5em 0.2em 0.5em } div.footer { background-color: #f0ae4a; color: #86989B; /* padding: 3px 8px 3px 0; */ /* clear: both; */ /* font-size: 0.8em; */ /* text-align: right; */ /* position:absolute; */ /* bottom: 0; */ /* width: 100% */ } div.footer a { color: #86989B; text-decoration: underline } div.pagination { margin-top: 2em; padding-top: 0.5em; border-top: 1px solid black; text-align: center } div.sphinxsidebar ul.toc { margin: 1em 0 1em 0; padding: 0 0 0 0.5em; list-style: none } div.sphinxsidebar ul.toc li { margin: 0.5em 0 0.5em 0; font-size: 0.9em; line-height: 130% } div.sphinxsidebar ul.toc li p { margin: 0; padding: 0 } div.sphinxsidebar ul.toc ul { margin: 0.2em 0 0.2em 0; padding: 0 0 0 1.8em } div.sphinxsidebar ul.toc ul li { padding: 0 } div.admonition, div.warning { font-size: 0.9em; margin: 1em 40px 10px 40px; border: 1px solid #86989B; background-color: #fffdea } div.admonition p, div.warning p { margin: 0.5em 1em 0.5em 1em; padding: 0 } div.admonition pre, div.warning pre { margin: 0.4em 1em 0.4em 1em } div.admonition p.admonition-title, div.warning p.admonition-title { margin: 0; padding: 0.1em 0 0.1em 0.5em; color: white; border-bottom: 1px solid #86989B; font-weight: bold; background-color: #fe6f20 } div.warning { border: 1px solid #940000 } div.warning p.admonition-title { background-color: #CF0000; border-bottom-color: #940000 } div.admonition ul, div.admonition ol, div.warning ul, div.warning ol { margin: 0.1em 0.5em 0.5em 3em; padding: 0 } div.versioninfo { margin: 1em 0 0 0; border: 1px solid #ccc; background-color: #DDEAF0; padding: 8px; line-height: 1.3em; font-size: 0.9em } a.headerlink { color: #c60f0f !important; font-size: 1em; margin-left: 6px; padding: 0 4px 0 4px; text-decoration: none !important; visibility: hidden } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible } a.headerlink:hover { background-color: #ccc; color: white !important } table.indextable td { text-align: left; vertical-align: top } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0 } table.indextable tr.pcap { height: 10px } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2 } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer } form.pfform { margin: 10px 0 20px 0 } table.contentstable { width: 90% } table.contentstable p.biglink { line-height: 150% } a.biglink { font-size: 1.3em } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90% } ul.search { margin: 10px 0 0 20px; padding: 0 } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px } ul.search li a { font-weight: bold } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left } ul.keywordmatches li.goodmatch a { font-weight: bold } div.sidebar { margin-left: 1em; margin-top: 1em; border: 1px solid orange; padding: 1em; background-color: #FFFFCC; width: 40%; float: right; margin-bottom: 20px } div.highlight-python { display: inline-block } /* Menu */ .horizontal_menu ul { list-style-type: none; margin: 0pt; padding: 0pt; z-index: 100 } /* All' li principale metto un altezza piu piccola e il fon a bold */ .horizontal_menu .mainli { line-height: 13px; font-weight: bold; -moz-border-radius-topleft: 4px; background: url(/static/img/color_tabs_left.gif) no-repeat scroll left top #fff; color: #FFF; float: left; letter-spacing: 1px; padding: 0 0 1px 3px; text-decoration: none } /* settaggi x tt le voci di menu */ .horizontal_menu ul li { display: inline; float: left; line-height: 21px; padding: 0px; position: relative; margin-right: 4px; font-weight: normal } .horizontal_menu ul li a { color: #ffffff; display: block; padding: 1px 8px; text-decoration: none } /* Colore dei sottomenu quando ci passo sopra */ .horizontal_menu .mainli ul li a:hover { background-color: #f0e84a } .horizontal_menu ul li a.active { /* background-color: #e6fc1e; */ background-color: #f0e84a } /* Settaggi per menu principale, quello sempre visibile */ .horizontal_menu .mainfoldericon { background: #f89c22 url(/static/img/color_tabs_left.gif) no-repeat left top; -moz-border-radius-topleft: 4px; float: left; color: #ffffff; padding: 0 0 1px 3px; text-decoration: none; letter-spacing: 1px } .horizontal_menu .mainfoldericon span { float: left; display: block; background: transparent url(/static/img/color_tabs_right.gif) no-repeat right top; padding: 4px 9px 2px 6px } /* Colore del menu principale quando ci passo sopra */ .horizontal_menu ul li a:hover { background-color: #f8c929 } /* Settaggi vari per corretta visualizzazione sottomenu */ .horizontal_menu ul li ul { display: block; left: 0pt; position: absolute; top: 1em; visibility: hidden; float: none } .horizontal_menu ul li ul li { display: list-item; float: none } .horizontal_menu ul li ul li ul { left: 159px; top: 0pt } .doc_copyright { color: #fff } .slogan { padding: 0px 30px 1px 1px } #description { margin: 15px 0px 0 0; position: absolute; right: 0; top: 45px; width: 250px; font-style: italic } #description a { color: #333 } .news { /* text-align: center; */ margin-bottom: 10px; background-color: #eee; /* border-width: 0ps 1px 0px 1px; */ border-color: #ddd; padding: 5px 5px 5px 5px; border-color: #333; border-top-width: 1px; border-bottom-width: 1px } .slide h2 { -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; background: none repeat scroll 0 0 #DAD7D3; margin: 0; padding: 8px 1px 8px 8px; color: #333; font-size: 24px; line-height: 20px; margin: 0 0 8px } h1, h2, h3, h4, h5, h6 { font-weight: normal } h4 { font-weight: normal; font-size: 110%; border-color: #333333; border-style: solid; border-width: 0pt 0pt 0px } .slide { min-height: 300px; /* font-family:"Trebuchet MS",Arial,Helvetica,sans-serif; */ font-weight: normal; color: #333 } .text { width: 400px; /* display: inline; */ /* float: left; */ margin-left: 8px; margin-right: 8px; position: relative; font-size: 15px } .img { padding: 8px 5px; right: 0; text-align: center; top: 0; width: 350px; margin-right: 0; display: inline; float: right; margin-left: 8px } h1 { font-size: 150%; font-size: 26px; padding: 8px 0 2px; margin-bottom: 25px } /* .slide { */ /* padding-left: 20px */ /* } */ /* .slide ul { */ /* list-style: disc outside none; */ /* list-style: disc; */ /* margin: 0 0 0 25px; */ /* } */ /* .slide ul li { */ /* display: list-item; */ /* } */ /* .slide { padding: 10px 30px; } */ .anythingSlider .slide .text ul { list-style: disc; margin: 0 0 0 25px } .anythingSlider .slide .text ul li { display: list-item } .promo { float: left; font-size: 13px; margin: 12px -20px 20px 0; overflow: hidden; width: 770px; color: #a91819; } .box { /* color: #a91819; */ /* color: #FFFFFF; */ float: left; /* height: 118px; */ margin: 0 19px 0 0; padding: 9px 0 0 10px; /* background-color: #ddd; */ -moz-border-radius: 8px; background: url("../_static/sfondo-box.png") no-repeat scroll 0 0 transparent; } .promo h1 { color: #fff; } .promo p { color: #333; } .promo a { color: #fff; } .box1 { width: 330px; } .box2 { width: 350px; } .box h1 sname { color: #a91819; } .box sname { color: #a91819; } .slider-text { color: #fff; } sqlkit-0.9.5/doc/static/sphinx.png0000755000175000017500000012277211714210425016460 0ustar sandrosandroPNG  IHDRs+hsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwxdw}}6Ψj`\0f0%@ <@^jx@L 7pz޾*U׌ϜqfѮՆ}]S)^}meY>>>>>>>>>>Dm"[[= _H.QZմ4kB<6>>BhtaeYmz>(z{_$>Q>>>>>>> ji[]l.>>>>>>>M~/+]'jVըVmhaDbI g_\.SU1 P8L8lŚ_gJi~gP+mXRP.)T*j:-mmmDǨgseBB|\bLM0-Md(hbm8n:y iFbD""'*94fa Frţ-(- ,,A(EQhp)AAA) dboY 0ܟf ;wl!Hxk\Zh4J B!h :>>>>>>>uBryo7d,YeʥJjF< H[0 FOq("uu[8:R7A7pF G4Zx|Υt K˾J K2e" -]\{vl;i/,f)x]wy!{[Q"Vߛ@Mw#vcc WW"XYl-w[ͯnwl2jp8BGGLDCiOLx 㨚T]QwdL!dƝ]Ql$4UQ+"YYD[%QRxT"J"%t]Qu9LQ\tEBܰPuLtG5 pK[iT*v;*80v ӓn,sj(4]A|$Ig8pڈD"ȲB':>KF's~,\譼}z^>>>>>>YH61͓v56Oivf*H3K+G ^qb&=xk\.'nmz>>>>>>IHb^T*Q,j躾 Z:!I2Ph$BWWMbfv`:1I{-BͮS,$H!/l ǐ"ОM{'E82iYVSjFRZRun$KȒ؛('IڈVN(KMLsI`jdJG@RMtWT&e!X&`W+#]RE;2`A dГn=uiO:;F#(娪388Hoo/h׊=_/=lm-f>WSx0[;_$Y<X_t0V333CXl^ʒC HH$,Eʉѓ~1EZ\>{ɗj-HxʚzN6ellqjO}Su|y&8>zGɗT&5մ7mP1d -VEut]Gqh v&Ük]:4Q4,lE;g "v `( @P.T$TUsܜƙ؍{p$Lgg==5]gt|C9p$SYjVmhL11 2Wʠb p0H4&Ce0T0;<a0\q٣x S*8~8[n%x}c6[n22+,gz_?}7^w>~ >7Gkk:inLTiܼ-ZL 40 4Y\,29=,f)kI]ivKqAr dBIDvģ(TD'\- ,ǭ]1mCd]M'1554|(]#hd2IX4J(&T:/wYW} WiW`&3LMNbYl'C{{;;1<3B'l UU[դ [vMĥIBѫL7j躁aYdD_ݳ'^}x I( $ɳv%[Dd0 weLY>>>>>>>+VmC2cSg߆'pR=E2XEdI$HbpP{&Dqߵ۷08Ћe&''ba! XM[uNww74mm.bl,"rBBT9Ʊ_w;2(;tқ"I4%200+z٬'*Jb\ I: }|cVk<|$3YeAMk-,5[iUaan!:ܻ}iYibGR(b}/Oxcԩ D@ !G\q{6ֶ'R[͓^iL<{fCk<Yshii +KBr6WXV!i2.yW"T `9}A<ʲ"K Hvp{x0ٹ} M8;7I(Mc,Bub3;C:,\&grLLg99Hj%b"%b"`L<Άqڮt=Q*&rV3$dӕ`.z2]4,@ @(>9H&tvudbg;sa#:>΁#fTU$(5DP*)M.x2rEȣu!y ÞӻWFVܷkKWG}\]C\+V4jN]թ[]j 5DQ33-`&E$/gi?Ή,ӋLHDCv%x[yUEg^h }c?th Ou7{+poNбiƧZ%œ-͍מϕmYU:ɭ9"_0ȟ>uĩ,|X$H:Ko3>?__aif%N=Now<<3 e-{sjeAW[m"K׽Z(WU/H_8>stu˪}'s3s{w~y徯~nֽ#59 x]WRZjUnZDǢ$(!E" %"HP"IB\{ {w mx``ɱ)ʜ8ɩ UUggLLOMRrbGf/ Ք)aX"$-D̆x]#vVA4(H#,E/w-&w2KGGyffff躎(B!"WS.8pH$B[[]]]tuu _d ӳLN115S3LLSNJ)2̥'$Y PP! ՞=ET"!D"+(rs/|EkZ$o|tG`@>a;2ž#S|K./^pņ6-Cg**[ý3>&XuޖBri͍Uoooy>SRϺz=Bogc'V싆BwEa~~ױ 䟾t+7^w>SU=׵#u ,A;7MY|w__xz~*Ϟ{9/V}zǪW֏hkw]^|lמǿz kY}W_2WC^+ysžhB=Ƨ"HӹILqt|R͢nJ Ռ%۹SsA`" Z UC2%L'ӦQYˮ"RD-BIHTsuSD7rz0tY0PDdL'ՃD{ؽ}[bb1V(U/MR.,0=5kH$J,%HLc;Iwwk0rr9"eٹ>Ӏtm1_x ~O˷VH=`@&H\B(wo /~W;.޺ ɍ Վu:6HʲEUׂ$oYOM/>oWz5 O7'VmoKo:r r5uI0LsTU#  ZND@ $BPP/6v u-C-9Ha011'ODUKe$ z#N7 r$GG&982bՐs- ݲnA`7LdBM"E[I˱lÐ,8`qoLDA@ bAB7NRg&WF5DTSD3$4K@7 SDHݐ&8R'X3p{>EDٻsۆE 144͑f)JȲ칕@KYpخel6iA$!cEwֶ=o[$=Au*Z UUUTN foqɤה|=6xQ_?ik^o38 *"]:O}Gy;]_M^tr}"jFg{2B6_i#$mU5CQNrpdB.{^U_ren>^Kּ[׭y Xli IDATar,v0MQhY%4F:N#$7H=r56# |KfK ![Ch+/SOu|Ͻ5U|E_jz>>>>cUneC!CuJ&I%$qRI ǥ^3554SSz-dDInr"F&8tOQtՐ!;\mJ薈]m`htIP2-$lmc.zȩ2!KF@-*j!x8Slc(! `'Ǣad;/0 fMs||g)֠fu[XꦀfH`Ky= f >cӞTSVY\\X,R.4s*eYF$NKW(=-Ae`0H" J5UifϾfB;mClO8&cM4 |>O>_\.Q-0 6Mt7Ӳ0t]&e"2DrL<f9O<|6j~$OgyӋ+nI\`]hK!G/yxiY;KH&RYiFӉ.g-9^w_Ѧl%v̤^?|g{y1۶w?tVd#nϚIWH2 zBHXX,R,(E "WVgpp?\'9x$NLStW8Ɏ([N ڮ于p J&!ٴţbm7ԘcY.@.H6%RTGc 8am֥6%101 sEt  e)FI&S>f1:1CG&)u?5]@dQDEIP6 t&'6 e馭-E{{;%iFV^y$I#QtC֍!< Br!IxG>s;7>cSޖZU.vu+ww}#EWn\6ۍ@8p57(Nˁ3 ~YWhLuYb!/01_O oܖ-kC[c-/͚5s 1:a&4[(6ֹ\4nݴ5 M?c'9ptɹ"5] xѝ&# : $ AP!$D1m}O[j)dY S,1 p8D  twSNHKF@2Q$؋7 J'dLArtwu288$IB!LӤVQ*طo˴uw3<`ze},VLHU"5jȂ,  LY (?"Y$Azכ&Fx|}i""mmmr9$ps$FP;`l*F<2 {_.qVVL}7^{^K!yxlngΏֹ.AM~ vshY]y˿Ƴr[φ6:Bw:^o_wseE>c&5;toNizZi^3ň:hp$B*)]gzvI)uGC@5@5{: KdH&AB+-ŠE{.8yͫ3J"&g;54cTj5ģ-".E2(vZ6*!mc~vn'nn~癝#ϣk:]=Nw{9PEQe˲( EY'?DKl ?dm$ Q{6Ҝp+HQVT*OTK%;]]\\kQjJjnhH]=Fh!`!uD!$؏E{ (? 0Lt4L")j>r=wF>'Jٸ|7֊}qpdٍ MV6\kOFVmS,i?CX]n z.֍V}7~ xxM,h;W=3hdO}n^Ww}gMQdOsHD?Aǧ51\ZFXT*S(ڎbBi*u:/9)`v3p˙ #( 񨘄1#2fس}m[76 9CYD"tuvFD"^:v8VUU0B6k! (I$$KbVBjhD@B`Ж 9nu+hD"b&QYV)  "|c!;xu\qQ8{ɤ;BJ9sj5Bh& 5IF:n;- PY:Ht*ElJ #EB$ jEnT*y1ouGp8L<\JBTX,/XcllT2 =߽woeP}GޣL̗PQ-[T.`lebao3rv:eXG40 ˲d'&?9@?Xs%YWolk;y)o{yGy?ϻSu aYPWW:LMt ɰuVROb{96z;;(TL*v[%8N!reڗ8Mqn (*?觯GR,F+;Gt_֏S9lA<]R(UVs^ϹmH|j]WGJUsy;qf|pZ~t!n[ZzRg㳜5; \`B\jbZN)+wdHH$P@$S϶-v|,BZT(ɬY˲R6]L;.X:Ih{GR<*잠mdY_~#7h . ¡5DZYW~x?w?t&E9eM#]?P`#XTUj;+(RDbaBѠDw{;1<~F' YXR*q8tH$9` Y^$IagRO8|8vl㏮1   q-:Z DK'hՈR "5{4cccuϜn WX' /2ϳH6}dΝttsr2~j9/>l^rIt^ `'RDADA!E$% Oyҵ011A\&z-ȥO)/;mp#Ctx˯ٴsYw]qh˦,O}#sHnv|yFB } x_-j|˅ l W?^U? wIޱg#mWv y[9cȶ B$OxعmΎN8C-.FIRR)ڈDQp:a"I`d2Igg'rۓ;hwe)Z&u!.ܑ`Ȩ(Hґ:Ϲ2[O~YW]Vyi_>7{7Mt(C֙]ɑTZ(JC|=˜]\Uy;7?x[N?sC^gbnnz|{Y17 \q[z|cU4"B( #XUB\4Ox,PMӘ'C"X5}&k&צIUUjZJBZò`Ϟ=ܹpخ-uC~,,VLjfzo N3#}p@$$:R.>;];*@q 1 G O oW[|\Dkag8F0L>Z/K-yZžKIY8|6ժ*f:|=ϗ+-Wx~o~&27_J-o~U|փk43}|||Άu6V;DDALGclAiXP,L,InYX9@${ؾu-룯CZ* ϓfN#HˊVyy`T*ά355q Rz?6\ d\!)[ubnc-,,,099IWW^X{=4{WGE{b:'86E.8DnQLj biϢl2!l/d]2==M6EE}b$I7\yޓ.ಽj~z8G>ţvsSyAZ^!Wӵ{v/b_Jʑlr02|񵖍w~EDB+C.?ɯ*" /CmVޱb7l;~'~(+)ȉ) e հ; H=mR(AYer c{XPbx(ö-l ywZ\.bEi@knX|y%RV@n[E262 ;vlp(.w5w%ejrx< b1jQ(nt0i+G9yXVH$ٵk' `c~FPRuvʁ膀ae:|]P$$DPOO|E\;ZQh4Joo/h<\nVZm?}7S>.Wr\syŭ0C#ʮiK7=-+s7(|žM]3q7c-?g6\7 )d|jnOQ פi9SoL& ",m+es&(`of-μl;\STrr9hԫ^^ݽ&L,x2N^PWW+D_۶]{y~v8@*4R)Ț,8nh{[KۧpXW68ڻwqs&D U)7/'o i 󦋖/QRhXY5:RrV<)7TǞ\]j)#c+sÛW&ɣiZL6*2mN<GA<'J̫{vލi#aj7DM<ʩ'ǩ'#%ظm//l˦>2$gK .`;(d,*LP B~C2f .ŜY3 y'L,r9@ G*=Ĝw`4M 4<^l6afk/hX}d6͗I E$?0Ě y~n6iC!eȘ S!kIL ,v>ݛh&@W!C/.ͥNCkjMtb`@ @}^eb%dKR2XU.'];4}"YLC:z954dAH-;j{5nyjcA>2RJq}cgnUKP*b*LHzI}>X 4 u͌ cczjBMHg[3mͼ v ;[m Tق[$UѭG g0wf3E#ypd2I:&0rUPQ\?PUUQ5 G9 Q.PnnLN:@X,N$/Dؼg'ARXiOdm>YΓ>BT\ E391yƒcc1ۉb5C lz9qݥ+*r5+9ɿ/V"GQnp>sNޭ\ڋO2 ?OGJ>HMBɷEm,x*{`r~q2j*b:veOeuSѼLt:M*ݍmDkk+Xf鱳yswk6ᙍ80%gC.o8 G`*9G@W%~M7M04s4ֆVx|ZebY[_ҕ8-kO EGUMUEU5TUAQTTME/RyS!ɲ=ر}IJXQҖBrIi' EHtu뒘:"“Xv֭["3glx!sۗ:xH`fCe 5iSIժbEpav _ŌeƒW~,.Vz, w<3,ɋ;.H9~e߾w)>x!hT"`MUъ|-_*XX*H1Wp1SlV1rJRx%O}t׊rD'&q)5,匓4aǎt{3gR,-#&LvԷHVB4EGf}%˱P:HUeu{ʮ3,>8yG%l=7Ao}%$3xlMWv\v"ښ&?PWQEUL轗mbfKGRJ&Yz5>k@[[m3hmK;Qmsټg˓J K*JwJIJC?ZDs^np+,ct m-O=92>ڀMC}활غu+RJL ^ *m=ⲋN[0v* 5SVXGPʱb_4Ԫ㑴[joWsէY1_kJ3mII(v|w)zҲl;L}j;< Nc*qԕS,wW4, ۶ QϞ默X&)vƍDN,9u|9-Y;M0!5,|8h8RtT,CA  (WEl w(O#^L #)E&8.qnTe,ue~SU p@-lU"}Bm6>1۶Yvccc44 kkkK_N%y-_3'.j?L?<]vىǵ~cSLerQI%|1LHa; hyګܺܟ%'GO$Ƈbzs7?I}?]8:(})8a  qYuTVô^r"YI{1d%Rt$}XD\# hkW旌M.}YQ$39O|O}5$0,w˧}wFw{㪢*xщDLLHT (!^5ϟrl,I*b`={v(MĬFf7r0:f^gCW`cFο˼*$/7*@Xo "O&'I_VS9O)K)EJn: o}YX_M$芉_X(&wlA!p845֑L&Yz 5;dq—۔]PMmZ674{`o{ \z1AJxf^uÓ"^=,]zHm/gD^ڸ)|1"֧!<}(GSۛ Wtro| 5ͯ\d.W[7/ ;投KQc*bLHi^P+m.!TXի{,:[hoA<┥9ella0ر LBF#TWK H`Sc2oܖA!]QH;88XLDbW BZ%39,;ٝ-t&ehF!!sNwӃlݶ8mmm%_'&L&woy(;Xu|fͨ.Bs]!o(EP]}fz*w!kJ,#*E%"v(1FGyASf'asDĒQ"WHI*;dSHPx~מ},#cY>{'-9}zJT]t.o鷟ß\ɋ;ʦ*(&ńѫ=O*Ax(u'PO4`FR# l^كaajhoP0Hkc 5Ɛ$L.z97-ۭMSݗ>|i>zl慭0B:UP"uҳP6ԓO1{ϛW;d>\\nozIIpPis߻Dۉ|3ye'x2ž\@_UTQEUTQ+Sbfa``>ijjdhel޼MO5,Zךk 3Y8.sfϢz$nꖣI&QŽ^u%&y)m'F ! 7<<'˖-t&ˍ=0bd,)I̗֟ g `Æ 44҂O0MEi0|5\ev0,aIaddT*E (G{ޫ*8(mL&0TN:X, -۸"c[7Z" $SlC$K-,TBQB&_,L K) 刢7/(GD,a066mm̙3Ӵwd6UPlb,u,9}+.Ķm֯_O<ښ,'yh`9^jѲrCƀ [hC]] FWdE**╉DS<%3LrgD?l&2Ilre  R((BAU|_4b!ڈ3k6˥MH$#d"dz(g.)pc.q~}ì۝b8HD}9j g,m3V d AZ[[Gu2Lzs3ͼ(C֔$ /3w\v eYDQh**rH$ͱLq֙صge$iZ)AA(njEQ),F"9d|/ozAt]'|%b;' z{{FQK.?SH*#1A?xݪ3شi3ءP!DY"隶e>F Cph1khoc8pS__OMM P_.**x1HWzM$8D"ڽ~fd 1mʴPTE D؎`9^ΔLgϖUha.hhT*E(*ǼZDQݍ_dby= $ [J7Ϥ6ą * ;v0r@8FӴI?"iXy:yqqșDWJn.^si| $Iz{{f^ZWWQEUTQE/-*Nxɒ7!Dzd G>}> Ms+J hqY 9شy3t6bY|A6pGlSD&#%-le l[*-MMdU~~!4V#*dJfddΙ?>K"UU! T.¹+i'B $#5(8.ll&Wض8+xT*UdѢEԐdo`)Cj[-p9m&y%2KRDazzc۸6 " р ,Y[PheY 222RDh-*%dqQƒcX_S&NS]8=]Iܹ۶8pՕ;܆ 䀤pSR!a*%T!cٲ!GF7;pGPp4ECKOa8úu@GG;܎MʞQ[}=e8K$O_PVia\ BH$BMM `Wޮ**&ai0 4T rhnnqxv=Lۑ Maf4yӕGuzzzXz5LT*EcS l,DM PWW 742dd"(\V:{;h1QI[.q$5~헟1gaY/]JcchOsxD0 r9xHM.(t6y5ZkHP(Dcc#Pp8L8& n**Qbڶ,l6K*"RW[(Kitjc!kœ~ HR߿JL9Ă| hPU B8UfϚDIӴWdIy9"9(8aennCHHF2* !]Rph }Jfua/]K(lS"+EJgfAm׋iN?RU$~M4D5U,>v>izj `֬YB.i j**b"4(Ml.3Xq;HtJ8,^t,$g襵z|n'2l4XH> i~ ֐RRLT*Ȝ9xfF~'H F 9%5A9-A~JZd]X4Fkk ?S@UU d2YzDRH@XW +dK-m 3g,^"\\**Ci#Ja`Y|n`Z6 ꄃ: u455aYTPc1ּ>mM ٺs/DHOGs <˗.!+H瓘L&T*1 q< ħDtImc[ $)6lXOMM 3f̠p8\ ЕR;M۶q UhP!s%u6/ĀUL_ $g #Dx +ю=**_I>`.ql7lG"|*AF0ࣹ\.G4-T]1M4}}{ 8fl*wcR:D#IٵkP`}K1qKRȾ>~?+V@UUFFeݎ~҂dN`:~ b I.>8V{ ǎ;hjj#tL&|6n'cXf>FJT SN^Z#tvtL</( =!yb}787݂ҲemUTQEUecd!ciSS|x,&:F J CCC={6 466 G</3u);388Hgg'۸z#&#AG:uF-cmvimmP;Ks}/R)LӤvxv629#$A$`n[ihR@+JBDl2 ¯6dzz'8tOlA/琫*5I4:rHP@UTE)"(  GD"a8͞]8$ * R@SSSBrXr$ Tdo/itR a{2$2%Qp$U45cYk׮CJBHDz;qyQEmFds")7(JS5%L"HBt9"?FD ?FInE&{1K`fpP2ٍR?(㠐Τez2CS `Xkȑ=iBc!Nw?9_hc{ %2 vQ32D7"ʱ}def n,s80Ǟ:c`rt"4mw!ceB[Gz>M34ʌ+!}|WS S(sط ^#5f=9Wrt"X{twupldb" Rd/hY:)V:7hA} 0+H͗T!ppOJx,@DK:=(8dl6K.+O("ňF-Ȧ8O:ftt~c9!-w<} !h*D%\s~ƒI֭]G$mRHoq3%G^t"gXض' c1M7j> k^mz'ɬ?@=.fҶN?8leو`;zؽHen34R`e q0jW/{Cn\Px s?zG8 7LQ7uV}ս#Fdn 팏_xJ U'o?icskm"mliB4,@i<.vb==m^DD|}e֙6'- @QQ jglc67vBOVlzg7H=Cs^qcT*?3[ nGg:c/ԒU7a9_JpGy;Ei;۽~$O\23:7T9-o^O[ˮgQ'_=˒a9~o ꒫üsjF)#MXOoc|sP瞏o:6"3ChN`V5E%?xCz|IK͏ҼeY.rهU빟a/X> Jӱ%d?:c^+C_!~rnn8b?Dy{^ ׂsױ :Qj:QD;(M2{^6ƝzFpl~eƊɫyp =HsE#2o}uLc=q=ξgv/:nNmdo8g+WxN~ߔbQGzQhEU FGK$-*$BiP:f̘̙33g+̙Cgg'--- _rlb^HOOO{졷-u8o{>sO~]:l :9&r ~%~zzAKKK!ѷh:1˹ryH<"icࠫt0<rh'w!mXOwco|%l=̇>c_~8LeBD[vdBy>vspv=~oW+@&r7^osW1g 3C#s0)8~D濎z; .{3su8۰VL˱7aœ\VS.$_|D9ԎCca?) 1x/N Y8SB&]+Ж]:BՁZ k\;9Qsz煈4<uy(us AڨsE~(*?S Ɲ^6kˮgo3n6ܕ9~EshK8ޘ29t?O{K1h'kGz'yOPTԅ1i)R:GF"m 㳰e)کw֧Q:NcE{cm8^SWmhˮE{>`pv>8y;'߿p|eYS]xIa rd7׹sc`w>TҲUS(;shǽ9~K k/q\zs"܀2 ^Ҹps*ZɱAwy rhNJӱ}n#eXOOq>w9*d>sij>IJ)C##躎a bUS(STPQP3{(.H(CCC Yb>Mc,?{J:$2WӯP7 0PeV1&jkk=_Js|CPhguEe(2<}DP cn}J7{_bMޅ~OHbXk~Ltr[?" w_I1q}Y[i>,. o-,s!3Cg$K%wu`0bB1~ obryr7̎ Jy;"Ҍֺr/XBM8QOٮOJ]=R3}|+Pb4oeb;ދ~em"":݋'JۉW؂ۄ/hK|_(\4-Ei]^?a= };=e֚_"{7nHxcWC[v-1N<ʹ7"~u%)p=/Oh~ kL)}pZ+nD=n!؇;/oCr4#doJ|<~z&Տ#A Dl"X q'&C((`^?|s?gw~²JNɱip+ :BDZ]t;guPro-iC"{ n#l̇ pj'@i<{p}d/0G<`e((!u3>D"A0hkm.`Z69"gZ  H$ g.LT@ @0$|/K bollbdd p146o7Ka=Cc&Ƨ8ă3as?O2B!d2jjܛ]{d-9 ˶pODmrd AQdBMSM2":7$P ^TI@ ?o'@);3h}d -POH?nee6QD9#W݄:SЄ $^c IG t]w'sUE#'lW/}9kR;0GD"+q9-' 3<`ƌ%J2ɐdopo8uΙmYHFH,'GFjd1I+WPeo.Y,A;#{0fOgs.+{od!br7LG1(Q$GYxA-rwً͛G|^e̻"ڂ~_/߲0Ж\]T u5(v|i:=Fhg~}g|;-!n'E xsXHJ@snFh?Sً{ӟKXD$q}/*b>+{ϟ~` ]tV&̓Þ"i==7 /KBA;`eKwbos}祑xUչ+n9?:ht'B]xia7T^9wzcQ5$"jf5zꇠP+ ~9agDŇ|/ܦv׊1s5q( p)dx~ƉK4vHf FY7166V0oIy ,&cD^CWW"8̘1˲x'߿?C9鬅"$)bA5v2aMl޲&:;]x<^y8D؇3` jd=cQ_|*sg#`4(>_S۸݈HS`/QѸfM99փy߿ߗ#)ts`:b"9XĞQ xeoF[v+Gq?;y|*0@(dyLwaI b)+6eb3{J-fN&J(] a:gts_mٵֆ*NNaѰu/m8a>ȱRua "#h'm; V;Ya|7"TJ.q7W>/E+~l0kmo-xJnMa IDATQ Bi;ߪ6I>0~ks22R!PdhAu:D2njB08c."S^ Ʒ+w?S@&o ~Vs 5H 10H3O $H$\x.WRb}}h `4Mc˶|7q]u Ip1ô.0ǕG{ M Oɤ5s&455"SQ3 go1HZyH)0`訍eC?ӿvՇ~ 7@#I|&m):)LŦ'oSɲb`O!SnfSQ7-;⹘$n9XMRQN7{VrC;ߏ:\7!.e2g%P}h'/2?Xfp'c,(;4 |?dbyQ>PvŁ1nupl߱r.?i12ep$E(i6jb1N;T"###8/j[jɲ%ٲf 1m̾ K¼I2Ir2/yɛ$ GL $ 1HjZUqm;m[|?]#R|0 i4 X ƐL&ڊls4ÆͯcGDS2*Ғ N#2zYtl_9.cOO/Q i4B!|>+ax'e֑=}(d,|"bQ\ ch_ya+f `歃2 (0K>[٥m~B +1l0߈侫pn&hט(/;hkI2DV.żb 4qO:P\=uGMkQX0JӅMg/B1KAĠ|$>]o,D昚o}M+`2 Pn)o]7cT%O\ [r^)܇X_q?@O_ZZy.s0@t̞qz̒*{焑ΑEVG`W}&Ewsarڎ>b8vۉ.OmDd,Y`0D"L&cv9-,#Df`R``(.y8xOX} @ɺhL[oo {8 Ǯp9QpC>l`M0gn%hN$r*qΥBlSy}CsEc?( pm\) adbк6G.pכb:5~4+GR0&"к^ֵˆh_1~܅SL:+[;F*\Q$@mcsAqgԪ;׃ dZ&h?; mS(I$AG0D4Ҍ]}FȊhbKeP^gLLqggkmx,qS;=Ϻԧs{)C%˲%mN&`YPͳ}m a00~̡D5{l,PD@U ]WAXeD:dd$ Ws<(VPVP6 HZ"HVf tC@KRRl6+y"۶:\)1wC@zr_/X*XP\J$p9)!{Ow6w]0Χ]Vl0&"W$G`MW^E0&~j;qBG K0GKYZ,y\2&P;h,|Hg-ef;)9rS)ۼ,Ra EnH^? b/p短$c6)#rl3/PW =z pk~Z-U7BȬz|ῌ#yԿ;e-{֝$Bumv/M:(T$q9Wu=a#փ/K>Yto^A~x=n+;_˗1)) 1<@x,7+6l܄L&ۍ:ȲD"R)Hc1{("d-tK1`ddA?1wtt`hll>4g{$Fci&eP0s1h | |k7iZRmߎ#>}:0c v>d%IUU0ddkC\< CKSmfBuzV5OE6C<=JcKH7N1vB}ݼ'Rm䈙D1!+$vHO\os v" n2MyǴ_gs.݊ȻqWo"IbrC/Du}\$)vZu"YMTgĖ1[<*F8TBѾI0skYεnRZS%zvYжH=XyW楹Q;^$VUc UxUA|*mkGŹ azHH5TamoxN}xM#>EPsqd_o5+aҏDߒK&R3.@^+;|7;tm#+G(Y"Ѭ Q__Od`{{l'bbRAQqt$眅E 0 $I$Ix<A,Rr@l&$ tzɲ AЀP(g`;K T Y<".>o9._ >èm>E"4 H$v$r8".\Pa\.x^K?X'%*a*5NML-fo[Klqf桚N͉:ݏ-. @gY#J(Ru 9\0Y >"Ny2kI 0y]/nM8\_]bNre MZOrCM!YفIY"i'`wrpuo]C#{Bb,5Esփ|EsCmWA?h"t^"^ka@v@oX_2!N=DZ}>Nŵ]Vh.v ;wI3oz(:VyhW 30&Fn%՘P{{!y$zځW zʢvtyd)ҮQ;0*އJxH䗿GĶN 3NpKhrk ޵dBT_/OЏvB =BEҜ`0,ruz=UV, )< 2Ċ|ĥEQy~"M`؁4/?(Ĥq G10Go6'<ڿaF888$R&&&,Аu}aaf,^sE(سw|w=ų_=Gw4$bIO! уk֞.A8[oxr%XUcPel"2 g._ 0044@ PO,O-r ` :0LHVjOQ. f+sY>˲W[L mrY|r5~ݏYYbW~˪!$JAyh2 .ǣ`ϸ[yK>h #$ TUK` hmgn|ƫC"]յP1sL455Y"?zv( E("96t2 p ~.0 Apߟ&g(ڶ[MdǷMj73[[cJ g sFrTY$ZLKSkHZI2-%Hj%UPh䉞]~o)jCy]|!?K%:ޛh'V#cQd([~ 8Ӫl8?N$Nd=b$ %H2i7fEMX\~ A* V}璕dcx.bV@b-se UE(gn ̼u`N|cU-g$TÜG5?eV G(PU=!|5~ȳMB* jD{!4D`$,:׿`Nz(V-])ۑܧ*s2g]5H_]f ?u ) z’`uv!| H6k|MX5~BGWpu˰G?-$k2T$ I.$'2cF,Zg#"I*~aX1AUIapV6t^tuBOo? Ri 22 Y`Y^ȣU.ǪgbZ#IIR޿DuuhoP(oA~qv׶EbcHq]ӂO[]148fK$x-I[\!I[_D ;6,aS40"{=K.PPya0Yy (]mǾ`@{9ڮF&Mh Wmٳ>X 2^v7]#=?B `ŕ̀p닐%AC1EJ3T@So##87;+;>wW0#9 {:|ů@\Q/E1Yгzՙw`Z$e8WDB gKe6N9$ HFTG^J%#9 mD;T қ4["5Y$WCy;ܳܪj\}h7ԝgꇬ6/\ᦧ!=y!LG7/}7|P&u̽K,̬0 ~+ɭ/lսeǦ%nۈCŬ1.ݏ9Df(vE:+/8>>GΝ{ȱ mq)1rPz?{On= Fd?O@N\m,j_ ΟY|=Kk?1vdz^DҌEuuuua#֬m-x?Ľ??l|TZIgd$&$Ē\{EDi CX46ԣc\2ӧ7YW^WMC"@4xC#>x)8))H$^S׉1E>3O_Ϝ{,ge1&&нX@P__o͌쏣&Hg>G#4{޶J` egLת(H&D"0F#cd$\:Hꖭx$Y`h qx]<0@xhuֵDc)EC#u+%cJAIC04 t)Buj[ zˀEU>z$Zow@NA=zXGXqNR=Џ8q}H$b2966=zcccގUa06Ë^XBӈUeiQ9E.ȳ8 M)"\ ٌpEՠEeU a1DXV,G{[Κq} rGnhY>.Kyݣ(zzzps9 ‚9wۅ={a̞=V<8pDUvaZX08bj DžBcCnj|:lٺ<AUBR48ɾh0 %mHIMסi]sڒEeh< c! ,f4a dQ͝'\Yq>$Sh͗Mܟd B84=+vNoI8phd2%)㱪߈ ˅h4M^B}(S,슟{U;IDATYlXwyمMz b,P5##+@#EQb=Fw CY zq8u<,蘃P}.&t]!v! X%< {ֶxIJ,t] gq`ɢPUBSSSH}8pO'( ,ZLˤY1"Ja֭ؾ]ĢE p|,_˗!X=Gq@bt[hØG2Kbfk3hQ`0Fã`Y>z-)"xjO,=[X,\BNx^444|28p'%L$͗KzGGC&3 ۍ"}zz- ! ];Hf%IB+`񢅸ꊵw}`*x28pc.͝H$H$,B9Nchh==*.uЀ>+HILL1>>88B]]nw6M麶 Dtm%"̈́MSqti{B$^qi;p>ts*= Ǟx HӘ$I)Ģꂦiphԫfh4Àe0, dF,IPU,By˅6026.{2id톆444@uݻT ---6m`8pܚ0eY`L&Y%I" 5+rnX0 >/غ 8%&ti4#˲q4 DH;~VI NLL`׮]`YhjjB0r\8p'D\n3uJMӬcEQR""'c',˂eY4օr=N?$&ľ}PWWӧ>c>8p|(*4M0 κc~&Y&!D.nN>ʼFy<{"[vk8p5>r'@00LQHIKJJL9>>e1sL466" X.}8pGn^!3]ϥaoI2̊E҉4K9;p|Zk]!I8R ð41A088ɓ쏔>2]Yqt|\pI 1rFt|k*IENDB`sqlkit-0.9.5/doc/static/arrow_down.png0000644000175000017500000000031511714210425017311 0ustar sandrosandroPNG  IHDR ǴtEXtSoftwareAdobe ImageReadyqe<oIDATxl 0 C3Cd?AAQkk3`kr7DS4cO3aº-&G[ Aŧ[Ř]H.Qv ]`TGXIENDB`sqlkit-0.9.5/doc/static/jMenu.jquery.css0000644000175000017500000000347211714210425017537 0ustar sandrosandro/* .horizontal_menu { */ /* height: 20px; */ /* } */ #jMenu { /* display: table; */ margin: 0; font-weight: bold } /********************/ /** premier niveau **/ /********************/ #jMenu li { display: table-cell; /* background-color: #f89c22; */ /* margin: 0 */ padding-right: 7px } #jMenu li a { padding: 10px; background-color: #da812d; display: block; -moz-border-radius-topright: 6px; /* background-color: transparent; */ color: #fff; cursor: pointer; font-size: 13px } #jMenu li a:hover { background-color: #f8d322; color: #a91819 } /*******************/ /** second niveau **/ /*******************/ #jMenu li:hover, #jMenu li ul li:hover { color: #a91819; -moz-border-radius-topright: 6px; text-decoration: none } #jMenu li ul { display: none; position: absolute; padding: 0; margin: 0 } #jMenu li ul li { background-color: #f8d322; -moz-border-radius-topright: 6px; display: block; border-bottom: 1px solid #da812d; padding: 0 } #jMenu li ul li.arrow { background: #f5f053 url(../arrow_down.png) no-repeat center center; color: #red; height: 6px; padding: 0; border-bottom: none; padding-bottom: 10px } #jMenu li ul li a { font-size: 11px; text-transform: none; background-color: #f8d322; color: #a91819; padding: 7px; display: block; border-top: 1px solid transparent; border-bottom: 1px solid transparent } #jMenu li ul li a.isParent { background: #f8d322 url(../arrow_right.png) no-repeat right center; color: #a91819 } #jMenu li ul li a:hover { background-color: #f5f053; -moz-border-radius-topright: 6px; color: #a91819; border-top: 1px solid #322f32; border-bottom: 1px solid #322f32 } sqlkit-0.9.5/doc/static/AnythingSlider/0000755000175000017500000000000011714210425017347 5ustar sandrosandrosqlkit-0.9.5/doc/static/AnythingSlider/js/0000755000175000017500000000000011714210425017763 5ustar sandrosandrosqlkit-0.9.5/doc/static/AnythingSlider/js/jquery.easing.1.2.js0000644000175000017500000001122511714210425023405 0ustar sandrosandro/* * jQuery EasIng v1.1.2 - http://gsgd.co.uk/sandbox/jquery.easIng.php * * Uses the built In easIng capabilities added In jQuery 1.1 * to offer multiple easIng options * * Copyright (c) 2007 George Smith * Licensed under the MIT License: * http://www.opensource.org/licenses/mit-license.php */ // t: current time, b: begInnIng value, c: change In value, d: duration jQuery.extend( jQuery.easing, { easeInQuad: function (x, t, b, c, d) { return c*(t/=d)*t + b; }, easeOutQuad: function (x, t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, easeInOutQuad: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }, easeInCubic: function (x, t, b, c, d) { return c*(t/=d)*t*t + b; }, easeOutCubic: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t + 1) + b; }, easeInOutCubic: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; }, easeInQuart: function (x, t, b, c, d) { return c*(t/=d)*t*t*t + b; }, easeOutQuart: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }, easeInOutQuart: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; }, easeInQuint: function (x, t, b, c, d) { return c*(t/=d)*t*t*t*t + b; }, easeOutQuint: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t*t*t + 1) + b; }, easeInOutQuint: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; }, easeInSine: function (x, t, b, c, d) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; }, easeOutSine: function (x, t, b, c, d) { return c * Math.sin(t/d * (Math.PI/2)) + b; }, easeInOutSine: function (x, t, b, c, d) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; }, easeInExpo: function (x, t, b, c, d) { return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, easeOutExpo: function (x, t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, easeInOutExpo: function (x, t, b, c, d) { if (t==0) return b; if (t==d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; }, easeInCirc: function (x, t, b, c, d) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; }, easeOutCirc: function (x, t, b, c, d) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, easeInOutCirc: function (x, t, b, c, d) { if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; }, easeInElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; }, easeOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; }, easeInOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; }, easeInBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*(t/=d)*t*((s+1)*t - s) + b; }, easeOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, easeInOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; }, easeInBounce: function (x, t, b, c, d) { return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; }, easeOutBounce: function (x, t, b, c, d) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } }, easeInOutBounce: function (x, t, b, c, d) { if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; } });sqlkit-0.9.5/doc/static/AnythingSlider/css/0000755000175000017500000000000011714210425020137 5ustar sandrosandrosqlkit-0.9.5/doc/static/AnythingSlider/css/theme-cs-portfolio.css0000644000175000017500000001137611714210425024401 0ustar sandrosandro/* AnythingSlider v1.41 cs-portfolio By Curtis Scott (http://www.curtisscott.com/portfolio.html) updated to work with the new themes */ /*** Note: the nav-cs-portfolio.png used for navigation and slideshow buttons uses a semi-transparent png, through which the background color is seen... so colors set will turn out darker than normal ***/ /****** SET COLORS HERE *******/ /* Default/Acitve State */ div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a { background-color: #fff; } div.anythingSlider-cs-portfolio .thumbNav a:hover, div.anythingSlider-cs-portfolio .thumbNav a.cur, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a:hover, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a.cur { background-color: #0d5c9f; } div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover { background-color: #080; } div.anythingSlider-cs-portfolio .start-stop:hover { background-color: #0f0; } div.anythingSlider-cs-portfolio .start-stop.playing { background-color: #a00; } div.anythingSlider-cs-portfolio .start-stop.playing:hover { background-color: #f00; } /* Overall */ div.anythingSlider-cs-portfolio { /*margin-bottom: 50px; add 50px to fit bar below the slider */ } div.anythingSlider-cs-portfolio .anythingWindow, div.anythingSlider-cs-portfolio.activeSlider .anythingWindow { border: 0; } /* Navigation Arrows */ div.anythingSlider-cs-portfolio .arrow { display: block; bottom: -43px; position: absolute; z-index: 100; } div.anythingSlider-cs-portfolio .arrow a { display: block; bottom: 50px; position: absolute; height: 35px; width: 35px; outline: 0; background: url(../images/arrows-cs-portfolio.jpg) no-repeat; text-indent: -9999px; } div.anythingSlider-cs-portfolio .forward { right: 70px; } div.anythingSlider-cs-portfolio .back { left: 50px; } div.anythingSlider-cs-portfolio .forward a { background-position: left top; } div.anythingSlider-cs-portfolio .back a { background-position: right top; } div.anythingSlider-cs-portfolio .forward a:hover, div.anythingSlider-cs-portfolio .forward a.hover { background-position: left bottom; } div.anythingSlider-cs-portfolio .back a:hover, div.anythingSlider-cs-portfolio .back a.hover { background-position: right bottom; } /* Navigation Links */ div.anythingSlider-cs-portfolio .anythingControls { background: url(../images/bg-cs-portfolio.jpg) repeat-x bottom center; height: 50px; margin: 0 auto 50px auto; text-align: center; z-index: 100; } div.anythingSlider-cs-portfolio .thumbNav { float: none; margin: 0; z-index: 100; } div.anythingSlider-cs-portfolio .thumbNav li { display: inline; } div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a { display: inline-block; width: 18px; height: 15px; margin: 20px 5px 0 0; padding: 0; text-indent: -9999px; outline: 0; border: 0; } div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a, div.anythingSlider-cs-portfolio .thumbNav a:hover, div.anythingSlider-cs-portfolio .thumbNav a.cur, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a:hover, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a.cur { background-image: url(../images/nav-cs-portfolio.png); background-position: center top; background-repeat: no-repeat; } /* slider autoplay right-to-left, reverse order of nav links to look better */ div.anythingSlider-cs-portfolio.rtl .thumbNav a { float: right; } /* reverse order of nav links */ div.anythingSlider-cs-portfolio.rtl .thumbNav { float: left; } /* move nav link group to left */ /* div.anythingSlider-cs-portfolio.rtl .start-stop { float: right; } */ /* move start/stop button - in case you want to switch sides */ /* Autoplay Start/Stop button */ div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover { margin: 18px 50px 0 0; padding: 0; display: inline-block; text-align: center; width: 18px; height: 18px; z-index: 100; text-indent: -9999px; border: 0; } div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover, div.anythingSlider-cs-portfolio .start-stop:hover, div.anythingSlider-cs-portfolio .start-stop.playing, div.anythingSlider-cs-portfolio .start-stop.playing:hover { background-image: url(../images/nav-cs-portfolio.png); background-position: center bottom; background-repeat: no-repeat; } /* Extra - replace defaults */ div.anythingSlider-cs-portfolio { /* padding: 0 23px 50px 0; */ padding: 25px 30px 90px 30px; background-color: #444; } div.anythingSlider-cs-portfolio pre { padding: 0; background-color: #444; border: 0; } div.anythingSlider-cs-portfolio h2 { color: #eee; } div.anythingSlider-cs-portfolio tt { color: #eee; } sqlkit-0.9.5/doc/static/AnythingSlider/css/anythingslider.css0000644000175000017500000001126511714210425023702 0ustar sandrosandro/* AnythingSlider v1.4.1+ Default (base) theme By Chris Coyier: http://css-tricks.com with major improvements by Doug Neiner: http://pixelgraphics.us/ based on work by Remy Sharp: http://jqueryfordesigners.com/ */ /******* SET DEFAULT DIMENSIONS HERE ********/ div.anythingSlider { width: 700px; height: 390px; margin: 0 auto; overflow: hidden; /* needed for Opera and Safari */ } /****** SET COLORS HERE *******/ /* Default State */ div.anythingSlider .thumbNav a.cur, div.anythingSlider .thumbNav a { background: #777; color: #000; } div.anythingSlider .anythingWindow { border-top: 3px solid #777; border-bottom: 3px solid #777; } div.anythingSlider .start-stop { background-color: #040; color: #fff; } div.anythingSlider .start-stop.playing { background-color: #800; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { color: #ddd; } /* Active State */ div.anythingSlider.activeSlider .anythingWindow { border-color: #7C9127; } div.anythingSlider.activeSlider .thumbNav a.cur, div.anythingSlider.activeSlider .thumbNav a { background-color: #7C9127; } div.anythingSlider .start-stop { background-color: #080; color: #fff; } div.anythingSlider .start-stop.playing { background-color: #d00; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { color: #fff; } /**** DO NOT CHANGE BELOW THIS LINE ****/ /* anythingSlider viewport window */ div.anythingSlider .anythingWindow { overflow: hidden; position: relative; width: 100%; height: 100%; } /* wrapper: 45px right & left padding for the arrows, 28px @ bottom for navigation */ div.anythingSlider { position: relative; padding: 0 45px 28px 45px; } /* anythingSlider base UL */ ul.anythingBase { background: transparent; list-style: none; position: absolute; top: 0; left: 0; margin: 0; padding: 0; } ul.anythingBase li.panel { background: transparent; display: block; overflow: hidden; float: left; padding: 0; margin: 0; } /* Navigation Arrows */ div.anythingSlider .arrow { top: 50%; position: absolute; display: block; } div.anythingSlider .arrow a { display: block; height: 120px; margin: -60px 0 0 0; width: 45px; text-align: center; outline: 0; background: url(../images/arrows-default.png) no-repeat; text-indent: -9999px; } div.anythingSlider .forward { right: 0; } div.anythingSlider .back { left: 0; } div.anythingSlider .forward a { background-position: 0 -40px; } div.anythingSlider .back a { background-position: -88px -40px; } div.anythingSlider .forward a:hover, div.anythingSlider .forward a.hover { background-position: 0 -240px; } div.anythingSlider .back a:hover, div.anythingSlider .back a.hover { background-position: -88px -240px; } div.anythingSlider .forward.disabled { display: none; } /* disabled arrows, hide or reduce opacity: opacity: .5; filter: alpha(opacity=50); */ div.anythingSlider .back.disabled { display: none; } /* Navigation Links */ div.anythingSlider .anythingControls { outline: 0; } div.anythingSlider .thumbNav { margin: 0; } div.anythingSlider .thumbNav li { display: inline; } div.anythingSlider .thumbNav a { font: 11px/18px Georgia, Serif; display: inline-block; text-decoration: none; padding: 2px 8px; height: 18px; margin: 0 5px 0 0; background-image: url(../images/cellshade.png); background-repeat: repeat-x; text-align: center; outline: 0; border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px; } div.anythingSlider .thumbNav a:hover { background-image: none; } /* slider autoplay right-to-left, reverse order of nav links to look better */ div.anythingSlider.rtl .thumbNav a { float: right; } /* reverse order of nav links */ div.anythingSlider.rtl .thumbNav { float: left; } /* move nav link group to left */ div.anythingSlider.rtl .anythingWindow { direction: ltr; unicode-bidi: bidi-override; } /* div.anythingSlider.rtl .start-stop { float: right; } */ /* move start/stop button - in case you want to switch sides */ /* Autoplay Start/Stop button */ div.anythingSlider .start-stop { background-image: url(../images/cellshade.png); background-repeat: repeat-x; background-position: center top; padding: 2px 5px; width: 40px; text-align: center; text-decoration: none; float: right; z-index: 100; outline: 0; border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { background-image: none; } div.anythingSlider, div.anythingSlider .anythingWindow, div.anythingSlider .thumbNav a, div.anythingSlider .arrow a, div.anythingSlider .start-stop { transition-duration: 0; -o-transition-duration: 0; -moz-transition-duration: 0; -webkit-transition-duration: 0; }sqlkit-0.9.5/doc/Makefile0000644000175000017500000000635611714210425014606 0ustar sandrosandro# Makefile for Sphinx documentation # VER = 0.9.5 DEBVER = 0.8.6.1-1 WINVER = 0.8.6.1 LNXVER = 0.9-rc1 # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d .build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html web pickle htmlhelp latex changes linkcheck all: html help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " pickle to make pickle files (usable by e.g. sphinx-web)" @echo " htmlhelp to make HTML files and a HTML help project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview over all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" clean: -rm -rf .build/* html: mkdir -p .build/html .build/doctrees $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) .build/html @echo @echo "Build finished. The HTML pages are in .build/html." @perl -i -p -e 's/DEBVER/$(DEBVER)/g' .build/html/misc/download.html @perl -i -p -e 's/WINVER/$(WINVER)/g' .build/html/misc/download.html @perl -i -p -e 's/LNXVER/$(LNXVER)/g' .build/html/misc/download.html @perl -i -p -e 's/VER/$(VER)/g' .build/html/misc/download.html @perl -i -p -e 's/VER/$(VER)/g' .build/html/misc/tutorial.html pickle: mkdir -p .build/pickle .build/doctrees $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) .build/pickle @echo @echo "Build finished; now you can process the pickle files or run" @echo " sphinx-web .build/pickle" @echo "to start the sphinx-web server." web: pickle htmlhelp: mkdir -p .build/htmlhelp .build/doctrees $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) .build/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in .build/htmlhelp." latex: mkdir -p .build/latex .build/doctrees $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) .build/latex @echo @echo "Build finished; the LaTeX files are in .build/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: mkdir -p .build/changes .build/doctrees $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) .build/changes @echo @echo "The overview file is in .build/changes." linkcheck: mkdir -p .build/linkcheck .build/doctrees $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) .build/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in .build/linkcheck/output.txt." rsync: html rsync -r --delete --progress --stats .build/html/ www3:docs/sqlkit/ pdf: latex cd .build/latex; make all-pdf Print -s -o "-b 20" .build/latex/sqlkit.pdf gv --orientation=seascape .build/latex/sqlkit.2.ps galeon: galeon .build/html/index.html firefox: firefox .build/html/index.html chtml: clean html refs: find -name \*rst -exec grep -oE '\.\. \_([^:]+):\s*' {} \; css: cp static/*css ./.build/html/_static/ cp ./static/AnythingSlider/css/page.css ./.build/html/_static/AnythingSlider refresh: touch misc/tour.rst misc/download.rst misc/sqledit.rst make sqlkit-0.9.5/doc/sqlkit/0000755000175000017500000000000011714210425014443 5ustar sandrosandrosqlkit-0.9.5/doc/sqlkit/totals.rst0000644000175000017500000000262211714210425016505 0ustar sandrosandro.. _totals: ======== Totals ======== Numeric columns of a Table have the option to show totals and subtotals. Subtotals are determined by brake fields/functions. This may be done interactively from the column menu or from code. The following example is very stupid but... you get the feeling!:: t = SqlTable('movie', dbproxy=db, order_by='director_id') t.totals.add_break('director_id') t.totals.add_total('year') .. image:: ../img/totals.png Personalization and colors ========================== Totals are generated by a class ``sqlkit.widgets.table.totals.Totals`` that can be inherited and modified to set different total behavior. The colors are defined in a class in the same module ``TotalObj`` whose method ``set_value_and_colors`` can be used to personalize colors and markup of the cell. Dates ===== Since date breaks are probably very common a function makes it easy to brake on dates. Totals... with no totals! ========================= If you only need subtotals and not totals you can prevent totals using option ``hide_total`` when declaring the column to sum. Signals ======= :computed: this signal is emitted when the total is computed. The signature of the callback function is: .. function:: computed_cb(total): :param sqlwidget: the total instance that emitted the signal API --- .. automodule:: sqlkit.widgets.table.totals sqlkit-0.9.5/doc/sqlkit/validation.rst0000644000175000017500000000424711714210425017336 0ustar sandrosandro.. _validation: =========== Validation =========== Validation is very important and is mainly accomplished via :ref:`hooks`: ``on_validation`` and ``on_field_validation__field_name`` are expressly created for that with all the variants for related tables. There are many different snippets in the demo related to validation and hooks: they are part of the documentation. Validation Errors ------------------ The validation has the following steps: 1. Each field in ``gui_fields``, i.e. all fields that have a graphical representation are looked for the value and a hook named ``on_field_validation__field_name`` -if present- is run . .. note:: At present fields representing m2m or o2m relations (e.g. *movies* in a *director* class) are not searched for ``on_field_validation__field_name`` hook. You can use ``on_validation`` hook to set validation on relations Each method of the hooks class can raise a ValidationWarning directly that is caught by the main validation loop, and populates ``validation_error`` dict or can feed this dict via the sqlwidget's method :meth:`add_validation_error ` or :meth:`add_not_null_error ` This operation is repeated for each related process and the main ``validation_error`` dict is updated. 2. If the ``validation_error`` dict has collected some errors a *ValidationErrorDialog* is presented to the user:: class Hooks(object): def on_field_validation__year(self, mask, field_name, field_value, field): if field_value > 2020: raise ValidationError("Hey: how can you know the future!") t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) that would raise a ValidationDialog as follows: .. image:: ../img/year.png Validation Warnings ------------------- In a similar way you can raise/add a Validation Warning that will warn with a message but will not abort the process. *ValidationWarningDialog* is presented after *ValidationErrorDialog* if both are needed. sqlkit-0.9.5/doc/sqlkit/printing.rst0000644000175000017500000000307711714210425017036 0ustar sandrosandro========== Printing ========== Sqlkit adds printing capability throught :ref:`oootemplate` module that in turn uses templates created with Openoffice.org_ and very simple syntax. In this module 'print' is used in a loose way. In all situations 'print' means producing a printable file, it can be an ``.odt`` file or a ``.pdf`` one. .. note:: In a network environment you'll probably use a **remote** server that means the file will not be generated locally. Openoffice 3.1 comes with python2.6 interpreter and uno module even under Windows. So that there's no problem using that interpreter or using a different interpreter but pointing to it's modules. If you need to have uno modules under windows you can follow instructions in this tutorial_ or on stackoverflow_ .. automodule:: sqlkit.misc.printing .. _psignals: Signals ======== :context-ready: the context has been prepared. You can connect to this signal to add element to the context. .. function:: context_ready_cb(printtool, context, template_name, sqlwidget): :param printer: the printing.PrintTool object that emitted the signal :param context: the context :param template_name: the template name that is rendered :param sqlwidget: the sqlwidget .. _oootemplate: http://oootemplate.argolinux.org .. _Openoffice.org: http://www.openoffice.org .. _stackoverflow: http://stackoverflow.com/questions/4270962/using-pyuno-with-my-existing-pythonn-installation .. _tutorial: http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=36370&p=166783 sqlkit-0.9.5/doc/sqlkit/filters.rst0000644000175000017500000000733311714210425016653 0ustar sandrosandro.. _filters: ========= Filters ========= .. image:: ../img/filter-panel.png :align: left Filtering is a very powerful feature of sqlkit. Any single field in a SqlWidget may become a filter criteria and many filters on a field are accepted. A filter panel handles the filters that may also belong to related table: in that case a join will be built when selecting (read the note on selecting through joins in :ref:`relationships`) As you can see in the image a filter panel gives also the opportunity to limit the query. Any filter can be enabled or disabled by clicking on it's toggle. The result of a filter operation is shown differently in Table or Mask: Table shows the result directly, Mask shows the list of selected records in the Filter Panel's Output page: .. image:: ../img/filter-output.png :align: right Each record is shown with it's __str__ representation that can be set in the way described in :ref:`description`. In this Output Page it's possible to set a field and have records grouped by that field:: t.filter_panel.tree = 'field_name' Filtering Foreign Keys ====================== Filtering works also with foreign keys. In that case the filter acts on the filter that represents the record, what I call the :ref:`"search" ` field of the record. In this case the operator used for the search defaults to regexp/match that in turn uses different operators in each database backend: '~*' for postgresql, REGEXP for mysql and ILIKE for the others (well, ILIKE is not present in sqlite but it uses the sqlalchemy implementation of ilike) adding leading and trailing '%' symbols. Adding Filters programmatically =============================== Filters can be added programmatically via method ``add_filter`` that uses django_like syntax, of interactively. As an example:: t.add_filter(numeric_field__gte=5, boolean_field=True) t2.add_filter(date_field__lte='y', string_field__null=False) more examples can be found in the :ref:`constraints` sections as they share the same syntax. Note that a filter can be changed by the user while a constraint is invisible to him. filters and join ---------------- When filtering programmatically on a join you must use the field_name as known by the mapper, i.e. composition of table_name + field_name. Look demo on join too see how it works:: t = SqlTable(tables="movie director", dbproxy=db ) t.add_filter(director_nation='IT') # NOTE director_nation Here the field *nation* of table *director* is referenced as ``director_nation`` Expressions =========== Filter work just in the same way for real column as for expressions. Example 30 in the demo shows how to create a mapper that have a column with the number of film of a director, and you can verify that constraints and filter work on that table just as any normal column:: class Director2(object): pass ## create the mapper as suggested in the SqlAlchemy tutorial... m = mapper(Director2, model.Director.__table__, properties={ 'film_number': column_property( select( [func.count(model.Movie.__table__.c.id)], model.Director.__table__.c.id == model.Movie.__table__.c.director_id ).label('film_number') ) } ) field_list = "last_name, first_name, nation, film_number" t = SqlTable(m, field_list=field_list, dbproxy=db) t.add_filter(film_number=3) t.add_constraint(film_number__lt = 5) .. _date_filters: Date filters ============= Date filters deserve a special chapter. It's very common the need for a filter based on relative dates (i.e.: the beginning of the month, the year, the last month and so on), that's the only way to allow saving queries that will behave the same all the time. .. automodule:: sqlkit.misc.datetools .. automodule:: sqlkit.widgets.common.sqlfilter sqlkit-0.9.5/doc/sqlkit/defaults.rst0000644000175000017500000000360211714210425017005 0ustar sandrosandro.. _defaults: ========== Defaults ========== Defaults are a handy way to avoid typing too much. Defaults are handled by sqlkit.db.defaults.Default class:: from sqlkit.db import defaults def_visita = defaults.Defaults('cliente_visita', metadata=db.metadata, local=False) def_visita.set_defaults( {'data' : def_visita.now, 'telefonata' : True}) def_visita.set_default( title='Write a title...', duration=90) def_visita.fk('director_id', 'first_name', 'Olmi') *Defaults* class requires a table name and a metaclass to know where to autoload/read table definition. Defaults can be local to a sqlwidget or global to the application. set_default =========== The main method is ``set_defaults`` that requires a dictionary as argument with *field_name* as keys and *field defaults* as value. Alternatively ``set_default`` accepts keyword args. fk === Default in foreign key are set literally via ``fk``. It will follow the reference and find the correct value now & today ============= attributes ``now`` and ``today`` will be substituted appropriately when creating the field SA defaults & server defaults ============================= At the moment Sqlalchemy defaults are handled only in case of a fixed value (not a callable) nor defaults defined in the server. Adding a new record with an unhandled default will result in an empty value. .. _local_defaults: Defaults local to the application ================================= Sqlwidgets have an attribute called ``defaults`` that is an instance of ``sqlwidget.db.defaults.Defaults`` already correctly instantiated that allows to set defaults local only to the instance of sqlwidget they belong to. You can use such a default in any of the following ways:: m = SqlMask('movie', ...) m.defaults.set_default(actor=False) m.defaults.set_defaults( {'title' : 'write your preferred...'} ) m.defaults.fk('director_id', 'first_name', 'Fellini') sqlkit-0.9.5/doc/sqlkit/relationship.rst0000644000175000017500000001422211714210425017677 0ustar sandrosandro.. _relationships: ================ Relationships ================ Sqlkit makes editing of data that have relation as one2many or many2many very simple. It's as easy as adding a field in the layout of the SqlMask:: lay = """first_name last_name o2m=movies - - - """ One2Many in a SqlMak ===================== This is a very powerful feature of ``sqlkit`` that is built on top of ``sqlalchemy``'s mechanism of properties and session and on the ability of ``layout`` to let you define a widget in a symbolic way. In the following example we will use a ``SqlMask`` to represent ``Directors`` and a ``SqlTable`` to represent films, connected to the first by a OneToMany relationship. Suppose you already define a couple of tables in sqlalchemy, we will do it using elixir_ that slightly simplifies the syntax, in the package the demo uses declarative layer instead:: class Director(Base): __tablename__ = 'director' id = Column(Integer, primary_key=True) last_name = Column(String(60), nullable=False) first_name = Column(String(60), nullable=False) nation = Column(String(6)) movies = relation('Movie', backref='director', cascade='all, delete-orphan',) class Movie(Base): __tablename__ = 'movie' id = Column(Integer, primary_key=True) title = Column(String(60), nullable=False) image = Column(String(250), info={'render' : 'image', 'base_dir' : './images', 'thumbnail_size' : (30,30)}) description = Column(String(512)) year = Column(Integer()) date_release = Column(Date()) director_id = Column(Integer, ForeignKey('director.id'), nullable=False, info={'attach_instance': 'director'}) To get a mask that: 1. lets you see director and films together 2. lets you edit them adding and deleting at will You just have to start the SqlMask like this:: lay = """first_name last_name nation o2m=movies - - - """ SqlMask(model.Director,layout=lay, ...) And you will get a mask that looks like this: .. image:: ../img/o2m.png delete behavior ---------------- We leave to sqlalchemy the responsibility to decide if a film must be deleted from the database or must just updated to set the director_id to NULL. To change this behavior you must act on the cascade property of the relation (or directly on the database definition). If yo want to delete a film in stead of just setting the property to NULL you can define the table in this way:: class Director(elixir.Entity): ... movies = elixir.OneToMany('Movie', inverse='director', cascade="all, delete-orphan" ### note this line ) ... .. _elixir: http://elixir.ematia.de Filtering ----------- It is now possible to filter on a field present in related tables, e.g.: filtering all films that have a certain genre. .. note:: Starting from 0.8.6-pre2 filters on a related table are *aliased* (see `sqlalchemy docs`_). This is relevant if you filter on related table via different path (e.g: filtering staff and manager of a table projects, where both staff and managers are a m2m relation to User). There are cases where this is really what you want but in some cases this is not. Here you really need to deeply understand what is happening underneath at the SQL level. As an example if you have a sqlmask of directors with a related table of films, if you filter on films and use a filter on title (containing 'la') and a filter on year (> 2005), the two filters will not be related to the same film: you'll get all directors that have a film with 'la' in the title and (possibly a different) film produce after 2005. This may or may not be what you want... Many2many relationship ========================= many2many relationship are as easy as one2many. Once again sqlalchemy does the heavy job. Definition is as simple as:: lay = """year title m2m=genres - - - """ tm = sqlkit.SqlMask(Movie.mapper, metadata=__metadata__, session=elixir.session,layout=lay) lay = """ name m2m=movies - """ tm2 = sqlkit.SqlMask(Genre.mapper, metadata=__metadata__, session=elixir.session,layout=lay) and will pop up a couple of windows as the following, showing the same data from the two different main tables: movies with their genres and genres with they're movies. .. image:: ../img/m2m.png adding & completion ------------------- .. note:: .. versionadded:: 0.8.4 When using :ref:`completion` in a m2m table, adding from completion behaves differently that adding from m2o in that it requires the field to be already present and does not allow to edit it This behavior can be changed setting it's 'm2m_editable' property to True (new in 0.8.4):: t.related.genres.set_editable(True) .. 2. it complains if it cannot get one single element with that value Many2One or ForeignKey ====================== Many2One is a simpler case. The table we start from **has** a field that holds a ForeignKey, we just need to follow it to know the value. This again happens with no effort at all. In this case it's also possible to use this field in a filter selection. Options ========== You can set the field_list directly from the layout as well as the number of rows:: m2m=actors:5:first_name,last_name will set a 5 rows table, and a field list of ``first_name``, ``last_name``. The real dimension of the table depends also on the expand attributes of the containers. you may need to set them to ``gtk.EXPAND|gtk.FILL`` by hand. There's an example that demonstrates it. Behind the scenes =================== The way sqlkit understands that ``movies`` is an entry point for a relationship is that it analyzes the ``mapper``, looks for a property with that name and realizes that it's a PropertyLoader. That means that such an entry point has been put there by a ``relation``. .. _`sqlalchemy docs`: http://www.sqlalchemy.org/docs/05/sqlexpression.html?highlight=alias#using-aliases sqlkit-0.9.5/doc/sqlkit/contents.rst0000644000175000017500000000043711714210425017036 0ustar sandrosandro======= SQLKit ======= .. sidebar:: Release This documentation is relative to the **last mercurial release**. Documentation for your release is in :file:`doc` directory .. toctree:: :maxdepth: 2 widgets browsing editing advanced/contents ../printing/contents sqlkit-0.9.5/doc/sqlkit/completion.rst0000644000175000017500000002712711714210425017357 0ustar sandrosandro.. _completion: ============ Completion ============ .. image:: ../img/completion.png :align: right Completion is the way you can avoid writing too much if the system already has your data. It means you write some text that will be used as filter in a select statements of all possible values. Completion is active * in text fields * in foreign key fields * in many2many fields with :meth:`set_editable(True) ` This operation has the following main actors: 1. the **search field**: the field where what you type is searched for. In the image, 'f' is the typed text that will be searched for in the field *last_name* that is the *search_field*. The letter 'f' will be used to filter the output. In this example the first_name (Roberto, Federico, Françoise) is not part of the search, but is used to better represent the possible matches. 2. the **object that should be returned**: * the *string* to be used (as in enum mode, or in completion in values used in the same column). This is only available for char/text fields No validation is done by default on this field, the completed text can be further edited. * the *foreign key* (frequently an ``id``) along with a *representation of the referenced record* (e.g.: the fullname of a director). A check is made: there's no way to input a text that is not matched on the remote field (see :ref:`description` below). * a *complete record* as when we edit a relation and we use a filter on one field but we aim at setting a complete record. E.g.: we select an actor from a list and we may be filtering the name or the nation. See example 40d. 3. the **representation of the result**: when using completion on a ForeignKey or on an m2m relation, the representation of the possible values needs to be taken. In the example of the image 'Fellini' the the representation of the referenced record that was referenced by a numeric id. 4. the **operator** used when searching: normally ``regexp`` or ``like``. In the image here you see that 'Truffault' is returned that does not start with 'F' but *contains* 'f'. The match of the completion has these *modes*: :start: partially written text is used as a filter and completion must match from the start of the field. It uses LIKE operator :regexp: the match is done via regular expression if db supports it or via LIKE operator adding ``%`` on both sides :enum: any value matches. All possible values are shown. This mimics an enum field. When there are only few values this may result more natural than the others Since completion implies a search that may return *many* records, it's only triggered on demand. Normal binding are: :shift Return: triggers 'start' mode completion :control Return: triggers 'regexp' mode completion. This is also triggered pressing the down arrow of a foreign key :shift control Return: triggers 'enum' mode Pressing ``Alt`` along with the other keys will prevent any :ref:`filter_completion` .. _description: foreign key description & search field ====================================== When displaying data (and data shown by a completion are no exception), a foreign key is substituted by a more descriptive text. Let's see how to customize it. Make sure you understood the limitation on ForeignKey expressed in :ref:`basic_limitations` You can customize the way a record is represented by: * using :class:`sqlkit.db.utils.TableDescr` class * writing configuration info in a database table (normally ``_sqlkit_table``) sqlkit.db.utils ---------------- You would use module sqlkit.db.utils and create an object :ref:`TableDescr`:: from sqlkit.db.utils import TableDescr utils.TableDescr('movie', format='%(title)s - %(year)s', metadata=db.metadata) metadata is necessary so that TableDescr knows where to go and auto-load Table to introspect it if you don't provide the search_field (see below) .. _sqlkit_table: _sqlkit_table -------------- It's possible to write in the database the format string to be used in the table. A table called ``_sqlkit_table`` is searched for (firebird backend doesn't allow leading '_' in table names so '_' is stripped for it). :search_field: The value of this field is used for the search. If no such field was defined, the first char field of the tale is used, if it exists. :format: the value of this field is used to represent the record, e.g.: "%(title)s %(year)s" You can easily edit this table using :ref:`sqledit` format and __str__ ------------------- When SqlWidget creates a class on the fly it looks for the 'format' field to add a __str__ method to the class, so that this representation is used whenever suitable (e.g.: when a filter action is performed in a Mask) .. _autostart: Autocompletion ============== You can force completion to start after n chars has been entered setting the completion object ``autostart`` value:: t = SqlMask('movie', dbproxy=db) t.completions.director_id.autostart = 2 Completion will be recalculated every time the written string is shorter that the last text that triggered the completion. Take care not to use a little value for ``autostart`` on large tables. completion and Return in Tables -------------------------------- There is another situation in which a completion is started automatically. When editing a foreign key (or an m2m): if a ``Return`` is hit, a select is issued to check if it's a valid value and if not that value is used as base for completion. The difference from triggering a normal completion is that if a valid value is found, no further completion is done. .. warning:: Since completion uses the already written chars to filter possible solution, if you further delete such chars you are not seeing all the real possible solutions but only the already retrieved ones. You can request a new completion... Group_by ======== There is also en easy possibility to add grouping of completion via a foreign_key attribute. It's enough to set the group_by attribute of completion_group_by:: t.completions.director_id.group_by = 'nation' .. image:: ../img/completion_group_by.png .. _`filter_completion`: Filtering completion ===================== You can programmatically decide to filter what a completion returns in a very easy way using :ref:`django like syntax ` (the same used to set constraints):: t = SqlTable(Movie,...) t.completions.title.filter(title__icontains='love') this line will instruct the completion to only show titles that contain the world "love". Filters on a field that is a foreign key will be relative to the related table:: # nation_cod is a field_name of the table t.completions.director_id.filter(nation_cod='ITA') will build a constraint on the **director** table filtering only italian directors. If you set relation on your Director class as in:: class Director(Base): ... nation_cod = Column(ForeignKey(Nation.cod)) nation = relation(Nation) you can set filter on the completion based on this relation:: ## nation__code (note the double underscore!!!) will trigger ## a filter on the cod field_name of the relation nation t.completions.director_id.filter(nation__cod='ITA') dynamic filters ---------------- It's also possible to set a "dynamic filter" i.e. a filter depending on the value already of another field:: t.completions.last_name.filter(nation='$nation') In this case the value of $nation will be set using ``t.get_value('nation')`` In case you have a related table you can go back to the main table:: t.related.movie.completions.title.filter(director_id='$main.director_id') enum mode with foreign keys --------------------------- Enum mode is the way you can mimic a standard enumeration field: you see all fields independently from what you have in your entry. This is more natural in some circumstances if you only have few values. You can get this behavior all the times just hitting Control-Shift-Return or in ForeignKey fields double-clicking the down arrow. Since you probably want this depending on the values of the table you can programmatically choose to serve completion *only* via this way setting ``force_enum = True`` on the completion:: t.completions.director_id.force_enum = True enum mode w/o foreign key --------------------------- There is another way that mimics enum mode, i.e. setting directly the possible completion values via the method :meth:`set_values `:: t.completions.status.set_values(['open', 'closed', 'waiting for input']) or:: t.completions.status.set_values(a_function_that_returns_possible_values) the signature of this function must be: .. function:: my_values(value) :param value: the value that may have been written in the entry, used to filter values, as usual more customization ------------------- There are normally 2 possible completion according to the filter level. An example can better clarify: suppose you have an entry where you are supposed to enter a username. You set a filter on *active* users, now you need to fix an old record that really has a user that is no longer *active*. You need to loosen you filtering criterion momentarily. You can do that Pressing the ``Alt`` key along with normal ``Ctrl-Enter`` or ``Shift-Enter`` These two filtering criteria are stored in two ``session.query`` objects and are stored in the completion with attributes: :filtered_query: the query with filters. You set filters on this query with ``filter()`` method. If a ``filtered_query`` already_exists, filters are added, otherwise it's written from ``query`` :query: the default query, used when no filter is desired (``Alt`` is pressed). You set filters on this adding argument "main_query = True" to ``.filter(main_query=True)``. Of course each one can be customized with the normal sqlalchemy syntax also. Note that if you have both filters (with and without main_query option) order makes difference as ``filtered_query`` is built based on ``query`` when .filter is called for the first time. If you change ``query`` after calling ``.filter()`` you end up with unrelated filter condition (that's allowed as you may really want this). Remember that :ref:`validation` is a completely different mechanism than completion even if it's not possible to add a field that doesn't come from a completion. .. _customizing_description: customizing description ----------------------- The ``query`` attribute of the completion determines which fields will be present in the completions list, the ``format`` attribute decides how it will be represented. Default value is determined as described :ref:`above `, but you can customize it as you prefer, as far as you use fields present in the query. As an example:: t.completions.director_id.format = '%(first_name)s %(last_name)s -- %(nation)s' Behavior on completion with m2m/m2o relationship ================================================= When the completion is in a SqlTable that represents a not editable m2m relationship (as ``actors`` would be for ``movies``), the completion does not simply add the single field but substitutes the whole record. On such a relationship table's completion you can set filter that will act on all fields. Rationale: if you have a movie/actor relationship and set a constraint on actors so that only female should be selected, you probably want to retain that filter independently from the fact that you select the nation, the first or the last name. .. automodule:: sqlkit.widgets.common.completion sqlkit-0.9.5/doc/sqlkit/constraints.rst0000644000175000017500000001217411714210425017551 0ustar sandrosandro.. _constraints: ============ Constraints ============ Constraints are a mean to limit browsing of data to a limited set. It's a different concept from filtering, as this enforces a limitation while filtering allows the user to change the values at will. .. _django-syntax: Django-like syntax ================== A constraint can be as simple as:: t = SqlTable('movie', dbproxy=db) t.add_constraints(actors__country='ITA', genres__name='drama', year__gte=1950, year__lte=1970) And browsing of movies will be constrained into dramas with at least one Italian actor, produced between 1950 and 1970. The double underscore '__' will allow spanning from one table to a related one (provided a relation was established at sqlalchemy level) and from attribute name to operator to be used. .. note:: This syntax is inspired to django_'s ORM syntax. The same syntax can be used to add filters to the sqlwidget, to the :ref:`completion ` and when ``.reload()`` method is used. .. _django: http://www.djangoproject.com query ======= limitation acts on ``self.query`` directly. Self query can be modified in any way accepted by ``sqlalchemy`` syntax operators --------- these operators are recognized:: OPERATORS = { 'notlike' : 'not like', 'like' : 'like', 'ilike' : 'ilike', 'notilike' : 'not ilike', 'iregexp' : '~*', 'regexp' : '~', 'notiregexp' : '!~*', 'notregexp' : '!~', 'lt' : '<', 'gt' : '>', 'lte' : '<=', 'gte' : '>=', 'eq' : '=', 'neq' : '!=', 'equal' : '=', 'in' : 'IN', 'notin' : 'NOT IN', 'null' : 'IS NULL', 'notnull' : 'IS NOT NULL', } and ``icontains`` that at the moment map to regexp operators if they exist or like with % on both sides. conjunctions ------------ In the example all conditions have been ``AND`` ed. It's possible to use ``OR`` operator adding ``or_=True`` argument. django2sqlalchemy ----------------- You can get a sqlalchemy ``ClauseList`` object starting from a django-like expression using django2sqlalchemy function:: from sqlkit.db.django_syntax import django2sqlalchemy clause_list, join_paths = django2sqlalchemy(mapper, *args) where ``args`` can be or_=boolean or any django-like query. Django2sqlalchemy returns a tuple: (clause_list, join_path): :clause_list: the list of column expression connected with ``AND`` or ``OR`` according to ``or_`` argument :join_path: the list of path needed to add to query via ``query.join()`` to have all the fields the query needs to apply the filters in clause_list A possibility is to add:: for path in join_path: query.join(path).reset_joinpoint() Filter simplified syntax ------------------------- .. automodule:: sqlkit.db.django_syntax Native sqlalchemy constraint ============================ The django syntax is in no way the only possibility. If that is not sufficient to express the constraints you need you can just use any filter on the query directly:: tk_tbl = ticket.Ticket.__table__ my_id = self.db.get_session().query(ticket.User).filter_by(username=setup.USERNAME).one().id t = sk.SqlTable(ticket.Ticket, field_list=field_list, order_by='priority', **self.meta) t.add_filter(status=1) t.query = t.query.filter(or_(tk_tbl.c['assigned_by_id'] == my_id , tk_tbl.c['assigned_to_id'] == my_id , )) this example shows how to force a constraint on ticket requiring that the ticket be assigned *by* or *to* USERNAME. Any further filter applied interactively will be applied *on top* of this constraint. This is equivalent to writing:: t.add_constraint(assigned_by_id=my_id, assigned_to_id=my_id, OR=True) At the moment of this writing it's not possible to write:: t.add_constraint(assigned_by_id__username=USERNAME) as the foreign key relation is not followed in this context. aliased constraints -------------------- Sometimes you need to have aliased constraints in order to ha sqlalchemy build an aliased join to related classes on which you may want to set constraints. This may be necessary if you want to use constraints along with order_by and possibly filters on foreign keys. example +++++++ Suppose you have an 'address' table with a ``user_id`` field that is a ForeignKey to a ``user.id`` column. Suppose you want to open a SqlTable on table addresses, ordered on ``user_id`` (well... surely you don't want to order by the ``id`` value, you probably have a ``first_name`` field that is much more appropriate for sorting). Now suppose you also have an ``active`` field on the user_table. You can achieve this (simple) in the following way:: t = SqlTable('address', order_by='user_id__first_name', ...) t.add_constraint(user_id__active=True, aliased=True) the order_by argument implicitly have built a join with the user table, the same stands for the constraint ``user_id__active``. Sqlalchemy would have complained that the table 'user' was already present, so you need to alias it. I'll probably make this the default in a future version. sqlkit-0.9.5/doc/sqlkit/sqlwidget.rst0000644000175000017500000006205211714210425017205 0ustar sandrosandro============ Sqlwidget ============ This is the base class for :ref:`mask` and :ref:`table` that implements the common interface. mandatory argument ================== The first and mandatory argument is the ``mapper`` of the object that will be retrieved and displayed. Alternatively any other objects from which the sqlwidget infers them (e.g.: the tablename and the metadata where the table can be autoloaded): * a mapper * a mapped class * table name (requires you also pass metadata) In older releases different options: ``class_``, ``table``, or ``mapper`` where used. Now a DeprecationWarning is raised if you use those opts. main options ============ Any sqlwidget needs a ``session`` as well. Metadata is also used when auto-loading tables referenced by foreign keys to display a better representation of the referenced record. .. _dbproxy: dbproxy ------- Since a typical scenario is to have to provide a session different in each SqlWidget and a metadata, an object is provided -``dbproxy``- that can be initialized from the engine specification:: from sqlkit.widgets import SqlMask, SqlTable from sqlkit import dbproxy db = proxy.DbProxy(engine="sqlite:///model/movies.sqlite") SqlTable("movies", dbproxy=db) below you can see some alternatives that would work as well:: Session = sessionmaker(bind=self.metadata.bind, autocommit=False) sess = Session() meta = MetaData() meta.bind = "sqlite:///model/movies.sqlite" # SqlTable("movies", session=sess, metadata=meta) # passing a mapped class (Movie here is build with declarative layer): # the metadata is found from the mapper.local_table.metadata SqlTable(Movies, session=sess) Session are created with ``autoflush=False``, ``expire_on_commit=False`` but can be changed when building ``DbProxy``. Since version 0.8.6 default value for *autocommit* has been turned to *True* to prevent `idle in transaction`_ in postgresql. session & expire_on_commit ========================== The reason to have ``expire_on_commit=False`` is that if you don't set it, after every commit, you have to reload all objects and the interface turns very slow, especially when working with a remote database. ``expire_on_commit`` is a recent addition to sqlalchemy session (around sa rel. 5.03rc) so I try it and fallback to default that would turn to be slower. Previous 0.5 rel called it ``autoflush`` .. autoclass:: sqlkit.widgets.common.sqlwidget.SqlWidget(see_below) :members: __init__, add_constraint, add_filter, resize, set_records, set_format, is_mask, is_table, sb, get_value, set_value, get_current_obj, set_mode, add_temporary_item, reload, commit, add_validation_error, add_not_null_error Attributes =========== .. attribute:: filter_panel The :ref:`filter_panel` widget .. attribute:: related a container for all related sqlwidgets (i.e.: SqlWidgets that have a relation with this sqlwidget defined by sqlalchemy and that are displayed in the widget). This is used in all situation in which you need to fine-tune the configuration of a related table (completion, layout, ...) .. attribute:: completions a container of all completion objects. Needed to change the behavior of some completion (see :ref:`completion`) .. attribute:: gui_fields a container for all validation fields. A validation fields is an object that lives in ``sqlkit.fields`` and that knows how to represent a field and how to validate it. .. attribute:: gui_field_mapping A dict used to force a map between a ``sqlkit.Field`` and a gui field. Read more in :ref:`fields`. .. attribute:: query the sqlalchemy query object (``session.query(mapper)``) with all constraints applied. Can be manipulated as necessary as long as it stay a query object. .. attribute:: layout the layout definition for this SqlWidget. This is the used definition for SqlMask while for SqlTable is only used if the record is opened in SqlMask (right click on the record in SqlTable). You can also set it on a related table:: GUI="director @ m2m=movies" t = SqlMask(Director, layout=GUI, ...) t.related.movies.layout = "title @ m2m=actors" .. attribute:: lay_obj the sqlkit.layout.Layout object used to create the layout .. attribute:: current the object represented by a mask or a possible selected record in a table or None. In :class:`SqlTable ` in the transient in which you are saving a record in a table (when selection changes) ``self.current`` will point to the obj that is to be saved (while the selected object may already be another one). .. attribute:: field_list a list of the field names the GUI is handling. It comprises PropertyLoaders (i.e. properties of the class that act as a loader of other info - all relation are seen as PropertLoaders w/o column) .. attribute:: order_by an order_by string or clause element. Same as parameter passed to the class. It's a property, can be set in any moment. .. attribute:: session the sqlalchemy session used for querying .. attribute:: defaults an instance of ``sqlkit.db.defaults.Defauls`` instantiated with local=True as explained in :ref:`local_defaults`. Any default set with this instance will only be visible in this sqlwidget. .. attribute:: title the title of the Window. .. attribute:: mode the mode describing permissions of the widget. See :ref:`set_mode ` .. attribute:: noup noup can be a set of field_name or a comma separated string with possible +- sign to add/remove field_names to the set of field_names that will not be possible to update Note that to add a non editable field_name you must used '+field_name'. Using simply 'field_name' will reset the list to only that field_name .. attribute:: actiongroup_* see :ref:`uimanager` .. attribute:: ui_manager see :ref:`uimanager` .. attribute:: relationship_leader Th case this sqlwidget is representing a relation of a SqlMask, that SqlMask is referred to as a relationship_leader. .. _sqlalchemy: http://www.sqlalchemy.org .. _signals: Signals ======== :pre-display: A record is about to be displayed. .. function:: pre_display_cb(sqlwidget, obj): :param sqlwidget: the widget that emitted the signal :param obj: the object that is about to be displayed :record-selected: A record has been displayed in Mask or selected in Table. The callback will just receive the widget as argument. .. function:: records_selected_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-saved: A record has been saved. This signal is *not* emitted from within session extension. That means you are sure there will be just one signal for each button press on "save" button. This is issued from within the :meth:`SqlWidget.commit` method independently from the fact that a real modification occurred, so you are not guaranteed any modification took place. Was originally added to implement a destroy of the widget when the save operation was performed. The callback will just receive the widget as argument. .. function:: record_saved_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-new: A new record has been added. The callback will just receive the widget as argument. .. function:: record_new_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-deleted: A record has been deleted. The callback will receive the widget and the obj. .. function:: record_new_cb(sqlwidget, deleted_obj): :param sqlwidget: the widget that emitted the signal :param deleted_obj: the obje that was deleted Note that this signal is emitted only for records deleted explicitly, i.e. records that where the ``current record`` in a mask/table. If a record is deleted as a side effect (e.g.: becouse cascade="delete-orphan" is set) no signal is emitted for that obj. :records-displayed: Records have been displayed. This may be after :meth:`SqlWidget.reload` or :meth:`SqlWidget.set_records`. The callback will just receive the widget as argument. See also :ref:`context changed ` to see another signal that better tracks *any* change .. function:: records_displayed_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :after-flush: flush has occurred, normally commit should not add any errors. This is implemented with a SessionExtension: if you used the default session obtained via get_session() you are assured that it will be correct. If you create a session by yourself, be sure to add ``sqlkit.db.proxy.SKSessionExtension`` to the session extensions or you won't have this signal. Read more detailed explanation in :ref:`hook on_after_flush ` .. function:: after_flush_callback(sqlwidget, object, session) :param sqlwidget: the sqlwidget that emitted the signal :param object: the object that was *current* when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in "dirty state", "new" or "deleted", but current was the one selected in a table or displayed in the main mask :param session: the session that was flushed. The moment in which the signal is emitted you can still dig into ``session.dirty``, ``session.new`` and ``session.deleted`` and find which attributes have been changed (you may want to use :func:`get_differences`) :after-commit: run from within ``after-commit`` SessionExtension. Callback signature is identical to ``after_flush_callback`` above. :delete-event: emitted when the sqlwidget is destroyed .. function:: delete_event_callback(sqlwidget) :param sqlwidget: the sqlwidget that emitted the signal .. _hooks: Hooks ===== Beside signals there is another way to add controls: hooks. A hook is a function that will be called in particular moments only if present. Hooks are the main way to customize the behavior of a sqlwidget. Some of the hooks (``on_validation.*``) are related to validation other are related to configuration (``on init``), others (``on_activate``) may be used to save typing. Hooks are searched for in the methods of the instance of a class declared in the optional ``hooks`` argument or in the global registered hooks (see below). Hooks can be *registered* using :func:`sqlkit.db.utils.register_hook` (and :func:`get_hook ` module) so that any sqlwidget built on that table will use those hooks (unless the table is part of a join or another selectable!!). The advantage is that browsing data (e.g. using right click on a table row) can lead to opening tables that are not configured: registering hooks is a way to enforce configuration (and possibly constraints) on any widget. As layout hooks can be registered with a nick (default is ``default``) so that you can register different hooks for different editing flavors. E.g/: you can have a table for *people* and you can decide to open it with *customer* or *provider* layout/hooks just registering both layout and hooks and using argument ``layout_nick``. Use it with care as it may lead in situation in which not all fields are present (due e.g. to a different layout or different field_list) The following hooks are defined: :on_change_value__field_name: this hook is used to trace *changes* in the widget. It's mainly meant to be used interactively but it is also triggered when :meth:`set_value ` is invoked with initial=False. It's **not** changed if the value is set on record change. Currently it behaves differently for Table widgets or Mask widgets. Mask widgets invoke it each meaningful changes i.e. each char for varchar/int field, each time a value is choosen for enum/foreign key fields, when a date is selected or validated for Date widgets. Table widgets invoke this hook only when the field is leaved or activated (Return is pressed). .. note:: Implementation of this hooks is currently limited to some fields: Varchar, Int, Float, Numeric, Enum, ForeignKey, Date, DateTime, Bool. Text, Images, Time and Interval are not implemented. Example 63c is a complete example that shows it. .. function:: on_change_value__field_name(sqlwidget, field_name, value, fkvalue, field) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that runs the hook :param field_name: the field_name :param value: the new value :param fkvalue: if field is a ForeignKey or enum, the displayed value :param field: the field .. _on_completion: :on_completion__field_name: called when a completion is chosen .. function:: on_completion__field_name(sqlwidget, field_name, obj) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that runs the hook :param field_name: the field_name :param obj: the matched object in the completion. This obj has attributes for each *field_name* named in :attr:`attrs ` attribute of the completion. You can add field_names to that attribute if you need them in this hook function:: sqlwidget.completions.director_id.attrs += ['nation'] allows you to add a completion hook:: def on_completion__director_id(self, sqlwidget, field_name, obj): print obj.nation sqlwidget.set_value('nation', obj.nation) you can also reach field attributes as dict values: obj['nation']. .. _on_save_as: :save_as: .. versionadded:: 0.9.3 this hook is invoked each time a record is saved as a duplicate of another one. After the new record is filled and before it's saved you can customize at your will. Callback function: .. function:: on_save_as(sqlwidget, old, new): :param sqlwidget: the mask/table that invoked the hook :param old: the old object that was copied :param new: the new object When this hook is present, no warning on how the copy is handled is raised as it's considered that the programmer has already coped with all the tricky issues. :on_validation: called when all the values have been collected in the object, before calling validation on all fields and related widgets. That's a good point to implement any procedure to add automatism's. Within this hook you can propagate errors to the validation machinery in two of ways: 1. raising ``sqlkit.exc.ValidationError``. Simple when just one error is found 2. filling self.validation_errors: a dict holding all errors. The *key* is the field_name or "record_validation", the value a list of error messages .. function:: on_validation(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. _on_field__validation: :on_field_validation__field_name: called to validate a single field as for the previous ``on_validation``. This is called from within the :meth:`field's validation method ` 1. raising ``sqlkit.exc.ValidationError``. Simple when just one error is found 2. filling self.validation_errors: a dict holding all errors. The key is the field_name or "record_validation", the value a list of error messages .. function:: on_validation(sqlwidget, field_name, field_value, field) :on_activate__field_name: when *Return* is pressed in Mask or Table. Good to complete fields via calculation on other fields (e.g.: total, vat...). The name derive from the GTK name 'activate' that is when you press 'Return' in an entry, even thought in a Table's treeview it's really connected to the cell's ``edited`` signal (limited to Varchar and Numeric columns). .. function:: on_activate__field_name(sqlwidget, field_name, field) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook :param field_name: the field_name :param field: the ``sqlkit.widgets.common.field`` :on_init: run as the last command of __init__. It's main purpose is to allow to configure a widget in a way that will be handed over to a possible SqlMask generated right-clicking from a table row (see :ref:`recordinmask`). .. function:: on_init(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. _on_pre_layout: :on_pre_layout: run before the layout is setup. It's main purpose is to allow to add fields in :attr:`gui_field_mapping` (that is only useful for SqlMask). Note that you can force a field for a table's attribute if you want setting info's *field* key pointing to that field's class. .. function:: on_init(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. note:: **Hooks invoked within the session extensions** The hooks that are called from within the session extensions, can be called **several times** if there are more sqlwigets sharing the same session (that happens for example each time you open a mask to edit the row of a table i.e.: :ref:`recordinmask`) .. _on_after_flush: :on_after_flush: run as the signal with the same name from within ``after_flush`` session extension method. This hook is completely similar to ``after-flush`` signal, but is meant to be defined in a separate class so that it's easier to propagate validation hooks to a spawned child (i.e.: :ref:`recordinmask`, when opening a mask by right clicking on a table record). You'll see that ``on_after_flush`` is called eather. From the sqlalchemy documentation: "Note that the session's state is still in pre-flush, i.e. *new*, *dirty*, and *deleted* lists still show pre-flush state as well as the history settings on instance attributes". This is true for *after_commit* hook as well, it is not true for *after_flush_postexec*, that on the other had has already setup relation. I end up in some circumstances to split the callback in two phases: one that detects if an action is needed from within the after_flush/after_commit phase, the second (may be a mail, or any other action) from within the :ref:`hook on_record_saved `, so that I can use the relations. Beware that you may have one call to ``on_after_flush`` and more different calls to ``on_after_commit`` .. function:: on_after_flush_callback(sqlwidget, object, session) :param sqlwidget: the sqlwidget that emitted the signal :param object: the object that was *current* when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in "dirty state", "new" or "deleted", but current was the one selected in a table or displayed in the main mask :param session: the session that was flushed. The moment in which the signal is emitted you can still dig into ``session.dirty``, ``session.new`` and ``session.deleted`` and find which attributes have been changed (you may want to use `get_differences`), but you won't have correctly setup relation's object :on_after_flush_postexec: run as the signal with the same name from within ``after_flush_postexec`` session extension method. As in the precedent hook this is called exactly within the session extension method by the same name. When it's run the session will have no longer information on session.dirty/session.new/session.delete but will have all relations set-up. The callback has the same signature as for :ref:`hook on_after_flush ` :on_after_commit: run after commit after_commit session extension method The callback has the same signature as for :ref:`hook on_after_flush ` .. _on_record_saved: :on_record_saved: run from within the commit method of the widget, that assures that will be issued just once. It's just equivalent to the signal with the callback has the same signature: .. function:: record_saved_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal Hooks for related widgets ------------------------- If you need to add hooks for related sqlwidgets (those that display related records as explained in :ref:`relationships`) you need to add the relationship_path in the method name, i.e. the value you have used in the table definition for the relation (and in the layout as in m2m=actors) .. _wav_example: example +++++++ This example shows how to use hooks to play a sound on completion:: class Hook(obj): def on_completion__genre(self, field_name, obj): if obj.genre == 'horror': play('horror.wav') MovieMask(class=models.Movie, hooks=Hook(), ...) Registered hooks and SessionEtensions ------------------------------------- When hooks are :ref:`registered ` their customizations are enforced each time the model for which they're registered is called. That adds some complications you should be sure to understand if using one of these hooks: * on_after_flush * on_after_commit * on_after_flush_postexec These hooks are called within a SessionExtension that calls hooks on any sqlwidget that may be using the same session. An m2m, m2o relation table share the same session as the Mask that holds them so that it's pretty normal to have several different tables within the same session. From the SessionExtension hooks are searched for in any of these so that you should write the hooks keeping in mind it can be called from another sqlwiget's commit. As an example, suppose you have a Mask with the following layout:: first_name last_name m2m=genres m2m=actors suppose *movies*, *genres* and *actors* have registered ``on_after_commit`` hooks. They will all be called on any commit. Adding a genre object will trigger *on_after_commit* on the Actor's table and vice verse. .. _diff: get_differences --------------- Module :ref:`sqlkit.db.utils ` provides a simple function that yields all modified attributes of an object, along with their old and new values .. function:: get_differences(obj) :param obj: the object to be inspected :param session: the session the object belongs to :rtype: field_name, old_value, new_value. Old value and new value are lists. .. function:: get_history(obj, field_name) :param obj: the object to be inspected :param field_name: the field_name :param session: the session the object belongs to :rtype: new_value, unchanged_value, old_value, . Old value and new value are lists. The order is different from ``get_differences`` as this is exactly what is returned from the SA function Saving varchar and text fields ============================== Text fields with empty values will be saved as NULL. To change this behavior you need to set blank=True on fields:: t.gui_fields[field_name].blank = True .. _`idle in transaction`: http://groups.google.it/group/sqlalchemy/browse_frm/thread/6abb815a3728d41c?hl=it&tvc=1&q=idle .. _uimanager: UiManager: menu and actions =========================== Menu entries are handled via standard gtk.UiManager interface. One interface is created for each toplevel Window and for each :ref:`view ` in a SqlTable widget. You can see some examples in the demo (70-72). Standard actions are divided into the following categories:: General (self.actiongroup_general) PendingDifferences Quit Go Modify Tools File Help About Table (self.actiongroup_table) HideColumns ShowColumns Records MaskViewFKey Export MaskView Zoom-fit Insert (self.actiongroup_insert) New Save-as (just for SqlMask) Delete (self.actiongroup_delete) Delete RecordDelete Update (self.actiongroup_update) Save Undo (just for SqlMask) Browse (self.actiongroup_browse) Filters Reload Select (self.actiongroup_select) Back Forward Print (self.actiongroup_print) Print Debug (self.actiongroup_debug) Gtk-tree While for each table's view you have:: Table HideColumns ShowColumns Records MaskViewFKey Export MaskView Zoom-fit Insert New Delete Delete RecordDelete Changing an entry ------------------ To change an entry you can: #. add an actiongroup in which you have defined an action with the same name #. insert this actiongroup *before* it's relevant one (position 0 is normally a good choice Example #72 in the demo shows how to do it Adding an entry --------------- the standard way is shown in demo snippet #70: #. create an xml definition and add it to ui_manager #. create an action and add it to an actiongroup You can also use a SqlTable method :ref:`add_temporary_item ` that will add a temporary entry, so that it can be contextually changed. This way is demonstrated in demo snippet #71 sqlkit-0.9.5/doc/sqlkit/mask.rst0000644000175000017500000001330411714210425016131 0ustar sandrosandro.. _mask: ========= SqlMask ========= .. image:: ../img/layout-simple.png :align: right layout ======= The most powerful part of SqlMask is the ability to **define the layout via simple text description**. In the examples you'll see how easy it is to create fancy layout that have all nice gtk Widgets like expanders, notebooks, panes and it makes it incredibly easy to nest SqlTables into SqlMask to build complex layouts that can also represent :ref:`relationships`. If you know ReST language and glade, it relates to glade as ReSt to html. If no layout is provided a flat one will be generated on the fly. The key is that a textual layout is parsed to see if the token are recognized as fields of the table that the mask is editing, in which case introspection is done to understand which widget should be used to edit the data (according to rules above) and a label is added:: mylayout = """ image title date_release TXS=description director_id """ m = SqlMask(model.Movie, layout=mylayout, ...) will creare a mask with an image, an entry, a date a text and a fkey. ``title`` and ``director_id`` will be already instrumented with completion: ``director_id`` will try to complete choosing the values of directors from the ``director`` table, ``title`` will suggest completion based on title present in the table (and in this case may not be very useful). ``description`` would render as an entry as it's a *varchar*, we wanted to "cast" it to *text* using ``TXS=``. The description language you can use is pretty rich (and dynamic, so you can add your custom made widgets): you'd better have a look at the demo of sqlkit.layout that you find in sqlkit.demo.layout When parsing the textual layout, any token that is not recognized as a field is passed as is to ``sqlkit.layout.Layout`` gtk refinements --------------- Occasionally you may need to refine your layout, change packing, visibility, attribute an so on. You can reach the gtk widgets via the ``widgets`` attribute of the SqlWidget. In example 26 we use:: Tbl = t.widgets['T.a'] Tbl.get_parent().child_set_property(Tbl, 'y-options', gtk.EXPAND|gtk.FILL) that changes pack properties to a gtk.Table whose name is ``T.a``. If you have ipython you can start the demo with option -i to gain an interactive shell to play with an experiment with the widgets. By default each field has a label whose key in widgets dict is ``l=field_name`` while each entry is ``e=field_name``. Shortcuts ---------- :mouse scrolling: allows to browse the records that have been loaded by a reload operation :Control-s: saves the record :Control-q: quits the table :Control-n: opens a new record Signals ======== :pre-display: this signal is emitted when current object has already been set but field values have no been set. It can be used to configure custom widgets whose appearance may depend on other values. .. method:: pre_display_cb(mask, obj) :param mask: the SqlMask that emitted the signal :param obj: the object currently selected or None .. versionadded:: 0.8.8 .. image:: ../img/mask-demo.png :align: right :scale: 50% :class: preview .. _save-as: Save as ======= Masks have a function *save as new record*, that allows to duplicate a record (corresponding to table's :ref:`row duplicating `). It's important you understand exactly what it does so that: * it nullifyes the primary key, so that a new one will be generated. If the primary key is visible and editable in the mask, it's up to you to delete it or change it according to your needs * m2m relations: for each *visible* relation, i.e. each relation that is shown by the mask **and is of type many to many** it copies the records. As an example: actors would be copied in the second film. If you want to change the new record in the related table (e.g.: actors) do that **only after saving** otherwise you will also change the cast of the original film, that's different from other fields as table'mode is to save in the same moment you edit. * o2m relations: if o2m relations are present a warning is displayed that those fields will not be copied **unless you have an on_save_as hook** that would mean you are already handling this case. Blindly copying the records would *steal* the references from the other record. Just to be explicit: suppose to have a mask of a director with his films, the director is Fellini and the film *La strada*. Duplicating the record "Fellini" with "Fellini 2" should probably be a 1^st step in personalizing some fields. In no way you want to divert the ``director_id`` of the film "La strada" that should continue to point to the original record "Fellini". * visible fields are copied from one record to the other. Please note carefully this point: a new empty record is set as current record and each visible fields is furtherly saved in the new record. If a field is not visible that field will not get copied. You may have problems if required fields are not visible. To allow to fill with new values a :ref:`hook ` is invoked named :ref:`on_save_as `. .. autoclass:: sqlkit.widgets.SqlMask() :members: get_widget, clear_mask, set_frame_label Introspection of the table is used to determine which widgets will be used to edit the data. The following rules are applied: :varchar: gtk.Entry :numbers: gtk.Entry with right alignment :bool: gtk.CheckBox :text: gtk.Text :FKey: fk_edit (a custom ComboBoxEntry) :date/datetime: dateedit (entry + arrow for calendar + time) :default: gtk.Entry All fields will have a label that is sensitive to mouse clicks. A mouse click pops up a :ref:`filter ` widget. sqlkit-0.9.5/doc/sqlkit/table.rst0000644000175000017500000002445211714210425016273 0ustar sandrosandro.. _table: =========== SqlTable =========== .. image:: ../img/table-demo.png :align: right :scale: 50% :class: preview :alt: table opened on movies SqlTable is a widget that presents data in a table mode. Columns that are foreign keys are presented in blue and a :ref:`description` of the foreign key is used. .. autoclass:: sqlkit.widgets.SqlTable :members: views, totals, edited_path, create_view, set_field_list, set_editable, set_opts, select_path, hide_fields, get_selected_path, get_obj_at_path, add_record, set_fk_layout, record_in_mask, fkey_record_in_mask, add_row_filter .. image:: ../img/menu.png :align: right Column headers ============== Each column is clickable. Clicking on the column pops up a menu that enables: - to add a filter on this field (see :ref:`filters`). This same action can be done by pressing ``f`` on the column w/o opening the column menu. Only persisted fields can be filtered on. - to sort on this field (database sorting). Note that sorting on a ForeignKey will trigger a join with the referenced table and an attempt to sort on the :ref:`description` (In general you don't want to sort on the ``id``). A "local sort", i.e. a sort done without hitting the database is performed when you sort a related table (e.g.: as explained in :ref:`relationships`) or when you press ``s`` on a column after clicking the table (to set the *focus* on the table) - to add a total on this column (if the column is numeric). This same action can be done by pressing ``t`` on the column w/o opening the column menu. - to toggle a brake on this column (see docs for :ref:`totals`). This same action can be done by pressing ``b`` on the column w/o opening the column menu. - to hide a column. This same action can be done by pressing ``h`` on the column w/o opening the column menu. Cell renderers ============== Boolean ------- Booleans are represented with CellRendererToggle, if the field is nullable, the value loops over 3 states. Text ---- I don't have a satisfactory CellRender for long text. Any hint is appreciated. Export ======= A very minimal export function allows to export visible data in a .csv (comma separated value) format. Follow File -> Export. That function can be reach also right-clicking on the table, thus allowing to export that particular :ref:`view `. Shortcut ======== in the treeeview ---------------- :Control-x: eliminates a line :Control-k: eliminates a line :Control-s: saves the record :Control-q: quits the table :Control-n: opens a new record :Control-z: zoom the treeview :Shift-Z: zoom the treeview in a related table (nested in a mask) :s: sorts the table locally (toggle between ascending and descending) :f: open a filter for this column :t: create a total if the column is numeric :v: toggle visibility of some rows, needs that you set a visibility function via :meth:`sqlkit.widgets.SqlTable.add_row_filter` :b: toggles a break for subtotals on this column. In addition to this operation it also sort on the column as it often happens you want a subtotal after having ordered. Adding a break from the column menu does not order. in the editable cell -------------------- :Control-Enter: pops a completion (regexp mode) :Shift-Enter: pops a completion (start mode) :Esc: aborts editing Signals ======= :button-press-event: this event is emitted when clicking in the treeview if there are no pending validation errors (in which case you are forced to solve them before). It is mainly used to handle the default menu. Look in the example to see how you can manipulate the menu. .. method:: button_press_cb(table, event, obj, field_name, menu, treeview) :param table: the SqlTable that emitted the signal :param event: the gtk.gdk.Event associated :param obj: the object currently selected or None :param field_name: the field_name of the selected column :param menu: the default menu that il popped up by this button-press-event if event.button == 3 or None :param treeview: the TreeView widget that was clicked. You may get the view the treeview belongs to with ``treeview.get_data('view')`` .. versionadded:: 8.7 .. _context_changed: :context-changed: Records have been displayed or selection was changed. This is used to track any change in the records both selected or displayed and was added to be used by RecordInMask below .. function:: records_displayed_cb(sqlwidget, current): :param sqlwidget: the widget that emitted the signal :param current: the obj that was current in the row or the first record in the liststore. .. _recordinmask: RecordInMask ============ Clicking on any row of the treeview gives the possibility to show the record in a Mask View. If a layout was passed to the ``SqlTable``, it will be passed to SqlMask. In this case all hooks, fields possibly configured and completions will be copied in the new Mask. Same for the hooks. If you need to configure this Mask in a particular fashion you can put all configuration in a hook named :ref:`on_init `. This code will be executed both for Table and Mask generation that allows to use the same Hook for both widgets. Signals and callbacks are arranged so that it will follow the selection of the table. The newly created mask will inherit the :ref:`mode ` of the table, i.e. if the table was read-only the mask will be opened in read-only mode. On the other hand the mask will be have browsing inhibited. If a layout was registered for the database table it will be used. You can programmatically open this SqlMask using :meth:`fkey_record_in_mask `:: t = SqlTable(...) m = t.record_in_mask() RecordInMask for ForeignKeys ============================ Similarly, if the column in which you click is a ForeignKey, the popped menu will show an entry to edit the referenced record in a mask. To be sure it will have the customization you want you can :ref:`register ` Layout, Hooks and Class. In the case you have several possible layout for the same fk, and you want to use a layout that is not the default one, you can use ``set_fk_layout`` You can programmatically open the foreign key mask using :meth:`record_in_mask `:: t = SqlTable(model.Movie, ...) m = t.fkey_record_in_mask('director_id') .. _views: Views ===== Views (added in rel 0.8.8) are a way to view the same data in two different TreeViews. This way you can split a very large table into different chunks (vertically), leaving some columns to a view and others to a different view. The same column may be repeated in different views. .. figure:: ../img/table-views.png :align: right An example of a table with views: *numbers* and *dates* are really fields of the same table in the database. .. _add_view_column: Adding a column --------------- You can add columns to a view if there's a corresponding ``field`` (sqlkit.fields.Field) in ``table.gui_field``. Examples #31 and #32 show how to do that that essentially boils down to: #. Create a Field class that has a method that uses as input the object and produces the output, you'll need a clean_value() method s well if you want to be able to create a total on that column (see example #32) #. Add an instance of that Field to the fields used in the gui: table.gui_fields #. Create a Column and add it to the View :: class ObjField(fields.VarcharField): """ A field that presents the obj """ def clean_value(self, value): return value and "%(year)s %(title)s" % DictLike(value) my_field = ObjField('new_column', {'editable' : False, 'type' : str, 'length' : 30}) t.gui_fields['new_column'] = my_field ## create a column col = columns.VarcharColumn(t, 'new_column', 'My New Column', field=my_field) ## add it to the view t.views['main'].add_column(col, 0) .. note:: Adding a column that is not mapped to any db-field leads to a column that cannot be filtered on. On the other hand you can sort (locally, read below) on that column and also get totals. Sorting columns =============== Sorting columns can be done in 2 different ways: #. using :attr:`sqlkit.widgets.common.SqlWidget.order_by` attribute of sqlwidget, that triggers an ``ORDER BY`` clause on the database #. using ``order_by`` clause of ``modelproxy``, that triggers a function locally. This option is faster since you don't need to reload data This is the only method you can use to order related table, apart from playing with relation's order_by attribute in the model Currently it can be done: :programmatically: by ``modelproxy.order_by`` with argument a string with field_names possibly prefixed by + or - to define the order:: t = SqlTable(...) t.modelproxy.order_by('status -description') it can also be used to order a related table:: lay = """ director o2m=movies """ m = SqlMask(..., layout=layout) m.related.movies.modelproxy.order_by('-year title') when sorting a field that represent a foreign key, the value of the lookup (i.e.: the value shown) is used rather than the value of the key :interactively: currently limited to just one column at a time pressing ``s`` when **focus in in the treeview** (i.e. you ave already clicked the treeview). Sorting will toggle ascending and descending. .. _row-duplicating: Duplicating a row ================= The row menu (right click) allows to duplicate a row, it requires some understanding of the related issues: 1. Only visible fields are copied 2. Primary keys are not copied 3. If a foreig key is copied and that corresponds to a relation that has ``cascade='delete-orphan'`` that relation requires an instance to be attached. The field is able to retrieve such instance and attach it only if you configure the model's class properly. You can read more info on the field's method :meth:`add_related_object ` As for :ref:`save-as` mask function, there's a hook named :ref:`on_save_as ` that can be used to configure proper action to be taken before saving the duplicated row sqlkit-0.9.5/doc/sqlkit/browsing.rst0000644000175000017500000000020111714210425017020 0ustar sandrosandro============== Browsing data ============== .. toctree:: :maxdepth: 2 constraints filters totals localization sqlkit-0.9.5/doc/sqlkit/localization.rst0000644000175000017500000001164111714210425017670 0ustar sandrosandro.. _localization: ============== Localization ============== localization support comprises: * localization of number (decimal separators and thousand grouping) * localization of date/datetime fields * library messages translation (we need help bu translators, there is a project in launchpad_) * translation of field_name (see below) Numbers and dates localization is provided by babel_ Numbers ======= Numbers are represented according to your locale settings as follows: :integer: grouping thousands (format: '#.###') :floats: grouping thousands, no limit on decimal (format: '#,###.#') :decimals: grouping thousands, number of digits according to type.scale (format: '#,###.00', for Numeric(8,2)) forcing format representation ------------------------------ A different representation can be forced using `Number Format Pattern Syntax`_ and setting the new values in the gui_field class via the set_format method that takes a dictionary as argument or the format argument to the widget:: fmt = { 'year' : '#', # don't show thousand grouping 'quantity' : ('#,###.#', 'en'), } t = SqlTable(.... format=fmt) # fmt as argument t.set_format(fmt) # fmt as method where the two possible forms are shown. If the value is a tuple, the second element is a locale to be used for the representation (as `quantity` in the example) Dates ===== dates are formatted according to your locale and the default format is 'short' for date/datetime and 'HH:mm' for time. forcing format representation ------------------------------ A different representation can be forced using `Date Format Pattern Syntax`_ and setting the new values in the gui_field class via the set_format as illustrated above for numbers. .. note:: for the time being this only works for ``SqlTable``, ``SqlMask`` uses a different widget that needs to be improved, so it's possible to set a different date format but not using babel. Temporarily you should set date_format attribute on the gui_field.widget.gtkwidget... Messages ========= Translation of messages is provided by the standard gettext module. If you'd like to contribute a translation for your language read the README.localization in the distribution folder. Translation of field_names of each column ------------------------------------------ Since many of the labels that appear in a mask and column headers of a table default to the name of the column, it's important to add the possibility to translate those strings as well, in order to give to the resulting mask a friendly look. The same column name may be translated differently in different tables so sqlkit adds a layer in the way of translation via a "label_map" that can be passed directly to the layout object or to the sqlwidget (label_map option). This ``label_map`` is a dictionary whose key is the ``field_name`` and the value is a tuple with a description and a help_text to show as tooltip on the label. It instead of a tuple a string is found, it's considered a label and the tooltip is set to None, Each of these are to be considered msgid that gettext will further try to translate:: { 'first_name' : ('First Name', 'Write the family name of the director')} Description and help_text can be stored in the table ``_sqlkit_field`` that can be easily edit with ``sqledit -c URL``. Each time a sqlwidget is instantiated it will try to see if any info for the tables it is editing was written in the database and add those to what can be passed to the widget as "label_map". label translation for related tables ++++++++++++++++++++++++++++++++++++ When related tables are used, the name of the columns may need different translation in each related table. Suppose you have a project mask with related table that point to users, one for 'staff' and another for 'manager', as in:: m2m=staff:username m2m=manager:username both would show up as 'username', that is pretty misleading. You can then add two keys to label_map:: 'staff.username': ('staff', None) 'manager.username': ('manager', None) that would translate as desired. That can be conveniently set in the _sqlkit_field table, in which case only the description column needs to be filled with 'staff'/'manager' Tooltips ---------- What we described is also the simpler way to add tooltips to labels and buttons in your layout. This helps delegating to the customer the personalization of the hints he wants to add to the GUI. In the table ``_sqlkit_field`` each row should represent a field of a table, but there's no harm adding to it more fields that can be the name of buttons or relationships (e.g.: author) just for the sake of translation and tooltip. .. _`Number Format Pattern Syntax`: http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html#numberpattern .. _`Date Format Pattern Syntax`: http://java.sun.com/docs/books/tutorial/i18n/format/dateFormat.html .. _babel: http://babel.edgewall.org .. _launchpad: https://launchpad.net/sqlkit sqlkit-0.9.5/doc/sqlkit/widgets.rst0000644000175000017500000000014611714210425016644 0ustar sandrosandro======== Widgets ======== .. toctree:: :maxdepth: 3 sqlwidget mask table printing sqlkit-0.9.5/doc/sqlkit/advanced/0000755000175000017500000000000011714210425016210 5ustar sandrosandrosqlkit-0.9.5/doc/sqlkit/advanced/views.rst0000644000175000017500000000020311714210425020072 0ustar sandrosandro.. automodule:: sqlkit.widgets.table.modelproxy .. _view_class: View ===== .. automodule:: sqlkit.widgets.table.columns sqlkit-0.9.5/doc/sqlkit/advanced/field_widgets.rst0000644000175000017500000000111611714210425021552 0ustar sandrosandro.. _field-widgets: ============= Field Widgets ============= Each field is represented using a proper gtk widget. Table and Mask need two different objects, a cell renderer for tables and a widget from module sqlkit.widgets.mask.miniwidgets for masks. Cell renderers and function to represent the data are defined in sqlkit.widgets.table.columns. Widgets defined in sqlkit.widgets.mask.miniwidgets are just interfaces (or proxies) to other gtk widgets. .. automodule:: sqlkit.widgets.mask.miniwidgets .. automodule:: sqlkit.layout.image_widget .. automodule:: sqlkit.layout.dateedit sqlkit-0.9.5/doc/sqlkit/advanced/contents.rst0000644000175000017500000000032011714210425020572 0ustar sandrosandro======================= Advanced configuration ======================= These classes are only needed for advanced configurations .. toctree:: :maxdepth: 2 fields views dbutils field_widgets sqlkit-0.9.5/doc/sqlkit/advanced/dbutils.rst0000644000175000017500000000004211714210425020404 0ustar sandrosandro .. automodule:: sqlkit.db.utils sqlkit-0.9.5/doc/sqlkit/advanced/fields.rst0000644000175000017500000000004011714210425020202 0ustar sandrosandro .. automodule:: sqlkit.fields sqlkit-0.9.5/doc/sqlkit/editing.rst0000644000175000017500000000020411714210425016614 0ustar sandrosandro============== Editing data ============== .. toctree:: :maxdepth: 2 completion validation relationship defaults sqlkit-0.9.5/doc/db/0000755000175000017500000000000011714210425013521 5ustar sandrosandrosqlkit-0.9.5/doc/db/session_elixir.py0000644000175000017500000001041311714210425017131 0ustar sandrosandro#!/usr/bin/python import sys sys.path.insert(0, '../..') import dbg dbg.debug(False) dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask') but = 'set_fkey_descr|(lookup|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb|lookup_value|is_fkey|instance2' dbg.trace_function(exclude=but) #from elixir import * import elixir from datetime import datetime, timedelta import time import sqlkit from sqlkit.db import proxy from sqlkit import completion #from sqlkit.completion import set_description_field #import gtk elixir.metadata.bind = "sqlite:///movies.sqlite" #metadata = elixir.metadata #elixir.metadata.bind.echo = True db = proxy.DbProxy(engine="sqlite:///movies.sqlite") __metadata__ = db.metadata #__metadata__.bind.echo = True #__session__ = db.get_session() #ss = db.get_session() # def get_session(obj): # from sqlalchemy.orm import sessionmaker # Session = sessionmaker(bind=__metadata__.bind) # return Session.object_session(obj) class Director(elixir.Entity): last_name = elixir.Field(elixir.String(60)) first_name = elixir.Field(elixir.String(60)) movies = elixir.OneToMany('Movie', inverse='director', lazy="False") # movies = elixir.OneToMany('Movie', inverse='director', lazy="False", cascade="all, delete-orphan" ) elixir.using_options(tablename='director') # elixir.using_options(session=ss) # elixir.using_options(metadata=elixir.metadata) def __repr__(self): return u'' %self.last_name class Movie(elixir.Entity): #id = Field(Integer, primary_key=True) title = elixir.Field(elixir.String(60)) description = elixir.Field(elixir.String(512)) year = elixir.Field(elixir.Integer()) director = elixir.ManyToOne('Director', inverse='movies' , lazy="False") # actors = elixir.ManyToMany('Actor', inverse='movies', tablename='movie_casting') # genres = elixir.ManyToMany('Genre', inverse='movies', tablename='movie_genre') # elixir.using_options(session=ss) elixir.using_options(tablename='movie') def __repr__(self): return u'' % self.title class Genre(elixir.Entity): name = elixir.Field(elixir.Unicode(15), primary_key=True) movies = elixir.ManyToMany('Movie') elixir.using_options(tablename='genre') # elixir.using_options(session=ss) def __repr__(self): return u'' % self.name class Actor(elixir.Entity): name = elixir.Field(elixir.String(60)) # movies = elixir.ManyToMany('Movie', inverse='actors', tablename='movie_casting') elixir.using_options(tablename='actor') # elixir.using_options(session=ss) elixir.setup_all() lay = """first_name last_name o2m=movies - - - """ tm = sqlkit.SqlMask(Director.mapper, metadata=__metadata__, session=elixir.session,layout=lay) t = tm.mfields['movies'].widget.table #olmi = Director.get_by(last_name='Olmi', first_name='Ermanno') #ofilms = olmi.movies #ofilms.pop() #olmi.movies = ofilms #elixir.session.commit() #print olmi.movies #f100 = Movie.get_by(title='100 chiodi') #re = Movie(title='La leggenda del re pescatore') #pirati = Movie(title='Cantando dietro il paraventi') #olmi.movies=[f100, re, pirati] #olmi.movies.append(re) #olmi.movies.append(pirati) #create_all() #session.flush() #t1 = sqlkit.SqlMask(Movie.mapper, metadata=metadata, session=elixir.session,) #t1.reload() #print lay #tm = sqlkit.SqlMask(Director.mapper, metadata=metadata, session=elixir.session) #tm = sqlkit.SqlTable(Director.mapper, metadata=metadata, session=elixir.session) #td = sqlkit.SqlTable(Movie.mapper, metadata=metadata, session=elixir.session,) # addto=tm.w['A=uno'], naked=True) #tm = sqlkit.SqlTable(Movie.mapper, dbproxy=db) #tm = sqlkit.SqlMask(Movie.mapper, metadata=metadata, session=elixir.session) completion.set_description_fields('director', 'last_name', add='first_name', format="%(first_name)s %(last_name)s") completion.set_description_fields('director', 'last_name', add='first_name', format="%(last_name)s - (%(first_name)s)") #print f100.director #print t1.records #print t1.records[0].director # for j in t1.session: # print j, get_session(j) # print j.director #q = session.query(Movie) #r2 = q.filter_by(director_id=1).all() #gtk.main() sqlkit-0.9.5/doc/db/movie.py0000644000175000017500000000276211714210425015221 0ustar sandrosandro#!/usr/bin/python import sys from sqlkit import debug as dbg #dbg.debug(True) dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|set|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb' dbg.trace_function(exclude=but) import sqlkit from sqlkit.widgets import SqlTable, SqlMask from sqlkit.db import proxy, defaults, utils import gtk db = proxy.DbProxy(engine="sqlite:///movies.sqlite") session = db.get_session() utils.TableDescr('director', format='%(first_name).1s. %(last_name)s', metadata=db.metadata) utils.TableDescr('movie', format='%(title)s. %(year)s', metadata=db.metadata) def_director = defaults.Defaults('director', metadata=db.metadata) def_director.set_defaults( {'last_name':'cognome...',}) def_movie = defaults.Defaults('movie', metadata=db.metadata) #def_movie.fk( 'director_id', 'last_name', 'Olmi') lay = """ title year description date_release director_id """ if 'm' in sys.argv: t = SqlMask('movie', dbproxy=db, single=True, layout=lay) else: t = SqlTable('movie', dbproxy=db, single=True, dev=False, order_by='director_id', rows=20) t.reload() t.totals.add_break('director_id') t.totals.add_total('year') t.completions.director_id.group_by = 'nation' #t.mapper.local_table.metadata.bind.echo = True t.completions.director_id.autostart = 2 try: gtk.main() except KeyboardInterrupt: pass sqlkit-0.9.5/doc/db/film.py0000644000175000017500000000335611714210425015031 0ustar sandrosandrofrom elixir import * metadata.bind = "sqlite:///movies.sqlite" #metadata.bind.echo = True from datetime import datetime, timedelta import time #import sqlkit #from sqlkit.db import proxy #import gtk #db = proxy.DbProxy(engine="sqlite:///movies.sqlite") class Director(Entity): last_name = Field(String(60)) first_name = Field(String(60)) movies = OneToMany('Movie', inverse='director') using_options(tablename='director') def __repr__(self): return u'' %self.last_name class Movie(Entity): #id = Field(Integer, primary_key=True) title = Field(String(60)) description = Field(String(512)) date_release = Field(DateTime) director = ManyToOne('Director', inverse='movies') actors = ManyToMany('Actor', inverse='movies', tablename='movie_casting') genres = ManyToMany('Genre', inverse='movies', tablename='movie_genre') using_options(tablename='movie') def __repr__(self): return u'' % self.title class Genre(Entity): name = Field(Unicode(15), primary_key=True) movies = ManyToMany('Movie') using_options(tablename='genre') def __repr__(self): return u'' % self.name class Actor(Entity): name = Field(String(60)) movies = ManyToMany('Movie', inverse='actors', tablename='movie_casting') using_options(tablename='actor') setup_all() # olmi = Director(last_name='Olmi', first_name='Ermanno') # f100 = Movie(title='100 chiodi') # re = Movie(title='La leggenda del re pescatore') # pirati = Movie(title='Cantando dietro il paraventi') # olmi.movies=[f100, re, pirati] #olmi.movies.append(re) #olmi.movies.append(pirati) cento = Movie.get_by(title='100 chiodi') print cento.director #create_all() #session.flush() sqlkit-0.9.5/doc/db/m2o.py0000644000175000017500000000320711714210425014572 0ustar sandrosandro#!/usr/bin/python import sys import gtk sys.path.insert(0, '../..') import dbg # dbg.debug(True, gtk=True) # uncommenti this to get gtk debug treeview dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask') but = 'set_fkey_descr|(lookup|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb|lookup_value|is_fkey|instance2' dbg.trace_function(exclude=but) import elixir from datetime import datetime, timedelta import time import sqlkit from sqlkit.db import proxy from sqlkit import completion elixir.metadata.bind = "sqlite:///movies.sqlite" db = proxy.DbProxy(engine="sqlite:///movies.sqlite") __metadata__ = db.metadata class Director(elixir.Entity): last_name = elixir.Field(elixir.String(60)) first_name = elixir.Field(elixir.String(60)) movies = elixir.OneToMany('Movie', inverse='director', lazy="False") # movies = elixir.OneToMany('Movie', inverse='director', lazy="False", cascade="all, delete-orphan" ) elixir.using_options(tablename='director') def __repr__(self): return u'' %self.last_name class Movie(elixir.Entity): title = elixir.Field(elixir.String(60)) description = elixir.Field(elixir.String(512)) year = elixir.Field(elixir.Integer()) director = elixir.ManyToOne('Director', inverse='movies' , lazy="False") elixir.using_options(tablename='movie') def __repr__(self): return u'' % self.title elixir.setup_all() lay = """first_name last_name o2m=movies - - - """ tm = sqlkit.SqlMask(Director.mapper, metadata=__metadata__, session=elixir.session,layout=lay) t = tm.vfields['movies'].widget.table gtk.main() sqlkit-0.9.5/doc/db/m2m.py0000755000175000017500000000240011714210425014565 0ustar sandrosandro#!/usr/bin/python import sys import gtk from sqlkit import debug as dbg dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb|lookup_value|is_fkey|instance2' dbg.trace_function(exclude=but) #from elixir_model import Director, Movie, Actor, Genre #from db_model import Director, Movie, Actor, Genre from db_model_autoload import Director, Movie, Actor, Genre import sqlkit from sqlkit.db import proxy from sqlkit import completion from sqlkit.db.utils import TableDescr db = proxy.DbProxy(engine="sqlite:///movies.sqlite") TableDescr('director', format="%(first_name)s %(last_name)s", metadata=db.metadata) TableDescr('movie', format="%(title)s (%(year)s)", metadata=db.metadata) lay = """ title year:5< director_id - - - {T.a m2m=genres:5 m2m=actors:5:first_name,last_name } - - - """ t = sqlkit.SqlMask(Movie, dbproxy=db , layout=lay) #t.add_constraint(genres__name__like='sto%') Tbl = t.widgets['T.a'] Tbl.get_parent().child_set_property(Tbl, 'y-options', gtk.EXPAND|gtk.FILL) t.reload() t.mapper.local_table.metadata.bind.echo = True gtk.main() sqlkit-0.9.5/doc/db/director.py0000755000175000017500000000305711714210425015716 0ustar sandrosandro#!/usr/bin/python import sys from sqlkit import debug as dbg #dbg.debug(True) dbg.debug(True, gtk=True) dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|set|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb' dbg.trace_function(exclude=but) import sqlkit from sqlkit.widgets import SqlTable, SqlMask from sqlkit.db import proxy, defaults, utils import gtk db = proxy.DbProxy(engine="sqlite:///../../demo/sql/model/movies.sqlite") session = db.get_session() utils.TableDescr('director', format='%(first_name).1s. %(last_name)s', metadata=db.metadata) utils.TableDescr('movie', format='%(title)s. %(year)s', metadata=db.metadata) def_director = defaults.Defaults('director', metadata=db.metadata) def_director.set_defaults( {'first_name':'nome...',}) def_movie = defaults.Defaults('movie', metadata=db.metadata) #def_movie.fk( 'director_id', 'last_name', 'Olmi') lay = """ first_name last_name nation o2m=films - """ if 'm' in sys.argv: t = SqlMask('director', dbproxy=db, single=True, layout=lay) else: t = SqlTable('director', dbproxy=db, single=True, dev=False, order_by='last_name', rows=6, #field_list='director_id,title' ) # t.reload() t.completions.last_name.filter(nation='$nation') # t.totals.add_total('year') #t.mapper.local_table.metadata.bind.echo = True #t.completions.director_id.autostart=1 try: gtk.main() except KeyboardInterrupt: pass sqlkit-0.9.5/doc/debug/0000755000175000017500000000000011714210425014222 5ustar sandrosandrosqlkit-0.9.5/doc/debug/debug.rst0000644000175000017500000000470111714210425016044 0ustar sandrosandrodebug module ============== :: from sqlkit import debug as dbg purpose ========= .. image:: ../img/gtk_debug.png :align: right This module provides easy way to debug with print commands. This is not in general my preferred way of debugging (``pdb`` within ipython being my preferred solution). Nevertheless I found myself in need of these functions. Printing of messages can be easily switched on or off. Printing can diverted to a gtk widget. Methods of Classes can be tracked in the way reported in the image, where blue rows correspond to ``dbg.write`` commands and all the rest is tracking of methods calls. use ===== switching debug on & off ------------------------ The way to use this is 1. in you library:: from sqlkit import debug as dbg dbg.write('text message', 'text2') 2. in you application:: from sqlkit import debug as dbg dbg.debug(True) write() - caller() ------------------ from now on, each time you use dbg.write()/dbg.caller() you'll see the text you want to log anly if you enabled it with dbg.debug(True) If you want the log to happen in a gtk.TreeView window, you can specify:; import dbg dbg.debug(True, gtk=True) Logging methods --------------- Following recipe 198078 in the ASP Python CookBook (see in the code) this module provides also a metaclass to trace the use of methods of a particular class. It will be instantiated as:: class A(object): __metaclass__ = dbg.LogTheMethods for each method call a line will be logged (unless configured to be ignored) with function called caller class arguments caller function line of code return code calling class time elapsed IMPORTANT: since the logging occurs with metaclasses you need to import dbg and set debugging *before* importing the class you want to trace:: from sqlkit import debug as dbg dbg.debug(True, gtk=True) dbg.trace_class('SqlTable2') # optional dbg.trace_function(exclude='cell_default_cb|cell_bool_cb') # optional import sqlkit TraceIt ------- in case you need to understand who is changing the value of a variable, you can use TraceIt as:: commit_allowed = dbg.TraceIt(True, name='commit_allowed', mode='rw') and use it as a normal variable. You'll get lines in your output stating who changed the value of that variable, in the form:: __str__/function_name: old_value => new_value __get__/function_name: value sqlkit-0.9.5/doc/debug/contents.rst0000644000175000017500000000010211714210425016602 0ustar sandrosandro============= Debug helpers ============= .. toctree:: debug sqlkit-0.9.5/doc/hidden.rst0000644000175000017500000000041111714210425015115 0ustar sandrosandro.. toctree:: :maxdepth: 3 sqlkit/contents misc/tour .. toctree:: :maxdepth: 3 misc/sqledit .. toctree:: :maxdepth: 2 misc/contents layout/contents debug/contents .. sqlkit/advanced .. toctree:: :maxdepth: 1 misc/tutorials sqlkit-0.9.5/doc/printing/0000755000175000017500000000000011714210425014766 5ustar sandrosandrosqlkit-0.9.5/doc/printing/contents.rst0000644000175000017500000000042111714210425017352 0ustar sandrosandro============ Printing ============ Printing in sqlkit uses the ``oootemplate`` module that is distributed along with sqlkit but could be used in a totally independent way and does not depend in any way on other parts of sqlkit. .. automodule:: sqlkit.misc.oootemplate sqlkit-0.9.5/doc/img/0000755000175000017500000000000011714210425013710 5ustar sandrosandrosqlkit-0.9.5/doc/img/totals.png0000644000175000017500000013371311714210425015734 0ustar sandrosandroPNG  IHDRPsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxwx׀ٞ+ &"JS:Ył  vD,VD]V,btBIۖd&&Y>vι33Ν;.2*^ '+vHUtwG-2̝q ~>F$i'ިR3oԲhVwlLS}$|Q KQ@  IgDҙw(~ׁѿeӑK{JXOVt%Ee G/Wa؉֫wwZQ]KIJ, !tTx&EX *X  [PtxW $6'v&TEƒ"ۼ*X /8$k 4%[' ۬Xe^`559F-;~]~E,ƪ|4F: ?@h "f; sn7NG޿+֥LUn}g;M|dBQl6/;G͆(ѝVo=INvYW][ѥUXrdF~>AO!ksN<2Led֩@P~-z5/)sI;|{w}6yNuU2zm2/j|+m Ż^Yp5ˑmVf}m}N[cYO<\3\5G;*W޳\1|8FFA4h4׾A#I3obm8xMJ| { "/BFA&>>qFQcvdWsR0O.#MD%Z >.oB._8}Cgn0tx9e[P6Iqxem[Y\26, ,OrCzunOĶ憓̍dow, LMc;7*DydWpS8xFwQ:A iXR~eu 4ؖάzeUk̃x 'O NC@\Ot>AlݸGcTHM3noϸ*]ɩp³&vqY׳-멜+D3kb]pvH,hTYF\T4#HgnS)&jSB+z/-@#I߲v?e%lbe% i#ZV{W#cJl?^XcZ}j`K I+q_Ol̥cKeloraK펻my4,3w"Of> aKKjk7v{x]NiCωpZ%۲mK[)eۥoW]'kұXuWLG )( "x/򙞰7lʊGr*lrEP9r2Wuvx\3c{*o2JK\O#JメS*9~M<"SZRZ#oW:GGDz#n1:₋ؿ~) _|HVi.hOr\^owZɐ[b\i51,>Dޖ!w= >TtQI13^eNsak_ *3埤b:Ei:͓vkR{>/7.1q9t$>&If O͡S%:QL-[nnMF#){v: mNЁmN䝢GۋH=v[7ѳZҒEVb"W3۰Z͎;9fjʩZg8we(د:kBBBTTWNCr ;X 200a%TYA*:i5>qpH\+'E\@tl3T5 hۍV VsdE ϢkѲ|$I&d)I_@H-1dlISZzvlipkJćS+X1FAk%>#+#U!@%ow4Лî (><͗X]R[۵q{3lQ ė_B4V4|gG5YcgΗ894,ߙˏV}Q֍-U圹~;{;ŤrGO޽+O4]ҷ,Űl[׾;/_Yc݄gQY߇IG[1ȵ]}=~>@UO 4iK=<9'es1Z1 3eΣk`t X3w0kz@-.^Ki[UQW=H ٻWsd&^?|BK/[^7uFAѐq kKPr:*c-,ū/Z+iuʗQ ӊNSD;2WWO۲R 主ryS>-Xgu!yʗ+?TTvg*)hژW`Ə$e)5͂ ąY3x'닿IKq/lO)y{]_z/J/̦`ҵ/~:ZHŒ\y|[3|ˋ>bZ 6L|˵lO.Ģ`+93~_YnH5Sb15t6,H0}54mmMѷe3]I*lG>|SyV,6?Dp-Bą۳hb!R2c M273әV.[4hbĤhC#g戹JǸ_c]Y:䴏sȹי{E:y$@Sg_]qC3i9)D;(CL#fE& m"th*tchG_YUެfcx'_Z)(  DiL3}ֆ ~3PfQfP/: JvcO~HNr;D(*EUk%#UOH[mOU4M]HʱQfUQUk%ZhV! 6:lmGS>>ҭ _ KEF*TV+:MaoWWrzq烜~tt`':TUzTĭd%ݽ%UFbi]y v#$T\G2c5ֹcp{0Ȉ 1RP? UD'jj7OtvU'qm#0շEۼ.+S\w d&w,؈QeALk?zu;_SR垽!wx}NpI|y8|{(dxƨs]jcMפMfβS(hiG0=&TH{Z'_S)L$FIǫB\#Q6QRFwl8RDvB׊^}SݕJwp^)1vg @(1Q0~Yi EN@ :z@ 8K#yeyOVU@ hT~+%=5ߦMw@ &E^ Ɖ&\:{@ hz P09B Ya|xvrnoQ l塙q:-x.i,DcS <=v ]9E)Pٶy/ӳK7 %@P 'twx"':(;=ڵmtzPUUF]>ǎsxbdTPٶ1`gݾl֬%##__uMggM[lЭFܥl[@ 8W4gx_ rz߫pA?___ࢼbAӝU32fҍLw%_|sg"~%=g_w|B{zwEPâ:uN\e/ /LCS\\,zLe S{|줃b执gߋ"=ګ/ ~T6 wul 'NKdۂvv'͚Šk b|[6KL]k֮cثԽj?{~Dګ/>WD:.jW9}Ȟ}YlZ _HVmz 6\؅¦[+`˜txug_mϜ:ƫ/rMrsb6٥=쿇o7AGEoM7Ljm׬cܕckW]hcOeՖ^>}f|'ܕU]ݥv>V=[lVł7Z "x3xΡ}NZm{?0Gχ?P{=4[L>[oFre+xⱙʓ=/ey쩼=lůJRrkmkmyy8UoF{܆r.@fV&99D3kL{|t>6c3^l_DAo4hQC{wPN9OL{TƏ7|_Ck!y3~\fdd;i,)&{nu9fS0 /88l"#qm޾sOL ^wciN6|<]hެYYx}z6װU/?sr\uUz'_Ï=NC78 D)Z D*!gt sJ?zF{n<+ `ǮD=Lmpq~\3wLF=x*]*vۋ^GUU֬[O>xMWwi+۔zaa^m_@ 8x}s믙Ȭϐr6C3}Loޝ[).Gy/Mvv6Ͽ"cFp_>f$/2999dgg3wދ5S~I?S6z]moڝoX;b())au˛m\u9SrQl6+"+WuuV|+΋+.ADfNթ)Z}{-SňLZjw湬Q%1lXF {9nfyF-7Mf.q%WҪe ƍ+vwn-@]=+x(#]ʷ]:{Aʉjْ^xu9+mك˯Hqq F iwy}," O]~#yYiHsYɿR{d5VEE:v́m5#&_vzjSMKHNIaa" FĭKs_T 4^jX_P'nulkzO*@ h0/.m@ 4D^ & @qO&=z@ :8+#T^32.}M@ 4>jX_ ASA8{@ 8 @^ &p@ 4qSWܳzZ?Pc\bBQ5ψCiӺu}a [><^2ɚ<ُA%X,V&.߿s_E ޣ^~Lht:zgͷ_1mTu,Z7ozp_&֭_vØe,u%GbxOnό:+.˶[Yo̙2?XwNu~E:^{>f񢛅&B=@AAr"Zzff~ؾdef)j8QZZAoɾ* llVtz=#G__~bsJ;cj~/Gܲߣ2AķkON]x7Hc]0vH.1mω%D6M&(@ 4$I(o߾#@Qd|LFv45ʠSUYVJϠc\>n<Z¨ý"&VSO0~8/(zM6m.];3k?` STTDFF /?? zU_縡JQq)úmI8QDVCg0` _ 9 r**ZW;J(dB lıDDDZAOFFV"88^}/?bKCef??7^OdD99|׳mvN>ѣGIIIa„ $$$,t܅Xf<1+zU/EQ}YLk-Z_@Mhbbð=,^pp@ЈiGUW^1(6xE)**$8$"+ Q}VviifsK.ݻKxUʴnݖǏ-Ge+h<`8/!7/^O^el獧mӑ^-#Q% E YYUQ%0OYM A#a^4* fwg+l eLPɄ*h4ZlV+t%!!:{zCʉdl6hZ۵UVPPP@dd$F(4.Dn8A GABQ_P YQI(m,"@ 4Rz6a|Y(2,#Vק!S(+-C׃ AA(f`0ѹ(czpi(jG_t>N>=iӺ-$aZ fk,˔hb޹!RU^EV%Tҳ8Z@zZFb'onGXX(z+zӰ~ỳUKdYj"6E!ILۻk{CH\;EW/$D2G&$$Ie @qq HOKcm;+ȲϿ BfE IDATB&n#ڔCߖFÞvGБhѫFW}tt(3yC<,_g fHHgFHGQ2@_ +ؐA<< ?>{fOVVl2MQRTN;noۓn E]4uwW@'on-@ 4Nի\pH7 +2l6hrGO Jn>8tݯĶ}1Jf TkRN4?B!C_ʐjAِe6|ŵ 3+N-Gk?F S hVKn^.vѣW֫=!S6Uq'?i*Zh_>hb:v|x-jf2_+zsAiެLqtԓWСC?N=8r+V e:>Fl X9q:[jKlL,Ȋ[O" @8z@ hBgD@/V ë*2槃,~PU4h; Gyʩ}~#kz=ZTST\DpH0))' `0 2 IcbH^XtfXN痒oEV5F!?SK#z^ǑixpAqAҠZϮo`4@  ^sYuwYAq:C~hZ݈U?T!bKڄ|pRpgI݉kb:eY&66#GBBBL&Z7#"//^Պ^C; #')Th: lޓNtA[VO} nnITŪO@g:㼽H[yϾŊV]iP@ FaRTG$H:ݮ@~ߨEZo]EAUUJKKf3???  *kyiiih뭓N%.DhN)!b7ŻqQx4C-$ L.,,#G}uӳWT"+2H4mNa'hb:@IRj+EN%+3 n^]dR+Zn`WAW򲲲jSu zڅwG >Ah4Z2v,X̾ =n8o a} 4 iDIN);@𚳯O^UUY$9޺ՌԺJV,Ƿ5+DY2ЂҼQ2 j7L-(ii go2I@nQ>FZ`|[H=DУ5PPa9sVLb_  N%g]}W~~Ȋ\> TIBRUpd%H EVDwDӼP2)( 5]mW- M̾@`wkZᄌ'CCC8}: 2 4f|0Vnh hZ>zLz4Z=NghusFuUiAAl5h@*T]m4hVy.(e-mGQӐwVr%+ vj- #KUUuȡYmDGPf.#??ߩLʉY:zbd4j1zp72׳J_$!IA؃{eG6?#!88As@p_/,YUnm$/YQj4U1[X,,Tw vgkLFm.RNTIj l朰j(Ҡu $qWdJVQ$t@aA;޽*zY9$' ?6mObcc djw$Id2ucv^KNa?|#C1J,"3~ax{ _֝L&W^~%gshJf_"[ZdTT:uMh2q4?B+hA@-WQUFzem|V%"<Nǭcl۵v_X@qqSɄDddC ! t5 lт t9")Gc14a4G+ ڴ"&&nj}@ 4ko$/+B׬e?aa/z45Rt`%ґhRw_ nҼ׿z*`(+3;kZ4; h4Jh@ӣڛV2"''b*0MJDx8AL&iUUeȟjK;zC@pMf\Aݐ$ NNVG g1.+AG8{@ 8 @^ &p@ 4q\?oC AiG\:2 @ h|a|@ 8 @^ &N5{[@ S9gg&!Qr_@-Z',2#Y^@ G~n6GmBChjdqtNtzA!U @/aowEpx[G}px]zao/@ 2&BT?(__GyEvJ$ !$ijWE$ܸp@ جmۈjiP= @p2m"7+;Z ^  UU9\r8߭^ OUM_˱dvK?‰#U_߳1OFF^<vmIۄ@ 4cǓl("""ؼa 5RmlҶSw]0ɷZZYY uo6Z'b\۽g/]:w^ xU{{n]+=#= N,u)|ξkgC5@ x@=\k#gls {`aO`EOS^n}՜}YY ŰQӱsw$'7כ %w lSܩS Ȳ_υӱK{!ySN;stܝnJVv#u|~) LZf5*`"2Xڷ` ""ۊkx5KN<;gc~ի z^p1 ={гgN˯cy9^ÜgU'qXrY,8$$tp%%'вUKNW,o az[ud]w0W3z;o ɾvzzCGv_}^2{Z:@ddY3kܱ"S'TmSzl^7|o,"?|.*_pI]3Ѫ^+$דkҟ0|ur~];#)_`V/wT͓8ܙϿ(Vo+jx0pug_qб#x1+] ?{vb4@(z٥B^z5v_pEDorx?tyRNq"51z"5)>>u.5t֕} UUYf-3}-zݮܼ<b._-pZ\^ߦu+1l6rfLF#+vUvuJJJJ+kPq-z wnT'.xbN<ʩWaexX,VY_]|p[Q6Oڣ1ug-S `ĘO&@pKN <8Q]6NL> ܳh©.O/4qW^?.Wߋx|S\|EN> .K>̚ @zmGcDпfF<.d,%7ع+P6;1\qswNm,|z?Y5JOMbv\@P7Eߓ̘ѣj/ -EUPAENUJŶ P; 1m;tyfh4UMq2XjEQ +eTEqlcΧ QSCq'n@ hԫg/2r@APU?s?  *T=&ݺuas=ݺuaկ?2xUn[,Vk} JtJH +俭Y6;G|yyV,K} @ 8[-,g/A^~K@ ߽:r&m=e~\cڎ;9ycUU$B"2x9R h܈y˱ǘtÍ<8vo~FA8:={@p" nk.ZnyGC,lٺmILׅuӵ{OuzShS}^ H})?={2p@Nf5qa]n߁ ٵkWzKyysBN<4-#8g/{Z Æ ec7RfǪ jIMۚo[⭷9Kן}3~pUi,7Գ-ap༦CȉDDEaAOb _fmrOPO?9O?/CYY:V%ٻ`9NAcb `tʾ, {KI-8U,q"-r?Y;`nfBBB{*tMtѫZ￶$'pӔi׾ڴIr',)ԖYy~ tփmi;{է/[l63cô@ |fS}?."Zj#ٻw6\sV޴2-omfhѪ \x_|u{F;׺7*PUܜ\PUOez!fS@QAGdy>#O>喛0O_|eټiu)*74SocO.w]~y+O<`ؽ{\ĝF{yw+8y_"-- ײ~N<_vʳaF~^#exQmPAmm箭i'e<{}0sC>%?|{C?Wb.=2Eev{Kq1bA!DrӔ̬1<DFEÓOrזȘ1WNVmO7@Nduڹ;z>>Uf8ºYauhuz,YJq\}IwYcIuF>3l6ϾgM6iG6>s̤E3-[:wז^[qUiߞfqiמ+iii 81ݭGj ֑dS˖-fy_mmW͞4om:^ˆ_?G}9'^e|r͚ʤ&SXXX-_NN.͚W-y9z+t=Ul涩F\\K^y:^ZZ-(+v#?~8Wа+]j`oкM;&Z .˗#'2"y/eƍ5gCLL <3>g}YO>qWK\\wQzꉏ~ N`d.l\'λSsc̷yFa6no;KGDD8+o{^w-Sc6Np~ԕHvld_M5dSrrۑP[էcs]Ԗ6{|ݻxg1^WΉ7Eo6z"jBCC?~<|S~ȵ^CHșȊ!jiSY<28t[ma߫O!?Ç}6֯_fU{8BdڷoJ'Cmvl޼l\VR/M?~^Xc}ݫcWϞ۷U"-=-[uOv^_TTl64kVo3>}:s};wҽ[W 禛n&11:,^={)E8wҟ~qxzO;-*eeeM&))'#jroq23f>BRR26pTWٳ"++YOf\cR[V޲.ej[w=CcZQU{G!י=siK-Z{vr8FEF̻;qsZR\lR=zP59dE\ƪqݓTomƪs߷FݵQՐFD{\&'[6jJݻ'<F-+-v엕ZtV|{Z IDAT5)5)uԉX?K--.t>HMOOSO>&''׫֭Ul٬8_ݾ}z%111!C^={;wlsYAe2mŅj^N:h]v+/;֖^5wj԰0s7_5~y/U5u+.W_?OҥB/nW!7;S;͚͛5S57;mIV޲.ej[_ڧwo5<<\o_u=oبO燐Y4o>5N ,G{cǎc8jv1,[g~RM+uOQa>Z/਷j+_lܸǟx;wQ\Ee9i5k/>5 Q{ķ@Fzt.=95O ̲o?f.Ӗ,Yʁj7$,3!!$&?"88YO<ΕWH13xU.S:mNtt4֮&66֣w>'^…gMVTTIIIm*rV_dd$)))nduuO3|0),,$<&/3 ̄w^nv_۾c:u8k.dF' t;wFVOj1x`(..]|; hZ3O宻q1l6{a  JAA!o6~e : Y^̘YddfC3T57?BVV6YY̘YpeeLFL&IIIy+ G{.WyäI̞4O̞47NqM7wqU_ 2 Tsޘo Jh\Y$W_{{" \p3d+p xs??'9gĉܾ}۷;t*XYZA5oo_ ASJAr/|܂D⹿hּ;tʺ} F~̸q?:cǎI`llziպFyacaa'u֣qFzW˖8kׁf͚X7q XPbbb]8tƿCмY3ԭGh٢}،.oԬIU7w>| j066Ύ+bffV*12*k"ŋŗKϗv(B}L2 ֭K;@5{!Dtf/)ܾ}[ H.K;$! c& !^ I%+oԬI5"8xji$BKȏ?fvB!Ddf/BqB2Nu2nG?ys2nOubccYƾxuHBV}cn-+OSK䦟}"7$ _k^FUMb?,oY zG|9:9O}%GfBV3=f|g?FΙ 2?9BFUDZ _W=.RDZM?1vt-ʓ@FF-3vt~ҩKR٥ ,}.?2!^J0K`,bggg +~ Q$ !^Z=bGQ#s~(㑺0++kMS{i*n4lܔWVedd] .[.bϨY.U=?~4l*nݾ#gϞU3f8<]'§舥%a!USfanΔɓevϜ1L>3gΨ6lDXh( ;;;j6mLHp0'QIB Z!YM9r:$%%QG'gyĊWeݻ:ФYsvܥ.KHH%߾iG'g]MJFk' 755%33S˓SG׾*W~rse}K^#DYRqﯓh¤j녑CyۿN͛``E8}"IrIz U#,jצ{=007a=^_Qegadh*;- 3==usa;v&(0ccc֭ˊRmƎ 9~=ww<۳88|ܵ#::U*.B?bbn/je yCxΙCI76IvpUct~6Hu8D̂/0=SC0`Cz׽*3,J miެW7,ř:kydddR,{w|TwSCj߯/Nff&/\`חPP*L ѹ|}Frr2Ʉj$ .IBիN9:ghEjd$$[?=&ʭ0R(}cc,jSgt(.[k#jژ m۠oj1*VVV,-- sw+0ecw9;uCq&<|:_,Z7~X<==x4jҌʕ*>Oƍyx1:tyA&iێf͚6O_SacZiK)WNKlk٬jQ^_FF1$!ijRnݎr6<=p;2Oyu;PwϤO34q wةseS<ώkؽoeהŽOs._c޻5]8G5dggoK](ϕ+/G*PD ھa%>[vFCGkB)*I;ve;-p|\7(WF:/)gcؑ<8|G+ptR Ij|+++}}}k+>}e)uSCB?v ="$$;vHHxH?zƕq>#"ߺɷ4f)\K:w`߽+ffS+mz`D`m>zUR"ׅs4kъ4ڷkOP@@i$J${!^u*{?T*l;GѩzxDT?Og`M떺12BI9?*20X"C+l5mC f(e@OW\$DnŅh.hcQ&N%;= yD;DKuk9u"7w;=q !t'3{!^qΞrN_ ==2ZVfQeee1}gԬ] كOTqaj,##0ިUot2utr?аqS\v={V]Θpv7YDGƨc}}x\/$ !^Z|>1'Q3a}g6NccçGC`x._<Ϧ 8qB]1w.^b׎q07uy#ΝN .='$9rA׬ܿ?f;vN$ !^jVgc`hz϶/{{{Zjpttܜ)'˶rn%$s#XrZZZs3gL33S̙3 7BΎiaZǥM7-kId5Ujz;>>|4=⛯1B"Ҳ= əJ]U.cƎȈ];kx?7{ᅿtA]ީC7bBuqc୷ӨI3*Ws5 5Mv4kTy ̜ ӺM[ׯOrݟx9lDa( /sKĄ9g0> ֭߿Nur:voٸ5eqӜ?auYll,~o,[q],=prrʷ?Q\χ}9zPi"t}J|:v˷֍([r3epTvv z졑®" --IJz1ӛo]d 669]{<<<|2w$= G.]w0gg@ w rnIIIAaWujNLT2q$ܹOtzU}>k: TZU]jU֮a4hj5غuy&5kQwvvs q033ӸTO{\z7xQNE[mk[$cE}6UQv\]]QzLaPV-_W|CBz7FmDEE]zAl,X0??oEqpp Gϫz[]߳?ϏkWHOKAx+SBPز~ZI&&:isΖwBLJ,ٿݮ=?q+!,nܸ)Ҹq#u޽z2~| $$&2n\.o?Ο?=*2̼tR裡իdffr|gm_=k iicbbBTT~EoW^r/6܏XRSSٲgO)u.o?Ξ=Kff&W^e萏=SBQb~ʔɬ]ڵ`Qފ|xU~T ž=իAoXɑuyΝ}ml<nlر4iڼ{xhּ;tʺ} kW%߳ղeNƖux{oū-+OraM4Y8Vҥ)rz݈.|} V6ն^^,\0R['x5oߑS&Ӻui=!. Bfv^x%!DJ?_!d/BqJ/cz_ƘD5{!+AoFR>9ReWZmN/գs_xīC═lgiqPKL0B۱.^[]<kq$zQ\r_J76 F>\k?ʥܧ{LٻolRa R*>#]G !DDuGTv5=/T瓽)ʕ.sϾә0q[~ fxYY̜5kC:thOĜXo枱}%nˋo={ kTX1G; g|FdOdff2v(הO>e˖ڵ 066z<ə`%ߢj*̞5kQQ|h117b ^ͬ8fQ2df/x5v,@?oAS!; gω ))cG{8pPE_pvӧؘ2y#ΝN o#G!0`vϞCu;i7b76ƢN-*~{u2=gNÆPNgYI 0d=|>Íżz5<> #b===sT*R^cFVmBkB!sn*goWh]JE^107ǢV}HuoƑJ9[[IBdf/Dк>E߶d@?93I*8 #s+`2 )qp ))ڰCK"NIҀx̜p["?g^2QBN7/~߮@D}ˣHul;Vt?wFFb&(9(C2sN4seL Poǯ^Gz\<֭ZN~2}ܪs"/x>$ Q{MT[{I;Xl*w΁C(:Ǵun,jޔ] C+K"n*􍌰BKo3ٰx^df/D(=G:xH̸zΜ>kX|oشi 3" OFFuK7__/XXڡ \d5uI[;[ZӹsW _[>xxzˁ'gFƦ,|U<061i 68;2wgYWնmۉ3 lP|bEp;vd)ʒu!>2g Kl\,Ͼ\ZuqIOOר&== ] ~=M7>R~۳6mqFMqjǡ}`-ucccS>Ozz:cƎËudP}y!޷[F܈A4h0S?%9)=cE~cǎq}ua Eҥ y~3,īJRٳ0䍚5{YYY&L"$x*&&&5֒ҰA8~iiiȑ#ٽcGdN\bc}=Ysš9x`75;Bk~0tΜ:ɩNpHX5\xM"9qD=j$s';;[]w 23{NIII;r;P}k٬jQ:gFƦ:6LII˛1{0]T~MuGGGY;w˗/Fxs>ҒY3g4a?,Ν;Ԯ]9ڽ߇^~l]qd޽{,KvE( ԑbeḛs'ݧ?strЁTZWѳ{Xـ qs +W=:GmJM9s|6oŦ#cSmlQ* nvMV[a[nQJvժU)f!^U_,Z۷RՍڵ6O=jժ+Wʕσ&2{,^dҒ)'\r5$%%g ԧT*quuQoxJ?֠A}6nf ,߈gŊRo81 R(lYn-w$sNN)>O~Μ9CjyV?;B9rsCCCRS}vOf117P(ZP(4\m>X6tp _'sF4./wQݺuYrΞ>irQ[ڷ\rl&Ǐ} N2K IiicbbBTT~ړ}qy222PTdfeˬt3z8D% L1 *ɝ #66Tliddu}qY233z*C|DONR(KCزg͛LoYO]F,YT 8)O?!$%%DpHݻ,--]vajp0JRɧSMZZ&&C`Є5._w'yV%~===F%K3z4FFFWݗRdjpHP})d__-[F@mly]5k.;ӫX2y'|ruرciҴy35^Y>Z*R{!^fOϲs:tXs&Mhּ%*t2~\/ʆڶ˓ 4^yՌ;M[вu\n_RRRjusfmxUAhѼy~5j[oӰqSll*.NNh4!;;;6kAf-ppĠ@mύ $4wOozM W|6>>| N>_qߘ>]Oaܵ>g33t`OHy3+goWh]JE^107ǢFqdRCk|Jy+ p0z<7~5H5iШ SC{n EPP( aMZoܴ vvvL e&m-74q{˷ HNNɉs-,ؼ%gv5LJGӆ >rk ssL/۶)`w+ GMhd/DкqxFB"@)\ .ϥ^Gq񨲳a5wi,6 -f%3fiiɔɓ}n]̚?GԏZ+J\]]ۮ.?ӵ[w=ptrË|~2KvhҬ9;v*vGd޼dee1g\Fajj͛ذa# cڵ?~͛OV>뾩1}{nݺZ%d/DS$SRa3SR4/39+֭H7ğ6qT7jJFr2F 6%<;B9rsCCCRSS۷o6:(R(m_VkÆ3h:q7t<**ߺuew9{$çomwhߎr1㳙?~ɷss3\]\زgLLMx[deeTqu,{{{:2E\l 7oD)`OoV/Yu\"/IB1r}uř߮Jp8&43/mX6j9(:Jh.Rɥd?zT+{b˖ILL$++7o2}gf=u5jd2RSS'hoMM@P`o8;~҄ hڬM"?7P=ѫ7 4(p\:t`ࡸ{z>/iP[UwZiRo.vBbb">>u>Oƍyx1:tй+_)J^fMT2^FF/"ǥId9k:-ԟ_ @9 '`ݲ9T9| QtjN7~Y5o׮%oro +-߲[7:blg/xJ; PQ6H52vdi (Er~F!($ !e${!d/BqB2XtB!xNdf/Bqr47%TTR$ !e${!kL Q8VZ 4>3˔X${!kgp222\pp^ER%]w슑);wյz[!JCd\\100֖.]:!rN~3ǧ?rjթcǍ'==]Ft򮎗wu&h7Ubco2kv6`!9uS'Ý|9j |<6Drĉb?zHΛOvv :d0׾S$Ν{n/kpYt~ cp,tH@DJ[]%w2~| ^03/OEGzo{ǹsiݪUۏ`A $!1*ɓ`_с|ɧT*ˍM|)*>1>oNNZ4hԄ!ܽ{HP(P(a&7nDXh ;;;q6{͛cbbeL8[Ą[ $''܈9Ŋlޒ3z{~A +b,*U)C ÇlgN.ұ9c:7|gΜ:yXӧ/ܱpi{̘}bhhXp |qcéS' ~8_ \(=UkΜ <9YZZ2e$~߳kW.f0"}?J%.mWWO?ڭ;^8:9Errruz{m4i֜;w;ѣF2o3w.#05-쪍,33S˱.}AuPb{1=*U```={ku4®"~#ԧ=;xz;i|m} :ѱC,,,cg38tpWaa8::HXX(߯\syn=/3j󢧧;B9rsCCCRSM}vON!@PhݗBhsz4Z 6A׉ܼͥ5άVn]V|O2=|ihCv+W־8B_%fM?#GjB8Ν;ٳ!$$xr> !m]B<{n4n\`s7ܹs:?Һu+*UT??{b˖ILL$++7o2}gf=u5jd2RSS'h[vFCG}>ͼO'**J}5습KfHk&z5s<-Z'5j\9s _ыҖ?⊷3 IDATڵ(t\Pbv퉌[ deeq LƍՓIHL$!1qxwb$,Ys&N̯~7{}䬁#..s9133YgPrrvEX0/XD1)S&v:j׮Ey+ZaCVQ]',,Tիc%GBCCK$(h111ԮSONr:tX#):͚Qn=ԭG-8pg!`1<paի[ڡ\ !^^wdIn]ڡr^g5{yb;vBL.!e${!d/BqB2NBQIB!ʸbzRq!+əؘ+q,CfB!D'^ڹw>SChԤ)ήUV& xq׳*ΫWucU4 r:{.īJs!>2g Kl\Ͼ\ZuqIOOר&== ] ~=M7>R~۳6mqFMqjǡ}`-ucccS>Ozz:cƎËudc(X6FG'gklЈJ] m |gp94ћۑƆŊfL|~ڳ7d鲴kߑ֭Yx|~۷ aN4jv֭Kf-S>)6tm~5[|7...oЈ Z ^zYx(["]uN:նuk>teWd֬|R077LtUo;>qp(HҴyK4Uw>>w5b0իKQ\ A֭mmh] 3x#K+GxNѿމ۱` 9/װh2Zl35?`E8y5V`8 ذl'Bbj'rWRܹyͲ~1$$AUbؐXn=cӦM+V._B iӹvSd~бCCxξiҦSe4|lbRs' Ç!..Ki;ᢦMYh>Ec/ \| Y|:jF+~H@` g:WaX3DT*6oJ@@ 0E8oPBTrHh(5kjCPdrBnǎ`g쿁gkkK||=..;;;ϜY~3sgQJ"KRx1֮^ܡEsĿX6.-]So*+q .Qr%ߩVF`޽z2q4bbbg>h#Ie?}>%KWv |rxȑ&N gAr_eO# 6,>?!(8uILHu|B!D3l|!Dֶ]&z{{;@f +/;!DBaݝŋ&McggwǟGt@H1k ܰem_; '''$ !:L4?8Dw SDqϑzGEEQ䋡%Kstԙ+^2*4~~-Gyּ۠!~?htGdɒe(J.^aCI}uh^5=lH)"}o5BAe9ÏY/_>5ˏ=zm۠qvvֻ/gggm JugCЯΟ#~o@VYf͚lv׮\b۶[jsܹsv-\0< Xk8>#Ɠd,>B\vcϞDT9sԩR ׬%110'L|~~DGGtQSfoo?UCL%**(NczIJJ+KKٯ!ÆsRRRPATůP(=j$VaXZ~}fHMM͛ 6ܨ ,ٛ22n+T"mѥkwa*O_K9hݺ-oD^VL4E\)Ɣ)Sά+.**2e߃ǎ={h~SJ.KIHH`ݚ՚:}P vD _knݺ4iւ:XoaCЦZ3'xBߠEw1Ϗ+ҥ[wԮ_m[OQ|Ef͚+J/~sssʔ)Mn]èW.z|Dr6lm[6x U=q07/:K+&ڵkOe|/Ç8rk֮L`@c}A;FI IHLd֬ܺyK_ߟo{߯`^sFݝSehӹ1]_tAkuiџ&m:Y~?Af0~@@:w  iysfϢTRL׮]}zmZZٰv:*T]jzĉlO`kgOzj\b%˔:fݫ|fcH֭\.]lw'޽Цuk(¼s8y73cnnn1c .y&Q#3_"T*6oJ@@ h"dIة F ߣB x8Ҡ~}iMh߉壏zоCG~۷#2".]0kGyud&'j摣G9z"CСC 3Uzt_~ZeO_np9DNڄ^$+Vd%O+O '$$PBEO11,Z_~ݻwqqqcL2GVV6<ɩk]6-~fXh 9\tn{/{(_|umlICR'1!Π;ǎP(2|9Kx39fԩ4mBXZPщh t`֬\pؘg <ϟ?ujDo?O$DZ=J.]ٲ_g#..N 3{ GPBSɒdqO6t(@TdQ* *VȒŋ8vǏ83̈I};vO~l~u\JeνX.PJ˯^Jf͵BM$'%%bmm5ZҢe+vJ%gԫW63#W,۷MffSm˖`I߷Z<Ӭr2'N'44P|}ӧwo˟Syyܕ"Dbduk2ˇNhٚ mgI|};իkB|B<[nhx6ǎ˻j!}63"o!885<~şE;RbklߧNfĨф@f%r'y"G;|gD^*$ + 3-Q?EaV +sC`Ϙ59gDx${!D?\FD̎q5؎/O~ 4/qʕ+:z!q{$ !rQ E&9UEr_Ά`ٟT*INQW܌7g6o!qUuw3쌋 3g맟NIkݢ)Z(666 )q̟;GkW4yF bmm}LÑG !qf"w[9Vi'Q&R07SP(Q<c\R*)*́B&>!R O/nccWEEEQd rɒ%ҪS;:xM^ٳ9{6W^%!ً2aCHE&gB2}ff SIHN%>ERzBS)߂q T/](Sbrvv&({Aś φ0_?.?G nߺ[gU"wd/ȱZtνH &f %l޳3TXSފAQpϔ:th4__b4_:v`$%%aem%AAxyhϿf{"wd/ȱ*,Hn7E(`c9=ٟ>MJ0{ ʹN@jQd漝s7...oЈ Z ^&ŋGҭ;uj*6tm~Yzŭe&$xW>MQ{-Re\M&6!*jȧƒ" ',"&XZga/>dgNd~u)Rb%ѷ@BBB5&!q*Zf4?/{L>SpzEGGSJu[Z}\l8xdᝒ^ e LS7)bg&z7!&ƏÆ n^H*Uէ$DVXrcF^.==} Y 9q$Μ_'8v87m6Ys3ҧVӃ  . `ׁ AB&|TI==o&ˣedNi&xR9lܴ8A[\ ;;;vb %.6*uIIɌ'ڵK'-A1'&&Qd#B yABb"Jad7kߩy9rr^nݺM=8.e0. -…<ښ)={ .6J: ų\71kA1'$j:&D.qԭ[[g7Y7 .8ySL0[LPW?fi'EKYd޼9pvvu;v?lݢ]>p3~g|ȅ._τk:j숋3M]LTL fzZ^} k۽.""ҥJjK*IDDA1=y/We谑K;vA cʗ+-(sqqqlO4eKfnBEsĽ U ^T" ,?= `=w>"7.-]So*+qE R׮]ei݅w]'IDAT!eψQcYp.%֭,XP;];wgkV{"ݺtֽC7eeWn)D^UZ_b iyyBmϏ{7s6[*| <ӧ'长:k>~P}!Ddzh,]u"4,5.N㽩V;u`:ж }[8:U"8i*uk ֖S/.}:Ufo^Vz9v;2}=no73cT ED3΋> @ݺuXd˗lWxf(:vÈa53WP(68ТyIFFSҥH8ĵkuvSRTLrnVSk_xxmHS'S43iѢ,,137P{QSӭ'O-Ӷ]&z{{;76j8*V'+}z'Y~ewHM)}B\s9=!r 9B!8IB!D'^!$ !y${!"3j6U~S!BU^>əBIB!8IB!D'^!$ !y${!9!ϐd/"Ml%{K+ -^v,^T!!кu[qu+J~Y_V3id qSLEV\nieڏ^MWFR,]7O}nki5+8,N L'0N٫T*֮]ǢE Xn=**;"OZlc?Cȃ`._{Y\xsQ6lrɉZ?"9}:m1~3Ǝc6O}HN^p֯Jj{b39ZQْߏcAS!8Unie+)]VϞ֗7E݋Q+K-OJJba8٥C ')))K$DNw/i;;; 0oN:̘ᇛnnn̘&EwP ݻȷ?䣏h:okkA͚Y9Ww1m.%E!peQW?N_򋳆vK$[56t(C>5׾V̙34I ̜9o}NhH(ׯ_ڵ+3}_tFСC[oPS .pu+=UUg(J@d]n3x^vFf޸jpPg<}?o ǯu?|{Vh8ƷQϨ8{,ݻwcIRT)ME Y߼Ymovq`ŴlՆyff.]̸uⰳ,(P'O .T*~:y;*rOzfxx1I{vD?4RXV57Nڮ&mgk׮#22 {G,lwp$22kisssZ tiUVti"""LȑGܥ+[6S|y숋{|<=w˴~-[ '^jL˷g^BC w:}z6w]Fjj*wet):(L]+eӦ+>!!#7ltƚ]X )1|>mƘ1y 11ɌHݽdÆK_pڸ &]f 涟}6kl{ʔTTZPR%()1ÏE SrU*W[Q7OB6>S'OѠA}MAҰAjFM7jD .Ա#}f[RBy/g6:g/V ++s?%K:hY{eO&'{S4R%'jVSë͎{eUf9f}K)7xW})b ^!D֪U&MHPx?~qbłhт|_~fT4|lBK"Og !y !93mIB!D;ZZBVV_,T}VWly%-B!D'^!$ !yQSLB!2LB!rISyk{~ԝS'e=B!r~l͚0mDړd/B0|b5kPFu/Yy2IB!DdmmW^F_%F%BɩYy^! Wוd/B!3T?/^β V-GIYY;@ϿRYN[ B/~ kB!D^'^!㌾f'`ccBa1ŋݥBNUjVHLLN&8vȴcEiBΘ) Lwn^TrƄ"BdPRݱ;ԆJ(q|88::LJw\|5OTz @j B!DvRUi毌03SPم<{:u(?ΡQ;TiؓB,֑`PǏLҖJ*ZнZ&5JW<3{TȀIoYa54.7RbUs42CuT*%/#:ƅxl:ZȀ|BH0~DHOX,,2)0Ø,߹rNa|!\@P< {BdtH^LŚuTL˿Sۗ:j2_ @VR*0jU+=Eհ9 3xRL-,-R>w_"=t - BM@F /gflM` z Ukbei5{!2"8,OO"C0j/Z,4'%%Xݶg,t~Wly,K{6 ) φyiX޸Bè3xf_R5Ӝ/f#q>A/3{/DcBąd_|n]=O5p(䒡#Ԩ䚽B+_R}XDqƨdD !ll3ֻgV d퐆BaT͚9a-<]æYx zNh o^+$B0\!${!"d/Bqi^xrV!BYkLo!D.dVE!BNr^!rL-ڵ74tD%#r[B!IzmOSvE!0^N_yFu153u keW !9dr5u(m>h:B|o`Ǟ_ұ};TDӧl۾KW0w ڍ\yrl,;B2[JJ 'Oиa}臅f4#"z&\25jժ2z0Ycz>8ISQ݋QqS߷7{~T*lΡCDڵ6d666;[[[ӿO/zT#Gq=غm${!ጚ~_,cʥDFFuvEܵ;2k>_[6~o^{Ѣ|&ik!l嗤QqΝX`.?n /˷_ҒM[^lmd<~5G۶ǏYz%KkZ\=sfLm]~e*VY \2eJ| !0?keF%Oigr :80=~B6+.jU$#.]VZwUeێ ۨ_5_oш`~89Ɔ޽>3?eЧpr*m~듡x^ߑcǵO%>qc>tVbͷ lf]RR֚e jO!D|Ĉџk{_P񼺿X.˼Ǎeضc'mi>,bRQ5IIIKXjF!D YfTwpp <"7g <"t/X}{Y666$%%ѳπ ;_>sƌKY0gfǝGǂ̟=BuGDj%7`W*/_,|ƣVp+WeC[&(Q8{wU߻/VඅB0x`b´>ʨa M>~L|ͷ4nX_Snk!Z<}K ,,-d՚biӪ2} 1ڴjkR^PД7}= OК`L<5jȆ5|A{e+J ZJ*e/0c=@ev=zģGزm;͚7^!{q}#z|qMy2gIIIk#o\Bؾf1>hӚ4nctԻ7Gb(Zԍn]:i{tF @.(ug :kk:o 5bńGPcF0/60rxoښV-)oݲ-K"|o_Ѹ'N$BA}Z4|ly6B!ҧkB!˙BýC>JB2t0Ӥ̈M!&`нac'O+-2B!90rNVhSSR3&!BHJ{K~?|Tѽkg޼Lbl L!o.:c7>zMfM&%VV!"79@d\荒(&!Bб__4 IENDB`sqlkit-0.9.5/doc/img/viste.png0000644000175000017500000022567311714210425015567 0ustar sandrosandroPNG  IHDR\5\RsRGBbKGD pHYs  tIME 7 IDATxw|FB@zSQzz_{#Vz] bJ AzI%23,YH$H lvv93ggΔU?7 )rosR@z(5RՋWjkJ-ȵj,J ?^1 ;9EQC]6 t]8 ;rf/W,uCUS@Vu@FŧLnUSP50if]n**& n+hU=gNR~wޯTk3 LX9N;2J4N%v`qxcgI{'hΌ %aD7DP1:&Å($!|գ([ǝ*DqR2JMdw5mHhd>C)s29k 2cxqJ5NˮS@U wkl]S_ALVMJy.E]!BDUdIi=\*M!Bԯ\(hyB_ F{WxEbB!wn9/>Ws/FLL4cԠBjPK=oT!*0c2߻#((*Gg:Ϭ )G!Brݠu{溾I+`3]nx#c'}B#B!nfz;=r)B0GޝK{;o9OM~oq׌$!BH<\"gEM5EeWegNEaAj񼍠\NHJBH"9BrݠuK]Rރ OTkHV 62$l]J0FjZvU[P<ϚlӲ1]7K `[NEU*G!9,!n亊g.FI尿i=nY\IN[ۑΟSk>Sˮ|kcʛgMq&Op <_ڊzr^.Yo#\wf{9sB3fQ\<"5QK9xQQ\qe>g%e:Ill,F璉/:1}_"55ƍqۭ7eןw:5-s5r 3+eWwWYV4?u%|\UMyꍯrʛ:l8~𑣪 j=BH"9,!n~u(Rx~Vݚ_~a [o=ߜAÆ Yr^n{g^䝷^nS6zE,62|nff6|1IexO7}z駞Ĥ싙lDyJ`mLyYtM+wٳ>+/O>㣏\zDil_e <4^{ ~sXw.ǟ1"OrOuA3s-wUVM[`x l2 xކYXΥ#G-w.gy,t)r"GY$gEr!$׭ϹznY璙VUN$('ާgE)w~2SŮ""‰纫`ܹ>4mo2 l6) p%cZph/o#:*pnr 7oɧv#y.?~V{ŋO+&aSa))WEi?;͏?-`ܘQγO裏gx?L|\lq*Ϧ-[yvꋸ\.nzvvʲJ:@Dh(Md̬SBw%. ?_6nLΝشe+w0tܙ̧>qU+47.gQ@k2=YNuUg9U]vY˩J#,H"9\D/*((꩗o2z_T_zF xǿ8v,ɏ=B]u/G.oGjZ.~&5-<7o&.wz3?eFGGInn.z:njŦ-[iroyǎYd/u%ΜO*_}Q)U!$gErY'-ͯKy,*{l>ܼʶ릲XYI v:*h܅]7IRw={BQUЕOp806?z !f[#GbB!,m.g(ZhMs{ :w=?DAA5~m.|L6N]{2jE[oБcص&\Ν;8)9})Ot:|?dч,%e.Iѳo#CqmwҵW?:uu7Jfff룲؅w/ }x'O_^yJg9AC{;ʜWu갼ul;[FdS$GbdAdvFέ%CJ~ٲm;+a٘*]Fe5kڕ=oeWeU>Ǟ|;ͫ_'55gɂy9«oOK2Y2UYg6va3qBRRSnNrkVD-:5od!L-mZ2<ԓ2?ӏ}JS)s^*ʶeARw={$ 2 Cվ¥RY_abaa!m۽[}4Gb'啫Ϟ3[Ȉp{~}{cY{d55x2owg5|>K^e͟L( عګض}|euH%˫x|eAPc\$H#GdAow{wy{^1bxߕPj^n+::;V{nwfVM4ݴIc22gdfҤq#啫gd,?6lWm>*+S&3~|B,?ˉ,wTm*uXOAb={BPO\K^7hЀ/?Ąj-*˯콸X>BfM8x01'5.g[ ӲES{cQPpέq}TV6OyN?b33yṧIJJ*l,RӲS%fEv+](={$HB!i.ag8xnvqwިØKdff/Qýnj /LVVL}rUQ_8^FJj*yLw\ÁffpL~*yCeeeb2vH['nݻ֨+.5/kuxux(**$5-)<3>2"U.eA3\={$ 2 yk٣W_3z}> qq 5ƑϽwɠ;RU;nV-[2ne >F zM}I^xU]{=u DXizvƘ.ao߸i3<>{xiy+R2\F~k׷믽]SJ( 2GH#GdAd8eZ[ZCvFw\d:ՓIE!N=:~n^.:9M%HB!\w|A{rO3IBlW!BZr%R$$B]B!J3Kj}]rX.$!BDR[.B!N7=B!+,pBQWHB!BpBQwHB!ABQ#m:v%!B`up5pԞBjH4{B!D}uKbPAKue!6B!BEpB!j)!BT!B!B!B!B[B^rOB!+\B!B!L:\B!B!ڷ|۶(wc$%T8?3|ZlY+]j {)< \VGzN߹rZ:WD׍jM J?Jb=B!/m2if,i旟s7V87x@Xh3?|c@ vyjm[l] cؼlݸ. #Fe5 HhL.\r̚qiK| C'??܂*OkZ0tiBy5{j={BQO:\j6+*Ѯ]v܁kmWJ`~bLy~>8Ə'**<᭷d_|V'.R&Mϕ|Ҹxݣ[`9@740oki:ith߁1'ٌ1m|0/(dѲ-\( 0[%w}ƬWvqh:~&f.Ɍ3PQK_㝶nv}߿NfՙFp8$'>tJ ]6{$ bB!.zegv <_)c4-4^u d"?f*Gg<ĉINN&6&Mܩ3IIx)pN`57EQQ!QQQc2X-l2ѧ/KC. :Koaͤh֢%ZGnG5$5mt{`I$=s_ٍHJJ?yٳ..W'nL P!!!4✜A|]t'GbO!BAKJ\Ƭ.]4 MsrDZZ:a` h:kEKӫ;ZFQ\.M6C4 &223/( %%""kNQaf B FMLlWaۻ#N=m`taqY0oޑP4C!5#sIr0|@KPUuA;N hGSسi Yinc ]ز“&'wabZ?ØC"wo&Fn(&&'<"""E5kEv: ¤;07NG44 thNН5'/9/9994Aw{t(g@CgF>pf3H%F?`|h9F:]Dݢ%H쩏G!A`T۰a .O(ezyR+z IDAT<ҫs\{0@e"Լa8u:Z;ky*zflk[Xv6q@̤I}ı/FU=I|8N ILH$++v:[r7ѠTCtp(%pc.N0\(&( "sz]yZFQ(L(,*QV[쟯b#"={B/;KI9RcbklC/>%*.Ivhn7tx$hڌc[M chf ?f~P~,5)^? == 5& Urx5XK:\Da4:a(Dz$l3]&3ڰ/=}:!cSpa F1=w( _rYhhdh1Z:]D#!B} зZm޲v fN޽HIII>|?@MԱ;}۳o>zvƾ.44?005vlYb AMALGZ~e"=AR$Ȍ-Ć[.ẋZ(h[OGDDD%hP V q;^:s&Y!!4>EФIbcb ܃/yyy1E`R=VsE )S1+JpQNtY= Rd}<&(pH:{$#Bpw, &p ڶkK~A>1޽FIۭt)$-DžfDFFDG`onbbٽ/m$iKPT ¿~(ljZf PM&TEAQ@1 ׃Ӊv-c`CDv;fٲetHNfೌ!=E]kiк;FACPq"C/PJurB~7ߎ{zb}4 ako;!$H쩣G!D$ hKF*}A|Lt|F{nbHNNvL&3qddd㙇˅l$b5H~uMGt"""NpYDd(KKCmq0!XϿh.Cn isPo?DewPy dί[R9gjYt\|vwCt \rf[PphL&&UEUT@uMCq:½r%#G&yJQF4kM YFFFI ~~A#t^ꊒNRImRdx]r;^4<-k as#%W$HϱG!FIl~Bu ànZItLTYH'5˘"<d2;~X%trziE10tYtj&T@۽{ CPp`ZDDt: Yҝ.;+\Rb͞9aV4lϰ&O]MbȻ3G j;ٲ&{8t &GbO0!BHKIou2z`^])r88I].visss C3ܬPT 4ad5cr<Ⲗr5PP#+Sx_ 9|)|ˆphj¹%ZY;Pa'ITTT@%**:wMIKMù3[Jgpy={B!.&:˨)Dsks rAAull GprknaahV D0g{&^CmImKSv`<%*#sf@jҤQCUpteE0XZPMɌj2c(&y ݫyAF޳ժP8t;M|~:&Ѯ]{9}D"""Z~Լf֬%h@Bb͚6=- fP4l$H #Bppz.6Pn RJⳙF8b a`2٬t؁|1# rӰaE"rrr|9x $44cܳffdR>?N{줏BF[ν#'m-t12QB!*T/p !BQ-EB!B!&.B!B!VpeB!B!‰RUrpRAl6cX"Zl;5緇Ke !B*):wfQIA~n;*EDѬe;[XqReCdiTJ=g2hHϥUR!vh;5u99` !Bᯜcٹ݈mAnV{olW tT_K8w(5mR9]8r\fqR)v~Z=t%DB8'(mG!Byv£b4MxT,*/K~SبTDjҼ)G"jJo0j"mGV6n"":N*BԧM!u#uR+ZUUAQ}"WirQ2\Zo;mGU!Bzrm.g)W!B!oK4[˱4F@+.B!B![2 ?7#/Y޺! !BO˶9gӤѲm2]z,g˶ɴl|UuoluəwB|D7<Կrh[~Z!B[~OygΝ:(]} ,_xÇrkdلǾ]O|ZMrEؕ.`28Uc!.B!"xmڼ:win*kQ^9M^ +QN-E-&39w`uqmsN'>$guY{Ot:}/xPw°Qc}z[.:;?wNi^~^}ӡs7~ NY/!jr}ppC|Vw=w}g#D3(**dȈѴԕ /gy1ŗxPuBĔ}ߖۘpԅ> dSxnK9{9lLx٩ Oz*O]xG޻;u;7f1ԅg>>3x#СSW=?Hֱcun[_Y[ ZH}' nCnt՗kz322h6zyoUI/~{~e2ZM&3+ edxmإG蔨~+LJW8eg_SGwu/=C8a:}rw+ݔn=0{cW{sz9Sz;\l(=w˯N'%5_ďKrQ^k>ӯ\}u2~&-[Χ2zyp:,\{2O;o/rJ9gxϜv*s!>{ز3SXj5u/i\v{^~3?}OXX7?s'nݎ%8psТ):u̔ӛ}ov9oѼT|JeS^9ƏW~z&ϾO=z],Xߜ֍yWٷ[nd߭{ӟvSh\3bň싙3dDEEMr5Wv%[⹩/zJ64uxƟ|.3;\yI 6\9 . xr̙7g>CRRCv;]s5<9̜S&?NÆЃd_.L &{ߞ;O<0qcK"8Ilڼ'xÇbZ޽_gqۿسw? fIxu rsxiܸ-7Ay8:&ltLߊj%""&Mt֕K&N@Q=YXX/> Qдx%i.x'f1qEYm2=pfñX,:tػ32xi׮-\y$㉏gmO j֬)y`kzz:ҽ)ejѼiϲֱ)glY(]xwOPUקB>Ow+ݔ̳s<Uk}v&}LSt|96z^V\]r3͙8'ߩ(K<]J?y=%::n{w\L5mY&dffL3}YgjjC-Κ]7ôj٢v\\9=<_v8#j ;U-; T`P6[G[FV-OߺRrϲJSCJ*://-lu~S_;ذa#/O{#ooҷw/eg _'r.O8)~KCYl-9{ܡ)e:WOyg?=eg_SG'?(WL_~VhR=}MuWW=?瞞Rna @xx8սw4gxN]*g 6u 0J/~'ƴg8B{[xKQEƎ3Ͻ@Ff&<3Fx8x͟;wr=|F)aEyHII%77g}{ܘQ<4#1:MڎAgwv_ϙ^s/`֬q:|^~6mZ}y,]Jֱc<>)4MnӢ>c[AI- ,N п*>AfV|=X'?Uŝ3:x̘7}x_Yx1Ǐg?_am>G<[8N>lظ eu9zRaT?S^9OdiQs1ŗPTK.7\Yg~|Y?;o- IDATϖW|յO~~>)aat*p`ٰl:|Ǟ혉`}U4ߩK<:ߠg4LLy덩%y11< 1c6{.> N~6!22Ѳm2;ԃG.%OW3Y6uڬ?u,q5HYݹ[3j$o͘SΒ}M͉gUu\M+3Ӧ>sSѩK.gM7\ϸ.9iY4uxN]*syzP5W!;#E2JxYn 23]M>c߻w\Zm a$&DȎ^'Ҷzx?7~/zn2qc=4Rs6nby瓗}z~VgOC˭$lO)߹J{:~glo)(-u'%mU8}rrr٩/2Lΐ !=ȑ#Gq8|;{.ϜI- BڬB,U ęդIc.:"!/{JB3$2"͚2~&aC4;0VQ;*z>ؾ]ۥY!Dڱ Z/ymx|jE3ګګɢX,w]}H6+8]Zjiz^k}y_VkjL&IE!Msc2"jRvtmGf;4u @.RR6?nN*"WTQ|$REM2o(CͶIRC`b۩~WrIuLN&#/.RR6?ԍԉ_egdfj Im1Ϫ47b73xhZl;'ێn_.~ZcN2.NS*_!BӣGw3wݻw'30R2޽;)wW{[PE6gCaȥjAd2АиisZl;]/kVHN0 =mmt+\B!Dуѣ!УGߎ$.U0]zұKO v9q카Qm7Y:[*[!BT ';.?ٻ௤fR*tKJPXugR(Tr)E.~.a׺r ͺf(j4FCI33139O39|rQ,)PxNC""E]}nܸ} [;{5{.ؽ7!ih>LcǍGfV H#M\z[R[:M%H P\\, C"EEEx9b1b1>=\<~Ŕ^L)L]։SӖ<P#G8}O?'z݂?/>ճ'\L_w`Xn|g\3335CqaFcbSm4Y,/ I;D Ii=KaamHIρu}j6GV;v8–QFׇHdC p4[^5L" (h!;#GƓ'OMsm <"55G~6a֯Фisŵ/m/Qkћ[~s1edXZZ@ lL+ D66v:YT>}`bbk+!i;alb&Moՙa!m ^ԖNۂ.e[4%iWnJ &NѢu;0 9/nqjRnX,Ƃ`i6] X,C5DkѱSx43gԻHMf͚_Xw(230p@xOVfy-z3GŰJ/Zipe\7R`%U>VE:*}ኍ[N ?a§X9Y8z$gΞՙ`!m$q*>jImA-%jӎ˧1a\8s 'V [Go߼&iuHјC8r OÚ<FrV\r3 |Ulx)N{ 'x,>}:ǎť gѯ{p4͎ѰAL[{դÃ+Laݺppp@ݺulY(~rOzxxfff Y}6n{{{c+_LNn.DVVXf5E"X[#"b ~^)G7x s/?W8Ph4dgg[6oҝ`!-p-Z4ZRE܇n[. ٻA `ei b}rJ1_*طA`iaK ̫r Y ;;[BL7yyyYH˗('R=%177Chhx{#FVxM;ٴ!Gdž[?}:=zB= + `^9rXXԓݯSXZX +;}233233|uL8C~DӦMNc8|8]\ѼE+߿_gʄ}4IY \l Uӕ`KB5O)z6T؅|e8\&.Wɧ4pt=nX99@aa!Ν}lÆb?dfe!3+ gGÇ)5cȺ0ArFk׮J(Lٰy!nͭZiҗ(آ)ees"RXX@C)Z$Դ.%M~cird ;'KBasO1.ĝwQTTII3G}}lJ>xJ}Cyb!M;vmv0knz Y~g>ps"7o.:w}އF,8[l\y0g O>"yM4ATADEGE˖,|4|lK-[F˖ago%K+5c}Pca^A;ݹJUMjs1tjI2|I)wr =GFF233\y0h(={&[[o/W@>>* Geϕ H8 7ƎOMhֲ L̬l֡fEAAay1SڵU1v(, Y.v;9> ~36ĶE]=poTUv\M3k#6=6b=fΒ{-=Tw ݳ zI2ĥ琒a:"ƍǎ>3ME$${͚6AriN<q/|3fV;W|8?d/N8\ ~HU_\];*0 :jΞSgB9=}GUb1O'{7ӶKy. r=,,,w^+Baee%{sV HdkkW@]Ac(dϟG`d$)/^#GҬ[v0Sf@b.oGEqnj¬36b= |: jQn_mDvߘ7?G(H dg~}|z)m<U9sZ4o333ӥ` GA ;v,N'OرcņJ-NM[bmpq늖m~ b))8eZsF0v$dȥ)]SB"`eZt-~3} //O [gh슯V/ێЬU[0W];Ϋy""J}]G~Zq$ߩI׆=̂S/Ϲ"33ܼ(r EgdB8Yxk[GZBdS99 ֫WOvGEF/\ĎpQn_mDt) xn6yKt=bc­Kwҙ2siGyܑгJd!]\tJs=8q,YX~lۧ1a\8s 'V [!>1ٍ[IWeon߼&7[qU۳Ϝ@ U6|Gc&9]gЧey.+DD5]ɢZT~6ylj+bm-B򝗋Nor|r0eDtOe#^*T<*$0mdL< ӽ@ Tni0~dܡJi;Gqvք(w6siGyPWsât@` G(1&Z+KKXYZ"8h~/5}@u/NK0c{N,Z[[o6cdw;- LMbaw`X'N PUd1nFQQ\+?/YYʆ 2X(,??/[_P@;w*yTqmldЇX <~!˖-坈ȑcUjUtGڵa``CCCB @(uԁ1ԩ'O@___gGmuvtӼ<f̔m[qd IDAT a+qo,Xc ػ@7y0h(={&[|vI63U aƴ4>+~Jl4已]t^4]g7W@"Qlm)}}}tvݩz=Qw4fk\Ԫ嗈H]2R1i/FdUoIDDDDU".oXOOO60D*9o/f/(LPϔ""""""""B@-=RimDDDDDDDDT zBDDDDDDDLYDD- uXDDDDT%U'.KDDDDDDD z.DDDDDDDDʥǀ o~Q~ ZE>{dC8w 4ɫ!.ƍT Y DDDDܹ 'NDqqqdff"66C ,ҥ3Ҁ_∈t;ָ<lX> H'OBDDR6T4c7n˛ֽ[p[ߣGti(ػCݺ&ڵOF/ C6oo\ }z*XD,J d5iիW1hPܸ~U-4D"@ p) ?{lD"1bDg۫Zit""\ޓG#G[q8ǏGXrYM @?lK#1֬]ˀ i6wvug ]ѣ * %AҁM{^^jK^ HDEE_ J)%""-wq- EF CAhd#gAA Pf9j҄e}6  KꚚʒ[~&M.XjjmAAzOV"xOV- \2e2,-- O/6©quJs(* w)4moVC5ρ^>011H+joڎUlL,<{R[:ml۶ > Q\\ҁ\HuvwHH8ׇ&.̗zb*\pg$ 5Tn ̙3p/.RSYf;8?̌4 8Ӧɶ-Zipe\7R`%@%;z(U2/qY=‚g 0S, ^ܜ,=3gʶ)vQ[N _Q{v/b1NC-AHH1p@<}wFbb"d.UHر= n݂H$‡"((ꙗoÁ{X,]q2O쾑BBirlܰ}}\;-[!"b z+('7"+ QWHdpiiΆ#lTC5Wb%̙P8MEM˃PBhfjI lmm!ѣG3ڷo/l.G8ϟ?G~~oϟ?g᳭J!44]={ F0gl5J-PNgffI @%@VvvήJivlǢ+hWPt<. ?M*_HKOI.XiANR)&N(a~:-#\H -?L+kwE&emTOOO͛75ᰶp_[[[]iuHJ8)~Ji;"#wb?v gUJ[Q{v/Iʺ RYbjK ꊸ85 gΜk]€K s/FE P ;Y8(886dhY[9ޱ!jWQ;~9@vBUSLAwiiiX&:177CRR5k&{nI5hР_+V~,8  HNNF*s3[={.>>d Ы! c:toY#[8uHQ 0`z-HRIvH[7~CТE*i;6R;Wg!nyv`-k+{@ppkkDd;* _n3l___wӧOe5}YfX0lݲsGg 2r\t fe!CcɒŬ@%kҤ ba"ٳ|ms̑SպW$̀1lǸu5kVimt<@vɂI+joڎ*tr1tjImb`͚5[8…tNVzzL[[d𵰭;x6b;!`<,?OZjaoǀ垛^*D 622-uf6kѢ"wl/w{U^4ÇrF,UvÊڛc!UeE/ϞjK ^ ѫӈJ[t} .[H$N#*~-=Cl+k'NH/|tN__]6 `hhCCC/ N:066F:ul""""""7pUt]tF@@?k.:>pQ1-q5KSo|L""""""e>l(ʂ 0@:Xl+%q9vHڵѣ{7x`i9\5@lUrx! VbJ¤R)={ i666](D\==g~.KDbEEE r+(,,퓞&H?[.NM[mGW^h֪- ׮ݔL2 9Ev;asrd b @6 [㕕sjRnSy6uE6??bmiK˯xn}so,7>mSJeX6]Ц !.U*#[ʻT^>%""""" )E gbԴ.[];X/6D@,#b [ukV"m@Nn.NT*\o7q ?qp߼[(d|<#37}qNǻʞ"U}?*|:gdh!HR/X0O%y)/Rr=/|B """"P^866022pjA֭ 8y2^?"8h,-!¢*;2kDp^,-B!&<,Y$W>fuOMo+՞m@ٻO%WY#[tGbnn&/ QTTT.^r9p__c}0-kruX>sn Pz}*(,,P @T-> [\d`ir-[4mߠ ^\LM[+UEȲp@ۂ*YVүҿX99eA__e\&=t=2R+Ö!4l%ZOFs{s|g] ['W˗ p ڴw>Agwr5y0h(*^ڽ3l UV~U9YYWx[̝|V6¥d{Mf>'OMϽa6҃Ϝw4f+Jn{( aU6u]wt_ в27vvvj;R:N $$BPXJrb<}+W!vm5&:g&ct_aS=C7cSK=(""""e^OLbȐ!022Bg7W 4@xzz!j+Jg1vYjIVDR~k 0iSA"" ""Riiipwwի1n8# @ .DD*mZDzFfƚV/%LkV@帆ĊUሊ:ii066KGgxO>@I]c";׮]ǰFjI$ lKgߒgϞ H#F@,C T6QT666,!}16m 'NX05iF~O<妭 Jy$";GWjK^ HDEE@hͭaQ\F-)B sHڊI U)|f7l֡fEAA\uiT xq6n+?QƦ&4k&fVƦr)GȲ?n a000)Ww%n''cG#ac"{|8x8h[½.][տ"OTZt斶h|:ތM-+ʖ8Gn" }9*:|fL[IH:5my 9qbĽ[A?̘vE꿲'*mi wgSﯢ2&ň?ԒNӧO{n$&&" ൫u2B:˻=qB!!QHIwSĈ9]¶wx/MHuDRaώN荕(?|5 r.Z "^ȝҖljevDm6|%abb%珽rWʪ)RX[l96ݻwXx!bgհq:5kK@l"_fd$DzFsX>|NDshѼԒNB  >>GFHHѾ}{`pᢹ5cCx3O!PJ(ׇ=ؐmݞ^2UVJI7Ozcys Jq;9ӦBbhؠ23erjYri5l w?##S~Zq$W4䃔Yk%$9Kx yϞYՓݯcd"+R>Qi=V Dz035ŪtUt^&R-#GgOtR'NDV'`ӧ[$Np)!}pᲹ5ImCjVmzܢb=VNHu _3q\*:/SY;r.Q[:mP\\ WWWaԨQ8s \\\^ =pK,EEvmVNH w?/_aC?A6}b8>ll 1c/W/GGGܸUk=nM6=H>H9磧{x9FPPX3  !|/ UjXl&L|h REEuIՊDÇH:%6J CrN)"c``;p.2U>.]ѸIs> qch)bb\H"xaܾ;c.{Abaٶ9gam`ѴiS}f@w{i^WEA惔#F!l*xl ){ݜM_lA1*vv5kvMiybSYJO(/Ghڴ ٶYʦ2Qu*;~ݪᛦӶKqq1 fׂ͚-5!ࢷVzY#ĝ:J}/[x0g q/ޱ!jRex%r> ۉRֹ<#5bMnS=CV)T3͛ats`Æ ů]`K:u*͗V QK_8L~kr6paNAZP6#dzVNK8rMtN__J./'O&+= 6j=fg[a;!|ZinPx9}}}tv[(.5D"Q4;QmCVNӉA0xX%ADDDDDDD\ iK|S^RGl+,""""",R/~o^cAVj#qJLծ]=wgOcE_fJ讣PT*{Օ۟={ i666,8"""""-ƀ A(ʍp)..~VOzF&.0a!Q qn -G/4k knJ &NѢu;0 9/W?/7/-۴{7l׎WV{ΩiK%! 0mڻM{C,.!} ܺvǷk낪5l޺ .n]ѲM{Da-85mBmѻoyv =ޗFR|-T~VLuD"ة ZfA^^^QUGl)DDDDDr܄3gkx B@Pp~:ƍ8po5nа6 '7'`:T2k O _8sGiX=NF`;F5Ъ5='߃bu7*d|<#ϟŇ>O'ر8÷t,}>fuJۇܼW\ž=q),_l)+&4Zzz.50y\Z~}@u/NmZ+KKhaǞ0{ǟ~E%&Q +O+KK-U-@XRB(b¸CȒEr]|EcVT}x{N,Z[[o6cTR~[8j'OtE+p177 (***w /bp\z /|(Svvח=nX_m#'' 厝+϶oÐ]65VեcQ-@zr+WP1~V*W> >}O*D,ˀ }0Z4ouӃvN)i`8s8n%]}DJjq˴?xPiyz IDAT?ݔTXXXq`kRPJg{P-l+W>,Y!qt7VU"""":q'РA~:p),,P @T-> [\d`ir-[4mߠ ^\LM[[~cird ;'KBa`/? r.lڼUn/&J2'7!Vn 8}xԈ1?p!ܽ"HJL9J/ʂ- iC ;wa,_lV}Q #3 |fcGN\ʰe [m;Qc^nY033Ew{~Une!>6]0OݭcM) 39YYWx[̝~=& tt@P9fT-¶Rᩓ'3FVm;`\xyRz>+RH`HuN<l޼6m}Xp!O^7oFXXN[e+ݟqNavR_Kֽʕ+1|ȇx][ɿɘ4cl3t*165/wۚ Z̙1_Ņj?nzF–C>LLiӽ{t@..X-[nz&k"p:!uO>XzE"Qtz* ׯ%,l޼YCbGǏb>߸^{P{'j¯q` |g-{6,j AAAH$d^MwW\\-["<|6l>PV߿=1fh=?~3 /u>RGŗ0f8;{'#ml GATAxM{^^jK^ HDEE>ZDDOCʽ !,akc%e]EE^=sL6 ~m;alb&MoUh[YJ?g(0֭_I061k'$&^P`$4x(,,Ekj"3+&HafnFWx KO'`aP 6lXZZ_߾ط2Gey}l_7y=KL<ec(0 Ը :L+ D66]vaff9}qu}{-7|(///ט5 p!"Rkk[]6aUі[10ul޴En bQBd9{Vm8G{4/ yp̜9R"55k??r_Up"ΞI@j! 0EaQUz䩼Y䉓p<#3#K,}sY=‚gE#~^+WFףGf-ڽ2E¢m__m_?Bzes%CYL5Zl,:uRxꞷ4"M!q*>jI BBB퍁ӧؽ{7ZSj}}}KzLUG5Л}6<'o`aXd4j !-= pttĖ/h"6n zپ> [^]722BH4mּ;ؿeB7=9+;*H*{k֬?fjx~aa^J;-{5qkgkcٌۯ?|}fv(**W_mÑӫ{XbBL5Wb%̙FU-MHS$$Aannt=z4_8;;}5*RÈlqZz'"[;> E[lEVV6LP`SzƖ-//c8|8]\ѼE+߿_m( B@:uPTTTO'G^gaCY";߿۴MYqtlX+KKdff=7Se$Ur?##ϫugffI QX ׯ]AfM_Ӽysn ;`GGrj*f:C~DӦMNWS|Bj%^NsӂTNIRL8QQQqu[$ΏpanOދA")R$"t {v.d᳭ǟ~ͤ베0I7ѱ3~۽bݺ~HEj׮gϞV;ϟiH/ !;+_wlmm|_W2զ,^Ï?4OeINNݿskl/.y_o߾]i~иqc|ݷ쳙e{پST+(LIEFĘ1cpqXt"U+bbcѫTDAqq1\]]N?e`m䔢!!+3ҫT>Dֶ dž,|R;"⊆ NF;1z(= [oA*Tmm۶ڈu냇bUʟЬYRA|B$''#hap1y$x{OGD4hׯ_+O??ѣF~(((pAΪ,sa/'Ç Ŝ9~ՋyfϞ{(v.sp@.1iD'M`blwiW1?>LeL5˺q:t-Zr[@ &x!nͭZiT* ??:t lat Ѫ3ZuVqQ\ `[a;!ڴy3-*),l9Fcq-4k }mۼi&O尷9ؽ7 7.x엩[`<clDF,5bu>ЬY3,_ 4 =z"++ ffpy*wM_M-e(SAAފ d)cZeu|2K@i)miҤn&sOs;碣ШQcdfe[&[͛ƣ^ݺu)oC Fl"3z9H>ΝJ)S7hd uX{=ҩp%ÈKyZEE`ʕE-!",i.Ϊc'p/C73_ƃ{LDu-Oa#c@fIm .7Mo~=3zB#GFݺu0z(swFڵk[iDƂ-85_Yt]6mڴ2ڱgN2i$BDDDDDDd 'uv:w'ˑ BJf?Kty9\6ʔg)-DDDDT(}'Qi9\ȥ}\1BDD.%dvy$"""" c9.DDDDDDDD&"233Å\@A{W#.DDDDDDD2qcxjU>N>mO;qIhrǣ[n45Q-kN^Y Nb>>,b!r 呕΂ ""j*(JHdL$$%%!22˗//M|||p@VPA.B04l'j1XvY S@*~9rE /vݧX;;%Uw .=(05m˖n:^ gAb9#q-oDv\ݩޜ;w=샿%ܕZRp]N0ݻ7rssT*]nݸ_eTd29 DQ7!^(~\ZUۋ7Oւ\QSURi(Qz ;;^Rw7\4m8z'J)ņ+ybؐ=w> k{I?t[tpE}uVϊEzxpѱx]'4nڜ +WAŊa۹= <5ն+Y.=;BϷ!00o՝`; KeEWDuY[jVIw^ظ~p51S@BTxxzViKcfwV2 T;I$D!IpOq'?uu]8m Z}|*q!$^~~~5 ͑/nݸNuĨ1u˺Qテ+xt`1b:wLl߾gΜALL.آpn=\lV9 r=J \i9|_rkA;;vƐ~0<3`_aO?rJ4֬yqvƌhO^8i*nݺcv8y85p0? IT^6TzX!-- S@yҐۺw.\͚$G܋J.;v`hܸAūOqcI60(9 ]xaac̠g1qXDE@׺BX 2l`-MEhxUڡ3Z0|ָ1VL[JS=y^ Vc=\۾_0/fԭSͣ^S6FUηqy{{UU;k.>W_Tds<<nrb|өg`K_ *|||\.(+WDxxAp᤹mF"bݱط­[֭^L8&:k`~2yL`Rd2֨U|-_jOGN]pyܻwME G-q~v߸o4Aݧ`aH˟T1uuQ(ȭ./,ΆB|}} R ???# 4ORnebmcaμXl̝ϡFzC&+[;8tgHSc5>u2 d45}xxv Ngo.|Xt1JGwQ;y4mO>CL,{J;|oN=:!!2|ΊkW!77ּ`&뎹4ĺ2 yͥ]TfZmRrQ; .[͟nBxxMw펯wm܍ތ 5cf͜\5gRQnw*`z/L{w8̘>/t:un3Z^j/n0L0'7\l$!j*r W8UT+uu-DDDD޾QN B#| n^KDhx*y#$:DWvJXwXw8p?W'mB+߻!!d-,PUH|;^oզ=oV TCUWHv{wV^E`vºúúSWܜll2y?lÑ#G!uRRRnݺa BݽU!33qT~~ dP)whb3n߾@4k W_yie)TЬiSZ󺠆^1Bo[ 'ND@?ڷo˗bh( Ȇk;]Ϟ=.]g]]j(J]} & 44{Fnn.JWˀK16\ G\n߾_j8TT pI|z\<`09}kS{C 5bqX`!폸,"+%@AmeY }a֭/ '%""6g< 23cgZjAбCڵCիWѥkw+ǂ;#%5`[+V*UG1l1hY5 _@=Zؙ\*BʔC&_`8w<ѣGxgĻ aH}ODΣEd28}$n޸̈uH@eРA}st; gn\9sZ]&E̴Jd>&KB~9$t#}r?x#%9 zNޟƾHMnݺaĻ#ޑ7ѢysB;-DD71lx P?{y- 1dܿ 㪭CԪ]nХ9w/<K.mWp%kWak֪=wvښMIsMƍkE57ܥp wGBaTV ժ?PfMŋѶ+Hz٪2͕|~|tʕՕu0> آT*1ydnSqOeѦM(||![D=vb̔uTM RDDDBHII1xp 8q$^neUeʖGZZ:իW7=990X8(x2UV3;4Ȗ|w?9yZk=@X5j֫QF4W&\iYG[ *rV2=G{ݺ믾:o㹦PSؽ{Wk1+:{->pXӹ;I0tP!""wѢE `Z.#dQDDԨU@ r/[#x2r=DZjrqÉ߯]+Zp$^ hȗ##x l3=<@Ŋ ի-sebo^ &cs6sm|M)s5[7z{1bWMqD6ځx 8;'E͚5CBBlڴi`W"Kc|4y|8 ǃo>dW^<ǺCnlٳfbÆ1B$&&BR!337Xѣl)D5&M4aҤ)xכVca1b$\J }Κ|9b?Z+WFTtn z莉'#%5)0a2'/eʄ… YW+n9jVYic4i}ǹs琗IR|s-&rw…hѢKyIt0ر @ƍ -a\>%O2@^6 =VIRj5< UZvAXX XwXwȁq= /X_jTEسgn6nĤSqͷPR%L0[`(4jYY֭+fϞeu>L+V]Wڵk#f4ɗ#oȐÇbWi@n]1wĞ?-"n޼j(gWl<Ӕ)S42xTYLѣg/\rkUSk1ymhÇ#:* >>>.IiQフ+W xkE@X>BQBaPU 2Q+V֭ېvmZcy ~9X#V'|b,Z№oZ)7[W]ܣXs; Ջ>u p0"""N|tN.#;; >>>/Ji 00x6=<~Dm'| 9cnٖ?`t˦Lwư3 eʄul-ۿRĔIE! l j^euǚs\ݱ.Mںx\-(рKjjZi5A0VT`???G/Xt>{F;DCHy9it9 H.;֜˰Q!NDz莞= 40(Jkwhh\qOԨntُ[ ,̾;zfL^xAAA³:tě\H"5rYqչϺCDDDDDdZҭ;IFF_Xo^6c&]J.\qvNNJ*q&bbgYom&DVܥ#gQ$oGs!{3a"人c͹oNpc|uXwRG'īw ڠRJe f5AT3~ڶ~Rbx~cw4ic$uy'*H꼂9)8,\Xw{:twLPuJKY1+vze2 # 7ؤk hkq6S!A0`xΑꎥsR9mٺºC"""""\I prꊈuS^Cy ĀKq4Deo9)ID;DDDDDDn# .Ѥ;Ҳ $@$.KD;;DDDDDA\-,, aaa,""""""Aff&脒}J>8g.Qiw {׫yp!"""""""۷GUzqrH#+#ADDgO;qIhrǣ[n45Q-kN^Y Q@ ?q VR,p IH,_Dvv6bbb*}B!EDD ?8+U}zCk6mÚ׼"GN]p?x9Gγ@<:W*jbwƂ!:NΝ;g7qY:wVT*!"$I(Ej(b„  E޽ R Z]'P0_!.DD2 ~83Qn;'r_xeK 6 u1n(\zz!!0x( Ȇk?t[tL۳֭[de@aI='k3KKl߁d{=nNB4\MLĔ3py*^x%>޸ti֬ZAҝ;W.6_a欹R0mD=Q&Nmt+-hj5__|Lt*>XڪG# 6e2֮[o6jH7)1n(,Y༸y4n?82eݽ{kv ~~V^;o@,\0U@Ll\t:e"e\ ƚu +3 ݺuU+T*y[;x0ttٴiwj̘1m۶Edd7\3s<ˬ5DDT,7P9 4={K // .tӇ4GC)k?1p`8{l3&{6QLL1˪ː\Pk9:]])Xd\cGK`l(rssqI|FǼy_b׮]ػw/Μ9C-7`:I&""VqE tq~3g&= lٲzG|*WXdn=;u=V,_u6pWm͟;ͣ7p)|eʔڦ1֔a8x"6:cFBJ'nF;z? u:m~qip'9iiZ ֿƫdEl"tx3.N֩P*8~8˗/I&hԨAk{%V""lp7Xh۾#*WE`pyUAz ` CRs'5W3Te5WCJJ.is+ѦoVp5S*V E뺿&^3;_Gxx. yH2e0a:uatT9AD1n$dffYgw~ 1nHOo,CrAC㛯7&mJۤq#l5_+`Ԙ^U6uX1' c_{hK.K DQDf͐͛6mZ$ퟍ2JGRDk67R֭۸>N[ֽkL4aѭm <ƌǕWRg1`O)VZVo3$$/^2؎׊[NrJl-PdYV/#|&$϶^xk_ؙs{Ǐhf //$AGN4a٣;Or[ÅмYS Î;0`4n €K.=¼{z.3 Ŵ W;tFmJ3m ֩Q/gJ*egŢbP<Ӡ i5smM0Q-.V u4n5k?cO?zƌBZtK6Zq ~QテSc~2$ǚ37nD㦑:3+˪;^}#4*bgɘ"ѨtC+W3gLICE> ^RrQ-tpE>>>Xr% -!"z%BڞQ N^[FnZj"~m Xsܥ| Ka#ch4>Yg1J_$7=mncMD:1r.IOk"77ӈ[U+;5_9.6m@ \n6ډ1SfZ^D2kIo=?֭&E_~yo";;0b8ܸqS oܸƙ XصC={[T*|K <},xC^^1zcƏgvy*}!3'#""qv"""FkyXrdggCPR@4 [3|(V̅B(•+WrbcӧѰa}}V񧟣EgϡQ&}%\!} o{˗[-H_lx5$Iuwc@>&w~F>ڰN >|MW<_t.E3@nn)\Hveg~ >4ۺ }zavl ʗ/-8o  1Ҿ-~9r>>8zڶn>_hir?,'$`왺ץM8z moA9gA~9r *H3x Fr;""nr\ ~.DDTy]:kN)lMۻכ6c&4jzu,߻5vk~'`붟жu+s"F|>˖-k~{3#<,LwXPܿavGXŊi4s=[r 2>BqGbUXhe`(Wlt~~~ ñ'&am8_RiuG<9%"‹, ArJ "5˒SRכŘ2e ICDT0"sC y9=TWڵǟ~n+?dj\~+W-o԰> _h> 6i/O>EzzO?BH|x>:^h6Kg_!"*D!E-wS؛+1׺.ݻPRzt[ިa|hh6F ۴oKyG7l4У[Wݲ>o6}{*RTcd>}{cCN?k6Mo&"oߛܐ"(L"""""> qVRIǑp&ADD615ؐ䛉6r($\~$V$dߤDDTz}}Tw"""""cJApy#:^*vދ|ȡғoaĩV5=J9.پEDDDcpQhrǣ[n;nгGw)A&@@BW3*/Qc1pr"""cXjJ%$I2X&I˗cFLLw\ nzus{ =wŕ";""*[ȸh?盽Q A 2*۷7<%GoJSˉJVܾ}hUExD% 8),";gϞEO,RP*E$AEZ Q1a IDATwͅRZsЭd1mB3RǚMDDv=q*85r%m3f` ę3b{k.|Jٳ;vԽs.p-Y'öh<Ƃ ѯoeyh9m۶qY:wң֭[7S~K:5ex-4oL< o]df͚bwV՘={>sdff: c~ôi1x""""3}z*&L_~yyyxɧP14˰zܾ}9Z_aFcXPƎ +VիV#3+ =ztBT<&2W˚|9¤|J+0y$zGa m=c{++׭Ų+M zh5d={v~ ’ŋ^YU *uǙcР.K AM6{ Zƌ3жm[DFFzxeE@ѧF _A$jh$H(8|V-bu+ (F34"{;$HI,z CY4?2AP7ێK&*  LnoGeHp5Ǖ  7E* U#\Eyh~7I+p{mE @ EC2sr!,]:K.ӧN"88'LĎX[0x|U`E]a_"77-St?}4N: V6g\ܿ~3܊<=r`;w-Zh򘊓_[]fĉl_/xzgA$=$IC1g\,YX/ HBBBjx]?v9 jhѼ뛻fy:W]'tgűDZy.I ϟy/Į]w^9s[?LXJ%䝶 "'y!7l뢯ۇ ~, quؑ (FFm[a2 A! I@ eE!?P\BBhABjaiQ2e2r &?$G7# !\k!](&C@ZugNԮ]4ōOڴi/drسk'LiryD 9ê @ Mk͍ bZw`֑, _El[>02 (d- *9ԢpREnӒ?U6WE _)O"*_R̮smEIIIFHII1XGlT*ra~IH@nW_`4wtNP;~Onxieܝ$I:t(ݻwhѢAE;"D$F=* z9qSұHrT'[t DE'dy^b| h$ WbE5{_Ɔ~[8$G0hb3BxضmuÑx2rsu?9?\ܾuW#Fꖽջ1W/#C&[X_qׯX"_5Þ0911 }Ŋ.s[[RQ#1h=J7?Mr/W"Dl|M)ksuJ_ݱ眹ǣ^<" h֬<WӦM[~rB#Kqu[-45!8| … KC,mC$-4N 8nk%l)8[4Ql)eS!JB< >^S x"뻺?yL}fbش]?ozK0bH\r* [޷_;wyyy$ *uA/G秄1] K+oS"--ii4yrk>>/JPA 00x6=<a4]캯y[lÚ6֜K Vن`K~O&,ӂ-ȢgŊ#߫%\SZԢ(jpMp%Ǧ έ'oςzguЅV~ .I Afi.$-<Ip`B.BfID%l):4H`/S8sOh1d:'%õ/˴O4#׌P0i ɳ P21suaZh!""""@AԮ]+.InQԒI4e-]َtic=83\b f nЫ%`CX`!b\QS- A.&$A}t~B.`Z̟6?!JH\ɣ{&'ǠɏZD Ѿ6$P?!J-j'1BDDDDv۷OD֭B*!EQJY~#\& Hs2,8w#3llM[9]4/&D/{kf~[lѐaD M2͏6!JTj;cPjd(1g$ P#:!Q5WԬ XG/rATjMɞLLɫ-呕;#"""" ǎ4@ǣ[n45Q-kN^UB~<7 $IԒj/N x-P'aq Cϋ̺!C#SG!\oGn$9+kdd2%ۻq'n 2G?KGnф|d"**Ԑ DQ@ZG*9r2?B%!9A pTBy%G%# y#{j1\5AG!T𕋰B G= k֬)G6Ao3gE'쳾_l)lH~;o&m   8Ц~_n֮q;CoͷӯhnL0x~'rm/|lOaǃ߃O틡C?=Dk ?7<8SNKTES ///|\E /'ü^GGGE%۸8cqu7&>?xǰf.,sȡI'|ޞyom 輦rvO~UVW^Ŀ{ {ÅY>֔8q܋K/18d\zOmgg.ZKa65J AxLo^.a۠u) 1b3#ԩS1n83&@ڄ;TpOj-{7g|ɝ##D)(R'%[ɪ0lHȖ-) v/뢵l4UtJ]!Z qӣM}IQyzigi'?JȖbEȫtd1!^sIE0!^eq=&L-`i 4{ݧ3\.ŋ`6o٦XxI,XBomI7oK.aoCz z-ckAG؃}q|s֭\1i$<9r$z!kmd+kA!}ͅKۅTYp.z IDATHk /J)y-ҭ$= \Lz,,aCBԏl)w4\$aµT@FG["b;ª"qړJx|ċm#k))ci8b<_T742.2UH7KE6l(~w0tG7^_rCv յq'fy{z"L]S7q1WfR,|^S>;nÆ Ż{Ѹcpݎw+.M&GO:w~+v76Q޼[e{YK˵e]O`]w?lwb9,$s)yR xum)+u㧁d Uq<2J,!׃lgM-L(2D*LE bhyDKh0&[ҚxdKu{6!ss<%#Xl2'!ϙ%Kbɒ8aE?阣q)s+qѓ" {1~qtMo;f's}O=xZ|r0"hooǼwiZ.M< hXl-[C9:zzz,7xSAI58 a옝/[.VX9o]w_r:ÇGcȖKP87w-v#/>WPP2V?,dEa2+ ƨTIȖzRd Ցld*tL'eGdׄ:!cۚcyȸzZg8K |u8gfC9sQ줏^-4\j ~| vv#Gy{1dF8~%kwLwM:^wWW_3F M{ Ga舍/v۵)ooOA߾`'?q]l6{/."toN=$|fIf"AIhxo|{`ZGʗZVowBmc#,jlӦMÈ#dKpq~QfP,6%pw\@#ƺ;ؚuik5M]ɖ80'[,;ݿq%'IFH%(2!=epI 2IULp28YotTTב[ AF?<!Lm[0]9'kTa̵"[J7KZ\X-}2(@ HG}tXj]aY֮]L&l66D>}зo_Wh5d=JԯM(QIyJyz:i,AI'"^#%8_IuEPMUEaϻ".®nKXt=c-PRեRt J(ԙ +lR_/ flV`/`kxvC()G9AO&h496t|z.XՅ/K8ɠ 1Zٱn`ʔ)xRea{i 32.B_ɖ#^'ɮj2!F - ,>JIRik4 ْ^F;Qϖڒ-ϳ%lњ$[NΓ0VF4lĒVE2ǬSrfx9x+J359 3&g+ER8qz@ A~uǶȨF;~ -Ӻdߓ'ٓ~ȖGk-p4< ?Sw `u"(+~<U5d 3di&Hh^B@ ^KFwKDntYH6nKħ _$)jLPiliid"Jr8Mw"-^-ޱL ضB'2d Yn Qpp]mͱvHެAݺn@ A#oWd(Pj_1]V#[8$\ LY{IMyPZ--M8+nIAkJ%0c9mt2z-H8>QD^8)&ȤZyd+ϋܗ4VNJi!]@ 7LCX][T=M'Ĩ@u(9ON:Յ&MDf#[lk tPo%ī=F,KrlM(IB粌 '8?**f8(eN/f @1l6lC+Wsdt@ 0xJ-ZKgOs bZ*BץJ@8PI&%in ślbcƒ\\CdP!wF-YEiJP:/3FŴ;(Ч "ks1 s*Dv?)n)b q4zr>#.@ Huu$\EnnK҅Pd=]]hX-Aj؞-ZBP"#_|gk2@&h|lcdތ"!<W\3GbNc^b!/hX6mBΧ⧐^O@  rn3JeiFLׁ"(: eVw*SErdAHQzgQn(Qx={l20όVb>ْf,* *IVbr~7FL%\8k8$ ?OLy`ti4 *]Z&X[F&4V$@ 4  ӊڐ.ʊTI1HA_mۄ3v@vBx//fTRN&*R,?l2L`X(,6NC<] CqփBl"-a<C@ Xz5>OB;̱1}ڃWb[4 n*/k"Y_G[!d*nq=,^CxaXC&nQİH{,KͲfid0*\aS?s=J39esX6㇌wBE[y 1gbrq/_@ A㑿 ˗/'ܣFD dFBѬ:{R)ͳnP'![nC[q5 %2aDJgcER]a!rm[ Ż2laϛ|,mDG,{i36,F qz!*Lk=Z!*Cd%ciX>q]-@ x'&ޓ$SHQ-JfP0\I8ʭgKjvK8Y8RGd2{vɓ49hxD)PC>̊Lk{h~e*#vDwر+tշ͘κQʰTPlo6m^@ r{OXx1f͚>?8t8t!gf*JTЬ$d*qMB-A-xRt~!X#[\\dKhdVR*9ehAο'_V$EEۅ=B%P"ayi-REtAe USOchoo/ř ,/Gu֮])S*L3Hb_@Nt` m-$[[4-ք1,= 0̡9Xkd!jЫE9"&K5i$RȸKd4Sb$g`,K;<҇|^Un?3kWw&/ړǀ{/fmV16鑤.Y ̞=t^mv]5+lF{{O?Cŷ-twwm׽3gά9b:ɵ"lJ%62nB&\[IY}q 6+糘d e%S!@f-ny^KBPx/+k!ҎG" A.khࠁo~&O;QwXt)~y/q7?{VGt봐$y̜(&Lؿn$Gwߍ~19PϾRh`'kοywuW"q;*SQNLI5W趤XZ!R(=;ud sgd-MA812J;L UR5:Ƒ-qa)J42""MjF"WSp+(-\=c7x#sc6w\x!4x(>oxmQ )S~ٳ_>'!Cc|‰ ⬗upF6|_yUzlku]]vK/h /ᇱӘqA?lhtͽ;qŴ_`6qúudMY3ga[V ]nX'6=\L0; %\e㪚-UAU}̡KlK &[5jBl}E\/.2㊯&l!߬CXTMu>A 9W-((OIQeH(Cpjd >l0⩘:^|{ىS/ی;CGأw]5 :0}4Lq饗^nx`ԨQ+~n? 4m0p0.]&w-{Z,Z'|2N; ŋc7oXxqe…vJ%[O>r2Hwo~=P:?fmEɤ 70 YaA.`fL4 ?0Fzvmbv/p)"\zG(Q邸ET!0Fqi-T+d ᵺ+fBVyOV"[#tZ[xXQmϭĨl)Hd[9 33oOiy:-Pa~LhF-)춶6|Ӎ|‰Xzu8ad̛u}K'/Ygg{lذa7o~ܹx><^u~wad /Y8uV x -p5c-f[[o-Fo.'U@v( RP,Qbhݖfȳp%[уA˖d!kiRkLZYTuSs#u+cMNH~DI*Z!bhY~onv8I8Zvttt`޼y|B2bG\~%fC'38 ,%KpgC'zs Y#SO=>VX3|8xy;vllieE`A>/~|TUgĆMGĈW?B ˇQ9\J92I쮵bE3`kN*QdK!J#Cy;7ۜU*38ޔRƻ%>,v)#[J%XCD&,҉8har뮽gu '|{챻I כm%~iqm_| m=Fn8]tiv3YrybmŸcbM6q} & )S-ܫf=1c61h \peRvj{l]ʵᢵF6Ŵi0bĈ,eYՌ+1f;i\aYV6/oU5zbK){J"!8˫i>Z{=㲩8KYkƒKqgae``w58#X4;4||[g)jPpށCX)[RQepO͟t5dcۛDr{'⦂.hli;FfƓ-8ҎH q``"9((kO2X|$"*MլNj xPQ7-rI&(.CWY 1s,t u+, k׮E{{%WܿW^'IW;$vH-Χ[N؉x1bTIɖ""eB҅dr]xĜU^a7= B‰ơi~~0=d/Z0QJo `ܘ-*RPDe 8>\K2&qy$,KDF45?ْW_k%z^9& IDATrm3^DpўёёZŰ RqY*lp™8$=9_TЕ#뮛pnjCLqwPt1@ 8߅yoL7[xyx|_pMldAvDVPPr4.JU%Py҅gC/ ӆlqCME5Xd<'bd5"[k[yZ%nf'K+>]fP/\&4;!CN:kXdHa6D^ S\[+rt4zl+\.!1&L~y@ lhl蚷Y"UR8f˫:A:J7ZوZa QdZfܕ64 ڎxeB !) ·).~ݧ>{#]2J29ؙ ;cB$.@  iNGŊ(h1|-wKU #\t[vn62a -4RR-Hᮈ J^m][GzLDa}qUGrL:l'R"vx.+Fl&GϢ`ߨ%ܱ{MK]8n&%KiB ҖѓSzTB/ʒK@ A#\햰#]=*nCRΙ"QH_("-"*K&!6 %<•-5 (mBXR&[DC>6K&AMbeChuT?0קfh'krì8x!cq(RjHb8钵l*uRlp7c/>\^}l¥P4LF'bo(Mb4YqdJ;Ƙ1XsMdâ<ْ@6+'di)!27f:/rB D`WgDyM,J=BI;q`-h2Cb(ҰPmx9?tpӳ':\llXG5t ez@ A}Cے@)€!Cc^u6~ͽwjR![JT %\(x.IJTZ{lV$[.![|#1( 1n& b9ǣhI xċ/PA]; 6Sh'H-|E\QaQT:mR@VȐE) IŗqڹhrlP?6Z@ a\K:AAhzCqVrv|yZD(r)i~AT3lHnȖr\Uz- # v'!Y(>B䤃3"H )|*)f020y"iˆr/sYh.Ut^Ek #bf{l߇x@ "Zsޜcbͪz-Ϸ(}lR-BFDKi dKɢli)UJi*%,bdjAhtKђo-y.ݖ\o7J g)Al Ch)˸ġQ^?#s|WS*m2 YK0<J@CH#NXQY+,P([lA-i]e&[WOk\hR14WKT@+O$k%[CƃG w[P^eB4rZ!:'ӒWŁ=6G3 )~cb҅U[Xl ޞ2F4'\j`BJyE3e4 4{T-!!ElIy UyPJ S}.^wMlacSWGƵKxTO :B\rr2Q\Cu{&佊 "0a`2bF#GILd‹ޚHy]nqbْ[ d)-1c-/ۮ@ 4=ᢣ3!1n@!BIaR "]jp1#4kQKAH -fkvMlIѻ8N Ki/'r/84!9rQB=6ĐAp]\jTAl<Q][+C~0 >K1lfؚ€V4] ,ֆOo~ Sr_j A=lT^ A3.CFn990sӐ- &4֫uHԾ*  %.%ËJg5$ǬErAԫ_0=-Tr9& [<0=6>S2p&lBʤvŊ[(dDqQ-09Mz^~ o[n; ) @xo2;K>x@| 8QHІnq{쇞uXfej)kGTR&#HM41t)iNy8tC(ZOûdK=t[|[āA SQ٢=8q-?i >P"G6 #3%0 . #f<-GI(Et4+8i @̾p7_Tj荶]]G. V /D@KBͷ֭ MP꾱1K-r"ޙ`2y=@+{/\Y"`%m [2vt["o&̳E;d K\ -3ɧPPZh|(EkьD MߌAXآ˙A^d lvO&J#^5 L9%X"@J" f^V'Q.%O@]Y'q4Λr'g#NXo@ f"\vxckvaEѵFEsAeӝh/-i95%5rUQR/ V)-@&'0v-jdtɖ^Lx8JE8P2KH8hAm,| Ke珡̓f[P3wa#oޟ?@_@ L)b}ʍ42[$pk3~mty*^F͔(@dU\ O W9k e"GC zl_ قI¶(K ]:{C\۶ОM$*_R I]x@ z){L vhMjrS (ҏ']59{Py:#6ja)sPϱ2E`_# d3qތ.-ݹڒ-q(O*NCB8B҄7#k;='&'D̄)qX @ R%m ,0\_,@e%/;%_P"B [D$-e82Q,^Sz-i?Oq9$gq=EPJ)\j| PIKxy}#0R@  A|Dy b-cfPcf"R-S[+[K\- 529y[rX ?.( u|OX_-/\h@ N;~fLik.[9hvuq.ݒyPm"AJ.6-nXHYRH5УGq&*kR*7(B!9N%EK)ߘffb#O 'Oe(*VA̞7MQq}>+@ J~ յ|m(%:*JEx‰y4^$PN&a\ P"'9Pzm ԑlqaJ굖'^W[29߱.ۜLdk&IC^.1H;U0h @ USƳ.- bVb bJrBՑ-͔ڎQL(Qk$Q-TlR8K`zQMf" f F|$*d@ zzU4Cf4N(hlqTNW:MIOW2-JTy6+QòLU Jێր]1 5z]O8ڙ_zO{,n4T@lYqsO95xxWq䓚/6v@ ͊LeNdG3f& 5=aEدN|ºu("3lU]鶤qVԸYVET%&bkGL*0DIXu\AP)fh #8\۟)mG?#`#qv n;@ %Ȥz[> *0dnyX\M0Z9ݩk˨a-Q恊צGBzo- l<5RyDj#4E FĆL+QLElJyNwK+}R8p8i5w1f+3Gfw?̛,7ތٯvcƆ#Gȝ@ .՘A(7ʋobRa 7Kajj*)Yp7wK]&-%P"c41k1-ZC'LRWXqYܰ"W<7iկȧ?Cf NDI > 寏_BA& wȶGf>W^} WOp10aŕe8O6*L@0d [$FӔ8ukyKyèL߹S? @ h(T;вB1B ]%"[mt6:u[zeg#GR.H'H0ݓ')OjPPDdp8Ļgv`p@1 Isq>啸Xd)Qqݿ?2|+_­7^[n67m?~Wruw!xML{?G\Lroֆޞ/]/.j,[,VwaUWc{%pUW_;n?B̙fjr [lޏ7|裏q}cc.O AMp׽`˪-VkUk 2E9Vf D5nš:$Rc pCNBZLD-i4Q ȗ.i_"w"JJD/,-O< =߯I'zd_O=ЧOo⥗$>{Bs>IG|p$ ƿ545B՟f UtFV"[B3WW'̱& 1fdfJpYr%F >l(V\UssaݺuEk6.sǸ1;amG׼߅pi\|r#W`+Vba2~}ip@>8練u]]]^ڵkٙ'`f>{_5aN?'\_@ ԱbX )jPn2A~ALuJܰ`_E=ER--%-߀wK`ɳaD)DBP-^"(UA "vL|1(q cjXx~%WFiW_n{7֊B&cԓO5^kּ kP~h3`@aŁjKL9Lzu8~ѸRd[o{z{.6h#ٯ␃D߾}зoL<@̞ @ H 0!E +-e *&Hʥ|3+J5n ZuX&[#[Hoy#R* "DrUR.#Q:F7J&z*Q_ubUXjn},F[6l[/Yk~s}o(|n)T*r_oE`6y]L>o ˖}>8[SI̞yen93ʫ1m fS{/~]X|9/_ >o#{M6X @ Q!ERptj#gq2\ fml Bbo=I/&{8]Jč*^Db?G+$$sW '>zr}p 7cImW|üs 9iq-a1h@|_T/ KSw%8|?~|O| lHL<@&[oy6`U՟o}P\s8~_*E  IDAT]ǏåOE#qNJt>~B0c'E|as?픓pݍ7fkP Aj )"ߝ4]Ťhh>* :R:`6hcj+"[ž3pR phPސldFV[\9(GJ̈́ <*'[zlSg\fW- C%PfFƲLiKidԖl!xgc @ ]>?+.|by8p+[ne%D] 9:(ص" %J}TB ChIj܅G!'Ǹ4 rL"O$dK%x.7g4y|L{H d i@ (D )" )jP&FHJ=yg3[\%lzY16:Rhjx=T>0Re5 T vr`V `xKl)QDb"#% K@ A"*(,EuynP;F|daFZCd k˱PřS.G/W%٠\@ ŲE32ı[9=m1*vK^PoJBy&!fiv3PU_%PCECtPϋ³9a}^wKBTKoC,ӽ6zzWZ @ВY]| 'uט&V.d(L0,nn!4vKtӷ ( 4CG2Ut2 %w<3L j{OL /sX ZsI4'Sl*G$'Vz(P/_YLy/8-.4sB g&0/rH*^{{!KHHQ FvtvHdExT9; 'owt4K,KFFiẌ̱́;-c`eO5@N{52jzo}m]~n1ΡBWLVll6+fOϢ{^^04͡o_mٶ*T5m t@ yY2dp˹`Նۣ ˪hHEН| msЖ{dmݶ3wLhϽ~]] t5g}fi Pti3}+-!15[+翁W`tpb5`c 0ȇCGT9` D{ϣW؎ʆ[fA>. ٽas;`K-\++k 6"U`_w 7:TTdT Xf\wCpi@(jG45ܐsnKSWI(5@ݖ]0AFS)% p&j1^IMVuJ 6b@l f?^MW`wK|m~WȻeNQ4qAoMGPҰVۀT(B}٬F:ۏr:?m=߿J^ uеmpYVb]֕62# tfAY%3 @Yk/Vk75`:YhY2@co 9 &n[F64AG+z[Xhuwo{DM ]ȀA>-i^=k۱-p=-0}46@w%Aȹi%( Wj6F6j╖L"xs[ҩͧ{"jU;hNqIENDB`sqlkit-0.9.5/doc/img/filter-output.png0000644000175000017500000004237111714210425017250 0ustar sandrosandroPNG  IHDR"5sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|ulz Bh(FWbP" $P"g{w@ R HgwG%lM/紐13 gfgLxh~!?Ts1k5Xǫ1OpBdR OUO~!V=Z]d|]AWx}FBƥ88å gy_FP4ya*g+!h֭?R/Nwk4?O6<7'G=zm^ !D?ރ_Z\;%ᢡD㰁5/>Ji^6%Wa!hj:=#ۓwzG;r0?َNe餞d^)VTT:m6,ZO N6ݚk:Y[ -(("Ku,fe4.nv5f68h]%.O}a6mO]mZվzl"Quھ%U_tzZ-%uFբѕZj_OT烷vU;m>\!^i_Rvr\_i8Rթ0.\v +pO\*'w7{p{13O]K=#|x>~<=;䫎8 GILܮO%u8t+ͷӖ?Y)כ-kk;\4 _8k?v8*67MuA=EAѠ(4gٶ3uu=lx7j?LRc:'wLDOppy۝,~s%W'vd8Y=G q@]OU 誜{0NKz- Ҥ^UهNӡ73U Fc Z0AJy,28BeGSm߷1_bl?~ ߿'geD2PW3'%Z@6{WU8~l#%n%ĉnp:7^[ u|JsQ6  㓝B޶WѨzq߹v*N[āmҾ=͐fv$`c;㢃XxW~sh»8rUk+" LǷʨRP*M%<⨜b4͓sNz*Jd=HnN )rxzrSgpog;K!q `Yl޶lqEQTsqx(._eTؾf;7ߎq logá Vo8[m gynlg Rk偣줷bJyРTJ:E1&ʾ+t8x.b;H+ڏʹgcZ=]pm7mWU rN@@(o܅K\˄3 TD_]תefMOه[8 XeMv6[=PcS;?*ն cˮ8Όd#64~j*kR:0bh6;vrbx6&̪oH.TF3.EcC / 8T@A/W>_gQԾhϚJwX瓥JNPC;w6_aPTqTS~7Nk?*$CK>m5SW\bwX5O=hLQ l8V s⶞t!X^ޮ:9}%ҝ /Gp{/_Tu*vLj4o^KB;zėlCvʈXLGO|$Ul㍋(&[Cv3~Ϗus'oo^ 7vvT]eEc(?'P絖mx/miRyaiZIB!h4s5 {iKk@S~BPףzno6*MG?բ)(1%XW!Zgn3Wi24HB!7G6퓣B\,o\ʺv66vD3Q\aD6Μ y3B#gąB̈yMUW_Bc2p4S u!h@-GSUZZ>''ɉs{'Y6'%:=EE=bсPny] !!qNOI-qXچGPӓe/fkmʯjI8{o.nDtX=B48MDII)<ik?ղkVn=~}jSc_]q÷<3t:J~ 0:(cq884-Ds#SU\ͻ<&>^y!mQKwTѯO 'pp'E(׃^ (-FAIjq~Ϣy\J{ Ta vtݝIW=RI&"Gvv6>>۷'؜^*[QhKPS!M4£![_z{(/ ,2%%%,Z}/]NIɵO=t0w/,ZjHb`]7=֑it Z?S: W8w3."I$b;.tލ6!mƍ$88z]vb ڶmK۶mYb'WGC^MoVhŁ_XYomD^KQ#qr5giظ.\dF8}?l#YYYZ> ~:zP&E̞9~9O%%%:HvPBF<^o0bR ;y0eӦM <ذ}Q `߿?G9>jb] j^cx?v-]є-x|Q_ `yX3ǐgыE/ӈv\]$'7!XԦآөEANE WP[6-r HÔC1g~'þ<<== ^^^\zO?ۜU>+5/pnCѴ{99ڑc'];luY1CTVN?F}]O>*TUeǯxqqlPP()sr!KѩpwwGk NN899r&"c'#(R wR|̝;h gϞ >ӳgO냢(]U{g]~_:PzF>u;jGQ35>11x-rW䎱wx%;;Ÿ;o7{{yq\QǼ3(-բ׫t:ǵ3)I,DɾtC8;;jqE_OXpګ#R+W2|6o\-h<#,^4Xx1=BQ_jT԰?_ 5P樄5T㫪>7FǨ fFyGGs]2lX|}yn C"WaߨØ<[WԴ{8::Sh@》+O9k8~8'quEщ8fs5͛Grr2=z@QÒO?͍7HϞ=ٳ'7|3SN5^J-{Q-K^6g'h^.ep 3dSlų,qBzW*gV,_ŠK̖}j<5QL3Ӧ{h>ə]8z7_4 Nd]XUf3rhٹ-ϲvFqp@+ҳKa#EQx7y7k^/* ,&=#[1%kY|wwjyl@qu7T {uRO]qppׇ&3{9A!wF(e=*E՗4fHv|}}ZMAoժ^!=7FbbHwDFyJhh(MNr=WGTBid?Fw-} Mз{(MpVtϷkTd H;51'j4mrg;t@hh(^^hL|6'M(ii.z&L) ~̞ۥhu__"ڷãFu!lNގ).NONv 7gӏ?MΑ884-p:2{wqq!<< ??JKj&OZ}1Sad-7lh4쌯EEa-NNNθ쌃C .={C}#LQ4wY!0b2pܶc6M\ECx:Ж7vꜢ=[W; !D}182/6滢6L =,!D3g2p:4fiWmoMl'`*UEoo\jhbYx9n8V!hfo9*ٻg߁LT)x*UZ]V^{P*o0ZGM[KX ^ !DSd!pUm>n:`X ggՒM\YU(&u\ju)**&vtٛn={3":U+S/,aEt>oDF`n|jUgE!#k^#}h|TtV0l̝pp,ZJ~~Q}]Dps6;sVvlBZZoҦ)g ?++;~ݿ ++w]]:+HB4EGz6fXZ*??q~ىw{ߨgcjuwb `ْ1UO}fҗOKjWgռBĘq*:6^|z=7\\\1m*~iTOemʿ`m&}a;}8Y׮`2Q3ʪUF!Dd{j224q}-  %%"HNN&PёB\]]5jzuP:%IB4=GJ,msK`?FògTm luܝw23f鲗?NCzRXXą鼸hQޜ9{LW^%++תNEB4A9(֗*oWZ$)%([v^لD/|ݦV+gyKP` 7F&$97^}7mgh>̵T~j*wN3 |;Zym)2;U`ޒ*oW?}L~x W^5' <5P;+߭ձ[Z-OoDF駟OKCf3)**ǷYIXX{|xj[=\ڶ #799'7QkWg7Vn?B4jc]0{,Χ&D׮]oSxoRSpsscņe\|ӧNr~vmTZ~ٱl##&L`Fgefq ;۷mձ[ϖSY%r3ٶu3{^iB)K㸐vc9zORS[fs 23Xlc毟|bT_???_þu߾}ۻ]Jn6P6UMUe&^MC K\t0ZR\Xo `sKjPPMuo^a;%%I 6lmV=yaxҭ/Ѩ GX|-l鯹UV ;:j744T=vOci۶{c?vO]v6GjAyAU5""B={TդxΨ߅,R|Y w9WۻWՖYSս{~SK Ig=V դS 'bggԸF8zӐAjq  .nmSٴ4z]0a4N F孥[ϰV5C=vk_slڴ'[֯_oW?-dddcǎdddX,S;t@zzaұGEEѳG_ "<Ąno5mڴ!1aj0ULrra;))ɨtk[Bbba:M 7_Zgָ6BBAAATΟDppaڱ?;{k֮`ڵ̝3^Zۿ!jd(**WWW>cBM{gr9Z-L~x!&;YYdeetk[,2237/Vn)pqJKKQUڈ6#__N:eI2o^,ddf2wn 7b=ǞEL||~Cc5r$W\>Ó}k_k#DSѨb^yXG./0d`ÐC=v||cL0OOO:wohnzlNV5qqKKdWE[n6JحW7I=_ .ϯ]UU9s4xѕJ˗/#8${ҽ{OBۆlYzBtt?"FҥK:b3?ڨoGHפjR_m&֛o0z(z*A!5Kz4ڈc?%=#NGjj* 7\osߤ{7/L223;7dHx#?~RTUE6"ԩSf۟6)Oɹsj3)6뢾WW\]]ILLd Ka]Eŗ_һw<|[/?C˗L=޽'mCY,ΐ>~8&~,\?̐6g jKpcc2t܎?Sy&ږjۇu}DL|9j C qGBQZq!͓!v!.8BEBHBa B!"C!]$p!!v!.8BEBHhR^!jCB4ZO-~hd!.M2p$$$p/M #3l~g7VnS<1I|wV}JVŭbK?7?tO=ԧz[?;;+ k_OM{bCzQQLA`PA!L1"}L(<<x >|ĨU]ӱxK ןOի6Wt41{={SIMIk׮ηXf߾}ۻꈋ[W8}$g׮vа|_cX='%/ɶٻom/ ~+ԩdglrCҥq\Hc='))-3*ˎe0ag0סoɁٷw)Ip:;!Dhg;XRPP@d(Χ+)6ѡcgmLǎ8{,{2's}زu+/-7es֩s$11s0~<ڵvU[yōܹ3gΜaH8wزy#]tӌ} g /^HpA!_5v(6]III2G!Z3mÆ?g7|ʶXjаVGzz:۷7lGDD݇Ǐ񟯾̥{l_~o6m€խׯ:أCF파 CPر#F+;Zb{iiia +<<4ij&3ctR\T@Vf:i$''*ɤ3 @=!|*Vd4GGG 96:e?88ذl@PP]WզMMUL- !&8 quuՕDϘiu$b/ ++lbbU{eX \mKiS㔖*ZݵO{_wWQPP@ZZ3gͲT,23σoHoҽ̛KFf&̝Mn__N:eoڴ>}&ΝC䇧g!Dkau}DL|9j7`kOOO:wohnzl.w>>+,{lǍc}…kWU}|p-vvFC!:]gks˗/#8${ҽ{OBۆlYuϙ3A 1ϔGc vYxSsIƍȩ+ žG=9Pbbbͽt 5vkՁ#C={G^M!0EY?n'F'<ULÔMVedefq ;۷mqq˸| OڵۮצĻaYOM&5%];Z>s o>CqQA h㖞y%2 k׮gd0@RR=y$cpttDҽG/nDxxQ;ڴ-;eF:w ӧ٫^kuؙm[7ӱcGΞ=KLў5)6FR9BCCy!xx|`ZZcOQy٣|-&_Сa"Z_zz:۷7lGDDϪ~m -С[)BتINUiӆĄ镒B)ΞŚkXv-s̩BBBHLL4lW^` IIG6}&ΝC䇧?jH\|'}1ׇSNժdffɼyvw}l]`KRMQQ!.35uBQS8*̏a!s;>Ly1&N`b3?ژ3g5z޶KȮͷlnxzzҹK$}Fs dLYG./0d`[!jN2'Od:y"hxjbbbͽt 5vf8ADzEa% !D!sQYxv֬BԈ8BEBHBa B!"C!]$p!!v!.8BEBHBa8| !Dk#++N#M2տ_!F_~BaF 'N`M<7Yy]utꎧ}knУuxxz37*Sy}ݺe{yjB48,xKpuu)[ٺef-'LdQlGþ3fs/&c;~نZ3ؽW&SSBfgu^g[gsf?;%Kv}˗lsW_!D7U=^Qm|]-O+^f:-p̟k[(6&iZ+-yBh86|y:uz'MG_T||p-vFsar%k2hT}M۷ *gN駧r3)MKh)ٝNQ=:.ŮzLBQ^^Q#G1V,zѮz$p!D+_[Et>ӛ+z$p!D+_}7/`W֨Bۧg/q!D Eg^g5!hRSʟ~<8Gݗ$=muHINdG,sBB auF23.әrpp ( vk1p_T !htDѩKT)SUB!"C!]θE=:tz&!\w]Oͭz 8VCge,˹ٜ=FFcCv W[mGЩKz*-L3DvYm>c 6ա׫\C8:9Pݬb8w]FTU=z InIDATg5x\kmh4 ADǹSLj:yVRTU5Ӷo% !DyOI]X zjvzJUUOc^fM8K ԙDGޕZ QT&+I8O(JeFA dM8ud?W1EU ˄E~р^պIsđtmi){FVuQZR{zӘ́Uh3d]zvOUe%SR\HT8:9S,;ps!hX"SU1%$ѯ_?r.j]dTUKٙ;vkTԑ?i1jbM#WUuZ QTJkC?tFJL9w0S37UUKτE:Y^`*4# GUF PÓ㊢йg_\\=1=U%8DHM~d%j]563uv),˻]9iheMTzٺIsѱ[/ M^d齺Y ~]αqSfFrUh ɺ,p7}o=7r~u-,N=8NdW43EUrC!*^rN&&g:e_[pssア^hB4}ͮ 78 ?‚|\=ix+?td:&>Z5o1!hL[nBL 72! z)>TF!ˤB$p!!v!.8BEBHBQ͗_o`i8BT3m4i5zBREi2BOOTB*OOTB*OOTBMOTBMOTBMOTB[dJ!-SVJ oB! [,NUܽGq !hܽG1LUiKKOB!"m ;s'PQ:&N?ϕK<7w>PF Yx}"qqqm~ !h"4JpӐAjI!Dsjq+-ĻIENDB`sqlkit-0.9.5/doc/img/director_movies.png0000644000175000017500000006343211714210425017623 0ustar sandrosandroPNG  IHDRP3sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|ulOo$!ޤHQg v+gYߟTC:( ^MlH!لc|v>eygf>P'ӫ/BG6**뒁zC7dE1vB!h4ꢸ|Z_ 9wO!2,^Zf?%B!DRLV cPy"-|騥!hiK;ާ]9lB[rG6*&$0BѬk6N~=:4ߍ; :iW3!V%N oU >-Qn\ѓn5ga4Ђ8$))Lq躆vrB5t+j@_DT\*~ci>kIf3ehKw;xojj7NS`○jrh4Mv vijr4Vy^3Kv ̫gWbDBBeɮ˷ԪrSxR q|Ap_8V ^mz4x9B4ez~i;nWYQ.fߺ{.[r6M\u _,3kbrx!i#eCc%BtD5sX,\r,${m(hz_55uW3 7/Pb_NTqBbp琷tO˟X/!']knxB*tPv*ꖭK9oO^'ZMp(IO:F@bN&~-c6n4|]>YϾEWK3~3;,ae[h(g #oжUx-;qtE}Bz -)tq\_|˯e߮NJ8g{^]{B4'}ZA-;GVrڳUe^O/V㲵--}E/^ ˿lHZ*P誊VitP ߠPϧg}hn 'dx( V- U2߮Ϧ|;3C(N7غM_Š:H[u:^[~•%Odѳe0zT\-[C7W&ʠrxF:kUU׷;"@kMƣ sjeM%|:|QoZkY~k2`⬰XҎAćz=LNΞ}Lrss!gql:\BD}@!0AMN#8ܸ\vQS9oNyVW2;+(((O*TLCEtOH|lM8O!혻Y,lVڎ<\@10MLF.}8{|6^.u_vLDI~=K@ucЪ PB°ٽ# ӷ Tc[/!+ϏTҀW8VtĎ.9jUNEi-gFྉ}7# T.3JFfL*/:_x0Pu(9+6S9{ }{^vIS}dk0`z{N Sqg)ad5e֏KHC~^EgrtGD}~+=GW|47_SnU ӏ#)ƌt6*ۭhρ]Jm'v䢲 yZ&C)+_3PZC(3N}v^ TVX됛,Zk@JH6ANW1_;J(]` kUu3W9{Hz}΢ogc'г&N672f$zˮ 99ٻ's7N'fsVs}RFw\<7E;g?r۝wgz ;;_:{#9`~ҫkO׵)?_|]̫uX]}mL2$5@ko<˧U|{mNY-r>ZINd|{_ Oyy7HO`8z,_{ӛo~]_-`uU__mw_}MvXb)Iyn^߯7OO?wngmY/>Z1GTW]'밺ڶQ&dtjPH_/kf?- bjYF?lZcY)۾cw~Zc$&&-yYӢ?.;QU[kSNdD8}ڭCU^6 !j_hт/?:q2MᣴkC>^טGNȳǶPaڷ;.?g> C{}k=9JLt7=;;}yjVr}/mkmakHi@^^ԦBq:k ^uCnײ) &;;gDžy/p<ϛONN<¼jUS/xZ:<[.g3?%$ץL2$4΃ 9~=п?6<f>˜ F-'9{EL c&Nfrӽu'3I]AmWM萔ˮbI$lM{O;h4}~0k~}+8DEFr;雷lеWTu{ @[ 3%/e\}M 9{O-7WMi6$L2NSCV&/+;a/Ӣvvm]!ڂ`Kg^BԂ<"߫By8|B!j rB!Ts Bs BUGkz!ԣ ;+#BSGUTa!ByB!T!‡B!!B!|Hp B!G5Oe˖cjӏUc?3nh:$%ե:~-[ϼ3o3 RފXrU> Rp:]\~|mX,F-[4-_mtNA1$BSpc.p6L&f_ӧ֘ǛoF@Hp__q[˜,~Gˤ&~_<|_ >Zxa?g; *,*dsJW! ޼Q[&aX̘ff#FUUqT\.Ngr8\8NN/VX$8BSpPPPDff:!!aJ=tBzff/l6@lu ܰ%Kſ{WhJDx$;ug^H;`hXv`1[AuMPnn&'ӏsS?*eiI)aqл;W ^](**+XZL!s 1 _a2yr5\@PvHr 5xO-H7G$@B["B˻53_T źHJHJ~BCC m&gOh:HvŰl6ӶM+}t9xVZjwR\\B>} ac>uRU>v ?>Jbno(h:h(:oIiQw.c} DFFҡCLf'8СMքau?q2OΖ; Zɲ;XLuy:MAQ?ۃ( @5u͍ɞ;4]pl WU b@J=e˖F:wB)))86+)S X=TLС͆k F.qqt P{,84nUۛP44]) j:il[ϫ3r9580 X,l6>*Fhh(!hec0P`%] aj}<%@BCf 4UUQU 躎-8>ş[,y1CRGErۭҦM[TUHVv;w捻4B LR;&t@t eb%gQ6+ߎqQ}Fw!1Om׃`T]A = Hq0nD K+fyNDc6[R8N~ ϭn`@Q #44H4`6o`PLY1h5l`x04@Egvd`Kp8=l<)ƲeS"ln 2R~I !_߆ʺZd aCPACTT4SN22),,d4ƪulĀEUU\n99hjVT7bvx 1ܳ晢 [-zh܌ޱ17ᎢgαBmYOK[Y>ضtލ- //A8[s'AQs}9!\Wu7n\:AszN k u BIzzʃELGOg8 WH~Bj79k:=sz^@34;@p:p]!tT YyF ry54%Kp8K''';u[hcՎ^~0(]wwrB1Biz4mZT;z14(MGg{/]0*a?*a '% {uZ.2*:ş`#"J]nFUݸ/6m-XTMEursQ5ՂۭBQm}X! >ް-3CTˎrxQS v=M r19ĄDrriGolr. ڴA~Jޫ#X,fTMAAk׮.#"*АЀvs@L!cEG/*3͞ LVSޓ0B?|;\LFD))%8* F6VVy? sߟSXDp S%Vb0;)v;8(.ۍ{VNQNN(bccqVXDtt4]:wngw^`0:NX18ݕ{CYb0mBX0XLxb)i4tDttRBz1*BSMy+SUR| zY9ĊO ☕`$&&|TM3_ 9/=? 0J-A(Rtw)вA"[׹.fޝؾ `#z'^]p= ǯ[4cnA15Rzv{݋n4 skѣ)`) *Ipp`Ѫ $s;VpbM r.V[qV~۬V /e~awpMCXy0H` u9еaw8(c{F׫YAA>!!!eψHKK'$8zUYAQJL)ڱ]R,s}g(z0DFF0qdo2W]қhKb@1x&]bh]?w #>. .Ń}'''c(  =Y>|8, %))hjyՖMuR*VA_ @/b>6 Q=,.{҂ Po,M4T.NӉkcBU( 111\q$] ~8wJaAE:fkY9ܲ|:w@DD86[ݻaCMWD9Û]397y_ǽu_sn3ࢄ<Xu99Cbk p:uJ]\4 lzB$yDt ˜g2{O2y9XVobja6رc'L6mؾc'KEdo#]oZ9tWLtHj/" 1뗾zfWSu F]\r{5T]*::=^{X,F#-Z`2kXi+7o/,yz6`X:CDXXaaLa8bXΟz~znxz l3AX(ڶfzw;VJ-L\Lj1ѧyS$$$꽣!ڷkGQQ QPK&°٣ځNyyIddo-VnMh^ wh"$$X!D@(#ї\M^VZcr֬]GVv`ƶoÐs޹u?꺎invh`YP>ํy-`Z0̘ͦ Wq6ʮ_P.pxvhin'//̬,rrr(**Q FhhĶhAdd6Ariiilٺ ANhӦ5AAAՖYe*ؠuB]L~݂麎S=v.[aE+B&SG28B!8i!B@!>$8B!> s\S] q1L\ ۷hl'}KpЄfRU>iR@2g9Щ멮Co}KpЄI=QIlx$NCr4 ~_jUvJgپEc-Ar7v2}"NsZ&.._~XSn:9#-K]o L;F|bS] qhݮ=iNu5}t۷M(hލHoјNv@!><8Xz 's.Jq ȶE9U*4=;ّ!ĩ&;w1AB]H`٨hdضm'N$,,0&N֭[ZO~<8p8Lrd ~$h˨Q9rG/fݻTWOA48(%ԹT1?#Х,[؉ң7G/TWMe~/>r]zɗc.oTnNz@}SY>еgoN_7߯1yNN.*sѭW_A\ MQ^yڴi`{СV^zb >#tb_~>Nɓ f1a222|ywh߾wM6:n3uT"""`ڴiFXC!۾dٓO>Ƀ>mFxx8vm<̣ϩoګym*` 7ѣwc4\<p3lذONԩ<3-C4>ۗl5ڵ+K,o%11Dk/^L.]L!$%!R].Hu#۷hl'sAb9B!ĩ%B!|Hp B4aFMSPPU7FTW!۷hL'}KpЄŶL S] q8|-"۷hL'}KpЄ ;-};QUL4 Uu/y'} νTW!۷h uݾVmqcoHCU Rh$6%#FUv:g پEc-Af2[{=z8Ugˉ<8#-l4a%۷h!B@!>$8B! !g {IcڟLqQ>WAۤ.m rBQ7KF} nTYM)bM"bN<9 B4qJ^l[c`t՟UKSy!M\pЈZ&4"`[ʓ@!d-E|UHQ ~{ *3ny Bfrc%B iʎk`?A䃃OuB&EuvoYOa~.y9Yݾ)`@B!ٿs 9|9D]ɿQԹ;){v̫t!Bأf >!n/^SBfA@uB郤}N%T_UzE2w <{zk.w5O }=zCϥPj*v'= [>deg{ӓ:w?#ХGK-8(?]ggؾ}?~5׮j Wi!wΫoiyOnݿ2n6[şkVбCy26o}.qz:$/9exBBB5~uɩBuވᄆx{R˹os`Z c֌YbO?1'7UiVg0zDyrr#F !ĩ2ۘK\8q&O3ۆy~|Iii qqq q*F+Y-f h~F^y_z714;マy 44b7.NM賯k^kz玽k a۟R뮽CpNNl !D);o]ob1{6J>,<7#wK||n* D(#ї\M^VZ2b/) X~BlڼOa^vzt&_4I]؈5IU^tO+3kvN?|o^!Dsi_,r :BH!huV .rDٵ]@F]_(NB1w脦hZ٤j^}홯uݿAUUQU!B&KTtz)t*5Hpt:q\ B4Y=w'+ȩhu*4Lprt:"k!p.!R !g1_}neB!> NYrB!|Hp B!‡B!!B!|4b :U@dkN!DkB!NUJOQ[kw,77kBdT Zٳ/"2*Ȩ&N={:!ʴ`˶[ٟMvus@~Ĕ뮯uzS$.oDɤIYeUUgӪu""rzKKK[Mveh_- {_tܕp >͛xN_W篾5Xx7H -NYAeZnKdT S݆u~@Hh8:w>\.fΜEbŷW_z,>+~ǝElco̚5"##xؽ;ME.r.>ԃt҅3gjcÆ[é ?S'y76gU>K?˖~/;',w?YjjSߚ[ukగ9WazwSO[lr3KXn7駟aǎn-ɻwsaoZM@_s<ɱرc۷o%P*O>Tׇ o}=q^0a|ӛ e!J X!'̳X]rpjԹ+ .]HM=@%$%%o>]_- Ҏ%::[zmɯto٫w_WVWIHH_Uyܱ[{2fxRU~:vfƌxdZjwRN":w\eU^U]W:u]cƎ@ʾ!DU~k%<g8h0]য়~򦥥ru?_RR> B>| rssiԁ޽Ю];Zxoe*_742Ӹnʵ|No*Dpp͵S;8*nҲeKv;.`8op/<'>>xW| 'vշ%u:x qqqo|9z02w1gٔ*ˬ8[2^BE|y9uV<7}ʕXMERl6+6pǝ/Tfڴqtߏf۶m>W~^u̜YYdee3cC>K?W_u%3f>Dff8L223y\u_Bm#G(--gwލy^$/sMܹٳGӛF *;Z}3"2*c1t9s 6&7%_M9ұSggl6:_<"Թ I?ٶ˯. 6~ӹKWc3Z7y$j"bxw+|f>Ϭȼm֢Msש#G(koݙg<~R]A3Qn[6h($$XW(c[ZgZpz]hpcߦ&Wxxq@Λmh%):#;邋.6%,)4"FCO؜:%\ƉSBd=*++ͷLRLnMo]ee~W6ML{X]Rκ *))ix# /gJ_ؤ>:;"gnnEGu泯)**RڷoEEEKkn??'h}1o:  RddG=to@hS{.o֢OkE6͘7ݠ땳iz&L5mpb}sf޹x풎 S$=*((TiBӾ~Twj?wm߾C=XX}5=glrGlZ'M}`z+I~^J4Xx~eW-W^^~ G3}ַOXX.|1-ozK?jжO4$5kx?`@eYtycCh_A*mu!*)CJIM$U}۱u屝i=bwΤ>/=Aa&rӣW H(ya:oǐ?XII=$Iyy9\[6x#qj8ֹǚ6G(kcٸiz(_k}֯ߠ/UYGVU2W_(>.Ω??pbZq]>HI=%IB#yyU} QqQS_SEycyf~eXɰUzWt>=;rtͷAxx K_әgxG(2:N}bYVֹgOuxЭi߼|Ӻn]_o]w+m`PwBd.\}uy,ݺvў=Gp泯׆3I))lO?\R|\$>/Wh.\/\oRoo-Z,1B|6mjqj/PUM:u}eI4ےU\_M11ڰvc1 W芚nݶ]QQMg7ŗ^$+t͎2w}^ڶ~k&\{K' L7NIҥ-[)N7Vkb{֌RQQǟKuW;9tLS֭ghÆuͭs=SUPPM^S.ԔU~~U]S5&Fo]ljٺ#Wuuq>t1|sik j9 5_/j?U6u\wjÏ9]XEWjMwL15NR/0Sg"cu_GiȐ.1։:LW7ޫ%WƐa:e0SLUTT>*66F_i)s{ޅ^w޹ o]\+2I)%5Ma{eMgM7)n1oj'=ޯ;v(=cc2(8B% 25pP UW^ؿo-ZDxXEE(:&VS/0.wC.g}7:sNk(oy12E-*{|׵p"XL˗- 4k֛N\=;S1IvnWV ˯t7\U}~=ԓpFQg3xj|h**O?:XwNcUv]3~Fs?7O9h߾JJ|P.-77W WI jsc]y՚8TsՓO=޻G _vYڽ{|p k5iڹsۯ;KIxM|󕗟;S\7S^}՗80rwW Tf&sEHlۥ**#9%&hXf P~ɺ暫.m'eWrtiRzhTvӦ=PHuʈS؛2nm߾]0=z'|JwNYLyѣiy@f9Xx亥BI u[]E3|Xw=6/ƲMXZoۯ[t{v躛oo= 9'Դh>OO+f$nIwms,7#gV&L+ i`´0aZ0LV&L+ E                                  l(ðKd.ÐdUeCQ_#pHJNa>!02FҤV&`B8&`r}ˏf_ZZ&G8ְG5=  pA"ǙM+=1ݔػgL~oZM'fO88cXػB;J"40tUVV8yy{W EisdPI>Z@U Y,:p4^<0 yyY**..c!ð7=Fh,S.S,kR{Z̑giê**ȓԁn OV-Uȳ r!aJG74,j!T*$,Iy$jIP%"*VQ- pL,_.ТMS])#sdM#`Q6_+8ceet~ֽ [lިԣ, ;{Su톊hӚm #`l%IWp̰ðnka<ˢЎJꓮ5`p7u[HC›o~4$phz_M>$\u<%y$GYZ })c@vsZCvUe*u*syZ>ni}֟*ܳ[}fu%/Ð mIzI6{8pq`MVV*1`%X8Кeo#N# e i]TQ~HgCk V aZh-v)==]EZW+,Jh/(zB>+op;y.V\0`!֬e1u1Z.Ο]>VZ,kjs ZvM+4-)eNj´Bl|3Oa*H-|AbQbjݦi9\=Pk[8ǐҴ@y*+URmAGAmu}C+Tcic8rG0gKH8 S >!Dg䀻neΩ Mw,ٿWAaVy$tOu{JGN^\sh\6ڼfz_MpĞ}ү2Cu5$!Wy*-=ϹqةG$^\0N9˹_Wz& G~mA{re9>ފɧɽt֜-ї(!1I>mfj˦ lPn m]c ?|\zzJqR}|ԣW\z~kvp7ޡ1ݽC%%% jsn 6M>ުzjxxZG/H%zٲڹ{X,*..VtTFvk:pD+WWgK3Vtd:4IWgm߱Gqu_s]pPlyf]~UGmVZZ%K觟~nիuIqOR.%_Y,^ھs *kzG9dp]jn˗|}}w5fuɧk 젹7ZgGj vuM7? nWe=fE{5O4ږM:QihFN:)MgkYsgN1Ώ nאT\\= v#Q Oԭ[BB뫂6oڤN:S\|mH2n|RÇS\ծ];mްI7nԖ{P]ټvXde奄JLLm9'Ð,z9~[HݐfS@`*1cbbc%{uniZH`fUF@O*@ ֭V.I \cݰKVؽ#Ho-QwTE\W"=Iҥ/ց-A׭iBW=f.vEVRK@q*k T j"lڹkʕڧzHrlRaaGm#u#Qnl7dUVvHWii:u꤭9oF)ɪܦjNV(W}P??-׆uԇ $Y,xUM?~b,ysnb(?ȱ/p,ƍܼ*4go,i)K3ڕo4^Ii(AeW 4YK~MVThhշ뮺y#kǎ]~`@I u0 C]vC*ܦv_>vIwe2,QjH I ?jfdfaT%<,Xgy=m7m5CÇ;w[U.h8(?d~B6U۶8jۈ_QU{%]*LɗrчuL[nMgqڱ5GBh=[:F5Za7ZGAs 2dZsΣعJ ]ݳAH^ U$Yv<>g=bکGBYJ['dg۫o'|F hi-:r`e(11J?U1g|2I*"%dut#G_g*+*v-!/v 8ސd1mfqC <;rPL>Gm;$DKd}l H=-٠`*4|k# _8ezx zo/P_IMUg\tT}ζ[iSi(%9Yg8W1q2,Zhoب|ԅkע'&I1w,"èTiTR62ԻoJHpPj g^R2~(Nڞ%zor2 ҂>_~ysp^IRDxv IҜnv33P!{"""4tzO촂k;Dv:Y_'qs'gəC]vG#GKkJz$kQ6۷УO<_xΩh+Nȑ>zL7^}II=5L,Z~No@[c隫-'sޗn{Ceet@tu $YVl4lvƎH\IReeZIaC5aJU/D:p]ٱs:/Լ/kf,yiNo@[ 5ɜ%I6oޢO>7_{Yڵvlڶ}~ *,p<{|h;Jp ޭZ/gV(;艾csQ`DN\{vwkϿX c'pT{Iҷ?,\<"m^CI@+ܳS uS ,dbHo;~r% Z!F C ,YL&IENDB`sqlkit-0.9.5/doc/img/mask.png0000644000175000017500000005164311714210425015362 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|{=W${"T( XP(vZWDٱ4AE#ҥ sew\rKr$s޼<쎄 Iu.3-.U ӕ߹jY1"i;@ %l |99cE)C.E)ɭ\tF$ OgNoqt`\9+ X@p]IW:;,CypT%@EAQdlBQ7ЩuS}>e:;3'3j((tkX gy԰@ оy# hWYAټҎl m3:U䤠6x  1UUmVkYQUq{JO[hAQd%.3: Y,\0m|4} uBQl6fCQRQ7U-[οDz},eYe>m|XvdFn~A)̫AkCNܪd!9\قɠ=|_ǽЫA={(loX<+rZZvTmGYW[޳w2vvNuy>nݚ#Nቱ~5ڱ WUnpO Ѡh$ FckH#͋݃"bN8组w_3'I1jyZ 'H]ũ%v:0lٜٳ̭?5HZ>0_(*88Z@eB;"p؊rٵm?_WTtdU)׍ƎlU^Uykcgӷto!rr"'I9o*lC"w^''gƒnlϿM܈YټshxmL&7&"{?+3;9p㿐wgA[N@Ұl"ޚwmڟ[cqQ}Ƴor^x)́Sy,G#ul Ut K0VN̺˲]oyݵsuhfЖg 8[=ܲ 2P*r,T]EIqF:+|Hg)*؄ȝO~ЩQ#쑧^"R_Hy+Qq|:郣Il;OF&eNścLODʠaxiWLNp fȸxsfҶ,&kx/KH)sEcKNV}uvDNY[S-qUܴ0SO\ ?p D[ev[93K(.r} rb&`M&K9QH)J.F6#)<o&!'TA)<::' iqPb)4.0{\? Sh9>+Wwv*Y1<Xʉ\XU-ՔoɕCrj 7x ZagT ւ, bZi @fzfBJʙm?0 }IPw_!:,ycnhr+̳ 9W%>Tʽe;*ax*>{Vm9GUǹogَt\vRˎԥV*r .IG֜+B|e m6(Nș zZlX'mcLVe¤fu;$1f~AJ8ꀽр˱M B3ux*گW[v>WqulMnZV7a TY*ڱڅTWj9m.o.PS:+je)i.SY\gWa}T1dj|G$1$$I%mO$ 'o"ćVϏl>GA70 [O߿&%z{%$gڼR ; 'Lޔb6mxS(~hJ/mӔԔjZ])ձ4nIBަOytޛ:cbS3J4 7(H\`}<)42c Ժc:3~X"^# @\NY rvULg@+F/;-bvFt>Z9"-;-2res_>MthɝGFu\zs8$\˶QbUQUk%u4QsŀDbKLJh4.lgh?e)4+(*h$4ihA(޹:#;W7>UC8_~t1*ځ -9*e1U_1by';aM#40V[~PB]\ى 6,Zlt 㥖H[ru-Mb@Poh$=GOҶ5-#'{o)+unD1Q3|@͉jCNY/jB"{vjSw‚ bX .$:nn.p)rjWiSQ9o#e_V:jUq WT!WrUPn{..}c]1ќ˭ Oϳ/僆в]'=OaM4u{?W{ѼOIlۑ}Z';1?E7]N4nUGkO|}}Cf3:Tڥ3?e7.cw?06/[XFλxaωGYoEE?[wFm\zONݘ#zVɇs yL0̓S^tދOb1;?,uN~x|euB흶tV^{_N7sG\rr2wMǮt/YYYu:L}N/ƏѮ߭vCSi0VChhÇ -֮h4oe&?_9m߉¦̙<4ɇMZZ:-[ʥ9u:|[͟+ˏ ظo9g'`,ʧ{9t~U-'5-7cml^kIBVY~M6d0O̞9/g:4O=Su=n _~-z3 |#7n]W5޲-r-tpp(Zb3M?pV~h4=>&*d{KBxx(3%K_R|U|/\Qxt#n/?ѫG7F~<4mXpN-FP7 &N]wUo:<1iCACxW˭ 0}Av"#]lXppcd2 l9Xe~w| _=9ԽR#MNM~8t{3ɹYҕ/_ATddik^o`3yf<@Xh()h()1,)>9dq{~Dx'SHo\)᱇ӷoo(,,K>9Lǟ|FnݘLk\wz‡H$cеWT `28sLtS>̝s2ݵ.-fՊ4hOI9 ΃߇`pScSӹxƃstzl6:ĴsӖ{~Lp'{vla҄;]6^z,xWjG+MVV/J\\=^|Դ4xWq%f3FAOfyyۀ}IMKc%dgзws[wn.LdFjj*&۷s hRR5}k؂y?4zy7>s~.ےei߮- 87lK֭YJvj:62uBRcxIΝctڋ?ΕWL܌>~¸r(>=0? 9FwZe}*-w$!QobАFG;^xf6/:qܱǼMBb҄q<1iwS\v={&lJv{rӘ;(..fk/;L7nCb~Yj5]zѳG!s~.|ϘotwM77WSӱqgMl.^S]"ש"hu|OK9Z^xi@ jW#Zr-9@ոǏ\h?4kݱRKoywF w|̽w\]u7+VСC%,uÇ@9cxabغV9//W=]Uq!/ggR7?B'j_'OTJQ_;{žt"ZG;&=IrJWѤ$N:v<&#]/קeY2jYVLKebKF\3e,d_6/'}5 lڸp7r73[)(( ==ISsֹuEUU uzdmtOt6:sÔY֭\TU%3#,hFs̼2l~8t#tn_jݒPQQDN~CQ&,RܩS4dSW/ Ȝ-DS8d9G<2]m6 """j zYz5tރ~_ŕ\ ze~e"#ɦǓظq=[ӧ9r'N뮣UV!2m۴%&&O>_y/EQmͿ||7 'гi &4Z 1aN0[̽ d!z͆t\ADv"-}\]vwUU*;UdEFmN"SP(.^T׻e]鼼.z+ma2hڤCV+RN6'GкN6# %%zP!(8EQє( F6da<=h7b2~h;ڬ/j!1Zh_{N4Ih$IXVd!2Eh4Z22wEC@sEUUҝD4nM@/**Yє<Ҳ  S ٳn35#,,u_ :SzmL|fD40II[t C PhT+m!i1$BumUO?P+CwW˺ȲNa.1Ӭy3 9 ֭]6?KN"2VlY`4`( I`JD؞&gP[&v-] Q[k|P$IBC"/@Ցʆ2zp"2N-6 ‘U diٵhS6YDV1.pZ$:*ʺ w8UUAUQQ7T3ͥZ|c(T \$KR)IZ #թ%WFttlUQb.qyrzٌf#۞.u i֜)UOp>WYO g8<▂#"fA=&?e+\sԇfpt҅Tl6İo>233 f-lڹ|t =~X,o;FtH(2ddqtvhg^X,>\&s3%\0STT'Q+*% ەQs[ h4 F-L4Ԓ,P-sFA{["?y%zVƃCB]{%כHIIq, '* !#ئ-d2dEFQUrр&CA&_*p  Cjld B @SЏy|DPh8 ]?Ղl!6lE׈<()1s( ~XeRhV%L vĝ'6WUZEVPTUQJǟU@EW$[ FB-Ɂɨ%HZ@]J?n%$ւ|]-B瑖\߾=je|\{WII ?#ƍc|h#nσS} :sFed cXWl5*ZhEii Gv(򡵨ǑJ.z7^?]u%55 Baa!ǎQ8ڴIJeILL$)).:tGXJt}/|:6%6&dE潙p^7A6lKLT@mriKT+gE Ԣl3]R[hEؕui%-c6ą|v&)gfQd|ĸ[yCDkN\\7Olԕ "+;&fi >&9փѶrj=Z:c"jA{ IDATҼEs  ĉd0 Ȳ(Hail2bsȪ|}}h4`jlD78ʐ; KIjR|r; XgEUhEqJ77@%y ja lTnZ3ͥGZr`s!<=!e"BaQǏؑ%v}4nɄx~ FEwia:3A?"QďEa؎mB>)8 QrѤ젮#=ɲLll,&8(VZqqL&Z}"uxD9k/jE!IR.dpr.VןMxAhYV~VbէODEN`4 ̙Y.}GVv~[rڅ&$tN.p >1TcK8T'ZUQ\pefPTX}5_ŘLsz4]l-<,+~XVtyA)(mH: I烶H4iZ gRj틢(JqqSl`0G`` !A-==JxXXjkN%.DHv1!b7LGեc(lٙ:|cLhtz4X,21t*9$I ׃}x̙-On婈ݗ6ThvjJ&#AC Ͱ)ohѼٮ*Ա%W^jӒsx%gXP JJJ0La4TYv;] XgQsSM!41Ptd)F#Zy wZ nX{_db22Dl P:[eO9%Yh5ZBq=z훅g{>Ah4Z2f]pGԻl8] a] 4 iDQv1m=6gAK]AW<4id4׬IO2-ŏb0gN-"W\Uĺ u8`hPU"cZX,X, V'{Peܳ}j s3)+ʱ$mA:~w"Qs hAiXEl&'Cdڻ&S7[rC.}F955 ?__$r&}Yk9aMh:|udYl#iZm?@`ԫ e1<(tygr@Ri*b4V-FCDmhnnVet,ΎmW:ʋTZrUtڒk?ABBPղ;hIي"Һ]Z۪ ??dE.UTy8>Z-?DM{WGv(RPj}LmVyr&S\bpv^U/I(,<NNl.jչo0h͠n|Vh hZ>zLz4Z=NghusSihzJB{I֏'0DF~{NydcpB*Rb”u t"6 I*gߥVgXao9u4*Oui7]6[>//2Ώb5jn*(9Ha(ZQ23abTk h@m5W-QUNh}|< j#::s NyN$_~#I4j?&n]2VKpp'#k=O1 5$$Ioboeگ+AuBwm"Rl[ jݒ+n!teBhbd4rh/nɕu l@*Zn*Ԧ}Q?fH~-97J&jyv`0jG1[غc7[w䟭ϣ)dd"22䲞! t4 4nԈi'd2h,Fa&LJhG4Yxbbb?o):|BPPZD+n/IX֬eaaѝ/K})BڿZ4);D4񷐔zfQRbviZ4}H٫dYF$F:^֓(pL.dg_i.}h4Ghh(a2.W .ʖ$ . TUEeFb:ހdtJWU=Ǟx$BP{HNC[e$B՘@ \uWȭ@ j F@ j\]=wDžC D֮_bRJ,*!r"'/jb ༠^_m신$?ʖ kH V%<2nФYy)'z/՜DKEQHMc4hح|B伔kVqYD:t߷$ $6&`VYōcƹG{) 9@pBFi RdY]TWjiY\"W$4:5BB1]{) DC/ yivc|47}?lH.𢌕)6F vlM ϑ[s:PFz#'e)6k_շk獢edzhn}$e歜Zl]ŗGe^~uM˶2! ]ک+kw(VCxX]}9+HSbpߴ7nr Ma-lK߉eV\Y˩eYasҨ ōd/(vJgKhvbs#"bϞ,Y[7h4+L[Wzuoy^K.y3Nfصy>)< ¬Gǒ~lcWY7W~ٸ}|fZĨz3ys_ju-[q_?f6=iѬ!:!rw ~YO<<+wJS@+~⢸o4hvn'̙G=/DFڋO:}| fK\Hޛ;m+x? b XdS7_BlL8~&NSY3TCxXP}뺏߷#>&#~̙9˵.GFl>_c`/W\ޅw^{ʋ;:TӖ̜ ;wĭ:z&""|GTT+Ixmv#<2"c'Ri ǐ"ƒ]:%×Ϡ*+=NҞoݲo^+k)ums +Z%p]#9TnǞ'l8x)SrJC"=#YVH9gѽr~}22ϐyO w18*SK+(ȟSם#<}.Gfٳ/wDyuČɨh2pD*O C\tF]{}BTS=&ҽkƌG2mW^9Xj:^60ArLGGTD0mAnwSO12nsG30GqS'_OA;c\֣-G?JXq5'ʫ}Gg}Hx azhSk!fFdַ/+r oWW2YզIK9DKN x7BW#DN x5BV(@qdYAպ^CFz~}!xSr9a)B伔`ƃ;,GKgw^J\>h$k׮'#}XwUpɣjn/GB@]hݾK}"xՂj9!B伖Bl\tzҞg_^'QlXKfF .yZ-QtՇ&'DK9|XIÉkF-wbDQdN%gͪ&y)׬ܭG}"x Ŋ q8)$^JFibԷi8n"Ȳ,D՝9@\"w1-,.M.j/*B ،XƳs,J< 55ɓ'` 44aÆl2p"$6mڠSncʩ-!zuC'OҫW/"##?),,СCLDaa#5k<|$-ZO|Gj^KХ;ϛKhފy?W4iV>>[>j׉G{.yJyʰX,Ңu{u-{wgfĉ>O>Z_$I7 ..~ 0qD bҤI8qI`0ЩS'vqNqшs"+;gٰqS{|Ğ={Y'n^hW^uO(|;dǎ]oˡÇYϬRSٹsKу{]XtkWNFf&s|8-M:.ꫯ%5-?U-ԩӼƛNi6m[73d<1sGX|9wqG^xnʶmHKKIxFԩSIOO'--DOjZc#== 6zj=zS;9s搟Ϛ5kشiSl޼m۶9^,:sLN:šC8x 'N`, OY]֯_ONN^{-&M:x{YOUUU5'tn-lro{oӦ v͛5sjR}W}wT*eDu] VY)~m۲Qm2eZWa-ZPmTNiwNNR|<<|z)<:NZNac+qjTTZjDDS5Q?TG2D裏j,8VS1ɓNaP4hV\?++)^ӝS|y_ex ߾Q{YOieffװcQ\Ct v՗̬,GŚu>.b0YDFVz}y6$33dee(.Ω^YYNiM&6υpҜT'OҲeKG7/::ک;a틿?$Gfff|;زe | vbرddʕtؑMx:ѤI~&M*+w =oiM剉q-\4"FrgӺ]äCڷc yj&=9)Wv򾤜 FRu֍_~ ~ZuHJJr=z?O_s"A\4"wGZmc]{ s}4yyk̯l}%5IUy0yE2bγ3jssweΜ9$%%a(((`ŊN&Mđ#Gl޽o_\\dd2TIT9p~=cXJ7ܵw-w^V+:妛nr:\4FDݸhDiH~W0쪫٣S=&ҽkƌG2mW^9?ʁ3t̋믽{4mҔa#*uUjk >>$$$S8&5Q^_=4"?p!:*ӧ}OѸqc6nȉ'ӧ>>>;rJG{>}0p@|}}[kc1`zdgƌtcgMjF>(_~9Efh֬_C@ j F@ j +e+~*nF駟>ܵ)~ +<{___9pGenǗw.vލ8]wG]h٦wkBV|w?sըkٻoǟMNhשO̜LVYt)o2w\>SW eclZG&Mxl7?~ ;nf+ybl_}}.iim9N7޼ upݎ:D^|E1]D D&~[zh4#3n4<=H|||txvK=qˆg'YxS+؇ ƌ3!''3fPPPvdeeQ\\̂ ߘ1cF}TEp!Dm 7u$4oEvqJn27)>++FqqFq t5. }rrrhРZ">>pjoߞ>@p1"D{`*coq^vo0SR'S XOJÆ +܎FAQ "F\ fLF#Gy0yE2bγ3jp4|4Yp!sanr-l69¸q/h}Bj^Mrtɣ?4"?p!:*ӧzF}R~ZMs.2zAxx8 .$11ѣG3f|||ILLw9\H}/멮Y3ʳL6W5#ȬvCpIK9DKN x7BW#DN x5BVeZx !r^JDt iNַ)Ǐ-n"wk~[̑e[Yq>}U=X}պqwŕWcIDATg2SkZ""X?9/E7к}ZR߮\tجlVK ^9/-Gt:zݸ"zeˆdf绫ނV%<2nФYS{Y*Sm{kuZbo-ZH!r^J Nl\c(2frFc|$~[k Ω.޼ZNKJld/Fcb4g>kQZ_Q\R\zB]w{c[degv,]36nr0^9mo`˦u4m҄^x 8r|ҡ}N]>={dOlݼȋZ;wbɢ8zp9QS0}ƣL{v;vթ.+TUu4Z !ޝGEq{VWC +\yQ'FQFdI!I(7DFE\fqL&9dq/jhAQAm\hl 6]>Vsko =[>bAVv}/Ol,8&ܾs55wqM NHM E70  (*.D,b[O|Qu&**}M{CC|cY v??\fW'㽔4^шK/c~br{ü((s8x aaBH$0<R2 >|H%RH%2|}`y,!K0Vn&x$.GtldR)fΘ#Yk7.JĬobfsI@]]unY`Sޘ 0˙>ko C"a6t*B:61 XBx<",,B hǯX$ |0 Ӫ?oDd;ەe 0 LU%iHy?æ}޽=z4Nm0H$zHe~AJ R;y;Φ5/CIPTm]Q^E✅00!\reyPȕP*p'⪶u x e,řa@Ǜi?oEIhj-_v ^_ACC! 8}5|)P̫( >sz(G, ;_rH3M/ɼ0 .[ۡF|fGHg(tM PTRkP\bvV\ZmIԝ(qTtlŕy0dĕyؽ7wlGA% .ٌ_Xwe{7te*G, R@hX8?CVVuWc@H~&JrAC1pPOBhhho@EMMK7$QZw<yx<O{4{$Q>>>(*sPr˲+U6rDi|fe%yT0(qԍB9#B~ <c"g6QVvYB Z px/#.+k2qJ>vaGIeg"*?v?!C!#ٙxit@o0y D;of,"@)z.ҟwҕBy:  AaX-(8 e`ore2D% l}MFS/2ܰ6˲NQ#EwY+(x:#劆ܜI9oL3!β3\[ҿ;cX1վ:ADB dVOr9Mr\/W͘3Cڴ7/['idS;3hbFa fGD)>M-6hh0al!}>EҢ,44:%쌯p -2y/@Wyn2<"zOkpO|Ċ}5FoM{koZii)Ǝ@PZZnI.c)TVëz9#̙rde&fESIqYꐕ XurLAIIӳ7K[6ez)̩(.;s)1w$^܆¼miX7lgs/DmH(@ꪭ6}dE?`'ƜEmuHMbisSUؿgOyӧM=V>mߑkT.#׬ y/?>(T#o߃u\풵Q+vwCW9Q93"~~L}szgv.z!z wԦ?m@Is^DV>m2>{&$yFl޴}** yVЎWmQZZ#e"DuvlNCXm<:]}1tuU[sr'O_@ʭ=_ں4_CFisN˕- `lfޥB$-Z;]x}c^}9}±,m2VaC:>)"^Fp@NRp$h4mTC[x;ԟB!G[xry_$k3ұnl{ 2wkPyڮVđBFީuʴ; ]޵rU7G/W&EJ TVCWGrqs@,Xkjf\ȻKw4bY}_`NI/Arq*U޲cos}A_/0wmgN{smUm F~λ78; ^;}m}}KI=xb?:?i/02R'E`_0sf>~}0jv ys=(1v$H(b<9 <9 h.'ciC&W/_2Sgsy5wm]wm7s*c'pMC%yj>5jUOOB۰ W$# ]kːvT*Uǔk8ga#xr!-x5%9B[0}8eYfOA&%|F%k;j)*3wTRr Y Bq[qjuo(%9ÉVdot=L&3  cMQ F,fs7c{vDF@"n\帉9]w*q eP;vu9B g⑝yJ\jth 9K#JrAC1pPOBFC#F~wpëj9! Z>Jr?! HJH@srN$G4JrNksNs;B!fC!vvU}m&$oDsrN$G4JrN$G4JrN$G4Jrml$Gzfj^DzM!^yJ*!sTB8yJ*!+Q\%pB{%*NhDr)Q\%x-GJTjc};b#.sD\9~y6~Bx)rd`dLz<8ͼ0 W/G#*UXh ef34?X [/'$Nx)X"5b"9~UN-.cvSIENDB`sqlkit-0.9.5/doc/img/table.png0000644000175000017500000025320411714210425015513 0ustar sandrosandroPNG  IHDRtP6rsRGBbKGD pHYs  tIME%8X IDATxwt\չ}̨Km5WܩƐ`j /&&\FM  bm6WE5qF \%ϙ})Ovf>iڍ=TϙkK"kj[^oEu;߄CU߽ui@|>uܩ7|t_v#ͬĜM<"se! =q]j(yä7v%NGUeR޿dxr_5 <-r5BYwkt$ aFZ)fk$R{ŜaZl؛یL&_W{=":+ 0TP[^W+UE/MWVS0oFIøkt"g0Y-Y-]W  i?w=; ;7PH:T"A7ϛD2*D@.գg7=\#p[,ě\zS+]FS 5r<\ym(\}{,cKG/ȁ1OO8)G!I)9g/9kYȊϗ'Kjx$?)Yլ=tpvqٸ: "f%h*(M!AE1x4ECt 8G-*ז44yM'Ru'-8{W!84/%cӟ$H#gұj9l n]`O! v) :Z##Ԡx0۹E!WJ,u-E%k vh A]lc6 @iPX8:M-ƴ,kmN\Ɩ*H)O V1PuTr.zH0|D 0ĻjxV=~y#yĨ`(-K\ʒK85lMb@{",'L)ԌkBW\^$ukdxlMJƟ-D8V]{e8ÞR2jEPuHkip/s&KK *Ū8_u-7}fm{C12occ+vZ.)Gxۤ{tL4צ8?>D>Ni ԕ%~?c5|ClUcۘj80, tl0  Ύߙ#RV~ʥPݝ{xHDRZ&a8m i`&Ji;3Y#Y1"e0hL p]t,4.+KѴMzm!i!\t\±7/ dz ];ћk4-tUQ&eh( vnsEV bnp-D-m-ER"BY'/5b{طF*!qU_૿[B|J*!2II Y;U|mZ-=6Ah%zY5k汆}8 TW&Yߑ`fR-f- zLf˝1w"N9ԉD(ctQak#@ D1ݨj {q8`8Xl/z=^1؁D-z f?n`=X~&j8@Y5(: y@?=|?z)%v+Ӎ jؿ; X%T=`N~3jTrll4 B'D #Ѷ-\ƶ<+Po3OCbƕ "eĢ(XFGd(kP>T.xϿ`H[︊?~%uf|} c \kWő4um1Nԃ|ⅼS EUQ4ky٧{&UssC)%sI9 -U!v/jv/Us. R{~D~ Աx7C,2buPB1غwVSv`w'>i"46ZL]*İ\ֶ rڴ twAPvßdyE̜Ba9l&OA߽eLLN3L֒ljFۻw!sJxFlޱr ;i[r3!P:&"zj< o* =m0õL>bs$gOa"<_TE. r̻ٶxjˎ'G~g|mO[I$SԜ|9F<Nyc]?#Zzhä "uG)]P4ƿ[jOF:* ݂^7>oi6U' ?*c}w||/JH2MD! bіapoյ-\2 OxaXAٻC(;B2 :% ]4p]Ƕ]( EFcw帷_c-8u ӻ hP,p\MBtFc[X"-L2"}i @UT( (P229 XN>>Gtp9";F|$~GYO1xi<{ǜGe@1RǵW󡷶03h92QqW&Hl"{AY%5BRZT,sإcHIswhO[f2Ѡs5x?9.h<]l{A4e9_ 3//4#f]ĢG7"sXy+oyB 펬ƺfvu;#߻ozxim{h}}IL9Vo^ɩ{[XYz*}l̟0c]wO0aYlOfhW|s=vϨ_X4$(jI~.v!;­>IqbZƥ>V9}fb`s) 3'êr]1ӓuUSp,ǶL-Y.=a4.:Jey494[ F1O"do0Emh q( Wc1"qmǶpXXȣ6!!=?p'`݊'YyhIdY/6F:i"\U#'e:K xm O)Im"mb>>G5&9`G si(VM_㛋kn#A(²Elcf"E QGT%cYųGt`69f0gR,qYAuLphR@Q'j#XES8!ȴ^p \D8<5'2o$ܽh 4 D '(8Re>H4clپeqU \њeEEm::z.}{YKWQMT`ٶ 'c3'g˦ M嵓ǒ95ԍ_ fTr`XH@e[g8,|I !۶s~,ƅ3HE/x+]o7I:Mm8﫬9+TSOKC f:QQ2C 7WqNWB6_X(|QG;ҕ0hUFL,2 X,ELf+4l ih([;u"0euE,7<9X,ZX W4I犨p&0 BTTw&rd 빾y±,B+{4#&8 +@"//Ft]\ug>>G3.( m:ڶ-~& F|湧q r>s!&&!]h}),` uBEe5Y߾^:V1A:`zb>}H"d)W |bf w6L/ٹ}5-s ?i1cαXWRbҴ ==ZXS"r,[U?k CiuR  B,X8 NC*@$݀/mcP[?d,D /0u\;Rj'csر~5s&գWJf.d{R@:-eQxKp$s 'R.PM[ٹn9'S n^ڰ-/]T (sUە8븨B_:O'V=@ _شeȓ;dz׎zZ, X'B;va6l۹0p%Xm,tnԮb.^a9 =4N瞢/4XVEU@4ij$u%@/ E|JBz!#N)Q7*p9HE <{V?Nb!Ifh5W?=z>O_F[+toZJ(;.@+o#?E,eM'S~DAչS_mWa-E{vR1,>oHf"Mg0o3c$oX}  :wN|,[nEX=h$B B ]ڪ'0X"Jj\IC,G?o̚<)%HS8ƲzYc`34r%dn&LNTFUU ้(T gYW\M"qp;r?P0DgRUV/TeP]R2男aӄQp~&L"TJ-}a)(%%ƴ Di9ɊXu/8C_ֶ ~qU^E WP2*PQB4/BQP z +zv%KݰUlLȢNQQpٰm3p2:2o[ڼI yaĘɄJ ۦ ߹}$S>v>Φ5+iHhT Yl5O:ʒ)?d2\?Hy ]+Ηwb+Q_g;K[9as%Jz_yqS}4&_'nP$ ceA~&v<&D(kl _4C1=/= +q"K`{dn-gKm_ω-)B\06wv[/?,j ,[f-c zY6o|L&M:@?G7w`R@UC,z1sϳcmZKsN2 YjfIV˖ukYS}|yF6Eb%+A&(c?qz6r΍y6_G]!G##F, đ 5a>z:-&UFZ7oJ -Q6q66}M]E4ǘR"@m9/:M\} LS DJ۠лg"RLG_=,ߞ%oTF &!„& eޕMրx4ԉqf[GV-cvS.fXݞ'c+b((UQ,X$ap}+dA&1> K{P@Pf1ʶ,"D#!NTBc<˖.X8Qa׳w$a@ZQ`Kb0#mݏ_Gs*t=cYibC*X(jiQN$*^X}WhRc/>>"Q#2"TYo^{`xu ,e<UɒJ!ԮcTb8| \H)i 1pm@ \[Q(n*1BGܣj`~)B0p_bKJ(x)mT!KVʒnXtKֵb +e/ o)flwX@R[e3k,sxt0"FE"JђR]p P0% ΞS6mvـ8wo)[zd 2[gsNZyS 0 RЖ ^-3:"4̉4եOlE - hx*B PŰPkdxH\)QUSx\U%& =ĸ"T.e!U+} bm`Q.6je\yi I4 A83Ji \ BH8eZ-.;,T%OOb.Fo/Es[hU@X1hAv>|B@uM\ mMT2Ux c}h]  ˖%C_.MZzasGAWD#oy #xpMz3#% !]!,{XaoBTBQI1AXWF0,4f_NJew>~sL$@'V9kTt\o4VJG@1Zʣpv,׋ME,QU|v|9//ͩ`JJyTrA/QQ8R⸐J4mxT+]ԆP * RkFΓdq}8L'nxsl晇;Z>@Worc,@MؾXY5x/`t v3&k$ðd6z nQv%l|q+uIE(u MXZ9D'o&t4U,ubt<ECTMۯZ* ܨUVy]\)Ur(1MZ1Y%NE"-Wolgu~B tStV*DN8 7?^ bgeu jY1ǪCXREEªԦߞ:9=kgLQ06fGs;긆᨞T13 q|!X. JiYϡLQU&ϱ&U*1$ҵb$ʵ_V`rU%V ݷ_sԺT`fS MӼT-=(F~1=%>>[Kue)_kl`WzGUFJdY--aʡ]˒R Հ~̓ 螵K +T/^JIt]!uOlJT>zn_Ehw̠pP;Pfjz7onro׮kו iLk{Vrȕ|_|||||||||ޜD!@(}yRhU[ D! 5&usZ ql)=A:7S.cr-5/;oEJHбK;@Ͻ&\q"²lě4, B-#Px<4M_Y!sG褔A/_2y=oW4۶ߔ'bJnhqJq5aɒri c|K$uF#8ioXRJ"RJ /}||||79V%cI{WSL4-:'yC:Gz֭}~Ps(jŢ|sWh1wl "xw/5/,'x֪,Lj<3f߻X,J[{;hD2 YgeYX0ւj1%,[ ?FlKBҋQKd)`E(6rsVPUU [x]~;{cyGtzOd&>OKٲa#׼qW1F`L&5sʫL]/]I8&Li&ϸzֱr7RJTE;vb 1yRHڱ]{v B(dٴi D;w!ǟ B8};QU)[)//Ƕm6oBO_8q7d?i̳KuvףicNvK.}7{E.uuL<qX &J_d˲H&2~|QZĉ?mw0{L*RfZjj'b&F'?QfΚ}~˟8q{~aMd:|+W**q5χ:u2't(Zulsxu﹇l6w}5?ϰmScgTd vA4gimif7SQYI6عs/~/twmL0;wi:?SO;LKS#^pA5pV1EQd%ҋimiEسgNL*K.w>~ΞE,vo@̚9x<O<qwû=?ᅬϿFͩGЕ""/b*>/bY6S&O"JPJ PQzzzy'专NbU|_l:zKtdҤTEe6n̸zO(!TU?D?r^wf̞y ?%b`@G駟F876?p/ϭXE,翼&>b;6]]XEmm /.pAQ,P(pJq\@@e֭\p{Q*&m[47ru׳ia`p+>Wu|Qdrk xi:f̘/H0iSdX"c(JbZZܧ?IWw7l 6qM7q?Cyy0H&F #ϐPPU$\$DZ)+܌iDS)'?DZQAmʢq"_.<|KW!Mt6˫+!ds\t|?-,{. S_H$̢Osw^\WTUeoG'=@ĉƼysXzzG4HTUrXsC`a!\sqJF\ضK+ǥ}&Nmd*ٶ}R m+q]Rx͘wv]յk_bOi:~9r.tG:cň%EȤ|CضM@0q|COj 6SstcLjmn 2g סL&-Jo'L"hKGq%Dr3O'Lu64Eu<Ziin.k*UqY3]"ƜK_nIxP,~_^kK1+}rTZZ݄ua݆,^ N> UUfG:B'䔓SYYN6#0^5kJKyyp8ĢK沬\<}}$$lH,}\ď .} N8X"PI0:kb. E!:y2LX4JSc#];g)OƎvAvÕW|TUtB?est%AwwΡqjkxGx~.|sI345( M*BI** PZfϚIY"sESIh |]!9)E `ۤi "ҕDe؅=TVUJD*B<'bHidYe a.u xV\%W,"HH$E"$ɑeT]](B0$ Ng4T*Pz{zDJB*x+R)E?ѨgUU!"DBɧDQzzzF2(]]r`XdU^Wc (cOq߃$Kg%mxUriw[pby}F!p0{ܯ|9vGӁmx/mÁtu9^4" ϱ;燺a>>>>><@`AwpCZ o4~8ꬊ}ٗt~7+_8t+uGy=Ч:^8Cspg7~:L?_\~ߜϿDyG6b)eݐly9Ʀ-[!_2y3!ƬRR K\&M1cF:7 PUʺP$Ϙϛ;nԇC͆@h\-2Q\c)ƾv6}||||||||+FSc Zct]DzĹRJūw  b1rD,Q4Jd2YR)H ^}L]d2~6}׉vEL^zy[na\=tMTl6Gmm 6no9w?_t!>f^^]r1r7~N=y\wr% |Q:RJ߽EQ{u]^ze6oXuKq]tMX,A"0RBPNƍg_w7h u0 #fB=R(#KΎE>>>>>>Q>>>>>!$SIl$a86t`l6aX6j)X7P(&ud"x62{,TU=Ri88 'O!̳Xx[ FUUo̍ΛCeU _r)Vzc?CJN@/nuٲe+yB/+%tKil \AtB!OxE"AUԑdtpߛEdسg/SSSM0DTE5k0n8&6xӜ-}r/ ZH::x;L{{;?0^t;Ig(߸|CCYh?y؂,(dӑtݿ`@gzO /( Λ罗 $@ %8ڥHX/ JGާOWa  z@rlǦP,`ty晧O~O.}ӅB ۇeضM88LN22(/+#;t$eakunřN.>$ZB< gcsGŜ$KI+v@ AЍQyeyy(ǚ$>F%RKk+3O}iX9Y׺;K% yJXļ,~c3Ñ<|^ 7t=Kx0,-mq2K$Duajl)@+l֌GddY@q(x<.9%Fe4O2E3%lNnF$&Mi6$)YYbhhUQH$ <ՔKq?cSH N߁DcZUs*q$~NJՋ5 h4۴$\zmc&xӉ"x^z)# txFVUq</N.EfnYilI!!3SgB@  :Iߏ(X&0m(a rˍ,lTS7Px,JN ػC.qm++Pi$OԒ]Ԅ{`#VV>ZݫZ|~pMg`W;m#K2iֶAVV6Hxt,$a;P;ć3rS;Ƒe˅ #-QUE3R#(tDt,BQ@(~{yHnbb0p-I.OǟEQe:@ 4 BD"aә=s֘z2xBdge100@ffhIzP\TNrrr$ ƨXfcW8R̤EH(ɂ<&_*(7E ǶRle9)&e9YNM9@ ;m=LIJLn7aJJٺu;g`5L6ֶ6dY+WsE˯pm;8233PK ]H'jQY#9/U !$Idfft:x-gZVMS|>/CtO@U;wt~(P? -[g>Lqz|"($ Mimߋ rH㴴Oc(%{7t -GI@.l`Q 9!UU?b&Ã#'T(Iҿ~"IɇZ^uUy|꘾:{AGNvmzX$tLմ3*iJdM`G nt∺d!Qe$Y2}JƲúODKdYFc铒$a]mtAb IT5Np<ěCUGB$ ``(룍Pu]B!j>HĦ['?#33Nm348tXkq G4M5`088ȵY9cȶm~?/}`04:$0a`&C x$Ic&  K$I"H$u. ˶go+, nmд/A'2xvmêk%/( }i2?y]y5"Fh1z#Ih/~kgg=108a裖Ew`1#;;wrkY$=( aNx$I$|›7cW3'Og| n;^misOY%|}Ɋgז~ܓayE9|ξ^r5 /ɳOş|ζmY f9bql&-Ϝ9~9'F[Gsn0 uX9:@Ho.$aI"ɸ#RnԾݰҘ6n6eY $DBc|O280ĝƴ,2iin]y'hk@UU 1cO^~e)ӧO# s ]Ǯ|7{;2e2@̌ ʪ5NJJK9B} Zq8 Ǯ.Zba."p$>K$%'tvu\3-g /`og'`8_Q/Lm***x<3 iki(2#ۛ(**dph$.զ|˴ }}I[{;n0ؼe+?H2D5|( Pk?E7tI0䩧$"(;E^~>Ձ ].lÏ>[oq՟g[pX4̖mg?C,ܼ\3(IqqXg]̞mq޹0{#K3 +W;~Ovv%E47Ʃ'P  -ӝ<{BW;IzZk־Nee7臤/TTc[6yy_]y'bp k6Cqq1po]ԩS 7{defQTXDQQF4-oMb[5ina;;9S(,(x>.)eILzSLH #t$,R=k&u8N~{7ETVTPTR%_IJ+8s~D"䓟Iaa!W>{ n3ctN?mt:_ƤM|k_E4~tOCLvq`OK <Wh4]DccH<)a멩u됰q\46GvA1;).* #3*n po~pW_~Օp uDUe%Ȫ^]ʺuI4q߹:ҳ{AsrU .Z~I閟r9g˖/wEqy9lgޜٌf8K.o\x̞3;| Ln>f͠,G/gYtu[W~ݹStIO7݂ħ>} mL2ʊqsIp}tp?YǕ'dge_`k+?EC}-"/_Ac}*{~w2ʫ((G4r8>0|_ /a7 7ޱ_3NJ8{·u W/~4ذMsf⤹shg?*򕯰z~y9 xr'a||>M7UfΞE"%p]==|>̚.y䁿Q5~LbNr/E `bS= ֭_ϊUhjhnǓFppiSO$:e }h3{ZBCeM3xMB$)JOO/UUD7o瞔 #IbA*'ThEA7 vȒDo_cYaPSSMzvirf:s!$== ۶%ti{? $F/W`)'dm;vWHc/㫪ÄB!BÃhN_TWWc$QXOVV!e^&OAdxA‘0==EiY)`AKk+e<摓e%W>Sc1.C$ *+| W4&N@cSC| ֬^M(Y${zXv-mf ϧ.$ JJ`` y?4qO?łƛo[ot8p碻||K_ "2ӧO#H68h#3#u?sb&"A0 .` pHK$^xq j`80LqQ%%%%atqGFBƜZ6Vjexعmhtrw\]rP3PVV墹O^n*[m4MfN麟~.;W^}Mض},3yR 80 jYk455/"E_I*.b,ˢ %Ŝuvt]4NDuM-V:־Ӧ"#a&H |m+Vr֙gYMjcɉry`eW'|T$WȎܬtV9( PUYɣ?5km IJNV&Hɼd<ʓu-%,l Lۢz|/4*(/+奯"K2%384HiI1.kT;Ay:_tX͛PTTHiy9:mk~YYYĢ1Yg, '6$ ($188O=M~~>fLgoGF7#RUYiZbqL+6 /.RtFu]'-+_Z:VJȿғeQn,s$7qIX륨UVsÍ>6mق'??lҒ֭_ϭ Ass+ u__2/.y 43qNp!IpOj~? [(.*TOO`IyC?iDcO}<6o\DZZSH42j&_ n ضg466N[~z+W\v gy -A43 o?IZZ{4sWr:c1ƚ`.8aqSD IDAT_[ˣ=INN6۶o'"I2iZr֑$4]ORiw޵nHc LDtw,]޽{9de}09srr?_Uɮ]>BnN.ZBc]wzhmmKzF rꗾ_{r.pC5H4)bMlN9$2\tTʣʪXvR8 | {_[8|W`'ϛˋK^捷&24@NL{gYU ^f͘ήx'xuY.`YfR1n>+Wr³xL82vE,ז<眽u7Sֱ;)TJJJ ~:̞MiI1/X$7/eW_Ӧ}76{;;geIx梨YViY$hkoqdseW;Ͷ۹W^|`y3ibuIagY&q䉌?EBUR*Cecn&iii455eY466P^Vͧ0e&L0ƆzN_0lMJqQmv{1}:ӦN9@uXx<4M).*b`p,.jWZBQa!yy9v6u |>|^/dfeϣb>Jap(PYQAnnyR_WK/Ĝٳq\zETW#B! L~E9eH$0MsSNFL|>5}䓞Cʊt|p]='blvHOo^3grI(--!=-(t\n7ddESc#.̤塶Ǔ\i >m >.,QPTe_Imu5TN=ɓ&P[Cff&999  PYUɂ2iDJJ&QPOQQrkQQ!LbΜzQUeH)Bjk0 UU).."3#ysP[Sᠴܜ(.N0Mf͚Icc0kL-ɓ'qS.4rs^HYy)^ $ RS]HM)b&HLc՜~iSys;wgZ\n7fLgɸNjYp|rsb1iRraddd0? 4Śrcv8ܾZ:9yxMY̎~$ٶmc?BVTU5;p<dH0òLB0eqq{< X鸽^"* ^YU4x<+"AV'$۶znT%P("˸=\AK$Fs4zN$dm#sdx80,dE+_cSbk{Ƚ`y]:yM4W[Ƕ:\#߱>˲F{쾃meyTls\[z$Yses³6{~y`gNjihXfҽ|E2H ):XC? 'd'q 뻇wG/ uȌr^> =Gx5o-]I;2K; ͱ#;&-;umw=#9NBӘ:u ])j6yuMGDǶ L2MLHԙf*~v'IHRv/7N$FmZmdggݿZIRYa!4-,c7Ш9H$ڇbZ|PDƲ}{M)*,}¶eh6FM}|4]Z##Ô,;zAmi~_,|X1 #BяG ɸrCtaPZRB84e/?|ÇDnNpp}S$ss>\-F7 4]:E"m!v~xK\.1X(DWY&x?c$ 0"_B @ ǐ4egNFOh& Uo@ G) sBSYןyr @  : =C4>(/'DJU F8H&c@ ^8@ 9A'2`h(MHDd}`Z6͔*DI$-!2,*v#R6䢋/_<;N'W9bΪůo7=T;w200,!,KUPtdב;7_PsT <`;r |D]ed YNé/؂p<47FC{zQU/^̎;K0a$( A^zi s GvvvKF̚59sV}p\Gdߘ>}lؿq%X 'QXT g׾NOw99LHN*K_YoM^^gyEEE4^N>$3Xu"g}<1ctTU=h,ڽU+W288H~A>ϧ!^xz:e gt000͛il,KKtuw3SO  fZxEoh9===,_J:[y%D"fϞԩSp8˗H)uz{Mկ+ h]Yn66oڄ>@H#1w}HadY-;~|8g(|ا"˲Fu]ױSg>m*Z"&z@ #Gxgy71mז/疛B @N=t8֬^ͷBBXzE44 z)^"(O=4-^#aa#2p[̛oi*[s؁lڴ_W xydH$o<{;ob*wXYq+Kynbb1ya>T ˲Xt)?$Z"}^~v6n(2ᠭx",ȶm֬M7/Jz9YIMM_W][O~S_EKnn6oBiY9?eΜy<ӬXa̙̘9-7?<!Co,SA]]N_qC(?mFCC}+Vd2ؾ};M tuuǗE&L=޾^b̙3"  6  |kQZVv(,Ӵ8q"eazi" CCCSX躎ih޽<‘Y& Ʈ]bPXX֭[y <\tGl޲O>A¡0ť(JAA!]])UhokaϞ݌_Ezz !t {S ]7..Ƕr%-,?%LZ]Jnݸ,>p1]ZǏ~aή0}}Q$I.Ǝ}LN lh4>Oh [r%yyy\k ML__߼:͛$sY2Nh,MyGя~t:| KK6f@p$Ilټo)7|۔dht;eYh>:;YJgP.'i̯e "J 9x gi߿Nll ZÁ`Ӧ8]N&r|́HLL@e4Me޽o܋ H[֭[#"QAl?vݙ$5h@bb# TlUFQ%< ۲III#gmU"t 4r9v(GfL>T^ ? JvΝ[X`!_}˯i&}~.GFFF8a2#-E~zd҆ =k&;v`yodYblݚƺuk#0'gsΙ1OҺu+4H"33ߏYr%W`ȐԩSh7ڶa'cv6}aRSS\J9p&$Ea…do%sN=JbB" GJJc&LDږ-lMJu3pqFvZd˩Sء#n;ذavn'#}+nW1h i֬ H*UXn-{!5%4Y~Ep8zoo`{ v  7H h&fSM7!luLAM3,!/K$ìٳ9q[8p OMUu ;w:رϠkj֬En]3[n76UnB޹ ;u̜5h75EԭS;:2jxyJJcAppǰaL1ʫWψLvmtjլ$IԮUjժc %E?o~c'3edryfES˸{=z4kԭS]׹馛x:Mѯ__غu+իWeAٵ+M75~?bŊu]ɓI߶zE.)h׶֭c #`),(`KIP5qb&jӠzjݛ9s厢Zjt0|d>Ljj kԸh&qMz>t3,h\ye=Z΁T˥P/*"bU*Wr(b8o0ETI|*YG L4sg!70MI9mDQ: ]PPHN #GiPu% I~4=4IMB25LTՆa۶mѠAVN]$$vE\Q[6(99RqKs[6$ $aوnSZ5<;:pŅy 'hFǎ#@bb"j'32Ij*,c(kNʢvZԫ[7wP(PJ>[X!))Hi0Dv6 קf:̘1.`CѨQ#LC' 4lD5u 0ܽCQTQF->2_~رOrEIߖAQqX`08rnw11ݬOYv@PFh%k:~ɓ^x~5j;"l>Q$$$8=ߋSTXȌS:cz-:fmv;KͧV$Sp̞@ AW"$Y-BPT6 gNS2KJr۩6;E,KDG9"Fu|YIJI` iVpw3kԖ0(*#gzk@|}$ڕsD.\G2:dEAu$I*cDӯ  u+"rS`kPT0"9" Z53(>$"JZ0f& h(I0h{ş..xagfYk3LzA('̀=.$w(KYXXX.&uvs>ΏSʯgglεwވX粟?h?2rfrT v pYD^Ŕ ga : wb؇*ZmV㴰~cS IDAT8o ?UA{ahiUM<̿LЕ;5 S-Agaaaaaaa[%N9VEZo%,,,,,,,,.̲+*"Y/x~r"Ve 6g7̶gs~L~s_?8߳S}ZNGտPS*16QpY]N'" p-I'Oq/ԯ_*aHQ~vdi/FfͨWE?"\a4q "v _qa (`w4_!蘰3{ ʭ h@&'i%d `6gtx C' Bڣ>DPFs+%+DU1 aw9( ,`s1^Zd0 LJ,hA尓ڐd弅{B//alڴ)cdָlv[y\iDDsӍt.𛘐fjYXXXOȖmxك'NY3C;Gɧ:{ӛ LDebbӋݺҽ[WȾϓO˾xMUUq:˴i݊nݺлWv–-[>; C#cGQ$r {!9A+ + f'6" *& %鼽tJ;v-=}OLat'pqB?.WcqF't1 h7;wz3:&Y1 ;) raSU@_NCT\81A>YYc$99̘1U*c|1sN"Ifhw>h(Iڛ,_< ժUcǎ]>D̢NQf| _}51%'$~4@ YuL[;8$ItvGAn6o(y?Ӿ _BΝx٧yxx}E}+VĉlNf3ql6;cY\]׹Uʋ[ ٯ@:fe6+aQV-*ahN ۆ( l2Ng[E !b؆өpH8*w ,OQ0?~]H|x 4n$ۜiHzuTRXFyKҶu[7p3 p(+ѣۘ0sۏބ(\ue_nFTLڵkMe| /YJfM1 IԱ3Nw23w##v54nԈWc&5jTuVls еsgjתqD@E"o)].钙N5e_p/mvQW8PHTl<^_~x YsVaOp_g59UHd9dx<> ns 0E\E&cKHHH8k'҂:KyjVZ'fO:8$8`d_6oP\Cƍ ClSWƁشy :\8 ajef J84pYG&'v9O3N6LNT\ Y8y$>_ЬeK*THܳ[q߃8?qT,D~ٴSxCmǎ߸a `0{HP_@nilٜ{[wuDwGDA%-Ν;G~A$>>GErTܨ!Q&m1m+1rd9{=>=ӁeX0é^73w>^ 4DRm\3hz#FoF;nVrbbbFhNՉMt@ ʕvgM,,*d'pبV*=w( (\iHTr\ѷW4͛54M|>_B@xdeUd+E,lޜϿȃrJ*l4~$YELLt]4M%J S_͉jAΝ==4Puaj՚k$;v"5k( Ab|IOxXVY^aKZv:fDL'g+V"Az/&ڕǀ`Fdt&m/MqACK.!#$۶R)M2ʘRSnpIʬ[ Z޽'4V*,ȣ OqǏ$s'еvJ]x'mci<~9;Se -#<-LګDk&bKljhM Ŝ(,[^x#"rQ,uTV}( arB9(Jx=(€iа1xszCUѬ(Janv cxLCcOYv=׬ -mIp|^"uĄdY)W(ʕ+3'YݢrϴjQP Gۍ:t=oЀċǗd TRO1?\8Ay^N/3jOF^{c#}ΉDI@@/)ˁ@0,n~>r9vĔ2xX* ]z4MtC@V (v7W4H*( Bib۹ځ4nԈ~?>/,+I]JŦngab&`FN(jFaFmuaٲ;KGtB&,agaaasa0sַ|<~]7vt@7  |ǢAW_{"V^nDjْ?ϱ&qӪy^/׭=ݳ(TU9y \ӟW_{b[J+CxG(*.C2پc;11Wq$;Φ[ QX5kQ&NLlKOYHBbzlҶdǎ4j6mѸq2v=t1`K }zװ|!$)e2EYGzx|U U ֍aC##e?4MlvSwpMIoTqfYqQr U)"izoM,}MDE\ݏ $GŊ ʏݥ'#\Ԫj"˸QfCN(wdUmxyQ<83I(`qp[4w?#IU$IoXj5Yx)/`/HnܔLOOF>8F>J]wFڵ4'|9s瑒ژ{I69z<,'p%tzNCCҿ_*U#??HlߞA\L4W]y9'&M:)q}Aq^~/A رDݸ ;Y0M9 8Fb(D|xe<Gim!B^ v"c:al&躆aȲ,x<<^/(2(FV/@DbcczȲ|DY ?`x>4=X& &&Tp| Qrp88v {Mu W֯ I|cp9]Ve(.jFll,scw~4np\lD=AKHDE9|=~e=QQ+..Fu\.E0BHt=)Z$*TEDE4f ~ $`S%p1bYxxAEBQDDQ@CR 锱N;kz8gSpc)CRT~6oQ!ܙԮLRrJ9=#zíÆ3{w!ju푥[N.\'S%YBK~*2|6%M3vDbIEЍHCgaaa?;QQJ1Qʼu]GQ#۔t: G¿7Kul=l~a!bvH2EԮUzu¶$**㚦r^IMM!FKnFb[WrnV#)Iv&bA v\n\g'yLS\@ Bht8{mx9v35ҲS?8şϹC9*C]|j-뇀(-/'.vm n\\xyL 835rTlQ"zV8Qc.OQULIqC! YQyq# Fh?kR,f'. ^B^^vemiFڵm 4e"`% Lbu-O ( Z7 :01ty%i`}š0i)EU?nRijtE&?00JZ%1E9#m%,,,,,,,,xG"։4O*> `a=S Eu\w4MUEQOQ(Z 4 IPT(op3GU-oZMEwN1! (J_Ag {|4TU}Ȳ}ᗹi1c{:k׬f:"{Icd\qq16c{7R9:K -Qڇsmw.wi6u-?ҦϵTX壪7 DQm(%~Q'V(3 + …'J wlgH]wIqqj0i< 4`̛7S0QA_~ݻDw;xgʷy(J'dg .>*",n[m[缹dǨ6xK`^~O"Vd.(喆<?$s:ޔ;^xYTbsk?|Jo_6(Jo%QR? AsZ>,͎'cL vZJAv23wC#i۶-W\y/.bz8J#G4i"7mB9Ƨ9k?`"4MckN{1zkV^fw>k|󍘦S|F/ׯ?3䪫fwD.3qqPje&wNi۶-?@|;f:ӟa{<_VN3ĆFrrm۶(Fq<>mnkXD_~wM6m8|i4Qm&Ns8u5,|~?OCxz6L)ݺuK.|g}~s:Iٺu 7tmڴo(x^~-nTdY|w)S&Ӿ};ڴiٝlX}[n,nA]'I"2y衇ٝdn{n|;{( OѾ}{*VZqQyEQ`LzK/x5py t6@VEEa/ywiР*|9u*M6bPXJ׵@ddr&ܵ IRq(񔄄M3S' ;(DD䈱eytZz6Ն(JN^}ЂA /?-[6`S]ү_QUʅ~ a.2X(Hr;𝶭ih$ SBe`I|Dׂe &hyEZ0@v/$&&O"_O;Ͽ4r侅wvL7Qm@Vl垡 Ȋ3(zڋS%}D A˵CMBL |g_!"kg2)7$}%`w ѣdddd:vԳ߲}$l ` ;(r{|j2P8>]o%ݑ$'O2w\7j-yGxשRb:UE- xjj^}uƎL8;wsݢE|=-Z7R\\̙zRQR3 4;Ɗ+xb̓+ǣ:;v ɡZɶmؿMӻ7ڵ˺GڵPm*q/0tbϋ/Qr6kc՚> ϦM9ų) :KzF:99̛?1OGtڕ^zSO?Mn0` )))>rwVi0筷kS v''N';;Qg5THK gdHժX && 1klْ}ҫW/Xb%W\~9{eΝ<\w&'rJ`С=ΗunzA&MNcȶLjqϽm^=߀iL\N_|k}͈ IN!Cf2}v{l Z} ,_~W2qfkB #++$&V$UXΖ-iѳgz!dY!/={2Dݼ\}ux>>CFei$K_大;vlrSyWsAyŗ2x0_}(A6ofΜ9<쳼lܸYfL&b믦|rx5ƏcGKVVax׷//k.<󤤦2iDt ))呞U2g<;ݻw￧~?/_tn|SO1n ,Y 4~n_ZqP `KZ{dTT>_~ŋSO1et]#q }-[T4fzK xbϮLyO5kְtb&N yd&m:ͦkg&s~˜ٳx9rI?gyTX()dlNXr%'N$cv9 Y&z.P\\7ĤS8}Aywh۶-oM61id^}UO@% vyYz5,ddefرY;+JAA^_~C#dҥvEu?wq;w?`0fhVYÆ p(+ Mx饗)!ᕙʫtΛGBB;w:Ͱy9k6G⧟V0Yt)'O2x`5k(*, -m+)L:g>ͣ=Ɓ8i~2GO|Q;YZnҥ?pCyͷе ݻwsu~|9u|<~ҬY *TD wt4 qRrls`ԬQ52زeUT%660H_~na}~G|B"nw ܹsh߮=6m CtЁ< P,ǑG4ȬZo+UJE8ٶNFt4Mf(ǼAҶO,_v#%+khop}mJ})* Z9tU4M4M/'>Bɣ:F@m6!aG ~^3&>}p*V@llZϠi }E~duϵvԩS7ߌiҶncW_&>9sCaǎ#}.իW˦ r7w|.rڴnڹlOpz:+Wf;voûヌr2>N:ruƍq8tԉUĘ\riG v&7rGѭ{w5oN3`ŊXf W^y%d(?HQQA-H֭ѳn7^端ȑ}>6l؀(\{ zz,#F ++\~,]֭'11M~atp<wIrr RR%rssR իWGׂ{ߨ#˲ž={7o>,X_~޽{Xx1j+سgod /ЩcG֮]Ǜo 7RvmڵkWViѢ)1c ̺u [+T4-PdeQB4iBS_}=={0h wƲe˙5{ab |>>Ӧm$I *pÍ7ӱcg\.' 7/Yf[o"sbÆ ޽{HےFaQ!}]MՁy!]WYey|w*WP^n>FPEPE}0")楈aHLA~~16Ν;aϙ:j*&N̰or9r0x`j֨f#*޾};(Pv}`02 @U[RNmbbuM+sj LÏ˘?>M6%**xϧJ/pE) 2tvUW5$T(e]jSz7ܐʸWjI3MYqG), DQ L ?o\GNTT AaAa$}pcFL" /}A$Ͽ`54jLZp8(Ϋ#_²av#\\G;V@0ʑ *M%*+"ҴIrssT"}zSGr#*0h(FMNZx4$*2L4ǏQNר~Q&6dY쌸vխKQdYtII4kք?ڵjKÆ 9$VH$aEEEz-m&bN'f8YVӮ+2oz֭<,vyG=f4114lD^~>ժV݌ij,^yѴI*WLLL,^,L;'"##-ӠAF蚦aO}0 $QĦId#] II@HDDI_|΂ [iٲEȶիWԬQ\NE!>>ECUp9ø^@FUj~B^}u#t GK/qF<;KZV (< :,s v j:\sR]FITRGyѣFqUٳ5k\ϴR%'YYT -8))hQN'lrH.(b8N'W!Wvm2|{$Ya̘Q{^;= gIۺ,@ĕ]m-!Ko6'!>#n3TUv|4$yd8 ؼy{eú4{һg4!DF}%mPQzAaa!wk@ִPҧpg,wu,odEf-|f_eHY` וW^7ĸ^nJbB"c*?>A&a Ӆ8QqՉVhku=(*8 K@ $$~z"=<|L8LsIII#--=ݦkz|M[빂iiq 'pfwi5b@(6ko0- ϭn$;Çqcrđ 2>}{sg`xӵcƌbĈ*,&!!3a{ ryYx<;l9y@|lLϞ=Yj%,Z4L\x<èq_z[o{^>a<I'裏榛oEE/_pmT}O%99 M7@ޭ)IXɓ&b:GLύ UUƽ3at"h|Y6mnH$i?0 "0Jk3/W^ᦛp.>C!J6EnF <Ŵ2b돒S~<]1M ]0ڵ/Mo5E:u=}ZeSp]1 Q-C1tYqq-7̳Ϣ* %%YEZZ2:N~ @ݺIA{1|>?}2P>!ӽ~Uu{qmFE^/VFUUr]x(j{}>9 Ƞ;vlܻp8 eU|>zmO"! UW]+a¤$FڵkinP[[m\rfdjjyŗx?`P<ig*Ν;ص ]rג_J~~/W_}5.q|g\xx}~^xEyQ^ pt`wAbƌùt-$Iddd=ÆSTTLFz:yyTWU֭{,"11HB8i z*c;~83N?[62 uYgɍ7HO,222?RU$TE2n\=z>wo=4MG BM4l繶5#6m?Ұ,40pAeYuU)2.wcZcM]cRJh HD#:-MPTAbimv-,Gp\ y;\g_o}[DDEtmCH(6;Ya^x%owlO *AFEIV,r K;[2.bv(\xytdKZ%qNN"2[e(}g:#+*!+j\˭k+{־FӴ%^X2M CIHԇp|CRT7Z4q)4E]چa`v_uy1t-^o-ښ*>>/D&2b{̶bt,ӈ/h5}[վ$ֆM#(\s5~?ڕahfi_[c;L+gc:c&?g:o_w,=e(ڡε/e<\r){!\8ՎѡobUpuvT&zIJ4fk[1t{D=j K@bK',P\ ZTCQE,S@%H=_zn# ~̈u:a`~v|tы^};LXDQˆ9e̚r 0~Z\jN!f\iwf' t԰ξ:0q }ԃ&]z#j:[gf|".ܟ}hw}~hٱX:@u<{׭eE#(SO>MjJ2}uCv~rY8` ;s>bc[:Si3*uev׉@(ꦾ| {'碸8u~T|ۗsw֏lL=8иb+)n\ Z?hl;EEE{sb">DA[oR~2˯333{c\s52ȑQaFBu晴.6m2M9N'A$UUest#?" =%PU .δgp= r3}_jz qS`F]u?mxi?zbu?1zw)ońg\on+ mL -iT7h۱G3l؂N98888888|l J调Jva9BAg&@b|CloB~ 8>AgY.o޺ALr?cdYěookh H zȒ?AÚ5kyAPbgNG7- 6;vHeYv'쟽$Iq#"ƎKiYV۹ "^˲eߏ7rK~TU=$EQPlhyoʲ,no@sVI(/oVOt;@FiQMŗ^+PdGnutWE)f䙦A^$$nLnݾ!nzO,ZYպaC{{I~={p)'Ν+Hx4]g!vtnwa=%3=B0 a:@">JJzy_BqwfUl˯FSsS&Oq!Ke;pX\mmoKm[Qؾ *hft0'MA@0;x|gv# nCviq<}" 10h߯ ,=- k=*q IDAT劄CaC֭۸?ifDA@eƎRnFKK ,z&6oJyy--AjkkinnFDѣڌv%X(B4}nF(BQd'g!>3], wg/y׈F.{SУ.TTTPq 2k׮g477J~x?OPUU0tdgѽGH'>NY lJyW(-+1k[Zb唔l$7/?C=-[ԝ, nx eN /m{H";v4}{#ᰎ,޵(uaJKغh aXgx)DAT~Rzf4,*ʚؼy/Y~U-Qjlڴ&QEF 4l#A(ĦM{ {HVo[jj*r*&O#LeUs/O=|3es/P˄$W??IUeAdرc0M={09446ե g<*D$'dǎANN.]r9888Q].׃z܉|l矜p4(/݆ǭ0x 淿ͼ3_G:{yl|O>I]]qcs9blm7 Juani$+Ud.AB#3xڻ  % ŋVsS>DkjxUouuu̘q Gb_RQ AYx1P˧.غu;Hd E套_/K.4yp|VaiP(LKK ijKS}/ňHsK ;wArJ -MͶgs 46Y*fC};JK>|( 4CuGTddg4)..#+.]^%]77.Yxn)ހp73ib>l㨣 8vZw֮fRTjtM3x @Y1 F(V~aVf5L)&⥕H4b0bd6W^5/Yt-:k>&sKnnl?_:!C2C<;j;_<- D4 4?G1EQ]w#W:/SO9 &T_OiY6mw,Zr"n=2XsQa%Ib[-[k֬O\rۜ;8888|*bZA$I4 Z8 PYU[(ٓ.]21M͛PPO۝_ 6|I^^.II!r$BlhFb>yeN8wSGt4&װzu("%͸s=&P4}}3# wrɀDDޝ^LKƪU4$\?}rԑ]70,<)XEN- !uhFADdYFL]u `X&(~HDIKY",Bg6r 4]Wq1)|ZX~a|Xթa&{3OG駝L~|p!!ccCծrӨ۽ۃkMd˖TUUsί0 dڵz<=`yY"mcV2t`DQBUU|vI GHcvQG ϩ3d׮0@ d -ŢŻ8br>>*A?[hKzUL%+|]hH$ `n)M19t/45iϯvKhaM3#YeFuKwqʏ\ETm'ҌeZXi'X&ir{Bp=.0}B/<$7x^x~ʘ#xYo!J0p{Eشy3<\nE,IzFZ$bge+e:OƲ+0?'x<^U+W\NE9cƩ\M9mRRSHMKŲ,e5߭ͳs ID}^e'?XQRvYŗ 8AHKM%%9_xm[jj닢ʫ̝1ilݲ5!b_rc9"4$%EsSKJٸU+7]3Zg~\ m&eA0eK=if( JJe;gfL咸9~Zw*v5woG/GYV'#+.]r钕C\s钝Kfv.]+ !1 AW+~p]0}iӛc=~} N8 ǔ90nrr 6s9gȨsof@~qÈD"qSP˅( Rba9,&M\!#xɧӷ;Hk o}1]r f 6oN(m "2_B*O< C f=8LB(KHKM"?_veKzwgCr00 t'ӻ/_v.?dd$Ida|e }zХӴ4(cvK?b4N>pXQU1 xmG+lcOu1qBQI^{} ݺ%",rI`_͂?c^d,RDAx=.v]wN˲ DQ:eYn[Cv$ x*>'#'"S0-\.V(YAZF٧DRTnFX tCǴLY%׭"h˅(A}S n=C;( ih B7 E^}U>p.W* D"H(@RQiln&QU[JKU}.k>_~> ˴hl  /.Yƌ0J$ď%C:nEBQ쐲 nX44kX&$\iZ#&,ds4ڳQ'9AFM,@%֙k $$ :kp(`,j3ZE}nX$%(4t˲hjl/?˕h8epvïe+tʡO(J ϼY: H xIJݣf;$I$?~ oI0fnwZlC.v nLIRśMDQ$-9{qppppp0Mۍl2ARb"G~~ѨeYxn{ \ܽamihFٟ1i|>oܢ(/nE>GBri$xx'Lғ]{&3G'v$;!-Y0LEBڮ!IӴxn2 b( !veYAU]4XƁo9f7A9#Xlyy 6=z;r7uv-ǮǫS%رcbeh1I&|+sHc^ 7+w_ ֝5,ƛɵZuD%ygе(rCBm.a#2SEqh44b}wCrP:5,n ze#t;. qx]g3"DA%S09~eYx<<^ג`uppppppp _`KJqh&|I._iYe :8888888|c0 aH88888888|#`Wyg OǠsp8D$ EQ^%I!'8?!E˄c}PA~ڵ%Krs޽k\ߴJĮ]ٹb @(/$:|} Mw_"xk ٸ:.^Z~qގ}7Q]4-O QiBSSK hF0$ ;O8Iϧ㢤nA*^Vy<(){q]k|7dYfC&q$(d?D" H[ELӤ%$;i- uzi׾f~c DQBzlx @niH$Bsss9h4H$B(4ϫz||?;tާݏ( D":nRYš5U(jG[h47kBѨ3E?# B:>sٱ_;̦ GScz*yyyH ]own,SGeLCC1&eiQdYAw|(ʖ[5TUU#ŽƌC1Mu446ѣIMM0 DQYo裏&~Z4 ={;wp{0jH~UU[k.VX=\4s&ii3}vµ^ˤ sgyF>?]ӻÇrQSSҥعs'gРݞ]a"x/u,ZTر47GYd7Æz_?w+VUe@@8ũHmKShDG@xiY1)$$SDD 9'K/yfXv.Qcx|'tZ@T2o\noMMQc.bkVkD FΥLkū}u|;t `ԨB!{{^mDnZnXꕄ( q)@CC YY>^o}nҳgf8QF^SG755!?8&M梋friQ3NǞ[no~dd /aƍI'"&CacǦM9bc9|Tuu <W_} )HE.n6^x9>Cf~DILLٳ3}[tN8D돡Gxɓz!>6l(矣gw‘(㟄#! bI;|+}yyyl()!2|Hiryc.\Ȅ ݧϿӦGB_/ >_456bsΥ%~w cƌ#Fzyؾcyy| Ѵ(fbk1 .]pW\q|"+v3%o̜yv;K[ذܼ|^}Un >ٳ>fZНm[7tR>(\.]nߙ[v*~ {h"Ǝ;@ _ny 5G}@$ছon?$ p".h&6mf5JKWӭ["W\>ͼ[tNIhp n h4Чo&6';QH%Â̬YXKfĈN^-kO>p_'pōS %x׿ŭ#U< IW]ys/55%KIKM+.'// `:3gd|4o%6pgqSxtƯD?` ]xGBe7eHsS3?IReYxO>kd_=^mFŮ]p \~eڽw~uֳ@BUUc{y9ׯ/W7ĤDN9T>p.e(§'l_P]U[P5ޱz***سgqǝ_:JJJHKKښ=\{, ,O>Au.Z(\r 6̡ .8}w^Uayoꫯ&+;Y --A/YBKޭwWV^ŋ14ƍKK%wf(2iSO=Muu-5uK5뮻#G?ߎ c;Lv,z*555?xB/Jf͚Ÿ'%%yz8ē0($&&RY]E(|gÇK,,ˢ 7gEBBƎ0 _ѵ{c;Wss* {EcS#f )+/'-- ?-xEQ$wؾ}Ç22lDff*TpQGo?׏Cc|`Ϟzx`#g񛫆)D#Qjj__oU¡hUѬ^U޽QBl(.aU{;w;ӎ-dƌ>eϲz|j5w40qBWҼ躣o,3/O߾Ά ,ZCEOre <#lwgBfFݺwg(+iǒo@z_~y~C !#8 &izB (+/u߀ǥxbn7׬4 {ywvq' ˯p*d$+,xsW 231b uB4E, 05`ſ./_FyYW^>[5[P~Mt`lڴ Qڵ`_se?lXBAAW0 0<1Rbb"U쭫czz>4և @0eӦ:<;. ],VZTGQ5o,v+އ$эjp1E` ҅PH'+q$~szk:MILLkó=OFF4MGV;c%5W_MFFF|:;j]e躾 `u?, 0vbJg-1J1b ;˹p{<(,<~CK;eC2yd23?/daC`g3Mó:޽ݻ|Zѳ'>Mvvib&]==ݵlٲ5kVp=ADD,ˌW?^rBcu]'5-fdĈ44s˭ꠞV-8M3NVxҖCL07x9Y]b#" wi'ts>i~*A7x7|/Q#;ds\PЕ(P0BjZ* u@0،,K$$$hyxo^{ŋr,Y;uErrR| X8xXs|>/E,#=Ad9dY~uJQt`g>l(T|>oh4Lbb"o6 _`nrHoeW W0ȊdkY[5YDQ]>K2QdEТZ<&"풩lN:P8LCcC̕FQ49x<,X ^` 0M۪v\dffp">[J*+1M/֭A$Qеz7457n:Æ /@OQQ/SO?] )OK/˗sdӲ,>?]25ߏ]w'rE46ֳqc cFfȐA|)466!vq[4; a a.44.O?ƥ<&N$ 4Yzxnϟ(I$%%1va7aÆ Xh C(bΜwwBߓ1i:<,LNNN6[6o&;;//,L?4?s٥0fh,2h2|PDii)O?4t=&{ӦMc֬Y{ďL&̘qoz?UuѣGwSW_#99.]:tSAV^M4eu|嗴4?/@mM "kQ.B_$O:\ s塇 ##'/֯cҥ5 UUIgYwy7pFz_HKKǟ`t-Ʊ]4773` DV駟P5uVZ5\lڼÇswS\ṮNErss2ᤓNW_۷'NL|2{/qܴikc("CtM0*̝nIdg(.NisZt|iw޹ PcӋe(.NcĮ>F`,vh6Ȍ,{xN=AZFtKB"˲ DQ:'zǘIuM-~@NkNw:ݻ(lw"L0MvWWں:mہg|>4]Qm۩?/}g7m!7' J֭۩#)11)FL"5%YCOHond[ȌɎ(- uvۀGzZ-TRXXHss# $&p&:$QVD~e$TVVmvTUWa!@P(DYNz(4x{˘TCzZ*5u}z;܇(JlذB(v[L$(/+4ihldm}>.U%+T*)++0 zFRRR<Է}G9{rQ\TKlܸ25 h'0t]gGi 躎i}TrI(ʞJnFRR" { ]D2]fggZ]KK -`\A'epR^$:dP/ E~}q*JJ#)1zXj%k֬ klٺP(HnHMMFm(2z8d"Pa主 uddxeG J]AJ1 UINvѣ-?b(77&)B)74-e"hEV{4%Pqh$s?)S0vYt1}ONJ r˘5=[t{ A801`-6 F7軆u'^s?%+jL4W?@l{Ad>cW_!Cε_v&,>$DNe9&mv2VIUl-Z4>`ݶڄ۷>11`x _#ofI-8ybYVepqU}\׭$IHr|-7'q@ /\ƽ˖avZܹ')Ģ17n]Cڵq=,Xݻ.y":uȐ/BUU|)<(VY,Ks#dUcӦ}]wɚk$ C^=z?"%I"X]#nyf?jy-KK#e9z PK.gvꄪl޼9sk ,4U!-AQTb8K8j}\Eelۦ<㸤4{@𿁦i3]&M̍ï/bqQRRʌ2ص̌ |>( +Vۥhܸ}<qڵ oMxڵŗ^"Iܹ_jZ<ȸo VPXؔY=hٲ9kh'_xd%1k~ΪK$b!Ʉ@Q)9u!-EESe%`;.*,%Ui" @Q4x~r%dg{_#OkƐpĦ"b2iU9L!LuC ]k$_GS-d^0 {Ջ~(aXD#._ /̊+kk%33ŋЩOh~h$%Cs K@ -<">zE-x'ؿw,HIYԩ׈̌8([fMٵ˶wMbqIֵ+'%)*-E1 !l޼ 6/gGWѽڷN@n&06-E >ʣO|CC+d"ޛ47ǮbsKhɍۢ2<  lZ-hWX̦Ctꔇ(~[0~/yUb<;ltfͺol7V1.<^ߎ7/v2vb|hS'_ĶrF=tϽL_3~#I$Y./3:t vm ))"1z Ksg#*׬^gp4l؈oN$I̞=W_~+yT'1D(\n= ߠ>pP8DZZ*^\<"zan݆(D"Qo/VnјAjJ 0mEn5+/aFv̡2dE4ٷ?a86V/cbqmt>3ϷhD5fMs#a,v/S߄߯{w]aLzBƎ_֭e(J(C$b]xIii[x|>{ee12-EuV=VQ#atN4U%݀ko_qӍtUtO$jj-t'Ҵ,TU9JlWSyGl?e׮bRRSغuX^gdЋ 0`DcQJJO),lJ h VZI'q\,)ndq&MifV\g~+}۶YaP^bA<҃mY5[7j H 8NRI8H8vL$$$_@ff&M6AUS"<ˆ6cu K*1˝zK;l.#F>6gUUǥ6#vNo$IDc?l C i޺fLι(lI hƲm-'uٳ.}z7@SemzUxݍVS,&l޼CuǃmD",ǟ_d;x̝%> #f hj|Ee0$+,t@ .=аaC6_/%@x<|^X{wb!rjץYg V8+WӬYi)Ba0MHJxtqXx gYz1:DBg䢪*E;vu65+$++qWrz.>>]ˏe;X0Dc6tOΰ1%qWyL|Ʉ y <7j)ڍ) .ѨBN3ͦM~й.h˶bIeK2py7lҺUKf͞CڵHO>z>}>Yݺ",t@ ./W]ɼ/IdgPVVSѮm[ ^@QU6IZzmZ=g.%5mzs6^lܴH,d^uaFn@VZԯWW_CFؼy+]v7ao0 V-[x멠]ӡCm^{mO<~InnU2y-BrYtFm,N\hS<8hI 6m3"ѪU60lfõ]$Ţs\RcCu󩀃@ZNx9>,ɧQ ꧐Ӹq:]q.?nMh8W;*ۅ,{^%TUM KyyԔhEGy<GII)뒑NZـ8عX -5@Lޞ4y_ Yd'd(x7퐟GjJ*VґR 4%%۷t1PC0{A%j"N&9f䞻Gд4q]={+˭Mff&eŇK 5-`T+V\}ϗZ\X,F^^.)))ضA^^Q IDATະ@/neXf %?/ Ӵ)/7zU|>Y={¤{д=$ vQ^ ri:bee1DGdeyQ5GAQe _CUz4va6u/we. _{>}߽enבVWl#7MkUem4p]zXljժ$m;qT\EiA$IqǗB4%G|,¶-\LPXX$eB a&YYԮU Ƕl;)Ru]lƵԪC~}W.a$CqXhi7lYK~^.y}Me6l~kضFpWe['qx9ǣPX.q:Iʪ. ,˭Q.4laؤ烮+0 UUhP?Y]ף& y\at$I²dzFKй|+caY5 .q0kBtp:urs/vc@ 8j֓*  (B4MG&IRI">ѯJz,߸kSi60JO5[4A#M9ᘥJEiצڷ# G NJ0 a$I  @pK*½ @ 8\Zt jȲ,B @ $p\:y9WйD @p M'['&#@ -NE&'@  ?I9{46\Eӽ~̨}{Pt<Dzm6mBǒtT =J8YͲ&m#I֗iƀp`e:mѥkwpׯU5 Yնq\$@r`&ֱ[Am@Inm@QuX #mqD^{JiZҵ[hYQؘն Ǯmbk e(lڔYfq%C(+/GWӽyj<^~en,wCaaSyaNʄ ~posFqyQPЄ)S'33=z I> {jܦ(R˴ c[ɵV5]Imߝ*m'. G\.owu#i۲k69H$Eq~ӍtK.]; t]m6,[ M)))_z >s12-z>,ZlU\>>tƌ_@9ex<N?tˣL{w1Ç߀ B-_uXtѝm۶3b*uu/8\}U$z^$mTy1+ԫW!&%%+WpPK,iaSCػws%+++s=&eV‘z=+kzۿ0#쏠i23ߐ$[̬ۑp IQ̘91PCΣ}uG2ezn+3_d]Ԯ8J<^/V` C3+%t'˷KF\8B  ychtɷ,Y={Wa( &+i&x@ @0X,˶ |4^=;w*j"SgϞlڸ?>Ό3HIMhGC.؜lb|ч/o~Q# jk.\ ֭[GE0*Ρ۷-[`w2K{ڃ7V2q.o3 IOj:oPZg^}FZ֭eIYD1qH-[رs'Ǝ!==E,"(Zl?;&LIJL?~QUTT0.ɂ )*γ=Ǣo躇H$Bqq1hgy2,EU?mj&)مڲu3{vn.ć;?|}!r᠋p_z/eмy R #vEE*+㳙!Ieeelذ\tL>}$ ӈ֛5k6xl6nH,5@ 6,z}q x{Yh1\s 4[osg۷]8U +bl$̢EVL{8ӕѹs r((fZ>̻>}2rBJK# ۻ1ڴ)bhÖ-A a6o_޽! q@&2qw%!~܋y .8m۸iYI:`;7z>#G>KAA@6ѠA $9)lTU%1 vj{re'VhFYY֭u[̬'c˖-Фqc<wq>.r pmĹS<(-]JFF>7Хs} LTZn|=J۷K1M#9!I2X,{,+(BE( q?EE*c| U Welf"V\att&%X^0cy1Ng^*d[((_lGBEdee%ӭ{7)~?v}wlN|磪*YYY:TiDt/#55WNrUoEe{1 T=2FB۲lE5TU&bqܣ C==l7$TU"Xazi$-i.m$5.qOHp)Pa[wcw%,]4.۶p۶ͤ8öm[i֬s٥CsLjzWAz8id`BNoߞeK1{\ܕqZ8SGtL>&JKKQT^zۅ2 ~sT^~jOڱ`U6ӧϠG\{ ؖy8OYUaWEbϨ_~zآ+-e%l/NflՆ `oao:M (_-;)b+ٺMktUW]IvN}i8x*O_ByF~DZ m#"X^N8bw^r3S;v4rŕWSOr5гg/&N@^n.~:L0km۶;oӸq#s8N,^ |@ƍ(*AzbBIŲ,ƭm.S;С-ҥ{r€My̞yv det85eeuc=x< sSrp p VD5Fܹ.2p`3vWyS mg~](##ﯢӓ ^!7v2 UUȯSSN=tݏ'??p▫*o6kdgsz8p ~26yٷokM&M`%šMzz:&fĸ 6m*-[@T2˘ p]v9*  IAK+pM(npT.B dxYr%{.#3oҤKH%Mٓ! iiIa 7رc l/0:tLN[=U ,EEɩ5^G}l4]r}^}v/&;^y4/8{E411M'ُ>@ VT#y |O?-^$IȲ,I1uJ\%5#F_CyleiGT5]w.F ` p 6fuGZlm۶E\3>{ލƣ(2ry):v<g j'G`͚D^^I8`<)^dYJG[]fC˰˚S7qf0,KtG>5><7QǣJ-t$a67oMҥ`IbB4 Puzԝ`LwcZi1c* txd̶]]A`Y.`RRESdQ `x ]],F%Dzb.eb]WJuOAq Vyiܨ>0{}vY{:d0|LO-xQN>e%X(fڴ=fh -74*TEAet]笳)JȠMA-W+*<={,s޹kbQy=&7gўnf9+q<9!d9s]@clڰt3ϱvzZm1+zt23tx<>z ;'jה`>W uM$ ȯJcta.Gy(h99>֮+eM|y)!cw˽=oeC {dF Иhbܝ0͵̚Um]wu$2Ӓ$G& h'> 'w'j$=ڵkʫE%{k|J-^z)'D<<ޚ_L ʏNzw2eGS3v,PfǮ|\5|1g&C.L,:@ 8a"M 5kP=)B!/YB(&1; /_ΓOep]KZJ*NFhݺO' ӴLvqlpط/L?e0kΝA,IJ!CZr5*R 쨾p-xw|6 ؘ>]lQ $s2Lp!t"a!'Xo,Ȋ0\vΡnF|>*$.;ΤJf׮]L6eeeȲ̗_cŃA֎DUX*GdK,e۶ m۶qFC+hidC/LΝ0MC<@s>1<ģQS]SmIѲEsNsҶM `Y6i,RQQ'ߟ|Y8.Y^5"I UP IȊIh((@Z*rNz̘{šCѨŎAdYUiYBUe~H"#3>?/{U+XIѽ+Vr`)~%Lۇp({Oߦ|]&UM7hGzۀX̤y MSqgtQ8亮[o|X.,*u6F%CЯ@lh4F -H qf bPr _ķɲ7ŃplEFw { KSQVE|ͿpǶȚ?.(HDD4i辔jvhmpǃ@%R.@K e* DCnGF8O6> .Thҟ]Uet/s ̘B4ja)>!vP o@#$5-$M,ʉmr\N :?Oֶl\ץ"dk/ҧo_viݮ#[,+Fo>.(a1z,4m+Vr"pa`F~o}*V>McLxe<y0M _ 1WSo JyĵbDQ14*@p.n)IibYU k/!@]: IV@ Hg'/ :uPU't@ s5p-E>t.+U~@  u-af@ m@p#I*e'RkDM@[lx;ֶ6T5}4F%IG~ʹMaٲ=̟#o=aNQR5˥*im@ (J#(*S̩Zeɲ\m,(rwI\A̩RmQUIK2n teT5^n@Fv0gv>PH, xodڵ;cƽ޿1bRQ5fd |;ӈ"M,_׈E4 I_m8N|zIiI+Kض71klKZz:_>͛ئ+\Z\zPZnmٔks.ڶi˯ˌә8q"@ P]:wamۢN?<ʕy 9993YU&MZ kի!(,!+2aW9NÈHR|OYGt=.,5MFelqeb̙=m۶WKB$&L@8#<6nXGqq1_~1UV{|()aX[‘HwcG_~1˗b;v2m@ b,DYf-]y衇ٻo5 {xxt:v 7Do5kѡCG6o̴iyl^Ā8޴l M~U'HbU̟g_#77q*G(̞%=z4x*O?sAeI,K,_Vǭov" %dFYb, z/lJJ ~iuƢoxG4h0=zr`)nZh(iӆ:u0l߾ѻO?VX;L" r! L~u]@p8Gs#++@JcӬYskmKF7o;n6`t2;IOOFZZ>0/{0L֭[O>}0`0]\̵]K4##= 2h od &mڴqؖ%2@ 8p]MӨWn/ry[Vҕe˖ѯ_?,3FfV6))JJJŴjZc(ah̞{عcYmB+YvKӷO#4]ٔغ,_X|4 4M:0酔Mp\ %qܷKxq1˖O#5}!lABbʽbp], +ˇe z"_boNjZwO?/;vh+^_ ii\|EԭאC)*A:uԩ3qBE;vpٽ98xG8xSqP$^xիp 33(Ag`6$.u~ڮK,KNh^8TTTK/`B4h@ 8$cIUNɁ0ׇeW=PTeh㺸Hxy:.b6s]w݃$I:m!2OWt@"tKA=&pâHʨe5xpZjl't/oAA֭[GVm(-=8kkp23s*uGGU2rΩM^~>嘦9YD<`/{g%U}kի^jY`b'` 10a9' $3d!aN9s2a`&`ccc[d[eIekZKWUWwKl!uWzg{Xۖ{~] h˺{='C*e[t2 @68! pɍI]V`l֭tv5oC %JR)3 a|M\u\}U\wݵr˭#Tfq:;;ض6?_?0yLS3gwأo^̼siokO`Fs>.*||z|,YJB8n|SϿ e͚5 E.]BKK3W^q%/s >t zy5|_Oo\OoX,-5/Wt/^׿9L@)53%J@@~?q#Nkk_P~g.icǎa^ziT\{mxgyݿC)ك٫tE,G/__*J)Xx1{졻˗al,]ݻwxb }ᦛnbtt}ttv|CW>_tvvtF9O]Pd-lظ}EP{~QM9 ksa?`Μ|gVG'_W_g>LfE[]b1n=mo%s˄ipUs?^?|IH=;]rBYuv76qۉ',okx=W\MJ.p~+X K/h9_O\ JCo^p_޽4+.1DRJ ] 8Ӵp6BJ J4e3ض!~p!!| )!AR(,d"ɵR(+XM:4Ty~^*Es6iDaBqH!TjTUZ(*A*tٗTV"Z !@ |E".&P(Y WJ+! !JA6kR,*2R\>|&cbxLyX8uUb~pHunZX dKXlŔm $Oskun7VLI% n0SLLSJaY&MY@oBJEu\PbNhsR&O$9ĢıMZ[PR$.D b}.Dk^^V9" 2,晴XqܚA"aL ;~ӄq)fƏ xO)K .__^kv{ْt8OPh4גEg}5Ts«tOO:=R-'|;Nה: sa(0O]ضuFuUFh4WJb;%{L>cuZi4FѼBaH[ J)UPIT"eQA"Ʋ,l}Fh4qƵ8jW&e_EŗDCH);4 Fh4zR8BDHC :kuZA7n%kHCWh4F9[Q'À+ G Nk3]>-4Fh^ a(yb:?"_(`&ꢕ|DSsu Fh4+qlz{{d2{j,f 3iA7U5Fќʔ4DTCzyyrlo_yJPvlTѝ3 粏*Qǫ% @eYyi4^Ŕ =1}@p}0MA)K)mLu!0I-TQ:Z%z[nZ2o>?#x5ƹ D)T* tN0T* 188H.' 3|ӧl޼#GN"h43[|CrrFtkAVcpp|C Rr8#@]XT*P1>ccZ#E6n:J-|«lb3[26'wYlda_4ApeK }z{I9p {ytΜIϼ9dx 6eT.3֖f?h.OsS -q1/]  86t׷\rhkuw{~\y,[}3o DZ 7oa3]뮹Ez˱M~m/[ߌ0 |gvw+IgR o3gbܹ80cӦc^w7xr¶緱v-oΎNy ˶x7Ԕ%܌WO| D66}|䖷(y_gYz5k-۶HX LP*}ǤS)}K2&??ȥXB,fi1aWR |.9sy,B::+.FC{v[R85{ CE*ʕdhk>/a[\i{|r"-eb%ݽ itUL<`يhmo?1Rjd0\ba`>ÐjF cDY]`zG<#!(0MC2DJx^hi.iQ(1-R86eQy h"GTZk:s a[wD!aJ0M jaG-cPT*L#@<kmnVq^YOu*LDśeYT*UlBbzPֈ'H).X1n}b8F H)sRQ}k~Uˎ\Xt/P $jBCaFd+1CH RP#qQJZ-@)Ll۬'e(,Ӡꅄu-Lݳm9EI}<ϛHf7ia֓50LB4ܿF`\(&+ #zOYcy'?V)g7h?a4** Dh[ٸƆ0zQo1~9(R oq9Iı+Ưqh4DS^ϛj|?1%h=*G򙘢TR52W's4DbDjLΤ-4b5U5{'2Q ʄ8耓ytgrPPps ƕP0Fh4ׂm<,#0J%\m4bL"Cż'RS YQF\ %%i"OTQ̙!#D:h 5 IDATUќj,;NFќ|cb0 6ԭ eH6ӆiZ'DJJ 11{"E'0s#CsFdIDiH%d\xٔ:з= @Q49MJ(È\3iA\ue^th4gam3:xi;r]s!"#X8ejUfX?-$^-A }ēid4L˦\#¶/\ʐ񪕆7 b1j4͙O\İmJJ:bxdl3#̜1C$S۠1̝W.yGќiP- zUqA^=Sa'VM"JSyYΰlɉev@۬nfDRTԧL@I2 bFs~"C)BF*0TD*CӲ*ㆬcGjUzW&Hd;n8Rqx^= aWFʪlC & h43aR&L(47e?LGlTsQ͙ÁiՁi:,ӗRќ)BV,tyĆ{_x/0_99 :a;VOLzݵ.TQ Е59̫VxjD[kW'P;g3s -^{0$]Z4 d(1uza.$ɲHg)s矠S :JӳB|vb'X 9!TJ'u F9yUFp% XRT* }9D&&/PUAJ5B-4Y`D"MD ]=Dhnts B :=a8KZ*<3щOEY ch46a.6jO\9A+<7h4ta̋w4ËwyV\יvgBj}7ǬWJpx,!0 אmmòzcu㸮 JL&b˲bb˲b.xt6MV#/cmGcmؤO>_ubض5e?Ű-!lq\qkhf}1LU>6~b#}~LTFww7yxq/-/k9Fh4/iз?g9/N)\ˈc߰D:iRnN>qG3{6N>zE< x[f2|peb7t=s ׳cNꏚx{T*&FGNSOm<{ۻ+Vu($a* CC,^G?Afq}'>~;o[x(_ WFϼy onR߸&n&6=~nFbYvdḪ脄!3g!(! app۱ioo}[yy+h4\TX23m*I$ͬXGسga2t"!H`Y6L˲,OoC ^|.O:͒ Ӡ zزY~𣟰'ٷrQımlʾwx-T*I'}x*:;?+xRXָqrB0~@,_lfgٵ{7?ظ6> )C0d[hjr%.b1nzw ˴h,%N`2kQX,W_V0=sT $ciR(q;h45m[8xr-ulܸ7}i^C.\6l}L#Cttbժtwu!Aݜjk6/_ʪU+ioo˖,ga&6:.eN^TFhin&a&|BH,dPTIRd\ HAe?CJE6&`3 rBn,e[IZxIB6 C׍-`K|47e5 `tlJB6!7hccxO6&LFŀeL$suj^1RS)D"722Jss n= qS_'$2*,r` -Ci6ډO~= D߅)y\LEۉ)90q+FJiQq4- 4 8X51˜u.BP6RLyDKD,^f5860WFhoRHIjZK8RT3Hu~W[O:m- 6n7V_Xɓ̿XWiVFIH^4d]y(R՜"tM+Gr/i'6.Ά'}+bɥ564yZk ku Va6T5-lhz4TMrF|KTVc'$iH8ĪG4Y"R9bވb6f hbʜۚR%MܐDT3ԕ~pγbXa!s>!h@Y߫(]tDqUNT]!^b! FlS#ʄq(~O&`ޱS'JǴ!-DG A#Tu=ʵ8Go}AH9<5a\PANt+ މ%3nF/ds7q2>[LMFF T[oS#> I9s4Ok W*K8҂BXp jQJa6jQ8 MHz3PbW{EW,5TUX 0~FϞ=͛yy\9CZړwecMOF)X 4,>\F8k+ǞNPD̛fq8jšST*R~EӠG|{t+WنWsӧq2PW(w*8҄0 bީ_ݺ^{FtipزNa1u8.ADhwS⼵YN@ً 6bzұ妋M 48TEkX|Op4rwVgTo⸢ 1J ޻@ӉZص1= R˦YM.[IT!7A_p[Dgc8حE~;cgC+ǤwvA_Yǎ))0V nu4rmGZ+&%YvM9C4Wk9gWFdPU{Q^:Tq(XI疋ɒ8獻>‡t!@UU6?IÁ(brZ߫޿IǸ_)^+HͿXq؋vx,Gqؙ?+om+rLr3xs^7ko`Xsϑ$IU_,^>}z3Wxw9^o!|9]w?,+=]ٲe/Ι˛o~mǎ{oc64J ZD5M-[K~Ĩ1Wr/]̈Qc%iޖSR9)q_z>T%UWc?pwyzK$Sl pPIZI{K &=%0Hdߡ3Yh{ǧH:=>G~(["h{|4&/X5 ^PFtK#1G($s2ҷ@~TPZ38X{_EVns_mr9맆1gRT2H$ı)ds!!wyZt97\F+nj˯fe\}Xv͘Lj/(ȱUV':,]Qc:g]eS-RXMCQ(B7zΌ"ċoltkDg6~ $Fu#1n_׹;w&cH۽- !4@GxK@YGޚw;^7niEx=3 vn!r F`FjBk*+DNKyZ}-ʋ~}f-u(.)ͼ*@VBYMSF4 c8OGRRRwctD9+=97g}ퟚvN:e p[WxtzS:^SSղ+QcE\s_mPB7v>[Xȣ{׺g!ƐOKfb$ }̙>?wжXz>@t 19Qu/&zt0 ٯCV$~6_Żz{Hێ(59Vʌ爮sUӮ #^0{4O_ݞsU ;VQW$aDGq&)tbH.e$9%0ף49̠S ${bbv;vo3$0 F Dvm#лO %x>]v!c[ɷ:ȳ5_Ձnuou^YZU9;ϳUjH; .SPv7B3jMMnH>3>/{dYb,Zӧ^ο U $ot}r|K^{J^)=rˎuQ/6LNmd: (=/cH uv6sKKIjW9;!cp>,͗%Lsw4H߹qwh1 `2XPxd#Yig4|:Y&#Fx3疓yMewK*]HG(k\mE+ W,䑭+ܷDySNg r-?Y]O|[EI\v#[3{r+{[f0T%FE.{;R._.+?2vHƎ*}3ǹm7l` \a>6WWQ)?VUWuW(pX@p~F5@ UB 5J~oTh# drZ0.nj$F/@ ۆjl ѹn @`rZgqL~ 9w[ZRQ[*k@ V!N@ rY-Uf\<,)"]^Q/2m)J6!T +Z*r=mش/u.礕UԖ/ğXh={ѻEpXCXYڟ?VT2t@ %|Pνps/ '%V+zmҭM+-<Ns QNs 6*eؕ{v!A ϴ Bti۩m)/\(^}}}\Fԕ/ˇ=9j6o[Ȑcta^s=wcYstݏ̌lV'Mw|4SSv;BҳO+ĉ{C\xQ_:w;'N"==ԶO;2.ћ'={NzE)ٛr[.f{~[41-Yf=,_zIӛro?qCϗWuO}Xѹ8zF(νps/{œ))ga\wY)`OG_K_xn:q& |iugL{_DF3O=ΒKӗ,;'vU?.v9$8J[d$8cb݆>p=//YvN>6noW6oʙ1)bc 0s޳ׯ}*OF)_8¹νp"ʂ,W*jO0|U܍vqHO u}7Ms84kY&gzi4m^qVNemt\xQ?uFמ}?< pk[&MHKOsC<7k6:K@m󦜰А ?ı>M{nX8νps/{;RF^]%S2ZEFDpiZ4o'(kd[*lWe폎LlqN]~qOB~~=?<ٞNJOOGf_aۼ)qag^Gaaf,񦍂Vs>8nI/u)ǹ,ǒ6r8˹,K;lݶ_=(,,[xjsYνܬ|e{ySNs1U>*Nȱ//:2r3}?qx_O3ں#2{ˤ΋/̨\G 祗_%##tfyZ /%)9f}ՕVdb21 O(vuEXjvmu-{wnm+qJu4Թ/^ֹt羼z?z8;y:4M?<Ĺ/^ֹ4HK;׹Nz:V˖Qܗ|w!>(4gWRܕ;g~׽tSC|hc< Q 9!#ͣ=Jpِz:ژ'K-{͍ 68W套_[6a"=]m=wcsQ\wi3hߥkNւ6܇e>Ϛ1_Vg߁u}vKkxzlX ^8¹0$p3y{Й57Zg-*1F zwf:|(<(L+Wks/[Qr_Ѓ};gUUzw؂{Çγfj|:g=䜒a٘> w~fv_Ǐqp[eoy N9CLL4w1^zzWOmkA>0/`İ!<4F+lk7Tv K*leڌ111Lk͚JO(((ACh8ǎ " ^ ~ǕMM>u)@PԭڢX8@ 8ƶ\tpSV):jb Acy{xnKQ+Ư&NbNFhGFYV_f-0Lr&q6ÆU˖59kod sxr*W\:F[~oj'ZgN"ε?PeʚP,K\2p,nΥ6@P8޽g/>6ԔTz=ۯx{*w>.7_}Ĥ{kc/al7+~Z9h Z&8ns"ÇՉ#7/GOY΄ZBnnh4`40  :t:`+vl6bڱZmlvm^á`4 q,\ִsoa,{ z rrHMMb 礧}/]Ǐ@Zj ѝlտϫ\=F6ܩ .)I1aƎaCi߶RXX`rJ4TUEq8p8 %5pAaA!1Mhյ#?5֌呓_}Fyyyh*pA5+r,{ z&$IFU$u`BلVd˒dzf8YN㚫"44\Pe?rAgƍ/-L^^)))L|x 8^=>j hKk(JZr w`UװlBF Zk'i]|'H+2q5}8ɂn5#cw9s&>{t ᭷B\?ѵo>̙_ަG1Lu悶ZDb/c?.U3 IDAT5"s/{@pű?䨆p 2lFQ6nƋsyDǒA^^>ǎal{ gΜСC?~k;(t܅NR8|BBCCF`0HIIaՄѣw~eC_Q'f,I=-: 3NF >!֝'͂yj6e||<8o3Jӈx>Czg]v{pٴԙ :;;Djz#WyFs/{܋bGq,K!kR%DTq8S%,< lٲ5G&((Bf[#Fg4i@XX{`0мY uEUv}m`/a / /b˪|5v( 7xiZuG4IB@EBQA@!6.¼\u,Zaaaj 4k֔`f3Q_u(ƭGp8;Rd֩ jŨ}ࢗh3! dkM9Ս ^8¹Jd~ ,U U-8Ձpj%l{"8 ..NG6HLLbbb0M"K2G!#3Ï"T //ٌȲNtL :t$ر#}ucUUi~pNݺIA4$U#)#63_}mj`dhl6 LPPA ƍ0] +V PP/#Ț=F-z J K>Krk 99νps/Đv Hb[BJ=?!&}glX(qM: *^[Ll{ xfBZYYD!9I]z離/X6 %##fq: DVTPJĨ@h\ 4; dVUu,;?KR-? ъҝ F{]"8zmd$? V_Fs/{7L^P"5@RjQ#|%|CB#П5pm(a:/fI[UA423QTɈá@^X Wز559<^trOP:)*rdZZ+̡ud!C/OB|Y4;Ԫ0)zm]G/ k<| р8?6q5J%ȯFei}EbTЊsISl٨E`B=h+MЊ2!8Z 4$)@9.GB'L۹nX8¹}sJwn}]jAA\"iڤ 'O$??GѼE3:wβei߾=Ggn9~XQPκtlAGWCl@!9T.QEK='Cz^`¡Bng$'*̥טQ'5Kyg||CF@QS I2 8M:V+:EKOB-HF:smI8F -3.+K#.070 L5{_D8¹νpkP֤`@DlFvm#,))b]Ql\rsrYr΀ Rh#v@@`?&$}c -/Nv:K($0<d 3)~~=ۺ͏kVoF@)>OUrdn""͆d#98vDied`'::F_뉈]6}] &2 FĦA^5'jl??~ RStFM֗~6 8RaoM9HmhQO՟}ups/ Eů{c\TMK=E4%bY+.h4"3% iSiidT_|)ee#$C $|9=dlhBQӎCš2ҵM8l- ,Y1H0o1vJgpv+wdG^%ĈӡtneH2 4M&r5))nc-rSMxc0d!5WU ^8¹ν1.EV+AI99X,⟑MJJHppOXS2X%!I_IFw}i0Vۑ?Nvp=X@jn -Aڳf2" 0I糝$0w LPQLADfc1}2edO̳iq򦛸sGX,bZ}:> %VatLLlL644/u .ikmS6))6ކܔ#Fs/{ ^p~YVVV˸l6Cj7Se~3""3gLvY,(R<yiZ X&r\]QvDMڋpXTToB24Mcph]{@WtFd]sYg@uzdMa͕ 4&ĺt99|YNJ.|K 5dǍ]Ill,e~}.7#s/CJ &6͚j2zߨB60* ֚r9νps/{AV3Z!F&.:hi*0tԁ>_ .."kn?qK.R2͛$dr: 1̝S8QִsBNBپ53)^4{!jZl_fwG8kS> ee]IM_RHW,J$IF:J \\DXXOkccbmű ܙh$7/hZlIDD$,0LGc[ YJ,uN˃+J"5W5r,{ ^8-K\ZYn|S,؂83RT,ivUjc((*TOFF$I"22e+Xr11tlߞܜSݲLiSi9ٴmՊ>u#!!{_̀W. mlV$h'pl-{A58Ma$ L||<7M[Xs-} lcT {ly,ah;]H|| h!{@ hx_Rq,}pA㢜JH^X 4AM omHIȱ@ X ȌH%iPr424 641'v;Ct B- q\l;B24B¸d>`08q0mXKZj NNGTL, UCB7ǽ9""yF@>divDc8uX@n&5!D jfzwLҤY 1 Qܠ @\c VO?W#׬wO3]:(qicUp˄FbJ IBȲ; OcͪLFַc%4k\gA40oYTԤ3 e! &X ^9K^}M4iJLL,+/hT`}(J!=OW-W׎{lBzW-X_~Y QY*@w{^7:+A1^caV,AZ!@ 4!Aeێ_H5}WߏV̲_pơE!~A@ $nbs=疾~bb0tPuG쩓BYDu(}U\k}q˶op uʨW{^W'|aO:t'tk.|;we1y ?2•2(}u.ӟ]ϯG.heh[zDgy t*mfpD&7Of@|~IMw/eoa&MkЛĶɥccd%QϺE~;m/Cybu?iqL#VU(.iV#ܓ&M"00'No>>̧~J9uNO>7t@ 8yy o7_} :)XwMĝwg˟k:Zj nl'WwMd5|OٱOFΓJ|,6K/Z#m*2-5Nz$঻&_Oi*g^\;9iە?~^wG=͕#0y"%'2=_To)~>I9^\^5}y_)7pӭ,Ymg}%zt½q9^FvҥKy7 ""y'SO=EXXaaaL6{z^AeRA8|0cǎ%88̈#HIIj_EQxꩧ!00qƑJ/,,ob/)IHLLh4ҽ{wmV&be„ _y)?O˗ӥKF#|^׳~(ϳq=Jhh('N:bX;Zb /#%@ٜqsC@@モvy.Dpp0O}u6 Yl;n'??Y3g۵,ӧ<=k}vL&=&c'=#4D7]۝g.tR9EL{Wg-#5/O>kF_ @Ϣ/5AWx7غu7xNxM8ITT?$%%ѴiS Wڵk6mۜkz^ש Kep <:u UU<ɓ[MرcGuxH'8r9vOR^zh"RSSyݦr* >ƺ=zXa88;E^=]{pӭӳgwo='@=xѩ 2F?Au &z9W i }.j~^S=?_A?×Ѿu4GKޠiB ":*ϣ )(U$a4v«וnNu7xksJJ <Ǻ=55G}oY*+{`Aj%b﫸[=_:YtD֭wb"ƌ1`2yk8t3aԐlY(cǘ8}<>rb3c=}6?-O1΋]if\8coϹ @AC21kJNxM85!+zZ/O>DEE{pB_|k֬!!!֯__|uzM3{u XOd)g{zL2_N'd 4@nfjWY ytܙ}b0N'f͚w|WtEme K"eOA@A~* nQeˆKKKWrmHڴI4MG$˭]~; M4AΝW۷/ {{{̞=y׳3gUVj߿+V uEݺu˗:!!! {7Z[?pz@ҴiӐ8̘1kTj)00Gvv6Sxm㉪Ep,,, " B!`ATS0QE2e LRD?û&Sqû& tLU/T.&s186^¿r [Nf\;^bN{UncG]XUUMӅK");_t69_P0'ó7^,)q:`aaڧzB{wӡ}xWDk-,7mM[0(y Hk饮HL0Ľ0ع%Um_Qd߱ wW ~/YDDȜv`pLDU'%!.ETnDTbDDjP\1J˱Q54CǦR#J`_ ͳb@LDDe5DH ֭heJDCZj2GFptuTZr*"Sp%,f1{" E ,rU0>DTNOs/vT*ꅴGѽ*y'""1v#K ] r ?[&QUi\agg#"">qBZݣ(rh'JVٵ[{kghױ3joADD*33q^kx '+/8..yƮGkwpSkjkHNI_'z}'ϜiS&pqM?ĝw6˗!u!^#kKݺ~hնmDT&7Fm01y|7ny ޝ8[>܆{ApSn?[$~{ap34kSg!%5_""]c:?Y=8T+·1+l:=ƽMַE Xp^ kk$&$"5%>>5j2C~  5p\y/7n&W&q'Ȩ(\. ~;SREGOwf%\|+[OƄ擗mZ믾Dnn.~9rwE/!)) ֬WӧHl;/3?b$ȲO@Wz!d)wb6lڲ5j`тxIӳq|Wiqe?иQCL0PF sfNW`yUOhh3 8=Vxpqqwæ͸r5ÇEKpY~Q C 8|p?06έ^e7hWI{嘈><*a1c""""j[Yr8\vU} Z"Bd1&3DDZ,؎̊)Jcp,&d^ND@dǒX JEFFApu@ Gg :MP(`B#^'O(geea[c[A26^mc'[ѺM[\|E9>''cƎSvmӶ6o {Gԭ~[, Vlww/wK]u7ϯ]0vѺ))Cj SRRW ֱ,jem6oAP+ """h~I"+0u &Oш#,lNӮZ.\Ĺ"6>lmm1o%K"--wn… qi鵍6I8o߾jOJL7qrm鲾%=-,^)ɉ81s^YR9BdH-^>v*bcdRƅqM$'`n  vk]6l(\\e]ϝ;sENr 2-ڒRMɑ. ;PA#8pBh8u4?4nL25MiQR2>//aaO/olA\"J KsUx3xguwt.6V>}zAl[~ԯ_V-[#&> 0q AAA{Qk-=r=4r;:uܹ%m6?]ַe׮S3gNG>}>VX;u*kWy~zr޽^Ed]~-n^L&\.G!ﯶ~eYW+k[܏@͚5hUc?7Yū (BPy(ݵg-yQ;az:D2j5w<ܸq;>Q1歱8r13x lڸf-i<׶-rss{ˑVNӯ__lXؼe+֮]nݺba?:ge=-Z/`ǎao˗cݺٳ[dFAHH0Vz1L=2%yiG'Qg}W~p|Y̛?.]FfffJ$eg_r\}#Us@Ɠ4XXXr9k(6^U,WX}={Nذ~-zzj+M5m2ִ߳fymާO?:ǟ~d]5Mm}xR+[phX[R)9%|\]܊ +d;.Qw7v]= իWl^&Dc4.ᯍ#*r")1m漽yO3xnNZgvk6^^^R,ۮm~YVZA,6mڀ'dSm###Q4><==uI'b֭-[bi=vHCv_IˍG``_T Ϝ9_z.>SaE,}e=Jq{ !!Am\PPx!^RSCEvvllacc(wӎ7O@DDr91C FجHJJFRR2fV^xmf!6IHHLČavm+>rnܸ<Bn}N}ڰ!aƌ0$$&"!1ӧ!KO'&&aY>l޵K?G۶*t]{PZeі:7Ƈ$/22`I#!‹>T_cv g7t ڷoZ4oN/tRk_yַO5tRӡC{4o7+/^׶O4ӧ*|]{гZeӖ:7fユdPըcx?N̙aJLYI /3^cSq-o(x"sT}zrAggg 7PӦOܹJ? llDGGӧOŴi3J,} ӶZk!##Cr0g<|EÔɓ siCMakˇ#9V@1 3g!511k[Ddlll鮝H{h̘>M- tk>wGz{woc»jF۰Lm=Ιcii"C=R`kk?FrRc ʑ>U9" Wk?ƍCS.㉈z9 :&K`Zɫ@&NĤ)+э@fKT^DDD YQu ?B`eJ@xp\Y fQqRŕ/^ w@fz2w""DDWkбc߅A2UDXDFEaY >7`J| 5Nط߅6MZMoV8}oЪm8yQp(|9d IDATjTwtMX<λ 7޾8ysԦ߹k7+WCmW(Xdj7/ddd[HUNVV6d2쇈z&xА0wqQn~=wAy82Ғt{W"==7]¿g3Wtﶍ.̛qȏ8+OgOz%$$$bيUqKGqz?\/bbbj)/x>zbiW-Bkmŋp㈺w 666Xh)TMʒjK"KK0g ؼC^""R M쪼N:Xd!~xӬZ:oaӸc˴ǏOvƞOw4NmA\|<燏~`r^z%<<5W~r֭]_6 kSvv2e".䳽cú-]j(8K_P߾_.$%ڒ7q~<+%""@jw?:9t}'zЖm-mAuڥ-ZYl. pi$}`Z'''^BI`h=n ޾$=(cbߏk$ 1 o۪dkﶵlXnd2`+feG܌mnAJDT ~B^Zhڼ ,#̽[Umfr~HM}\l_->Vڒ `qpvqoب׺geea[cӯ[ĖZ|### y3zQtm-]6vZGdJP2Qud*RVi؄U…8Y܇-_Ps.0`vڥ6|]6l(\\ duuY,Y$ܾ}'kݗ,Ytܹ} .ǩSTF 61Q~}*Ν;sENS~ڄDDT*L~ٽ{6m___ԨQ+V,ǷI'㏷C.r9>d'Llec'6lX8ukWXvu xsg ˖-űcǺko!UѠ1QuTw0ÇiL*, 4@p8|[ <Fvx* k~U2љ3g1o|\tZ[5kHU40>ʸ[j%5Q]rsJزu+`֭>mZ- QQQʿU2?OOODGG+~hk#񈊼HJW>1ȬdDDg \$2nX?LjG] = >ڶ  msN}v5l $$$&bƌ07t`͚d$%%cf2Qvvllacc(wBմDDDDT28Ɨ;(`VL^'gW[&Ogb[MuP+Zd1\\Q^}h^6^,Y ԩ[ѶmXZZ]v;o̰pvqC}vzm7QNc""*fjsRqRb̒.[лO?ܾuAUFf'n^ aWAP( /^Q ػk;&Z2c0v4r(+ҏ<)Ib|3g!511kӛBDDDd,  @pH7/^B!"""22M8&NdA`1Uin10&" Ub""b-R(q^S"b`LDD&-DDDDd$ fc"""""DDDDD) p""""2[:nUzDDDDTAWiehиirDDDDT*1DDDDd[q8a;ᓝ"I DpY|>4knh|g\v]m;wFPg\E[tDT4$FDDT%a9O#1:x#GO?|Я/&NV} DO~VUadv """2؈-/?d 4Pin??_b„w6Svߴa-||jSLec"bTZpLDD"[MB"}vwqqDd1b""jk׮+=Ն;UZLYYʠ;%%Gc""I1ʪa MB[4+ >3;99Ν~~~IlݲEmJ~=شi|}}QF Xm966xIII#"""2A$P|oggq\./q"I3 ~-bXl9aZٓGED( ߋP*[\'$Joooן{ZLO*2/U8|k_At4c""""UCWP(@"ÅbVat2nX?LjG4mӦMa&<}>Ą˼G7Q!Wy%TDAtR̞ٳ^f8Yϼ8ն!CcȐ<ʈ [Ȋ] vكC+}Mi]cǎ%@gc?#""-.̘1;8aÆJ/SZlUDb' B +ޱeCk{ DDUP&]OCC<0VI2;+dv) &JY8DDU:WNBGʾzDDDDD1(E@DU#""Dd-Xb_6Kc`LDD&GR%8yR'2˖.F\\<~;r/uƢsV_F\LTӸAaMϞ]->DTnp&4?6k6c""ҪvWnwKEAꋈlllsy|񇰱X""*O"Nfpvuye _DT\٧(ADD+A+he8fQy)r>/ ,DDՖ D*'Fal^W1Q  TPd~dp ѭJϟ2 > ?wQhmAV DGı_0pDDTm<}HKӧ>ϱmfz͋]G.F.#"" /vfprTZr+ HKIDčh߹1i5青u6^w^Dv]A, DDUAݛl],@*34h'e^**c2*gMy4u10&"DDDXb '9='lm!~KWLDC*c""2Fe"J^餥&p2]Khԑ0obܼuc0K., v1ZJ*rDGEO`mmcv1'"*I`XXXYQ;W~ IqLRVdkLĝe8-  U( /,X@F$R)j}c?cȈ7ͦ+1烻ᕾ7$ܻq 2K qDDH!gɵ+E>a*1:FOꘉR9w k 1Y{"2,]I8{^H Dܾm:DAk(]F ""n]CPÐwHJQhѢ2SjB=݆t?dr,Z8]ܤǸ:brOD,`G7;8"3=դ7R" ܾrҲ=23u\DU;jѢy~xjf0sҩ}^vG62brO*Ty~Y7 IsT*)>Ʀ\ܸ<C|89 ztฒNvzqqqvUȜc=޿y^7TZI*#0;W+wHly~-Z 5)\) !x2N>بBfۘ09YhЬ5dU,8kɉZ*o -/-͛7(rځK6k kW"ܓ{b Y]QƬɉ~ B[WưJslie-!e/rR+X7L nt kWZ۽sO*dn )ekrqB/C" /3$ ƾISKY2&.-Z4Grڙn~+~7; RJWƏ_U* jE b ^X~u!;ΕKu~xde{_kl9}P5˽'Nڵjbbqzqݻ:k%LcDx}},33gΜ' BW]øSѦu+3 􅥥-$)b<@rR ӱh\r-Z5L2 dė>Ǡ4N~x o %*X~1grODQH+Bo1P(("99 D╫XJܷ))! 9QѨQB֭ѪU(voo텏w+HO@bb~pBK|iY.D)3uf;v?\\]aee;p]D޿ kkkr1Us4 IDAT @.W@*"((u~t3?(R`L</WO5W(Cnn2[!ytӶ\".\y=x#GsR.Add'|sAܣr.Yr'޽H?•q?:J+{]KDT*ƀ vvȓb )$ѭk,# WѺEK4 A$&'!)9.ΰƭX^mr \ԩ ܹ3h֬ml`EBT)Aqz%1g@*q\\ ?k&999{""EOƓo~lPV-ɓt [7J|r )$x1]aceHOO/ԓ!i&Ҟ'{Jn+*@H">DNvCBPn=ǯ_ ggӊ]*/6fr䞈WlËRݺu )<} ",4k 2Z5M׬i="7/nBnO2뱣puvFfSx w#GbIK$H[S,-@"+%A H`gcU QeFL㊮\ jwiM<° c@ h;pU_YC!-5m۴3Cիpv?\ XZ 66PllΏ3'":󉢈Z~~E$GU ,$^˿/@.uWۿr @AP>t} kjcGݮã$c&L Py^. 9cb}C`PՐM=0Ep2-WU./kחqQ4mW\PQ#_aieu>noa $%: E={"h5]  VA"}K@~~?d?!w$Pt\媴lX٠/.}xhQgjHu"br䞈Pb.I!˃p 3}iP[ IbucX dRX[[nP-= . xu\ *"IDTI3'jz~/|UPJ$SjĜS 'BA "rs S R8;C">.PR`XrRcr䞈<̘L￿2b}. ˶C!wAm!wd-$6~\i*akk:{q^fq Ri~.(S\dg@. 2;^*90fr䞈̸̇2^5RQ3O#%*@~`ܬ?Xg͟[d,YF KCMQ<ɿO`͊2Qa;>`7K!R D1˓+'C"y ѸI#څG,QeLT#2}*Tggg8;;@m \;\B @H˼Bk~Sg~ėArJ AQh< /Err2[ֲ`dsmY"LpVDj@{(WݨmϷo۷ܹ3:ẉ br䞈̣Ef!F-3[.OsLb!<"""""DTm~"""D|瘈.`110&""* ӥt K.YS݂bFDDTqI:"Kcb&DD櫴F 3'"2oeaeelL#B#o".qùGThzw`eCD䞈L%0V|Wl|LL,VZ?.^7Ý;wamcЦMFՅGQ5! qI-4\Ehh~#z&!J%#XaV-_cL:yM@[[[|S4] qFBJJ* ߏ#+;mZĻ-@.؇зwO@^^>ٵ>ءƌ~%./O.wg`gk>*K/[@m\^=pC<{m/,HMMe!58;wbз5;? {؇nj|uD`U_+žHNI "6o_~u9ӱcۖin+W! ܸy ӧL*6.''?+6 Bd$}qqRAkɽ羟9˖#"*PG~c2Pcǘop={N9ɓhwxQprtƼ&N}ӌ~\pmޮXE:7?7Gh!2qUؼ C,1g 99YcrіHLL—_(uyq5A&!zO{?ܘSQldqv6^p~"MII)1 -=]9>51<=4+-- ^^ʿ<=^?N4muiִI×{?E`Gy8[r(ЯO/pDFB]+D,;99!>!5'$Q"ٹ4 ZWQkgkk~}zoH#,+EEu(-W;7J$U>ԙ/>"v\'5 ڭ >xP(p?:7nVNdקHNNAfS|gr\k^#-=;?݃ڕNw>WNk2{FF&<|*}#>BǏžhР>4"#e٭BSkG8|pUB5פ0/Orl'͚=ݡa@>/5޿Xu:|psq -\~}zMV<l#o%;`YwGT zW{CL4h e^O""S=i$.]si p+FQPqZ qn%n(K+W]*V(HZ9$iNmn@f$˄A (C"+!(腉/RO!CFJٻ+U}ҹQ*! I* * !FML!dq=2%ȝ YwW޽ {vYloں,]f !dq=e8˿C>!$8]& ʙsXpBRI'* 8-E>/,QBHYY}*o7̽=!&,":м 7EDg2:qRN !X^[_]ry$! -YY}*7j7XTАED5 !d$,/}Zk,(vѣ~l|j@$Eh/%ظB+iX8 &HHi/hxD%Jm v25JOf; ǠԨqҟl+!dԉ1co* )\tZ 1:N(p'<["!8+^m]JqjPnn&? AY)E6cX҇6c=)+ƔbRK c3w2%WPv7nߑ־GqTRZ5@N\U묄RRfwB,N$O(x<]5A1tg]J{15)Ίq]b/BK! ,Dݽl^u^2OOUb9n o,r^.!3F'V(~D'ԫ_qᄸ/2 FtH1Ƒb #1Ӊ1,eH<|X$:Y@.}&R-1 R#psv!3Bb8=3Oe?3Ŕ^ qdG8!Ɔ: OX,kgPL6+Qqݷ+fua"2pGO!i@x+QRMV6Ss?SC~ (Į6 $g? IENDB`sqlkit-0.9.5/doc/img/year.png0000644000175000017500000004456011714210425015367 0ustar sandrosandroPNG  IHDR+@MHsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|l6ɦB'@{kC!xmkA]l!!NfDzn%ޯfʙ3朙٣ƕgfh !w٠ԞwֳO*j,Մ\BfYTVoiZG0:\yf6KE5c.C5JBQB(=ͼ:Zz_XFMQ>j B!뵥\YDxl2w EPeRQU+B C!o2edޤn؞ͫ_~ugTz`U&sEcEMa!!TJTI:YU V9hyT+\TI7,DkK}u۟^SAᛦX-f5&-7bFTG@pԗUPU+fcە ]p_qɈdtuj+E@GGWs02ߐܛg=݆ZU5{y㺏zo.K:UUX,֜"z4o\OyqWg`UU:5V e&E5S. kGbJr^ CP_ 7-%q keK`x;^8tnh~$((Tmh*l&yj1;?j p2T%h@ӰZ-X%ECTsp2z:Ogۮ%L"x@LՕtAbL̽meC[dB]"@MEt#:}*I!{r}c{xk-oZ 㶶>7~w,`4z.Vg82~,|s//]+wM8* CG0FֿpIOc:QWc7,;)^e®rshF|!j+?~>P^۟FsgtZvv.ә%C x~;u!vN.ZQ=t9NIqZlZ$E/Aq.SϾ7Ƞa;MUGiH^qY.ATO#alm|{GCe)I!w3JGbD}@z欌fӋr>^=3Uu<55F_TCtVS)NsZFUTWUy ]*2'=paw'#s g;R`oDFES\DfhJ( YNߝNo`T%%Nb7>1Uoge 3GQ_^EQ@%-?0<>ezrr{6;=zJKI|BG$Ի=u+"WYvYUDgplBqEe*k6|Vclq<)扂5sn!:Cqi- 2Bm߫I cU 9Ͽb%en@Q,VJ0Ӟv@tB:˥7po("(2X̤v JnkJ^GjӀjMG3FQR\bCxo}_ Ͽ59nfצ3IMJ(uӸlc-ˮ3e1;S&z-\gAq< qn% GjV\L. hn^tDFyn~|oZcKv:{U[[킋Deĥ_|Bp7=_ & q݅g[0WFscp[O׾n 1]0پh5W<= 67ΩݹsoynY6l,pLց>6~-G,L䷬9|v:EA]rj0Y4b=9l oHԹuh*+7#Ԍɢ=>1tѻ4w e?>'*@B :r<ۻ\#ŕ <$~C):N4[pz扮陉=ۅ]מS=bYذW,{]9+>]rE-`o=wƦZE (@~Y)ME;E9o8G T4405= m*:^ۚF &XiBl[| SESlh4۹x==(ԭ'vZmc9o{SYcNHn zമ!D)VQU@`~3Bxd2u>nn8+۹C.S7-D%O7yGyJ+,*zq٠b?jB!z_4iO;WxtvL2YL,B4m! >Iݚ}Np2vQB.וW%?6C7gj !mčg^ ((o !meM;"BqчE'Bq[r{#B#(B (BBqRJIm[Z[KBJ]jh4RYYI ;hd2` ""Ȉp0 ]]>05JIM:5uO$ MӨha!Gseer\VKhHqMJ@vУ; ;OHHc$ɇGLMӨ`=l]c}@ot*-#9!Az>AGID@RL,H~M*֥߰{Oh#'%57~ѭgL&<8g fC= YoLz=gԸ|O<\{0}pQXT|O=iCGһ@nn*++=m4^y EZz 5>6uSRÏ5n"=gڌsؾccoݢi=w{+!cj6 2o;5}Xc2q}HK`Ѽ$\v3֏C%_ӱtQcc1L ޷]޹l2aEߟiw.~|~\fskhM6ػ{iO=#o;>絜>6l\>77nXkdޭk/۶m%n򺯫׬囥K8ZXo6c}rj>C6[S&3V=;j{*6o{+!cpыb闟?rȉ/xiم(*._~`W 6eaگ7մMӨ$;{/_ٲȐPtAZVTUUtni gUWUEC;tJMx1ѣ;aaaҍ:ƪ?NiCG%kWˮ`=3bxnZLDrr_6;Xqy7-L;VѵK1ˮdo4x׺)ilX(>g fώ-y_y[y2r=G-k>6j,ݗ)g6.9ؚ4M͛1 dг'ZP",PQPT6]UmiKe%J`  ̈́;E8)w4Ν:9~ܩ#EE^yE<,|"#L0moO<Ŷ;ˮ^]~~~N2Sǎ. \ oXك`0`Xߗ([ 9ӣG ܩ#4}XXX{*H {Y ]߾Xt㭧((v"ӎ8CQdZS[7 "d$OQqqqu}`.^?7^} MXosВ;y7f4TVV1k |$% tO!!mxdžԯk}UFBB<m!6}׽!b+_է9Gȑ|ˏ1>yt^XTY9b+0ޓE?PTl BGOgo* hrC(hVejf3ֲ2t$쎈 =AAA-3w?eq-|]  j29Z-CuB\<>M< #uf]9|/MX,Vf08LÌswݢ-BL{f4۷KT'ݥ]Īӹ :D7]Oǐĩu(NUm/UZJpV| hV2h>ie>V]P*~}l,' y/[n@'ppπf2w2 !m0-@4JJJ ?zU}՚S NKsBPP:v;N=8~TULb],4jEGD|tۮfw>|YVZMUU{ev"k᪭wkmJ-@UUJKKRZ}9 _Jv툎`0x 8szӜX,EDeeX"JKQU?(}99˹[X0o.al߾xM*94 UTz[Q;>bEtt4 $%%Dbb" $$$O\\cbbvMTTDEE9ᄅnV FB++zњGm⋈&8(xc5dզ>6׀)iG?^}3eY]~9&(syqOgϞL~{d9OZ[cwc7vp-IhhF _3P u;; H MCwZ,h=hKXYkxu,j)z8,keuq٩=O˜$990|}s~$"##x`a#IJJ$$$믽mO|ysi.W^Aee%2mm ;v(V^KRU0v@>3oO}v 1un-rlF t| KkkgEGCk4dUw3kc4v@ۘhM5Ю:x;#c,k|b/*]x7n_|vꬱ6vpƬߐ[[BEb:qF|}-:b@P0BBB\-ka,^jWAWIHg]cpbڒ|թ%x;tԉ}CHƢVGtliiHCzF璒RmuD((DEFRxڇhR+*Ъ1LTWWSUUEuua4jjj0L.\aXjAՊ:e aa.C7ٻ8jOSܤt%p9'L)!::J(kW>||Yx)F#i{su8iȠ4f VZ6;vhYl3}T{_uf w׵U?<<{AeXo V+Xb3V+ѣEE.]zϭEA u!!"#1őӥ ?tBi׷ HACG%kWˮ`[cݎwh7lX>z1]tq1qʙ5n"ݻpig9ѐe|S2RRذfc=Hz`m>n>z/~n)]א"Z|i`o)ҡ};B9оvdӶm ;LBbL&fut:@t(P*9oIIX!w/:wHTTAAAt: 6:+,,SD֟Y2kS ؁kk:z]D h0Jdz";g?'!z\QIlu555U` Xp0B 䇅-:H҉.]h߮QQ !r5igފhe8 Y?c6f56|a] !E rߒNRRi{1v(N1.G|t9c OL1<6qw&;d̨QERR'wYә ),*b]38e $௭5pcHnum` z}8DӾ]{jL5k0MMf]Al&8( zkT}!֮BњJ޽iG|jc# b<.+]B!N*2O`k!F'/?MYI![£b 5x]FlܴhMs! (:-t:EyB!N V`1|/Bj}JJ صq͉oBiڼce%mJ 5jBawf L?tY;*ɾB!Dsg`3h^Zo0C!hz' j!w1B!:zV+O< I~λt^="-=!Go~̨q٧?f'<ȵ7LwW\}EE.G?^}3eY]~9&(s ڟƛoֳi&Mz`33٘L&!pY.yضm;_/u 88e]b~~9XVi*N2Yv̻YO֬ݺ1?]?شn5gϘΕ\/fLz?xLlڴ|=N{ꙅOC^a~9!K=b+(-X,ڽ3ϚOPT\LaQs?ިm`&(8zhN%5L¢" xtc̘>B!h@r .*.{]w18sL Ҩm?1/x3K`РFO}y랻"!>qOgINJQB*/! _|{S^.B!ZӞ44Pm[hj9$BtvjVMUmU2BB2ZX֖)EUhr 46dl67{%NE}(?BSB2zB_ٜd$-D!^q_T=B&ϼٷ5ϾfBKeyioCnB!@B! D!@B! D!@<kڵ̵9kS׵bcԩK+DkNrz-yf7smzNu] \BCC\!Z[Cܹy999tܼy '|\g=o1~Dtؙ{KILJ&]{^zeǼ2:wJPpQ=k91!1q'KxwitԅFт#\u|\{YYYѽ^3Lڵ?G<̑ÇxwCb&ЫW/6mHޡdd +rCII `zl8L6nݺlQ&M>QFg7kVbm}O>/ w ]{cYYYF>#y-[orϽOEQ0q˾ϞO?\z Cq)3kO=z6LdggӧO{e,e9WdP uocҥ_1iDL5՘j꫼|j1SM55*vعSffu(//g{ҋ/mzs/˙0AQǯ.{W_{1>;oW^`m/RKZZ.e˖棏;K/Ĭ/K/n"22N:ҋ/8_}UoIdd$W\q9}LK͛oN׮] ^&OįEϿ9gϤ[n>s 2إ:v҅7F gٲi߾Ϻ71Xv'&}|jAvuNVVݺu믾dРLz}yڵۺ _xi7v,[laŊTTTW_3`@Ə.Hӏ8p 'H!P[f||\vڰa#%;?[22ħ}믿фٺIrm˖L8ǴM61p _5^{:$%蒍7vZn&BC0L>В/䚫3l{Ϩ(>N:mۗ-z3qD~w:{뵟׬aHζlJDD8?09gsۭ:Wݛ[Lz{zն'+NaWcWNs>ܽ{7Ǐs[o!{ukk,Y%UUUq; {cTU_/d2wɮ]OgԨ\p_^uocr[m[9vkֳ_V!Dm V^M7DPPcmK/ƍة3)zҋ/s]w8yw;ݛSNg` RSS''+Wy 5W3t}NM'&6oaÆ9ZYYYtE57o׬=:xkߑ~l߾_~dXc;uh߁okK_o%)=3> &8-zn!W_}-1q4VtG+yRz{2i$]O?k]|SO9s!>!/3`0xl;ɓ&/rm6uocpbxy{u򵎻ô_XAiaݮH]]V:RScbov1]O!^''' k!5{jEU?s+:ۦ,~Un:ep-wml2JJJ[$ E*}9-zիVvu'1gorss6l(gMZ!ZKhXiii| k!N2~b֞]Q!Jf!|BBH !BH !BH !BH !BH !BH !BH !BH !B"cw (&]B!e*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*B4) D! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B!зvBBT@CEM=W543 D!'}Tx੎4MjMWL2B!@B! D!@B!F!I&=/>yuBJ@@#Oz||2B!@B! D!@B!F!I.{&̩3=SWPr(-?;LKؕYr$BE{DȠ1MTV׻ D!'=]@!!#CǝFEEe4S݄B w`±c%LJ|cfB,4MEU5tJyyqBJUb֩(+u D!'㦰gzJ PUݢQRX1neT!I%*&=q0k';:BxD4{\XY:B!DKKlG\b&-SLB$B 億(ύ77bB!N-} ײR]mdqM5@,+)"{VHdl{>%z:Fn=VS!'Y[gOޮHQ^\H|^=IId*uo4j,!'MSf?t:Rewv2ny*£bTۯ@U%┦yȀ74q9һnZ┳;+L*ʋ\SkҜ۹(uFS||TtcZٽyE7h5uiZ׌4TڨB*4?[yt~4l2c*5Bh)I q4L `׀b{s7e**#33#z.ĩ.SҢݾ^7h;S{ZJz]Ns竩U┓iVk qTKL|FND)npjn~b`P} gMq9O]WHn<р.vR1MZ% (B 6y\}CGhށF=T{0^gtgЈ5TT5-]=zѱ{*Fcy2!qu{ڰB*4?[)QSSMoԼbXd e^\e*NAGd5s!N@,(+&,2)@ڇ][-mQ ~4T9(8!֦eGٻcf6QQ#!!FX\7S5eB!ZǷKW}[䋽G nd)92:E)IDATH!yk/8y^ۊB!ĩ}B!)LQ!@Q!$B@Q!]B!}Ojo}II5f<={iPBF~# ŧlKW.S!h#~[οRzJ0S^O^iѥ÷TVV]q!h#ΥS.J :u‘ùTTTyBw$Zz$N7ܫB)EQ!-m=BѦ~"F.oBmC8r0 Wf$(mI!eeE(;Fjj fO CBfD1h(:@lBB%m!k4l!h*-$5Fܘ/m̛+2mqf o1C6Q enC\Ν+Iݗ}+ٱc2:c.2N+ޤPBѦ4oU ˠNXSvY4,0i/'gL5+Yf%SLK.%'gӲ'_~=w~An&׆sBц4bU9͠P RlaY `9uk.b㙅xn3os]繅2zfgI !D[ w1JCw1DԀc^PmW4AzP -~YWg9d.ӗ-Gݷ"_f߆@BS= ~,VjT PzVT,53VL fFLMb9~+F]%ՙGII˴[n^Xd 6FQ!ڐj-b')h5(*(j`x ZP53b{aBSp< ]5:u"A]G ", j}m Bq a~ Ţmj P*k152jc8}ffߺ ŶRZb@bňP_5/*R!$bQ1xуAԃ!@Dlhр J!`@[YC[*Ȳ۝sdf33L7K"ُIOj+iϼڴe.[JdDV^LJˢ9}f""^bFŞn>J$ƀ17bstћJM?;l+U߈___| &8ȶ˝âE 'Z7ɲOf7K("! ,ؕ..D5rg0-nI(IHפx{U5Ʀަ̽o6oj͝3/vnIe>(ED$;yH `2zsV~opCga#!{cأ8kV ;mfMNj[N@("!!H3}:ȁ?By3BkD.3Tf G7 ΩZfg jm=y"+%d"F1hiCWE3,'F5(TLEy9i5s7-q"53+8* DﺓFLɶl ~l ,|>(3FKU$PRRԊ9)ED<`n'Z\Lea4"b6X&w-2>g P(6pNi0u]…9osP xHr `6EEEcx@۱I؎͙J.cg~u(-+U_YFFi锌T(ED<xitn?Zi u >""Q9 9|Βp.iܰ2BއQDCjfJ]ʸc;`p8 D %s)""QDDP D@(""?֛:DDDj9z,ikB:Οa =6,7gM"""c*Kn:|<Ӵ{.0q :X[:|ѺI6QHN]08yz[ܲZ*#Pr&IENDB`sqlkit-0.9.5/doc/img/completion.png0000644000175000017500000003236611714210425016601 0ustar sandrosandroPNG  IHDRQn^sRGBbKGD pHYs  tIME  Wפ IDATx]u|G~g$!FB.w-Np-BZJ)ł|8B!y~̻=SCo<<<E' K<R!Dŗy6- ڞ(JyB$ X0##_D6",QXB0!5}LLĝgʕs01EA9YCwWU‘ v>rchk5_ !=<||~#p-2^b^FcAgbvI((s볇`v,o|CtOo$Յ}ͦcE³na;W|(zv)Llqێ^jfڷ\Dp.U׼DtO[`Ԟ͟\-tΒ/n͞MXgۺ;r{ufǞEhҳRQzV4cm>ՠ}g܌MUgF |V*=tk;Ǩ٧ dKt;f?o&av$#f[\\A`yNY.k\;h԰MWwr@~p5S>5|S\lnVS̀wLb'lmϧ?Pۆ5mIny K{jT1W6y2yMq7~\sԐYmٔWx;QÖj>vK{gIP^|vМi6 he7~rGUĉ__O;63֤,yե~j>j>ܬ3{jV6,N>4IqXyKOw/? FyQGLR?_0}]~39,CqGg|w7+߮S2{P+gY&ah:׸ZW[xNZ5+6onS|B{om&"E@] KEFBВϷR7+bJT'<}CYSi;;LRdr?$8.^ J)t|A`J߾]~Dڮbuj]g`_wu"S tŭ6僧N5sq ASw k`!H翕$WI4$;,IJFlN3@$8!pSnaeI}^jlR8w>QXyZ>Y<\ Z!~cC&} fk{ڞU;T,5K^unj. $v.IYqUڼbLG26 QSsnHlbbr}X1GaQlYw_]X6vگ1&wE= :}ϪǨB#k =kH/?x(0&Yǃkw)=۷^ž 70XcUأ[AARuo\q`3Օe;ם{"*ţ6?D|凞FG_M![.m^MŜ(Ijg>{}{VnDo ech'MPh`&10wU_~#J֎cV[3ni2ŪiWW.9>wl=o[sO31جȡmrͯJ2w4|_^sM2r0i7>y5 q4ٌ7_rԣ4ϿWޣ29!՛;i)Ԭy%k";$Do7uc=<mS]Nl S1ӱ& / wJLHr*@=ECtB0|xJ|\ շ}x1˺xx7E]E+' 9CI$DW,2,G9_pZ}SLT8!ٺήBR_@b+x!b8KM7)( y:i#yyKo8ED<_^$Q/$L9m,V1 BHvr+B@$65!536 Q( ' / x\ߡ:4I/ɡlJ&<7 !Z+*GK/h &F O:Ɔ"SY{'Ep7&Cp!@?~ 뷘_` }XC0``H"$,2>XX"d8KDabґD9y 󶤥i鴟EHَ"`( X EӉ!ݜ|Ĥ_j,^9K~CN0a^۞_&ҕO!!1na[7qcdoe:5m4)_jŠfXoΙC\Җ-m_ ɛ&ؖ;U!bSUEk`"+ђ}vfe k}UʑCXY!:۷H`Zb&S-gm"?+pK){?YMtD.td U lgWbn8Фx׳:vչĨ}9Mػ"q ·[e /o{%Whguԑ+oFY+Yag,s;Z6ya]-,gPEvDbrΜ9۸q#J1h@;K J߇'S%L2Wl:_iK+$2$c>ϋO>?ָW5O?QgZul8Ky>o/?|8|¾Ouߥ{ZOnFPܶ [~qwfl5jFJZjY[ $q{f͚*}ZZ)vl_s045(Ƃ9F/dD z|2B#^0vh^YrW7K|pql69$ZUr,.ٌhh.u7owku-\CEeyY#G۶mR)zvmbn/=zC\[TBU:k.6Qcnyh)06gHa7<էz)׭?/zPK*%߹}C33J"^ZZ|V4tBޮ~ңIR48Ұgw$Qy@qn%Q[Ze^/@Rzxu!"hUJYiL{ϦM?tWD&c>\ A16M8Ϲ0mIm歠w%a]}VzpO^$>xauDֵԔn쾁G zp'NƼ<~xګ8=mw ;w8b`iaU=ڸݥ¤s'I((ݦM 6ZҾH^G0LALDZ / !E~^igh@ޑQ7gfmY"ڼjӊ3l`T-V6~ւ¹Xe(MSڥNcu]7}=Ԧ[-޸m}c^B#|_jUJE~>stn 5 nmcB`Szxڴœ3,-&]?8/a իgc,W\9cƌ#Gٷ_u3R~O|W@udz0<>m!r{|#0b$^%rz?KPΤGիz2=]7+nj3f̘˗~ԨARAL$#A#<'$M!`,!+$`>ݜ// `ήK^"Ġf#|9[LN~e|< |YOS^Xr)d7e2$`bY]1 #w"? î@vqPY3oqa4Q4i"͠=QӍWxZXY 1s~ aYb:0!!IJaAea]Qp0s[1 RCȍMJg{C0Jij6y:Vτ7]E'ɰAœ8*Hܬ'b3WLEc0N.\ .ekU\~ 01 bb0+A>aXR̝e*c#zp4-%oX3k򭭊Ul$I%A$ &X1/*geE ! hZP$| ^aXXeYcy8;>\=!dl '/M.q|!s`,/c1wRNފb%"d5 f*EAKzmjJQKVB \RT*yBypK'-o)emT0Z ro 0TMv/z7=$yEa ljq6[}ձ[|l4dl);8KXlFAúkgRCxOI!K4~膤BtH㒊7S"/'vP/~>֜~U37}hCtu>5kl"Mݺtq| XZhBsogP, @v RQÎ7]3 3&O(O~դ;D. n'gǝ{/ l:n>2WUQ}n!H u$dkk&E,\t$L EaAg?$ڔ7?~r?0M9,+rs AMAh畦9 $&ƛ0i61Ŋ " `B1U(JI{u#Jiٯ@9jŁkY=YCKqp~پfӱt`Y+ZQGտꣿ|mJI@eYZhCN[ G^3&uˤ Zzy{,1"p}w"Ɂϟ$P>~ _vעm̕˓VK&Nڷ5#oXQ5䤩h ~]1앒B:^PKJ%%1JJR @H6Eǝ_2{[ْƳՈyl\fmy5fy\wcHfn޺if.>m# cC;X$8l S\Yi֭甉D. o@Xvpdb sb%Gl~?l#eŠ^4+'D8ʂg]]o9h|V|kWՀss*s(O)>w2^_@fvH髗s<3L3̿298++Y ]*:g`g;7=7Tł D}O#XtxlbŘMJ+V~ʧm|ܜWl6hF{oUue Up.]LjJxfÝgXw%>۬R"bժvsذYMg>L޻l9wc&\Ph~^V.hL"nĵ9sU/2^2dW,;j]9TȾ?:pu+hη\oy1qV0ڌCoTJj4ӆtߡN..%uCN< cj9,޴W-`-0<="R`!c!#! %q갳갳iM633W K +e,-,U*5Bcј\}~BhƣʣZq̻;JpaRQ;U\>20LpxLD&K` u,|Gu.I*{6^G;XkkXUr͡ ?ꤍ}'V̢WRr%}&NqQJeCJ6 ^ć օ׭&lÓD`XnʬgWJ5m{Q;qot'{l".<38{C?cnԇg\If&wd'xR*'s=l(wi1\!ߧfsvU)/O]b0Tw7{wُr(%'2]jIDAT ebitͥC=|!6O֞7n1BV1zCäBa9!dHY:*'r]ؙ#¶3zXř g$0sv`cn MLLN'u61b¢Bٲ !'s$sJtpwb]"ڕ/l_L{YVˋwGWO82g;!a2}3Ϯ=Z4B~-<ܥc,.ϞnHDxHŽ+b,:.!) PJNA}i7|F$eɥJ e쒧unz^ d'Rt78iSRc㒵.>)))9)55US$IJJNJOO[-3~F-]5ɷUwc`ec6jU~YYaVcuۮۺzTY?_F廓dscU ]9kKTPٸ)=x@|6a!6!\§kr;Vn{2l/?,b@d]jt_22J8~߹i+b]$_(1XA(IcA9lF`0cnKrs(d)eK:˓Kh4߶[mxsӨv5_GC_QdyS+~]4U]Mn.j-˓ˆuh4w*ZɁMh4u ]q.R@ :zU#;6޸׏ 4b49>mԚw<g9`*t`'^flݽ!}s2 7B.h1|/>dwW1]I6d=Wb[8_U۔7p32:uk5]3,_02R헸V'wjktܼ3ms{ 2śt҅`m*^PyF&ܖbtzԽָDݒ օ~I{4zo ,3;l Ϲ=oTNbtMMqz{ ] R/)F+U(FFn(Lҵ : {J_&*h_<\K.ϱR6yRy_0F1qj5oE}/m\kt#s*ºM,OV}k"u?ZEuMc{ y y y y y y y y y y y/Zv'\;e FSgܕT}@hJ>t]]DAj]g&h:,~qx]9*[֔eglF:U2[v첿ӺN9_c 0%Uٻvb6?s3KKDzu}l,pLdα ,-Qѣkh4՛ǣ4JttMc{jh4M]"^cZ7X_ˆ3RʕQ_s]KXshNڃϧ]נɬ['fopc|cFh9[gY^NK A;h4gJՊrDa|:MYGd26J%}utc>{F.<ld_G6_Ŏ|@@@@cC[OV߆yǗu5 ܜjZhl_I:zOohT[ֿKq??=so +fb^t+p>),uUN%pt6BF5Te/UB9yojT[/uI$P6N- Xp^Q]Rifu7iռ^H]mesrhϛ`Q>ג=kQioNk=:ͨWwZUﶜh|셋ϯn]տONH;{ed2K:۫{V=OQZ ebicZ 7IbCmX#LĐtWmVaۦ "4n(̍hHC?aѮ,<&sgDG>t.4O84c<~WБUgkF\ x&zC9_SSmߠQN-?*Ҳut;GZ$XPےՙu9zlv-[v!YK :=-Կsv?%:fUFշAT_SʲAU/ժY cuT:PIⅩ#.w[7ٗȨ5ŗ,adU\ ؞"_#( ЇԲiM~}W>T8 O#T (HQ⛍C,2 [sZt#tHчFt7\I:QrR)ʣV:^ꜮG?b/dIT=8՛2X,U%o;<'Ka{i8)pnD >-5ttl_Q:}- 9nٶ00z֝͘ 㗭OֶT!SJc3W}߁l}mߍtuGѷ#ť|ZaP۳`v,o|Crջ?Os6Fȃ{ȳ~Twˡ5.gC~a8bƒ( -aL̓)Kfo oÊG{~oisWvڰhbuSG,qb=I'ӵ}/k\,-cdj_gjZuq˃|w%u5̶t_oAoڿ Rs;/,y͑ 'T1 =i>%^:pݳQ^F8b;=h:mVbYqTYRjZ( 2r^ϛ 4)ePlueycDJ_"2-־ZiW{휽p_ fLY`V۞x^yU7_Wbk[j#ϻ νYCZU4G>׾㯧]Ǖn8K%^j]ykd˖s6cs Yvlez  ($Ic |2yyt\ 1WV0}]~39%2mФ O9ܺ03,)+W&Ih*. !g̬B0 -omŒ]=OxJ O986e{hއi7Cl *5~v{{,V+옹sWy 2<%A)B$u4g>?:HK9b<¹\:/髆6o-۴ԳZӯ_cϾV]4ůeݖ?L*f@pL87~E)ws^iϱB`YV$L O9I8oJLx{r|:Ty-Lil=P۷ɮi+o!Z! w. M4i]zYW+)ڕѮhnj%KJ,5crswu%ݺOosv`cn ilPXضu&~yg{|rPƯ.wyޥY,q O|apC40~F-]5ɷUw0ۮۺzTY? I.%* w(lܔ< E޲`v=p}ca=vwh/ĪT zy+x߾=+]7jyN?ӄ!);6{I,M5ؠL E=BcY!roJxJ'۴7}㟟}l9Bwy/;# ~Tl5?Q~_~5t| !a >0FS& SD1 }N؞rw~LC/PoZaBUPIENDB`sqlkit-0.9.5/doc/img/filter-panel.png0000644000175000017500000003345611714210425017013 0ustar sandrosandroPNG  IHDRO`sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|&ݥ-t 2U/~)( Awin MW66Фmh>&|޹bPB}4!dyҲo(1Bq t/(9BfHƀa0 a{M P҃D1Dh`/RzǏW#qssԏya/iB%(|Kt111f̙3eθ׎ ppwЕID1ih Zoh$ߧԼv̚1 ҲABe*y*HJ.Ԭv2JP(AFx^0{`绕]jZ;eP"%(%=DWweh}'34P;eQ"EC|&HXmez6iz%+ڭ{)!(JPtl̛/[t;{w,w~\ݽz@?1U(ABe"zPWgcJ)ޱ =zzjuj JPx!%qUEfJRvL)3]^;eI!b@>+5E!z&^}]kN*|S{^}y(AB7u=#Mflm٨W~JP""=-LӞک}Ù!=(cuvJP"Q{(P"TIJJBtf\REKMkC Cn+Dw5{16!8JPJ]K!&Q"b=I޺jZ;b'rҬa$ b%5,>BHCC|Bl} Y1B!aè"'FUj|؛T*L&v;WClL&CJ-:~YgKPq<*MV?q; "ȑBj'G\vHU憏?9F7cۖ+^W !5YǠ,P=j|3NN:v^ژ!:kܿVʢAd=;a[.q} S`L Y+`Y"@) 񕑧J69I*3):I3!H{Zrw029: #0TP@(.ʃPqrv*...&~RB-!2 zM!JRݰ^Px Xg+cYm|Lz `$D6A IJ +IV hu1\3O aBz8sWR*cc!ur-wO@ 3o,E2M={C7YQzU*0, 0 m %(7Mj$ 9<\ƽ S𺈭77$FF s8njٳgϞ1zՊ* 0/jalJ0`@DN`"<_'!{#Φ8뷞w+, ];5CT@xrT& 8AA? BרPUlL1'&bڌzU H&5f0ʴӣ4-7EQʿJFM-xǯ)ja'0 '0h -/Vg_EN  <rAVZƔ)S._Q(A7͛3~j7grURr 뮘f`jT_C0-4?o3hBY=P '16>ڡN`J?s܂J%իWW\ eNe*_CҨ pz}Ah|~G zP8&\RȁyD~?*տaۮmNHWްq3p1t_4n;M?bjxO8;!CS !AN@Ǹr/x?:W.`wp?a ШQv j֭EرckbΜ9Sԩ9s`͚5.ҳe- yeP+Xͥ}QEi7䥟-Czz;qA~7S`-m^deee!h_x^{NcpX$lU* ?p`9t|F7_{-AZr 9zӇ5{r*Obb"{9/e˖111xb B*TY\pp5o[HS Qvދ>>ӧ!FtL2YW>md 6&O|`; 220u1瓏*Kezju a۩Qߧ/E:x`9')) _uJ%tݑWrJ/K]T7u2SwBj\` d-:+;h>[߬_}ww7Lp2:!QU.B, HyoZua.zr:|0͛7#""B7 JčVk83c n<8N.nBIWWW89ɠhn!ng"$WcHmҥxq 2ӧOq}L>oFE0ULm*q~׏͗2 Ld|*B3,t]?$LLt4^~:wG-//;nl!o "ұM歞â%c9EH-9` a!wSU{qm*  *ijQoǨ-ݻW9ʐɜдA-\[ 3^#80^\>}cZ @Bh3W=$ uйUmס.P` ppg z/B*IN b7NB!6Խ䛖hиC TR AH= oRUBMKa%kܬ!"rT$r:!8R['x'7pp4&|Bji#N 7+M7Jy 4joA#qxz琕ܘX _iohp!*At ǫZh|PK`,+wgp[Q=^@PUx-xSp^r vHbqq r0 0y!`0!ԭ'v|mB#omCWٻm@dݷvbF`J(U3՘jRUF`=(m3m;tE](..ҕ7o:󚷎4o\qW۾>=u!đTe|rez]2mظ |4puq'}֙9}*| /^LlniiU)wo3Ù镟9?-둕+VWβ BcP9Fz]:!uEXdeeg8qN{D;/OOL?{ԕWOyK@;w_b,;Q"82gU$'*]۶nF%r$0!u!Cu˫?:s_g)!$77o-;x@|4ܹsj\w&ԟJv T8ժU Z^^,ˢXҽ8OW烴txת^OO!wwa{#G =_EXyG@V-1`jߝԭ?F©K '79Xrw+oؠ>mE*2s0zu!9%U^=GW+RNիĀ!q)5˾7f4.7OH0fH?r7\J. ƌZ=`(z ھiOCǗcEhx CGE]z8͚>CF遑o Vz=(Oƴ{>Vx򉺟/~EV bXB 6*l~Ww/b`hmBIOcyP=(+jt ŧ;eJA1sI̶)=BAe8=($󧎉!vl`OLt9!đY6fe!>B! ZC|b!đLPEJKaWWWeK>_C Qw9pq$ !+la_~(!!Dͮպʔ !8G&KǠ!$JPBl%(B!6ɢxޒ͉?E!ڲkW.!=# v8 X) @ĵD J$wn#q^u7+MmO8o^Ƕ-!W8#,C2JVCX; @R)d2mZ;t`z  1+Ad#?{vnk^= d2$©G;1?eVš^Hۣ% TCpHBTtG\vHySq^ %(p@ ʒΝKBttsJY8~x!-Zv( ?܌ -JPbdyrg= kADpX82%( 2Ǟ)JUihXX J-r{?iuÈhР 8@?6_PAA~AAq [MBlDgYPv0[m V~\N gw}ȐHJn16$ebIC%"sкH{ی5Li=^}v4p1̂ PB@]<;f)fm6#s!ك3n<ڴn*:_~akpʕHH$$LBLt4Ļ4tBPC4x &=?v嫐A XT&i&4˽z+rJ)?BӘ6hSĝJ[xSWD=]{ϜϿlCuu^v]qXSz5m !??_*TstbHXC  6TO.vݎ8EFE"={4Y MN/g`SRv .Jwe$N~ްIN"KgL1KWϑCоݸ|[8v6['=0l(>r?Oݺb嗯X /a֟qQ(r_DZm g1^ǙD>* s5*  /ą_L%Y%A}2k쌷å˗ue;vƌS___|4m vڭٳB7A~~>fϚ7K7mނYӧ" xR%};b.h Ž7oaoK4jk`ͷJF¤ J]Y_T_Χ!3:8 JP1(/OOkBw96$$9z[yeL@n/0OM̯a_êÿŏ!adu-]8kT~ Ņ6mN@wR Š.fVZU4BNFJ=RRR]Zu/!V$ْ=ztC*ƕV0kѹk9;;݈h(n\Ad#JNvJ C!?KHMG{N=HEpHX7%-8$ iRT*X"!F=(p'& NΝ3k}㇄X D'+-G JD)-N(gJnnn%&O';A0"4Eɟ z} *$8cO $A@=(q|V T'$Lmₕ,=ӿ]߃7rblHĚD{A JDvuz6 //| - mkHz...h"? <ն ch][QߺcOu۷oAӹ]? a,ܼu!*8 cbv6|`3":JP2Mlq TrssqI|2ĵ}Ro^{_Zg鼼<|f-ƽ.UlRpʕHH$$LBLt4"E 4tBPC4x &=?v嫐A XZ[qh*v8,\)Z=?m؄v fxWo\|EW~7%#F3ѭhHdeg[1tuMٲ'Nb /77~y7#7n]2c{{!2*\mrؔB!a݄hYΝ;>`YGsDWߍW3uqM(p Jpv.p7 *85 bU/RRRQneezTw0BvpssE~~>ƴVb,9S0 ի>DV_X aW~hspuuE`@߸IW]GM0z::w8o7"#@4~_Kc>SC5xJPecP{o.qo]٠0eL$MFk1>J]\T \'RRSOYcl٠ׇa}HKKşy/C*&;vÇ>@RCR#)oL on1wTD/7Z`.""*icElNmDC|<=bUΘѺQ# o"#3ubܘ{٘3o!Ƽ3~~1|v+Ǩcwƍ?>Rڵ}.-}xb^zc1pk9M8;cꌏpN2 << SU߼zAYֿ__=v6nF~}ѿ__q@4p`ߐ.Z:iS[&BL&Ōibƴ*H$=rFQ^4:/s'wW>AՌt}X<kRmR>.N`*.c*,Y0X`zHư,KpjYuKk{NE?ulGF;#ǠJ9`U hc#x7˱,zb!hGcyL "$(ǽ /e&$DHMC JDnoiO/"(AHb;ʚNHhRCPm 59͛[;BP +eQJi3N+3{$ 0) gPH5$# hbH:v[6[^G p'؆bq8D^zȡDlJNC' ڶ:pvvvH8JP"j0(JkBLJpV(jPqHDJ;8B1%(]r ~i4ggJw@DTckCC%;o/zY`vhpul۲r3Z;$j; T*L& Qkw`C^C!&`R4l>cέxm6=T+ɐvSdEDJf=!$QIڃTUxsPbۂC JҦTճ8.b히8G4c-B@d Qۣ%NÕ\D ʒΝKBtnwJY8~xfrt}-Fv(cʾÛ}@S@Νm0𯍜;EozP"eV%?LTͭJpް04LDJ$9I JL<;oѠA!5QDe+C/(ׇ R[J⁃8vBB-&!zPUP/ n]Xh غm;\\\GtU8{-Rr!)kN=B%(KFUJN&UߺcOu۷oAӹ]? a,ܼu!*8 cbv6|`3":JP2M>xooOOZ8N0܃r*{( HB869]?8 ^kO㭾ݱt*gP"V8U/ ~ڰ :tA3r޸tJֽVhl4xk$u*ޟ2 Šu>vyW/^yUQ:kyƌE{>b4gDz==Miit=P_ , ]ξ#D*'I/aND|Θ:cV{kX {cΜ?ubμO?_9{o_Pޭ<S㳥qRWY-[?igszˆ3:jq'T@a.V\e^^]7ᅸP $ }2k<=o/S١{-1yE7o]XZ>6^a}F>שS|ÏHsSLĎ 0k?xU| {n3t_#%(BX%A&'P( sIh .^B Keee#$8Hڧ-/((m;p5t{)ճaB~A robq3]*PSo0 )vuw0BvpssE~~>ƴѕ %B)֗J(**B<72ɳ'Nb8pZ`~E899=lg6q?f `ܵ 8;;݈h(n\ ='O[n^=ӟĠ^ɓS2i+Ln10ݣ""x\x\DED p8 bC\qb̳UD 69!O;I̟;s-3[b"Z|҄@܋Wۺ^#A4i>Xm&z~zw`ʟvm߂7 eh2q2E~}y]: i*! 07Z:iS1Bj;+gwΝ }}/ ֛M7έ??h@r;mIq2Mp/Y0X`EF,ˢ(uֶsp9|ݒڞFQ`YVxv5gw  !"2r ֬^e=˭?3˲>@BM%(ciպJhGcy v= q"-=‹pA"#ĶT$d Cݶ8FvRE6j (k: #qJxGvIDLσ>(AʶAͭUx7BZz&*^jKKτwx#ۡ%VBVY[рu1}`ߗщ{uNp<px~"ۣHаzTCjJ2lF@d z#s r#*Xޡh5|"[ޞm_g:v[6[^gɿ8 ;ɷw6tWkdT愆ma8ZZ%jD^zȡDlJNC' ڶ:pvvvHFYbgAQQDTcATZ;bV*BWWWkBChSJ;8B1e B!v!D BM2ٳgȜB!:&%͚` !0m8!=t BME!&Q"b(ABI!$g+QXoX!) *XC!bK®{Q|lx!8{=~"atǠ4j5c"Ez%xZ֕Qg!8{xw'CGdT4HeNֈBygKN Tc D!O+nIENDB`sqlkit-0.9.5/doc/img/layout-simple.png0000644000175000017500000013767111714210425017241 0ustar sandrosandroPNG  IHDRaq}sRGBbKGD pHYs  tIME "' IDATxw|\Ź>3m.Yd˽aijHH!J@.{B7$$$4J %c:66Vj۩3# ٖd}>kV9;gy뼃˯R`bKAGySaﱯzq Ɨ_pU| κ V6uFxPߕ&9q? hDޣtu9,*=4 tGL-?ZGT ~&""dVL"IRHaqyuY ;8gno֢q @\:M/|p 4BT!D ADԺr҂gЃF0B g[fZEhrٴE6)i?ϽA=X{?_=| bjQs/Y!r.ϓ\a'dy.U9E4LGkL$ @\ALǿɸZmLD9GDFv4(PVU+v Tݘ8u*v ˃E*`촰4"ʮe} .K$U1> #~gE)I ~j|ue4VU^V (M$tURpְ1<~}yeoьh_<}C,_ 91t陞e䛞9}?h\4󃛮;/Z=cVeAo{xS"gMl1X+,s=QQ=!) )キd51HQ^U+3E T@D2Dj:$)M8{s-7VP.tӕV%d9] pA猵tzsTF"&XA_>${t߹^=|cW|yg{Te^C˽\.^|5GztFfIl_\N\vHrEHMt sYregzh=7S^kыۿXU7jD{L~O*_}Vy\zgҞJ1[Z|g+<(m>L<+Ǟ8ܳ ]TU' ` \0R_7'>o?0ϝNCqT; (S8qQ~&"*%Eg$ᮥҒx:'4Huá$_"#?q# B(޾ 2])C8Rz6 [Z>I˚D2]r™r>y}[pk)DjRy<8' =?'2>$G8=^w~\9Smzzf֚$d{o[lD 6nB}B\6zx 8{Zo+{y)#\xQiGN6b>jo*D P(p'a[؞x9m\6Fa+*wzcEa؞k4Ñ@ (PH;.#B$n"3Tf,|_=n;A/Iv/o.gH !/Tm ,E%}CpPUDp]/Rik\uU(6޾Xa(`誢%SYD^[;I_Nm0Ι'A~tdځٹT3m b[[/?@x}2rs]\77/#H*=gzlw{f0lj͛/wG_wTqg魿tD͗n,@z(꘹Lֶto-8雗<7+`HPuab__;ѓfPhq햣UZZtuwvrYUUCpIIIQa2T<=8˦bXI Z\F{*Zy Am4Bͨ)@ȽE͉ib[;W2vޙ9$VPk(hls!H#j@y+e?8Wb-PLA?A{8߃GnYQq "L±Rl)TS)6qUu >O"9zS<2_Iye?<,y䑧Jy䩒GyM=\DRJao_c(` IG{謡G^[W`nIcU~?jk%Ǟ5onԱ[xSl ߽GTw-"8yXqHEs[e1+_yQ]|ʽ'^6{fQ9SCi䙢\Ea3uӊV( K 8qFlD\;:;H,ʲkreDfDط}"yl8am/8o8>J>J;iz.? 1Jlظ +yG<=%ʶkr3PzxW~sz cбF@W(SXۛ/x+[,΀nahF@5] +/>! U7}K>zB ] LQA#ufF/u AU|N ?ZqswڄԼjm.m ]劮T%UD#`p "b\ 0B픷݆jp-}iCNHo7o >2h~{~7vPyq?km]7xWo¨0BACWٿ>YW6#y:|/|o@/?`b2;vW]s[?W"ȁ^:??ś8|cO-Jrşu+8\ޓ]b}ë]o-i(>/PhpWd "37LCgnyvkvpɓkf^Pj_=+"73ǯUwM<϶{ߢ{h\v_/wAAٗ}To]>mvY[x-G-Sn~ƟoQtDt^Ze}/6>q¬/աW~7_|Ͻ8yatƟ-|0[/Iŭ9tW|qnm5 }E=^5MCI騱?L[0_ڣEx-B붍 j :ku1M-JvO>Ӵ-!j*'~瞛'M-aH^hKv_rjCP(S/Otsj_?`{*̬po̟Y`ޅ_]s+.K"c QI#^WԻ'XdcKF{ Go?/TC}k `Vw*4o?h;ūnoތذo纃>| Y߻_cқϼU~_6?/ (K,9nbQv[w_2+ h|P^} ']t]}HAuإ{}+yW):)LгK./B۲ݿ} g>93ΛQbjs9 ?:qVM>wÔ(v9$McN|MX(:ps_%mD*Wa!# @9:NlcgUr)׿HeK=i]k}uWG^=ˏ2T.'@ոyTŋO jlî̆ Y$K};Ys,( Gfq%31zB-6Lsh8`F{?~)te8[ Mq=x;ء2*f 䑘Rû5;˘OO9qCP )3@R""T c8 Ad[?~tOq~%v\F9a: fկ];έ=y{Ѣ9;xa=rٻЯ si?c;8rM*W&6u +~`d:]7SMYr&{0}b=< q"Ү ()*;ݳ{)6[5im^6~ƅzsR-Cɴ-9-+*5*g۷ˀbmɦM,{/o\֜XEIIHܸ-a;J>pRCDem7w{spoۢw7wݝ+uE+BcV_0EG ֘>ZJL=ف4ظON:e ^fɀ)ubs/~t/3"[Y?Wpq YHΔDP*fO[]#޷8XE^ Ю ߕ~W"W>wןɫy ؉g-Z?z/% Ȥ?0so|?:pWΙ=zlBUo8h $8~_ `vEy_<&iNo,^ng)c/-BD(4 <3VΑQEYѸ1F]]+~agc&}q)E?c8ctʦw &VqI8M<>l]ʿ?0ac#b[['3j"ѝ~!磥nORPMj]|:D* @ybCDCe#> gjvli}L:?2x '_yZP6| i"T@4wwv]]׭(++hm' QT&p8d X{ >ٚ|g{~FIj*N+iFN`dpyB^ytCv\أN,Z&W`ey!s=:nN5g_L _,ݒ|s@Q(P/-@$"p4~ ӦʶLתEg8hsh(^0L3О}vE߼ݓ6.KW5>}ML4Ms`'"vL$UuԼy{DY6< %7wmo]탃`c رMfHUUVՠehW}@zy#uK,/=pM7ñ iDL7pƘ_@u˦ˣ5i-wR IDATdO MgdgX25Mƚ)N?LAn2'1&oqF%q.YΩ9˱G-תu}``@J8>PFԸ)M fkr]y)7ac[LR"cu OXt#eh{7p,zRII@B\rT%[7>(mOd3k=i-o /SԁiJ$:dΔ/Ӕ'ah-r4fUɅ .q%5NJxP({hE{Ziljhx@θzg2Y]mۈX_8ƿiYs9ݢyD RcgBxD#ﻢp\r(HܴL@=k{U=HbEl2t!mWlr9`01zȌCHy22U\ _wˋ.[3gib1@O*t+JeeyynY޸-p Bx8STTdz*0=[/֋/I!C>x0drDDR5oo߿罫"A/Nሮ 8 \ZXYW4hze>F["y JɌf"ML͍2e.WlFdl'g:Yu-+/UFeZH4 ]usfV+((PU5F"Ƙu@*Is08p0x #]ׅ\NQMӒfoRew^a+~0m;,yk#E_9O\d92ɑ۸u`Ӷ939/:}ח92|eCݛa$rD8CWHM;sk۳;z a+K+oi=X<1:G{a$e@fG*LA-ܵl[əW%7ʻFz'Odr\$)*iKRn_477~ݨL c D0,(('ŗ L4M" B[\=HGyNXŌ Of LS2#d^6TWK"JmIAšiozNI.b[F{*ꅻ'Cۖf K8'C{*Bx&MRU՟S"2|=2jjj OL(ʠȲ\.s.^ bYYi]HYw|qCz^BD_/V.y\֠G~ǝC@.7{Yʑ y3 seK-$-ΈRvʋbޓnd}mnIǹUs$m[T њ^\ҪBiC@xzLppBo+م$D3'~kkr_E(\6_u3DZ0 \TbV.},>c['͉Q1R9(cDxhR9c8_v)sӶ]_VR>7πSqN=bG,?Uza(8FeIƟRcse ---d%KjkkKJJh$S-2M߮zh~\ce˖EѲH$bcq4BϚ9}/RExB.Ur"/6T@{b?ޙL';Zou3}'ܱ噿( [\3eU!0И\u]3#9YlT~RK,ZzZ'&e7תmM ']uT(p5sMou=W39%S2UU|!/UFBpg2t&dmK)x,VT8#3CWXX,ɶm$~05kCM͙Lz-4SW8D[1UUUyّi۞}#CtHGXQ8P9y "WU-NJ¿zp6nl:S"kR7db)3E/s.d)\`e}GݷS\l'5ι41INbEp%+o>?g rZZZlbYcdXa׽TU[̪URa#bsmDr%ɑW uM,C4 RZ5iup8R7B!G_i[eI97g P9xQHeޤ'm)4iwcf 1nQ׀M 5\a\i\h,^_%p )uQPGgf^48OLQulw7.mJ"T\34m '"rb)OqF `](g>6nܸqqygfKKӗ,YyE]J֭[d-[VSSc7> "B cҐOml\}3f̘>}(m۶mڴiOx/¹G;yE+WjnnUUU=P?""2"/) PXeI߆Tozb׵I3Q;xTjQ1Vb^YpXU\ݞCV0M:0d$0‘U/$"1adŧqH$BDwɓ/_`iӦ%ڢ@ 3ϬYljj3g΢EfΜƍjkkڵkO:餩Svvv6mobH(4I{ &pA"eHt@*(GU߸eSgO8_Ȧ~kO)L^v@Ghb~=OS.H (v0N16,4>> t1?DꫯJ)_{SO=uů(Ce>oXBK/ %ֻ<:FTO"Qy_VT?09ZjjJ\0v}p0ڠn(iޢضqU5x^x_)\KD>O |pA$CH ^{rx[l"yn?p~7w]zI.{Ʊ7 S%_S-nQjx$(My9$ cθ5/XZ}- U%v?|٫Y@b4DD@2 k Os܍9?";_h+[r8qpgA' <;+RX,ݙH$h"xg{wf}%-}vnm۷oBLϞ2er8r;w <Cc91f۶UOOO ={t̗Haf۶mۺuk: sΝ0a(J]uU@}Ȍ;GuԻ 4<J6Fuuual޼9˲萐֭[w 7lٲ3d*D&tgr&UWHG$ ] 鐚NKp88!. Jh 5"@>a<_ %Cb΍$"KJff]oЊy---}Q*PApC@,.uvl ұ}A(jRɅ! #1 ,TL A'rFp;}}> e0ZݬceW5,o40I\" ݛ3T&Hш.W/U>M5*˾/L頗Ax\Ej&fcVE|N>"ܸ+F&ZP9ӝ\ʜH0 2`+NS;|h6qVp@HI*Iùa~$DE멧[uoݡyZU*u2`MƄPQ2c^RᚗnRBdB3 &HA8SYlʒH1Wp,DGj~1UzxثCnYĐ\1ɈqbF]sX'QLD򤔊+47ܦ+o*Y"D`HOhutI>' IDAT` (F>ɜbӤ15!A㌃%FB`. ԡPJB DXӞUqv`5{Y­aQ1s W9Q  h&PRG/baM: *cjkkk>okkb :T0$-Ԇri٤ctd.6LCTJU%CNWUjS "&$F'x'J)0"RJJcǎ-[^F_uULMM)7MXxիKRZ,P(pkmRǾkvB~/g \2{i x0RUO@Gtr- =dt/uF#č'ҽ)npԵT1ӵl6,s`cc@$58Ղ"D.ߵkal6[7ڶ]8SsDAض====ϫ51ZZZvܩi"~:TN Ȃxr0@Kxqܙǵ8HBqNej|,FTA4lZ2dJ)x|8=܅_Btz…R\.FΝ;9DsD"q'TUx2OSXhag}_I) u=s]_ׯዖDQ]g3!8cq36 9%w*eam0>P+0%2NT 9"RAtuu͚5>… =ϫ#uzzzz{{׭[wުbΜ9w^*\|ŵXXyUbkOp9sԴ^n&hB KAie!*Vիsl<$vʻ6Dfp*mɊ!0@b5?I@:dYhX,kllu]]8PX(6o~CCCزBjXfΝ{mll|LܳgOR9>'N---sOM橧r]2C 1$@ }v5bd |謪|h)?tWɜDZB-rHf %HO[n1-E}022rlBeZȈes\6=ؽ$D`u{!0Pje_5d0>}o{+OeChTIg׳3Ds:DJo$+HO:Yj@HTGD? ęfɸa'kOQ\E":PR@J"W˩Bq,?0:<*DƮx|e(ceKǎ\qRJ"0X s MiFwe%@"4:* r@#%%< 0 hTk"PPs-} "6tn>ȃ/N6P(a sbP r%[WR3:?9otUASã?ٴtng>{sچznYFP1`(b@QWrG.l@+%+2P+Eiofis@[G~B6bojH&TLShOd2=ˍe t\R |k\ji^YjZxƺ;h+9#)~ݫ Rz"Ys "r:hUT@BDdʀeJ,"W\;<>ɍX5Tv&9i&|Ůx]C9<}{2L+e\NSBW18x*Aƙc$Ir1HX@o9KPC*B` AY_.ԥWO[\p]?3홒3-XL749w:ibP.eRT"!u]-D̛;{2S` c@u/*JR,JGjq~TW ;@JI1dX;Uf>҉ ܎>=r7\O: 3O.Yed~~]3BIRGsSo[s[ct$5obx:L3#TSB\[H4|1O$%AGaV4Ñ(s*D@H !q*-k_@;uzRK*Ue¹ Y5V<~zO keOo|\&*ݟl9s%5rtN:iMyαc'OYy XA/< F$bu4bӎūNݺic*uK;; :*k Iàl ό\^#Z4@:)Ha.:b<o~6;Z9WH٧SAgE]dc[ۛza}lzq^Uu)3ATUBs薲FSP9D[a+&סRWB@J0u@"%ƅ3uTSG @:2?"vX]8˗5P~15[!BSH@D6,WH5anfkݏ|g[ 0{wO䂳_"~gH䗛w>̀g)M}!*Aȴ8A5¡.nEcPBL49 ||2=|T9 H,L]kdH֡/Uo9 .p}KT`ȍ*jSPA %F {1]ىyZzhl]%Pa@4f٠5!P(yQF1D5PbΦwEf]ya(!XrӵMZ3$4kVe3RCM/_!F2|/^klK67:T)e9OL)_kXsǬS%:{5{x|.jODSt<>Aligܼ `& E kވSA5m?b߭_wןlF 4xnQM?RXg_|/-]Lɶ<]ȈaǣP4W!ԫh?U̥H/y|yGtmr ;Z߰nN` ծBUA58&=t+c&/ZQr&E/CD8x⅗rJgKG,d0 VK{%+,I W]q%4jjj9U- S{~~ӗ7hϰBvB#'ܤxzvοZg܏]aAZH6?p\yvaPi_W/Zy2̀Ϝ1t] -JbF BW"JhlVгVYi!_}$nҏݨI_j˅AVP4]vlb"PL1B*_b%/f5(1#lp<=wޜeYlřghsCr1](H tB|c*A, aJ @>s3 `"Lڢ7ӻ~?'ny7_BRr:-H} =PDJ!k>4}%jxhBM|=FzXӔQݒ _zxt\D e;ep4<"09|>SyslKnj:mhGSW!TCD :6֐ ""/Nm2NkQkN\Iv ) ZM͟2S|dyZӗ@ @F5Jv !(.dIQEXiH$Qvz:5=voC<Y3:t)2ug=t{lj|zir_ܳ[Cɀ]wVyd".zꊀRкNg=Ȥ7h7%׀/w_O 8֖]%| uQA 'eǩ2@OJ;v_У]:Se/ U4l|9O+[_Z5蝟 P]1Ox'OH1} #L$J#s.柤 Dӵ5Kyt ]ܺO2x/5⟼K@G d49<\̎ ۛ [\ $bpxALMC3Œ]){/LM+^改nLDĊlIrvwڹw]!'Tk!qMAȍ"DD T !*d b9җnߺmm^JR@H/U&D($ qb\qGBH~O&vm?lV7YE-N@RuJ M,.QL-S o6tݝWYxFCO$] in 28vv.Z/`]g/^w(y(AM`gz?ЃX%7mƒS?N#z:|>4YJ $L-鎙 ¡@wBSJJu᚝`X2.>WLcOj_=]ZU?jo Y_vjsD̞ IDATb`d@n_peL">ʧXkW,(JRF֞GGbyH){o]M>_r3Hm4״b%7*.:`c@mA ,fX\y$CA(k-]^ǛVϽ>qgyW阫{U]7 :ȗpÃh$<3:wZxL`:WQB^KVhYu]Jן}uWlݽWuw6_٦0nfN6ӡbN;/| W]y%ciΠhR26YvҶui<̀$J)ȡ6DQ*&ll:ꉳyVꌉIҋ HSuuzj .YǮ,;ml\ RyAR.EohLOO(/`2@]ө|νt MI)cj`sN #$@UG,J]{5_s > ?(& X re^3*ԃl"ePW,)% !"(D}N[sllC+N?41V)S.l^\)mq4Swu4+ܸy׾CZuK)zSw>48cj3*F?lݺpZ3On8\lF}ҹP-UO' ۲S۳nB7yw{lpjjѹ"CQC4Px/V!6tGg3DB%kN?әBRiE}o:rpzlNM{nµgz1и+UJyH$T hf[}`sw#=63ΤDfi:}1Ǝds /=ڛfу ŗ‚)_IIea[EPwf y]]QzM 'i=:!HpTj "ƍ|o~۬P(_};80:/c=}-ᆢ˜fT6obim6$C0Y 9+q׃څe+?ѿ܍9GEdE2Rq.{Fﭭ |UufV\hhp *Ɛ2يtFVxS"bAU$TCUi+A2%H@q Ɨ<[>dJKl;Gtjzs[TwϜƸ51tM1tUxSks[^o?qg!lNΝ& WkJsDD)q`ə_W<1I'ː1A =eA4b.ø@T01ԛ&=Q} %s@øzNqIRJ.:N19hfhj2KRqU[ɿ۽{'ט"D@g :TNU)b.!&C:Obn+V,< djdLd$$q֨k&CU$tI3>/D2#R, עtᅭry𜪩mͭJ)9S\FX7C{=loLf^{Zb-N]s )U=gYE*unP薞+=o5N*uL+Q.P"h,iuR#`(It]BOHp<DfFe3Rw\;RTx>\x^W2nh,TbҹŠ ._fcn,LLm:w͚B*5q =s/j)kd!g7& jj C{9B=-|poy聇TJLc@![-X#A'=DƤY2(Y* U @j1e1JpHIigFohjTʖ)=)0_) T6Cn& _8cpP0f-{n5o޲Ň^x ŷmz͟ ஻iN>v [{gyds]|W\S|VΨu^EӅB=x1S4"Q$sTPGȑHC`w428dJhr {@qvO'?൶Og yǩT*U]SJƸ',=lx\`Qyb<75lK_ve;OS;3$QN_w'~7h:#CBt|c7=TzKԡrz5 @i j:D,z͟d\ZJ P"EDJɻ3]J_uW^u>=S׾ڎֶÇmݿs]e'Yx'}E7 1 M|j:cሆ84nWg\GR.<أ-'2ӸyL3s7u5oF*O)u⣥042SxHHv"U7 C_EcA6=O_?'pӓ45͝H{`kk{Lxٲ 9~7]֬!N;QTbqj.b5ӊ5%_j@R7$P,ޔ|ϋBԺ #Ïr+P'uCy"!M|:{ƙg~[_҈# %A""%%$dtLDcIwSu7zAOH:!::`dlF=}s-+tr?p•sJlkm(Z[Nb9pt.7tޮ 0quvٶɖk@dz/XWQJHD!* IJ:;Dq9lh, v>Av*THCC&r*qo~󛉉B1IxK,]gDf|NMz 2CD*A)<~K##{vy)0DJY0 QcM͒QWR^cp+_rgW.9U# oyY>Eo}>/jG!i&qnnonۻo\1k}]K2/oωkWwtYK_:(]{w~M mٲe:]eeum\.\n«D $ `#2B I jd(@23%_Y+V[.3X+Wrɩ׾\!}Ǯt*[d=szNG=U>ˠhkSJ_ ܊SL@x4/՛ 3E#)dozϝOnHl.5ɩaZѮV*$()Zgu gnw]rR)?9s?3\{mvRJL O }RB 8u䶯(  P)$tG 4EB*Vj$X<<{|ޗnjm@! ;jWH#j%DP)yB.ݶ&4/.ϧko۲MCsέ[s}bT:rj |>R|V*'W+.^s6]t4R" b  $(;""o#LR5FPxdQ 3FGG'`?ψjiݮRn{g1C*RԱ-/S-K<̑5DD"PĀ!5o̙ky"R:w\BH^^竬n #̛7o8__|ycc#ƘR`?l6~zڅnYg"0ėT̯Cq# L A qDI 00,~D|ۧ5lܛCw}*.>BH6ډUvfkJȗ90rm@88/,Ӌa٤h ^: P`1 Ph۞.0ƨ>LoYi]O hkAtnOS B 1f91&V$!jLy>*Zu9}&304*SvݸqCsss@eT0YC>Sn.Yr1] U.(P + džjc"uN)H\WXXxunbXXݼSٳg7q*Z*q\d2Y,J%IdZFX,Vk۵Z^Z.nB  6R9aBTz}U:u>4$|dE'@ -qJ@"O$ @"JQ`nW9%Xbǁr;T\` NgH"U2C[q@Sg9J)`*@JD)eF$Yq"Z0(r+(0 B7LȋťQrAj$,>~IIIN3,,LCCC=Zx<q< .h4B27BCCzfBd>8Ψ(0zeYܲZEQ"lNIIZ111.a(fCAkNFY}TWWӧ6<eY1*111 h40FS[[0O$Y ?Id(vN!0$d *s3JReerL&$`0TUU&4770n!dX=st:]MMͼ^$IMMMav;qe^/,˚fr&ӃeYYUtAe?wUժT*%2228.h j:.///##C;QY( 0@=z8;GDDXVADQ}VkXXu8!!!rnTWWl] % **I|>vA-QKʅk$IǏx'TWW.A)MNNPTNf Y-,[ZZ<OaaaDD^/**Zr`M["##YeY6""[nv]eTWW悂 Sl߆&bQrž5 Xb, QBg@MM.0M8E ΁9`"5Yuɓ򢢢_III}} rd,8'k@,=˲kwerj49'./_0LFFFuUfkaNd6- Lɡ`PUoש,'B ?rs7ڵtJ$V@t:ٝhllu8'N,--Z999!!!ɄWRR֩S'BHaaauuubbbdddffg{^Nwo9""bݺuG?,,"՜OT6۔)Sdرc;v|vQPPvZcbbdt:F#ߢY{DRvQ7{+L*)>4:ÀY*Q[[k1\zJ%"BpC8˲!!!  q: '9Ut::.%%t| L& |>BV p0 ew8\OxX< Fc\\\SSSccZNHHN DB$.q*bStS理 Y9 C-$ӣRyl6Pj-vr`pȓJNلKp;յZ]ss< |>_qqq;]@RD.T}ڳS LN뽜auU~OgzNtJ5;t*"}HwYAҎjFI^Z4UW ¼â wp jlmNwΊV%中)oخl NrڪхDK;w NUZDZ:4c߁1qIRkpb*h:x a?Ót\v{m^j p+Jm>?( NQg-M Vfİ|Ƽf 9\@!ꪩt`İE(ʊ=lFKK7:95o,w B KJw?$!XEжm8rtSeRqFԃ_bwFf|7/Y΢O |IV(m^}g6³3c/6{4-)ް~֪7v$ IQd*Ot`*~=%Uw<7uSWF\2]fh`Y,?-w0#;q dTI߸*ygu[;T:~$"I0qTZr%*јMXKJ:1рvUrHaUZFhJD+ԼyB_ByW)*yB1(; $zvrRӉpSbPYy  {xaSF.j5?/ JP¼%r HaK@cm%p ’ eWctVpw~JEP_ \Dab/s8}#͇r.(PD9K+rwt>hϭ>R{VqAHWVVĖullwID6>/W{t nd4))].NWb_a}vka֖nαP_/?1;le9e5aqOrmXbٹzz7:Ô6J/iv 8 sr$A0R_l.[ΎMH)p8ӷ+OpљMaɩ _ڥN4CuU;~鷜J_̬(}cQ?m9{tg Tm9Pvp}1=6rh_X]B@o&66sШ~Q maBb>(D$5h{N8uHaԽ$kw7f\60)!_jtn1rpǔ~QlJ1'Lf}zw!;Y#JL_b}t$<Aӭ{z(=j?lgLQX똝"gAEcz7J ¸_E)Ea$ @e I"`(Œ3DIJ9]ҕKM_ba[3(J$"7-QIJ D 0bcI$ 0I"-a)caJ(%@ F$ʯHݵ~Λ/])"H3 D< J0H0""$3 IR,fhg 'N$bYڅJ$u[~e*bKI@ ' HE($~鎴[!wneOkN^"Ӛ1Co \=sЪ1%> \&QB6Y\+3$tꍤ[P))mï<(%H??@ƓP&ۯV\P+"Sled!4.a4`#6q"" xG_~- /*9xjc}-qKVA0,öEQ<(!U~mKx0q|NS'u=!w7JZtnm.q@q$$[2j;'.SA( GWHLjЫBQiQrmϭ.)~U|\OUUqs"*K~wlHH곞7enI!!@ 0SV#TRix2)>H's.R_ cR (n?%~~klb=F0,CG %"3UR1/7 X|^q!|dCǞ,TWS(Uk!jr-ic"֜PB%"(e [hkF BL{@G0& U|F27BSKmNX9ߛ60dv! QOSMK²z;=N=܌1Ro52⑏6$@y˷l@q-ϵSH"߿7ó9!Umng sgge^u+EHz4hРAt?;%%~B"###";D'F&%FuHD O$=ZRڜ-kv;wJe6l-i%69[]Sp}jU3-kVֹ3^qY/53mغ^Out]'Cث  qqՖ:Ul+'H G6 4_<"Pwd<:aH8:w]uaJ4yLIZE_<G?VSbӗدr7ۑr9N>F|ǜ?ĸ6<;?{^x;7qѶGV6%$26!*u :҆FX樨ƈHmrWQD@ Z"(~c>AkM7 ^'B:t1[nCnjj 6k^ƛ[I9=t1>+wRFULw ?>Wh,9r쐤-| ^_:tmG*/ =n9kQL׽r;Z,v[ Ak7V6UT~tS?> Ǿ\1e}olBDB^-^(j)H?}.eiFщx7PReeܘ6\QozsE(dh8Z%˒LY 9sS?tI҈Q(%nZW5M=cJ+p "f^їEEr&D,}*Tb0x0=8y>zX/B9GV҂:?;Cs^fļy,z6>'R90P%=,F\ *󚍅_̐."@@cYɲNL=F#i SpN[49 =u26 961]J5.T0#{=ULqpGd 1$qκCR\PrYIۯLkxJ$J)%Z(!c4j&<Ìv'N JPJsr3vɞT=}ݺ̨ED}:5r :vؽ3ʂ΂uy1""Ja+,|n~X_eTUCzG3(Tr ٙvC~戜J?msz(Wވ.iYܐNb}]7(6e`x~ݿ6mp$so15mxڤoY ]wmM}rC4 Ь|)غvgԒ,z6mQ3|ǫ|%u""QJ(Ţ(z=nj4V0+ xYB$y2Ƥs]0L)Bj.wC4W!=uW8$62ഐԌd,/*ؔ.iF5+/kr&%F-n詮%q!yk-=kJ ҂GPS|*KO1{0tb;c%nMdnLO3[R+b-v(!PyП߂a(>*gTP )]|b}rɭ'ĥv;ƬC] ḦH KJ s$w H6a zZRS1)cuC{mZS-e)pH-C MDJ|`Kߓ{&(Lleuݺ\:(@w8X, ! HayW_Ĺ() #@e ^'"RImv7z|~cpP%P/ΐ5t(.ϔSjDb's{XSnS)L0*g((Ћ E=gsI(84Z2UhRϣ. (T83n'F 0]H8j ;Db)RIPJ%Ibܡ)$aeFJ$3|;i(1D" AF|MtE*|%>T8e˖}ldIDAT' '7:ff 6<}MU'/]Eͽ1YȦfyOnϽl@"m*r,(݃@,_yԫo%H]t?&9o^o^\x[|ZR B]{u!\/񮗷Wf!ĠQZ\XkjduzYf2RB3܂d!Fky>j8_]ݓ\ca jbPPt>$L SBļ8gNh3V?mĀac&N5*[s 7ϿSnWTW 7`֗{ ngO5bݍ98+sM.kKuo<0lpk+.6}B9޹i;]b]_V,im [pG!?WVqذaÆdNuU,=dě_=ۃ3F<]U%! i\RL5x~)_9icA5l!&*WBH^m^}ع_xBZMAH,C qכk]"r'ܴ: !r'=S6=T4Xm;KmXuO!}yOsaIqջ6|0ׅHۿm׾[fW,^] > e޼cSC/, 1v􉔯-|0 qxC[mc`$5kXY>X!zq3vKprJDUv4Uc?..r=ۿ2Y(׽nO~wj=Rܱ}SQJ)_q%Lz7.mG$[owt:tkn}K1nj\}ٹ> m|}rwꊥߋTZ2bEܵgogNԐ>S ˮWU/ڱg^gS%7{vクXYnZy7ARƩrs`j'D`XAZn`~M_uV m5*F$-w5"@$w[ @W;|xr;acy=^ /@WEk(_7y(B;yMgb+&}RkLڸuixsk]=YXh@[2)Tu ь>|AzөwV؁Zaޟ-3Hr ª*;ݝ':l¨kozuW] #  aFJ}m[6x{CXb +QQ Ō4Y?ԩ<08+w!od% Z9InI_o\MMeғۺh"'yiw /lnyC +z" !K_i"D"b"|t7eq7m]WAaBnfTlBB1+"{[wy;zJurWDX7`漏hϭGn͜H7>)6a ӵw)1{0\* ߼- {?P1qRc.: f~ɻuݪȬ؄5hĘv4#Mj/mS.47T%t JŵIN)/y':Qw庄WtcNg5UlZ 64K|ɍ[󭯵O/ˢ[r>_)Cmp-kzy%Uh"ͣ,:Og+x͈W:ΐuɛĻ Fr#?Ki$ pIF; .Pj;mmMi*0B7{^|m¼ʈֿ;? (tMO=7ߐ l-R,x7_4~˷ܴR̘|c|yvh0w[[պ1?=_ygȏޟl4[xM'lQ}x;{~yŀ+op~j}TKU\2= ےǞ1qYu i^׽qW\_M߽?c`?;5>|8 eլO>'#|矿޼fP)oڴ[Ycb,_!XCC;{Dw>3P_U0%X@d5\^'DP,_ouKi oeq&E$D ڝ#+IjQF@(U|]B1**`͊*`Pq?tT9Z*4-fNS}U._搏H2t[wPwBw=BF1 ZHclB{}{}"t}ؔWPxI K׿4qg&#O7g)Zx֏_pQf=o"n|EqUWNszQ[ FH)]]k}mRfҩO˕'?N^DBh {_rwx)x%{>)Pʓ)l!֦(}~ 8* ./%~֟J!W?M&!yW' (PPE* (TQ@B (PPE* (TQ@ U(PPE* (TQ@ U(P@B (TQ@ U(P@B (PPE U(+^vX ~^S@)FJ$BA@RR9F H#'Z}>R{osRRx ǎVkC;t\TA;ޱJ" i#9J) DDiM(0Br5D\$1@ MRd0FxfIgݟƨ7,ryÄ1}fN08[T.iv_`RJi#j&giCEoZs<}_ֈ&ׇl*#Jrom|(g_o;[kӒi{v9;`BY@,͇NMjPv%j.l,٪}Ԅ~ƒufc3\(yoP[]e #Œ:cvSuhZ[5ZʩatHQ9Pџާ2Pa (9}R6uчקIJ=~x, Ꝣ!:K|eA5kL[Z˛9Lj0wX Jκ͕6AX+!}DL0[qecHPk%z1!SQTY]XmPA݅@oBT7槥0bĠe6~TOLP\E3ear>:#3o:bO .}}/?Ѵ_bCEO4KsihK5\w= .avrPiȲ_GRІybaanˏhfǗ}ӎ Ìޠ3F2'^K#.Bǖ|6=eef,y|ы+/W:bxnG㛯|hmi'quk:%OÛ>_~jb }O-xş=?C+v~vŸt{_~(,ZEt{)) !Iqu6t6)˴ svΰJߔSa5A j=Tbôyq;GEa8rtY؜o7Ve2t)q{ VéX,ŏ}YP:h4"i> (S(R/h{KU7kW_א!lH CGwSw`;^?+FwvrVWEEk|)A[zb9U[;\OrӇ?{ʜN7Fg+I%e'޶/GL GnBmB7,xyte@RӪ"Lݻ )IDHтn]D)={W΁C"UGlUK"$gw_u;K J(𴑭-P ]vr"ZyeF/$G'g¢z”BY_b{1D5Q)aHYMFDa.kE'K#;Xa]G{zTBq&F@ 0 J61 5:L = AlxҜO#ðWPf! c/n~FBBlNUk6#%٤FHc@cZlHݡx :WAȮZʲBS]NNy0ީّH$8c&P \H׌8-).>..)ݲD3;sX s>]w*B@(Q"am]ӪuIGJwnq}u)ڻeMAc$%ɰ,TF( sZkjQN)<#5O$vK'[vKҕKM_b[N4Tl* @4H[ ئϿ8ɂ_fqZIZ;`AbO3zuO"n0ztI1ٳUP\[OZ~lt2W$"O-BLV lrd4͌qh1|'lӷSqm'9%Cm>vlQ"GP R/8s uxRoH>AQc;^ ZRB"fxw-[~ޝ,/.~ ukY^Tˣ])/@ZGp.u ©|/7zZ֣rWVaj$q/;ۭ." (E((0c埖 *" .KFnj|#V]4UZݟح甶X4U,ÇVt 0*hCUY$ܹ?03OENh"jDD0+L9k(Rp bƨJis Y{)zͬp . "NФ+D)C#?2IENDB`sqlkit-0.9.5/doc/img/sqledit.png0000644000175000017500000005662111714210425016075 0ustar sandrosandroPNG  IHDR hsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw\w]z{wcO~^kލ%EcbMbK"Ǖq܁7fHC6U5 w/JH1DFDfw Q%|X6hhWz>N^:IAb!Gba̦ϞgO絭 HK'CpT"H ^U*pv⢱w⋎4_XRQU(cDHV;S\{}6 T(pvc'Ji2{etJME{,GV*Y*5*@Q)L\ Zij2G 򎀾[[IݦBQR&zIh4jSeux@V7ϑmdAMz^Ȥ鞾+$R.ջP12kvbdI&/T\0RK {_}#vZRD\DVz}q;FIsa4WFՕCEW*fj@RGzZvIpmtpG)O#.@wa8kAر<l؏g7K7` p"bGmLR1o-FpsM'z*1;- ӳS?M>+p!VQzJ̥=\+'w2zV-}6aaCQ< 1XQN]LBN=0`GL2J_Fycgꖳ*3~#c@.QJԻH$,/h4ŹJG<͋l_m~ƣ^OZT*y|d-oV/}{ƣ~o"n1pA7-*+$wJф=7JƐ V*yԲOs@RT- t&}?lJO?ME!i2Y;-Gn=vfҬVYʳs>Ҍ3]³a"DH-9,q T*%6)S1\I+rtQc'"ڹ%b}[;jxiԁbj teE&XhT*J HRm Jx$oJ&%e3J'+ ?3^q~T_ѸLZ')!c+U!I2C/$vSdTovWq'Q2 !)I{!1-ů {kL9E3*8cEO2u(iIkYB4+SE?^~"~H)$. >b׹(Oo]|,"BӖMYpK^)}hTo~srQ)ШFI *e,NRb3ߤJӡI}-'NRI!M%VRj{[,ز~//.Q?XnCzs7DE#$HHw7[Ӑ2r ŋN|^Ƥu@s ʔ\yBC\9q`Ȑg8n˺Ex$D)6+ce!A6NP=՛h[Jj1`hJo/%ȝ}ecpܵWR 2IMqJ\Ncd2Gs輅nnBB"[2 vPp>b~\"͇u$$i?"-eY*1o_?|5'' tnnty>b~Zm[[ʈWBRyݦIx Q>Rr\ZXc4` +5#0oy/5NiT괷n6iHJWψi[5}Hk'ygr@xmID轉I7!% I)"ToљJ\<#!1̝?k"?G.3(n$OO.p@KGћwt[,ПRg2#Q3F~V FÓW7>Opo'(O³PBNsdV}'"[w͒qݓ ud'ǎgF} a׷Ku""g(S^J<lw$Imc4+F1rȶi:(l[mm7] οn9W!MW:pJt$֏b̯ۿ̹s4h"ՙ9kN?}ߖE~1y]'%Vͫ,XS'ӼYWg9b"2y͔,~\%ˍU<䲞>{g^вEs\b5'a,[F|ZbcN9?e=m؟kףRk\O޺|iωʐYavѴiuk+(<\]ԭ] {;FƉOcLꌫ3'?ZnEdT$1Ocdڴ_vbxyz`ggØ9_F։J%݃_ $*T;Y{k”cK[6{4<|'gOr|2-C&Ɵ)U%̏n}: Β|)+\dd$NNNڴ.h nܼM|y,%4yuv/QQF\b)_Ȫqpg4krƎauMǘ[2=iйW`=H֫9vRwĄl>+ 6vm[ӮMkS(S:gj7rv= Rζ=5Uëxwi B{JRH JH y󂈽=NNNjM\:s H>}֟aĩ&,c={N#{kh4Hd2d2irx=.89;ckggbB`iTZUk2i4 6=(8 |)_^}MOT(;~e+VV݆l27d/dYߔuc|4DNf]3zeCB>zuV b OUj7]{hө 57u^]oަUuێ\Tmx2kWn_#*RMN &1QABB<.NfXla}G'4 KԦ8{r쿜;}Ŋ1gmW=uڠ K~޽G4Zo>}כbrze3n];kna5:/\dWk9vx/7dt?/CGeؑ|P uێ8{gnۑUcݐ$,K(Gܺg+V`3a"VVTJ^x3s!Mv^m7WWM̧=z1qXC++7f$ 7l6iުM2̞>'Zζn\WvOkr+ HL;Bޕ~_fV/]Hrerˋv`lt{vgȭ-ɃE\\%vE^KQ?CNHFB"OÆ )\0.nHtt4EE ݾp".yƯpaL>3{NrzGk_eXx9kOgY '!!H;OVm Rtҷ3mZ7D"&Sne򥋩\2&N&Q@nełsݳ[7y4 LT"A" hh4(/\8olfZX8x͛7ѣ)[%veJpHE6}L8&booO\\Ԧ 1h/3#;y{z|ҙnӛ>i\@5U|<~%V.b8:8нG9[ӻgwʕ-̈́ISY0o6j̱0y{3p2 TT"A QTH HJBym YR)>C~j_ˢC6̚3E 0s\:oMOLLZ.J.'8$%KWԡ=s-d9h4f͙!2̞RXy al $;be-⧳?e=QZ.Gn%IX8ڜN>Rpg% 1ZMnhyK59r@4%&?-$WիH^ErG911( qww[[S%CR ^ƎMIxyz2fm2gBW'{QzUG~ABoԌ6:Qv-~d&td0\vKuҕk̝>IG&e}ʸQX&;1xx*U,'}Owsue;xo1d.H֫9vFz:nM[ wqA,! ͞=Mi IDAT)-Z7h04f׮]ԨQB޽{iڴ=W.!P3}"}>n5֯cLí&žm+Dt&Yu0AP+9;lV>^J%AahmäE,&mw߿׬&6%2 ^^>>E2ϐG`!0)*VR5_ʾ(( zŽ]i]D9o=  wݐ;np#GdYA*SN?)) RiBR G9tp?1K"7J$]vF%pDEFh?LL&ÓuPD 2G䣮/V"W,,,(Q,uξ'=q#O4o_QRyFnV ~ı#˭+W.`$~Eߩs~E {Bll^'qTYԮ Z|} K>ze2 *eʫ"X 2 Ȱ'x7fHD=0ѨyT*z=4w-E1mFϿ'ORju|KӷecnA (LϤ .f<zhk\217 ܲquneFF26=;@Y$$$b!e|?#jtdҲ] -^Ҷʥ+U*-XDj5)Q,'6.NGƯfzOgߐ/)ѻJ@Re޳7QQ:>~nUעdr;te0#GY(Mz *2gY }#z=\J5_r5KC.3w.]b ξ!_R۟}py.?CbŘ5{gr>=qH.[ ;bhFk6.\_#emy;fL ;;;&ǁudMkm9tb-c9viSqss͕Ӧ{lk-Iӧxi'r,ڟz;up4oO"99eh_.\̢%˸~zmx9k=ܜwfY56l :q<͚66w ?agFS˥ݟz;u͍?o#66r4Ǐq*5ȅ<h"<}^3Rl\Fñ'?i*O͚A>`7D_S;::PG'3y BP)Uܹse;l$&$b## eʴ|`111OyԾ^2wĨqܿRFj::;ֲЯ/>W^qu>WУw?""#` F Ƃy`1ÃǾ:zWFG69b^2e 6`YxA[FA>AҰ^ͱx0gD&Ϙk" sgLfxzzjl1k/E: 2GTf`gk _Q5k_{%2"L"092 w/6i4` XUv;rabF,2#YFM) VTrC =&c6.U~E :2 l3c,mu]=!@}^GG>ٱWvQ: f#,`WNwQ:27~M: ([*&L&QJbeԨ]20|h⌟L}̜?l@,7n&+UH &NFŪ5X&LCx7iޚwiܵ[~=4o ?ǎӪmJD&ٶ(b`ccCre>uwWj )e];Zir$X8{r쿜;}Ŋ1gγ=LdT+V֫~gN"˙hIN)GG̚1HJJ2:Wc.ܹe+ 8rh?OTÆ9{<aaL5G{19{F0jxF~1γtJN5ڥ3'Nn:SZ&woj׍v!/Z9r?rqcFҠqsiS&ziS&iތ;:w~WƎ]3szN*Ȅ:kQNmZø#3ex~mwo|ڣvZ~KO߰}>v̙xSr"#"yơxv;1pIC /2߸3qj YAmuQ#࣏?YԨ^-Syݹ`).WխYs,^85_nk\tE 'Zf׮c58;0mD7kSE1"#prrng5..]ahj4 MjMW-EVˑ`1x&OOF '..Uk_4yȮǏ'#փɓ%/ٳs{󻺺\^dg\\NpHor͙cb=wwWWO2aTT*}Gr%ӮV@j2֡]f͙OTt4Q̜=6j؀9h:g4j@+3lhݻORFڌ*MHH̚3ɨõiY=3vʖ)Ư%!!L.GG.!r$X,79RR5>ދիQHqvvҩ|>pjT[>T#вeӫGw"o葸Ѥyk4o'cFЦ7nԐڷK ۵%**ƍ-a\l颷/[P,Jz? ܊yl7kgy0Ï?^:ؿz'F#e .]BMyL}wZ?=ԓ#91@ ܽuF РNTI^WkAR`!`@RV^TT*4jv=y4F|.B @`z!*JLWPUh4c~ ͂  "K̛ren-~>X$%i7=s"I@!Z zũCfNN]sM`~s͠@ =^އfѩ@ 0 ,Q`!B @`"X(0Jnw='Q .-- cyri|foK os"B_W^qFzu FäI˛)SEU޹`6ca! O|7?q˗.r9vM6ڭ|YRju)Q4~IHqrvϯ(K-7epEzMk"_cOOR1eT| QɅn{KE;lf͚7̚5l1[ }cDGÜ9{V6cL"s;ѿfo}tؑիV0lP."1ިDZy,\ą 9{4!a)zue,qMW{3QRUq =ʿUچ'aOϏ i~3ww-[B 2Եnz,XĞݻQzifϝ;kztY- boovppK1r}v0q_ԨYeʱw^mZxx8퀀VXE$PRbemϯ(zeZA'6Mpx%r2¬EuBb2 MÇ8!vܕgxx_JlV">ʕ .^\r&(bV{ܼy$4 JR.;(""#=zA]6mfze qΝ} 4T*vݺ+ղOnݘ>}O<ɓ'L>=a*XtЁ]ʤISؼӐ3DV:72)ooo[dGIsqcP~=ZnC!'zDݲǀ_Tr4lЀ>}zڭ|  d16( Yh6c/$CAfղK>:57'< 9}> !!(DF!@ 0 ,Q`!B @`"XB (DFQ`E^L2BB ".N {sm&&h^>tރPS/y炅b 6Gܽs *OMVĬE~d(@>GDҍd2 .+~Fbb^YH`yN:TF|1K.ڭ|Y4P !ÇqH!(Ucǎ{ .\;wn̙DE5kiִ]ɗIޒ˗ 3occ3)Yҥ~/]c޼tee"AHi z?؛Y,$CNi8a%ٕh~WWV&*d EbAxxĄWDEhtdR#<<QB A,."B @`fwϢlܼp2ݺ@ 0-fٲHmԾd D(_!Mc'Ow[r66ԫS#!^*f4$}c}1dӖIgjըT&e0߮3k{k4p6oX7L~%ի5ܗZr˄ôrF )y#oHF ]{~<^OL4b@>֗F ˉ&S<=1yl֙T .|qAKWbnݹK||$G@Yv[4eM\Үx{c:RlL6|u_mގq#ҸAT |o^LMWڥ|Gg1v D:իxhk3uCkҠ.k7|ʵ|:ב yJS$>}6͕-_Ѷ,(_4ApYf[_9)ܻejr kҢaMܽijWrb%f<%rKBBzlfi=AA{S{wcL4 K%+d1,^6k?3,exya_oG0m/JUƼ ̂ٹݐưd:B'ݍ^vo ig>=W\vT*/ڴLJ |i?^ӲYc襕ӽ+cƿHaMoҰ&L'q(E;} LF^{y,4G?ꤵy!S_/Ri:=uW.:Rd$ӣ+}zt՛ުyZ5o7M k l j {OpH(-5[^F \`E7laWQk|cFsMv :t/>ޓ@ab߳ JXv-7}˚5_2ϑJu{9zDnmm Yξbbbe:7mVRJJ)e!]:Kٽ{oܠG9?:}?,J"&&FWh޲5uCuoظ.?LB-r9c#пr<̀>o\!Aܹ}Sf\*Un&_93vN_w{r\/ sDdC(CMOHH@nmʊ`Ǝ!ܹ{$4PT:s?kZ9aJncjK1s&K]YNz֭oKfΜY~*OOOv_~bt+Sbv786(M 2{*u{t?׷vSt1S=+A̢e #d <`Hqrvϯ(K-IOo%a+H 6eT| QɅn{Km$Ǝoa<<Vr |>h0n{2h y1c&QQܹsG ʟ={gN  qEΞ9MH#lll4yV~9ܸqsgpmo7x3x7qU1cAw<?mK˖-1(d"S ɿifVX/̝;ݿ֦oa6ɩKҼm?oglyAȝ{qtv 0(â(i<v; :ۡTXYg_O2ՙ""" GUǾVjժ{,<==yv;1xyy0;Hxyyh>qwwϒOA~ E /F4*P"C`.;(""#=zl8AJ%׮][7ѝ#Fc={Θ1o;9Ν;:tѣIDd$F.߮%JZ4j(KǍChպ \ѳ7wM2e2eʔz.S"Ei#GN:OCf͚e˖lxx3s挷.@`n$wCW5*a:c'O,*L)<+n$/Gصv]޼zUf3v*jENBVkד 7U'83s`9sygwFxϹ~?^Ɨ{r6lφ8־:y` zɶjjjp=iφ eeWhJr̤ͣ !Ѐdfeac $O!MRܸq bRS`nÂfh6m5Sτ7Uim%/MӐ E ))?so=c/}K eK,[[D$ ÂdaX, "aڊg}NSdh㛛3TD۷H qiqi}-LDD)Rdҥf/*ĉǑwDk"Bp87B]m5㑘0?>7??_b7*"[lA`` Ԕp)}-LDD)Rdht*"^i777ܸqCky_ af߲GY٭kKKK*"hޘ=,,N*=ju#0TD?L&QȐ;B; ӧO5 SIdonnnRETT9r9:"#]fp{$$,,~WI-7&O^bHThllBMm-W"~<%AvXzS3^)R)ri`?mm裏 3sݠ>oM#};vF<EbYr2%'+d[)ڊA8:C_2F )8j6t0^q&"2zmvO_v itDDF#"2 a |ǘ:}&F ̙3:߻1,hJ8q$ݣS_][CWzonBQawEUuv`PSS#y_#?7j=ѣᇐR{7'MRٙcTrX)]Ě,kdj~:_@~~M;nd4K >6[WWZnp^kiWW^2(aAF{5?~ uwǕWr;Rc0?JK}}}>2-"K20 C.NXW_[j477cuVf~pAg:3jjĂs%1Âfiiк+RSYbڌ8 ,KJ|=3ף???̌ 3B@pLW)bnQ4k43xxYlOC yY|&k\p!t(>ܱ _ӴS[Q痦p˂aX, "âD6R[Q|X0{X89b۶툈a4yyG!fMķ$ p$&-E[[.^lDQhllz1jh466¡-[;wݣ_dffgCER d~Q+* hiiOy~r[?xL؇01gC()By(,8AƍC#((NήarÔSQ^^ڳdN:Kp\ G΍hA0-WEY]t+5vuuu;vcǢq11-ϻv}3g (%]ņ]슠`\,||Gwww\r+._Cݕճ())\\\ ???˖'c;RSR$a!駟OwL}_JLL48? &&] Va QW_4,YY>gl47_'qGlDvXly V {swZ'Sdtos"Cb}&Ru}n6dz-[Q]UPo ,2*#GxzVb5Ulƌ Wh\p1yܣA0XDEE)18PMMV;z?xy^>P4?߱AL: Q1ؽ{@ϦGܧΝaAajVq Xr$QR|Z(SL>CUe~{J(++\ZRRoؠU* =/KDS%4Z ho_>4Ν;v <=/%JπZi~e qq3U"C 0yTDFE!ۦmېʀfy/y xz`ժWk`;}#++ Gl$L> W[j H[t7 ٘B̛E )pJKS 5HO_ ^yK? <&ƏG[5<#\t1,n@gYr2%'+ q !"YD$ ÂdaX, "aAD0,H° "YD$ ÂdHDšo#yTŠV7 $4_F gY/?fmb!E 1ԶbANήؼe+Ɔ-طo?~BFa!E ӶbAp1͈nxYtW ,2d  rrvEiO1b5=*tAdff?_A9Ĕcp*2R %$,Ud;Y}6TGN cKWbUഄ"CӶ.hYh-"CӶ.ht",,Ȑb>ƴmړ[ r0 '!K"C|ȞnMr 3Mrz3, "aAD0,H° "YD$ ÂdaX, "aADDXp"ӳɤ\G ƺY]XE/[JX7E +S\\.zxb޼xk7Fi]>ٮׯw,6/WlgccdTVP@kU] }}]7{= T+VI"C҂HTVk+Hԟ' &}(c267b;G~g=/o_89ju$dv܅ SS?œHJLDI\kk(^PA"O7l*mݰjmmpqq JJJ $0\PA"?{rr憸JwŪY\Xl߶ i xz`{`;u^% ٗ7 mEݰz,2DdCl'Y° "YD$ ÂdaX,EȆ#DQD@Q B״Qo^)aAd£Aa hBAcp30$n° "YD$ ÂdN"5a8|}^/6v "um^/$s7daX, "aAD'pjKzИAZFԻb3,섃Bc&q:^m6>ÂȎ8v"c/`0Dr`XٝA#jT88:FxFA{| A(1s\ )ܲ Q--mrbֹr"ZZ$1,lT}sq>u Q]'w\p7F AhxX GGDh |dXtHD|G H[ !"YD$c ua:A~G2,xxʥz5x66ΰIn{Me%Vt]$"$<'=üdv@`1[Ё |~}dIpw hye:τμK06,$mV)sf#{^ADSPMJ,L5Q`\ŋ⧂hzYhDsS~r;sf#W0h]')lي q3xȎ}}^E(IENDB`sqlkit-0.9.5/doc/img/o2m.png0000644000175000017500000013434411714210425015124 0ustar sandrosandroPNG  IHDRsRGBbKGD pHYs  tIME4&> IDATxw|lEU%[ nM  !< ! [BKT0n{Ub۷Μ+ ˲djߜ9;sJmuʊJJ)xxxxx5.bQ+@%qSVS{MRZUPh¯^Mjm[[W^yxxxT!j*w&_Zm1& ǷTTD9 %%;C8  R@z zcC13A~k!>xH %B,lnlG.J >B- ks.TQ` eȊ BpIo|D)uS,Na!8'Ka˙$i!""mw{jpn ǠR$iޕxxBD,V_S뺜J!D6UcIP5*+%e陹mm 0% a{[gim-Y P9.pL ks.q8[[װm[ewYYW' [$@O>E"Ź́P2ܴ$vtiϞs O&FU:skNcϿR'_^_7dQv2OyGN۳򽿿zV3O8jF:M,ӯ~Q3c[ s!23:2GrlY[PCN:qE;zÜ'OfSs㢥;G0׿}wVww_>tDg_ڙ=# N$N:pLjP~iPUV6G1G .c~v:Gڶl}fnvE޹g*Gϫ?~?ԛ~7>+qO6ھ{$\7طuF0]U!i)Jz^nNy랆 Wܢl_WgXpSc's[ {s{Tںek󴡅RHSs'/>HtaeZflo {<[o4ջ4?Z?5եʏ.X2e͎u{#E46FmwnFi-?0ǔ^ytn2hto?sž5O sб9x;PI]/[rR461EQ[[[~'dggK=c/@B%Y$.w$D33o\ncd; 1M _:&SnNcYYi0Qf& Dzݽ\%+:ۖ߸yBֈ_]~T>6=o'L܈cO, |ƈt"jB]ז%Yb$ԟ3SG \Q0/RDЭzg6׫OJN|㋫21hf{V%xoϛwwy57Ce>ww??8wN)s7,vUp8"r-XXRwSʊӈ|}BԤpg_2pu?w͢D[ڑ7hc.͟_yE>4޷?n7vOps-r67Bg7 7]^ 8Oo7{tc7L8cKwmj~?4EěqpP%.2?^T]O-{1iĘD$iB|*+y3\}zj(HdYn ي`fV~K1&]s5,(Ae8qiw]yӹ>)4-ݑ),8Al۽3|%2Q+,Pi)<HTLD!xݼg}~6燄Tqgu9{Ѡiʳb1G"6v?a oI< M) H%q]n;GI3P;s1,GVOB͈zW7_ o}K7~5\wTSu͌l*t)gT8ôfHpKf^_u{OEwV4VGQWs+;W[еt/*8~צ{,m\BK?߹z΋<4W zHǟy1|o&FecJrv:* LsDΤD&9>[-O=<oeUeFL;ٙ';s*(@HE9L$Y˹p7**,r̟nsx]Ғg)_ZVK+6Z%J 2w+qVQbTtBiiAyIQBt⡰iygm#\_li©ꮻߖv͵S>za/pƈ+-/=Ry=׵M?yِ3~u/ts`3~sBvii;D\~G_^|Ve4y~_f2aHsĢu ƄcPG|8t9"B5h6Gtq Q.EJ*ے0';( T5;bc$Z҅d1ei+'ID2 &(3.Pjlغ)\qfIC6դ-'` *+1% 3P-QmAtI)}E,Rۯ \IpaI6RBX2XR5fQ qPJ\'.e&'@)$CIJ8imǤ4_l9gblͺI"xAC %LOٰ1Q@ ؾs^YU=͑pW Wgeegg:oȖYkS3sI "'2|V`d %vLڦb&;9CPB5J'f tp (̀SZf P$5 ?ET[c_"yAӯ$h :u趗Ah꒡QK >%2D)ntU$_@PJ$CUMW#rj\NP1R42Hh+Eia"BpE=,”!wR@P LWuP.@ V7a7a'"FcIqaSqKefS 0">}ʲBb_׹HӴ fB_+F-6-QmR;=crfnN"Vt~'yL%9&O1 JDGU%CU1&M ʒw?]ZY٘~1ZpPmDDbdmDdZϗ,lb= 9GkO` "Qd"$@Vf$I]J+!vp<'@VfH. SBm:!2:V2b$w|`S@R$Z'hA>%WUG #R( HT$l$h2I2 #"2 (\"yOJ% 45s/u^ PNQp*!e~ wA/Մ.|BHՑ"־ Kv{ O=|aF7_Dv$|WٹޗV;@ ~gMKHêp5[x~ћA1y6gT~ $tH ͸ O$e1B ZXetNݳkI}$QR>1"7"STO<7ldF wS sX%9'h4LW*~]v´]LV~8h\{DOM#0i%c$G i #R`ƉGJ R^%fs/'pN 7֨IRU4MWe:TՙPt]t]ԯdҤd%3#nHt2pN@Oauk(_cF4lE⺬~/ArmV:p_^SLp~M"͌E4G"qcI%LSC-oO\S-+-r-!\c˻o?_N-o`{`YхXs -qcm;ݔvމPR虴c?92'L4/$]|Gnm{XVl֛.ݺp/ivN=mƍEg b;#CD/?7/'/MK`=Esŧ늮|!\5I=d|G_AE!oܱO>|[dK/AD" +˕ŀ( 2 _4L m^ DA~R`IV LHr# !Do>Rلo*(iDE}}N8 D@.p5a8$!^c[kc2l"u$#/;1Y&7q19B? )w~M&\B%Fx&>YX6B(VWD!TAz/mxZw|,)[¥u[1^7ɐ??Z8v@LHڐ1cN]rm ?mE }r AG{LEB} 8e D]{fb FҽBuݵ93;視ƘWF(L;EsH%#BSh1@ff=X$CտX?ԊNή]sWE8O9[֔+m2B}7šKgy{ *JI@Ӓ/۬ IDAT׾0ҽfJ" >fOkxO߫Ee>Ҽ!!:+.K[-W-Z 8U͛꺶֖U,_*PllXi8JO7.+.>rFQYj_8LKwv֮Q+dEv[RY-b|1o2R=g7c.w%I-dů2W )?OǏ?3\wc]$@z.Bb.p\ W]G^GFS.;}u+O>|ڤ1@P|>4]W*use.>M7̗Kݙv_,)ivl8gغ~Q'xßw~:%@$y_yb^r09m软_p_$՟: -%蔥Waןg='_~5&SYtK,\)6_B%-2Թ G.y/[ˋL J?5W>#{m=}|[|l q%6BlaRJaooY<~;: v@ PY҈HڜrdI%BkY?e|jRDz\Lm@(SUQ"\ײ rB4]YWlLda TbZUTEb,IWeqPLڢ,Z>MrLHEaQp躎E]MjGV&K(%:p$LeDQmZ !J*+ EU]L$+Ip*I"(m90U_&EQd q)$..%Mmآ}[YIhXBi ɲ*3\AǶȊHc\RE eٮ$kg6k رw,#9tvu mW[Cl+ ˴)' j? y3-* }v>=U 7t;*&ؖ ];t}?Qkn縊m[kﭺi:);䦝:C{% cNesJſ+$9iT$s\W{o4=˶s\yυe<<<;ϖ|Iʒg<~0((k֮}?Z <;iӦ:cx!s=~0qGow-88NYYْ%6l$IN)8e""&kAJTXX~u9`'xA#u1RU]k{|:#2c_~?gܜ?l$@ xE<>7d`J PI){sssqq̙3wշ|Ԩzii4<'e頛mrM;]L>Bt?`}w!\ Î*yԶ:MrEڔ-!ml a%oc&;k^3,c,ְkO9n&7#z `y'] r!uaÆJ@rn'* SrF(# seY6u"f 0ۃPңwaҎ ~n n+84,#:4fee9S[[qʻ9B}󞛮ʚ>]^D{ziiMc)MҊO6G. hrĞ:tJ=G?nVo&5޸*^}&. &Lk%1g9C}SϽ'(sj@@$r`0p.8݊(/N"L`3G 1+ٖmzr)ASQޥd#b$ްa=":ǫk|=Jw"IeK%iiGB @uerN= PB1I}a-P6sxa4T5%.(%>+n[w]чC^N!ڶ׋K$L>_Mt;h2{=q՗o=/&5*S(ISW4 x[̎S>݃F0_/ &qv aF򧯽UVm-wh^q8M]!$)Җ.Cr =<~⎈s#-nm rILcg-ݼ݊[e[7DDDtDZN5.MoOr |x۶ʻWex.vPxrqp,JX]٫⮻upyy΅t0Mb֋ 17/kŐ+ナ|#{$ܷ4.+)fL9 c<{Ql9جH!fKs#]嚻7K+QrGyS_ؖG& _wY e'>/Z9$ 3n,$V4씜ܪ*p%p!ݸqc>cKkqIÌkK>mkΝJ [@ 88ۈx8fvh4w笾j0 B 3$#1{5`}ز\+?,=P@E$4%;jъPbR&7 L ' qiw 0-Nn^Tݛ$Iy0DD.1.E|:nmaQ.2$4|D" `")P]jE-aߎZ󸁗" Bz:t#]`Tɯ2_@Kı~")(嶺 Ќ,vUnXHYjwk:jsk(3*ؗU1#%LsW5Mf,X7Ph²y'"DwF $J}{=<~ž{4}~Dm[Ӵ|qAH(J"t)eP ^:!D@)PL`49N8ܺA=CWX,g%&.+hƔIy.o].QנggnUfo}k9oV[T()꾉sR&+V\6DaA,*1$3R_YwCTk:G-Q=c}l.$|io==A+7""x"=<~s*F}>i$!8d0 J뺶mG#D"Q@JUU]:[ǻ-#ܳѣ:UREc4?gnҼ#&nx,U~ߨk.G81/>6֬!iC#ӳCneA#SRkB.$J+_Sb9DnK,{"l3c$zc5ʕTm&YάH({sf}Sxc ƣA͕#-9{xL$ !EL(횱ds7iZ p>{EG0aպMCOMdҪ Zר҄jWvD16T 0  !NK̭e8,P%'v  Gg? Cw(q>K6$*߼+TD^[{ѱV˛wIQ}Ҟ^vvvqqq^^^( >1&,˶Tιm[l=s\+dG>l,**Ou]<3\!@aDihˍG3 ~Fe{-F!&禞 4ūVHrxu#WL{J beuywDL9w@}Up۝rQ.kII$IvaF!u-LILU <粻XahIh칣1 4TZiYib^z.y1lذLMd. =JpOE 귟.}i\ˆQd*TzfD+h4iG<}=UdƩJd 80 Gg$‘4In X͖/zk}5 \!l3|1i+}o9O8:;7}Ye0\tmeL۶64+YeqIQ:D?^q;iD(󷤆 0gϾ@K1y? e}(MӼ+Ii`v8^,w@3m. X.4Է6tdM5odUͼeɆeLY~܁O~AwIuMF fv%7YU{{g3 er7y1*!1&s+jyx#w<^c$q17-eRLA8d7G?swRnkDxHQ`Pp8Dq]ZZ `j ,Ī dY Ro;q(Pp.Q(%ztBDA"Rʀj& ;LH2I1B(^GcҚt7[3BPf!ZLL7/T1###t% 4N B,۩b~t/UΚH)=ڂT򇠛B`wfPQ] =<<~aj5~#!;bj!GoÛቻ'{xxxxxቻ'{xxxxxm_"oT2&ZݽM"ƣmW/V6`?{UMGo -Ov;_Bxwo{W-_iۖ5'?)5bcޫc:z[lc^ F!")0Ex_#wMVB-\ʅ㝤_@?:-ӃOz3 <~VBSbm޺nkv䫴QmB5'^>$Do DݔAį%GC2Nv){+)Ny׍qHW_zWO^7o#BiYag'4P}ɒD(i6ߴ|ڝ-|~ w_ȟr [ v^,8nh/ 6#9w[wdgsUKvc{˫?[Q憐?*/J7/rg7 Wsd[8nw3{yǯG+dUukÝ%%rP~ 2$^鎸/ʤ4Jejc"q%:/ wTĔAͥ5kwTA a@EWșyEBÅePS`=2pӜ9??RAָ';z#5RV~L{ꙫ@*ADoMFIQҗxG+ǶTuTdݜ;n~lv""=r<.+F̊%nGU[?7k\u5[JPD9yyEE9iuOh+˫69g%xӎ;_nV=s[46߱Eaų/\j-~0Oor ͳo|Nb[`Mor -7~NRϽ%QUyʄxdKCr[QaÊZՍs Ӄf:qln4oȔIHIr@gNm v̤>!bo_z湎spa>A>紁Y կ|74X#Ewm6$s= ˟<Ei&D oHŇ]q3U]r cFJ]X2N {-~4zލNs~rCӊ~]`Kf޼dϹ'Sgsd{Eu'9G$"Z\5gL9vܚgדw@$sPϺܒi':a;Z A3]kQ5'U ?{'E_UwO0sXr IfOQ1 b:x#IA2(H9-sމU~"} *~߷{U]eik%A'u57|~dcNf&LttOXF19+4̲D8V`h _͢\8 iA#b5p]. IDAT (hDZ0`X]ejnRJ~p}m"5e-:M(EũGPj6X*+ڜ ͯBB# zj]ûx׏!]w.{i!tOKc+H.HXաjO0,7A 5TuZSGMAY;vV $w]i]-6wRJ}ϝ={޿9Ge}üY1wœgmqdۏ֊-Ch,!|ZV['iر 7F06gR9K8Pmu>#^VݡWR0Z+WPdrc)]+4[( A63,2ZfM޶sq⃵qFtHNM "յ,} u5JkND&m•" :UϪJ"NĽ;7m(I@ r7ߵ'g;CT;9v{z8AQQH*LV ۫e.2#^R[7EqǺjxR+u]2I.i޶/>=4, ȹToڕ߹G]D6;EGs9;TV(2ƑPI qJD !3.P""4y׹SIsDL$ɔxJ% B2!*QJ L9cM.Dz* ,SԫGI @()hL!,! qT()MD%G"+2R+$K@dL:[1YxN-iP[p5!3%8 O{x`Es L4 π7B1%>VeW zsθ 3A߫\31W~m+ |}SHr-VK#$`"m v+(@)^Hu={ r #.n݁';rRJ[IcAm#lg|TLp|a UDKLIHn\dBδE"zO<$"Xp ˾o9+W]32{H74T5`L=ڥ0]a١3g,> h۽k#LS^)C]nQ9E~Cw3䂟gcڳuf!d (ܻO9suF;KP9κe9G^ lp*K!bZ~#'!@B$} @f6gݳ#;^ l+SV潱SD$tʹB;?ppC͞0KȠx[_O_摗oK.fl.Ry#S:|Q/{Q۞ֶmqAjmsuص6ίrcJu}jDfNĔ]%-#ڗi,_9&PK}_J-6'X56ѷn4}2B# {@.]8={RJ~ʢlpB3=tߤYn7_* >~r<+k2\M*77;z +( ´xզm٭zv`Llq!`Ih܂4{pș$wj"k3vH/\1g5:k~LyLIuiDhx+ҴYmxw>;9:~㍝FHupװV93u?}\pTN?lԱ?9@<юa%e}{0SyU:[4 k}m|Hqwo0o d rA;z]v ߽}R: uڸhVz{~`O`/q|?7Ơp3Z׼Sì7t%Ch [r'ܽ7>H6DtуDTkK0i ív7i>u%$e$F N2$c\ٝUxI5?/dֽ*I~d'4BLd~{s)2MbjguoaJ@.<1EG'3MZ#jK݀b39k]CU L4>Z\ӹې0$[ZK%kFCx f``J jQ+rW*io?"\|fm[n⏞>'"%'r]ּ[g|Kz4@XMq]hqV^>9hP$$JROH⣴Q4@ϝ ߡTQ ~tot,~J ˊM%Mf=RLEDI3 g/!Qa溃+I;G,QˊPgj<SKhdőⷥp(Tڥ['^SuwT{_-Iֱ W"ۍT_o; 2F' ?# ## `՜y@" Wmeӟv6oRgNcȖ?˟4"Dqַ8,0x;P/ J)h!Əyexc}%e@ϝF;S$yu/]:F_XLm5!Go}i=j7DqJRoHE&R`{@.0>'P`qWw(m]:CFzo1Ψ0ZdWr`緟qObBޣߥpDOeg{fe J K4M;-ɊsUu][֜jn"nMu=  )W$#d$.# I=prxi iL њ(IxMub۰&m( .GD5iF- YMuɊT'빓ֺ7D ,9>rNPSKmx|]\ $7f†4(BMµڄ4;Qx2Z{G)DNڌW=MuYT !*K )jl"@g `;@zټv"ޖna Ky_coH3qQi5)l5xZ2D^ 4'u6gM@)-;BxsA4b~)HULcwb$4,Tu*6}WR6 A@}P.z[zDxB?`yo߭ *M$Z6\iz=@HrQUo_!/{ Ө;\:J%_ӆć!mGOfӈG^!b}ف!' > ('eOzFC&6HW5$g@D>``V괫.GphHfP?@|/< "/򤪲eFdKaD42!`F4 z=WzQ <}  0d~#lu89A}8!EG S%#:.)@^}ݻW7rD}GͰ'$zEr?һA?(/%>ui-`bg3ٟlRDC(zCT~Hҳ}V z+-c?LzH=ԣt>#O7u=Z  (P o4.}vY^'?Eq&7 JCBM`@ ޡU |r{BD#li z&QY#P Bu$Hg)z+MM8㾡c@GD6,uG^no^Xqv~/Y7xoC^ϺpG G|MEIYiOr̷ߟ@/l`z[Aæ;vJO PGg:{H7j"7^(8l&>;uCy c~FW.77sZ!E{P??F(AЬ^_xMlh/r! ??M/5MTg[<6Ao$Vcz eYa.W]a$}&78C|Z9 Z\!c@+7Byy}>xz}7!Nӈ=#n# `ɟJtc*wsnي?zM1W>&*?xpQi<94|;&V3G>Hݜ3TECF7R􋄡@o +Ux &>N}x(0{} FO1V"#xC8zCˆ~j7 oxVOx&z$]mTHp}s厀3 %N๧k׶S h>Kbv\7^W[CGR(Qp΄_ {eAGis5D l)mG@;4Q LS҃o#ڢ}JpX)k-!b21l;gO9`?;X̾W M70fO7C{8{eɑ-F_60̹GBPt`1"))kku,R Kkߐ]լ]rg6vtg Qw2^؂GՄŲ6ԁl2&_C؄h@j/XJ ܯUV[rSVmpΊ-г IDATQH֯ۚgca-۸\s4`@ـ9uJUA;ٚ?Za *s6]MBes.x<\Rs&Gѳ}GC>!8kɢ"?Pp <E k wlbg\!=93l) Rbz=E.>gim`Ǹe?HB8?nۼżSv% >( }m;Yѥ ݬ\tƎ#Fv ^r]צ2$3bu-E&.>L|vQ=LEW=ҶeYZ 9ZxШMsgwFk؜*#|quHzU,~)v~ðV-+HIȌ1q,TNFB~4xZÈ "TQv{^=3a?Rå?V=lVQ[/H}^+wZE}|YXSBeiܿ-Ϧ "g式&_ݟZGMnEA=ZjV>l#X҇nӧsӮolͶo딢p&;WVk:NrBs>4m[~e7%tG׌_Emv).wٜUV}wI̥c/fp.}wCW}(HfLJ_;qTâC.CD:,~oY+:}Ǟ6'(ؾk"e{낢ݕylMkjbZDŦ5-k*K,c{ l \ qԱCkg zl@5\Tv'u:HfpEz+&%o3 Jb}VqG P":iD\Xd^DJZZW#vk0C.8wIjWI]fcå6ûپ1nm[b+wM9qfۏ/+VᎭYe}:;I"TK}c)ڎ2/(͇Hצ%}8ڏӉvo{!k-"d"s9Izoٽ{e67oP #.  =* (c(ܱ{֢Wvg. TtJ \`Ua*G{ `PncFƅxcra3r*, ʙCp^Qpk`yuAJ#B,&y(6%QѸq/ʦ‰@8"z>@ &^ݣU[RSx$TèZ[΁Z"bΜ48z^!(BODa q:!)$-䖭,>ںS6ieޖw14ޢn.a;K( " @(ZuYZ*FA['@ ڃ*2J- 4"2Dq,P#8' HZZC^ͨV] @ʻsEp0DMu)%Ԝ"Σy T 3j-3 l [h}K& fU9E*"/!hPd4s% J$>L,RHMl6K$a"+DǶ%ipeP3k[2-޺ōcǵPHߞ}YK'd1.B<-ڎlIhCv1rBw*ŎQ&pU",QsX4W4@| 厜"D. j¼giU’*3M&A-7eTqd9lߣ5 f^irٕCk%xϢ훮BP 8g c3 c !gqSRךm5ƺQ}¿7!2;(J._/,\Nڣ(  @Xl୛T"Pn%S KHזXkc- 'ӹ4.X );^`YiHZuЂ8g &#߽/?|ںwlLڤJ3ԐduقRR2~EUc3(g P\P!(w Z-[v[ܙm7 qׯ0$e}Kw]2&Fpެ2@C""55QR5qbhoظn1͔'f&DD|sĴf2:Z-[\ߵPrM㇧}<)-Hj\R!]9gk*?׿xhZf+J۹F_}4,ҪC(\dvuD3Vפ1{[RQcF{B|(ؔ0seCR?vnaR딤]ܾhܪ$˖ֽ$/C&8$|ܰ -2Uu>qFAv9׭\-U%;Jr sU5ܹ5":&H5ŕ LQV೮(BCjk|+eRXpVVZ]Ua)p[in951G dRjpRQ;n𚒒j''RhxD\dfr Q@]W[RG &LIXxbEv %.>jj\搤H(’*'LAI QjILDE~it,|eXQƐ(U[PA-ZUۢb" 6J)26&: 4WQ#&&BhBC "Әl%U.j d0Cqq5&,VSr{m*'[(((w0f•RQUX\T,Vf3'%E='!ZĘ0 QqV%aiy(+Sjq[AݚL  !jͫI AEeU6UHrdl\l +KkUЄ0#(r{B'̞_Pas3Pq &kJJ(c,4A  Uea\WZRXD ,I.a0HuKMI&6B5,9RJVa|…J8o2I%t"BdXȱ"q (qg/$TVdvcleYܘ;  ye#0(=TP<~&%_pkx\9ggp L{As{E?7)Gt7sd!Yb['@$q(V's'g=م GCa@&@C p0t0XBWw* G3Ä[g.-wk/}㜹.8#\4"wݾkmۡ]Jw% RSQq<$ L6$I& N4Us:V* !+HTS pIe361\rrĘr٨2a  `췚9'IR/e988oK\.f:9m.1 7؅1&rJrRYUEttȊкezQA>wqUSQ y"r`PGye:vX^Q١#sjSR <@ Hr~ZJd*,*)(-/)%i))UA$!X@ RS]ǝv0;wj4 qpDHttl4>թ䥽O ' _a;cC{ǹC~'[S 6L7$O&g2$Ir8aё&p0G8ΧI!'{khop(U5A:>O풃9AnGjuf>')k6fcZp7P GUvCߨ"⌅ #꽣X!һ^wL݁ůj*| 3U#_h5Cvxs1SBjÔq7}meJT1]]"xsLcy'4W"5g՗ߎD$0V1? TX\k^z8ә^Kn7o¢A飦@F99r'fuS}62ާ Vcѻ,*)}~a6|6+X=g;.jͿҴ佳ߛn6{.Ϥ`J2Y*˔Ǧ#7זAojV͛\u&m:8s:!~ʛY SBE΍5);FKummFL}Zi?X7 ΜM?%qѫw\ MaK|*ܙe-=fכֿ^UcxVbo)Ý1Tٻ,*k|[8'U{7=rkDJo^ 9]ֶ7>X83EQwYy&E=v6jGp$zDaeYTXYueyEYTs\C%HӴȨ(t:O)q%2{?KuofʄcL vԹ^sﭗ7%ֽ^?p PW%tyAl%}BUk~'{dZuΌYK+D%}uク.}.Êֶm/j ߯5'NCz(ó~XI+;Gnbt?64s@G;Y/lúl]q#;DozmG+}sx)WF..;<=tƒO «_Uŏxzڵ1\kW+ ɔh902cԴ&[%q-=\bpjCZM?߅e$jO'61N]:ܻI ZΊ[}ẅ́K{CX6 88g>1VmŌҫ3g%/F3>gIJbqL0"1ZI1`S>\=aeT&EUg2ioFH͑?]h{YL|}N KU&}|<ٻtO[< w=+a?|.+签-\u296EO1,+VrNA&1(u~MyWN}"|}guG nV~i7s_RS3/>>)ӻYQ^]w*Z'~sd0LosʋxuH6`ȋy},SnJMY_zai6ѫa珥Aڙ_>u%MbE,ٰ?V>y=>8]vM~UG$D)\[9}c1w#9]ڽpa7@R3'nYyݴWO^y9;}vFm|@|@\Oz99%>IR̦ MeN~ܬ으’ҲҲB%K(J-w"ˊ,=e1j?|Czg'\γ^v5[w9zGm/[6}^>l}#LJ?Evk44i/U/uM{N<ɍɓ{oM_5|#}tG`n}pgzVκ J}Mop7"gγo\ݰ)_٨zkEO{γO9wrӞWN]R P>#BBSڥ'XafI㣜A}0Jm/4,7?oWF)p}c&$E}^Lqnҡwhdm1u^1hxcVޮ~kDe˳A6M(/"l IɑGS`??yƠO0mٷ/{z>f=ErZEum4/,zvJl{]ͱE՝:yZ0枸e[-PBzlıֺ#AGްedh=fC塺_?ۚtS{?7J}1g&HۏnW/WMO7nr=Cdp.}k!2Elsr#zo@]Ztxׄ]lv4g naQ3UNw䈜&$wP@8)yzw IDAT.CYtYsD_; 6V~2A~vpw^ :\1ĸkuȔxmӵdr!#*::*6:jU Gt]:hP5svjr|M.졏vcAsgn}0ӀݛkRG*k"^sd5B^׽zſuP?W9oM~['%w8m5wWΧ7q2ξw.m$ԫeY&+nkzIw]ٮݍg'6鿣[dPcDnlԶp'\3H%SxjrCRՔ JAmu3>ya@{Uj,g?%M pZ ;4 T)wv 4~mB憾SW~WLha[tйyq}a^\ gtBԇfς~[5X?)qajbɯk^ØqҀp}S+@B$Ej6qTʱt*,c5TtO, BhdWOWL}uS,.<8iĉ7>>ip(ޝ³Duws[:EJF퇧ŞvQ2@&Y (eN`W%@d 99%I Q:nLJ2sVT3ʳSR^%w G+H]k./*w`ǟr\{B $H ti|a/bA?ElRTP Ez jm)$8?4|gʣ*,mְ +C=eե]oÊmֵ<3_8KHM]T<_ S 1/b~J~?\5n1@ XPW? 7#0]͝%'յ5BerVl`'O,/kKr~(U{ٴyBb9P=h'=ʞGqQ>vEw[Inٰepr+=1r7/L;NzV y-|'McFra_9^ e?֚rs ?]L0fZn2@-!#y-sY|64#uCP?9&-{|5bDngr}e`\h:P@6e[7e,\@@Ffj,1>\UvR֔`֝pa Ƒ.SI3jpiI Awj:!^RUz.Ql H{ć-<42WX`/]qoC_|8ܓqJv7?5O6=_Mzx柩?o\ |j>GDHkgY> \3c~?)űb}[09R79D?9YdAC;?`ruHX,kjEXĚ]YvQ@C5]Ɔ,+c|薡$SBC%#kL2Ԭz4 FO.{OEyg-N|qw6Nt_(/LO=>` *e/7L(rX9^hOk<cOh,p"Ć?Ge9%, '5Vu=5?( u дsʛs K%vɲty }W4%Ej2(abxhee 201s9>P7jpLt'^/M*z>qM?ە(?oq1`w;WYπWŮիaV tw_^rs.閻47urX~[qx׈E^ȟ 0%X~Ò(p<% j~Xx޽%7mPWٜf{$Zm9,sƜr'>3+޸\9浑ڜ>Ia OǃXԮ#mwM/Ov $o7<3w +XTc3ѻyv~4S,^q/YxWl[RS[:3(i@/O$3g}h\]T)_=Q/߾dgA_ԁ]GFNגNq] 1ē ط'-2|<p۷l9^Ue_Qw=@siqWaw0D_E}tͱzWҼ}'gyIˎWuΟԍчxO-9 W}M7b=/ԲTi/|xׇ&(/nO U@HԜ-HznEq#M%oSVJߩGiJW}xW_~,Ԯ4]~LiEVyҀ~=)Rayv1ݍ>Q%^=Hl9V'ϯqyu,/z#Dݽ>q5[L~Q^4wt:фDGpߞc՜waf%t6:x"_;js vVcU*؀n`Uf̨㒻z`8| N2SL-8^I D`/ٷX (HP_k˩!zǜPF^lM\ wfHy(62?6ogc tFFFlll5*/-Z,VYQFZ_P`Z63Ͱ yر7Rݿ E 5VxbM)̃rH%¤8YU!,M1cj屽Nuc~{%;ؑqp^SKJNp]Իp};ək/-{VQ͗2wEnsnFH\.qTQdxP{<$K\&{]Wx [UsUQϧ hA*8pТ{kg`tNV]ppF!VuQf[u)Qc{{& R=iepĢɼCSzk#jHe2aoP]z&im٧hXpxmhD/UvCx|OZA|dʝw#Ow#MSf)5U]Ʈה9v- Qcn{+{')k*{0/9̀QV}J3V3?|xx}f$cRv3sс'̒<[(Ϭ%FuZa -ʤR/CevQwh\@H3VّL᫫:Lnr]zPFHRes]!/#@v׬;.D J[ҋ.9Ͻ 5Zq%X6m śdZA 6 q}-fsmmMXHhTT/?nYK0.]ff~jڒvEE[o}e@me~ݞo.oZZkM˪׬l[(Ƃء}nt)oyn>Vm˓[XK{iR/󧝎]5ehh-~qoӤmJoq}F5V_kJ7t$yHƲOȲkҺöPZ^Ӽ\-Ӣ_[Ŧj~5:]Χm!OmT#4;Ij0 j͒x} ha.̿mh_x2Ļ%%M*F$ʣ&)7jJ[yk-jđmaCڨH-Tζ˶׋_mSJ ֞kuiMiOpwە=s2P{.pQJcïA{w[I.Σ-5hS;2&ZcZ6m痔T^XȻݒ$R<<<%ںu3g>R"f~I7O+F{zWc{z/eN+ȐAfuF˯Wﭠ)CSNS.٭ִÇTk=ǿjaa VT}PJBm12q;.QETK7g)) 5}H+96驹1[2_jJf+gC:rLr52Z*E7oM>M!R(P@Q0 $J%6*%[L OO=Yv.@6-w-h$DE ڃUؾ{ǟvRBTG>xSU_B r JOô27pp{>9ڛq[G@7=4!*R͟z'~w]J'=" G1էΝ Lo5hܲ;~sr?oά?fU5-CWMvv|eSQw==3/_/x׏ ˾e,=o xzk.%@*;˒UяьxE_;k>r~rU\Pҭ?婷=pKJڜ]]_#(#`]_5CNlw4D3@o^r7ykNp;Lh~ٹ߹pgtBӟ{eޏ'd8pTnb'v+siɳ׍:~ҼElIWTF*ܾ=G~ݍ?}7_^Myw]'bth&zl9U?Uwg>?o`wY+o61n|8ckf,tت0GjBnnQ\\LJT Cc-;Jr<~M@HaJ2:埦;2!q;lIS x پRiL&F*+ΛHvH9lݽQz oUl}~ٻ!>9D`Oeɽdsr "6Z. $nǎHp㐭2{=wϨ(O4zH(֞!gs#RiyCҽRa&C~Zдlo9ٟI1F/Z_\g|~D)QSlscRIEQ0vmN 9Lµ7C>4uwě )69RLށu yZĴL<;pԢ,+饂jj,pE|Klr`MnMO# @֣yOyj4 10'̝[Ę:`Lˤ.78rnYaۑ%yu?V$4xX/t.*/k?|cu[&"k%^,U$ Abcī%,\w/f ]Y?#ǺeqN8:@ڎqe,M~e#o ;y>;iլ7Ȼ3gB4w$a=8in-RL6S7tPɩ?l ^b,91s2o3 F`N)B}OUv[=ҳGϮ*Yۂմ$ѳe<" x]3`pW?KCyUWB-GXπqK.I)4n {p [JW"h3.\/c*9%zǎ}~CfvM-:"<@p84}&7 * 4Ee+@6B-Ǿk3tG=զME>}:&&Fsiihil63 V/Ⱦ(oȓkq;E᪜csۙ*"q<#{սD!a{/ Еp#lu-)!5.{)bއ_v^'y7"z0*_!,f}Ƅzcגi{ k#{͸grԛ&}ǀwG^/cDb3\.âG_YUTmJi/ܳOYgᰇ_xm).RPyy̬"x*WZtd9 Q_byER*2 ݔCHbU~faMB H?EC~0x{W}b@ -9unIg nn#u%Yf >AF2/#_xTI*/tRh5+,=s(-Fe͵bҡ&!-e9rpo̪uj<#\B }Fkˌ-?e4ax뭏J)a1~VX]w$/ #I!H|׌^[8wے7q~KPG^ȳ\Q6?QK\V2I<'uӴD)_/BtwC$=|{JBIyӏ)h-zci^=n`x7&'y{@=vac酷M5Xy}"Ib͜&Ob]~p{Z%E5+}bfMK'$ 7E=O^OLё[|Ι/aCjB{nힽ~:7Ԥ<=d5Kԃ >wOLd*!!f5ߟVsk<̲S#{lY/%ř33½|'%+_6qGbN~k\Iiy?~UZ1%Ej%$HGWa@P6w ;t-C [ 2n+O~~Κy4k ҫ!KA#k PC \N+Nz>գ%u^:~(k]Nٖ{=_l̙.Gfh-]qkyO^?ﭭޫ}k|֟/\}tN 9 # hg 9. |9Ds]'1B߲|#?%/]5u%#OL!$=(?Ob|韬ϼMOgc|oP㪣NՈ ^]~ojq#ybl7DgVx(δzeS)(?;ndq\+SId }ELKBܩ<[0aU%.-DJ!N2jt/u:;{8P[}\=j>/HI9ΊvJI8wg+Ȧץ(Y2{jqG m?mc !QíG5>=r@ QP'E/2XM; vu䑱* Pyjr;#*S C\1gi5U|Xـnjr>|q!& V1 t_wG8z˸1]TWxgQb_" B XrqOUwqT@pt*q dҪO~!>&(ȩjBތl 68m9TAfR^xBy)zQOHg@e_;әӞi2lgfcR7-)CըigiŔ"ta1}dU0:NG%YnhbV#t7ƆN4ua,:?m]'ά}W |?^xF''FsA.}Ms˔/Nz}yٌ}x/n&Oznp~L\L0҅A^jܜv dɳ WXEkgMj牌nG(O58sґ ڢ 6&vJf-GvV^{ 6BI9pMI'F2 {Bp8 ~x^;>o`cGԼ!wh uGN=wRRWGeمy95țw:u'Vka?%=4լ{*k*7\y̫ӭ3+شj_ڨgqO^yƦ+ j._3kΊʣV&"pd\91;8qNw%9~[׬ܟK5 5V6P 1~ސsͳac-HeޞϿ2(KMqW/)/M]|֋۵LW!09^XzϷUfce6}5ϠPRׅzt~ =g~2ǏOf VИՄ샏̛ƩK#)i S'àęs_ܠO1V f_#/Vys*PFLvϬi-U%~MBxv)ޥ s;L*ޠa/xhPvd) ΟT& E(:0{+zɂ{_<'Otϒ+ڸZqq/ϽQWW0Fsu -R>W/jdm&;յcrl6`7p+Cۦ.sX5U'EČF,9~㝕'D#@o2OOm$nno4c9j3zdZwӸp9v[FܰaL3",KE( N5""c℉eeՒUiIw+*e1{qش/W2[&ѻ EeB:zDQq ק7qivA5u|ESuX(UZn"%E Mq3sSi(Vyt@nAMs:#f5WYjJYVYER^(*??wֱGf Y,(9WΩM2"KJ o˷IENDB`sqlkit-0.9.5/doc/img/table-demo.png0000644000175000017500000013360511714210425016437 0ustar sandrosandroPNG  IHDRxsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|N$л*TD뵀wvXPQ,XPĂ(# @ =23$f@!SNۙə >\2kB!:4s7Mh85f6` d9zB!h榴 2Bl&^ruuY  "B¿B!G1PvLp 2vգf5gAr 6B!jzۍG$+NI9qύz~N1tt]S#B!Do~΢_.-;xO|BcW~m*e. {1\\Ź-ZB!ı@tR;PEMIhՓ"a0t W>tӢ !DX[ q 3 h|7W`Z96UR(K QmFrxLaw !rlI;xA&y-{ЫŷshL{QڐzFSE>gm6cc-Bt]j :ZeV_9smkP֬r6]>CNMPXHk;FejvQ !4S9ג۸5X/Ì){N'v,g cyn^7eR«pAT#9l] ~[Iژ?g9{C$3Eq u;2j٠S+ y7{e=~mh9gVs>?YmV9YUSSeر((*(&ϘH_N\&pxdYLj$t BؽnVlg0<mc]O%pT,daeȪLFݸ'نEP3ꕝ,YMHZ C8N?ֱqX4~eD'\w(0@*3 3t@.־A? j,W0Bqo:wD:9+v2CA/ZOOq:ZA/XE//&dUĥ fSz5nItԝ|6VNEa7qo.>e-]u [ʚؖ6$giA)'R|޹Xu!*7k\?m̹\wЏhkx߬oݦsDq;oeQy3~z1V.DwnՊK+٤괊PqXbH+3*RAD!>ybc*K=Xn/fI nMo[V/ܱ6{_Ƣhg:՜̙dz)#(s:dT\uTw^_Fk̾>69})^p-?Ō'&{D>}8QFBG+؞;-ԣ3: = 1c:;PC6lscbk: nB[ l'fSإ%:=ULj}+\VOh)ry5U]ҧ׵k :Z**^>zMrN}gO7ͪ mߠr47iju@*QCө xZp'K")]|WYSr;>ɤMT4ocuϑ^ś=ǟr~ߩ0{&ylC =G#}_9C)npg{<7fzw &V A_+,wG!{ 'ҹ;3okwѝɃ]n9FtVP^F""v.Zc](/+ŰBfbqtb܍L9/*B a|w^|1Ǵ'gN]oIy=\Ղݬ`BfToTxt4gy.*}6WUVBy}kܕ׫(kR9v'AcEPR~I/gͦ?wΘ2 Y57 o{5ǀϣRu;)1gf rZV1SW^z=loAqT.Sm̄XǜUw>#,\Ngt,R'rj8e_owӶZNMmKu7|=}2}flX[ڃmp`+!U6;K(/YB;NJJn`YWNS5y֝V:up:Gj͑ΚPjz !(B|qyl3)PB y+YYpHjc"_PGQ6DC(X:yG:ӗϥ؇::L(G8T^Z$=tWx[M(+f80/ KAPZ 1AIZ4aXUs494UDiDf)>{^ ]:L+8NԗRΊYߑi1Ԯ`kT!@ex! c9,|a& +&F!OO_ՠ&9?3}/qƉ]iKrŘA!)'5G;}ԙ?O!dFIݹ3tf573[g ԟ.g]J7\Żiuw<諴UJ|ɥ`T}"ʺ=p"6݅d[$LZttDsHX\j\Dwƹ\[{Ҡsn<ލ1`܏QcP{ֵ˷a~Y.Can;p>Lsgc&us‚ J\vTz?dOqt=A."FO 8r?1EbQi :3n+}MEj_jА0{OtA6;=TCr3=ۨDǘ3ɱ[(ހ߭ [ bS_ے< V7Ose76}Wr$浜jjPt[_B$_ s\"m$D[,:ѭCio׈s;N)}L!L'àRNX|r>X,f9Tu R̯ 8fӢeW ʗ»}Qcop^'ynoIo<ÁN0ӻ7V?_q&̊"(()1aVP&n:n5>SmX | XBQ}:WhsZo /㰿C9ImIԡn9Pgk_GߒƇa35AB4&F|3Ӫ2p77+m [)tz14O&-;ְ2¢7A)Ua|g5ξkVf[_('~-߾wT{FM$uu%vDL0h{?kYt2!gօϵ28X+# cp-gc#yrsψS2Z?΍ZGG1<[9h^ Waw[3AvZ|,/ҟ;kwӎcQ^@-Z/Y>N{;w(XXO{t;JQjL+؉wl P\91]NSҽA3#AB!hqeNg^(/诮B!8F>B!8#edVO4zI!Bj~}ڛ}>L^!BKdS!B4 :%B!MlG:%B!-\ !BqLsz_\,Ƅ6?6h!xm?B!G׍&_`4~,jFTm|~q1[7bo &$I$I%|F⟹ 7]ki,d4|B5(Tcslr֙c*U2Zm}Ɏ^u?F΢ButqѦ#[>\sh/lڸ&b3YYr#dR`n6O簹oZOI$I$!?ob ?siXck? )$gHk~, }U2kgcr{7WOвFVlL!߹l-k3#ľ :m U\=$UP .~FM!1e,__Kne~#1ݬ>8 ?M嵧kx3ew=54U3!ل##Og=Ho+⟹pT{ J!WUשO:̅WIBWⲽl{?g@bzORK1 V+wpPg2&70c Ws)/]&O=K$I$I- [Z8y u3]ƦrM'\2 Wo=bNE2 àh,fN 42V=yd_gxE.r%wڧo๜xs 60CR`[i3$t 3Wl`۟ضyVSΆg[חT;([oKg뺵l^&-~!Gkz?ؒ/dzOK.ZtdI87nE6i=6߬ENV{a6'T]yb*~X}n[xcw~Mkݩż>} ,­ʦ?ټߏj6X')<267O{>b7 0k-̅^̰69į- P@q$sɥq8G2Ŵv=ʡ:ůA\t %/u(g k1m>sX?iB3w)#9E$2l@H^Y.ёH9']1[~$`nũ %/Humgλx(Ͻ:WgrE}ПN6*9)􉱠ַ\J/dX (u \u i{hZ׬v6dsځàs.[8R'ywW.;9*N>.m7u[/ZI$I$f:\o@@ffQ)Jnơe=ǓBlA PLx%#^Y_xl=VāHFZ׳u%|=,c͵ZޞZ,qGsyK.Pڬ~rZXs h,'@u4^{s{'M_wok x\:,S8#L>Qg! S++sq57zA`bkob+X*'ٖU՚T.7z1?/ByـӨ\0Lks8['1Lpxk@=Ug+L|`.! FI:hg[qaV\Yi: W AeflPF gK֡rwrË,<+1!VԜys7G7SLfk 7_H߫ mOKPDu]דaGP@kc_0*FwZ+ʦX5;-6X`zޜ~3 Wn[LTOwb%'mY}sX=۾ĝB!fHo9mι\zm򔑝KRVCaUuZ*KD&,eXS3g->:ґ~br+pgG{|8k~pl_fUrMLCg҆y}I!霚>o{&r0Zn3P^~/~e56+o`: P摖QQk:e28f;]KMY>xUַi Yuod:{N;kMt8;᭟jo|3жmO$I$IG&[_mv&ʓH#8O2A-Цfmsc=7mu~[B b-s}ؗ\:j)'$j[Rnz{,厉#=& >nvї[:G0`O>JD.˧}EKϤkԆ_ ϵ'qǹ3gŜ <t6\tF0LNR+*܉ V^qoOLR;`}7Lff%hU[j,c.Go*i`gm_$I$Ij2rPcedVOtV^Z!Blkos#U!BhB!B4׏v-B!1u ;B!DˑN!BqHB!8|w8B!Lj=6g9G2B!S5 :-,B!DSu!Bě IDAT!ZB!∓S!Bqt !B#NN!BqI)B!8Hcs3ӺUdy]f1tNHhJu}R~_AumM*%-^yִGjrz|ڕX.B!84)ܜolf3K哹sbQɜ7%Kyɧ]&-- /`qM*<3z}1sکXv5 [W]NBIKِ^Y׽ç/_*B!ZLNʞ{]ZܹU X,dg#ը a躎1[,x~L<:ߺjz~\+/zڷ>G4"Hڍ=zW䒫o`)9uX'v9*(/+'4-S9ŋ$B!Dhr (*7xnݒضm 0 tUQSjn &@trf=S'OaW_2a)ןE/GaɄRXT ::/[A=9̳9s)))!+++n}A2 r_br*l\8e(f9놏%B!D5+40ЍA7Aulv;Yi<;SQQNxx8L&,V YYY,Z1'hjz'x'mE^~%%ޝβeKXz-ر={0uTF4zEmy-Z/]Yx|p:tJ`h0BB&݀m8릏s%B!D4+0P?JYuz[=FII1lX*uyyNg}Iii}!881'Т)! ; !88kN8~Gvm`˖-~yX,:A9i|r^v61CQ Qt 0ThAyI2z/_XO!B4YNEAQ}{=hoйmwq:كU|M-#'; ())fc:jvGRRDrr2gn<L&vSNQTTD\\6VZ**kѺ62o^zM!22P*(hAfV9Cq]t !ɚtZ,v<ߧ5̓khk͞a؃)Ny:-0 <"]7P+bFCϒ\l`'`Ov`t4[NI ؏ ]PMXhF4RTDNn[RVZJff&ABC[&07 ">v{f(:)a"972TRg+QX,B!7Y/He퓦i=nr5 ۣ͊kEžo9~J߼+/a чM2aYx4(ɡw*NPaQ19OѳP#1(֣.ߣN9x>C~ £b09Uq<4̓夰;_"TT8IWA߄`ܚj}Et.gi݆|ݗB!ZV#GizlJkc!5!k&vG¦Q,I11zGK #{7J('c 8EfX aࠁdf]۶۷RvM٣GIOOg@".r+6<:ٛQȴ3ЦuBtWEhhB!hQGΣ{ÒFn^G#88CHڢ0 a9F0 ԸDL=b9f+kb`2 U+n={ ji(J Pa= ['B7JPP Ŋbf{z&n@xPT l{ 6uB!ĿG9Z>&eѩk:Z=lZ PB!88 E; Ű:p=]Ѷ.BhYeZrW46mڰ}v"#INNfvL&wFCAA7ٌ(>.-X7XBX {[M_Sio0젨x ܖLS!̠Ө~Ք eS9Y\T&$$ۍ(tP1u?UA6Sim 5.c嵦;NV+^9 *I)33:&G*+'(2 T;KjذaQU6hvTK9 <^!Ƕf5w)#a^4]"""jo6X bf'ԃiCYՁ*m=gzd']iSkzUjTjPdv2,,(\L(?b5?וGUU,A&5 kg?xdmG:ڶ!$$͆ɤ6>F4""S]¯۷PN QVTEAQTdΊK$""Yז !ߩAoObhTy3T0pNEEE3 -oy܈~+;Fc0ez F^<a/+v}чMkփV+YO kGry ?0{ع3CMքa7{EnӺukΛ4kYo%(. {K+2(-!,vsqI}hݺ5vE$B&#;3?R9u@5)G.͍ۥa`Уwr30FcF]6 %{0Q_yPXd9 ^ſ|Jrrs[1 2Q7UeP>)z]GzOW_x<**rL&TͪqI彏~R͊lb1袮TTTPPPHvNyyytzA섄ElL v 8BhkonZ) @4%Nn>bb۪Gm唺B!*-O8z} \z`:cs4SE:P괭!Ѽnl+rIy2<|7LBbN-3d$Se&+g]| 3dZ t&}O'eLd_5FR⼾x3AcycyJ_R:#1FV>c %mw}*wOx+&L~)K_!91 9q<^=ջ=%}P]6g؅kzZ4 ~>N!<:R9sٓ) =9k_fh`XJzSꥬzJʘ뿯e/qgZVORV_ygR6/!;]>ql(^7|E7Y&}ɿs}?S^߀ LJU{ hL~ {}4u7z2#h3A4Mˁ'6ob3kDL~GE1YGϡ\$&Fޖ>J-cL |ɴ}F"%Dƚġ}y㑳f(a3SxvnaȌ׸xL/U5=|sl[@ig^'gSg77?vjHg/fPȥO)+R35m-@W&r3Xp5 2v7'vC!n $qmA~1Ƶ=~iĕUkϦAЀv}(Kf]̠8ke._Z>A= 3v^I8Gzo\g,$b1Ph`{<iu:>b1 P~);P{=vy}[.Y0/@eN]; 8{zdzJb&H\³PeNY1%@Hf,2[(VP=kի^uրhHk5E2*-H )<[s=eڇ^im''i;}e秃Pk&FD!gr<(bt졲mq݈)_wh}S bì>SjΪJ6@K}=iA]=4&hOcZŭ_}C1 è]Z7L~D]рj8w&35?'YInrun"2~CNlw qU@vW{gyo"rG}>ҺjJſSbwMwR"2?|E*~dz:Uz= e|Ft#hOZ"-qe]uDСu2ǿ WtR.d{2C`Oq1aS D>(-Y.&F<_:N^P̀ǻ/1ƑǶCe;hMDBT+rL~ӍP+jLB#[ǣ!оހ>8l x|1 P~ōX6`{;vy>ZIk _á ۘnJw3sT4cOT$C𡝲!+'6+wQwYבILb6ٱY~tF)uf? ΙKyoqY;bWqރOs}oBr7/;ei P|rqj:(Qٗ$fo2\[lElxFޛeLXؖQGw*݇cugfo6%&$un=*B XR\@z>:>4Vc l{}4lɥ&]tz?:'Ȍ{| ,[5n"R̭97ny|Lثxzȫ'h?PF.zn&vӝ=R^@;wf}}+zK!-:7N޻W22?cn눁V,߶Dpڳnl1>=s]q߼qrSmN0 s&7ƅ4vu;nUmm #GF^yΦO0ܛH1) }n-c}5 E6-8.Q}Ivsï; %g֩׳C׸5v> Kk@B;_"?w7mB) :F6a{ \C5&jO.oG2rPced6x%{PeuAius#3+FϴEw| m+M;}ڛBO C t}W6QJI)D ׌tV=@ўW~ IDATk[79Nk^4k C׫_{?{Tp[hi@){!["c){O-D" " d) TVetG'mڤ%Rܛ{OޜsMɢI)<7 ГԚ~ڭBUֽHX~!G<.BlSҩ!a01kV`!z f <<=yX'xtѦ( 'KB!^6&?s:7_W'&%EB!D1oyJ:ILzwB!Ϯ<%'bv!B'm^O<%;B!OXlTKn$B!$B!"I)B!$B!"I)B!]K:#w֭="F[Ͳ !xk&AV! %hTPP(M$bl2* (m죋0UhP(R镽'V@Ho] G  {apF8.8mY{vͦO8(U(ec>`An:^sZgԗx%.HБΙ /Ï6gݵ{<dž$YT.ij!It,B|i͚+ݹ.e4JT%kϕ812dt-jo^äMb©F;G~CvS{ͽ\}x2Igm)7c [gi]skυ鿍1M)>Yz*}كWq%t}4G'δx>7tM9Nhv '>}q+k>5h[XQ}~9Ȼ=|(ߨMsWdei7EL gA[YG1٠!YNTs0Qll].o uܪՅj7FfՊto&yI+AdND{ҫ+*}b$ 3;W(lm;rCf:XO wWQ2ʼn=_d$-ը[0z0{6r.6|G!| TV>pijXʶ.hS3%+Q4ؐj&/nHKjs->,Hԩ/QTdX$^_&MM97ҾX%ZLbĒw(okY~[gZ,jy{/w 'tQ'\e pZXPYRcKP]i(]S46 {l Y/xN@\qjJtd\uV)#U+sE'p;<7QDq5=e=-vv<^o" 90vwmͨ> R>v@Bq/{H4[.ve}ЍE#lf0΂8[_%ч8l/0ga.UL9Lcԥ{2͵L}}:Kp(~vT͎ҿRqUf+nPE4:%ДNm5x'[ֳ?y-D?1eʹҟC^Vj7e&8mM]qr[Gv݋I^օ>´Gq KLwKl 1u>瓮4Fs؀Qgj8 6nJe;s837vAکNfڛc}ynx "[ZFB!Ϡhˤ9>:/FElL&~<^2h0W3jl cßB!HΓNoU: "2'B痪/- B!NN!B$B!NN!B$B!Y\+7p)Y0Jxyz=)!./ !BgDcFc/g-B!D1Q0*~0{AiL !B,=.IB!"yz/)L !B,dz]!B;^B!N+<B!xD= *< H^$Ou<'^bVN4+2`Z3x>EݤG3/&&bqE xŋbW.h N~~؁ ZW괞\c m[+2-іf')O??zuL_qE\Q1>(H=Ѫxypv>K=eB)μy7 sNb[yn2L׃uE⻒OndQ9|[ ",^#g,=3yJA#TSYOۜÈJ}dEU}d^eN/eZќiV ƕ{2-z/WsGqD~<|/)g pQ:yBY %JFUeVm[Nu(DU‹'RAL>)i;km˪q,ۚɟ,(7g'kQ| {]ɳے\9) Z-Ljg_xcxwza[>RSac./Gx%vbQ_ro0{:L s5dۄS` ]USHӑ Eԅ5AZ@`AG% [?O7t]n\AwX\۟cnpϒ1.G_cF/4rKB2pUz** -M˪괧U[P& 뮰|@cwWԚr{sܷ<<?UZl,s=\ey! cUβ'tz@7JSԒEux0Zz:PCU&,xr :_?Z0eQJbskxR25t`\#eIkȭhr[ʘWqIK |:L&dfc;ܺ Cld@!,b$%+STd'"g^3  ;Ba7}縑A%y);9WJ5OӑaDEm)h8&!Ҙxŝe02.6{>* Y6$g zu6mSak焻1uT*T5dZ!|QYN uYG[{ʻd;:tzcrl4~-x<{Po&Oч`whm^r,^LBMJ Kw9!FZF4)HFQ_N^T*Y fS8]/SCz Ȳfeq"?PUsZ?D0H]OMr][?8R:RUE1~٘[wQJܚt=}э/űWw"x}is^TK!,sfLlV.ytPZӎBLufvTM3%kxӅ|ӿvRJ>.UJZ ݗ]q(S1?'F"ɍhIV5ϱ>Zt-MI1>+Z$n휿?fkOx7kjx+1!eg2z ސo=F! l\Qg[GH-[HG:B!,ƮӎmY: "_"BQH^sxt !B|'IB!wt !B|'IB!wt !B|픖<B!(2ާ3HB!wt !B|'IB!wt !B|'IB!wtfnY@YSAn[ayne?2?q,lII$Ω xV$IzV!DcŤ@oj]U(jJ}[.cBip d]677m(LLm|F"=WVUC\HW ֪B[!0/"wVB(Fgh-=fQg1ϻJ u9IgؗW_HMx >5ԟ+"xys5MIXחG=\[KcygM,)u}yl1ß~ƝShu'qH•#1t+-s[umpby?} Qip_?XB_j+TqQbk_ z%0smtRBgI J EM>/ J%(OGaEے=9q7Jf1Sn9~ pUPhkeaWsnBa_q>e֞p'vVBYZo.D!u7rm7b7*BJ~>Wmt rm7d܄TѨP(xߚ q-Yښ2oiّMAG;]}+GQ:~xlIEg[읝i53&Y e~["ѶLePC[Gf~EQ(3$&2G6(UԺOMIft.G^N o-$xQǖ1mQPWN:PVo!(ETՋgRF3jO>Ǫw'j:nYz*}كWqEՅ[i6b281b8a>Î[a_^ٯMיЃo'N)1=xk.^F:#϶bPCjzpM6 sMZo8}t涍_+aDNOxÿV_&̭ ZxUҿ%ruRn:oԞ=3ۣG Rڔ>wK:P֞vcQD`πxt\Ořux$1#n kg$1}+WpѭT9F,3ȻK/6se .nXNNʘX{.HmهolJqNW^AMr̵2b"ng@$ :6ޚ:@L-vgVrS9AfaD\67q1(up:'c3u6e>n*>jٙK7:C9r:\MtIa܎-Amf@Q-ebcgl;fTje7jP*)%OcFMćg2k@eL~ٹR^}&; 3r 7[<DpvU[dQ\_ƪƕ/j3#NΟci-ͫ[sI'MǕr@&<嶽9~&ڕBTH;_8\OW+g=3/9E[:Wft)N٣:Wzx"n{`LBT{G uQtGY5v:,=%s! .ǍMN|1a'BtBN|ܒXumŊ+8C|ecVj:\[I,WIH8CQ٬o6됫6F/VNN}&o26{iuvz2,€_k1dZ#ήچĩxkVZo1ćgOӒ:ʉ+T.oͭ[sd4]0̩/4ǒ+E$"{kxOb]L{Q5a.;O/KlNO𴳠ܒW>N>WXz tdgn3q"v_%a1wì\sZ &}î(s$MKiQiJtwL#'E>[Qu)(,,Ȇ>#;+JqcD?1eʹҟC^VmttAك`Ǽh/#/|Z3LmY'TKZ S6kRnۘ N_[m.lp/y,c_Fyu&\Hej7e&8mM]qr[Gv݋I^ĜgY__`A>s$w^M~aQ QMԞZ|=vh1ghmқZ;6jǭ~ҩ[k%%;:=Mk>r z̾i>´Gq K$*U(/]7QW*NJ9l 2o<=n{32qgnTN_Si99xf߱Y٣:j .^gql6ʹ>nx "[ZUE۪+u9K]^fkSE ϋаG[BgqF@4eR]0O5^H{Lx.!7d[`H;yׯfԤx` !UbvSnɌןÄ3(M̹e/z5ńS<tWy҉X'AvBDZ"w֭'/ϓ<&Zj/:[FϊHծS2SB<,|+D$ip nmKM3Mo+Pu!B$B!NׅB!na4F ?`4&mHۈјԇyt !BT`HI* iɥhh0fzސxZgyJ:y !B!s:B!DS!B;I:B!D B!?o%B!i[;Ex=>,z]^]B!x* O)ɥEݤv96B}* czÿU2,-ZU+$ł3Do̯'CZɕBw *&G-KͲ-U۔gt^y;>׍]gZo(YZ'ȐQwת[&(9lER;7v8ږUX5?kFn'e}1/~m{_ -4NLYZ ί͕\ n#=Oڦ0.\e{2|KVsw'Af=ˡn=Ɣ_ucFvP nNe\JNx7> "mv'>_+ŢCO=c3WsM76o ʅtcBAY%8|5cz;%R2\ͤΔ8hܱhOxl=ye`u93gz]gz\7:uYm)n8xѧm4_LOrCr;mbzGr//u[䭸 ,XMZGeyTuf"KG/ap>p8ACku:,̵/(SF-W{=L"9~K^J.Ҝz+xkZXm9/wѲtt-fGcFfO=?p}ǔѥ4bדjoľgSXD>hSFT` 4a- P_G ZGcfDvϦ4LKR}ur ~G7t=;Teyoi[^Ba vT~XӃ{ rΤ|;/OGGЍDߠAӛeϔYh)϶|Ͳ3> kIV<(ݨq6U fMG ~ ) / h;5'ӨeB!(b8< SpdD$_ 4.?3Ͽ>jlCo1x31䓭K 68+'ג B!&5vvmI)x;Ԥ]}(7T&_ӆu0GS*أp*GQQn}hӏF_.l=+1|Eỏ׹g&F猺p y(ԫ:7,$-%"KO-=;)H>w2mjydt}9-*ɶ 1rmKp(a$t0F><@v}y..4j՘?\,ciNsWkRB[.3s?mœ1U'~%~qz=rmK4a̾4(;~n~TԽ ZxU'8a)7_BWO غe\1.#n'tq˰]8i3@͡?F9t"@t\3 9RF_R,]Ƃ3KÉ{qpv #Z~o1Ǚ:|ɽsixy+g2gى>Yz*}كWq%Lf?S̕/gqTnK5SCrtͱZKŭx op6jҾg8!_hХcyJteϹIŔhfޘŞcNX2j-d=㘒r}JĈ%P2!r~ZҕMV9\hcd4&.~)eMiS^ʵ$?{ i*C<ċ赣/{jQZArJM*fF;O6MLH%|Cjߎ9֧Nz9C1?n?q1R:u{̕/a$z f藃d$eD{{"b'Fo~Y^C_8֙%S#o[^>eUנ.Q_g([=Su_fӦ%ʣ8tP[*dڢ{N@\qjJ֕^ amv@Bq/{H6-D>єOq꽭|x0k%= 'K:8n:P=N3߬Sߖlf_o_11`O Yȡړx$RTeG9;s Fu(GN[2l6N{ aʁlƍQ7j2X%g>*xd&<ӵ겭ע5[C"K5+7_Si}'-ZdETh ץ`Ѕb_Ă ,rR#?sϵj\)tBoRh$1epgS~عR.s/FeHFu$}"#؏+Joh6 2}^aR Mww\ۥt܎EcT1n#Oóm5eWk3^+yݻb-uzYwgJvKڢe,?s](78É M Qj?DqZ1l{fzG4խ{9ޓ&nwJNs0nM(ooAym_ɺvP*U(Ήҝp՟ijeK9*!9#rm-:ِIC]{+%j8 I=Ys(*qԵyC+ƮX]1YITwmLB{D30k*fs.dSrov趪 oOl.y%:Fmj'ͫD @U RX6bX9gR1MϪQ"al~?3,_SqV^N( ^qks6: aDN }-LkUp8og϶/3~NNJ B߾%L}d$/ӷS4_LXʼn,$y  ˮ `ksۆ =5F1z.͚0dHΓ˲<xS7<,(OsŅbSP}++nÎrRەGa2%b:sA[?GԀ-CYڵZw_̦c_k^\"MM?Bؽ}kX"yQB|M!: ;*ĊfG_8* h|7bLr+Ho;GXS?sjwdcM3ʚ3jL%^n 5 6͚;A`RkLސJ! ?6vy}Oi_ x¢9Է.hk$֎b"^6&mڴNAn{|v1jlCo1xBBX!k0x]N! Uvt}2I)D !D>E[־ȇ?O.Djg nBDN! !+^B!NN!B$B!NN!Bt!QRB!M&!BdS!B;I:B!DS!B;I:B!DS!B;'Q+>QV,B!Q2)Dau勠B:IgF7 D_ךPFZSz1!'iXm/MC+ۂGݤf9jL {Yc@J uEN뉻HL)Wԏ<-gRљ%x&犺TwqU? e"GjfYZ2gӜ35M3GniU#wme8rd#{ý?@A/׋,99ozd}3ߥ焷7 ޽OQNJgꌭ~cH/5+_Ui榰LrLB`[PMYc%B+~t"3y)6*Tc)If6wBwçDve}eƌב.Nfs #HƄ)N*胯IlysB%"\723o#6J}ps Y;^e |^^(9jZgٯ,'vJleg`u#Wcbu/ŧav>>@#Q}YHF#`B4t+cLp 93oUC3ƞ`Z SssЙt< U^O8Ű0bng~_<2gnBE?I%MJ))Yg洵cŦ7cOv`9ŏW(//gspx+  :3N|׷` {'56i=z>gwoR> !/yhxgij|ώs-:F ۽'_(q\Yw+wHV-ͱH }J̩K61YSI ڒFgBx%#8-tFxNk{mlDOUp:Χ?ǂBEFR{\JcHpnڣ* B!}z:cSчf}-<ln}w qٯMkOJ! Fc^om#NҭjXPB+|>v唨=#QW!(RVTD'fGphhs}*ltxzk5'puh6SYL+l~BvfUUWA2mkqgyA~ADwi1rՖʿ!_y! `〧. !)וXǸpxcL9%`Sȅ2%8p_~'3YT0]jF\,sn.K ^`J|[(GPX02ϙO RhЪBwrUs.lLkJYWA3-?rNg>\qSH[{ tӬ8Zxc>we{5[ɉdBq+Ih.5mH0"3Y'xl(=i׽4'@`" 2k/xvo;sJBZ WGm[;a5xyp\֫z1셧bd ~1?qY.>4-ze0e\N5š н^X4^ZeO1m++ƹR76{|3a_܍&3ӱnyJ A͝gae:VJ;ѐf.צΝ)0kܝwUxWnvn|9%%D—xQcX))k wUO\XùY%]]z5be:~+1;H۹ӎޔO~}m28se\]NPwLW*<]R6jh6Ԕ͞FfͰQ6~ 3)8\n)BgXvyR -SD!٥~<=ESߊ=u-B!DB!(rt !B"'AB!(rt !B"WDJG!I:EMF:B!DS!B9 :B!DS!B9 :B!D|n-c/ig}1YnB{:G:%y2M!xjY&|܂6K=bmZ{\%x젹ZJE{4#03;IEe&(tR:<5ZT/ ⻄(OHB!C$&%>]XƁ(bO[&椓ƵUx%V] tt V_''I1;hO2Z*cگgՇ4v@Rz+ۘ9Jx6Ay:gTw֢Ҷ`G@fv`!q1,+{7CE)Mޫ8o̳,|/4*v;-qТR;S=@| ~_,J*۹Ob)<;ğ+|I{iڮo4)2?%rg6#cfb@\ &:Oݸcętۺ32.7bF7b?laoVN\hw&C7ړƟ737+]e[*G~Ā*l>;f^X} N|@ugF4)Qƒ5=_}FBs'lFzM-+UnBH.bQA]-.BSo=*zcWA8mbdJ8kf(|ąĬ4Lߟƞl=7!G/Diz?KbkXa^-B]™.4sI cO_Ԑf*Ntrf%~A CÓ vnL<=UlW!BդslwE$7gWD43 IĤj(ҖD. 6^>U(5(߻&#Y2x|&;5!# Д(i宾IQDM-U>$-YQہ9S4!60mܨ*j$˪w!Zx `#Zp33z?KZ"WYQ\)ٞDn&cӓH{v׫}Be!~5>BQtX5 HPՈڌt2$ < IQ$ꓧL_zV "19˩oCܗf̹MI~(#8\] 2%×p4$,ؖݨ3߈Q8DRqX=ui[~X0`{.G@m|{$ZmW'X: NMmy|ϳhQӈ8ҡS9}&=JaِOrY6L| 0}j[7u6`q\N&%-ӗغ 9/$Gaҡ~ӹ),~!$hIb[0zZ2si#ɹ3c.p@<]$X}2;} $g}/#n?{[q~H-8>]n2 9YwVxl#^Tz*f5{^⍲8Toߧ:h3y'+}O3IiyD6Eճ0mU{EGʶ+Il";Tj6#N/jGG?1~zUqA_[PM_ZAO-qwxο 숱ά e|ٴVV{4ޑyծӫ|e`}~S;#S<.k.^߳l5;zXXoj͝G̻s<s]-:W9ɰ.ؕHEe'jjKGI_zVpdioGbΧ·QC㡣lj1 > v"敖0|u!B<6b>^4{͚5F<ڹ!c&x?#B!'AB!(r$QxmQB!dS!B9 :B!DS!B9 :B!D+BDKC!B+{-K4'=kxw 烃9)u.lt"9+oRs:N "f<̇rIΧFNe&LUpCw¹F'>N33şƷ:']ĉhCmfFɼQ3WwS8Mϰ,'vJleg;iYf'oѹLH=3ۭE~)>-3po^pLCOslۋKmg5m4)+Q}jSc՘k7eOKپKQyҤo_uɏqR~3g^%*_e^9LTl{7Wq䯙dNİMXMngn ;yFO9 0ixN[rF*7WXyjqz˱'H=Ӛq6[ݘ_ßîߏsD`4b$)@%vRx8ӗv9a2;CR0Gjom1_:u5xx٣Һb~ԱĨ9[6{[ՠ#\5k&,_`ÇO;㡔: +GaFṊkO֫1~ Mub̌jwN_Gdث%p(wz,QBmu:ֺ)9BrY2$8B®kDK:D[ϻl#$gܙu[uZ|΀9]SEtZS!{BFim#df'UGq2(? 'rl8k{SB+Fn @N5hɷC'x5Y|q,(XK'\n_njR8UTp@o Xi(TS;SIƩ&MIS?p.{b# 3r.S_Ht\8BBld_63PZe=l9rq|/KzX Vaט`׽SN24#611I΢ &R3u/==EqmNIBqU$F+h 5ܻx#"wN lxo_*?9ښ¡ w%DBd8ϱr_`$xe#?㌩@1r3ù<AR!Mu){KW"$@|-IDATkt4AΔ\5NKqk;*83>w0 4wϘEcLa(עUc6dѢҔAU7U!@*v盞hhzVW \ҕ=1m#6cR}K9,TexD8NRhy0 !iXm C[ע^֥&]N}\J4M~\;7un43ciԏi{+Lh|+3#PqHw*mp%NP΁s|}5֜"9i/msb[mJ0bk^'qdL D]DIwbo\3MEu{{!KFZSIωv֛~Éײ+Yo݋ox/Q !qOs*vM-+n܄2!'lF:@!:eNۗmHOvRfԩ] ڵjp _8ahyoYt1#*5֔j3./+9NUg>4pdV0я5?hͼ:դ ~mϠiCј ۵?-&CRmA[FLCr̰''k/jH3oZ'WN9`Hxq`$]ugy'if$9tB/PAibC1Jj?%ѤƐl0#ݡ HJN"%!FS׾42qHi4V}˥h4BU Z%Bm[Tj-jw1˖nhH9CiLp(枝kbQE!wg'ef>:%LM{sL`K <~&fݒwoF|"x 52su IQ$X:0m$w,d|4K#֬)əi~19NN"~ﷳ(9T:ᭋlqvxFp>$;-%ᶞ-PQヷ8܍;~[b i[GPJTP2#ӌxbDJ[ukŻm)>M=m? "FB)_q?xs.;hx-~!_:H xkH7AF,^3^)Ơ3 8Zi|T|ep#dzdȶ&=JaِOrY6L8@We% I")(-%e7j芣]B䢰lV~ X6d-3qV\$,Z@p=;㶮|+eCN*ɑalt(E]տWys-YD$!?fXr^QEe7vH̱(۳^J3cgDߙl;BBZ QW2Ox;u仼Xh\}w\p+| GT?Rֈˋa! ļjU}loA5Y[ⱙ^8xu`Sl'DA)py)@Ҕoh- y5: Q91pK=^sp?Ԧs6Q`zGUN*їM+TeVTn X.xbCDeD=w_PSJ%г3%+H%|ۧe7q;]kfnw% C7nݬ=NtN_}B!)!6׮^񣄇Թ5.1|*=8pWs]A!B+S>$oB!ENN!BQ }QȰ`:vVBa~z_aB!к}"FKΘH|wtJAѽۘ_x>B!ƵT|}g|4f͚(0f훑y+jҞ^*a*vE_ҩ _jh4`x )!Bh0(U&?h޵VVx@˶9x`Y ףs(ܭFW6B!O4c1`eǭ]l^ڵj2o/64JϿȵ g ~yM?B<"C-X!,[ތysfV;RQB"BAjQktyczz]BH֭CxC=BK2blDŽja*贲Rdܚ?B!0!N; m~f[C#|S\5VLm|>gtB/ Z6|џaH)Bu+G^ۊ3St,*X 'c.s=U.sИϾU Ⱓ]U ql^5?5%a9r=OxR aHTS1ٟH IJW^XCv M/Lv&d҇[( [U$\15#߬`Di0߮}QY^}?6hk2hBkjiځ]C 9K8uzEOšT$!B<:yzb,X n$uUn?n$Y|ِq˿b/1X00mdzo~>>ò'Zi&oTg5lj:5,&̥omL\KSƯ'd~ ɐEٺy l+?+dEVi||&1In{Ԣ H r5Jk)] ˄B![Sd |Ub+$04)&f7ZRs?1uޮ^iփ_>&ZF6tѢԕp:t)79ֽޠjIUV}Za,zFJ攟zFvvբw;SUkf.Ϝ}^ۘ6V60N=JP١LO$!BTQ$C]`nlH̽/@F<-=Mt,KIEcm])79}vFDR{޺s:B:T#pxllQ>[A!Ϙ舐< t&BCAh7P@P(sDZ:teV#?jg쬁 Ǥ\:/{RVUzDqwinF3pY\zTQ:V +?7M7og?.}YJ˙>W}DJ-BB%HJ[p e!iyvGtD=s+u:UmZETE*y]Ju,܍!^4kvoT*Qٱfu^[1tR4Ztjٵz#WMVl[HKO!_l^sTպnSI̎; N-hYʋf صj;g“IІM\H2oL1uN6.̙STD )~lY*BgV)uLߥ̛i(V$U6uyZe|Zmj1Kf@<:}0sh<9ܕ#ywtizc4M| q _}x#*j4Rh|~C uZ4UwY嫩83w0Mf]7&=d. Fd%lpo>Ġr>jVB!9G54mx$B!yM^ A߁6B!`RB!x5sB!yws&AB!ȗ9SN&B!̙Rc'lhj(B!I9׎=v };=p- y>mX!g'Mߒ+|ZojN9َt襝I˵&]Sg%N6,8;MC_U}oʜn޶u a+i~Nss K*SNMSqWZe9,`UGa4T4 -3[+v�n6ejW !\4Mtbqaq:4 $騮ϗ+S;`J*kl]b#jl@U]˵2Ts4DWYQV"c3R9!Dby*K]޺)Y2c,ڞ'MS]=7v&QQRwirSu;O6T[l;nk!yS_H>&v\JzJj(3z4`@Q  y,4U.u!rT,cQP(<7۾V9=&}"زE^ۗfA1H^.Y;~m-<:lUo2't 8? =="n~Q-pf{2/]MU 1F]cn0 IBҵRt0[5NWUU6dwEOxN*(EQNyCQU'$QBIwc g+hס+p#,ct=&t $l]Γ%}s ̏Umq4;G͆W:cO_@;`xWMef>{j>^#^=? ;چ7 n$2h؍9KDۨ$uKZq&{EfCDVѳwq,FA#= tmߠ:ۜA/O0+h\2Np̛H:@d{uXB'uZʗIY){dr^+?&f9)X4*9FšD{]w/=G/ٿ^1-5߭2<9(W\+( NU'dkt][jqlZ6칩X¢d 2˭}@bNl'DiR\Ra|*>Qu;%>^1Y-W'Tߦk)ײ$5abJIEި*N!wI&5 P|u6>٘Ϭ I5-Gwagbۼ|l%y?~cK+=W`ݬ*˔*4@`B?BFfu@"W&u#ߦWzQQidС3ʌH?xWTUe$DnQa\0lclG~1h٨i&.ՙ^>̌4 6WBZ71k^^<󊳡1td!Mob(뉢X_7Pꯤ|Q!XLFYE*m{~$ 42W[OF~hTu@ۛnnЦro]#IB! m*|+.* ?L$B`,^qB!hPLaj۱+vy9zJ= ܇1|عqSCyJB!ΈZDwVa;ؿ{Gy_jUG6Y[5t]g܅I8xwԩk({m{c~},[T֭+^s%C v+{~KU_u*SϭBN=Qw{Cixe+>l{oI@@obnl2﫦r$ mv'1tp%QeNf5l6(, w|cX},'Zr'MS^ 9^X;V{0=W6dM$Jkު:z=ڵu+aXxvK^USG$33-[n&s5rG /))G'y_z 232s~X7Tz=~{;̸xU\{Uu+V1I5Wu1⎙uZ>CmX;Vc}B\t^~M=&lI^fd:wpgjz ~#i$3~w/۪>Aauλ^xİ%z ddfҢy3͛ᾟAf1ǫoqϿ ǎb aZt}aaddf42ҽ1o׹޵],ۯo6A^b7eoclj+r'o_KMLto~f{mynjmqu?n4y 222x0~s$##yc\S[5rOBBN]Q5էVwG"33y> M6d*bUW=}uµ7DZjo6q;~S0j$ƍgϺV>$F'((i^+5ĶjI}r]1|GidfeL]]|˶øm=ML$U+}82{S.qcFq[| !hpW^Kvz{ pz]L? ƜPuڛ=;70BAkvJaϜ-_@ 7qQƍ%߹HS!y¥S4, ߹GS!?,;oN\l3_^~sm%BqVUOZZ MfS%Bq֨u(4Mv:o4MjqϱAQq }GRi*4|k:>]])th߁ '_c>meS#qɄ[.];s./~㪫$??Tfqz*~P]N~A?/U'1b W_Nzq+O{":idff߮3fX^p;m&di!N::^CJ$4 ՊUO0rC^}Wz#_HK\BO,$1 I&F~fRSSYlaaawFV/<ԳD6iJfV&9rkWyN8ABBGK.cǎ7@UUtBtt <(˗|Ӹ4M咝9BiG6!Y1 h:DD{r/X-ul2p:C@h}>M)H"%IuEuUSQU~MSCu(*j%7/MHH8aέHOӯk&~yOP5ȦMh٪%avmcoۯQPl?|CyqqmHMKjLahт#. ..={߶YٙfZhEXhQUϿ]?%UƌNф7 d6c@ #MhԢ#oX+@( )6~&hߟiNgBYdUCIk?E}>'4]f` SՇjt:U qqh&#G=2c7olO/M@|,V+a0q:4CB@@;v믾` |EEՇGp:DEEa4oێX %22BӦM1(9DfVOc45tэFPt'h$qͼ|o{FNZJ (g0^Y~`턷 kIiL)!ouz58eN4MEUUTi4t]_jt:Ziʝ@9wj}x :}U(.*d6ah$fv9?Xqqf;ԡ]Il6( (ƒ%[Bfà;rdADI>+7|]_U{tQUupql6ƷCjބΞF=gͫ 3#MUphE1`MZG I\*FhzM$dlTNN,a❮_Mwm`A9l~ %BWרjWV+WflizN'_@%Ocs"DOeBC*;m`Nߏ+yP"QAOK@N(Ɛ6A$R.ʓϽȬ__L޽IMMt_P@tt4w&==`Nκ)d0pM3ngD5뀊BZF!wnex{ neN[kZI{$Me+볖'o+ >7Ulf-:`pVzA2gnw(`T 8&T~I59xʅ5 E2$%%ЪUvԣ#c.G.Đ|gdfdj*N AIZV_sT B(I0}5φF^0/}Ƙ 9vTUu\="-ZmO*G\ U`CI(bDǐEyUna9˺ubi몪:Ct/CLjc`g?c2uJ#(F8 >d6D}HNNyf$%%QPPGh٪;d%oߞCѻg=;&,:H<ô)m!$$USykIr4MuSKzJ2 Io( 3ѳ`L&SC~ #a$B;g_UKMO"detxmфiz}<-ZѪU+z]fdf9Up8_VzzN *wci4cЎr/}>gIlh4DӦ:/'QG&i'oz[NCDN9T@HH0 `6a68p(1wm'E(tc,Kc !#=4]Y#i{Iq(P@K܈^,&Pb`c,!]|{#“ϒSݸI_P\WIT&((eڴiNmt֝VZaZ5߯U!`>@%(#R1N nyxe(a- 0C6:ӦJÁ mDǎ9rVѵPi&l2(JP9iF F~Al+ݽOwӬg?š>,qˣI(j%4YhZ_W.:X^Qfr fgga6dtJc]sJ˔i] װd`:yj3N l.w ~3hߖpBzPWֲGmzcR6aJqq1P,V+j# USA07[gH Dd!e?Pb/BwX~ \#ycQUHH_T+X|rQHkr͵AzzFpkΗG{k!a F29I`>Baf[[|fTD*'; E\ɢ}0Uh7a?SKplY. D?l`yW4m62]ֳZkLC`` jw$&'@ppp(ˆAYc Y[&Ld8NnC1w?JP` !"WOjPUD*7;  CHhKIb0`QJ.ԔTN΁Z+ B.u2IPz{r} CA`TO`vN];<@TM- :z08knKhS0q wCݵ-y.cQB)mkNnGu >vUU_]PPވ'Rيq8u~hȾMj~0a011(&фhBWl0N4_Hd(*cy|b!22T s$BӥN6kt JRtvH^rbbY?~{}hv <}c1ԆvdвrP"NAwEb0q+`jIu&#:u|ؑp:DEESl+&''ǣģ 2ҳؗpb j`4mh4aHX{(~{Nŀbpm*>0vHB47*ltw|Hr|M@ !iTW!3=l`,jFkG8{* ]46\(шZt!M|R `a21M>4bsHKO'33l%߭hX $<<&jJ!LJaϜ]$J_uUU]C6q)e6aZܽj5'BԤ4qVPɄd" L#BHCB!jA(!BZ$J!$B!IB!jOصt!BqVj۩G+M:Y!BpB!D-H%BQ D !BԂ|Kp8p:g: !a20ޝ[ϱzd6I~41o{ӡsw6 ~C.E=~"6ooeq:.*> C1n * _W?BdE4j6iMb^ykd/T@=R6;~֓j=wL>d4 ݯ$0rOA!hqIXl߼1GÏ0]7-ְq*ڴn͓(joIr 3Xl~;l`:|{?~ ׯbsu#vGvE͹oO= aݟog;@aR!;@bR*?G7foQK\Bq6R__z-g:".#[7#,,b;+=ACGijw-ҨQxǮ=++[a#wiڵxZz:&NaڕuW{kBT*),ѡݳWoӼ¹MF{$Pm^/ϧmfOHbܔ9Lωӵ!g;̭L9/ jtoٺ߻vSTT(K+[gJJ*#nj|n.~]<@AaqS2ku}iYǵ&5-IB#z,z> LPPtٷNu6iҘo|IӦ>jW$<~!88"ZOvjΡ#'*퉪LdF>L5IJ !䜨l6 ~ IIs>9_*44}<[g=ťS$V!!$g=œs^\qݳuJBQqp*Kof^f-Z1tDV\MZzTU=! ! hIdCFLYV^$3ԭ7>ӡ!Îa\IΜ3Bq0LfέXD=2$95+IOKAӘfg: !мU,i'>_LUUB!|h4bIB!jDw<׬]GN7mզNo(R/ !?*:U_|^xCw!$vyKB!'IT5l6&!B=Zb%O]n 6/Z>*> C1n Ǐ&&rMҹ{o:tεӦ>ߑ>Ѻ]'O{Χ{~Hqڿ۽USeddfro[֬]W7'Nd֬YB뮻ǧNc=F^^+V`:W\ƲeXz5YYYL23fx o+d,^l.R:͛͛ٲe )))s}B!N2kNOZ8CZV(ߑꮯkϾ߽¹Mޯ)4/>Ve:̈<`쇉vSħXwP˖-3g'OyVWOeE!##pw[8bQt"""++[gll, ۻ(%%ݻWHBo=>g&w̙[9)I>svDX&3th~ϓA2rxPY=lʥ_EnHǮ=lN AY jtj ZIRϗ֬Y BQwRRj…,]=zЦM-ZTJ(ǶXwTZ}evӛ^Onc=5}yOgܭW\}-{;oIm7" ILLL\WMu; aጛpS㮻"00 .z袋 =C>}|^TMϠA1b\yL2 !5T6\!wVM,B!8r+ RB!ĹB(IBO2׀F4MD!BHUFϗ$kMcg: !✐t0M$Q !Y"FUkBON>f^9Q X2|-iɨ !h4$2FҬE+IՀ~t֛NzPBsa{u$Q XQaNgΙC!8gL&fws,fd㚕pBQ FƑM;`۶$Q ر#,})F'E+ ?)BMS9x,bz=/Jl?:zwCB!f͚ٔ_-ಫ,qЀ%iL3BqNh*^/IT !>b4Ni~$QB!p&Q7l!Lr֛7o'Ov?hڴ)]tg:ZAA/2w{Ou7_|^eω8t?BQs6ڽg/C >aTP=c?3QQQDEE/x裏ٳ'c7|i۶{ߎ;6l~i2UB֮]; e}?B_:g(;3{wy;ý?119s'TxL-ZĔ)S<a2e~8rzN>BKdU}nCеg_˃?n(1`0Zg_0Q܍'i[GNݘ0i {ss41o{ӡsw6**s]$$$0j(ƌW/ҴiӘ={6]tPfӦM 0}BQ^z-Z`0~},YB.]#66w}Һ( o6ѳgOm>~A&MDpp0VqƑQޢuX,tªUi׮Ν;w˨<@dd$\~yտ6l_5礼y>~>—$tԡݷ2))? /Qn7ܷ˽o՚5|'l߼ɓ.fl >vlcG0]7-ְq*ڴn͓2._?upVZ&99hҡ;][yׯg˖-hԩSycŊ[Һl2V^MVVSLaƌc'Nd֬YB뮻DFFzԱeZ5 ]{V׵g_>tY :MWWW]L ~bڴikM۶m0`k֬a\('|¨Q'77cHIQ7ٲEرcĸlْ9s0yd7ocj( PXXHhhhyC K IDATu~(Bzz:ぁ366_~]ߗBIN>pJaaa\V}='=|T|!DM3;̭L9쉪LFF-[po٢9MJ&KV}N}֭\zUt֋tړ̬,]Gu&L@JJ )))=S#ϬYxyeIJJ:v&P .dҥу6mڰhѢ:]G6@@@fLPPߐ*}s.-_پu;v:<5l\IM<uf!?& $7_YL*֯^}عuc>V[?`PΗKܹ'W67\sZ:wWk.{*w:_VQQVʡC|fy73c p:ܹ/ 0qDꮹ2|Njz B_$Q=&3ۻ(PE b]X$K4A b5{L,%ybIbL4֘GEAil(n sϛ׾8vvgfEC~_1ѓt~Kcђh6| aa!2c}E`Xl6m*]vZxnݿӧO֭[Z322Æ #OoVD[nEtt4ѳgOtܹyΘ1]tADDob 11gΜY9W6QUlAD+;&<\[K.O?deȐ!ر#f̘acM3F{Q͡rQG& D1Dd*DA)F2AD=QdP|c6-l""՘喨VZH.EKQV-ge< +# ""iwDz_p;!b1CDD oܭqNkԤ)ENv&biVZFѨIS;< ʌծcajfYҒb-(3ICnqNDiA2'I(|XoQ=I@H+9{o6""""ҧ"No`;8yZJ$v|,jױK=w<"""g8֡pT@eW\=<:g8DEppF8N1b\E]W/_R>b؎:WLucee TeV.bEDDD/ DŒŸ~sq3" (ĞDE7^£yu-Vg3"dB؟Hڜ+_E^=uY)HuC'e DRb'""]agP% V+^"I$?PMWxLyS{~h0?͛2E-[cgѧ@l/t)Caa!o}D@`u)SoȪ0EŘ:}&ۄ ]2bWF;#u;L4'OK]zD`=Ҵ,Z a:!8غ]ۿ ]{t]M[Dx.li3fXf>Tqrh\ Gp̜=2]#Am1`P\%RVWHEUEmس~=?(+ϒ{ͩj"L6Cz~شKyy $%57*oDty}b,ɹM9YQ~ꔱbkGc2h (--[=ܿ_n<[A]JJ*%nڕKX`<}'Lɧw;b<|n/6}\,[J'O~x̻rL#:fY<gΞI|\vKOKWGLؿ"64mϐx~ 2y_r; ſp>rrsvݧKJW8rZdfecpAddê5d9w~\:~}`jZͣT2\ٱ͞؋rWOx,%Sv֬ãGp>uL~9m9yglѸ| .͛cђej:):'+O2V7zhcLV];uN>V/ i/3 A8^߱W0*7R  H~UOvػg'$%#o8::S?q0x2.Z [_}K-P MܼvU-U앫ڽ')ܦc߯ѬiSuK|K6bpSx̻ۧ7B;ܧYYW~\@~쐴\Ixc8{*ݱ_Y3zc[8 _<WWeW0ۄ#1j#JUѤ]檎᯼awPR+T_.ݱo\&wѳjTտ}SKu41ymyyeTvG=N٧tYi3~a?|.[u7errsv9s23QTT$ݶE _|^<z|Y ̺|ew]+@ u-NnM7\2YY%uVVrXu?BD*UTE)Or5i꟬D"=/(OfPZZv5RG*NV؞:d0o3gLT_s/7WF =w6nHUտ.)X|%]ӧO(mCչM9Y2:WkC[lU\DA 忟=֗^|FAT9QIeOȱ|DP``l˗}7`ݧ2_hۦ4kenVO"yx#5-M@jZLم忈۶? 8qΘ @Jj+QXt+%5 nn}i%GU:o>ż ѧw/Iu#=#C_}pf}4=u &$\^vmήqSչM9Y2Ku{ ŠsRRR@z;AeWU-? p|p\'lU9_ܹ`QTTQ^~9+&FO3<bђ!W$‚EKeGf̚))(--̈́L4E)u6JJJ!$b4m!p23#,\,*ae2x4ٹ.nIR`RD0b I{HeUymO>)ӱnJ|&L^VՇjUm7xKXt9󑗟EK 梨vEjZfΞ'[܆BdeecIQU6edy*A&cС} ń1'BޯO!Zb銕Xj4mE[hP!ax="ziw/+t)#GL$OQG*S֮kseǞ=o>=]CDbμ iTݔN]{_A F:u_Us|b,Zچo!,,D&}8rڄc eQU6edy*& DnV b lGc+뺱 D[Ơ żEDDDL/A٣?#[""""}"_3ڱD >%"""QP3 oq@DDDlHo⳩*).4vtW H ` P" ~d>DTJY[pHJɀWC@j^}QOװV^{'ZFLL|PW3Idj:#GDvvQqN7 G"`-XrlE"""zj ''pqq믿n<~"@~n&39)NKvF 0tpsD]gW iÇ.$ Nh۶m6jwhck-[@ ptrFxqUbck͛e 꺠]HN>AH󌋋#1{4j.nxs([AeݏwSVL秪 )EuTCd9 ^^^Opi"  >@zZ RSNys Oߏ EOɓ8ywdgCdd$ƍS,8QdgkCp!9|9ٙ>|8ƍnl \7.?ԻY+cڼi EE=lڸY&}E~=/G͛HOK28\RSRep.?'*g~ľ#:TVg>?Uul>O)RCd =^x(pUN~nN2Gg*lla=y~- h뇘hDF<6ȼ7zcyxǓV.=eMZϽʺyk_@Vv6ڇ#56$t o%mBf>[󫲿vjڬ9~;v-Z$&&Ot[;hРr('UYu_:6tT֧:,GMcyll, CI_nX^֯XA"C"y%!H$e%zfL6Gcf%cƹu9znk=w护wѣ}x u888T/o]<322кMt(Ifw~-ɅK=٥rrry6Qrhvv>>>U&* *3D?bќ:cCAMTu9?;wPdy&EE!96 7' H۷GFz֮]JroYҡ⢧j OB|HL!no!FR󼼼,;)) Z}Gs>?Uul>>Cd*vڅ7|?:t`rX vvvHNN#GF||'H6440QDDDDp8H #""2c$iyDDDDZp^ڷGDDDDUScO!zTwX[[ڈLAU;dZv12K^!M_9e~{Qry9ik n.z:c3CPv4|6kVl9[@p˲/"?㏋PR*AؾUx9I0s(V}u9l5Rslq$g#ȷ>ZZy n8m?]'#缌֪3Nv%>]%b|W|2JQQaq)&. {\#agS яwH ~J.{3,{t\K&2]q]yU>8mcHVxoȉ1.8>́3OĬEf'/Cx; p+y7AxOP*H>{%?؛(xZ 9{孫gzJ 4evEN~v-~Y,p/|Y24rE~NG;DD$~nqZ֪~\3 Й7/|k3O&hGOmZQx Ucj KH%M֓ONG6b^ToԆg]I|*?W/w'$;NnN*YUG;DD#-UPXRX -@9Ϋȏv!vJJ$Jbik];$$hvOɺԂ]ZHNCԢ*˧jh/PZ*F\b&ha/qj>tZ7F25tEX`#9|I+~Az1(m}Zc=F1Wǫ}(Sc#.\p1j+IDATVk -ZZ?~ @s4o_|Ϋ6{K2رe6GvFQb_\}2gbV{Ы.Gvƞ#(-uO7j<Sw|1!wǼ{-{/c`Ԁ`3AkB0JhL\qCn9w+̏ꥺ+3t?b!""9v(<}s3ucEDDDHAW,k H$(+D 0a1'k_,XO+):?׀%!""B?A?ETt^%8ÖMVuvk׮VpYZjsH;}xVdz<""""C<""""C3},^(및-vh= W\ewf v<""""#k^ Ȭn ]Y^cEDDD ApusBXagEDDD˺V-lލ| c̣bEDDDv: =ʇYrb9YA@"`mmU%M"|9iW"\ܴW@"aEDdw} [ "'JQno^O@krDA Z­düj=&2W$Z a:b#Rf W/Au:am"D$bI+Aɕ("sȸw֬Jb}7mgvA(2kE:yLdA)hNC_l[=J9Q\Pp8,QRJBCCYDy9Q^| H5Q~mp/(N&y V6$ s=&2WD Y3*LKĨe{%rtޱ\'W<2s)t\QEi:m'3= T%jq<0.njw>"$EDdx%SwD"FFZN~} sکpYǧEpU;*{UZI {>-r쬴d?Yݽ!k_z( nDiADiADiA'*^LVY'7ڽ CDDDd.UY'7!s H H H p Hc*L{M""""f*L(""" *q8HM8GDDDCx#"""RB󈈈P4<""""% q8u8GDDDT:Cx* lDDDD&K!QA!%?ͤ]aXV#Xq(pٜ4{]+從79?$ItAҗeJ\FDI#XFKjg;?W`G8/ϱەy8[Bes Ndžt~Ujy%<2I5ߝo&GM=u e\{<-|g5=N~v Q1!Wz~ʳOO.Df|[1u/s: ZL *?'w}Wʠ8v0ˆ:}|$ \5nc៵ynj[d׎-X9WncϗS+_T'm˷ss[ɝ={rʨ$IT^"3W i39OҴ1lP4HÖ 8_:ʼ)ijέuD7[HoiD7ÈO6qF=l +Q6f̓m] +8׆ѩic϶jBΑs_ooLtt'C߰+r-3i?>%8[iS!V`cѲ??wJ'M~?^ =BVnX)lc Ļ<-]1 {> 5En܂iqfΏP'sN}/wfe^i0彌/6_7 u_$I[*I+!]lйuA&mä_7kw7xCGpQ͘r4&c-cy/dx{+Y'˻AJf|dW|Cdؽ>oqr^߆7n`ꏸ9{-r@\˓/"`TVožMy:&L\Ir~ ?xiNc]+tӥ{9bZ5/Y{\h~n>ΉcY~=׭Qgfaזg `̚.K$IRK 2dn6MӠ ^r!os72䅡[~G_ؗ{qy0lx(˧.'əS1b0 tl6}~0-Cx4g=6U^3~0-C,Ca)<鼲.32xp:T͏>2}P@OuA}ˉ˻ ?7-qs :<3qtgBK:ՎXS22|'_-S- }{q_o˼yp_jЮ oLCHF6U-JAee/`Z9:X9Jcӕ'х8Ҝ{b@O؍ȷ|YWI$k0i-|ZhV1F^&dͭ_Z^cw>{dv7#ϭdp*7!ۙCF csE#P;9j*Ə ॷgž8 !-s!>7zi: y_C@CzzP>ߧMbC󿾋G? +_CrɌCoeFŅZ:ޛۛKJeH3}"9[ge dݣI) ByfJeBabS?*ףӴoW ;m;t8Zc- ?"pgr׃?Uli]3,.yzQB)MrnMCCv2U.nV²`V _񛖾/_i:ux:}ƱoP؎}sA!h0X.wq2/Ys8 ;?2-6f]GL#wmёN|3[bSplaߓݩu<ݐ7o}y8Noc_sA ZۻկX*g~1gk9UXlxp$ݤؑ}]RwkX0}=Uڐx)&t.IɗWiMuzATr n-ؖ*>Ȍ[;]$S{9>hć<3;q}+ޗeмhd^q};Шc,H+Ք'HhѴ5NbSԱ]skf aUЫP^5硡HM؁Mg>4ZFZ{ohgUq6d# a:6oMo-Vٮ\˔^j~i7Gi53a?ffQ.o/I$I/4MRjp wHJA2hRA\̈A<2  k1#j PŌHAAʵȌ  Z̜</^A7/ P~L AAzq  "fAAD  AAČ  AAcY4j?ٳwEP9,8^=S+2RθpQ֮[uK]km`koY6٧s'qN)q:] ?~: $.fONlh4b2Ι==%_йKgm~z@θpI1^r8<~[{Jv3`!@~7e3K/7ߗYCUYl?cbEBY3YIx{r "Νu{d"Ş @Hp9Bl]4TUEqq]M&zϯ~ᦾ2CeWfgGTE/:4ߐ)S?tr"|oM^=][X\7 p#_3o"hN^Nz8UUZξ#SUȓ{aNJ(h2j~Wj4 PdխG[/?ӧwOjz&<n_32A)S?aM4lԀ]wTUekv1]TI~XT­5f3ûD\D?H16HOO'44TsSB2r7sseNД4 K޽(ϻ(deejjAxR]AFfxrعq?bKgBr 77# ~fʔ),_<'WDYd{2V___WADD}Zjح+۷:IKd2QZ +E6բԨGnMUJMF4(Mo ,)љ#2C5^qeq͠לT7ԾDhʴ1EUUPEqy)9َiXm^R)]vcZ]Y!r0}Ռ̦}u_AUr0L?fYYn OģEuB *Չhݲ"kp\v+TVEQF7r:Mbb"6/|}+Mie0Ԩ Eӡš$:)zٽv/ "(&Y.tLuG:@S $t:8?蟜u_xjOԤtzot @T!k8" Ac,'`[+NS8DEGaQTƶx;TP1rH=(-f\nUq鱞Hy3vړg3Z2v#jW9TڟߖAm8v(X Nx.'9$''`$)12-\dw h:P42K55,,XILev211 L!!S3{/A `Ph?P^)h47.P:=*业tX^G==!BOy h 83 鲈8Ad^Д 1q#ϑ'BtיL6oބwv LbA&ľ *UQ<]g>mcJCBхFǣ%t{LD9 ];ؓO1hZhArr"nl*W={III[pq{,9YzƎYdݖ#WUʍ mgO #E'd2v\z%M4:TYsh &'h.tȭjYwuIhy=ʏtoN"5= r!fO]vD;q5jDt:IN>v9ګM4$""{QTM#Ub1v+B2C=R5Mne=}?+݉m7LG _!w9/>ǿR0A.'ۍq;jձɨ./A܉\.LF#:BT F6ڕD֞|t6rm^4kM@Uc(\ )LB*X,TLvSJjBџH^IL +EGlT2.f':@I!';onϏj"'+3zY.#38.||q\3@8]хCg[-^GeJtH;QaiYPi6Ϗ@ȍX)e911ࠠ|S1Z@@@oce~ѮE4z~U*[Mt*T6&VύF#*UNt0?|Je ;5Vb&ϩ]Gx|m`62ؘ cԉRJ2•Ff*g =xy[O^^?,V+:MCQTt):(к"0GKOĽIq+ׅ44:g.;eBԔcyoBnn.)S,/7Zol 6΍>GSRNc WŘ+d28*[s GckQ|8({OӽCX*A-33T94es4ѹJF L' x ⑃4nZ,d25oC_3-̆^ˈ)WbJ"3ϵ+pf90o a4 pp:99pk*SXZ^:t:4pǔ~mq9ED=16kˏG}CG@Z1^*ygR=7.j-y0yFF:(aKLLf׷ˆBuYc=gX1veN6gӛ+܊ 󊃜4J]ƍ7j/S<_Ь[s)LRQ^QQD;.NrR2΍l([B̋b"3SG etthq=9.FF1_>(?4EwK<}a`0롯eR}Ff Ѣ*<3NŭׯHԦ8_R I<\nwfFӽUsjߌdeZɄ`Bg4z> F#΀#Sz4Ҩv6%fAR 4,ժ!bj@E̳cdʖ)b H BB@y> "2:ShWmi԰AWy79r6uh(IG͕<\h1=6-M`4`i\Ӿݻv L#";~n {z|||Z, S u>\]Ɍ^C?Cӣ{z"2y>|ohI@gTZA~=T]Hɂڌ*sBƣ 4MKKI< 8 ~6:1Mpf{^ܚx!2(/})\%OK';ͣUhUVp\$6lGM8SdS9,6-ҮuKBCBPsJFF&Gc- շ-JXlFۈi Et_FԨ^??%? IDAT+:nT>NtT{U"99q1ԊYLt.*tnv'Ԟonb0v&<x"2Lf@Hp0F?b$vv6n%=3-ZجVBCCھ-۶ ___|}1+VACDzGXS+8AoJ ȘHpU&+WGL1Ŋ[q-+ߟz FmeNȔ5q_>}MD6|C'Փ}ݾ%6UQ6o4 UUq9Ύ1 9c &=e{<X&L&c8p&//t))#]so*UDHp0XV2BLAi(:sr9=3'djUT' A]Nhh4b!PAAČ  AA3  AA(G4=K  _1UxIA2t3  bFAAČ   3.rv |b28~{ٷ \pQV-6 ^/ n wK& A# G "f(ҵ$ AA1S^k1exc?zE A3¿/6 "f*0(bΤ8 r~Y|EBAČ  "f|7nYa_v.?{<֏1D6W_gsqI t[UOX6f1DFe٩bebD|z0t&R O{1JQv)gX?:5!2}I/+9^OLBn.|1T -tz.?c=#Gi[HOl_bMv[Hh3'/m[K7/v.cm3t-֯5oḙ(foi<=wSnu[ٳ ǁإ]Kq߆WneO-C{9ea枦|qw ,rY L8ysv}Dws/0Ʒ`ڦEdٗ77anRr95Gw5c9x/dXe+y)w.ľi:=YE2'߬8.us`9Xz'q >lاc/?Y+V. t7RNA(#b&w?f؋CiWhK8ٛ g*oL`+֐f }~ւKEF8UEq#igb֟‹a*ԚjZ5o5#& bbxUInސnŶd ;sx]n7!܊wx#~V"y敛hjxˍ竟 ~V_;IĺȹH($?sA ]d MN<_/2&Lottl*:Y{{#ׯ sVMU 1Vwk-{ IСf :@ä+AKΩJt 1]d CpVi sl96Mۗ:ф8fiE6d*OdN CLEl2]sH4A9[r1R?\u1$8{gWX!G;?0Q3pc2?y/:"jBFqkbҢORLeѳP,ᑄ[v^M<\]Jؗaux9ãۮTl>{~ ބp:FɷD eDJIOH>iυݙOJAlXB)caF0]xL@;e%y~b;C]8uMϳ YvaO=;s&whO|fKgO=v$&J`mM~ԬŤ'X?\5TθLb5s룍r{gdݚ= cb ;$ό>i ]// P"^7W]vbƫ3lw2o|+<:g2c,8plI=xJo]L0ՇqrYG^g}8ؔʁ/]VŔkPlfN-))[qҮk?#|^Ȏ%b=&d{:aY=zj1[$Iփ`#ĔB],=ÕGε1v [ma2W::]x|u3c#j .=VoolzrO禳J|' GbZ?)OR=Z7>sܮ):?ɟ5SׄT>%ä;Q~s:=4vwjȰA|ѯ?!Tܲ<oZ>3`H4 wA~ v]3G;7^0Ӟi59}57OP:^Mlg _ya+԰?A _dna`;;`\vw7`/ԗ>\]~+ MVŎc+1 4z+ & x67uhPj{w B[NMӴij )ıC_=˹,_1y~{@xX%ς" oddowܤLmYRZ #;{U!$UA.FbU~3x8:m1㚟&))z"s#&衯i`* Lw3 t3 BF$\`@UUq  bF(Wƞ, "frJndÆ9HFA(ȘX ''(8DA1#&3FY! vK\ OnN6nw8L2q rv˛gEdCORt Chծ#CAkr le:gǡ1#GYR:vKDqU e͊߱XTVC"BIܿLNmڭ<#bF(ukVй4oFqR*a,Y8;PjbMWTx;NqG.@XDq5j \G"1#\H?`G \5햮%3  -L: BE&[ϗg=fQe{9ۉ" _Yc)Kvؙ-&*'o+ Ϋg&c:UǦӡ٨AfF;ʙ>հtr-d@t '} z=}c Ο/]a[Anƒ/ ky!=Nhzǧ̋w^ڳ;'scl;// 7-x!$bF(=Z&!/жrؑ6𬒻-<߱?s(3mbwlG@~N4 Muuj55ZŨ[d@a}}ǥ,/MCVr>A#07~)9$}׻Laoy~~+fr\ιk,hBL_x{iCdt {>̴YEZ\wMeۿ5imDdt >뎳s Иbz>W Γ>iݜnc`KznMo{"zqh^':7nr%na VzbvkmCXхkp4zoEo9ί}yZc/+Ny.EtF3pS4Jȑn)0ّ+3AHRyTqQK83eBW/i5ti֭?#03][hG_{ kO1g\$BsL=j!Xu>i2=W΀wh-n}x {9Zpt&󄧩W'i2t.ϖ=#Gi[,:Q H>Ĵ[{m.ϥҥD[!WqN0=4{/uNVZ^t!=qd&{s6_6jG~!g~G=;:?VG1nzo߸Ytso-̾ع}ϘWד^pr|;g7; }l~gě[.͎c6r+{}m)r'>ʷOLėx^5Vet8q՝49y7>Fc_:/uo~=}Ա1O؜^*nb_q${ɋ¶~]Uh) mi;+Gtls#da|/:G](KӕWZrq^fQI9¢)_9Q^6k[ytW3nXB=W|߂it~O._K=s!Oi9[Rk~͹F9|ٕ=ω?z X\Ō>ONLoPZ}/usQcxaL;W-pm[ɳϼ^dމ#P_7?1ʛ HUffcs*o8V^xdCZ964Zk2jPZZ`Ą!XWʿfcpTRMԑwWU.X?yJmzOP2HHc?k_34t z&mq1ֈSxc*Zуz7דQ܏Ⱦď {rOz]V ZRۤOzx 2h|3"gWI iWՄ˛Q?Ɯ|ĨIin^hBak9&};w.3ӛ9Ee{6|oAj^Xkp;XE+7*|ȉ%VL@p x۩]PJMh>¢wrʙl4e欚]dїc1c!\3Y0.J=%0B-L]fT:ф8邊!KlKYpPc !ΩJ 9ZJzOXi5r7Ds}_.:z0-ݐdH48NS9^7ikp37ώh )X)~FOקxnΏ4˘Apr,ۋd8t2IF n' 9d\ ft#FU劦tq5ӧ,ic;]̗ }Ysw&Lct\{ѸY>Q ŌWgae+ߐVyplw'}˖̜-9]SJP#gtZvpRepeij֎VTo؞G^6~5[M<ǒ'+խśek:G(o  Iq[Yu܅#4^^Tɭ,?r0]ǚ\ &BL=.ݣζ +Y!/9OΡ]Vؼ4δ,gvե],=ÕGε1v JS~[maꄟ٘ 7y7KE@hƮwi{d|AFLapS_Z&/O|ӿAʘ9&^FvAxD_tii)g)]I_]gqt'Λ< T\Y5qȸ AJ͌amxl䶿AqdҼ2Fؽho F FM݃i`w Bi1 n_AAL$2#\`@Uk1 A.l48BČp9W&Iq5#ˠjA.J5cHL#.t3 YBY;Aՠ(n&q Eǰ馸Q^#f r~6oX=9E.`0Nv#9u ; ~["bF( ɌdG]N.8B&b4cդ$'\E zaaB,84lѴ1 NU5E߼KVv媞H=?"kGPʃ N8ƪeӹG"UǠ~PTSǏrowvݗ,_]v N`SVC[kqS媞H=/b!ju BGn՟jkb=NAZts zB,UU0LhUe't:tPU[sJ'",2Zz=reRelU^ aO<%Q(^o@S*hj*ekbkfUu:3mU*TE*sY2e72S!*f6edFF+CrpqmMS\ŰN#>ߥ218¼^Oʍ^ sdzEA20lY4>q̿t2q{,z/μ)d\gv߭Z_(a]A*P+j*OGXPQ #Q'\i'Ð_e[xW_I(JKV  }@)cU%þ.yд w^VujQvGo(y{4Y5[ؓoI١UkԢj<:ci˸(6dl來ZXe,FcYOˁݗ͕E\@cf~~[wiA< 0+ X#ؑs߲-Ý߷c/r{4mݑv3sw&VB+6 IDAT{=í]:Ҵìtst%Diں3=FNfe+Bc2pBj,N}dwmɿ1,;/m[I\=ϥݥ>zwi뎴 n:opO߮4mݑ}Y\7>̭f؛\9 {9eb.@6vo&M8wTTG ˲*Ɗ "{${7vc7k4KL4j[L4{E JG: ,~\`=Gߛr;ǻ-3$\cG(>w{sЗT;]i L}l{\6tVkZψ-r_G:]GOs46lH]Qj2ro6{33ʐ z[uLMÆT]g3S9T}xe&UƚlmͷG4hjRJLfou=jS=/&U&+1}Q; k> g_O3ҟCk1oq[IcyK9z.ɌX < ^tGZ~̠u1Iyy|p/˖>|.څ2%~B9.WrLrNq7IK1nwڄy%||5ͪ)]XIcy\[=u!1\@-[}8vȾ4SzO)q SL*ydj O*20uMf{ b4< y ^0C09uMr[ZnpSsfwTu5eKrxo'^:5|B0ZQcER&e< , ШըuXc.}k}Lhؿ7%| !=i;G2ڜWJn~nƦGtLG*]J0sN+Il63!?/_#+Zi5VVɪS^ @3HҾ$scx2)zJJ>%9ފ+% EK@+7~}a QYFH^Ű~P|tԟ9oW=Ueۖyް>g~u5l!u,gsI \.s͟?RsDeړ$c"Mn&27zJfЬ-Y9ڦ(N'QWQM1o V 2w1wqM7R4; .đN"<Մ2tkGMfrB˚x)yOn+=,;Y'zLTO~&{glM ^ݒkE04MϔM[/T$5_S%1ڴs9Tť5Uj3S0u?$v|U 34LzalWתXvQTr0ekʔ)׏9Wh2rˊS{yR ϫwPH &+(4^!MO&8_VDc֝P_}l%>6|Uc7DY3*Շ@ x?Wf$3R}B.]NH4v^ P"A"6tsX]yATNCஐ IH:യ c&UCh6'JӴc ʙfVyБ? Xnj5%,]{eE-4WM>R5A4Z!PU<\Ak_^mM4%U<& 5~ͨL鼍̩rV,e\$1v}t^rri%<ĀT2}]gF?s{\N&R?~EmJ0we6T坦S7:8>L KAV:ݧN r=P䬓W\! ?F ;Ú 1~ M~>3{H]%.sf 2?̪)~"\>{ |PK^(B'Uf~[} f0Cf$-WsElEaMV@ xhefeBs qV673"(T*RRR)_gJ122N&D0#M 穿/qѤ/斔*낭P@ @𶉎mJuJ|M4 q1QAjdP@ @6pR*XP SSSܣzFB!C)xD0x51B+3T=+!10 5U#ƬD"; @ {>q"VB @ x(o3ŞO] >L*uNzG2-KAcS %dRZ)ħٺ~1>aA,e8}$beF x$?Y Nan& 3% :^nLپ߷FB )ʣؼg[xl\UB@΂M-ǕqvoM׉JJ˞ݨY h?}?> oet“~jsL.'Sk\ad-w]Γ]^bOҧfF[󑬹}ҨήTj5Ѥϲ`OꎳkU]ʉlezB:8j݉#Cd${rG*\3 5?$}jtb4쎳k]ZMޗ]s`Uvٵlztj_{E wq"66 X 7Ma+7Ա2GH ܉0j Nm{9rZп.)E)bpv#L3@%dO= t_%@CX6g3m9|naWPG* _&M<͜skXv$<5}U2>N}(f_ \Êl^|M=_lmuX5-0& $qgHx盿.}e/cM2f3❄xԀo罓fnoB|޽3Ð; RX;5+ʏjɗmcӅYOTfٯISIQqisw8Id^; &=2FQU^OɓiSUz!6C~%]C6 r.cfQ?(KA92, 10F!U fiMjR<*#3 ,6ѵ*qו,_'UiGqA@ x+3FV'4YG(eAq |ò%!w"Yju,y˷qz | R h4R@yeSb<`' QU__OF%jH{@X+192G7ˊ6v@h4QzgXt/C#W)LPb`V 4OAO; E2#@ @ wx}ü{4zfCb uFIh00E@.QXX/q#QXX Eo O0R k"޷pPI(D |33w-+V&|n_%5U451`fnEU "޺Q |TP&EL*Y(B `F_LL̈́B(J7f^Ą8A@ W`@ "@ ܼus+&@ ԋf@ iٚZ3M M^?b.L}mE@ 4" 'S{'b* E0/+F;ή8ފ(gW@x/)(\A!3j<ܗ8֥;ήUwvQ{줥J' *6S\I\آ &w8({ P:ΦE͞z&l,:o~e&&_6 ߬*sumF:Ӷ**7=܊I;OWw]3hXgjUrǹvd\SGҦaM*QZylq>cu5]i3Hq8b"P!mo|xJ5k'{uʛqr&ݓ;Rʟ1h%^$?%iTgZ4ot“~jnw&xۚ'Z8+ނ>щ=Ң;n85ܪiӚj|35+\'A5uShS'VN'mrvI1WY+q+dzsK% Ji+.DHJ+U5:`ȷ̘T-ɱ;^즋)ftͬCܸ}S}kzO05  'e|GC.1_ؤqqwLJ1c MWi2}Y//{q}fPXܽܠ]\u?5 wg4ybϮ}zfm|S 95z޿q?;F2ǻ>u+{k1.͜c37֯S^J%dO= t_eF8ˏ{'-^^1$\s^Lx롛9w6<_9Ixn_mQ,% )悽l@Yk޲,2 ;Hp{J ~=@*Ѻ<)fbIv3JcV'*=+UVrqe4U%,ၝ.:oeVDG}l)"vRPC-LY J?w0 +Yä|o% b"U$%0pT)=.a4,@fJH/[^13S8@M,VtzF9 O/ӿ`ZIPI=uz5*Oh@Pɫw۹%B37a5hXcCfi0Abn (1{83B_c*t F&2+MI$c!kҘƙ ~L h?o5(i"0=SZ<tR!҇  pl;gl gz谧^y_G<7;D}V sp^v5#y=mJ;j*3֎sJ#¢gyexC. ZQ*A鳃c ,u<)1*mhjoM~; 2V ؛fn$CIFOL^}0W9 {9Kvs;$q!.q{̨"!fOs(DSlL 3S,v]UPjj2A+Kē+O(Q r4iTAxSy uP'P@;kIWA+o' \V\mGeZ {({^ZW75ĺz_DN.մjzFV'4֖IjI 9ΗSy.z- aYu$!wJ 7>W̯շF*qOyeSbd IDATi ^I_V{tSУ>6ENrW5g\O");q|&nib뜭&I$ M_̅8 gloIT T*~ $Grp.볁Mc9tY>@]8 wq5,kX#q;RQG=O (;ymCF%fݾX=M )L1tV^cW.6p">7\K:Sr=Z4qm5بG ߽M^?.;KI4;ŷ@ mD0#H#^#@P0oƞ+N0# &&@Y/fj5jVT5 &@R/fQTzJnn<6z)x0R,'JUr2beF AQ f.]hO A f:\hO @ @ `F @3@ D0#@ @7чi JnͭSx#%|DyI"`F ?԰͈q{PB@Z^1ܶ[]V h0g`dTXʖb,[AfZZŨ_q^ܝa*mbhӤg6eK4\Ԓ7-1c:sIO] rbٞ!޲ &zVWY|kLv(͊a-HKg[,9GBzBVc'[P֢Ԯ7x׬292gw<:oM~2+JP|$gxt)L&XQZpT&aun8!_hd7-^6t^a$oO]T3{>u=ת1*INRK,8n˹AUwҳp)>C|?*϶BaHn?^h$/<{LZ\&. \cRxz^M:i/w" je5>@En "&Dyѿ$' K%~uWg+)&VV|2Y?a !޶\XLK P8Ҹ---L΋38ʴaufqgy㒡5̖Sh{˗7a%[̓p`j f/֡p2clu~ǝ\zҟ3hR2ߦcWr12ԡՈ|PfϦ4}iiWIS:PB̑dˉD±2|Ge'dg9Xa"5BaLFZXd ՚b3l_#n4jvôʹc,Ogtm世ZK9vö{'c T󯪻z/oL0ՈJL),1,GS]E ;ilcy)>9,z&G2U5K*~BPɻ fY}ڐ]+S/ -\™;"?Ah' :C+0 6gl ʳb{^Z|!nCEO#!l?pO[9۽"ITE{m~}Nvx.;v(Ow_x%^]w_+鼈`Z58V*'6|ӚݨbԄ@Й Gx^%ŞcȼT=z.8;pz/tuq.DHp/W'BGl `dŨ=Q+2dŶj5.d' .k}K29H(޾ |Ã7 'llaS&J&Dš3Q}eDf[U%165шxP}}o1SC&H9yN~w㿌Q8kv1rb]-˕}Lnႝ!R9~ײm5EÒ:h8<ϥ.63v8:)]#(hwm,eԚ/hVU%z-Ggp;$L= ͜Зsa)TSHLJ׃SƯ "_Y:daUVa8(,(m-;?9M1-V1+&R^`Ʋaq4IX Wl&hP(#}yE?'fԦa3"\Re0|Np>xB{7gf e8$D2dJd 1r3jh[a˻D 8Ւ:*}Ө:wC|Xɜ~巐06n DFmh{$\WG8OlFLg(Bc)TuVG3μ$sIǙZP&?BH%?Vp43ԄYF:e0OJ|`m-p0Բ 4ۥD`{1Y69Եc*E4UYTx1e@y\ "&]GWMG0=iƎ`\'i|cO?[>՘M8cx]e{N$)IOũp(n6TrԒ -AjCYm(vSYWezv^5yQAg2ޡą!%댋 n{N$%)jV 6k>Mɧ]_Y"5ézW~?Û!m3ӱr,?㠬_ nfR0jYC|mEF +7e)Ztn7K~(1sܸ2*,s?A7wd ͋H#RJ)" < "HrGtyǏkaip/ՠ-;$D%7S\OߓZS4ଊfZk]3MʄpVTVkdKْU^֡@%ɉ wmvaюɘŷZ%9$(Ll]h=v,'MR#]?cRu0Ɣ5I[I C &8Ǜɫ#Y6S.Sm򳝑-eUYv_Yw_ .$؃ LJ8*< |W%Hbh\/Pp%y $C$RX6#M0c ׵1^A&)*~vyİGzSCQD? .] r}h+h~}&A_1)M={7bTbkZRDG;b0uJ %!EE\M}퉷sh.-D1L08sIÿ`UID>#xC%%Kyz.rc~#&4ټ$&#h ]t959?ix^pRab |A[v6-_fhU9o9]"R{9 l)ee{n:3Ĉ4UTҬ>Vk|U,={-hӥ \bmS vdzTsn2GQ&">B-`~8? Hq꼞}2i;*À 6XjNX\G33SYw  H[GkU sS Z6?m|)ge\G p/|i%m0zS(A462/)SgJ[Z`S{y}tmDv ,'fEn3 w>y_W]Ao"JLڽq@a_'=X{"tmCdp`6tά ʮ_FQ=ˬgIe#k!`^a^g%ͬp7? h4`3Ya(@Aȭh}u2}.[Y3vug?2Jo>%ݙGnQl:@¬7B{2#:&T`gĞcάH|9NDLni"7ƒ6ֿ벝r*{U}f@P+3@ D0#@ @ @ ()^U@ ?beF f@ D0#@ @ "@ @ (U)ʍHzRc[<㇎.=,8J ?F{Kcs)'"4p*?GX"V}.c?ۉ:R|w#TbԑlJO+;jt<ڿcPjOwXG$*Ѫ݃Aֽr[ar.SpD0P԰h͟hg*Ttl3W=&0EV8iaBgLN/()Vގ%YJQ,;OBzBYLv†B>"3S [r2(;&nXZآqN}pK෩(ki²"ͧ& }]}٩өU;rh"S2+;d=uZ"U4:f N #BweM3ną8=dZr-n&e3CghM.6Y#7Gs`yu "HُJb_Xב-Y1G{{5ɏi:sxLlSg߯ῌq\2a~ĤhL1\P P ϭO2{n< $?,/d;3B}x=%k/NbǨ`&I_soؑFKKjo_Y;1@>LNgR4YmZ\4Do w 9}*輲`]ua|Yb o/GSV>Kn#GY{=9MOϯ|˳7<=KآXI%:V#[H` qK׮FŶPe>=UsV1sv_EaՁ#ϴVb&Y29Ůr>; -5V\V$>4b}Jz3;`¢$忨TPqt\KznEU? MOXcU姌OXF? gb9!CG;z#2g1'6p?v~'4YU$& 6LmϚZMxj2흔swdO>B~q/  iӉǏ~z#(qwv̓%' Vvl@Yx—o aKλ:}&b,)ggpcq&ikOxpz({,]A1$h(zo>{Fl?'t1\NAt}!!̛#6rtS79ҿ &z`dUc"r;윧_]eZ/~̒Q[Wuglp)Ϻril*w[:,_Ы 6T4M 4/"3¥!\iD?g B+)eruRks,M,#z>cVNŌq|XW0DBiw=~ozE' Y?#[:EYofVؚH[nj-y<(Y%0+h8ϳfyק|vw1YylM ;nD9wOzF.e<%⮰n,?7c(8p1ksׯ6JL[<ڎ2݃ sWIis<.LIRY*/13~_||,MK!H(~n?w@bZ;YЙSf!|sUM%"g&.NrL3fpp':\iEa}ZȵC`oN)ki̳ :PiH__Rfo00X^>#BZVaa#F@fm<9o ͦ|@> 9bOnD -|$ў)#RJ24k00$)e!338ʓyrS"c15<c78wo$Qyt)oҵUIx<{ʬ~` IDAT0ߴ%4ۂga$ ϢWp6Y\'"D)F GfgWѯ68Bd2 VJ*3_R R5OS%sf*if"Cs[f9Nٯ}&ﶾ\ e-y7 8ed4AD!-S{I8O#GZm0vĭqa4Mё0"_IFl? 54'dax#, 'ʢ,=/_2WLVr SmI=N:^DOy֕Mz sͅ?qxFT^= '::Pr洕3,Jz&D(D^Hβ16wco9ʪyݝqӵj`}Œ\\/&25̿:Z+GՏ9xo#36p9^H™ fMt8b﷠Y)=E"1N1 :j'2,NI ^ Y@)-J-)^pVJ G96o'ikNLШL(DgMl([ okh >`6+ N lyCڰ!ܬ$V҂H{H5)gF$܍NՂqo?Fau,aeP6=]Ț2f ұzYW>6ͫ,#/-a R4>0C g|}ܡ K&1ol8+m6I[:)({E8*K%C@@FCpPA BK+{t]yHPhQx p}?ќ>w:\>')7@`;Vp1-sZK : (麇6r:!<ңj1 'б<'7wGKYqƱ8$Q5fmTƎHByW~W&f,rpԨЫ0IJz!eޣ|kT^#2X|%SN5: bBY=nF8Q0z3|zNIk3X1n&Y>v {QW}skdaY -zTI,f1{6\ 'C Ȁߘv-VȞ?kߜ/!;neW_Q#3 [IrsȼƖj;; ec +fs'穬x)1;v4Bm/MW%u`]{\z?\EEAU[le/OjxPr^_Iϗ} |s>h'%Yi:mǀfQߏ\Øj^9bM MCNdExzU/*<'7mP1 R !Ŏ(f^5ecUڰl,MMB*}&(۝UN(UN(Ux}^o|ԭ>n*'@{{rJ{],f*5q}+fSSqQ"T 2Y{̋#3u(21:vgցad3pqeixSqe9Gb>@6'm _EI"2 W[v kGЗu CN? cH mGӑȺqpLŖKH2ݢuB 6w,+fqXwGG23=8g˱+N>͚D2~ev5GLN:3Sk?'dR\G7^ ;8!OCvȯ;}V #u'|4̔l}W_ѺCyȷ:DBzIRO!ݟZvv%tLt84l˽-fRxzZ ˜fRZ'ӗ6**NZoB#+5mjEqˌ~wG;t.ty9'iYdȢ&QgfT+, |.@;f4)² 89\4U܎7WlAMK ]avljh1(Zڼݔh.˱<[h(}Ԩ}ZޢC6Z'+?83t0Zhfhvm,/M 7`Cnq`PH8X^3{4+ <]{Q{7gȂl7Wߙonb5pwAHx mEhRC8 rID*mѭۧiѳLd0<ɩ^218},#~!03̘ƒٍuRfԓt{G|v%U(TC^^-$8Lfp.X¡,2\#QYN/zU9G'1E7);h\&yƗ9_P{1/m:7 k$ڠe=tRrmܾsU1r/nwqGkmpD~i./W?bs9hUkߒ׉rW~y»-#wޛn~Nk ߊOQӇuufn[,e& Uc꧍ItˊeڍFc0t3M /Sg'qpLDUO>Ky|T-%;Z4fD BFpzWHRpq-+f*;yI& d"%Ij`ߖ(2 {FMKiuJ(ѯϾu=?&mdty5M-wŭ`VkYQSo>LAHvʟ,~josǎ|ldbΌjmFԉet%cZ:MѬ5lfŘNŘQOWxg>fY8ˬ?K:~Byʀ1v]G у;Y6s1ǰd1;֥&fʈ2gјN0bL81+ܗz@S;l21bɈwswC̩hCqv)szb;-y]X>v5GNJ19˅9pi1Fd)7>HTƼoBɆ_?|%EgmHIf`F '1rG3'F{G*i^Q"7Z_>\T⮝[=CP,V1$_Sh dmov!5{i 8п.xVڛ;L WMO}?:[K2cf?F[}@ֻMaXæmL˕]W&li77qCXrֶ9m}qֺS*=5o|D::ޗ{SSNBۂu"ku5}o;~ֻYcM$jun+f} +w6ݼ^S1Z꼰:0^MM:{E[#8Vw?vŒ_7ϝwpdj,㽳cpi4ʺ?LpV!ăMѤ* S'MX,vn{\!BM]c.T} |3%rPa2B!h23#B)fB!B!bF!}oYB!}23#B)fB!B!bF!R!BH1c#/!2By[/ȶ"b-,(B!b&uK=VP㙟X@W/҃wYoKfò]Jhۑz74<3v'wj0?1}]\unh|202ϧ_Zunh\kЬl.b@˦ad#%5=}oAYR!(It'v_#+ o^^h DDGܦ9c^|A8Cg0r ' @毽BJ)Q?3os9gӟ)AYQOwP'9(37^IDG߯:g--G[{%??_FFX~`f /0>Ε[3dLhYe~Pji;p 5cCdz0Brߊ4:AW%8dΜ}IIώ~fv^{\w) j|6~N>+ۇEu6"I֝ϑ!LOɶ3H<,m#,S`r='5>6T u)?Iz Ϝ֝ܡ;7x3Ąʤ82mYV̬Y7 C%%-bΠ0fcyy_lhڰ|ͫ>^L7I";T.0JWN|M3:70'{Sg&mWOtaLT)$^I%]CAGwsyy\H}Zõ&cmBHrDe:Q۾*7 #c1يus,.<@ìMq)bo-[5M %ֆwɘ+Ƽh_JE- _] (kNmoUgVLq~9Pŵ=L5f>7칡=cqW~"_ѧu-̓:7KcmP7eQ4TbNmp%Ӄ խ-g^i"P|t7bRqP+5=ѭMcnTb^׉ɤg<x*fx=Vk=y?;,MUrP¢63ҤpAE_%k‘~>1aŏo)D=xK8Ʈq8,k$iq}QY1V ]"OO_LdD~E!nRr3SRկOē~0/Q)A_GZ52,r>#>1e;q)1S5C@Bm227{(*fq8[|:{X&J{}eWqˍ'Ļ%qVԈ =3*3Ug'-,L9v8jTvbbY= /D=!81*+B'<{Ι0s@F c=JHc: >N1>cOѾumbY1VƋOXˑƄY5a-Y^PZ V[Eh ca]CF^/{-kDFթg<|1{Υ`12ޙ`%SN5: bBY=nFXևҞ{Pc]@HD2ƼNu%գKtl>B՟}};y!?Ȋ?y*+ ^S' [ ft->XFݬyy -ᇈ$ 9d^ cU՝ NG3֛x 7U!rx-ǫ~~V:E Z|IP}A{vjkϴL|-Z\V'kOX hxI˺}*KV%gwaYX{64gl_mmtˌ֢o{nlpO` 3gTmt{>l-o_ eIK?o:1aIDAT8`VV cҳqu''\{5_fLcZ.!o>ȲnAdE^ V?Pt&>p |g4R<'7mW­jgU/U4_DVn7^z"HHd. r)K5BwaofV|FF7Ӷb+};2*B1] <)m?eX̤F ^뵆97ͨ,ZO/mUT(4߄FW/jBՎї+v8\~sO!d%>DɐEMh@ͨVXHtK7]UEwzhSevpr'5&ˉh0$mowV"P2tqifW[RV^L[ԘJj-mnJP|4X^ŭԁZ4>j>xo`!GĄyhh@3COk/fƻ˹{Վђo$-⒡ il^eֳ'%>~g_Sa1${G|vE)P(9+Iojh7J44-9qp"@c$"(#‰|9l⇩[JvhNřc| KF#c]jlmbz+yY#Ƅ#,}w,CFX2b9]ݫvEe^~ӏ='&|4~ov(f{DOh}pQ5"[v>nM?AEJZדtXN~ O4ړmxm0څykOק3C_X^RiZio6s3'+/@_[6>l-ˌrouW]X6m[ph3w|6reWw:綾˜W!j~βy;.s26R`gX,~C>{⮥}ewV~N!܉@!R!BKZ]؛E !B!B)fB!B!ģoc$(B!B!B)fB!B!3B!R؈KtFH x: y,z1 R )|(f/QMc5u9XtU2(=}VB{+;v &όItX}3aDƲ3?4K!.~6B[&"8<7_&,fJPVI'̍j/kCj^w~I~W-ţ.,B LR4vuZYKtKxn\+)s:}<ѭp$R0:TI$=[.3YY7뗢ѯ;8e XF 9?DuFͳ,9myY1&Y>v~nht%fhQtCK8ZZ< йyq2!q?3?BGfE{? Zg3Dm ኍT3NKcDzUc)/Lf7gfa/Ǖ{Пf1{Ig4ûr:q(i}XT7a#.oL azbMDgiAdf sFwݓp^MȺʔދؤ\=ІgrpkzhNA$+12)L;x38|wIIx=3(X^E1]'v|A}+6\#+r?g`Dx[Vq{.ӽ [8HZaǰ$Bh[CSFk|Z'+b&-~3x‘7j<`QCVVtR4T2f~?W;.&@Wo*h6!0CP0x^Z8eFTs*X1[+ y_JJSi |bS/~bXO}?Ffkh@餡rTi_Pעפétp57o|sh##hcͯWRIoWBB^)x]aA;_De:wT>nx+c &T'V0d?|It>ؔByiu>3s^vZ,Lt73+\[bf1W*/yYѾ([5'|nܼAɿ.a5 K,s!}pD4jȇւ&ߐIF~ᾴjWcU&g;ggμm2pS:{*M*$*F%k% Gy܎ D=Dz\.N#bhsCTk DX54θ8X3[o!egTs;01ŭo(y(S唯 {:&H`*fq8EJd=HgTZS)<ΰZW ޞ?X0QWaeC+QEA٬Ƒ< WY18 ޝ<=s΄9'2bQB",`g]ͷ,v٬wzg57oy. 8jt0ńz >חS}̝3}(fD!~D=cx3m^"GrZj5StO3gk/T_{~]Ei0EVLbҳkqu[IP0=Dc-' ,)nh2.[BssG݅^g^c5BӜ}q,3f6H݀A},iV_#GLaLz:nkO 1oƴy9 ݼj9_ !g5co}BpPdz B/`ÛUZ;bIM/`ye !_'hR!R!BH1#B!ŌB!B!bF!B!B2W=!BdfF!R!BH1#B!ŌB!B!bF!B!B29HBo!5q@x텫#X  ~W( Fظ^bIݾ#sUUܫk+c9;8;u'E d=m"l23#B &9'PsU056r˾ǔm k<}3t5{[.v\ TRW!zO惨|55rwoh*cy64NJwTZ{3B!MáukEjTPDc=?Ă)5FVy6͚H4٩. Gufgg*cy~r+%\ ۉ3B!-0ۍ$P4ocK)ecɿM; WdN~>ZSrUQȿK`ꇽ ݨ"!&/̐U\ }e;'u=f"[cqFq#' +'Z8u Ğ;//'k~65"33B!Ͱw'pi9=7u^Fh< ~|F=2sO`m}6C[̡G1电d[9dG pBffB[N枍D{\4h:F(v`rsBXp9߯vqMH} Uz *<8 B1$k@Ꝗ_\@CeSbF!Y d|2˿P-~oSr?kH?Gڙhk吹SSp<J0Ұd8UY )L1\NC_L }V8qy>`Lpն;bIM/`y;H$B3J[XpQ#iW$#ENj`b|rH{ɈѶO&T;/W2g 1JW%L3B!x B!bF!B!B)fB!ŌB!3B!R!BH1#B)fB!B!i%<IENDB`sqlkit-0.9.5/doc/img/sqledit_setup.png0000644000175000017500000006606711714210425017322 0ustar sandrosandroPNG  IHDRW csBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw\Ue].\9PQq{eڰܫ̝iӕ+mʽgVfifW)"*?ʅ;^xy{< ,:LoiB^ 7S:V?L2^ʡDJ'⹤תIx VoMG0g''hCt%BPx(Qxx#/wo^bæoE`@>6ɱ>#/y*k[ uB,1$~c DNa !xA/-":VT{%f+{\J7U:<(nTwcQ=ԌBd:Jyxag[tj4h5>\;zU|$:&S3Ϸpe~W$kW#:x6MԴS#@S6EÈ?'~suq*^VFzi:SQCq/ANEm2ShcZ7w4O=^ؖ%k㕷 wO4NԆ.^wfq~Gݷҳ,RثGfPxxB+8^♞O6wdq|^j@ @qG6 įVWr,2޿kW/Qdnv{rkmz;qrW%{ݽ 'J!tx=x" V+t(%e$VM1ϹK/#m+Fᶟwj yw0gbW:I?Ru_3kf %yt*NXSWNZ4S++tZ7!U tb4,9طkZC}U;/M/[(WuS!7!c.ujQ-Zps 77nn Ptxto^wF_Us3w\*AB^(awMyl=x3w0׭6`GQ/KrH:PGĨS_Kµc^=RniܛgW^y I'ZNaZ/Wl's{KNh\xS~_%(bBfCӔ͖\kP(܈5֛m1[mK|+`Œx&Z'ۖ}LS/+yOA҅.?QbR=I wO>9oEQ>Л=+_>|UƸ}wƯ+x}faߛgҍ +-cm]Nggqj,( 뭦</kR_.O*M|KyQ( ;Z:osbf?P-b BQ}e=Pz2ˑ(ŻF=px*ť3֮+W@n5C1;_  ڒ7^h/7mM !f7{tiSr>C|6q 5^=zL]Ke@w 6mF RǓ>n!~o]PY-ѡ,M:5%mmV@i 5΅cq>[8jP6r+}OEdlˏ7OxOHC >\.8f>\Ճ,}|O.<򦄟' =~9ܨU<5 V|5Gri<ҵ#XǂY߱x4(Pv$o<27m2nT#Af8c/| r;l /ryʹzYj[7*N]l7yeѤ9KtcU^q=t.Mo&vUSs *1o/;y8T~*|8TeZ":A~06_T.؇jR,ᠬVTP>>ǃI! okjxL TjAC(Nf?)߰-jMxټ`hWk};h˷SX.^niVѐ_@'k|>,_.>~,ըkuiǗ㒓xhaM'EQHn)Sxy$T(Qf9܆q/o r<2J c < OV<7yݨT>hlK$?OO/~5C4ZOX\lW,?N׮E s>?߿I@NtDܕYr䆻 Ojxg;:CW]c{wT:9 }8!k}#/u{Me]O1n=xqj(+Tjq j$RIH02 i['W:٬͒T5 (^vsKM(<fߊ^qgwT3-rcjXjXC͏Esj)=(䀩֖Qݿ2_A:O|\3i\7-T0^w t''VZYCnJsSjM72|ʹ)P=mိSrŁx{ާ\w((Zա~2_A pfn-d~@AH0qLF}1E_q^r#>hk?M:Kޒ5*T>8{$Y^?o ý[C o j-D'Ҳt^FB9@a_%%JNNZwxG[V鮠t }kТޏc81HnK_<~of7^bHRb}`fDoCJڵȫ5`0eF3yQIbO;'~coQzaD*-K:d$o4\VU ҷK+_Hxټ$:#G>GpAj(X()KjU/Km["T{IQ׊$%%C)z$5\jӠ1^9(ܨwڂ>6`y%O_/+tob0Z'yw#X0i:^LRvcv|M4xX|yT@19fz m"(䟛mZp9bh9^A^}wOj4 PpL˥}dZam/ry"gNCaݲ/&?RmJ1͓7+|%6mq]Cq}qkŇH'e%C zEJ`+ۑ:<wOO4qӳN^v/dѹkOcغG-M ?O=Ҷ#miiۇTk֙?nOrlr"ѧ^>nuhr}֟J]hnv/ķV71T/r uWJ'RAP^/} %50)ox454GqOæ3\M&QMA|^"0'5yq㞖MgLJKn;Ut OϙD4˸L}%b^U/K>$4(9)$(MJ+z;+$N"켐`*!Id=(E|JyQ20e;gn~8pQ4y:+1I5zr秭S^=z=KcΌ,U\\ںroBǾK-SxM֩ՄȲ 0p䒡QoJx?|Q%8'Uk֭qqf;[B[/?'d[!?hf|ŋi@gBPңD& !FQBGxW1q7&eByǭB!3!AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HʢJqu,N-DS59@ !WY"u֓KM[d)oueܕ g\Z̲C@e|8=^mOByh4j[ÇdB!,ªT&\9~c=!!)ǨџVа|ѸiK.\h_3}p"t>|Wl>lomllϲ},Fʬj̚; a3umb DEdoҝnjoDžYvmBtti^~ЫG7ˡ)U$&|ee N9@x̧mk3%6.]75ݷ?`w{?̜ͩSYW؃Rd󶖆9#푞z:}:̆kٵ}3wbbw?6d'K9~tti~=O DG/ǙaoY[ٺ7I>nlϲ}*E;nL#FVeԪ׈Qxq._ʛowg 4ͨN,[%-KRR5=tO|yMCjpI,ukoe)^M[6[9uH67e*Y;11m2[lhތ@eN]գۙʱm_x{{g>yV~c=ΘƏ0oB1׻tfАtОMj;[ߑai{uM־H٫CF=jnE^ax ) :F-رobcc .Z4?hbcҝ;1-bqcǘ8i*NѣG( et֍h'V̨CjnݦY6fc9HO=Y'eE!&&[ߏ{|rQN&MF6 ooo sujٗX:L?.9z8P!c5־HZꐑ>nlGW2SzdiϟIIIL? k#-{wPݕ{vpiN;^Ow]o^ϔS:W+pU$驧#' ;~J̝G'jxxx2et;fDΜ9 .Zun Go4nNe- &G`7MYӮӳPj۶q%&6/Ǝ'ݓre=w>IIIܺu1~n3N/w䋱㈎;~i^rr2J%^J%##xgOZithߎq&GLl,_huٌ!O.]b67_џߵkh4Ν?#o) {^{l_cqq#;#xj^NMw Ϭ9xo&|Evhǧ_aN-Kثkj grmllϪ},FF?qӖ==qX޴аtz ֩m3!Rd)Z@f*T4o҄0 [ݩV-+Æ7o5 uԮUC>xU'lKڭ'+Uei޼-aykӑ[ZUZ~c߲yS DU8y*_OSxq:lzf6 66vm v IDATmKOPF Ge55[i96Ug>YUz0Aaw@"ڵ}.OE|0ANc_m⩓ $DRl.w|[Z.D $D";S-!.#AH!HvekPv: 0T\Kxs4j# 2jԮU!wu2JB^ B5hS%'3⃏L͜=fv>>>Ӌ._zOs:Ky82ؼӠ1%˔7M[|% 4lJhӞGkoTs[m Te= KSӦÃٽc ֯6}ڃ9̞3!;9Zg1h<ĉXW7޻U˗pA:FG~l߾eуmm֩ed:!D27AÜ@KME\?4k}ּgzE v:/K#س q>|}f@uB1;.|oٹ{m":([n xnczWWJ!p~M#V5-1IoH9u*}dƀc eVNZ828Wdd 62Yf3}N>Y=ԃq ع搕3m[e:E޸asw eiܰs&!!аNkT`/\ \9gϝPiW( fƴ)U׫C\ҝϚի L(a8޶ͬ>'.|7AÜѮ 'N".>xMds7^Qc8~**ǎQyV fj oϳ.5W Ε@L"3d do0.[9cİ!̙ вu{Upfv׿_:ґFJT1ZW߷u_ҙ~P111-8kBỹ R" oފd5 g.B^ Գ{7zvb! '"k $e$ !p BKovE*Gzd -؝_"x!PvRٶ}Z9MH=d dMܷt:V0Kۑ&DVerL稛7Mn߾`o0 f4b`P(xU*TJW:{^y߈pxл -4kE!TV,Ҫe RsnFG۬G[TReR߽kq RR܎؇,/KCGL=not0 v2mGA B)wtK*..ލGU6N˰|o.AA xʅ3\:w*Cek&V5i+csa:|oOܩp_"v6bi~舧vX_";rAhW'>.'MgM_R3ooŴ̀8sCBS^'r|||ez0OnҔiܽ{ϡAR2sҔiܿvE^}.7myp-MK͑ݫ;_ȝ޽O*Sv=lyRt`_w%#Vۥ,&Dv^`,<.wH=CiuZ_H :w5-zL2ǎP(Q#M.4N YEЯool݆ݲ қ%KSN=TF=.[jf,Mđ[x{{h27 ח3͟>m2ou'0g񴶣m4 /V-;FOCG<A{0!MfW|*M[Tl3,]#X& m {9)j5##rx%&›42;Z& m^8Whߎџ|sQ*TPY?|WXei0"{2A(\kݲkB9rB $e$ !p.==uBGsDӼh-%hMaM B=w_sSf؈8p_P\~7M.YiRQ8gm5gdi v(WJ 6B8{rtP2k apNZB7!{6;4(5K a8'@kB˲̠vpNZBs2]&pt9hM!,2#Gք²l`xB!'B!!.#AHdg=ޑ$AcrPBgOcB<{BL B*Q?%4a5x̧T*C`u4dӰtI-;wѢM{ʔDMYj˕(L͵xgΚ'''3⃏ f̜=z3gϥFGINQkׯӧT\r*ӽW_bbc*y…~7 .ҸiK }힬R1ÏM5gպ;R?d~ZISzzXCG`=ߑ/[4`69|ipBcΟwA&_ -m6uiӿ1[ĉXW.?nۼe+ogLYs7!]:3hp:vhO&jrbS}._J~3eoܔERd жܷj{+#?zpLCBLKr ewƚEg&#[e6(h411.T{…m9&嘣ǎTC #.>ޡr[WNm;1eVƐԭSH_Ě'hlW?gu6Z1 QNKg`*#7Lӑr dq=2t{z$(L;q|h+2wzvȏFs(g~~~܈2#oܰNʺE޸ߓ;h(G}H ȝ;7 p̙EYrIF|lڼbȑTg|둑6W?{/# 5WF %OBB&Ծmk7Xbbcbx"ڵq*re=w>IIIܺu1~n6\x Z^Gզ0~$㉋g܄Iv7aqq1vWthoRRH>5a;юOKMˤ;ogOLl,_hsy{2zu> ]F :©[H9d 4r0iܴ%$(0u*̈́ՠko.9lޔQrU&N&# !gΜmЄS5aWJ6o_| :4o҄0 [ݩV-l][5QѮ!kۆX5|# !o+JݺY%˨۰i6NǏ3gSqSJRQ8?̜NiYK([`4y۫7zd$FdyPJ*'~Ѿk]͑G_ҔV:6f~ȶ4G^g4nڒ2+Qa~5^oNeѩSkZ~Ν?Ow.6m`%KE$&&uwKLl,5d >ǏS9qKM/\q"bK{t(\+XfLIIشagNc䈡X/NHߡCGX5/^⣏?Iw_֛k9{M]{0mZdʴ̚=סشe /aT ȗ&driqlzNհ0Ν?Oۈ)[ Drg|̂Kj[GVRؕ g-Y>tёc~k4{|ODUAډSfV$OYq3??j2+pp.t*[B9I:vBeٰ0|aǺ,[ujSJu4j5W-L(mނkׯs&Ә5׮_Q2HʁRnNV݆Do'GYfLiZ^*W9׬춴љ~~ }MD*W3FT:S BЯ/K`ߛIJJw=wđy# ៓2lvPK;#ڸ ??U 9^ơGvTΝ: ҝ^Ѻqqqf;:|S7ʖ)ͪ?dt=2Q?.:*Yc8-#2ZR0(0|[2^r Νp"{AazT(† Yl9s ę(BڵR>,ҳLj2wISkfMh,tgm2AR1yGJݻұ#s/DR1gxi[pLj2qf7~:S&M`媟\&7ߦ^: 4v^|||岔,gwDfCKe4e? !!;wѽWLCd L#8v#G %׻tfҔi=vBAհ0Fiߣh4Z,ZLz .Zw㯍]DE4bi#e>m2ou'0lгh4.^ <^ǯe(y_oN׻rz$~!~˩zk+{ߏ׻vq+gwǠSg866w?ױgݖ-ٽJ7K.Fzxy)V5={fZ"{2&dVZ$'طk;pi?nDEu\rov1> znν{L @M7idv4M<{[lo,fϝO||#^4>{*#i;!qB!/ BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FBIw Pff9Y/z"+oBBt$2J"$>K@!AH!d(};eB($yWS[)$g 5 IDATɝ'j/K^|8YG2^lvN{ ܳ1NA%KtYA˞:GP)̋ g +Tbj^*4L*=YRU/~IM n߁;lZyzS%ܹi_֪MxIA^8vl|m?!RXŋ!iiFGB>Τ܊J߁ð~bzϷo?tϾn$&)R\muKAbi~t׵fM5xMSe 9rFd r*%vl)j?!lY@͔i|5ŝ<>0qV-us}^"=ߵ'uO:hޞdn'sr270PtfZ4נGeǐ Л;rlO[YTų*&apO4! Ah'*U:wҙ%% `h״|8ʃ()xy)THU,]Oep\U3 z5OۖW_љ}'F2hٲj?!lϓs zj8YgʛWυs2jQ'¼ȟ_=6 -?p;zRÑZʔS'U{RH`Z+W3eN-^P>=iVpGҐ/*^kIzwwlO[ڶvO /=5IqΤ5h&ͼ8t? 2FngIj2`dk( aKzgJ?HWz^;4,g"o*?N*,IY;N!HB2B!!.#AH!HB2vVh4:!OO;)Aӓq_V"rh*GhXCWG@>wCiiZ w7'Ȟ:QlM!lUWg7Y-ףpϢy$[vAY*kV?"8n F}g|~GNDՀNGTl¢>ofʻ|Ƅ;wr1HNNf֬Y3gș3g… k#G2g_Ν;ٿ?pkc*ba_{5233رc޽D<СCF.\CHNNˋӧۍvM=,.eo:Ǐő`j{ǘ}fZ]tRŋJq@”sΙϜ9cP.]dvԩS夤$%((ȡ۱cұcGEc7Zj)O;(Jvv`q@zY_Vp\J-luBY?Sy_Ǽ7Q˔})ҹNu%#([+_W[Gپgr~9uxҭsRWBu1;))ɴLݺuMu%99ٴvZ6oLVW7n9VRR}BCC͖/]DƍMjm&GŁ_|٬{ҭ[7|}}QTj5.???fvm`kV[is"3(=!UЪ=ΜCü'ř7;1dK``i}lذ+WtRHqy !!!tFQӡP"do!!!ZO< tF2fr6W36gD a˸VTuW9K>(Qb" e.BSNʕ+\r)S0|pSСCMSRR4iѦaÆq t:=^:O6+::iӦJjj$77cr9z=ǎs8ur;==ӧOөS'ٛO?ĉIHH ##)Srssӓ8gn{s.x"Dy)Š'ќp_:y E 9EP׮]i֬3w\Sۂ  ~ԯ_Pϟoj0`aX/Ү];w8͛7___iڴ)QQQvQ(nx? 0 _(FoGhW,'}@-n~ <7~a)S0k,6m-nٲ^xreP,*6V`4nOк0y^NwMaÆѿ܋_cOeGHJN{GRhLE&M$EPP|mb Qq1hẃ{"ل+#!-a8/0Z!SBh$!2:@ ]"ܨ GfI~]!n7V_x#?:ڸ:EHN.}iХG!n aTzvl(!EN!p)BN]Br]tWo#Igy] EHn`r~ա QSЅ^}*w;!*w;5$gu^bmslz"\KމQٜkWj0/vg^b_x0O.ʠ zj#9V{2{/ ݟ1-sZgּq00|2:=Ίc[su8j7*F>>M15/u'zns1(FFЦMnٜw5fݭrbi=Ϸ}Ƿ/gdkt~w֡t\OB2eomF|:޷M|>6Rc{vf-s,`i5|[YWYz;_>hUH}^6Æ{ElOYdzf+'b-터i%гNk}>9oL$gN:˿NopD ]p[9zO{и쏍".3O̪!ܺ:j>;,V ŧ_s6#9)s,8UCfwtx} v`t>8->'Rۉoy2s,OxtJ<<;FPެ``hr,mBX\.ܭm[?`ðGx >;q RgVN*Di9ePfqa5<} Q{Ȭϔǩ] O;Ms&ߟ S o7Oƶ{]GM?ƤC𩆯: {;OvTwsL0-^֍9&ȷ/w4/qa =}Jо&{yCW∬r ڄ4]㆏cD?nk?t65g9,v87yU'=:UMˡ~emC뎡jO͹S-4N]͹N3ϧIqE #-V{}4_ ';sfVJ˽NO[w5e{y+M O.J~ș%b.~m`kV[is"DY8 m# nr>5uZUHJ3{]^^UO),hjBbV8e}~s9+1%'fcf5y$=+n^UHN7h/oޮښ?{o?av$WOf؝kY(65g͙9{#8 1CQ+癵}OV,=yYekG*x*.]O`4Ȝ_0?R<+ٙd~c^\L)ۆ~[Oƍ,qe13xuKg$7FTgӚl26cy:}=? )4 \-nZ7.g8Y\lˉEr+ZUXx>>=@z5ּsmkv>F6Û=VWsQZ #[6j}7Ȗڢ`wI[߳U<|<ڬNXeI:Z&9Gbt[_.ko}›1sg\~S6Ӻ/fq`HI˛:C:#_.mm T#iw9Ok ͹jK9JٹgIE8o[>d*|w&zC<`q2sCrB<ύ"_#{︞KZ5>}­pNӾ-Wq΢rYvWoS`%ܞߖث˕Ae\e\q(nHpB8!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!hm5C!=UVl![7B!K !p)BB!\FB"$e !p v$ zùhBzw6ϡ^o^>?7iݽhPZCe.I6c?2pPkF+LizΝaUxxzYf[Wc J`4j9XQ$~ٱj5he.ߖ.lڶgD~&r-VKFM=m[&;;fk>Q+T Phj"j]8CRR/wvD.lJ@(VvIj?]^A?4vwOArHII!00j_ٿ)B&ހZEj>p})sf.NpE]C~{H]IKT<!k󞗗kUq}N!t]pNU<deqEEA1hD1p{twz)B>AgfdSEZCgEgGgcudfdtR + cR F0`@1LˊnɿH/ +&{!aGJyϣT *5(ՅmYJ3]Vyw1k)Bݤ ǚPXxT7.¤y貲8?z UK ѼݸNc0`xw we! ҅ C> ')ҪxzK/kgZ TFJ֨\<벮Cpz=^wTnEGj&uNæ; 7eeE彐!aؿ1]4na#9mK{9N497zBs֥"^֋NyTdddZV P^j=u*#GѠqS4nʈ8ytWtӴnׁe~|G 'mzۭA3fx1ڀSaB"x\M#hl9"UN H?{61cNS}?lrz3 (\ |, c}tԑ /Lr𶥽u1"7/}r1b霸_z#;{B^~žj5( Zrlq ${9~zE#+¿8ta#F0k1wdc׭o+Mm@K BR>@~WIl.ٺb2t(w®p?7uoXaۿ"$*[ Ϗ}3gϢ"_ZuW#s7o}'ˋM2YD5 mSQ1rWtu4Gf iӦҌWhܴ_Ʋ]tz=9ZQMeq<wUa"T;ceēחOggywɿJl_wf0S6gE3ŋ.Yͧ@EL k2[zDq$kÃ֭[pQ>G&ͨװ #FJj9Ӣu;kފe}}ġ17IukX#I6}ݷpay73fXE/â`ݦ7?# i]vfu0 ੧a9~3^1fINJf׎m ;y/EdxUGo[ͼ5g`̢V3CeD={4h@ `]6m^?[Ν|o7smzR-Uٴʬ-[ o-~]{KL׭7fM̞aO(u^ H{Szz~~5lmϦ==<>%:u߬ڵ߱+LٳfT4)B®\Ǿɣ(ŋ ̘9Ç_:^fZVSik\Km/Sz5S[Rr2eGRl\K%ULA~^pS֩ŤyehWJ>*#T F FcTt(¿ PT:Qn-_Ht@SR̶ _PNj:e7Baqsx~5j0n$1=;WB7ͩΟo_F9ѽ;Ι 0j4f};EudOyV8yLuׇZp5kr1!:ۜ})-9'$S_ }۶l~5Ͼ /?+z}yKcH[^~>'NbX[&Ohj> ^}m.&78},\q\h>{cWҸƼoX>ys/(Zv1<^) O3fՍ_}\]"ϘljШA)X/L;,W}CVvY9\OJg|Y])<+ʭWBz=yϜI2"m:&L0]۷S kn Utݓ&ekȸv?ńnm|<== R"3g1>AMRRv=o..}YyAK?K>>ײpV:İѬ~4h҂`߭vX߸С][53ezg-z݋S&P&=zG~1m$s;AaTxd_&6GSDfr]BA㉺1E.v[jTT_:uO6:uNnټc֩c1۶e{O;eγ?JALO ?/VLO_sCďg*Dnli*@Z@e:|gܽЪms-Ѿ}L}\0o.y<9r4m۴1>ԫ[GCbj+}Yy7[(e}d&y%5+:{ø',31 }>Ց볤E+ztΜ=>F_8ݿ-"$lC>عm 1 鮢j =M~f:o^.?6EyoѺ{ܩag]>ݿ-"$j>jGPJ/OO|||z 6 Aj tBe.I)N&J/00QQ~NtB;!!.#EH!HB2RB /q!ԻW=گ޾UB!j%&Nήo^seLB!*]~ٲ`˶'SG;Bۮ&_ZU&M}-vMz_xsusq !M6TX[(!!Nk5arIENDB`sqlkit-0.9.5/doc/img/completion_group_by.png0000644000175000017500000005145311714210425020505 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|TU;=@ {T,X;EXւYUQDMKD33dI2!!%s){|{$* = Ac;ʟ{y-ߦࡩX}HzN j8((zZK\/nSߍ4@P$`Așc<埮Zc`LE)&4>iO;nMΎ (@dNPhWQU9?C@Јp_| =;j;pDŽ.MΎX: *};ho>~ Ly^5,ꆪtkۂ#뼚oYQQdIQdt&=צ4U^5,hzO\h";p؊Ivi;PX"x;(ί0krЙ}jd_AQu V}DAPUY{Y̲(%P/(GNdDfy5..qPsA-W;Î$<*d)٧I_tϡwDج=jڨؽ+r(ɻT|*w[=sSJzjggn?9;v}Z;k%5RÕ5U;qc"tt:$IN'9:u$BUQ1>x$6a` ~o6otG;B-$)kޣ(3$*` E.̾dlz~7p䓩!u"s)sV <UQ11#fg9;_W4~w@S5#,mm6'O~vk)$I5>4EF"WF7fnǣϽIe8G>yjl!M,{S D ?W~ _ʛnPr>?5{~:"GOpa Xh+ow^}07nlEpDʁ-+X|^y 7>Cs JZ$z,DiJIrV_YEvJg5]*%US;żk̒wq+X((4W@\ʄ:SEUY\}o`%- ]m5)E.xa.nɼ"n n3a2/;a$./n dX=G\ /DHc)>6.?妜 jcMNQ]씊";o[?N*ayh`Rpf_ўr>\7tgPr"gSTXAgʉTNt⨪BQaQWqp&;.m 6Ytn@F$ l*; ΢S+?$ȰHv%m^mUF!d_;pYv=**' mePp(dۼ&p\d߳ë '2m::fA&bCyR_˚ͳQ]Q̺5U}GvVz..]\`Urh.Jnʇe܏._݃ȷ .gO 32?'dFD|6J~ޏ 3P#:<ʑt36U sPJwI꟪&_ܿN+*gہjrݖ-mT5꒜m0*A/H##ڝa$Ap3U"Dk`ى 2$jlާ빖75; Uմi A$.}/j{JFp^ZiSS~/QQ GqKI 7%Yqjbmn9^4,l6{e 6i* \umnp3:_+˯.tٗ>BAAA6<ዯ3r:wW}n)LD|5>|ؕn1'o = dmnΙG^8l$.ح,e/~ӻ`}+ԩSy}t3=0udff~TW0`cs8'퉝/͈1\+̫Gu2߫{=-8q.`L7O/v~~e,|x'9|X'vn>?7o1; Q=XixEKy%*2Շfϯ桍?<"XzݮfO+ÄǟVUU~U?f?+l2_f~Vq6ml+<449?̲Ϧ͛r׼*,'v{Ȉp||,>f8Xa^=UOՇW_Fq4\8x9S@h=Q9AA|,sdYvgfeۼ=V**'v=(WmVgpO(pxtxK_QiRQU] TiZ$BC| u ?9yVyxX('O%_=EttteNuK]=,}m0)**bp̙j񤌂 zok7iN:,>r?桍د cYKdffsϿĥǹ/t<Ͽ2YYYdff*-WUxIIM%7// +0MMFNbSW~TW6U,o&]6~}3vz^{XRV^whߎEJqqi9'$^RģwL޽zqmwң@f=8ᝲڬ%,4K&N撉g}]3F\2IW^K}*-WU;i+ɘqr-x UzMSѫGwyGueݳ]q #^JpPL{^3]zj/{X3jzu€or3WFV[Fq4ޣh6?4V\XqlK9w{C!+fuj >S,п"D|UU]IR]pxBO'_KLUˈ 2xXa{gÕ_N`?9y;͖[ҵ3\}-]wV@~~>iiiL/?}{6h)9﷦iP4:e_2aX7M#ņ'8x*b3r ^\s,y&tArr:|6% 7xd;D@ufWBeɫdk'0fl6>Gh=sYCP'44~4_rbAQJ՞ӧ3JWQ?5aNȏ+h{3ƫ {7_}H/ 11͛7$''s1N<~^ENa2J6АX,gC-8.A1BcW:NH^pl3~x (tuj4ZXEQUEQPhkZZ:a*q5eBV-]p80~ܓn멉H#;yՇڲ_ק'[#IdY!6PTXN'#3QXP@JJ V_C(*EF 0(UЕHLf231؈>N3+ E0^G4O o _MBS!5#If74t:S طa5s!44?*n'?2 N$@*`GPP\WtЮcЩ6T-[ԏd@UZ1;v6hfI'K^F $ R:Az=)-8il6ڴmw۾73bg{Յ/_~c<ѣGA$d;J^^>Ԕ6mΕ'zEQp2PلCVPIaIB%ZL:`]Y {uuTuϧ( ~ILfa(Frjl'ʒEf_+ѽc4w \ǎZܙ39_;}h}{ NyJ6 '88EUWp#Ы67'`h &&WrrrhfpJEUҜ h sr~H$}L:X2m0_1F?܊,5Kt5ڴ%9 εᒑyWC\}ݛdY&h?@FFȲfgai^EU紕sL ƌ#h8F @ o~ L@:3Ա,tv;Nլ 际߻Q]t 5kN)MϜ!:t~Jvf W&ʪUIVVW}p \: E!&#i2á"/0m6gV.Zbd(JE+t/%4pG;BK))k.(8+h@˖vN#p.G.Đr^|3rE}@`HZÎ"(l91c[5UA42QTل,+AmH>9Bb1t%iHImJT:5]mI*GCBR.C;b011dɦu/o=5RkW_M?…<8&Ee-\qg}Y9e trqHZII4{jq&ΠsB4نN5fd Ja! EUxgpM Hk>p~O[d6kCҼysBZ7fUFAs[Ys 09J$!Vv愳I*]ʋ]?sl5FJG2ҳI/YY0v{)me˖>r ob4Ѷ][ Sb2PUU$5 iZd:RBV_ߑ5rRAQ0~4 ]D[Ƣ<~gxn:,+HH*9E8P4Ҍhh4p4!qvZwa"gFף$$IB4$MCޱv-[2|B@==z4k֬CǎF^~~Gֺ':[6ZISPQȥ"#Q&,%H r? RŬTteפ#) IDAT`c<]j ^uڇRJE.,<\Nʉci߃nݺӲeKZ.®lr(pQر#X,zP2228s&ǙÁ`p~ v**k 3A/hyin7i}'Z ih9vQ ;GO吧 |W*9K׌|GPs\0Gx-E`xfz=z$4$UER$ 0ybbbh<EU^Ml6,&m{س}!\ͩTWCKJ檖]ZK+JN)p bwb*:\8F\J+#=‚b@ll<~@X,^ߤ*QTTvtdj@Pp`IMO&b+RRR -b咚\^n.v??)7TзA`F26 ]ACZ`0 8Uop,l:0xqn2mAg0nW6qҴPPz=8P\:bۑ$YF޻n'88p;^e0 ]6]_C~%%b 4"Y-6 +[ `2l4ڵmCHHȅ3P^jSSqkݎ |C)..lbA4UP"223ܮVRn9Q).NmΩ%uzBCCHQ3ˋ)}K=.=A3 ً"=P3NBsh4ѭM0vN']k%?ɘm1~٣k IGS% \9s:@9z"\CQ2O.8ݎw'Ŧ 0+!gG~Ry(}]Tk ֥UVlG1nqH"W\}5oX@h^C4nSlS\lCT=UUf#;)k&77ՊR&8%%/u9MQ-$$[x'93 )?'U8v|㜎vt#؊Aj=4b13zH{Dv~(>f z |W?tF,oDo2 fʨqX{в\]Zz_5`$]=:w$ J`` 6NOe-t7J B tҙYfK`` ]tMqRӰo)ے۰u]_NjUe0C8SHHkW8 v}ݎ"+;Ml=jAu!!$'`:+EUJ2@+\ڎ+}+utQ5Ԕ軌G DKw#jm6Od2<&1}z>}XBF$Wo@7IzlyUyLF\be 8v=77%t\r ǯvړNl"##/ ݝlmgc6=TkX:lR6 d3ΧHCޔ~k맙͊d;"}11t+>hޠl6ѭS>3Ə쐉VLNN[N2l0|}}HZ4i_ Ŀ_|0+E\jhM;ZN e9HEE{9WK8޹īz8Y{ Etb*tv.II<4Y+>AQ?AAu꫌`Ԩ/vJDNܙCd"/?p EIzf3 Uk*l۴ b~Vמ|Hbg AJY/,VT7Z7cM&$5Obe5tlߞ\T޽iFY$GnNm[DRА|Vtl/ v9lHFвN!-09GyԁS뼆U ܣ~r?~oDJ8 n(' GƶsDGGcX4lZHLDU5^2v0g0ͮdl`4ؿHX?@y qt2; mDϒgo1hu&!5 tz_U{3`2a0پk/w;˥_pF}ߟ oOx*Ck=ulttgЃVI筵s.-[ઉFu8m9zR!]nj:G2G mZ_ײ%0[n./ ѥsJ'Μ&; AyTЙdEϋA`` ;v7`6HC m̙]-[靵za ߏg#Yz,S\lszt:眷҉:sH飡EA$f Rtp%v95GmO!)sLde90j+Yl6[BxXAAX,z$&%%={f]66ǧR]#%I4 I'cډ@m4MCQgF \[sNhb1j-v5dZuT.)3  |}}ڝA @ hM!rI#DN 4i &MG{yM@ 3m:UB,@ \h@ hM!rI#u5Q,7A`0ܶ 2n="h F#ǦdUDbBAI߁Chզ5Q>~aĶD]*>Ⱥ+0-4mQ:!rMV3|dzЮ^YDDDrqGiC%=%Șf @u#=%B(&Ij,DN 4i5m>AcD\= DL h<# Z‘U 9@Ф"WJkim;_(<˯ҧ`:t3tmwv~҄#ܚ۷[7b6yi"C\x?oQQXVyx-Ne)G,j`RS3n۵^ "ݒDFF4+AD4WH@?ǎ':_7\csx$,sa[1 Q#nW\Maaa}gՅ5$>ƛN||sW;.\$#モwvUi}״Jm@ hM!rI#DQUy(2z"D & @uOq|!rMFe;|E .|E|g 6tb IYlK~Gc<ގ5Y FS @uda(&JQan." F8!rMH_>uEr7mGS9-lXԴ%zh I>!r^мDO}p BR_[v--Z{HZ=5\P>?ü[hЄF5;TKKػw/&Lhۂu(=j蓻(..n3M`h59]e}ki>㍷vm;s#$y]Q4zvm;|JtGv*4ʜyhS|[q78}763ѢǴh13ffSl|gǯ-p$^?~ɓ'ba„ ٖ@dF_`pHNN4En,bX֭i۷vd9.]}5G܂b`[\_xi;eٽFNvν)mRfּ|v6S ؾ:zII<UՖ~8M\'MČ3HKK#553k֬z/(}DEEKr-r-R"+ͣ&4z[na>D‚UZ\/>?+~~F?џ,M,xvPcTEMM6mģ>Ν;)((K^VTPmy5mZ3{|<(]Mh"C\Zx2F##}uŕDGVˉ<ڴ(p_7N$S#SՅkW^y@^^:<߯hիl˜y,&=lQvYkI8,?mwr_v<N. 'e8rlW^ފl"="h]嫯,EEEX,, LVA;j@9%(LK28FgߓǕˈiK/-{Dˣ]`XB>ӼyYs=977?%~It5X¢E5kV#G2xpAM_:hnfdSQ 'gƴa3 by,3nd+9O.7s=9;AdddqRNpON \4A,!r^<>"'}B/kžzAo0Ң=}o{ҵ!@˸xNu^wuD4dzFB('vĆvEp ;]N4mZ7X<ڊ!rM]4 Mݻ5 ,VFTk >]hرPd6+Z"D u)˩>Q#AF@ hM!rI#DN 4i &9@Ф"wc2Txdsj M2t+w%Tpǟ];}:~(?]zp]GQĒ2A@\ ~;(k]nbƘqoۑ7p[stΝ\sݍt֋ص'YnGDDԛ6;w0a¥n}n/[NG}>0uW~֭s, AAhт"9RojX_Cƌ+_y;wϻnb"#+{eİQPP@מ}ϗܹ NΝٹ*ڮjΙ GUVtޝ'O{:tP_EӡK 3v$uƐ%0 ^wAtݏ} oۑE#hծZ>бkOyl-JZ>M)v4_.fȈѴԍK'_T۶mc-؊ [Z,INs˯ᅲNt*vÏxKE &F^[YY_}Ǧ[yϏ1+gm|Kvoʸ0{޵C]Jy/5v#!00gyUVy-AӠш %<,清/^?ϛCTT$VG/}3idge q}t)%**y-ﹳ%4$RJ 剹ee?ys~ُnq~ "##aSٷqMեU7oaa2JFFWm.|eddۼEls4ƌj8̲o 8rowg_y""*~6o^/Rff&-bcʕ'((b ˲_  ˚gߗ9p [q!hMF#raaJJr Ɩ k]MÄ#ܚݻuaM<5.y-SՄ$7&4Խ\'O%REBݺuRXXӧ~5J_\\bbp gz u.qhD./EFf&O?[n#IdY?AW}3c82[q3ϒJnnOFf&)t֋ jIDATg>%vwh~^W^r=ptZj̈́I`ب1ȷ>{1~d Mppf vmLJgƈ1zQ<4kF7$y7W>NE{ζ[(,Cy*Ӝ}>y$OQa>ǎf=ũ.{t1đ۸3)Ǿhocg= >U|b6_#<{ #ӹyUIM:g6@ BAF\5\hMU@9@pA(2zBER £<_=#DN \(̱Ⱥ+0-> r_O[E=,Y&~ŇquBF@xgf͉d%\{VZH\6%3AOYl)^V^#[Ƒ:OINylZݵ\[q$'/DN zJg*✽_*w#&_dߘ>DFE^Ն74!C{ Y&V $r“n/kÖ-[߯?$e9 188⢂s6El+ݻ Z=QزŹc [vC Eshg0w!}7{&W4zk5bVgMn@E")rÇ;'#; Ǝ'o~%A-sZ449IiOQмfyQRQKYfxɻ""fٷN `OW(21#֬>{?{?5g> tGqq1 ܽCʑ\>R'`ll̚5}rGqENtOzJXxDʤ}xѩ/ĄT+GqV+)++ctٛ^z3ϯ;u&"rcuȕdX } ./K+[t5O.]вeK,99,%R|gNNN 5O3``,j 4Zu[z-ٷo{˩qT&z쯧-pp`QyV+ ݸ}{3'Q(,^͹s=\+? &zv~n]1Te>n5\P_s1/ T*U{{;O 5?`ooW#pr??'`cmMk|תrѡWϺǥR~#9}P(Xz%kWDPNP;z |V6<3OKgYt9@aaVʭ rulmmr6lj-o`073sп߶>R--<7ӟFrc!s'ڧZ*`{<Б/ ܭNXƳ*$I>37'bffF~~>]Qg6vvJ\Y9^PT*cb hՙ-u%)j+/>nݵwppIuD]zsozW5E!Kɾ}[,\X"/..XH 5-H-[sZyRK̘U!WVߙ\rR%**.c`@#Y~̙ ̌I^, E!K>;;['MrI^== sSS'cfƌ$33er6 ~cSJǎϲrŲA􉁫K/Uܺ}舄sڷbn}9k~4k|McFPE 0eLJFZ ''40uO,P' 2e%iּRvx-f(Q$Z?aiRPuIKM޾^hcB89PV }8}(KJQ,)~=]\iajڀ7ԇD9~8޷/[6cۺ5PlOܺ{6M6n!~n-˾\Nzz:]tfsx8N5 ڮ h99AejGR13s=-d R˗,]ٳz&dLLL;oDb%3#Ç3G-tgCz:m nmCwGfV'&:tPsZPP@'繞 s秃ptt #3ݜIMMV뽙~VmZQW#̙3ÿRڮJ奙b$4SR\T_ɱ e:tpOѯZ`0ʚ[87nRW'ug@fVDo055ETJ.\׏ݻ%NmAB89A000m[#ݻw+i1LJRU !IlooORUs,.*̬,FMo^M ZѮ];\zsww5rblؘ$H{{Ofi\v R U4-df>o_>c'^2zVZN (!K>ΓQXrn'&Mƻ>@T- ž<}|qsG$&%K#kl())aW3ISػ M7Gp1I=u;UhB/lMaa ʚo ~ƥ ²{u̯xMw:bM Y'f](IOtZ{\Ȋeٺy#FFF|̓E֛Cֈ0ڵm˖_K_|'z;'%5_.!l:s%%}fzp ޞ ;vFSPPȦ Xr&aTG8N:͑Ǚ9A]XK6mXǝ=E7Liedo‚{\ri>>rE߸Qow4>ƾF3Rqr󰱱fTo#G4kV0n;qqV-K/bdd)ƼϹs1-bb5kVhaDNU++ =kIkl=yJ]ǢeK<'Tyf$jDov0Unj9'5_ [ĝ;9m#SekYWѪ=NԌn#+ٸ|WBΆ1 gְ!W L/ܽ73ش9>HaQ]z>M M6Mj:8p >YkK_tyIGz-D)\Ǻ`֯w/lg %^lm[3m,JJKqޭQgϞ%X4-j Q뵺*aC0l׍*8^t VWk9$AS.!lZ@(K)\5=g0!Miq߀`bOWW '@ EgCCGb F.]( EvurБi7Q,l͟2B0n~ 7 @ࠚե Z 4:bO= s:9IENDB`sqlkit-0.9.5/doc/img/table-views.png0000644000175000017500000006770711714210425016661 0ustar sandrosandroPNG  IHDR8sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|I#j(!^D"6b]QP`E ҤB=!$%GL"fv{vfvvV 쪹?Ke%7kSx;𵘐t:@ MqW`fk~&l)^nU{qZQ PYb@ .\$ IoFg $31o)uyr5 [|0]Eݗ8 +;h_]UTU*@ '>sïKjT}uwc|ҶdVnݮa@flq:j5;>bټ5E_^z#.ʏ+?cJ|hPG' 1Ȧ\t^6(TÃJ5 ̵&wzsc;5uޞ z<7SJ"iJt@$Kg|JlI4iw~eb%pL݂OB_A X^ŭdh\ZRjO0W',~/[^Qٹ)+l^Eu]?@q񄄄$KJ!u ʠea̓L=ˎ%ɀ0, 7CEWeo#țy.nA DɰChȆ2.[Y}i4Dp'iU1dj+QЉ1wlIfK#xcqr.ÿޤ$aY}~h{<=Ū]9<%Я2MC * ez3YJA k]MNH aZ9yXQ]>\ɇ }H(TrA* thGWFņ{}YO=ygюd>X_.E<8Um)l* Z nI-w2e^8aC1ޫɘLXEt_#A׍VcoG;&>>n|}uMz6eI2-F!bod;Sׂwd.CstK:  G~aLUÁD-y#C$ՖnL=W-}2-[HuJz vM) P ZXP쀱MB+o98g\tclC5ʘ G6 /hɇ._#Ľ s؇d0h З| 9QLVq 8zQ}h1RA;Bu.s:!gϹ^(@֤)Zqyap2Zg?¡>J^˒K%XL@>!qoäٗj' D#{`[R NDԬq1f\'h8ؗz}qSo?4vE0?Ó6N)8QGX݃6bo>XM:V5L;Yv@'0ď2Aztnx7oQ X;<{F0i?NZw<0 ld&@\_қukra󽙳8$wߡw'J '9i%83 ܸ9?ch`!H9b%!S4_sw>{"W{9h~<#@/g+/x J%ιuAOHb=w;ۛ[xq~H_%{ZE}M <^zܾdY}U ~fu$IC+dj Ȩ@*Lf9!.IQS'7 "ʮsduDV2FB %x%,*@*Y_=MjDžIpS*S\J낞|iaol((;vX TL-)0" <@OF,f/N=B Ķ3[ 1~FbE˩ P%-0۹w)mIVl`wuzw\Ux/ >]K{|iE>{ƝڻCߧv{//뿭g2o AY#S~'Y}6ky*U)z<f4|%՝b=%s 罩֣(sΏc~B?X]){l]yٶ/0s5ܻ|=ۖU)|{q q /_ϞT>z;M^]i9Y:{-n>Mꖖl$geWe=c_$frG{Z@݅qSgoo# F(.p~@Ae*,v{8>^{"XM;"`?Wp3f֍۞˪. J 7?=a[inrӞr~*֥z=w;X0)W̘.woNޯCテg >}:ަ0œX^o{E64a܌;b7?~w]ٖ߫-ܕ|Y\_;3cʞ|PRԓ\2]ƣ&[u7ˎQ9xv;% f(g oMH2 T~Ì[˷2LN30@y?*oҰC^o&dfm%{lם-eQ5tA @??}'kգ.A:7vٮ]ُrqd0b_:O8P:5} +YhD֯ԦR~Pj u-Ulg,{epS^//E 4id{~: ʫTvߓIIza.QL/? - Y28b#8,ʠHcLR\z#0)v7Lmmz)O4mM9u!_{a7ch|ĮY5kJmOM  MN+'H%h%Nte'V-+)_BǼʎJW`{uem᳥A<Lli(w$۾f}o2DWҎ}SNc_-A=1guB&vg>)fo!]ءդ9gNjl 1oؚjŖ/_*zPsiVli;9fs/jk[s/Xw:{!~{:4(2&_,ĭ|ڇ%[%_ebR$[Mr]2ħPł>'q/4}1XV~ݦ =̗sbwo^QWyu VP[1+U .[dz*;;+;}$JwO?۟HF1t6yUKGпgH_bLh74@snܗKaCѴme[yh϶ZyC;f#O kۋnc^ccQ cKki]4^oӍɷ̯cԥ7=~Gr/_\ӏv]u[߂M?g@^t;GӾ2{+cZ8=oLòOa + s~/oYٱ/Hu4Aدzm@ڬ_d)|Q ׌M`?Y9Œ?btܑ^ 7e7&rssINNW_goUM+`=z'rIMF#N17&AZAKCC* Z@RUU1(Rp*NN9Z@`` aaat: Fɬ\ e˹d5jCI^x >| Nnnǎ%~ZnFbb"?~1cMpESNDF6g^`՟ k.UUYz?G ZO:UF!8}\NP!ṗT4gEs&)ur͕jEQmv 8E@H?"A<}a I{W=q5p )N'~e +!4Af Yd Gw4%cH#E_Mo0G=HJ:Cƍ9y$yyy=zf͛ұCW-vڑ@$?U#;iV=& 9N$fqխhوUaƳK I5ׄtTEeE0A46: GR\7-A Ng0LնI Ε Z)}hXޣ**_7<H~--/ (W"5d,;~ϊШQ#>LP`9vfεchXdffd8z$Iv 49|"UccӞ3*7 Ž+UiܵAM 8 4$srAK  ZZYɅÁ>;T#еa{$|\&$]g\'lhFAAA|hחRAh%$BCB 6:6O/A:L~fT}Ab7L`A=>fd$]!RznzoV*`UzZU(:;a-ݎT(C ?{>%8g٬8jh4ҤQz1wmK @#Abl@z׻NӣI:l9YI=D@ Vв]CX2AAHqL ?=۪25пo?epmhYI(;CB )@M<(@MM9 =µS4 ^d$C{>kF $""JVVV:OgРAXHIP|7ɄN'Wp]# dԐRֲ 7؈,IHqJ2JaY_F APP` Aurgry ۆBbdMp 6ݎnjT3 ZX.A[h8w.DK?hMbQQF#$W`_d9 Én׎lvY{hd!5=cE(zQH0N$If"##^-`̾z >*]#?-Cl)@P8w~~%*NB\xNڀvŁîѡst5@O#1@JF(tQqТGڃFNGXh(z 7ٺs'[n'+'Ruf3>f3a\ԯbO?z}{7,Gf3@LڴRf 1; '%BeiՂHg Am G[v=I^WZ6lDjZZB޽ xW&k{]HA|q.v T=hq?g77`c̾|w25dɡ {a*]LdDZcO0aA :++c89ۘ6y@liڛZT>~V䔩SWXY/rÈ@Ɠ:7^s3|{ %S@d㬇5`.esp(_9v=5γ;>!s?+r$W #b/g#G|mu#5?ύ_˽m$ ޿Z@l\r4V$;\6][rUgE;w2hwי4ߚяN"b/אwho=o煉TE)T?؁' +~|OkZ:y?mC~eÊ̸2/6 ذb>JaI(X^i1k}R?iSTC]y?Xc&ᱷ6,_p_ŹKؼ]7 ŊKX$:YUE.;=GÙb$F|~a[SK X7~ɓ7H5eW˻KVuݻ xіs[ *AKs?l#%hڲ5M[Eq&lŵ.&_-d6zʦ( P>.W]C/(,GCQ)SUfcЄe܌y-SqH z :r1Yā<% еt34咻n$|W%OE@MQPTIvqNZ16)wkRʵ٣/` 26]GSca[-xף1: ̑ FZE\_Uږs[ *1KJR 53s~bb )>џ{㵧j^^9C.Jh`vL֣S8^U:Eyh>>ie\\ jFdT28|7dfI[.8 +C;&t#>=F_<^Nyoz=ĂKԜ^e^®\_*i-Qeʞkd_B Oe=>|ā\c|Gs# 'Kk.[bôynM1g%f: '꭬-U[ *#HЇ3 ~Q-bTvX0[~ s'zZRyf\ >/(.vmv/>VX]O l箛EBˡw$D7ya4;l SdWF1R\m-o=v81EtecOT_ʖ{+7zVc}G#.]>`oy.~^ j|-2rfJ]xw[I[8w2h׮'35J2Oւhw37]mJio;_׶)rQP-K<5W\-j{ @ J]9٧\՚O^^v'{Z IC+a:UF3Fj.~~[FqmӾ?͖ fYL"ڷHTA\'N)3i|&zHT\N;B{W-8eT.U5`2h9%;Myz},?U/ Ԕ Jt\\7+S}^f-6?ᶧ>?Fet4[O'~xg7ٳUzny~<+.UV..KzOGڍ)j:Kz\HoUJO,svJ\lבvCΫѡ#QW&j^Ws |P9ڱOqPBF2T/#i?ID[|”YqhaÉ {(O(3 ?.~Iv7nNvjׅ>`^Lrn6Wߺa:eَ[׹#Qq'(QFrlwnc?sקY/pD1ʇ5$X))AO͠H|"0i'^_ ~6dofbD+Cb3sG`ٖʲgo`wGK\=9.$O /JN1. -ynQz싗%H v,xm(}V"ı&-ymh}nO|ǦYh[HVqq4_7ф/`g>਺-;}T@ٖ\̞I k\8N">1>(gtph"-kyi0a&kv_y9z-<Ԣ d;%|{"/Ûnbڏ ;ȏMعW$qS}e[mgZbGj}ΎD"2e'^_ |Cd施9XW?6_YGg^/''N,u+O z4'_d>}GK\D4ʧ(Q#aHN'ŮѨ0>fNF<68~c<@yK3q2}O}i,tXLH 5/gS IDATkI3[˷~tԜ;8}ΫP&~[_/i,z>Y8\2N>8pB,mXbC w?,sgw2hUs>x|u-Y秊sW ]1nzZlQ9# PY?wQTkͦ@ B -jl *UHA^EEXQDki ("]HM6G )wC{'Ofwv93;ߞ3g|n&,l/mwwc#{'͔ #P+.wrO\VNYKb}/Dz(|h%_ͅĜ iIF0Փg})6lOZHϚ\U ٽ H={DIJ'/CS kY:!\U % ث̚Q5~uD:=_>dLICfS~+F33-^ը[Mm0j8O 5n[QGHH.bĴCxg%# Ҳ  ixͿ&sY>Lz o4U㰨@GD0 Fc/Y09r4B#S1v1 4dVrϳb(~}fcײΝ]W+o /k LF' :as8v&r9s9Np͍ ')Ĉv(Y:qvf.i/iy'!( 'r{tOΜ:__x6%nDF]uN%8жHzx#ܵt;(3eDZӬ s5 u6̿(`,^kr4:)_aʛ#hי_X-څd?ϵ*xoH J䖅_ xqO|&Ń1[{|^.kuΆuGN4 :'OjҙKˍd@MZ mγ@ˬWlFG̬`@l_~C^Bro/w[bs$=C:Gof̺k_\pPv/X8-ڸ=` o9s58Ӓe嬕캐JNk'wyCo̟?:,xQDzrfm>F+U87*Xuǘ? _ݚۓK,} [SqWD s]>͸slבS{GjxQF:7_ʉ` @8Kȗ z}W[5KyBxw0V`ʞZ<6NhR6l^3-GGL~'g!*/E]'%1/ #]+P#GNSL~^ԲpTqd\i3UKũ̦"ώP5$;{F`Đh̟6NNBhШ Oo(6h0Mo8|.B^ԴMLq SAKףPXe01q AqZ Z:[iU˖$SL SsA+7zZB"nru:tRBq8UBfzrt*h=\!n&3-妧)gB ZB!܆-!nCB!AK!ې%RлV_V|>NAj>}ْZuEdHBR<αvD6{ԥ^Vtf݃-!܁k\Y`?vK OT0x-l⒠Ïb c ^f^ 8~Qz//KnwؕGkQ~r.{iF !bO6imRFYv^gCƌcх05J:tcƩ0,Z̿ K3atUT9EҊt7wa&lÌ̻̖\`Wh9ڲI3|xUj-7w#VcT,|-moztbADQmX r:hɉir؏ U'r hcH~:m>7ʵ}xjG̭ϖ43a^ƿI{=cf2`>M\2mȿ&G,>@F6ʼgqZg%=Ǔ^Ĝy@^{H>Gpta.O-2kfwD2Zb\R$*+x~珪Z+y:>#:Px#y.&diЋ1߉43Y?/3a4ayhO4KykMkk{?J0zKdk]CvT?\am1?%6hLڭ,zaO2l&i GҹOۑ2rsRr! M\ f onRQ7+WISc&[BSNS#%BD-UX$5bI3yKykM ,F/ռ+Mo|^FK. M1ENLf0 S l^ yٷL#5-*l.Ve(;m2-/{{AS'fĦ&qN 3%M&r".譜ǹNu ,Zn#+>~O>BCF z\ɫEiͰyeעϻo#k2^WrRrr0u'젉ⅧX1UԴ=0WBh1|FJ`qՠ |̭ϖ4+^ #?ufyAwLR|{Y˻}?1O 7{hSO&Oyi~|*?E]'%1/  +eRлRƝ)'.P|% 2z ?شP4u.gĘZ/Ne6yvW״ dJRO助+UO״JQS|!Z\{M+u;O=\jY6c-$śTZ枏 G{BJĵ5z&ӌOoK49 'MRn{:U{깶k!C:7U^'3P\r !Z\[O@WLVk=욖l,{s !Z\[2/'=,0&JVԚ{h{^D%b mǀʾTDݵqmMv^_t'Ƶ%/NP"Ԛ{h{sqНcԍ4<`8V%OJZ̞AK~ugSqrV`?v+xQ|:"~Nz2w}_@W\MQ/j}wyfWx%L'bP=(.c+1EӎB;s"Ճ; A5kDͺ ^-!.'̓B!܆4 !p<(mTsOBq+A!nCBJ<(B"̓B!܆4 !pTfTg8Tmq Ǔ\w[ē|k 91Ų×k/ENB&f=|u2 Gǜ(ޗ0|q*S<:7>r5y{%O8Jo ?G`7_N"3\se_=%}ʐqyO?-X9Nv2rB^ȩRϢ?1v6˓{lYc_#n۸RjqYuPemw[F{x/pG<޳c:YX/>G05 4Vߘ~~y]4mHC爌Džl쿞=-H bʼt?('}b:Մ`O49Ǟzk'Csٹb",3[)iK޷ػ-Olg}_D'b(h?vp2!?~ߋ_s|wc @ĹssK,ޝTN^ 1 U kEr_Tb3C*1fo$Ohg|RAC)7K52=߫: V=0lN 4stc/#+Wq">ACJme4 XgsS,Ӗ2-oMw-Olg}_DҎ]P۾o2쾡<~hA4ۆyGLK񗺹bzUU^ը_q7~W DNɹzkTwৌBFKMli9f?P$ 3GFD^!55Xb}%Ǟ1WCWK=L6/ew 6s4˴ľ5îox.4\{Ыѳf& Qn:{0r/g"V{][o$O`ٴFO]yON)/.m~ޛChkgZ~T&q2V5X>q ?f}gs 'scL}n~,z-O־/pqw%" !7?N4?]^̸)AD>;:Sf}Bu|[=I2ϘU+5CH'6MooRx~K/\A_:m 6Ci8t浅}896BC%v7~kdǞ<gya{g_9+ܦXZyZ:9}_Deѵq$ټs:{!ԝV˹,Wc1 bzCt}c&j[8uٴ/_1b!CB!AK!ې%mHB6$h !pB =譲 B!i,RB6$h !pB ZB!܆-!nVz˖ Z^ι{-V.\\)~A@[R4ח-6 Xꉸ@F6ʼgq@2?c =I$ZIk8\}L%o%>-kdVtDǜ=z'+%3tE'TR<[ΥfuOm?FBǤAiݬ>Lv7_cw2:Ԙ/0d߉$130mfsyGݜgxK3{>;_`{#X%RN݅ Tqdzu3.Z m4 &q7y] &oϓGlabS8~['bFEKTj4 oi7 [3Zꅯ+>l-ɿKn|Pؑ_`h9fUpDNݘs8|é^,FHυ_6E\߁Xܿ1~5h;<ߟG7ΘԴ=0WBh1|־[~81-!~w/MYcj7gZ @%B@_ZD`3 cWw4{'yj@=Ch}3>b=v1޻8Z`Sg/˩1$B̴% 2z ?شP4u.gĘZ/Ne6yvX!%mHB6$h !pB ZB!܆-!nCnB/h4Ee4O Pg; ZB!\iCAP2'р`, pYx <(mHB6$h !pB !i[Gmk׮]%it;lyP!ې%mHB6$h !pC!˝?y؋e o@Vm^ԴB\mY;{uЩ{4+AK!) jKפC׻Ցz%h !(4oיu0b$##Źu(oB!D^޴h HX%f40xx`0lq!5-!N&#]d^'++ۮe$h !pZ;p$'&`0Xn4$'&pt]HBVqHbϝTC57oM``233mNǩ\/!UGp0kk<(mHB6pvVP(lGD;B'nM5 htzG7TJMNtq7A!&盲wf^~cQϞ:AFMɒB.^8Ge߶q# FҮ'r#xy+ lZTкp$M#p FCʌFޙ, !0h4Bl%BT0 ZB!܆-!n5#Gov>BڵmS=AG;=3B!%)Ie3L}P!hrMK!ۨZA+3,_=BQJ ZIBT8A+3 eЬgb'qGN-Hvdڙt茔+i3pP2 7p0QBTkZPx}ǒ!&dHTlڑ@Xf㙆'xw."=}obP=})db8>hk,~XOw_$?¸wQp2Fbê.:<޲/ύAeλOf 媨Q= Lb̎awY2ZEZ^Y9{oumQ0dbڙtVZNߨʞOVS$zl,nB(̦Bm5c_i]ZKw{N b#a5-Y]~͹;܏&i>덅'L-}O0mZX>nh-'+OrݛQ8Djl8~>/FlX:|~`cd܄lN&{y'b6ngv ?_iø%jL:ZK?gE+'+1VlzN.~zrlB _}KPl숡nyw8ֹ/\?<BU]\1o;ۆ큧:Ώ AbJ/ν?mjԥmz9a>s?+ aV,U8nUfZK݋G]M5J||06Mlɿ- c>hflgzRD%V!˨QγGKǟ+lF5r2WLUFs Ŕh >XvLxKlGY? Z?QoQmG-bgwkI}]`KٔN܍^طKYt;4 !n8:)|0x|9ʮL(`,^CPCNLr]{=4U~29gݝx ©G/L}W[OhHϚC6l5|҉MCA0K%َ'ʬO:y^6esAm ꚴ/N|m[FEJb\OK٠/}U\".j=ӈIQT}߭_:tɧزz;Y{@efc<J5])}gMf=ؼ[In^dҏ8f:wEeG9Cr-krUgo ٘>te۪o9z-vZ[oiiOfŤ瑝|=_nrh;jF!UI5̓E;+m[DՈR|(Y竦հ)< 441W@mv<9"VN񷌠C>ݩD42^aDY3ޏ Lid|.> 9^5hNꝋlJ_E'1r,dBXb٘d}sO?X`o$::)}-,!D9+^<ѵq?$B&AS̓/ '!\Bk'5-!7Mm.MBƖ&A̓(u!U-Mw@P>9B!c'M}E̓y2B%7#<v[Gp*mfzdL!(.)Rxqv[Pb`w4̟=/o%*GG!n줩E JB=vq#B!~wL?J2IENDB`sqlkit-0.9.5/doc/img/gtk_debug.png0000644000175000017500000070244511714210425016365 0ustar sandrosandroPNG  IHDR^H&bKGD X pHYsHHFk> vpAg^\IDATxuG_u9.2;w@I@$$HK!Hp . c0zlݽ73f]UgSBJ)Q-o,3"ƇL}MC- ra0 `H$A l1 cEwŒ N +(Ѓr`|@ 98=tسvӮZ;Nk}~cĊ#ãi"Np{=I o+o8/B^zȕYQ0 w )LGH'o,?b>;EMbNyO{#Hz/=\$)5,2H 6( ^']@b #Pi1c;5U0Q(µw~rW:TPHBWGU/4(pxWAY%>!D? HyQp]O 3. @\5WC= _1/"u%UʵZqڻ@:&Vq K'`\"$63D@ÅB ëLjH; Y%ZBĝ_[އpX&{1DHHinpc^/QOp3? #2RNvi{D"/|9:3vm;I]V o| @4̔۹2/,sH:x=?7D~;e&8ld[ 9_LNl 3(M{L@:dd !HX&!SmV4,WU?P.R <_)E^"S|Lub~^;~U~T´ 8)DirMVDJ?¢lt#9S'x/cP\6`TN~2#@=Bx{d#y*h`<@mg`Z:g"U dO7!g_|> *$0sJSq }ci5T|hӵH(6*u)xprvwv]nS^,+A[ZzWe̱D|"ڈ@0NP`4-p됪 N~GSYy/ojjM. t|Ȼkig#cw[pkZәwCr<w H\f΋ARç#,![14T޼e#Ώu5FE+|@uP J$4Pj(I*h =\ 4< j Sr4coP3e;JΜ#fʌJ*g56=jxz+;?::HvA l]s-io7P#?ukWozS.(=j;շ{e|5L,ȹ% "T,c}bޭⱺ [Z{g`8 Z}`k<Tx;GA%5lS+ ȡ exW, 嵂^J@^ZV尖.52N|vW.`fnjrEsdWke0^D$!Q vTtӓqvN/NTLlX0ߍ wb$x^/ X~ޢ3d5)}-5pW}Z7O~ S(\ѪpzkAHӐ_7DBɆSw7|'=ܒwcDCYL8[AK{oH|Fv 1o{_ ^a -.q߳o ysF^L`AY*|IѺ,_9DeDz5{/PqPpV_75 8r%I*y2c5r8Q`y*2D$dng'@N wiNqȎytntRȂ\\΃X`18dk;v ?-w#X-K ͔ IA%g >w.28*<ҟs jlGK~p(Xl[v'V`b>x=S O dLUAYhJ09N$RT] zO ui?S@5>`1>$(yUxHFI`f)~)8wܚ szl\uCȚ\[-^r*@V-rwBU݃` !;+gbh8Dټn d~Ep6k'z?!(XƿkWvnaq;xtGr HL8;#G`m I.6]zO~E۽9^X"(fyQ_ <#O\^?|'A6#z|m3hn.,([gtL<.pȻ268=3s_Y/$ARȦ Ho}_BF#&O0Pϒ!MN wr\B~C 1D&VS=@ƫ#WYej$ݎ LR7 A=Uւiid3>L/_ckX&al G {dʙۋǁGpjdz.gbo܄߆O(kn@MnGÜg@̥[o#yl= \`?yE{HtЊDÃ.fK}㾞gć< ndu ~9Pg\Z`R/sƦP,cm? 7Y{zISbȵPFP9 \K Rg- Q<pt&VKx>4 爓O傺B]]xKNQObrz9| ȧ `ocM6nK}PyY bu:X)vPsc"}֢]d!ХT 4U֙KD-`P Ѡv*-sPv):P3D.+/KY՗AizAP>g'nmD޶`ZPE@' 2e !jP5b3YzDWVg[:/iq?n5WRs $V&ަ)0bV;< Im!ϵH4"&?ыa m OdZ,ǦBp8J6=N(i,^ պ%2;\SM'^>]9EHU!sk^!d-8.u.S"R*{7=BA:$_+Φ2j-^ѴyН f1mwꥮ7!?_a³]p5}Ogvބ@WWx4>m"Ae lcE/ BڴAn)!ތݠ+LYn @(ECPv΂%%N.2 EAvr _?Xv5H> >[!ws`nT6hmZOt\{v!֘/@Wy&ٳ_TDaL서~g 7y!:[G\i[\ I`wt %>C59 62ȥ;`J`ÆƋ9,@ D"G0`B1ް ^>&G%VJ12A~ 2@#_.pgh J͔A." @Vxb /˶я PxR?QA&^Q]!p$ Jm&m-rA<Ӊ*p>@ŁoDww$ oО;@2Y/˓I u@=ș[p^?Koyc DyyGga"|i$#jג%y%M~¾u^Sw&|3wI$])*|6TrE]w=7/{7̓\\Ay[^w$!_ę/&JwK#+,own|.uH-c3ͶJpgd6h Bi~Oݨk0 )B7Jeu6l۵D=D5Ql#;$CjQ9I0r,|2~h#0~+MʊP9t L}|n+{>D%Q`~Zy]>9V{͟9tqD } -nd%p3 lfxN"6 `0?~iLQ 'bdQy9qy82@.f?A+0PWϬ1 /TݨC$P $Hκ[[|_K޻<Lʩ\JzvA+WBqQ $Ol`0 r%c΁yOx;~N;i6Z?;,)`\ 3tg5h; /rnj1`0 ÿ2w ߁"> >J^^R(Fjٱ%j DH)?-Ao2Je@.8 ;ʎ|ǔRD'dTocYG?8o< D{x|?>Rp ".$j=@yr,8~ARD"B ~_^@{>5e# qy\L~[e0 GAG)84 EQ%!̜!@mٜ7 ;%ʃM g\\]=?Ǿ\#1 cq =Qi\}t|N-3OZ%umy/@WUP,YϓւhLI;d〝쑟1WRӯ5$2@;*G)RBtKm HQ9=-`T^Ѓ;BÇcF&˙6 `iYJIQszBRۧR?{W=ەi@u5%~Q udEHҴ/fZK$:]1)tMNj.* Q2.z*~y41@kF╇A|ʲ} r_H'ߧ$).plc9:/߸^j1(FZ9\Z |/+ué'Zw V~Szwmw"`!]ħdk^ٟ7MZ{@1@44 `0 |qx(7\J [-W P!TO2]~O_@Vun`c\6.:eyГ`;u%-pgӜjA928 8*-@|f {Nrt/ iɱAxͅ6i˓->Б1~/frZV=#|c;Js6Iv)c`iK {q[{ mmHzyW5sIP.UW5y_0`0 ϶4|Fwe.h]^Z*^Yhɱ'ǧd,W.S.gGk0Nn&Ė*(@馜!rF9Y9aop]k*mgCEjYQngiYWny>,R&AIS?t<3pVAj%luG) Kl̲|_Vn@eja#|ir ''Ydz6^>RΤ,J!ZDptթOr<5=t)<|pAx 'B:чvg0 ;O{AN]p|apy# 0=48DЏ3pP"DQCgOON8>#旋*$6Uu6dCHk:m>V{ =rϸ)g@Βo_xqavُfۀ̼n} Εnڢgq;!pW][>obfHm0  ,@W)/ -"uMC򓴰/0y W)zSYCv%W~g)LaJ^EDuiu!SwysFq_%y8g C֥ ]墲Lڃh GL91v٥X`{ W q$>=:d;ޖ Z>k/ܟ:~ZulJoG|=n΅ &65s}m.U4icds#dLb ?e\ `0~__qw{Mk}[S͍AtKk ,AY)`}wC #E3}ςRE)AhDȀ'ګ@'/`jh7~ kB_y|_2\Lܸ|\њPMo"&.vWcrkz@>6`sstt |}(x5pv H96$#df'Έic_ Tڔ!wIoiTNyα9 6 dNEH\#-k=ۛ`pH0`0 T)$|!{qP1xLpw:)ȫ%i]Z05N|79j߁_W " J d]-H J&۠h3^܂MsF`Rd݅WoPϝncMJ>vj*̎g\} E> rnTmOQE1^7Kk]e_?&TK ־R7[!rz~ҝAdrQ' PVӚZ󫁜c,Z\Ɛz2|%80zpE `0 Oi'qx-O? &$nA64\@9ha(q_܃I+灧{Ϛ%K: ۟]H}lQ7g ~"Z->>e.Xfyuy^hD,hW X; }vexO"p™\K 4~cJ-P fm9ݪ|փ=kA:T}^ i8/DB%Bp3ʮ@.*T>zюJtc(O 놧9`+>ӢW\@ϴo>%w]Z: >=@hA֝t$HH"DUTS@.j_W`0 e,~ ΅{-X>]usgB cCI 5h %R\3]́Y?7jLou @$uD-Y\YN.42[=@QVMвbhzS;i @{@4u3s\zfZ؏@ ]U@^ 8jK/ g@5 qLo.Y>@.!U8qU~ZH=r:WR 8O{yOׂRZ~a/ 9.@/y"C؋ki+CS!Rh'`0 [~}oWYS6*AAů7d?U5;Yprx`BpAmZf9`&I d>~BO.>#],2ȾPiDEḳ܁n )' ӖX&xG*X[X tI kQ2 Z% f. %wYM1_piԷFG:WK[@Ck5` pp/YFFu7P||r6*7}K-(?w8Di: sA*-8Zޏ7Cv9S,z+:rwh Ƙ> JN >R(AOdWB0ki*z,N@?8Cn0 !yjVdYGvrm@‹TH>77 1,%i+Fl {rRpfO+! I&HlfT` XayhINY'/ɣ g(QSTNV+f"`O96G~%S>VD>a:@:Y':OI줐HȤ\&"+ `0 9Q]4A}݅UV] x2E3lf0 ȵr-`}D} _70L2#?q>/oP2Sf, \r;: 4%yTA)!#<24~q^"P@(SՇ?"hʫ Sh0 ?ޔZ:u"0AyHlk0 `0oP^#8_iW`V޲ u^~lmG-K/_c|$m0 `ȗ`M4 ЌiY2H"7P&`r614?&»Pjs&QR>`g&'y a·Gt&ω_o!L^ooDS_p\pYl@vdO `0^IU*Aރ߀[z T_eEQDƃ(mqQ^l#O(?+Xg) nu]?p- ;z6$^=D7}+@^` NoY8z_}D/ ) DM | c9Pg8-:]VJk⪻U{u[ }'Hpαqf/wLȍqJ:/Kń`zaO_(6l E qā,&Kr4R]>w \,'_X ÿ?_ ߽e x| <h_}GDZG.aYU\C]f.U^>pH! *ݿqx7X-6 IbCb }Պ_ujKڀ}hQ;R7Q+usr@geJdL? |⊈OAd[Dm+aeK=J.^H*r^rcթFU`*FE&6e)CiE0/o8Ky #pTRIes#(2J=%eMY6_m(t]yLp8q<&gL}Y$c0 8 lv7^h1Jb+Cs{iH1.C]vL`|X.Ÿ}r<b$E^ /f @^e}> ^9Gx3Ym':p0P}/Iע!IvXvZ4(d= D>e*?   s I^5p9WsrCӌvR=r!r˵A̠̮ЎkA^[mk3S L|^'w{՝Ōv_q] H4^s<=W}ɩ32dnL[o!s\,;DHEdiY&Cܴ F!F;÷}h9=X-A4D#ݜ 2fHЦj `af[G ɹsۃ^J$kVQku5rzݚ 8' ߖ_"#ddl6$:Nd+FW1mF| l1v.xg5e9X3j58(8BW8{|*UӳV6փ r*;::49xmÏ.Pa#En/jT^\ʢ`-E/ )RS[ku4Ojt!Ubъ_p ÿ?h$*XA'HPznPJIx<<_8@,-=F zMsr_'>y+. nf$4xkVueU_Z4!mH=^sqZԥ&RJJQ+b!{nօV00+Աfu2 N9urYӠKMYgӒi:\mGPGX67;_uI75|rp<$#;8 G!T ]N=iiͺUJ4,1Bpe># >D εM}J#>/>>} ߷[2oٻۿi"upȿњG]j@QJoPW)r :D}-h4_2*P tEÒ~>?/~κX(e+էrػC 47;=eZC>w{S:dȾ .ο4{P1^!4h8t,5v qKN]:0\Rݐ@)*>zT C7}|/ցƓj:%\ F-ppcg}^{w3posmK(t0*mp}õdκN;uB mxLT#9{K"G1P?([ilhSVA:Kw^=2C#yPgxɵCGU>A]u w +NC? LL5L8tsz @iU*%2sZN˄oܡO޷zUeR۳ vN'ܢRWԀ_ IS'g\vɐ-1V;Lv]Vw1Aޠѻ}^u/B(*JoM]n9\[ty܊~#tjp'f7;un“YGyk~\-x&V=.]JXz3s#|͹5hO-:N.Ap~Pa@˭(1ctU 6N՝sg|eLܵ4ʢ f0 ^k1L%{'ל[rKv˛ 6~DM7 [7v983Mtx 2d.Ⱥ TWs$OτUn:ܺOWH*_,zH8̀=!=" I[L)E{MiDCЭy7?tlD308]'\n:ou^ ޥ3tn\Iq9j+BNBh!-@|&ƋסN;lZLhAdɈЈ@\Q rI@uFD|q?|D|8|1K/W ᅟ9D }X&Bx 3 >,VJ@лoVX5!Nxm=<@u+>˳ 2L;M+L@z  .-[t#P(sQ 'ʥr9` 2z 7y1NG])4[du=`߭ue^8X$Y`͚mg1(7܄X-*A= e/05]TۀnяM_u'pypōR̼Nr2J uO_{ z&(IJ2 %raF. o>l=lSAb yսc0 FސigsAz xKyc)1r.-}KF ɚzOq}զc$@|.fy`Y`)Y^l9r! ǕJ/^ԝls;L|d(X7ҟ5Akv@!U`0Xϧ@'uYPE 3% {|T$1P ,|4b;1+nx|I|\s RN+%Ⱦ>pzxr.c?oL[̅)dc#Xr KȽt\ ȇ|U׺"/q (t6pm`,kX5ae+.K=$8ePF\џwW&4A* hH_ic?Sf0b|;øL2 C P.* _u%^PnQD J"L_@ 9׾J((1}6|'rYrZgD7-u] 'Ir"%Bo70пKo ):#pMqRI!s=dWٕnށ%B)!WԫUA6 ecP&@tu }(WKyq벿IwԝAyOyO-"p`Fsun+k/rh.1r,x2>4'd~vv?n2ʼuj:-pg'gs;>Fkėb6 Vη;;o8s806\>e065bl6L_h2BQ.O?ϡ]WYڳFT?%?念!h e b )ZdyD?eb*)e^C:v}6\vʋ-%PB,"J<.`LH9y@~*C Yhy D sg1aM˜&ID8~A!{s2"ߟ v.uEmPB/JW }\zD{ b2BE)Pol7.ws@IR2T(:"@K }u:,7A@[`1\H ULW0v^"$UzgyGu߭ PӳF>PRK9[~lW[O\;zaKCo w'c+ÉCO~{U8̕?ºu_Oo5md?z[#@ƳRao} %:D`̓itw r$eM̂ĔHhrq. r3{>Ŀ;`'4j|DCBu ,(sІQ4k +i]8. D~ )-`bgs)qx,8|h,fCB“A`~hvS>N%\KSrHƼQ0 %b,mm2Wηp׿?~5(԰=* iNgVM2eI}^cH7:p6xtp(lvYo"Q7*=- >4*S+ֻO_u/(V@Ɂ8O g&A奕V?t5|<{V`Ѷ ڻB(U%*{z{{ĹpْZBeU|ǕG{i?r(i*>8IX3>@ŋ-v 0L>` c@GQP++?T PW*ԯ- =} ^޺k@(szkͮU|xy+쮵'a7Цjk{"G6v5عT5Sl]#qݻ|o,-({{P}FCFɯN:5^<[)hF >K"nR(QJ$ϊ_ǫu"ܢ 48)Szl'<LDSo\~|ᠬ)"\\X+ɤXSሇ_| |.V{޷?8 ?׻,H,U:8lPosI 27XF8}~Ppe,rpj˫afqc. U=o nأaC\GiQX h ʅ\t?~qVt , Vp u Qr?~)#*6/XN%o,dN+klb2+@}; !v]b;=2g>x CځBk1bV;aV!)yV([{_RoGݝ2u((Q:=H>ub}tُ!uvath?dwU_2JyO 5C@"PIU_S7%dd˪g.syyԍRM+v;>oAk6Lx9V.j=ZBB.Son`1,-X۟}/- ,+@ @UQ7$O 5՘vVW0 SUqsOͭ2e+{ހz!^Bp[oO6XcV3V ]"Wd.YI,A%7 1ÕWN]-KW,-\t%jG<+ypvعoΙ7 a¶By!0h3Wk  fl1O.x.N5_/[}s'!zd. n`R^ Rk>L&O1 Ch.fL:K5lm]8 Cю>~uz|.^ʥ $~|}phvqq{s1=s/++w*M' (q"`ƫSvKs>=GgewKԑSCAOr\:ayy*ULLp;ɽ]H+Z$XkϮK# .΄:joo {{W9a9!Po̦hױͶ:ǖ z[}Թ_qX YZɰ֭@ٽe/B^}=#&ABᄴa,D&(+J9PW)ŠlQ;g@{iilV6* aw½^t^ƩȦ"ׂ"RLL7L{mg) aa ȍ|'Y``뇖֕`jlX__O-m;|r¾?X/$lsǨ%ݚf6(0Ok6 vc8rlf(T֩rFhcu=Mπ[8W6 D0 q ܡΏuW) U+X-AhăP"$xo nV";=^֩pjך=1}X2 nSE{ӎkg@($)k/aT}lgǕ ";83$C"U;nH?3 Z,dnuj_m NC9xSd{lm1@Q\,R9Nğwfx#) i= |97M Y2CpѰNK!pO܏y4Uksu $E͵dteh/ 5e.AVH3CNpj+pB2d bJeVyPd*֚zOPԾ<K]7=?s [!'17+7 z& J^7y?r!W}4\X8֑G=LW|!ǟ E+ڥ V % "BDPy#;뮺'G/|gf|>5XpIqrK{žK{ ]_0]_nܻ Isv&턧ן? k.q. `Nhղe.w!/"mpAC¥/\/8 J3 hiZ QYQfđG̅QCAв|`X7[ om7잷RPIYK+󭬩҆/6րߕ[O1~XVi٧*R̆'99Y;@昛1A~.ZJ4S?]ِ<'y]e~El=cR?ƙCtn;r|fs)Z;-]ZK^V zn"H56Z}ղf˕Cتo*_߃cs#={7w tb`an ?_hWg{J%wM d4Yظ,O3W `0\MNqcW׽{XKDz-GC Ix`AXR_;f :$pH1[!tw#W=V&3S[xPWWzd7Xa=~f#-A 4`/gݬ/( B *gyYIn3_:c(\%c)nPq48ǽ5h;yFo~3'ezPla.r >U9̧@.X|FozՃ`0JH=Z/!{CA?߶TYT:D ?>B skW@p'850fۚ-h:nݮ}89Nu% a3JCܷ~L{^Ogsmv.;? _& SEW?:T**O78܄Ν;\78p ]vY9rWsU+.EXBmAIJ( Q]&FVfn5ݲAN~'+se?o#?FoT/ S*{ǁW>bS.}9Vj߮eC!Bބ':kJ/;p 姖Pdmڜu,མP^]VaaԲ2.~tᐗ[|OշX=;Jr(p/W>X@qWh 5 0vلç ߑ-ԡ+=tuޠ5+|S?Ȯd/'J_ N*r/!Қ~Вx M 6gk>@ rC>?W K>߁#qPxB2࢜Nȕ$߁12`%^? s ,], G̘A<A{6N!>SU  &.rJmDWyQ^_V EfijRhz&`l,?-@m͑jLLLl߲Ie6ЧSAU}T tt 5,[?. W{]{@A~%/@}j@CvPUVVY@yY>{⢨@wh}Vݮ~j[rnjՂUmReȊr } ܶmu{*R 04և"ٮ!07(&h7SloX|,΃)C;tNoӎq@a[A~&ʭ'b\L{=e_0К Oj\Rݏdg,.믷h`0 ` `0  `0rPTH%B) IrH!, VrE;ю[TL , 6b=&hv$2LyS JRxM9d J G"8`0~%;fy o jiii(KES_Q=Ĵ4*mDkq8e 1-0SeS%S)H%4Yq L;M[LkA'1 lC{ɧjZS%SySi0M fb: @5E P߫ ChPB`%pΠPEi+P+@%EIP;PGoC~'cn΂CB"9%GL`"lxrb&_ h(AWLP{~QQ1$qYAK  +EE5J5 D Ծb(U?1 D}\ M3 ~ t\pZ^- XCM02ob(6MtS)5 u): j>wy`kmbX$u:L+MM A-P הg6:QD-1:"D`\?7ˋw; q\9ӽ@=<7CXzeie p::ϺbƔ˧-ncM8q&>({MH>&0`Ŧ@jv9ˍ'\W%ȪU;l\Ѧ6kK- InlZVQJ+zhޡ\Xyieʹ-qO3`6_0νsnǰׅ{-uG1'w]68DX޵L̇m'n ?Y/6~/5T VAr!Mqyia0U5U2GAQj"" νqNɹ8Gq$2`l:g:I? i}M;P2VQjb(L :N#ja$f&%5r'RY)B{)fÃ{pPE`:k:i:MM@SnchPW x1N|`ۢ?_J vhd{}pG^.dhilP_)wQr@3]{W{Ԅ۱ $4ڲA/T׼4h"|Uީ7l1$:y-s tHLPaxHǏH_W""D9YNTb?+'nٺuzBV\VjV4~xҙcj UUX7 ?T d2ڃ2QRށl0p4$$tM#G{g3 =T]SūvP*)J ʫ8!Bgpb}'IeNﯼoM 1}#,7/o s,;v?]O}gz>/WnvI']c? :tkh8~lq_8<|< ۿ'??pEH 49̀e/G| "XȈ#G֢!}6N;qMhrU&@_z nlxk:ܾx3Q"@7wݾ hq7SmO@ـ2KD5ށ.?t)}m=r_V.=s&.3*PAmy8sJزx+6v]$U/TS 7x:k!}ꐖ&BtCCC(^Z^8*iS<('HP4a(zxcxv,~]c4tl ,#)w] '[!+?=xye+to__ 1b6CuaӕM~|jñO&>z;v6رhgpT˹$$%ׇSO8 ':r!t6LdJ0G;8x!ʆXi}սסP*SٙgI!P蓀AA䦋Ii 3f1YLy3hH3 BϊU)Ν΀4Ϸ=Okʵ宾q5-;!ĀSZu3>QFeCÌ ۊsPj74~vCQNPsE>5!vCOb(UqVLPjia:BךhM@f֙c>օ3w ${]&*"^Cz @{`9ss%d&Y{Y3{)LK ^xxcI #TRu׵&Vn=ئ|g%ukl ջVK,s, PQp4VJqҾi+מK^dq /\3 rCDxDerT9Y, lm6,7@ЭollUixtf/c%TTn՗!&’FR=G\ ƦWPOv}zM\iRjLU*:޸>733+B(aOs; N_qn]w-jVgBQۗ (Vt(UKwջ}kB砫oט.C֍O@^pp:w9 ;q 1bcFBOb+׀mb{M"Mµ3.n@UY ARYƦW4B"~u{&OZjCAaAXA%( 渍rEVE9Epi3VtOsqܿ K2Rޔ ZR5,I?< ! yV Y*,4V@ jU3}kZjZp|jZ[P9r~BQ⁸mG065*QU&UW" H+HI ;Zܱk4/o|`v.p)r+Cq+|0nMg6i ;q;n3P.,.Z]_k7hUU@-ku/]c3^0.^1E*&Bi#koAV@ yꦺBAp.LJNoG8q8l-8p Kږ i1t{nmK3f_)ٔjʁ[yyХkFh[M6_8ccG|0HUYQy ^EyIbX^<|dѣDp~zOJ]@Yi=Y| )I?\!a^_s\?bq66nkEօ=L!ΐ}Q*,C3-,4McibXIgfffEjZVg!(8= X""Gă [훷=A{[CIAEP22`_At]E:-p[v-}]v33vhUp|yv4)Cd=,yXzuٚ`ow_ G#7%}D:eyo/țq%`t61&C^ylRҷAD&@׻vt80̡%ؾjm/>=S6@5j͡P|uσ皊le ][ ai#^@uà/ 1Bl"?אָG5YYYej5_uk`k1ܕso&=̙?gGy4.i:\-)*^=1T~Foo@(]PSg¡&M?w* +*,G^n:msLH=V TJR)`SG'gvk 0EQNT8.=x.::oOw77[H6d]d,͸Y^i^'@kg6 amgm 1leʖ:ppއBE#էJ,+~;@N&ȱ@".640h ~>u,M`|]E0(gNBLhA`uOk{N@Mj״~o@U %%e'Xuj`CՐ;߂(sZ ~ֽסaZZZ C{6$mR$|Z,Y6< 6i v!5mSb(nW<0o^kO!v`[jԦ59Q@}`UeѪ)Xc[ 5plpsx mza6zƅjM/ϡo!DVBuSzu&@7jv nh?ϐ}#;.{{X_`خݛ0dŐzGCRNף&j 2 쾅i+*Q(#!ħb8'2``1bgƫՄ8: gT+vJ6P>TߦT״$ɂmxw C bP`ުU  z%8~v*wKޗ\ _Xv NNENQߵu/YM([2V:E/>`jcl`~U*`ot j8t~ w߹}[ߪzu pMꭡeE:pLȻ:-pT#Nj0yƵÕtp 8",soxSֿtpA@\wh [z/n!'@h">oSs p ECaH~ \kvaPy9uumPK֍ `hZFAL ū 41 ER@ωTA<t_uP :y@}fl5oA=PwU1Ok?`ff4-Y?•nzۜ]^V?]} 09Dƨ1j,\WTp\\|/O?Pv ԧj  ^m>^W,Yw_H1& RHT=w y5P +m>➈RI%LER %uY]-Z]3`lj{߲~Pv^9ꬺ-QA FQb$h7kePwTMUbX*Pd_D[LLAZZ%x@Y)d /诂jکM^^]k\:T:[upgkT %H)hp$(6pp΂gCo̺ FqK؎dZ^"PY*S值.ʋUߩrT*Zˁ kzCDxjzIM,d< Fz3kObmj'B/rArU bX \"x@QCZTZ~I3hoTTfTւaj^6^6^p ot)7]r/67 \VDU' "# w\jWԝ`_͵ŀ& FPvzGNkդi1}]As2L 'U'u+X "uSKR/r33?pn Si=c?A~+@t[hV !++k:`\qx}H49iD~y"}(W*?]%h+ _^L+PdenJJEtA k wo<\x 4?#=[!kl奛KBΛ9r6@VqYB;._F7PRZbU RL`fF{2V$aa<ZV _&T02e\ CEc bX,{w0 6GA+D8 h <@SZ 2r8m `9 .quʫP`Ex Jȃ ȭ`lkl) 7 2OZ=ccki r\'.qxqA1JuT rDx#gv133K'|⎈( 0kqZ4h-2k> ֲo-A"`3z@vdКkMOxPkCŤG5蓢E?Ս~Fwk?k~"񗟻?DB<魛7mn>m _"Y$$A"H>^U7x gn1Ζ~2d-;vqu`wnmptJ=k:DÅw/J> NNVסuV9.P,\XvZEig1Z)x60$m\4 )!/ay$4n`qr.JBbAҎy=uQqG8oaJVxZ { gݯX7Zw8%CGd{֡񷍿.VmM%Qn`>. :%Vm{֫A?_P7ٿ/L6 畜sV7ol;k@mE H7^ -CZVgD n'xqv&}{vwjPՐ!ۘm^N\pi% n[ݶ "z~Y~'AZltwʎ~5z% |=o@B(R l“'AbU h"i3/y_BB 8y~9sP=BZ?nuY?_C8qdNu;vq!kM_N8$ާwxaZo11Ƥc?qѥ"JnNÝBk3vDY(xV\-zh{ !iWJ3-l,,!4/jÓ&75yҋ} ao?{\xշdB,YjZ9vtCZAye..O_~R69­[GZ-wi-_kq,Μ9 ‹sέC+dC3n S75~0/]-B`,hzo0tКC~d[?g=jZà-g uaK:'NKpa ᖺU?cŻ]^r U,Wn7Jk F2d(*AZ$ 쵿vZGN'>\qF>;ܹc-AYY%%/Y'J\ F/W[pӲ \^ppmRMu1|7\3]"GLI]#mKk""ۊ%.3~5K&88{:%îݣwW;Cb7-B[8p@W Ǩўpeךh)NJW:܌Y;5r;瞋<柭wv%$tIxyrǮPٶQe81dp}C@NϺjJ3֤E|w=>~1>Dc޾ f1@k? ~)+8i p'Pޢ{Es(UrGw۞+{?-swٞ[7߈p9D *l->\~OW o^ފ#ǣA2$JD5$]+cX砵s4&v#vtizԎS- ~p; `}A!F4_55A`ji7ŋip饧C v͝=n4W loEc o 4m`܀`or$?{D]|h`rw~T?+EA7ZҚC ]'TF5v>7_:$.H: .w-LMEsQ珪Υ;U zs Dk9Ul| |^Ʈ#8rl =WT4e>|_ B\jFպRzkՆпd Q|:ޮ=7 RG9))Ջ;yٯ@r|rI(/=Y%!Ips0l륌xKjiQ)\Yw5T֪l_>@Q}i?jffoJWehַk=r/BŐC|ƶ͂ǺY!`rZrM]=4((, .7~-~?pJ+sYJF>_.ܪ99 $&&⯊&UF}Kg[I$8(t]ӷ@'՗n Zɖ) /@ f@zN`իb0}c޴j<z\u_ۦ11`fs?~#i;@5B_v> kP-%sGBua׆ S]=;w7N(Xh#Ǧw2{vmE01nL!Akk#VjrQ8 .!5Codjf6*Q!J hIsApskwrٳgΎ{{{/lp:c([2=ST}s|U0-6-5ӳ{]9:t,vWZ,1_))}jEU^OR65`t;ayׯkM0cy3tKAT6yeRJCy@ }v4:P?O p%;=a,au _JP`n0̤1-UsgPHe@4lSQL1Į Nڈ2@b>x2y .v ɺ| z^AQG ~&40&8h s(j齊{@ M-ެz vvIƍ`Y2*]j֞/[Ђ ei⦸ ↈAUOE¦Et /:ssCuk+]9{ xTc@=P *ll~m||[> 77.]uM]SϺ`=D~:Y@KYz!x :Ղe: qm&_.^Eފ T əPܤAq(TՂJ݂VZ\ז\z)lp[gA`,g[j9x@<@s"aU#%gxǦBO[%B*j y}`/q4hw>_O;v];־k5(FQæNm=)Pkspd15c]dZbr4j^>W}6M6q dl/yԆlD]=S5;i;@JmtcBvݡ$44\v[\w`[ 9}>|؝bCN DYy ^Pn=.$5l^mlaOgjffJ*i`ݺM o9Cn\K"`5jU٫jio9F;C{Cua禝}-{WU5;oo.s ~}|wAZjJ(ľxw^߮ 4 F7=R}U_l> ޫrnAAߡ'8qƫti Ե)ߖe@ Z y_fe%AkIpiGhV! >j\0Ə {ސ<&xZ# *LW@x /m|41 Y3|KAH'-<)4oˠMkݺe dNiV K]]HϺ^_BA7.]κԀO+>C!0:ݱelr.v ut*,*CC KtjZ@]:PT04:Hkt v,N Z}T<\q:QWvyЩZG>ZP&ǚ/llm67*=z{y{z'%oU/Bf<~8\BZ^<)xxv~ a+}^``\J/_ziZisGuWC؀SA69MQ=sg bEppgAvZfBTV+); mz"ӓ4ikee)`ia&1y[6 6L^qp bH[6TL2)y@OooUt9t&O:I3Y@ 1dfYA}q zD_ч@;@tݠ[X z^5 >n v]\)C"8) lTy FZV&x{ܷ}B= t&Ыt-=b'A{C{] %*$DK`–ۼ x,ǫ_Rx(#z'/} +"薺nr/x^0YJS* !y@qNgҴڴ#onh`ZV@0ZVK,͟{"(%3eȚOV]zoЗ+UOEuUA;Hi"H!`:c:o Lf2o6A{QM?Qw(K۽ |jz]Xa?RBi/“tvLZOoaqM]Q Ab LP*TK+'mz'Pm rY|͢M~O*& 4{FZ~{rӪߕXJB-rPN;=lv-k+܄ BV]vv GՋUo+XXud̐)E+Pqz<(F|#Sܤ@jYz7AJiIw=@AK 4!*U ߉%b < gAUϫZ{=\v`ǰ )%5U5%5ͮZ_>' fJP WjP`uou x"D{^CتePUwGCpײh2uDqS0)vTUϺ {ajZAA۪m6ѲxMT) -> 7/_?u7(XRgY,YV38jo O,F$߈ C }N蚙}@s5GI@[PBO tiS'k+@=Pv@$ gfffffffO"U 8O)*FgTXFo/- *UYbC7UT@v8`v+0sڞlk]N}8w3|͊XG|۰T1STܑS*CLȶlP1pBzAI&ou˾+HaIwл鍘 ʼnEǫmCŢp'1r`2}RqbӋAU &D;V䊎PSjϺSl Q7^G/KC̵ BIb *>HGA;w_(ˈPTUYnW.o@܉vv@Ib /Oջ-}~~aVav0f5ܾr`({ղO/T]/H8052ti)i.oac 눛5A?πxzI? u[کL86ؕc5Azk7.rRzG|6%2<<< ~­#¾ SW%@̩̘V&_5uP}ٞhؿeAX ~%ygό>{R=2~ zWv` .߻GP^UU w XqL8ϐ>:}Rr8 ʞ9 DRS2SLn# "9"qAs촿SKDqr[N}vXv6*3ȴͬ^p|Q#ȳ<ɓ m oy;guΞkݯ\`u zXPzzi,99巃+WUw S-9^41 )p?.=x r y  8tk~< -&*6T++͒饟e,˿,UeRU~= ƽBO >u`X0ӡ[Dw@˜1gG[ͷ6oؼwoOƔ$.| 'ˠ䫢af*=G+p;72'HwvI?j%@3O=z1Gؼi+`0^3[R n\nNVC^;bs۝. cJ{b|W?q>DE?}rWν8L(xp.!')ִ5ZK}1UۂXN_5m pBfB;~BSN^Ӝ Ȇ׌.a ԟSoT0I fvAp/:x~9 zo^ScaC1S.`!KptrO2=z mn8 wtL|TQVڿ=N퐶m9se5#_?u쾷b(9VRZb G ?6L{dk%}lKP~ɤ pya&Gfex#Q~{MOkG k#u_ w߉ ~x-ğOMoۻf)Imؔbʄ}5 o9ʸ{wpg!28E:vJj˦TRAE=hZd~uzN`Ū/BX>N0LT1P!O*ߓ3j@iZIrX /6l=_~]P ;7HxZG_A9+_q]ɠ:(k!khfNJ)#s[EgH0~yKXdQM ud<{jQ=ݎWQT'Tݶ} xԱZꌰm !+|n; Jv2B̠}jdr%'%Ec}#Y>sy 0}hBiOKizֶ_tJ0UGLWVZ@o޷Pл!CaW{;׊!ZⰤnUuKaHhkQjQ>ԏ?^c=z\zmɵ"[#?p DDߖ,+7>~>"=_AyMށeGog[RG!P! yZT{~sgj!bXoS5p1ig 04ذh7Wc{/6F Mj@rV`jh,:ZVtR>`0\H7A"uY`58j SaQ:VMQS՛ " +ꦺޓ< @2Q :5U POg/ɔ~Rk *+@5V-Us(1 ʢbr`W=Td\ x5=T7ä郡ukoZ6n-]_ C zL]'ώA(_ҞI|Qr&ޅ+\b^/cd,7vrKjuR.AkA/e¹翺P26f2L vB\|g]*V^! /*pXzq0΂Ie`x0O @23 т>~?b9N]JO. j od+b,/X y@EZ\Y8UvA睏&>L5w@iA+g]} 4hdl`ۇ9@ήlevi8E0>}<8G΅`ƾ>j^{@<(-s+paŻC4jr'}=DZ_9εeKy@hޱhv{ӵ3=j;Ooۀߋm} %sG^+[]%n'>//[Vpƭ[uʠ 9QPrE|4(X0Zna|\}} ѽMr`Z<%kI`T/M^0+}`fJ]Nr`/8C CHDwބa݆= ;899)j)lmv==|<\!=(9h=d*l**B M;6I}i} UgU?^'~ |^UBpz^a95M6iՠѲf9<)ׁ -loG#%ucTojpR:'* -FN֠lDX7Z>ϯ+L= wO R1]kXf mMƫDBhz']O*068-6 ߴjNMm(|mVkXBì3m~lp2v1;ޢ Ǔӛ{ܡcoffvXl)>MetK.v U'޽B1KA22"[`F]u/6orY}@AU yief6d[_!>숊$hv8rr *Q%׬c/ߺ!nxEZסWqcf4+@Wa"7ʪʭ*H >AAAa¯~ >˫]&dt̘*d|q6+~dcAW*Q%DUB-޳{L9a6.`=g.h颦uAT!䥐ѵ ff5C_BFI3{7:UMvsX QȆWW ܪ01=`d}w[;hҮxT\@|PƄr$=V-m+ؔf yH~&'AU{w{IjD9%9|s|?K#},ju>p77/y9*AT: <-P{^m=7 7YlbK\*6m$AסEY?T POVx&۸vRb[e-Pq*R]}( DP O}>ŀ$Ir@FI lr)nTS^'Rr H"(qhZAjfޑE1J[M:t^6mZƁ"_@9rؠ/j%w5,ҩ5Nj kA up1DȤ(%9"M(: UUcYiDHAdL{*BTQ'Fd| ^U 5 %02AǩxEyuqE\, 3dLuNW008l5}`r1yZ%X> xvL*JКiMF`jhjbj<}i6l mi2FFk rDH" LMMwyA{_{GEŠMA֓ue(hZj0 x5 ])+14/Cs&L& W@^d8z+H1Vtt*@j ]tV[ڦ/՗Aΐ几].itttTmUGmT["T@9B %@sl4 Zك)TK➸Le9.~0_Ώ/nvLUMY͏n \B` XzyI^gA,߈ogzhz KKgЛV }tݤ,/>VbX)~vS jCwa߈ %gJ0ӰC}01ƫ (mWPnv(=Q$7\VDU'Y/~!Vz^]B튺SlK׽>#h%ʵr~?+}/|%^,#0ʰ_TKhU۫vWӂ`1|ApuY~.A뇠xG њkiR~`5)zSZ|n^~Jo(=P07 xSͨC5c aVGܓ^gj333333uI]Vl6n8a :<OW*t`?~h`Q\vj Vs2'QCʞ<5fffffffyUW Ϻ1 *Pɝ *@diffffffffo(h%ZӤz^5EMA8t?ňA6dcd|dI&-A"H,3q\S ir-g-{n/>D.܅pQMT"@V+AXu n=G[kff!ZV-?S~L;MMA`[oRJAd,c}cmc0 y(ZX k,AƗ/mV[ My"Wd`vEZX 0 Y`8j8` b'O%K1A 00D_Һpj?Pb%D$IY (%CA Gr8B ) _WXX d,@ Ŕ(˳`|8Zh,/*V#DQ_BԷS7 r%!zPZѠ-5oDemo%ډv-GV<7oMAQ d̐!?m^ O˓ؿ?~@U[dYɏ%;蓢ϋ@ Eރb&PڧtpP5곪OEl d@vV;̥Gdm"P.y@l.<*dm D^yrWN:@|_TZ2wk P!e j2@Lk 7P2-'[PhkRҍM 9RQOҼY$s*qOr5 MنK3.։(µ*5 8Q1b>O~IKvY\P`hfm]m#}FiAX`h;-z0: yhn#bǓ+M&zhݵv70SPYP3bY$at1ڂ>+WFN{ Mw*RbR= <=Cs';b+aϦ={n| zn;CmNp+VIWm}dXd [s((`uNaOX"o,,'[|i83qP^q&MvN؁~F?Sn,q;wC{.oD4s9n{n [/:1V.I:@rvɓa}?p> tSqL=o=w0\5ʕ?h+ڈgJ|ϋ(qK#C6zځ%z *D(}-+ EO\89;o7.40⣧h0y <Y n3r'g^Ԭ5K)A|(>@qEkX9;W_.x`1FZcC36':>Įv1)`xdxhYO {. ] <|!eF_6bW{j-E[_ewCڑul- $KjdAꮯ;|V2 ?#& nӀ#<,z<޸ +cCa@ŋ{bRƒ@ PQe p0(3A}02YMPOt\,q)Avrqݎ6W@!P>+KP9p s V>u~ԴDU^iI@E큉 -,B@o XĽʸx h|LPM[4 u)LD gM.< lg&33&JE(:= MupG pnt(#TȈlmԲ5pt2nvuVu|)Xf?h~V v~˼o"`cxA?T W-NBfIfef truݧv}A 7qTVϺ|9W΁4ﴺi`!;|N>}q\$6PTiWwDéj_=]ZpcoDQ(f;. r~ٞnZ7K_,; UL IesoX1|Nl=O/˦)`;b63g[SpFBNG;C?A|Aذ C ;$- {V݆C=>4gU\lriƥ03MA[S۪6Vmb?ίʷ17խ`5jU7h|F͡(X\n>|,߶m7W_P=%[# o2:4<J{8~"-{wopA7K[W]{v7!qmC2y,!FżBtYڡvCSz%C ^j;i~ywbWƂRz0́΁>ZVm;/P%ΈS ޵D݌ V=9*ȨҨQE#:px  v9T)0.2 ũ .beffޤ&I1I?<ʾ(_\ֆ+.vǧOaa߆-nNp#0=v'햻U .1+!?1,_>tb)<4p*$LJXpvA~VC ˶??yNSTpFX7zMr侞1b W,.@}j :=N\LkՏ-oUe4q[%D': Z۪_[PA:]!Gy!r@}F 55IMk*@MQow>#!ñC{{]ܿrp^}dO^~A"4x~~0CfVG?{Nns.J\t"PX Կ=Nv8f}|\bse4oL?~r{;o왹ga\4\~i%ZD yAʹ8xMu!VvHv\?OlZh5]~װyKG/C ֽ>qpSO{k3]^d͜O7~ ק]: =qp xytNT0xc+!M-GXX0oqߌ>cc6؂ڣwkErˠ*Wd{d%vhY^'#YA vm 'u䵚@W\Ls[ &oJ/fdeW|ZuJ&(4>Q0^'S[Y>jp!ayG'G[@(+^d &%{A,z/s=x4ȏ:F߁&2D-n| i g@-;ZuaC?P5x禂ܿ`7Л=7ٿ_U^5;v3U! Nv,Cg{ >(\x%$> V1rՈ7ૂk7úȺ볠jcU&׍(^q1\xe,H5K;e?͊8'Ή@gΈ#p3ї>/0ь@5 H c(E%*\6veصy5|ܺG ;csn: b@ } z+.tv ďMX K6/&'X60Q 8Y}.@{rTpxxSa&יƫ K>џ~2Fy?N]@A0ix^*߬ {_7j`JK4]e`r1|0O Du.X߶> i_M{*ׄr={ Ze&hcsZ?~t,0 Nтij[t<{ /. 75؂'%:h- qжϭ; #434:; Mm (((}QFdQu JʫдiÐnC =}XZ<7nx R^|qޫfZ2gxĤ^Oo7fn&|40ÀBc.d37;ColP=n<.1NsҮpl~g>uz 3(ډoqyWH`{TR}1(C:X31x)ֻ[.L h-eΩK&.A$F쉨遞bI/&JeLL{0#j8}]]AMw)t bԘ nv b2X֐FPkZ qk|6b~{^؛ l:u I%JV9sΟM5TJ޺ }мWh 繟}Yw֟Cn>ռ3 #5#?vQc *j'SN>n$'u><q/8?0ېx= 5BzAD7o4GQPp;m&VuPW5-ukn &}NnS=Эu8y¡5.‘UG';}6\|PpKa8{i_X0z5y,* in1P#w mG@|&A@5PcΎ;ZAVYS^5aŲV*dp~Eg[ȟYZG8į'-@u<ƤE8z_;s@plhMgf} 1zu=H@w{,*cuTa(9N ! -!>rvJ03}ixycgg½{@a-:| lr/qZH`8b\o Uq_\h4ņV-zPO8}gZCEʮáaNk ߅j۪  ,CP'?~~ |_'uhh>}p"ꖺ-ZZ5ܳ >nAo-j  z}A~ԄLJz@gw1jeSwxPֵ)[HX5`e5b$ qӷ;@54hUՠ2Y>GPFL@+ӪO@yAg ]PɄ >- - TQ[^U@)dw}d`􍽋{_DXrw|pos4Ȳj`EP |&X>>lۗrO؂n|^~zD7S!~/l_i0}w*ԪUt}~JFk-Jʲq~!}[ 5nfM }ג^Yo&usy :o5:uyM:W]s- *lw= N󝶂*R8wߕo[^Sބwz~^ݯ[{`MMmػ𪊵ߙvz!'K.(tT)*D"ҤX轗zRI'{8zys812,ff^k}kkn YRl`(pLNi l.AZJwގ~  +tJ*eQ­EE6 ꮺԥ.uUJT U*/1C/@3kGd&*bUR+V-+,j 3C|\>uy L"#GԄ~.}g5;b܎{s^.=#3!X5K΋!MK;VUV ︾6M;WTj[MtHuPˮ{{+ƒ;ѻ{mԓW=ZuwMr^u[55+.x6y5[`aiE;D qL;P{}&t0Q{_WK_]}R*OnV[Aߊ>ֽV7 sVz+}bw:7輳SOVe`;D@C:BleaJ <<@{8=",ظTwյ=OO]Q{fi(ɷ4K@@;׎>(|@ۡm6&0ͥ`t2W\*bO a'5薚Ay E-EOxܺ"CCna9oAzsUmle z2|eM6{bX(9gNYO֖a6l$e02L#cH>c1XITv* .CvN;j Hҽ 8p@!(?;% 0$gpڼj㪥F:+ VrO} \]A}Ϋ3@J ,-|}l8nrn u [nymR!^h[p#оЯ9`0W qKv:)(PI&' nn|J*UT+>P>YTJ@~R?=V7" /#r=\?<'W3\ F#(S\8ܾrP|(oYv[BrP!vZfd(+M@#ntJ*UTڧ`dd8#j?ǨWJ_pjdx"p5kq׵2}~LeIzkD7MtN~MX ka g2c&- ˖2Ln&g$@ȡr &O h%/%h7+Zzȍm;>Ro%Btע\"A4Ec(R~!v ZWE=QOԃ7: |y\ ijʢ= rxtp./ ƷPsPtg()-1PY[7 3*X`_8bA4HwN<\#bWd_z#yT{dt+@;%>^Ѷ|]\@x; Z)wmJ WG5RF1qVL@rGu1PK&j;I%4_xA+;hd3y[r̟p>1[}Abr|[b':\3A˓_iA+ ^E/qz9Y<-Pd.f6LvA4y˶b(<-S~ (?hU4=EM hZb:ș"Dh%E}>b:;SywqcUzpYܪ (Ab׼/ӭ! 7Ŏg+{޳ҿ|qV@+J_ p}w[@G:InE@ %tjJ,O‚]kzL~;]S[6cq=p-_n7 F;d䋠j}X)@`x~mC$2}1Mɺ$彞 ֖,b&#|OORjʨI9yߦc'}XYYjΎs|Y_6:h}0\LmVI} -)y:o3nav^YE q&!3S g0&)?DmȌNNil>_I#^m;P-,dXq^ܗPmj5urR2Tœ6P\ܦ!y9 UYbgdS<%}ʹ~˝{Cwپ)߃Ϸ3.֐8:o`Z>$8~K'zdG}K_h"zA󚊗!qF Pn\.}A%:vuPͬv߆ȑio&ԅUKk*A`Gm.7j4kT,ۙ^Z]TlBD" ,x6CBpkc`ZCAPܠ~ x4<|addʈgy~y^bh'Si+X~b92gΌ[?ybkףn(̯Znu&OcM |{wy͗?]c$5;@'aUGiMn:"=dmJ׎^мAWA~}}*rxl{5W*]N͂65g]_uoz5:jvps}b}T-ÃBu0G f…\3׀QFA{Y/%CcP{R@9w>ݵ qq96)>`3]⑷0 U>xcpa[J H.ʫ!dqF*"!=uDy6ktp6J+yU${潟92} J ͦ`.!̜51w*Cڶm&HqOoo, f^вߚzk軷2-¶V4aҖ? |xk$8Zε ͏ oB8q™r 6:f_OvJ 6:cA\־0beauO5 nW,| :(X6,Fjr20wV`&M9 LB#lU =t'۟<;zZ oT7W?yօ !fWSxh?Ñw.a.@Ć#R_{B["e lXm wz9[zUj`qt zXhAQT-( ̘RKaCyOކ,a=v;m9@X[om/[@ٱ١`{v`ɢE77_վ0ҜRO;eoAƄ{3`d#$<fyFuyT{=.=wzXܵ0R`8N+@$DT#xq㯡߁~AuS|נê/txQ1qaD~ p㕹WJ!j#GZˎ`w(?A;k r9> c kQǠ5Լ "_76 %IyO5I}`kn\3`+Wwrnb,{"T5>g48;Ymr6BOݏW-jj}NUi%/U ,A\>yV% "?-Y' _ͩ ƦrV9se-Lln}2>prq s :A򢚹 n^ZƏQ2Jmm*C=aq+y(Z“:9Cp3/#Ǖ[=,=۲* gl-Vxϛ6BF9Vt2嶄Y!;!LG=کM>5,Jk@KUz;ͫ;/vQ=99%_L„dlDB`n>>wtxun䚝@,oBsm)͢^[ ~I}(:1ԏ<4 ߌwk$,4k^%`'lpJ.qBEvy XS;Фw SW&>PՆ=*KIFZm>aMEh ds5@;`5x 9W/CQwxvl?nNr[VwgNfi[? ~~ ]:R|֐1>L}= ;(-[| ;BvG Y[%KKZC+9 ꢺMH .f^}H'iKꊺ T6^k&Fm h Z% .-A|j85x;MYq\%be`,v<-# &QrWV3[:~jdE7Ь1Kߩ(~T?CK'`*dWˮ]U6_jtt?Bj_< _%?w;ݛv-`fPofl>Z^a . . 4Epnyӹ`/2ʀ*^_ u&χ].Cw<T8SW%m?@ vS] ]Vq}2$I:\fw.L4M x]3X UCA@{M }U:32tx'h׵Ϩ@>eͲ8d}b$'B'@ldv@6mD}ZTԇ9fEpKVe,<͹m2t6 Jg"ZP}21Sݽ]|v5j {S18Ko !vw H-J*2nS\ݛn^'pBs{W?KG/L>z\n]ud,w2NYO!WldɽhQz}pg6j6q; 3 </X׊FM|YmIcZi`LW5}+1._/fP/í(~Buthq/npm0"2V#uuK}W+4;RvǩFf.T*B^<-s7TZh5P %T[ j>x ;Rt>dMzc!;b pChjZ;[u4Q!pv`hvFm}]xxXߟ1k/tCAڴհSM+(Nεsyބ'>$/\_!n{[I!ʿ] -_h Pyefcvl w>udn8uPʲj:. `Ucx%gҧ'Q4jWL(Tͯz> 8yjpǹ oW`7?>dq{mYU}]OΧBUV}#RfNo_KT ( 8g/p[o@t oyl~28_qjv[(Z05s6-lVw6@5q6-}ٗ O?VrRн`>l0_Fsj ]eh8sl`e1yMޑ7`f~P\x5asS(\06m` kڡCY5r z:*O'`]ZBGM>_[|NZ{8I'nl m.fl/[3=ZW^Uw?_tKO;W#(0u5u1u۷mB#ƫT{{9!T1/.z >*VoѸ#r98nNb?*!QìA WAu>\*- RiˢPZ<|;^e` ~E& :g9|.m/85FBasg(UaȰ- zZךج4[-X*@#KH{ F:+K/amVoABH&۪}{0 ^xy6֦.pkdaQEl9!dsGhi￷y`ȱ*R ]{*ہ4 pb|YU2"z+Ni ?[@938ΐf=J+Ag#Po)z}5_Z**2?,rZ:'-M)McHhW^R8"#3mByu Kл \ݨ ޳?,-ޱlUQm6,]n﹏+G+ |tcIPwv]:PRBHNLHt~a-؍j=ٽK;mNhpZ mnj xn WϹpW016\԰T}K㦏UU9LN=@:΂jrS=S]S]`1s*CeL`7wl1.ܻ`q(hnm_m P&qxPgw>?H4DJ+Bp+w+r]v}x 賧w!x~(SwppMw]]G1c{Xd@c^<ssOgjWi8ù 8kD)j]U^Xqkx㝣>}6Sw>9\e|p_}/r_68::4dBG \"[G eRWzzջ~xyԯ߯\T{Z2xelY ?w\ nqLܠW^WL:u.:\42r]tn>vfOӳ ly|flپyRbH,;6"]uyO! ҋ٥Gm@=e$a:I}DUUb1="MJ5?D Uh렊|[y Ҁ.^50 [b߁hvS@}CG O B<NWV#^-n;i\`GhXۢTi%8v0F!15]`w*&WA5P+8NPj #@ cZJxXw6IDATTu .߉`D4I_x8tv.5{iBUdƀY$ 㺨,5gã/e1bTgk]UX,ր4QR)\ڋOцa PH#PkiAv-E_ w"ր!J40wUj+`DT%\P(e2ĨYC֒AΪ3Y(eFQr*7l%[f`cn`4'gc/t&@7u07O4OY&+d :bxKyI^ьe,kA-QKǀT?|?=lj>RY$ &RN8NfWi2+h0x#:\ h,hvJВs-s=s#"KdvKk1.W R XCoFӀPi*Q%ojb=}wMy 1H 3fll&Q2j";`$IF8##x՘ja+-{ꮺ8*B.ZOW 饤IkC7AMe 0|APQzAi&)&W/,,X_o{";7BWgR(mxRBW&Pެݰu6T~'1R4!orP^I 0" $p A.HPU/lg#-jBqEy(^֦x/8ZXU3R4:ϯ|pB!gVS81r>v,qSSyJ*U &,,scZajZ}[ߴl)ͳ kC/Y/N/:u )11ŵ +7\ Ňe'qwICڪn#p%Ѫp`@*OAE/PUx#Umt՗KQ' \ іzEsU' *W?aLJ*U3P_zxeYeݺꞺZ]V܋ APjG vU~`E5#iD=vJ8*H-UC@;HRJ*<ݘ³*\24+W%ueiTRJ*URVۅ|9OkrQӚTPA9nKlE&C~qk~;Owa.AU {UTD _ $s9c@8 g[#o'qzlO>>[i.iktm2{a+lA<mЯsj@ ;a"[d {]证Fl5VFe abz Н*N~6SMO qB緀 AOП [)H" n z2Tʋ^nTd@I۩mQG-m{"J@FpFiD= r-G" _#*ZŨ'k_}?`3A]S-.EL.Crn == W'Un-tCx$ɖ w&J9d$AޒdWK8<-,x~,%z@//ߔ3A#7U.BE;AȘ ܄p'$@6e}[w+M"ND+oP"ZDk-"E$'A\Z]X cd l)@Œ/  @Nȗ$ZOOq{|hKA ryB 2nBSRϤO_J*=Q2Vd$UOYZ$O{@;юI_pm61075 rE;L`+O@B| *!ů5j5!QGk wUgAT} FZV@w{V{Y"Qq[gεC烰6p^PC bbkCNam>ytT?5N<߼ p=A}>Q .yNgz=*B@ѵ' >?J&^Aَme{j7oݘN <:)" ezBAD< y T{tV!W3@zH7og v~Z.aq΄H85Զ3ް[ǀy}bUE;qqD}>Ex(:"E<Э4]3$6o`֡ñ? 3ag|&mZzῄ?x\"r ndܼOn5+b*)v & h}^ZgxљpgȹS2HVX| 7W<v.6>SOMwmA'Ʌ@rsBw xZ+hj H)(~1/ⵄ pLwMq ^.̀k 6碻E%j!M"{a(?\q+i[2ROR^L" 딷(I#'A;e8Ns7^xUGH ,tϜI7dBH2.!  !Swuۙw- e//a!܄#Ww7<7\ <-ҡdgɡTȮ=6{%dffLMŐ6 u_"_t3C⧉7;Aڲe|-AކE#~^B}Sthkϴw:. 9]YqV¦ .fåKCc HDxDx,q(R&~$:wB1.M-_yk!p5k<#O {6DϏ2>x\x8xX w/!탴i'aϓ=sVWY~̺ {䞉{CԒ}ao= i{Ӣߝww@l-<}Aޔe'c # 5kipz@EXÊ8ȿ_^.ܚw{WȈQ]@:d\&?KIa‰5|a)p8ܡ!H9-]l x{7j6s aS© (QVTcq(CB`ѦMujƪ6dfiYF_A d"EmוWO/o^RM: u4[Ac`ҋ Ke7xwH*_W6^v'N!nQGJ|`(ŦH[5ZsX/,,?b!n\y=!vFu` 37Aȼw"])7 |Bx㟠n:xB̑1!oWފPxUw`:o/X@s:\|WνW 1j.'kB|pxz֐9i7 T+HLK{Wimfn̟-.k1Gչ׶\U8} 2gp T:̘K'2^FC]BK9vژ`-_rdɞ=l4 ? ?j0 H;9,yK7(zeQ10fvOBK),n?ug(>"`N-8|;(_b7kp[>3D'` Mx֯:>,uo^8|[hKmǁ6W- i{] ||mc6N#5n'a'=Fυ/7K[nEuw*|lܒB_>́xv.|z(q/Pz M6~xu(ޫ :8>Śpb1Mz?,ޑ>}Ԧ9/^w-?/ = WM#@}'pvȹy@3U <>s\X72 OX[f:jӊ_iR UB06ߛCwh|4!_#aܭ {3dfgd`#sx-^[32viݵ&1_s rgɝ=@GEK M917yKr dDKR6{ރ@-jQ оVjAk5P"_1x d% sJ>y4M7I l/>X84PqbOE4 !Eep<~LߛNh!根jN=DEbª嫍/ _̬9k-O<`b\2{65s.t3pBg3/зAUCՋ`6N[@5ך(YVf;+ƢhZ;bMij_v=\1#Ь]jM\h6iROT& ,[[A^d]T {ʷoS*>O_4X@ŧn&Kp3ѥ[^uz]N.54oԿLhu6+K[L_V@۪!I!!Q-'sǂUup ptqsy@AVA:(T([+. 3s8G ^5/)a))g~`$u#ӥ2iNs5PuE]6m.YI O=I,, pKmԛjzlBX/ q7#~W|(/)7vF]V55D)"e߈|Z]u\T'@c6sdF_@nqȚj:.3A9d:D81i;WFA-W!ygEg_vʽuUƨ6Ѷ c\g[;IXp0@H^r=e&މ~'}c>Tl1C|fm53}$@ 1ֽVK*ePz 7.|?,xa86P^UAـ/FBY)H#o ءqU~b!pDrt N?️s[ni"8ι/@`@ooe4SH^Wo/v?M#9 1_Dg vCm6s ڃkaצ;@c^w-dcENj_g$ b($/z_E$BEdElE2|4=OyA9[жicR7ۥKS0GFU;V^ è3N':yhZQg? 3;\8Wrd^}Zo@Л@:NEbАAvD-Q3D!ŦT-0xO' `oZ glZ)TO?6..5/{E:pXPp\r}ϵ&\/+J.]AAj}`j-"nO~x36LL֦qsGmp-YOk~BC`?޾#ȖlYOK&鸓=08ljwKk[ Vm[W/> ކ6}%pTrs %Kn0Amdbu7 Gb x-5J}%JΖ$CPZ`@6[gX_&D^U%R@H Ųrwuy^:jelX1VH#a`-Z֞: Xco  * KvB{ڶQнW+?[m#[SG>ΨsT0uK^ʠ.3:fi^m?U3pp_|&_> hkؿM-'M@^;^ݼ8J- YdFu]V/|&E=ӇBK|a1#A@ffpz88&hVִ;e= QQQ!`9re+:5۸mu+~eZVoI z tiGe NDIhs cɵ_5i>U|ё[pWɢ;ҵ[F64WW? q泛s2,2[9/}F@0!`+X6[Yò$T^ 3?㬟 IPkbᠤҔ53{Ӏ=rħG}(Ţ岺IgK1_]8P1u xPZԄ׈W#k9˰6,Z%5ۃdloJ:*BTb0ʌR 5:+YJHaeX6 ?@ QƧgj0댯AⲸ(BdL5|\Piu*Xr|Ii`ko~mK<ˆko%A(v`\6xh )& 6<&X+֊56Mmsu.v,2T aA4E98tyyy)(夜Y3ElA;1dQ`EF)wB9B 3Id0:ZmbsES)t; dcP6WW6l hkS@G"ku?{߾"= '_ϲ/}@~.%45UM c䳬'֏YԃKxr>*8)黿b07hT~NvQr\lc7xb ʓ'`2A6Me0:F=dw #)00& PUq]\W@66p[5NW iRߤ)pGAKS0s/(ٮfY\qc7(4ۃrO ᠾP"~^u{Umɹ))7+x7l].o^ A0c,`6Qr{`Qbʴv\m@e <dOKC,@ɤ%_Z_^ǃzԣJ*UJ# d`Ȓ)`5F` -5[ߴh)ZB+vAچiwTh]kJES+F=(S\8ܾrP|(oYvVfiOQ*I%/8w| JUk-m=]U[/ꖺ VYpPyRJ*Sw"Zb#WX j>_T!*6z y?5@]S7_~2y(_T߼n c/,WjnLJ*U_ +@-VGϻ1tUx, *UTRJUwefx(2xZ$RJx_'2 \RRJ*U7쎩,2t ?ʝ@&<Ͻ1餓4ˑ U _T7,^Q(qǸݬ 1SQRIMq-{n\J p`膅э l}ޭRFI(wduY]O ,@>ЯY:jmV lqQ\gAPWDm6pU=Hz=^֧Y(eOo4bY+׊A{nh+ڊ@9TkRXa ZקSI ~? 2y@{AJ_'L2 zu ɭr3eYX*ȏ@OX{_{D=QO׬Ħ>YӳemM|l*pTMUKF3 dLyVX3+\peІa'qE9JӪ/e~ ?2˝\C\v҆kI3׀X(eŇ@6iL~FP.E. H" i" (egXFp KSw[q ?>XY W8(-byPUDH,(UwJY!ķjNo7CsӪh[MA zS^D ˭{}E_ч_5{VaDñ,g(0PBo+a4;%@I%L: ~YMg !BC]ކ= w⼋?_ U#'EuNQwwDZ}ASU?r J:7?"E@xI*W\;{`4@=Dw ** G9+r \zͫGJH'qyA;B!뛬MYAJ)3e¥ބ$$Jޝ|.X2>hOOOt/?mbNf Z֢qQ\=H{vX; -1Ẃ}O$>h'{\q- l-zt,Τ=H i fWm%Q_'Ɖq ։ub-hZm'=\0gO^NZKh'+kˮԻ툶_ t'AvV;r&MiFSd]Y *)ץtܴfb嫀+"O7-]-[ {gj91i1~@\oF5g){-b~ܱ̉yŦ'2n+PtLI``/n.t16sdR T4QwgU_V]\)WRK@ V$@P &gg},߶ZMm6 ={A|)0?QJcM` P'8LO^J~C Cŗ 67q-H*g|5&ANá!on]v; 慘cCv9@ eҵw %'%'h6ZO5Y#r>uuq~|cwCn 2[f~}ʶhz0F}tTw:ku4U/] 쾒sfYa !kB>d:vsPЪ p.iN`VMfEpyЕ-W|a 7&.zP{VX|$>ЀO:5N5P5€yA#-8$n軰}q;dn!>,>(K谱Ü53?/[[Xׂ>(8 ~cP}ؓ۞`P pmUk`䤑F=WbN6ez؍}MQمfg :g[;A'aiA- < ev^9.rYTXL_r~O20E,ס~Zt4)$y8nW!miw[`w} ̛եPSA}>d1N\v0H f/0/o":uuA~8Ǡ};TpaA96x20jUoPG U˖߂~pLYoNw݉bKk}9Լޟ> ʹڅjUt myĩ#}*:q+ :^p3+%4dkz1/퀷m7 Ɲ,:!5OtάU؏3~q o웽?=kc*ϷZco*Ҳe@>*Ul1bcÚkju9ΝP:$`S#ӠyÞ{[mی{)ܛ jI}M5 dp'7Bdnڒ O(_C ys7=%èFq.c1cbGu+j>lYyya t hn9yyy@Umxq:cC!a#ޒWfzڵ%Dό14kV*@l%_Ċޢld#ILJ3z>ueqpq !IVZyC^ћ:4ph@fn̓oz~sx<\r].Qr 9DZDF.neKoô6Smm >cG%+bcߊ}|s4l(T\-1_t~fG~;OvN;iur<zXQn5pt8Up[aª]N;uhk퐶@լ,;^ߙs2ut23_S}i#d;g>G2zgC-YS֔^',8{zhh"ͮFB6[K2R*dʌl=z5 ҜaNk篽s;F-pGCR>ɮdܓO]޲Gֆڿ` @V.iT%N~xC& C ܴ{6qk[/_@ii1P[QmAt7K\7߫UIȆoސNBll-]Vm{#ըnqݵ 6N]"A. P5ѳy~ obXފ=DVDMjP$D `a-u恸y{WA@<QBx@і6PWT90gs&Ku^ 0~ #:o?:r. Gi/ 4ȀW_ycҗsZZ5ޫѬFۙpw{O5O!Hޙ'p ¥ >t=,9yiCk078 zvCS`9O*gq8fy\@8%pG]h+h\E506u:mSBܺ<N;qv (CPPmÐ12cfZ)Miti3k }1c<@}h 6( S,Vo)M/4n~,D0rLPN@N)dť@;ޥ{)p湪ee8Lc ?M{Z/4i#v;@ypy3,0~ݸ^l}ӾpVhj Rٗ ux꺴;`lS! teo(8h|}' L7.Y6`R6=0>Q i@vc@Mq?EˠwKgtHŁ *P[ 6AupwTT_;/Rs-- ={@?TxW.4cL5 h;Ǯ?2 26ee_{jS7P;uxd}@rSUTB2)`9%~1\Mv&,X "]tP }GMH[v0m7r;=n05w1*|4B\ &&,I8 [nQ[µF:_jVU(Uvwt Mj6~h x/76 ^={xn"C'iniO{0^1^US{@/xmx_g5r|4v!srˋ~k_m /Ù~~}AXӯ9qFLh;WwRWO OO;}5z*6^\Ϻnw]\_pw,Կ-.Dߞ'VW/NS[͆o#b =i7fK!4#pvۧl6̆l ԛv{>m@j+W]} @5"MK Z'kcc7l'ǞJy,wZn]=[v/rYI7)lԲb+v]ͽ}kr/n \q nP޲sy %h^')?_T.iZ%Oz )'bAZo@^TYM>U $ lo_JpL* U^!yEV6XRj kf>f W@Y4DqE^E 66i:iZ jlc9rK@}bf]}"dܩF% yM`fj4;P':euLӨ}0r(55B ,Uy-}*U{% p_6 H&ZMWeesh93F5:s^?B6?g:vx:d ܇F>{6 #Ba n!*dn`doaۍH5Q{j}}{VZMiu  {MV=pj?z}Tu@ͤ\.]| W]u5 W"ub-;Lk[}R_ v|&hv[PU' uLV-c**c0A BP+G<" :wZکDeΖB;V*USY47SϮVȬnH.HN={!x7-nչaa5†B7nkzouo peU+ݡUl-oߟ|c STy-d%?jީy&Ϙ7=v=Cǩ SͷAMjvN!/Y,eiZ :ݖk5wGGwU]kc@ X bE *(m)ԡF)R(E { bePyγ>{]/ʕk{9Zs~QT(7h 6XTWu.ƔFGD}UVuz M 7ϫ0K7VKZh ^s&C9~*TO.88usnvxL} ٺ 뺆Oc. y7_|_o35uq0jҼ#eom^:>X\v9U@=Q՝}[KUT0$imA^z_(zFŲ 8o "m& Lt0abŻ/XttBGQJ `XA={T&'CEt G=M̤ت*(&hcA6YPEJeL_6걊r]JV@E5++gP"[d "Od; +IO 궺BXJI2QƂLۀ b?t E SeLqB@bR<1o 2\;~RkZOˠ檹=^{O ?Fѡb׿>@}>P d̐ɠ1hkp^eK+llqAg@~- AV!z5>zC.B/@elj.wOi8'Ή3 C`PO&m}7pk\yz-e/Iq\>'t. Z"^zo/37Anf^Yu^Qw]A\yor* j =B%0v_5QQMzJ jګVߠ DBж۞9W//Wr_[ zwP[fjڃh+[.+*(2+yTd !OAnz7v NםA^EY"TJW bcr."H})-S:pdTͨ ;xҠS`h t(WWtM;V2)X *U겺~a' oMnѳ406d7lY_ D{D%sUsm 1@5z6@#?']}\MޫS T~[YYYYYY+5'J- p|i3e5EG7-z^`aoq 7m 0RR>IƱOý[!ncsy uIË"euT Pػn3/W5֗蠭85^M{{ G| ꫂ^ttGqu !ເIcT&l5W}cOm=O|rv?fZǀꚺ8}u N&jF5:+?+* RA{]:sش T&X@E p'&!#XB,E@|4B45ᵲz0Im!*;bČPU%jjj:TZ$@'[GJIeZ+< o Jya "r\,r%ƗU)_ve;.Y:R1u,F<mkw3 mvy2wٿ׫] uw kinsZH5kt1VQĒd/@__tpWxuo?] % @RUl`#mm?S m - @SϖME$XR*iXq_C-C /hZuSxSb--R{Bˁ˄ ޢ >mo ZVwnv؁"~INk=_/B0604 2@:K'V {)G`ci Z+D- ׵¾%.CRtRiRs_% ŧ5*\g$X\2Ap G(AIY5eݟlY.eCݹw@oQx>pq_%Du~hh"Z'Z E/1Q8Jɿ+*BH1 Dx$/1@G8,=jɟ3at1X  8 OsY7A`k>yX(u`9hg9h/q f *= LXJx-(۲u ˽r7?J7AH !{Aς Dy-!!CAƾAɪ2>Xyq< =L}x\,߃]ۢm^k h.-v׎hA^W|X ~͆uk;Q^OԛQ@?o?^?_|.>'@eX#\܆Ad Z!ն-ܞq>/|g#h Aۮm66YY_֓u hS3xC ꉺ|w 0kc&)6DeM93@'!_]c5ld5,Lښ!Я/Q/ RSڕ^hY(vsfZK V8wѾe1TSn_v%otZ1*f4 &֦;fw?!Օr+47-VFnF;GmAtIr5E_VVVL|/@kwY=H2}cz2+~xx|>%P)*I%@~fmt x e@R~C7WCkym6_g]Rહ]!!!sC{<>ϥVDŽ_$F><ʉ`w֞{fǴjI0:tԼQy`w]c0v32΄+*:_,/~Kp>|nd!l{vm ]?ҩNX%n7ٝX`'ɉ#?v%偅oSbBwn?tiHC ~J9HB7쟻?l>1،KAW:5^۰lE`KmeX e'K H\>aD(XQXo/^ {m ͖6ٗxx1N㿍6o,}jc!`,5+A|RŃW.^UAC} j~^ՉNj5/_qva,Zٱ9W9F՚f6jBJڔu4*o^0,5|oX99s[{89;Bȵ Rv$NɃ߁Q5|7W1M?xu\?K.78/&;> j)X5{L>o~ْd &D  @!Yz>~L=؉(Q Rt'`&3nh1ELS@. Bg{v=Dݍ5 :UfQsWOOK!GgCNM;6ݟޠLb/ 2rԕ@*<@lIppKq 摈# >Np<r r %?h >g ptR,8^횞D@[_X8}o Q@7nxdܩ&h`10#%ȷ=ZwII/SΦhF<=Lݼ}t w4 QΘ{Om>h2,6,++=MLuh ,=2`F=utñ(HqM9eĮ{!~Z'$8}41ޓ''%6g_>| OOO9^r =0B*b PU; b_[qɂ6WV\;<>ꞫTTmeWPTj 8(6VxPZ1ILx+W5lXBC.Uvj[ o'uzO_|1 %/ L<2q;Xs}΍X ⿉wzK֣GgC[-c:@py!YӤ\fj#XSNMzKi:9.^8/|m}iM܂=':i%FPhF$> >:/.6N幕sBԹ{Y!Zhxh;Xm_5n`x0|1p9 :{B~ *5 qm3+,~t+λиIh͏>C)a┋M.EwXx&^ze)O <r?߻aNAogyoڠHH*;+T;Td[9$ ?AUej pQV~lvv;zSVUa >htmM{?9,Jd>t;e@I q]cp5A. wf\Y tm ^YPSj.zU ؕDB9j0mCVPfO': p Ę)Y:>AAVg2q]̓@\8&D̟(yDa٨NRsms=sSh٧Tm꺩:1t=un3굱5!HaAf-~2-RNݝrj5'] ",~g!pkʭ?>yq87O!akD iAn[=h1`Zk`6=lٴcvB@*.Rn1[42K Zo#.=C=csݚ55rivںak+8hї|Oيkpź_ ΀%Òa΂6cF4!H)fAKCA"uinp`bh> 3I={$Mb$D$ʏiq?/c-[ yuPƺƦ.`;f8M}d{d` 229Qv]]8d۔e5`vpq2l h\tK࿟&Ƃ(׶h 4/LDg}SLYiq>БtY$E6u짐?WJb;`FUsM68AGtK*JlԷڎe7wCCj%åW.B{pjr%ds9' }k뮅]z$LND{'0W&~"Nq콒uMl5"(LR/)߈`|4Ռo=J_S*gUjּ[& 9c#D8z]s7 z^GY<@]sս%" ( Աsl M|UϨ~ T&M&{l"9.5nw<jF!n}4lMf4%TEA劺qjNwk`m@}ܣn,zEȼml>j"![ ġb1Qâ^ f\sW2_j *w[֞T1)@r3/갲K8uP 7)KA0 DS/ܧx|##gB)- Jyt+>4%O\wN0hu⛓gjP;v!4~@"ʾ}z'ի(?Y~쾱mjD !nӡJ*-3b"1|fxD.89\:4u GVϯ?Q @C.>ϻ_cr"U@I%o^) c TsSL=F34ӗtDQ/zVNҽ{>ޡm,ȼgN>}thظs?0 sh&\wK7|@PϪ׋^CՆj׫v Z$?n.F躸Mp3帅e?Θ@qÒ~SmpqVs)bĖľw 76\`0Եec=^}P덭?j?jYC 5G}Gtkx׷Nm60:ءS2X'Z ~2^IA셧c+A.= ?N nx0=H][@#՗U]7u=͌ݡ E'[eS'%ۍzy%P?6_FM4Գ )oH.]Ӷ#=whȵQmlޣB=͈鷖BlGIXw%Q/cT:^ikom:LEǦRhA0El}k_G(r`*tqwꉺo3^ {P%D#eP~Kk0%r\ بyj>`ÀQ5{N ;:hMTe D$@'kLzP.6 )b֩] 6Uc\~KP}@,6Nz@$*Ai|8'``P (W%@K,BY?J)q Й)45MMU7 *A%T\CM%VV WCOzkT,@% lqJGAS 3d|/}AHOAޔe:%b*O嫂?;rJ&@f9Tvxуo(@4 @D"]leTfe}>F)yBJVm@z0S4Ї EQ D.sPUeuY]'ʠl-8j"BD ˳4_zS lcA;Pkj-3ьdU'\@ĉX HbP GS?b).tP/aj.r\+W'eg@*?Up?oe9 <#$PT}x jzOj^/sE&s}^]:^wQ} q]\ed$_ >uu[25upsO!ij/`@S_mlz%.(iB._s̀H\uX0]3ǁg`1k9RJmpllزa_kR4ߴ8'Q@EK`x&BEտ8!PDq(XZ[^Z\KA5SU߈xY hS)*%t7m'پ? \X>N_@DP; O %9K_#Iu T@@a鍦{3?qPـY^tVVVVVVVJꤺ L0@EԧVvl، ΁5\^UV&RsqYǀ_\#^thVVVVVVV/ .:./:NjGJWυo_5bFr8, oyu 9X Nb@QE)}A ' C1cS: @4 ECpn/:*"H4A|p-_0k?DtD0dSn0}lYa~Dk/(%cAq+cӲŐtkk Ib) H)ĀpvdsU䂰À <L\(e3vw4oF˞2 p@ dW[z 2]z#yi%` L07!7+E[ R @Dw(H__G-6$mEu`PtYY(=o0G+Η8/"Ǫk?D|).'@5^VdϫpK\I'k/v]] D ܅pd1YLm6K{cqL DGQtm6HZ3DDziyJmėKrgy _C`ЮkW '"&l"E$vS]Sz/ɺ%k2I&D?鷸- ,XBR/U}Q;222 R秮J j3Gi<&1D'ք_Dڋe"}a3A琐p{w_(įċޠJX)R@ Ib8%W )|(,r7E{2`‚g8\yCɩ  D%)ڋ  'Dv-1)ڒ񠅉Eh+ gN/K?e犌 q̧ 91Ub&>+f6e\D%6\1S."E/'Rk01ˊVbÓkY$w@At4S2N9dsEVf'<~C(?i`$7pSЎѠ}n,$/ߛ [V1 'Ekʇpsy^HrȾ* y /Z۪~L-{?CҮE@DDl2K:/@ƗNAqѾt(5OhTyYʉ t94Z Q&W DQDF\&Ɓ r8" 9( yAGU ob9 ?(Yz#q ky!bk0mg9"ȅ31hZd$h-ZVůmӶh?CdAdC^\^n;ȟ:! T; Éov^턭W ZDvDm(YNї b+qa[r ̠^oCN)Sx[e>e5z@d~d݇!}yU*1ALl1'bbcZCDOS,_“O=c:>\ h*b)RVv-]ZY^"D#lѹ 3$V*. C]+~vդ99o~<[ʳ P 5_. OHgAeˎ ,ERdw]vm\E+fqF෻Cve>ksA6 e=DSW@bYLZ ,~2X6M@D 'DGE43Ѓ4s%-0X~!ODSpv K _xjMxY!b_$M-7bXvC 쨔㐻ias߈=(,(w+ 9#RAA2A?F) cr@ݼiC|y!4y$-?qRzOi=$%-ToBƉe@料$De:w] nKg@Q-a˭г'!yiT̠fiW(*R OrBqc SJ(?g1',Pcy.`f9k>9% ³ǀms;Dh·5z!u蓪$B8pssev]eq$v[ x 4~Xn& Жw,罕 %[!gt邂@Xms#t "Ϧ? @19{S#蟪MzHȻ>deLqSOށ3AOg}!ӯ887.H(nm * κ oS;q KҫiG-Y,@ -̕Yiᐼs*0 _UÕU%}?*;]dcy)]L{ 2)vLu(8Xַ:XF?`,PK)8ZQ FۄiELS^MK| >CI@V[5 )[!FZp,&ql(Sv!0&B[ zV1PvBY$d̉e+Ň#]67?q6Jr@^J3DS!c`ƌ oo[ZU^SC9;Cv s,=Əq@i-nwO˟;qΖ|N?isė$8' -P |nא!g? $YX.%pmsp=K'/O^G9O/߅IqIagÝ_ԀSkOY/ =r΄w=~ lrCf(,.,/kxm;}9`i%uRx%.7w΅\v3@^&j Ǔo%gC\BK|k0777,Nq.gN;>J.ăX&BV`VǬѹGɏzBΤs~iuH"< ~2>(Xr$!qHX"@Ƀ%&( /-dU'~;?0m;={MJ淀 Ab_/_rrs޽v.O<֦}4C@-[h5a8gr*A{G#}[_UIdSC#NpE`n8es Z֭VEy 3e?ý)m3xnWi+(ij =&֍j⢏lY񽴁o<|_WJ~p s%$lϻ6jxj~pcVK q>{3Y}pZPpe[`)6E]a{Ԛ.m=-".@÷$ּqAblPc,CkZC[$5o[ֳ(x2n"hbd|V >n5_?_s1 TtVy.C}Xv-p5,nSGAR=jhYN<`rfMx╭"kuׂ@woBy/wRǹnMלg%Huf @|aD yiPzfP@%j X(kM>)(}ݴ<^$y<ٕA_vUp{ʳ: rs{n{ mHPRf/435, ZU"P߼;?#{ʮ7"n^YXlǧH3SXOm}`ii 5'8-c[nQ؛(p^@I~双Y+ͪZ+?:uh8pQGFC 23e>HsBx9uҗz_'_'Ч7^N XBB)pɹ7zLXo퍵Bv~?5qyC?)6[2 #!#&>;2L({|-jxnTrtkW)<:pФ(IP/F1c(h6Z \\U ,b! ٽ`P[nWnnrg*8]n^?? 6\`t>KiŦ_RJwy=04`m6A%ɓ1PXhta3cOa{}<l~jߤ_~#v`wݿ^6e z?Ox,"AK$8]p`*ttջ8tnC 累I<G'Uޞlف"Y( o~߇3KBm8d7tiӼ$nϦ=w 0cǸw ɩRS:Z` _,P:3촙mS8r{?em>khϫ`АޅS_q ؿkh}_Jϖ8E`;quX]R",֑X-l@kdG3?/?!HWQU SCNφm2zdvHPCZWyF[6쯂Fx20;2) s3ۗπ\"dpd{a،Na\0Hmڹ˹v{,g =Fӎ6Tgj翟W -Zp#@/'͈:d\]#t_e,=z,xqiq nUhGs/`,(HV.KbxUu.AVN[4.dJ?jAٽxV~CaKsZU@Z\e̋ @ȢQ@a%KFn[5UR f/ { f-WN0r_=vA/<* bt\/4PbZi\frH[zw:-15d)%omqCw-l3ضB2QSjtd iN1xtn TR,'@\eX"q`+g{ QUUI<.~g%z5Qdjl2$g ?A*_1 xdH*c@GBGM߃o]5hOsu/tm.?S 9g;vH005M8Hpj3Ph]q 8byK[2R|y]^!g]"a uCK@*NJQtӨpֱԵ0Zz=5P/rQP=Yȧ`o2 MޭE J£ƸV~AW]sg/h}ḱ'g;&ՃMٗ 4z eVà޽OWB[x}- U>qsʖVvli 2ȫ=\޵Z00UENƾдql\j`a/ٿz^8M}#e2COz2e]c )os pkppHYW_k%s#zZmWc=Od <6vݡbyD%%hjH 5{U;w]!죔yJ`sQ6!vg p[N&8k>& W=|i@UuBz1&f-JCxyyʾ. ū^_ʂ @o'p.Krˍ?Bwk 9%φSrWje`&4<];y8;]vHl&B}Nx5$>J&c ՊkSbh"zD γ:Pm 8t ;nu p-ˇZzROIuȑ<O&Y sGfyfs87hjYˮ ^$f[II5ZBwvku=z/Eֿ?u@RGRH#^_@ƒ̓;[n _Ġaן!_{O!m_rsgoRq'[E̐j X]'4wovi0Tʩtҏpd. &9!,ZX=Hq9Wx%uE]J* =nt{9T TR+ꪺ\皸 UJc,a!CB{ڃHgp3+ļde/p߭n2oa!`FLFܮ{ͭY`N6oKgp{ƭA֖84īWu8tppIgKk[놋Up/9=ps?ލ |j(_yIuS٭7UW+p$ceRkn5~ :RdqǪ`m0xG߫qbX}iФUMj>rNw;+W,aUvV2["Kr| jX7;| &~:>g w<@';͠Zjͻ7bN[)6G[!\[K__KIP@-[u~7!⦹6j\_i*PoNkK U~Y'&zC TmnگzV7ȼP% :/ۓx4uN65CDo@wUy 3FbȀ73jCB5}Iyv_B7IBAϲMCF/CUHvsNoԴmv*9“C4jcz6Pt֫ea'O;B5l ˒PDZRM%&ɈBn[+Bq14; _="}jׯ%N|]A|븃}= dx/shsݿ U:j?{Cm Oܱdxص*\VkÉg/h<'xX>! 8ؾpjV5or!xx}VcnP`heCাp+YȮ*7wzZN`Ls2,Օ>~]]^;bzA+W?^;]/bH]UMuQy[3``c+4^q sOCf3o?w?|ݳ3Z/k: 2fKU[7 :U [?<-jk84x u(/V@wtO!fй ?sЭCylkMsKwpITH_ȃ2@3Nw8J AD]n oVS`vhs}ں=Vkz505L} NFNۡuQ~RkU"aj8-toqIptDZ ǾC% :7\ho&߁;..O '=wNQ%hlys歯ӓN:5k.t|cqFͶ*(?hu'?;9jap>>Z, PB %}GO˃>p|!'Bȉ W^NBSM5q&mj)| ݢ[ZR,_AM5=SKЦW{w2դsPGFnaaa-"@u?4Ft OM2k5kEhլV|=i`{RyPXuvPà鎦M!yiʂ[pnkf״j}[Gy? *ϏAN v.F0(`A?џŴ`.)LIP{ M% b[:BWq@]CcPO}uEWEDD pGQl#P{ ` "bWFuQ) э @uܨRITg'pfh@ej'PnS՝rY|3x T.3UC[g#㳐{hqΊVM|LmYbĀh@j%VaAdzfi*U#^ly |#x.j*&-J=hK@Wj JcAmWinQUv)  /BEQ0P=XǛ yO%@Q" F qW-~}RAf(~P P E VSEszXtPZxCA sx@<|%>a fyU|j2{" t^L)F`i]prchh}XIM q D _qCmhA%3]WJ#hCuH O9ցLk1c&*_PUzSL18 8%N9ur\ Cy*/`{b%U2 b6w+\_߁^..sA*h1JUWU@{"l2*2Uspxуonv^@,ŷT1' ߐTP 5rԯj AUTOS$&1ԧ3!uH(y@5RA 1j 8qW ՗ *I%>izZV;⦸!XOTUU *@]Tīb4#?j:*O>`@ oQWQYef⣋6@Y32Wθ v{4559df k`b'>UDeg8*à*DwUtV EH@/@PctM e%C{_{t:5OoT0PknKP߭eְ&V K_B7ƱeAWN%ڪURT \Ւb)Īyii8}[Pv #zOBj([\8{p귬T$Xa|[ z[cCq@8xզrduK_ "?2o%D $x6^2yGk]`_5l.AID{YOS7UCO-@kuJ*yW}jꬶX]+++3\SOe`㮍v;D̟Z_/UT4! @odig1L K#P-> d7hOg*/5?V&5Ec**VK?RԠ5 .r`HkKii> ?X蟁i1z u %/MXd74},S--=hKy_7.sTtt&v-FrTtO`b r8o^Z{ODWeq dsLRH( D (uUEKf*C !tғ@s:ǫIEkrrքQ)׳TV=@ĄD5~Igfut1]ߪ. E_dVSzHwi> Έ3W >[V- mw?6;ObhZ_ɯB.EP1b1T C@6ӟϔ,]/"XLi2=*sϫ"șkM&ZC3L6'"+61 <@\yi ]tcD(M^&\)We |2_l}92s(\3yǴkbbtńl*Fy*) HPp 2J\'wȚ6D (KPN2eض΁@Qe[(@IbpNQ-b+3%vJr|({@^o3W,bhZ"/8)&&bY(s+3Ym3ؼс@|]Nt1ML0Q `e(Oc@ cœ)1FANSD TɆ esd| @Ň1\6A C]y G#1D !kӴ)xl9)ȓyy-@~ ߗ| e(ȪO>VRJ)aFQyyy!SePrjC7 q?DA 9qu>*!*2ee8+ փ1b̉9 iӮ O̍M} tBLb0>3K@3pDFJo=$.gϪ%L%g @Q(Rkj2B33LdYO6'㏦M ߓs={11D~;g(1ؖx\V_2Ò%R7!oZާ +* .jLH:3{PQuGןqbȳ<} "@wLUy 7kH#tSe{ '= _ +C2cgf$}4o?:iD+#2_+|ALX<,y8r[}r\6-l_o<9> ; ݆_@eqI\dN:Б4$քW {)SN1} ly2cEbNSV}&4K ДQx=siE'chV2t6J{ fqg=Tb8`@)Q"{X(܄:ߕѱ FLYT' /z HAT5E@t(U 5c8`P5TAހNOz0? ;A.[P5`+򊑳v &Ib"WBFV9 ߎώo AִvABQ ܸx/Bl$lL |qqT+ z^Oo]x )xM}m^P_P -'|9rA,{n 02(*+l;H9p wpΡB061M]l[4ivvŹT|i-[OB9Wdɖ‘G\‘GGj}&.VC.' 1!Q%vi#5|OblEcw]vv̀qDYlA#pH1TN ʷ\w0Poxapo4]HG¡Z>߁o8yq82.官]!7,a .~ypiƥmжmǟ>U :iii<[г|H=zӍs E%jZ#u$8iqXOX΃977ͱKƹ$kKPra߭|&ϫ0`J܂222̋_ګ?-a(&(lufH0aqR^,$X[:=a~bDĚBJ1?@q○ZAҬYkNEt/* ebx D[炰 PBUep*P"*8[Gz=&S~وTh#<2<dPPzppF@<&OHzP?b'gMΧr@4dt hcje~+}:{+(kd<Rx;-®aw?Aߠ*CQ5lK :9f~{֧Zzr嬆Ug"ȍxK~Dh/ `ģ ##A_/ї%tx1;0I`ݐuGAv!߃`efo8t=lyԖ6&eSJ aiԽ{ >_X1?7Y!lf0AeOg)ynzOgsE`XBu|˿. !CƄ M6޴.c  ""G@zݙv3ۙ`˹1KpURķ޿a6Do yM7;v޹=[m'wKp7:,=kv6N9Z4h_8OF._Zp) yzCVU=_~>}?oaF.ĉF<=z& o%X%KW~m-Klxy`b^g9"#ƒG\??>TSC=*ܲ^hõBpFgW^ ?gI7qO7~I["LoǬ~ts_C߅2g<[LoOWis0>6| !zU* DZHLH+ F$;[SnG _K8dbN><`sue (.AQ.XN/zwl6.\j6GzUTz냸7ąXaAy}d:L^ |ﴸ3Ay*/A@oK-xC9諨 DŽwuö(|w hUS^@g^ɳ}2ԿYkθU6@r;|O3׼ڃPz-EŒOg<^ q>;UUjw}7tAh2IGb3 1)1+%HV+s|xlokaf9MfiC =d%Fm58}4rJ+;@+5y6o ԙR{p| xȱ9\):j$8k8s+_>P XJ G 7A± _3j \Lu.S\:-FФe7Bʲ9 p]7AuVi>` )}癠UҊ4;(mZ־%NNc,``:&0~΍ǭq@mElDZ-irĂekMf4V]z+^ 6@:I`xtvp)S!ԯbaaЖAm4i&? :Y:^[jO 6l:4_|7~qc.\gpe+s@=HulBv`v;evvxcSlٸt "#E&M w[021-.̿`9s1Fx/\oLb-hAHhX&q>g 6w456ˁ>˽g`7-]ubOq?0E<~@z&vz[ WwW PjCEG;zjT-[|5bhZѵvJ?,Dw*T (7vzuSSUv*wA䘇'Yճ: r}FjZPz!ijg$8O?\ `NqNNKcB=df3 <8rrpʃAVBƧb+ФfgH^);w\#hr[Q dY φ˾M/tգgԯ_.]z=X5uC&yCf3aa`2k`o"A̚p[ՙW]w; ol|u.6w^|Xb}{ eK[߇'==D1T*SF0xz܃یoaUʔ q*gU?~:D,0OW6P_Z5~[ Oގ_ CU[is2?8,eL h0~Vs,;,$Lٟe-V6pOSN~Zo~89D8\e.w{7~-9 -?8|:TX*67(`-ȎѫlT40TyxB>tzueNt஺i? 媖s.w:z5,* 0?kx78s2u˷pő)GMa&Cŀ3!Z@7K (U [lzse@kaB{4$\L0&tٷZ׹n_o&O +<-?|pehM" bRf,K9 '2/e=15Vj]w tSɔLIr!  hA f0z> G[Q}AB T1U\)"[y+_IDžO@J)W=8y A W#hPJҀ.t3N~W799+gϘ <5QMR@V ƆpQcs rt ?ѯ6-޵>` ߧ ʷ-+UR["@IU 5P&\A$NbAy+ ҁ5LrUna~ el' y2/8!<\9m[h DU MUeFnNj%/Z,UTZg`kg})[,$& V'[5(4.+Hx_]^W@[jXd\x+9l jzԼ@"1jW :μ"_d~+y@0xO |$ʇ9?6?u8t~=(KˀJFKD9g*(y,~3̧D"_P,e jX=6њ[$6J_l+NI,+x Tm?۠l13з iTT'^vN*G*3xOлx' Wks%TVRJJA ւ@ 樹j.x ,^|YeNR_{O%_`fn~C z1}^,ZB, }m@%rbR(t() uW Q{`uDqRZS@᭟9"x bR  jZVJUi*pWX\X=ؓK7&"`-;^[{Jk%AMPπʫE푀qYd r[n !q<QuTx\RF[Mۻ֩pGƃ2)g}:FjT[La+dtɘ!awc܋/d|y }V.s 奜gUJK}@Fnizag4 ~g]'mpڗ,spp_$y& Vpy^ܖ[].i@~d.Usa% VkO1hZq:)ovB;q7qඊ 4-~~)=qcRA¥h_hAԆRX#h% i{];r%ܕh.k  [OvYrC˵'Xk/~?DEnN=}+dg ZVgD ߀Gzݑ*RE*~M_i CKbpbCPT_$L$ :Y+k f|kGkKU YQY2?<0s2hAk`^k^kyuf N,FȾ?n͆7S|0:Iğ{ȷ6w- ZwZ)8p%7X]-u:{`b1v6CTdԳFT'C҇=>{Z녟T?glu LIS04546ԃۓn mj@cofʃS{Ne2/-Nk ZV^+ 2[fw@ y*T<偷d ENyri΋+lEigpt߹;}bZt;^sGLQЧۧٿA3##zXrA<]< W=),T 'mO:@#- $ln \ud@@>3Az/JhSF ࢹegH49ﻧmU^+@uS=T/o_JI<sjdܔ^/VXS!`iXnZ-wC^kmRz?eW1 ;kwC/C;CJ*_ 4~y^g{&4u.W\;NfLڹրԕh whݡTru@I= %Za݄u7K'k `MkAfNPbRQ[BIwvm]sm|>w}Bߡ}G G> O./+TLzF?*wե2Au(,vkhOݞD]㰶nuJ63DS^hZ+;6voҲxvI% 0+Xk}7$T]ūaQs@nֺubpc l9g;π!Cm{/)I)_@7:i x{=zo(0)SKޱPd}uEIߢo{٣祅hE"`ɃsL"0"}G4ʩa/xbj`mFnN;^^+2VއqM}w*n:;h)D^.^vjv+ܼ6.k*[+kmׁD3H"$h`5xdO6 .L.{; Y .ց*UrD~EPp+egC ַª5 *:qFtqgx[eÒ4g!y^FTqixX7ceC^[ w}{gبo4mZVuzt*;Cﴙ y#,L0lptsN{l9sSRO Liw͍[@Sfyyl#`>:ؿ%2=Ph.UWBGSGSBUqނ]wvuΪDmJ7ި:6rljjKZ,mu:\}k q#ۛo€cGJiq8\xž Oʗ)sNڛj{C*΅'Ϝzv-yYfKs TΗ=ajA-___o ]^˽x~8)r꾦GYPm{o]vqqr f %?8]܏5N@^s3gn<jA&۩Z3JV+z=^j1ՀI(?CGVn{ 7b@6?L~l"P+WWPۼB<{Ȼi+N2k@9Á:&ty ~n^}YP<{iuԙm@IΫ{'*o e^Sz"1b;wo;PlUA 2 zbc[ W']=z2(Uf +=ˡnI'f}.>uiDuL7L.ƣe8H﫳NwbvŜ b{?XZuVeڟ|]H9\\#nϽ*x<n_Ӽppp y*gB > l qPru K=6$ž}ކ3p2dSKm}k=-["@Ni'cGu`ٹeU:z=I-R;jE :>OO kk +xni}~rY{~^eqٙ{oA^^5 !Y~>='\^y9k=dzQ+ij4e( N`2\Lw A@k{]Yw}w4հZ`^|;ch {WՏ t%uXDbloz;l_0U15"o4 H; lGcv>huZ($=K֒kB\G< ;40Ao|a:~|m5_xc h-[oQ,-,;`NZBJϗl«Ukp }oLTexmW++ P`CvǠTp tXrࣧφfv08 ɿ(~ ju>hrmT`*ျ9\pͺ3}LM}z[<`TT.9%hW} _ҽ} ؼ$ HsLі逫}>ucSKpqwE{ :33nݛkNl>)^X6-fn::{g? 9>u|āsSlY>OprH.]Ttt&'mA>2>Z-HLhP:8 P:=pmjm)( k M6n:B BoOEu|+&@rx6psj7u xn3H)`G?:>iּpw ruʯW)Yy?TZV+9]P|@x04wDГ io e UCPsg:Pysɕ+gpOOP5 Z'_­n] wޙv{iX3f7՝WAE> l?|78y# \N;_u_XK^=ܜ|`M[ X4x,{WoRJJy|}|Ӄ{{̈́nkR <x\ d 4N}>X)S~ip I'M  W鐰9\BUx6ٺgTʣYzҔWppO7;լf5_^1o53f=EOs Wj]!B !pnA7v.4Z9g/\.` VC0V}ÓoJp$=;#H)%%ڸ8;%^L|1ؿg>|?}ma6Rwϗ^%S;2"&j[%ӽyAJplOQKZ6 k箅T= C.@.j@PQ4*G+ D[NY`r5Gl(01ѲEPV (i/qpү7$H4m4-MM;4P`Pڥ"՞<ʯ>ZkPF`$0XŪj8=@R+K _z[ bckV@ZFhl*Uπ]o\=#Ȟ;EEVp4 Pp)O_G()xn]J$8S+PHR[c2ii ^ƽL$Dhj ]ragZoA w"Ӌ[)T v*d](SlP2!yHɐDl0ɢx,kFIpvub4_zn?d+(e찫/w@u~N~I_IfLW_y5ʙ3}<Յ='7޼55#kLj_@.;gK>-I( ysHǂTU_G*ݻD4Zk9x.mT0ื޼\. G7&Cee$-5&hZ= L^#ˎ=Zol 7[n1Wzk @uC'#K}nR ;6ZR[[6=-՗x~6* w-wM^,|;sHu=/dV8(]Juf6kl]`ÛA[~rQvgaRV@MVBѻE-B^ ) a k .sd+oC+b!lPѠ1v DhiR*#Jt,aeBC/B &k=oq)aC+fBf-L-5G⏎en ǠSyzo-߶5&Ph_MV\jYuxy ΩWAŨk˞ֿ 8TB$tgG{?0;`4(Mu X xܗԢhLcVCד@jZ;qRY* TVOFwί,)?<NjkU3 Đ%5g,@"O pf>T0Џ"@g#gAm`|.yL'O}(%0(UpXg|TSi*M4)j7| le [G<1Prv]Oէz lc <,G@': n:Ψ\eKdگ@8ᄃ8F4)1+]bYmV@ֲ4PU{VT+՚6 G%5Ec,cA-VbPw=vm6W}P*W~Q7C4Ы5ІiC e|Z$H@ªi z |*- 0GANI9jZց^ҽusI$%䄜``` v 2T@B m''.x0Zl|:SE[`g-aM|cޕEt۞9#- (j:hMgqiihCOuT}&IOH$%(h[[?9,z١9888888SGJWkoOtp\ `)?_oњ?瞷n( kAM:uHQ._$~筽 ZH$\&SrZL4[~~SWKsDHe|71Y%U%]3yqpp|#UT.dkvO2_~Y( dDH^$8 ' O2g|$"R#>6ܮz 8q9#Z-$Y2KDzp\rrSrs']dXnXnXb_h '+G m|<2ܓy\K`n3$x'TI Oӳ@R%Y 37*͂??8X.!=)%>Ic$ɻO$_U~w=M>sA&HvgZyVrw= O=y*hUժj²²z2'[!bnLɒtțmxYdHƴ5|u&@I9 eH='@$NH9e.?ٰ Zkmu060 ~}-:?$r#rQ'8 .|>q2$TB%TJ f׬Z.^xHOwK Z; 曾SUO ;7]zp1N7L'i x"?3PRxA/xiQRRJT_1}HEoA{$H㹷\K mgR-}g@g)Hn|3gPW ?2kȠ)A|m'l%.K{R&|@l- E묇@R'IN&=R6?=' rkϯoklk x>gt#Aj %%[X;v%# Fr%/'4Ai:u%+&9r:@6ٮٕ yR666ǐ(K|-91l mA^tޣ G톼Լr9 !93)*iy`mclyNzwRNIBz`z֐o7Mdu6[mA*Q`Q2_~2Cj *ε:y1'A:e'g4g*Ѥ2 ?Vxd/4VrPSy; *ھM@s"(ϪϪLkX@@$wW\*YsJjm6H?t̳*dx2+ɞ<ԆyA%N ٘o '?իZ1ȝ5ځ~]4-i =c.a,uitc-cU@eueDDkњCݱXvED=@mBً95BĚkk6-.6ͻ Uur2gj 68`}h`mǷ-NߤvnYu&j0D}tgx!p-賠0?Ϳ=z^Z)?Wb0᥇-~myHqKYR,`jjZ'h>|Lo, ܿ;VMnn ?x*]}wprq7@oiמ=+@}q3 /)d041421i'Si.p;rtl1hT_.9!GjkeBВA%A!Ka]6#秞#@c8}og-˒!s!q[S$gk 7C\BWj<>އNEӊ΁.wy+\luiSpUg[E>s6m J@YB|dic>KI myoC[dV7__IyegNN<a]N6A΂&@UV#"'AڈikպVsZo\1mc,M^}"éמniiC_f ʓ*dȮ[Gn?v٩$"3tޤp ҙT* q馠V*Pz s6){kqq@t[dz~zدHENr$ 2Z2v{UrLMyz_.0GZ<Mk3ַ9> Rӳa R=RUVr3P"?Xmf̱ woV%p+A} "˞4ͭĠ}ьf%_1Ccd\1j=3 :zNW@%~74~w;F*Upa/.ALw+S cc$N^|$7y5KG׋^ lGO~)x./7O允G'Pz77Mƪ`ylIAsӜ5KK_'f~EKIރag>-?mI2s;HO~Rwү14|Q; wI{4f zm5<% ygѵCxC|CkkUSߩِW oA}Q@/zVrnq>}Gg~ځ}dk>ou`,cTFήL)d:!a~}0G;*%7ܬF]15hvw;Û({`c4 YRTe~ۍw̪ӳ1i1u-lfBZ7d<[?tzqZ@]8(UlA@h>ySN:5|090;C̊3`Ϛ=̆ȧ'jEQ-^??ևBI9paUz[],Cc,R¥Ktmy.݇H98gxgCECCe!|q$:}8-wZVڴ-@&.Vt- =["P6yXA,\t]nύ;c?s{jgk >j0`/(dUH3n&Zl|ZZg[ƻv.%nz[@ [>3y#qGuؠwvi8p!`^pL65?ج*p9})[ج9~ijוs{dZE))RLs> 97r2!ASj59 T~DO~)Tu֞P 7ց]k;Ƿ>4fvz;??2Tx? RJDG7뇖߂G=spwݹ1 7BZ88`ܡip9ji1-bsB 5U? e(\Cٮe#<c[- 6J.y\Hr{Fjr(Pk/P#H5 dlm[ϯc<Dq2ۿ:?n׻M_ qu;eތο:mrE⋜+$5yDUǡ":Bw~D Csj$\ǫ *(<3gOO*UwS{QuJ%+eU 4Ϊ[O2=)Yyyꬴx+ctϠZ=K** jY*5Я詺$x:I~3)2,5-$MNJpvZ:TBp7e@&UB*o#/컘>'\w%J1SRG~v2^x3c9Ni04C>HJ qa7қVqx M`mcyn*4Ytl'T/((( *VU +W %du6Ap`Cыc|ݾ~@曙2׃~.=_3=J)YS{65t(ljPsq͊5 mt2$N N~wҩrlkkx򃡐>Ӊp|ZjvBCĭO`U]V'/m\jۺI`.?2TJW7o_hz 6_v~e@$df#y]_ w>M7 kr9nͼH۠vw5{{A A贐r!M7W?׬XeLeY5Vl4mlqV&,*9=%"\k+gOCpvЬGfަ{-/^ O,N:v|$Āe˗@b5Y9re}꾻%[)8^u /{Z[,A4_{=PRVʀղL@:J& Taa<`h b}94d :0t %ǀ_ 8V8F]UPE@W20X%JnB*z)0(N p0{{y@@jkLTAB@e| E$  Ρ>GiN3 9N /=a/3o,5 GlL2>ѐF jZ| @MUS4Gzw6mOmY n,`gx0 fEmfeޟ 2RFn]ݰEqqq%,.|d{ e\>KeAYM`bxIH?'3f4m?Y`65hKnH6<~O受z^M DoT@BU18ljI4IuDQ@OL064L3k==fz*4g8ԡ665 Q7.@TQ0 ޠj@%dw:Ie(Ҷζ 4ivni7@%*OJo8s;(c0za;ֱ@96٧dl UeU{{`4 c?@&cg<たXO?x@}>yE^lᶊ`ddx Z+$$F.g)b%oM/4I6o XY-;gޘvYۜ95-5WWG+, -0W^-GW[+-ߑ< eT8}tVnܦQ59BOeYV@ueZi!ArvrqFX[[@pH0A(sX":%pi=qrqOo;{{A75 W^ӗÿV_o !ޠSj >v}n|Wԫē@jr$/OAME?,yo~./;Tz~W/pWP_)j ao.H ˱hkyO.nŹEuAhUb,\e៊TBwppppppx{ esŋ/M>X*o[拯Y^ySސ7 u~O=رEOv⫤׳7(DQCr r:NAz/;82$|dG#ϫ(\ D1H _|7U;d Y) lߪ*HM)5@HY( | 橹dKRm 9"G0.%;A[gvArW% Tʀ+VQ||I;iNjj '%&dw A U!d"?V1q7I9sgIdCy3AFpJhO@Y92Z?+ 2@}Y*K `|*x014̟ 򞌑q'~usKLzjipֽoy %S<ă?~p"W('0٧IA6n<0\e | aPX~E2PRSjH)2\L P0owhkI-@I> _Pԕ:.ݤ+`//_ސׁb\y- WKJKq%߾Rf7Rospp(u*!e |%AI?y~ _W22_I?2C-1VHOV[V@Qk W[A\E@2/dƌ}`y$M>YgY\>H(}O|ټ|?*S,M+w~RI _g^( 9sdAˆ/<QlI:nN7CdgpKċ WaÊk))@wM+.vUH>|=9[[Ab+XXX@cѐ2uv'ѐCR.I!{v{|̇<gǞ{J(2Y&./L _ʗ2d~z {ʮO=N{~nh^+mxwpHL1TO^8[K+iB%Xy(_AƏ2NVB+ SMOK._ #A2;\p50W| K_h"eKUqw=Щ]ǥ@eǔJ*ر$+ Ր`x iKV]તK H\r݁PKٞsk ]5`gϮ@=ʛ9V *PP Pjb\4T44_ 5ր۝w9г90n'4z7{2H(IIP9m Hz *Uju:ޠܤrYuM_%Z~s̳,lN.]T*_2 <|:Cވy3C4xll _S NmA^,g,,we,pyG96ˬ%+eUn-~qd]ya}A{ A:0,,іǰC}gďl? TCay۳Mu[pw.q/v6`K68uU :zѺG T"_ݾ;휻+44nۺAKY3,H5,ں_ wCFF#!Ia]ҺAbĮmRJTk!޸~pyQ _Ґ^z.C]=\6? 8Ntom}סԙRK6R]b)7r}9d0kaZ(>}[꞊,=iii%dΚ6o?9N!\nLn&x>rF5׆˅ޯm}xx Eѐ~'3iNf4#y _kexNǜ8-oe? >`)ghG2.fv8tMAneyA% %)| _7_z o!᫡YfC+\CՐூO}>Aw6d2 D:к?Tu;6]>y/Nڶsr@kW˥;2ooO@D@rJЬ , UB &ؼ{h3 7]^ ΫԳVZ] p#} ,~C|P+tѳH Ř`dmz_j\ #K9ݺ @.W:tGKI j@FAen/lpppGZ3!ĸT7 x>\ 0H7{eL̨u'VR]kֆYM!¿Vݚ }MFrF.4WN(J=l % {^h7]Ty ܨu%s*Ϻ{Q&P!CAO3̗=z^Z9Vbcƾo0a6; )R )= Tɠ`h7Ԁ_7wm~| vjl|w%\ :;q`pA &8{º.gWZeAU>*?sw875JR) oڗ`hghmh#O'$Gx>n7,s,agO+#hs)t/P?^z+`Miڃ3@?U`XΰCsa!+ :2Q-B mhIxüǠI&{?N;-Xܲ{˫pͻPvƥʋ4HSOYN5aD![`@Wm~4ꑫ /}Rֱaqa9@l{mپq PP9-r?ɇXźV 9w=J<+taEkXÃWRxBdONԆ4v/KhV-.pJ:wBUprO^?i2c7bꎅΕ^5{ѮеTMk]y$3'Y0!>R[L^ӽyE,i.|`;FUS؉g'FJ_jqSYG{ O=hlPkFzrp@xHʠTH#K4,3 TO j~IDATL{w  jhI)$׭ =y=a1pA/7l>oFR3 #Sg#@\y%HZ7\\| ,2/G-rt}888!_2)w.z:3p2"s8 5 2U/U=.~-n,yVktSؐЊweo#p!bp=`fSf $}곙ˁDs'ܕ^wCCOߟlrM+?{ݦ+ ?4l~Mtͪqqq0 Xn(qs>XZYA_E;7+;ܲ>q>{<ʢIPn 3b^=왹C6[V}2+l)f H8p7 oW߾_s~ɽA ]va@Wِ Y]^ɚx3pm??Y2g-O37L  _BY3N?:EG{"%\6f^ (bL z n[Hjѹ Ѓ9Nd,iXXΘaEla A_/e|oM};FP# kS+ ׸*WhE`e+?;>!DڰТO oo1[sqs}vY؞c'42D}5`ffy6 `H!Gߍ-T`g}2νy~y_W~ _we;j? 2s2e2pKR/j-pХ/Vn.;XlHQZ@9Hrg5.=\*׀GN_8v||6d>4{.R[BwAM+v`KPѮ#\;m)I)/‚@M+:hMЮ]]X0`]t7gNdLZ"P:;#;ݱ.I37Jdyw@ i! uB`6Y414:q/,ppp,W |8?ǫ_[` i RCHu0nefe$gG9P-i@SY}~GwGC:k׫oL_'enܾƻ z>k݃K勵OIfc!5"uxjwr:2:My!e۱k ÔQM?v08yvpغsgj^*'ZAg. @8WçCx TPųnq[Zֱ8-0W, gY'{

z1 p&Lݳ <+vp>Bon#ZnZFvAryy0e,h?ȫ<3= *K*3ts\FAVY`yͰ5U= ^o򯗫GnR=J+omW4En}$NP&g}\<].>cgw'PH%߄`է UZ0sNIHl'q8w0xVYäv9zK{!Pϋk͎Fߊ*O[Yx4DZҒK'g9r?l~ILl+L  Ke* lfg>օpӍ>$-!lލ'pvfjfOs9غpmጆEAe!Щjmk՚UlzٳeBAUҪUmVXhD͠PlfؐO>۵w\n_S$Jn5o7}1rc5֬[E*UK 85 랭k>nэ$ST:u"5a^:Hgsy b-v]d4P*KiRYu=''SqJ(]ٕu,hZLA(/TrN΀mmm 1r,ʦtP}47U3>@{0Ƙ $j )֯i-k8PƁ&/uu=~0?OOFUkHQPCw;d„ 6v5  +0a >=~gϚ=z^l1DPj`-X{;{'{o7qH&]@7ЛHo7-U)P&d wAtdm6FEmp& 2P| $C! >0g%U{fkqܽu7> 4,fWѶ S;l>j W]}.gNJ ~D#@~AuY!}AZ`9p/*Z*V[>f_0NaKozjJARgRǛr UU x/,;)u߭hjwppppppwFi#aէ?] ֥u#7C;'KxQȅ樹@ܿsq<:C(|濠~>St˲t7_H t <*X.eUQK-b[jXISi !o[/+VUOőgTakwCJ+懀 Re/an RmNTAQtIvR;Vj o87t&@*Kel 'b]rrHqvJy/0͌2Wdn<c=N rGn-hoY3fD=^^\`ϲg͐C]`mc}rRNI$X]> H60sK3@Y~!ϿPԔRߪKxH#i$ /C_ڮV6 v;rO?0104vF;2{RSӚ5њb5IPA>| /Kq),8SPW-53?U?wxUڷY{I {])JE)*t^BBBHH ВH˚$x{.^{Z̚g͚M&iAh/=W8(Ͱ۸Կz RUVPv.5ѹtiGbI ig'K,ZnUv]=PPPlmkBEJ@T3տp|?##QoQU WL7.11 @ j,~xG' ih0`qD`ܒj@M!HA рqqp:ǺdA 8PR,P*c@ت>j`F1m%%ׁPh `x2~Gv_I? d0fu7f_ICa;Km  73[|$fB!!n =sh(o*(gOޕ+@N]YnA7999.]zРZaW.KzkWtJp䉬oÍ!7JU* Nw3Φvޫq/ Àݡ҆J*u:IRH9F%߂ S 灲^YhV ˖zzMkMMSOh}SQem7(w*"NxBGIF`7P8ptL}`OaDk6H"`g~ b!܅; `J`T.<$Bg,)9+1 X@ րff Ӟm@Y1% @$KT+^&X$qOFi)_(I(~N& pp?= .%A>v8P,oȼu(ÏU<>@O*Mw4M bBzRzKP9V[e|^Yذ?>!w\ FdLYy==v9C!@2C x7W,Niҕk; fM39:9 J߂o^N3S@BQ' zWc[/^A<><0mׯmT~ix0;qAfLkYA˾-,:uV՝;hc4d Ϲ*7$eoxCp_x8-GfA̜Lt]l;gÃ=\kWsA[u8q i-D**>%v*^ o7|{ꖙ1 [G'P(AJUhZn-OB'}3eo+;oYWQOvzFFm`WAm PO5uU˅r6N:||| ƚ` 7]1]u:СE. ;_KB6#TZEU2ČZx:i }}N_Yz7`Lh [o]W Jk/ CމGf?5'tKr a"U;9J{ M nw_}nWnxvc7#|s6':{wvhWi~@jz]m gzxh7 X8n1>?O;w ~٣!o"U/#?m 74(x# tlՅN!C @v|Vtޫp;y'Au#^ G\^|-?;\PP=nڸijYۦ)8gsvù лNn@8 i|H?p?!oț&OL1:_4S6diAH\CB%Hݛz(u7hk40kά3Cχ>#AMTǿs+RB(Pu]Ե:Xl+ o@ iz$y6P~:D/@/[7+|!jO1;(=xP]X:Î8NtBZ6r,wA^4 @o qvy~MA4470-A(OE*0*@"@ &>H5X@>LsAn;{3f!VP0tS4L=; d< 5\^;5<~XKɼ} D]RǷ=c~b]6Cm•ȴ oCGE'qq4pEGVgeϳp;C;qݍ^K"T\?!TtP2xT$PҕT1hiiC+̮ voUT?j=u@pwN8i x~jJSRCUU#>e8z"ěb(S{:3uqY8}:H;v+$]r:tY3xxB^T^t^Уlk(e&rCrj]MgwMo021[99.xU Bm9.9"/WX {0L ayrAcK'I ~|]n0@?T? a ~ B#φ A΄)9',!Hޝ<']Нԝ"e"ٟ-C"c4X>泚 kQnX b`€8lwOS)jL`P@8=M$@a ?MP+)@VjUk}W{Wo^ɚ'! fE.&) h=${`or3U9ǫ+d 2MGXOdy/c#(Wv4yX$v vauA4Iq3L( t%\M`f[PynInu4E!@]1-Gxܶbܫf̘3 fOs| -p{ zzc`PwQ%zԞ:Ljk8>_ohi*4tjx 5 Է Wm??Sz T5!{M֗~a۸(TrfYC[;.5O]t1v\/n<:zxlu:@m 6lCu jh uzOg$b&H!P(/koJWbqA*~*~ aH-Gk)} (Upq>4RNr'LJtuź|e1d=rCҰG_%~m%p^((魟^ >rjgw#@TDr]KgBۻS2V*Vm?שBB=ߦ|rlEi:P =d}/p^RM . FA zJ'P~4Mc'ccWP ?!~~|T+}M(O4:Pl{irM^MLLs@*ӎڠU2 u^r/{>Dkj"@{C{]Ci$0gԤo9 kGQyhKK:@k֫[M-bO<{,S)Gͧ4)$ֈub֙3#A;zX{@@.xZޭAK<<,)jz ußx?)?JKi dkL*R SMx* 44}/wHC=pdE@eQyڡ}}G O4%r{5(M78bufhҥQ U@6D<}՝Œ3`d3Ʀ7/wܽ4zZU#,߳o94fxys-4kpd:gf/p 6mm*̨0?T~'f5hѮ*oUy5_Ho@ozEPxTSz`e |§|G>T!. )`78vַpj|PC[# pN]OLd?*>

!pha/~X"^__nd#!ÜٹKu"/g*afMMj9o^Q_ |->v]ii1;;++2R^d*q+!g\m* Q:@tEEguڥ|[ߌ3¥TER124kfkiimP+)_ X|PeJ1_3c! VVV ۠T]ݧaA`db%\ VP|8(u-ZxT%9)3rb񦉠?Vƃh"`bf⺸.b`gp2x)fY e1X |ƭƽ`t4M,{054545}>K_VXMz1R D.?@ū7EG@EEJ~-\L6v\+RVUE XZ^ ;AB@;Iv,Fl![V`lele^\zlI}/c㧦y #h4 063/ƈjIgrqIYt,OQV(@].Wׂ&LsM_>@mAe`,گ_i?]. 'OO"{G-r8︚wfkz4mz 銎-h}px5,(ܜ;;&r',%!WdydEmu.;aӲS6she@. 5V<Y%(MP*&DG@ ?LU(}*VJNE-<Sy{._(J7:%,-r;Tq̘1/QPJ<\/R Rٗ,d̖9=eրЂ̓2rJd,Ϋc}҄ ##s[/-\Ų("Kf P(|e"YDK NerZ22Gvm(yx^nV.,(Zl^O);+Z3=8R^rɖ/>WY+sevP)[+%$O9zȯ9j]y1nׯR_:ޞ+e[D?"Y;ܾe}|e[N)OIs piUEмE_~U@?R~u: ]9~=̔-syp7tRR@fo` `=!рbL0x~c͍l[{qQ1c濞 |+|J\#()'҅ߔ_b[W>䳗3;2%eK^h ?_(c۱aaR}dvKBQ=zT#of.C,4/N﫣to"eaa~y3f̘1wq5D U_ ؔrs@^cċ 7-|.'h͌3f̘1cƌk?DQMJ * 's3f̘1c?@厢"P@EsќW|QGQUTU? 并݄paiD3+j}LoLq oQU_3f)OUuyWLyTDD+E{v*O9-ډ- $y` h&h'ھ<h'uo`)o}ʃx=[x omE[D QCy 'b0n72}>pDKRgQN+A<}¹,H_#L2AR8)N@>$:`6qqq.6h?=(*kU;ۧ `!Нӝ]Mzj] Ezry=˗WBF엏[ DҕPS/u?B&(ߩٔy:^ .,b׿{V^Jhн,SРبsf$UgbNX 1chhqa)R\͡,]4 EC0ez>暾Q_xUo/KFRXR:KG(%["`ll  ŠS_W"X UE5 oYڼ??x(L~*AAcdE꒍ bKeп m!I^n=rLy:bx-/ZB| 48J{nЯԯo' @k:GQq ?BI v]b]j7([ A^0Foc!`ޅX(ydZN~>~3Nntjkfpǯ{Ao,S~RC|~{0n/07&) 8~n߲RPF( DȭxxcF@iTZ eP*#^ DW0DV?F>$} DGaQr{XV[Bվ Tm\)p^ eL$=HO] "X5}`HP.54wԡ#,Uk?: xЭqZ?1WY=J_HP]uj1Gj( ڕo?BV+WDK (1vmUe& 4nMgd~սČ(kK륾Xܵx } gV1V҅KKA^%vm8.@%3Je^Y;;[`fazQp,,,*Lf]rݒ&o!G΄j C]GtqCCe^3C>rx<4=\]}1ܽ;| <{܄ vm> 0tߐ(pѹh]|WL+F;_ t;4YF 00wj7Я?qr| r6l v|32EV|񭎽^xe^=.{ $3uo B;.9<9cUnnB8 . ]EfwǮQ{/ ?~]6sԏO^X#kQWl,є(E%(W"(^UxhNhNj⡸)NlZƌPpw nv= r cUօV`PѰ~Z׮VͬZ|EM}8%NIlq`ji ַf-$qIdgdƪj!o(bP4P$hk5@ .}p2Fg;|$1'px6E`p4+('|swBc39k?y( b0yEʗRRh {h*#c@z@RW!]` Ith۾V2} !ih gz N߿jb<9*=8ENXk y]"6i--sM C}~.l8Sw< ԃϠCM?g; ~i a?[Ү]V >zc%,3eTzsjuHR x<){FУju\ʃQ >$s@wUhTi2˦1cEd߇c=u*$+*FCE}^^f.Q#F~h\z|HtyzP)=z*rtXevp4-u~7}t>tYyT*J5%PxAyOq c߀틷6p x|1٣/  MeUM#_ϸ zp!jyԝF#߃y2/;^%w"]X&ԊP~n;Vr)SX=]e24BSIJ] T!b3]v6AZK(J11 c>0Br]Ϗ\:Y^qÎwD|&V u {g̱`4; jBKhQFvdl#}-SΆ;oߤJzK&PW\y1w6Cp+t?pvۻ nhKg!u):tZh.hE Αo@pz.mdv*ʷ9s7l=.߻:^p,mX\5_GC=m Jf`<\jsi¥Ő=+ lGjpoeA$x)e| eDnl ?ˊ6fۛ6jt5u6 zK`I|65RJoV0ٜf&-Q8RK cw;&u]إ uy]^QЊ%DAvay!Ӣpql-Ap15g'@̔d{ro}sM[]GN7@n__aX 7]u,2>yg"JBAÝ˝tIm._9^j|ٺ:XѕXFtNry Rp]{50z >sb37ٯI ۞!.oyW ujoY nl9AJ01c 20KMAWrݏ ӵ1G`T7ߜ748ը~PtxਁGJ*Ubls{A޳:=$CȂЈ~qYbh~Y_a}wFUqWĉې7,i{N߁ϧ^ t-:CC.BA[-<m~ n'f&FL{E3.Ţ.nKS6Arɋi@*[.DG7^ 2OyDJ_ L4Nl*>M}JryooT_1+ЉNt1[Dm{|^!^_{._7OCVQY ))I>'+,t-u {nOYGrEo3B+x 6/lT*cʱ!ǯ@}BaX% {^S%+| ɘ$OesM.@akȌFD@*o ˶T8)vpfg;• ݡ-!^Ɠ=u@`>}gEƽVv ;Ѝ T(mMx+&f].piĥ5Ȳ˪Ssg~ )~p}a3Gmkq C# oňBࡓ:CjW23 3*7>>xH7yQË%VX)* ɃܰB5QˠNj}ˀg@vmr/W~C7Ϡފk43'2=b j=ȝئq8D+:2G=o(K^G%,k9`YAj j*+:DW9} }6'I 4d4S{AUY+R@ڲXVvYXAZ2Msb*xe\G䋞@hZfI8*e? u΂ue,$q wJ8;3!jd.k\ZsGɟʙ0Q[ X, ..ef2TYUiLy5tt_k?6P(5,>a9jTU l,x2^&j"k:bsr eZ'#p֐l+A)`K+ʼ+p.g: g=~6[{g@SIASͫxWpehsدI 3GH T*A.56`ŴʴxU?eb؝?a<8qY2jǃvvyG j:d) A`xSttߔ,|.l>q2hzۻmYAyҠ߱ %z^V^jW *bb*ؿKB s cop%&i#xhm@2\)%YXOM7*X M]] vŖ3f.j{XuHy7یpc>Pp]<PNoȬxz5NŠ8jV`wT쒳ΐ:1oN!kob} tQ:ln_%h<Ú,[ь3X)I O:Hqj}XJgf k?H !͘1^=Wh9Ѷ3h\2%2 Zthaj1\6:< ,Z A+f%Ի}5jȀ0w;k{ l^C (ܣw~;IEI`/w׀G7&c?X$|*:v;PjM9@ϛlq{_: 綞\}hۭm|ԅ"u xz=}{^{{"{ )@뻭fM. ׆oBVl  ܀ +Ud<[qo64m7݇7=Ba݊a^ʮ UZ>.3 F\GsV[C{ ?'7.\{A/nZmip @= ϨaУOSinM`a3&E-Q/b[6 ѾJ;'2e&< OF*yځPvAJ5W-AI_:Ω-Y`\dSdH_J6~ /gYb٤W y.M.24mE`M% [&M;bh( w5]2  ,Pس(\Mu nucc9b8,@u:鷂fTXǠ~,Pr ,?j-@V9Y2F&˻31@uD}()*I>1`qb}8iC½eehZw(|\Y aQb:KNWC FR_W008 4hT>G{i2MjSҰZ+xxwgMM]P)Q`\ol b!AΒ%Xl4-AAfO6X` şW w[Y =ڽ `jhjljС(5 z`7|ǂb-3`A-E@lw#Ad0ۃ6?_8C!l?yf4h Əs_CXWl6MM (_(_(:RBª UZm+P-+ZKeX6gl)ؾB)<ZJcaUv V{ K!QHP\Sh}SLML=@PA %%>%5A\S"ASIq×_dj6Ǭ7qqq hh+i=oL4f ڙ`T:%hM&@u8(j{~`1-/mmm5hy>xLzWgD?\Sy4pv@ARʂV1&߇iW'\yr?̺ .*ŁIs@ ԟRB,Υך\w'lZqW\fUmLd҈D-)*Xa$Hذ${i#Z$lݩ&Zy ~s 01cdM2`-CNXc" 3eQMx?+lk2YㅌZ}#OU_'/^@ᄃ̖2L78N5eI)>@2w@>䃗PlyM^rSیf4" dQ/dt;)I=W-i U*T) 5Kc]H TTǏqm 2\@L(?ŸdW\xhDòr*`10%DXij9f|t`9rÿ5 =|&\=yHs+ܡm0_[2U 2O̒Y2SfL0=5=*X_2`nzx9*e62Nɸb䍲o^{K,oi:}w#Y`l)@Yş0At]EG (5FE#}ǦdCqD]QG43š`0d@lGqLL#PqQAr|L"r{.@_/1b+bnMaP\Yh&X#ֈU֐5d-(^q4>`EbX`8#xR⹠=dok:d7K/-Z EӕtX9b}'VH:94P( ٣C-9! Er {(6J+HuBUʫDW@DP5Eل} 1lM[MKM=EV&>u:T vehEKI@Lgѹ,eFPש)cQ$AW$;hk9\.G*k: chj6h~yGޖ$. g8 7| PGշ@P3TP/X,|Yw54M= j{9UzZYbhMla=~YY/ͮ~~WG@TSCAV #(Jcຌ_6c濐Z+@5&U oK窳1YLQDr !ˆhbpy#끜 hʵ3P\*[hP `J 445A6 dS050Җv&ONH|$f~tp~Q\t;;`XmhG{X7{ik06^{?k /v'xEzk}PZ+ZpdGCO@fM4ڼ?f[Rހ¾? 5aСA;&A%X9Vy筄Gv&ó <][vIrjG׎lM.7>vl ufF݂BսgW{rn,ܬtʭ`mc^A=WOT/+0*8%c#b1ĀY颴QYfT3Buxy]+=: `1]7E<)H-Ȁ#׎4< "] eVM;[+ 3YnQw;@wQw_ y52Q`|l7 | E4#:qN|E?W٭EULi~V7l'O%?~?FQ]=߁GZ <<`;<{t"*g{5 |C+֯z8 g˨ ~Ud3V[>-}2J.rZ⾸)s7=ܛƒm}B :@/z,#R*T[+5D2O[nׇn3CVVV_WW(h|6Hp9q88p*cy-X􁀏̅))ΐ!Z~m[OO:u[zx{=q2l VukAdA~* V8PafIսÌ q[7@kp։'[@bPg_.FO")bCplC A̐NG?=޿z+^%L{ZY1aÆÝqQqwFͅ.]_TV*A02'fg7y{=OŁ-[7@w\r˘+`;v΁A7rnBT^aDH4~b8DU~( /_Bd;߱AB)sd9AT9^JO()P[&bPO'@.~s1ZkVaJr2dRf`ؾrMR!x>Unۮ]T8'mAEew7_{3`O=VUnq{*~7-S?: LP[ƵC_l==<%\O~z'\1rhl E WI3g˷.~Ep:A[@ŢE0È@fG Fr4%Z@kɶɾ`cgSl هOd?mWlֽWZ7VYJ>hY#dž{ 僔S$ m rZ͙n\9tqdG1 _ c,_xdfݚz75<_gϟ;-te` ^|Tflz}<#nf9d9uq/;8IfOCP>} ? e{`U3+ T\ <Z#|{W8%@餿T mjགƭSeSS{Hz=iۓƱ*O|,CvzBenlgn;v=y=:NK@+_X~SZ4>nd)6yUw 3fMq Oޱ17R};\_~]s`ؓa߽뭬nXp Pyq* 9~Ϳ<ީ B> 9R'} 6n`^)22ہ9_,C`S' ȯ\@VײV@r@5dTuPe2y0="'&CuH0tP1H| DOCtJ)t8>ڌnYW<=@#?l =# F؀VUγ"*_Jl{8D?<8cuHR~zuUWaxѰ:ѿgn\ ^]󅠓AZ1~r"<#[ Tf`.yT= ,q,k2nbh38P#ԛj<6Vg-NXk2A:X}OƀQ  Aax.4[d9U~66GW} d,95c+Rz߳g$2uY۠l V_`7ޮ]EYiM[P+UZ`h:b (}zTKf@o@Xxx$a=u-U1p:Ʊ ؎mg x>yUw3f=pv5絜>2dgނ^{%gJ@n]g5f4k4_rJ9.ցrKĀ1tڮ l.CsCgCtq]]]M+țߋl'N`[6r=s{և6ӌ`p247@nV!>=i(^vi3!!9$ :_s'ZhwĶ){gzP1B mNC6=nj!U%W G쿆g N~6RI*~2pe(J O)jR;L] ~~N%KB em!lSxZt=fwn gs f1vd3Huii1cf[D |AQ S7x1;61h)F`ohs r\PI_TV84% M>8t\8XM0!uK*dVSIKk' -|+;+m`dQ8#Ttx`pÜb6˥ TaŠW*-@tDKPsqq6Ă Wd,t:Tp jNRc-`b}z(q-*Ll]T_5@ >77(_л;댺0ٛL,k[VߙVWz1`zP|yDU%PMe*'3|bU9-GBY?G$xq!T[^}U]pҤ[P TW=ӽ{C-׵v>4]h>Oxqwk G\9zW}&@e߀܀S`Ye"xew 8kBѐ+126ټz HMx ȷG<0IHD5%mZ4)4Mmz hڽiQoB -; 66P*T=uCO||6u)M}ݒk[tS No8@ES4h1Uȿo|.kWXoK.lfx~ A5T5t`偕bշP@F@ T[-6+_ VYo6ؼ}kp?SOm<$(8_ g kd̹Û냝}+׷zZڦk]-4 *:׏ju/sN=A GO־V:g;_ B¡9DS r?5t*Ko*mNK[PЮ3KgPٿwQpp^]Ak> ͊w'*n8h<㍰9gK-ULST{Oeο8-L+ʔ'mܲaUz>l<@ޗWRP)\Fe5kX &ST#>!S)S@; JZO|"*0 05L4ݻ7AsVsX귃Pe9kʦ_j ~e5@iV@ީ| PCLc@FQh},Zx];peEoџJi  0T754iFC}R~(p*-h v|t,`!>3wD5߳a1dL{){NY8:4h!ӼV&KKu >01,{BQa 3_x* c [v@c1XE6L,~yVyA^Ur /_-[[G{_W`jmjmyjC^-Ѝҍ۟mN-]9U/B7 ?K/=47-{ jUZj+tUuH)-Xp`vҶ6}4|iL'`ȳH%\k9b;껂nmH0v51ڳړC RH y%yj^Цt`mʳJٙ_f17czíu߶6o :v oVՀh@!:3C(90pYV-O2XJ@X [^u׹4ZM6NjvxUEeq $ZԢ&pyKޒA ?Q CmjD2U4MDcшҽ}@\t5-ucD>yIװ%-it]fdh wGy#O>g<]ͯ r]u >G<" (Mc'D /k:DA@,H>I &1`=%.^Y/R͘oQyq]F܁$`/{\:׀` [ >9f6g<Xh&7A>cJ'̿H)z@g~2BF.cL&R[9`kp HFfgy%})Lf2dL+\}:kȋtC< bxS 4 ,`! @}Do`,g~)#e$f2f;f7{@i߈o@mjQ,yV A"4R:n?tQR*o@D^P t#( ʅrK8}K(G9 t#\&FU-<]&OH&ZZ:9s™g Ͼ {-ZE6< {աjE(+[8 bD.J1;f^52+ DQgb my!<>A zF <1cƌ3J_;pu: ‹?uYLXSjhvj1ȫ`czK}Xfv6'!rԹPt$wA֓?5˒({uwdNsW}f̘1c oՋj/_l@NS0N6N1N);f(#bǼfƌ3f̘1ckR/sjf̘1cƌWFyB{˷Y(Ֆm0;a%=aZB,8ELaWߌ3dE ^W ~Oی30QAYJZy3rD9qK@.$,P(2c8B8.-7wD>-r Sx WmG* (1PWWS/}<44TTԯyw0KE⽧ۂX4 XSaꅫ bzz_Ai)?X_pf̘G 7 _ZМXQ*AJ<:f 8:8vfff2\^vWZozl\ W͟MDcTj 6F@zIڦ$P(es%jZ T'Z]; F_TVD ja0P$W)ؕʴkO ydF;aqO״_PN Lm 2_LbX.@Cf&D$p4 8y@#ɷ@8j|Ums@|<%&n= e+G<L72TdG|@|+cK,o̟'gdfm mcmdkd5MMtTD"@^Q2t5]e5xE NwIhެYz(h] Bľ}TP}~W|S.zrOw_h}Z{8lqD@\1"dwC&e8(ݔb-/k"LXN2]_D oubbXB+,@KL Z'4YAƒt 6q9<3djD͸0zl1}G](Zh1<_P8FFP<wPBv^$W ggw޲ϗ7uA N˶EzzeqP#yOR@Og\=`" GTD~[1w@Ej5ADnjSwP>9q0&f. '6@f P] G UAxˁă_hP0`(?%.]wHOI QATx՝Ì3VQ&E%Y}Y~p廫&C-%~"M!$(6d2,9d] blķp1v<{!pɻ }ΆQXbk (t/ *lb[`n)oEqHH6u 9305{mhB+9F 0[_D b߻ +]| A.mCT^.> y<>ީ;< ?p3A7o]xk-`v 4J;J<n$p˦,7rsBզWWÏ Y [lB~r_tѴR I1\5:twp靉Bd o,tz$R#-Dӓߞ~һ[7rofISmO=v J2-4TZEgV2D !SOE]> }w!Ck;̇|Cu+K"Tԝ %Ek۴)xF6³ b矝7UU7=q `GnSwBA =t6o<ѶC,HESNiM-y{: 2IDAT{(~z&P|Iӥ5AAKAw&L'$^/ /rڠ6<7`)ʺף.A񌈈hbxv@uQ@tZ~ ;V Gpy >d]eI=sfay E WA<DQWZ]]A*C/CwVͬ> l9P!;?[*U><2dz@S(k!FFیPoEۊ?hF 71AF!~\iUVWt_M[Eeee.Tہ >W=% ^ /Rr' RuFU0PWEBeܩpnzԃůЏ֏ /K _wi笅))A fb(zPɇ ۄ -? ΄,+WkK0٦Y͡륮M+hkWkC=Q" ͣn QS /QJH2;u=<9O:bSDe4nPϾ}=A~a <7\!I!PKɻɟ%cFs47*,X԰'{wյ6x2q#F&(8H)PECqJI&^?{ysyhϙuqjBgZ{pkݷ<ϗ?}kůYra@rN^Ǻz۪+> 2(! {&{6y dj^*OUP55/hS)5'e9~/"R )e@Zl`lL G6+^7C}($tx]0ߩA0*ȋnn@spg> _z,UbTb$| @򺂌b3܁OYbs7 FQ `-2<\ NT_\۾9KO BGHB9X<)_/6 /~8D(X{[?̓c=;,퇠Ⰺ*>Xslnp'3RJT=j/ 9sC٬YV2"J{A#?$n;8:;97BZ )cyj,0|`chiҾH[|w<4vStEh|F!42hp`h pXa<ԅwjq_?ZVnr$4Iiq%9?J|@|/k!we={rOgdW+:_F>u U}w}G}0Wt)n~6HnK*hCZE=Q\lsY fUhW6, ,+wCDVw!HH^={wOHQJ.=T$W+Zoq뇐 E ʡr j2Bφ8,mlղbl ^[,=sl. [bݻS%/I?RjĆǾO=Yw^`~U"\q;Pml2'1;[ UCfUT*AvTBՠA;{}> X ԎjgB(!P3w5©Ngk|T*i|zh4T]eyĻgć ͆ ˇAeV "-b,~\3qp4\r|&!0',ZѲ4.tҎ{;K׾ߟ,=YXx o\oK)z}&|ݴ0t;&Ɓ{9&y$?rʅpjɻ'r(>]03O6Zwh*p|k80O???e܂ݬt{쳀g6^_f&ttG<?8AؒyUKz6$s+,4sYVnWVv 4nT]> pYWUA]-F5=ς.ǿ$_/ˊ,Ёʹ[G5"{Ⱦ d+)%@тab Q=zN"PR%=pf w A< pbdž@kD`tktK/:)6l3f1g>jZ :@`MA c eck 2Dw,K5 y^ @khu=+h5S-`@buz?K+QGlmmkL3ƀu;ϠKѽ=qL!f~`d4r&}E~.ؚzz:zzzt}tumMI[vŵ:_p{vu'(=^J 2A:>X߽9t˞GBz.] ܵqPX[?M M&e_~] z|RH!cg?D {`v>3|f-kAcZTTQ*+@X#" G2 2A B to^A0Nל@wN`NYYk}f F3@={DX-ZeeYS6P.N:hii낏t ж6XhU7 c]yo9S$Km,h ڴs*hkkf")藌b (/5eV!fYjZ PZ)qou ^g%hu: QqDQATy["RDHB ~~8N$. TavPߩ+Nk@l{n "M [?x)Fΐ@gy}q~mX%VfPϫ+ RDH1\ @E7ƀ8.# ~/C`u:DgIt1^L@eDi__>e# lr׹+잼' ޸k >zXP,J& \ExYeW3 o'9Ag g$ bk/ `ail[_\<nyjxk-h<4;&N8$}0iDG>t rܮnΉsYVV1#߇}K|T"rJY,{ϯX'I7d=Ȣrl"H,14/Ȓr hЎT5{枏h{"AΕ5)׀Xt>0& 5Bj䷠ɢ0J^{@kK$O<% S"N;Ƿ 21@| KUqRÀ֬f"M1!fPkwآ8E2 P@.$#]o+ ,Y1)eC+ &/x $@,(~p ȯ4^Cg%z'"melU`AP+fZӡΰ:Ck]tu bS }p. ԦuAd}!jSZ 9@AX x]bd-^} V&Z Dx)]&"2"{F&B%ÒQvDCy(, ^W)'։H=8+N~[) r6c0ּ6cZXj PRT)[Nnye;ii&D6RAIFelN IMk\rzR@zۍGPT}csw8,\jU ܌'7ہs3ldngUKՕG>8 /fexC}>]O:4So;L6x)^^An8 ŴJPsTR5e;9nqk \qkAm ힷ{ DM{`gN.O?gT~҇ ڲ,EY D+5QMiN ||''L95ʮy *.+\ W w֭+(!t Wlerp:3^@YO΃:u: y>jsN{;5Y Ox!!J*)O9cf5km?oTlwrN9 W=XMpϽ1]vQwIғpҧ.vv[74M]?M0 @̄-Ͼ^>~]嬋R,O}떬dZaF{#9j̇c j?U\Y!%NɆJϫK!=x]ѧƇշݣ˝/ q4|: o_p={w}l[lª˫h1eU'>: 40!so8_ߦPWk)|o\3qtSrN~s:p}7DS YyNu

`DSk1e-?>ӐxByVwf9PO9u0W᎒;<>Kda@Q*q#xVYqɘ1Kㅎ;dA҇C Eް#r缝M!}`"pk}NO1n LЦp兗 zl؅PmYMPT1]#}G&(4x|p/r-=e2ZGC  '2ng%}Kn~nXޱ|bh"nnv=Ly}],~jB쌸ጇ?%,[-[OpKjr;V~4Y8  ^BGVXVQ aV! +dkVVi]VA%;KƔ< Sfn")K$(e2LyEgm^),}bȯZP:!$oJ> ޗ{ ֔,uMt!ٔ\"^NҡTҁ 7diB+evG{=ƮKvmYcxNqrޮ}g;ζ- `Ő_"z~m8~x9вKSx0ώ_1(=( K/y Gw #*W:m n=[ jkkͅ&%9Y9?k۞G=7omBdn ~}7~  ۏ:6 dvk n]VCӇM49 Jx*.j ~Ob'%~$㓇U$7yT Oā&X 􀀓}@WCs]bCh)5@^O#@7Bzx[ Sw /dݱ( #oZ1=0V6Fs!/kW-Vn/U!Ljt 7WmQyc(|Xr: BA=e~|ʄI)=5 }[DH5vU=#  Mo{:  CŐci2el[&@?0d~9L)U yhvfK +Zh4.FV4sYjjg s~F?}h;]gE-AtXty&6D +63%nC1/*hH<1L Ctt4 .H]{^Sر+v7M>"8q<2453BzAD٢YK3BYKdXlN0O5q [B73~ŖTV{9$lY5wvqƦb?{ԲjY^'zF=5oԜUuygg:$$kz"ǁ(+ʉ2zxZ6eU@^B7܋GG y<mi< qN'>,}Jz6Oh8KWI^)pAۅͽ[p r p5Ẋ-A5EY@S/ʫP0x$p]@U4354!ř{ K֨`{`*T^H~(/1Dog1wVqfff6ȑr h b1V(l 5r94ʳr!kNjB}!?[. ՝W@nU%|}ep1;&H~h ˨J7%2bHq7(0zeH+'2x3=s1}jΧƑPg~Gs.q}НOBf5; ;?\s*ph՘Pf%`ְ(Q KNӹ{[\psJpVgѶ]N9=ӞBvׂryP[F{b_ß@m6qAA,d `IfCwIkкa+}/Za#[04N2N՝K5/oLu X0v:h=Λ@4DsX1ţlb)XLOML888pFP!І'$pȥVO).> 8T,{O[DGGW/q9qUi'̇i~b3+G@ZUN<;*yEw Mh jUZ+Npoջ 8$sVWku:ԡTR}H5?g 8XnH=+G3{c˅B\j8QִZ{"Ge֕\ ؿu%]",%D w$.iȦR/B`r p̝O W/>x 9 D8qAڍTH0:q?viO.\@KEW,X9/9,[&.AMA=m )S /?kn.m\"@I|ؾ}e5;i F_(2խs!d/8;w>^Wv{΀AL8vI]ZʵbwK P~eA,A][`hc[MO7M{U3fɟ!NˍA?GcC6Vbyݐ5? g"5+\'c=@?F;S| 'mm)% GyCzIYUA9*ȳg]+и)nX2'͹|mpmlUdgXa 0M0lyCѿ]?CkWde">wZKµQyH3ͻ'$Ott1+w>VSp\ 6/sAR1x45m5͠yAw!qJDG<{ۃGy bc oKK$sR5֫W[016MK%`h-32% ]t}ځ]2f۞AVx-ywG0$+CJhJ唶 j:x{x倬/fk-ѐ,39S@nyw>"ueu!盜9!sKk011PPHݒz&>v?RM!郤 :y s(C ϕN\ ·@YwM\6hJ >ٌ\OCOxu u{V-+D ]eM*z6n op23љYw .aJp0vLY2[ٻ5A-e+pw{ǻq |)zMl7ǀ$'QWϫ00Fz|OΖま[&@#BYN@NN${{z@g@W'`Ttmumtm=~!x_:ȓyrׁU<~~AuKXRp˽"PXvpvm* LIϥKo^}.]ю`^g^k^L v#m`+Y t)u!*p'Жj8Lд| I%?(#a&5}}}+?C,@^eAzűgeFe D1Vx@}j( H(~GHw[T+UA ?'d5YCy_ޗOH# 4)MA]=>x˓b\e2hNsTm2^PՠҖpRԉ˭ o_֬o*rL^m7/„#7[ꝿG<"m7?'x樹_˓qrqgb׬Ckÿ5G70o ojGAڤZaC(~a9?3=RH!O>y:餃2 Aq@r!d \e2jJגɿz 8^Cl[e5~7MH?ۋ-l6 .gꝆٳi`@{W )q2.Cl}ۣOxX΢& EД4%\)(.p;2( 8 < NoW*U@_DmQKT,A,RKd@@A/Eo&*RE2PH BDQQq_@W1Ws@l` J[@ ќS”-EK1MhxGCq9yV^1[3@ MwzU7y pty[x HS(sP&*_(oB"]«%r?B߁ \͍*ܾw@$ qhOJ 7$x#$@"0|,dK`,t$Yb3-i\Wp\&3/0.K41DiP[PX2gPiLe釳pNoC?y#`! |<&:37ge=X=BĀ2,QA_>p P>~G6:Oa{`+'=,d1XAFm3Ct^`!dO[h+ھ \bx9AN]SO6 Aa!2LCt]@<濻<* 6 `X2[</,⚸".IUr\,'9DkZ{=߮#d}ou*NXmܶζ|<(W{C"qm;O~ G ? AZkGiG:6 } Xk\PyX!?u@]D*Jxuե|udǮѠ\t(%0A|yZ {۳f^=I0&T[lj9JL*ipH.{x-lqDY!JkZVEqVL ')+W`"CAy4y~GautG%\уUJBRġI;iSwP<54A=6T[8'ΊR \>Q ~I#^Kxmmmܩygݩ6RRBh~踐hH/2Kk!&΁"狜ς$KO0(T4Npwu6}*z6X?+_z6q6KIs4ZdH%5+;{XС2JFα^B k~֮ R>e}j7x'p4d:v|j:* ޅuXb1m.6QЇ# M_mgiR=dG%ߑ}#:N#/ p_WQ Lަ\8? 5!ddҐLmiIL?@.|ۣ8+&E13cǦCLLlЪu-?чw%KY#xz{j=_L2 H+V0 v5h/F5חBM5^Z\oq}`mabbӠʶJ16ح{U<˯WeP*{RۨAVeM`';ur8o e8̖3u@]>G;!7uBP?~@pzjɭ [jE["n8Ҷ8ȹ/'{Y\+zjg㟝x튝.4 tG98Ȅ# *%j¥lP+QG=:3ncǸՂOڄϾ?'o|tVj]Ac՜P_| ")pg߅#O| '\:E)GSraMAkͥPVCmZڥ'zr}R85MKI+ @԰| 9J*-4.T^SLqʹ6_7}4?Hj^rTRmK}'N9֙V+9qFy3x|y𦤞$NK1)BĆk9kEϋ׍ zG5zULkjp ?)Y8E,Nw >*SAtJyS vd sVϙIPE1-StDp&_m5s畟cwDX npzݡpWܭsw=pSmxuj_z)}0\bu $)$*d1l1nge?\{J]񗠌RF(e.}LC e+W. t=5u !giƜ=ֽн݇TZ> d-,[ZO}5/W.LsJI@'GgϪ/ﭽy{w&6CظbAh"xmB8h<8e?2pDGxS.#]W6>6 ,,sA)ϐWܢܖ 5A hnjӵw#c3:{ALQ#lJxoM|TשS<*l_8܃ܷ)@3XezyX]uի؛ٛ;Rn)79wJp+z@7s@s{Q9ij{,.,\V r?,zuu+BQ~I$KJfiƂnn)&N VW;CAs49Lk߼OS;&NP|DנB^YX&=N@҄}ykp9rȼZzAdH]&-z׭׬irм'VuՇSĨc\ugnT `uȉK9 S@+y!\e`g[i {aPRUhAsca{wu?۾¬ahq)HjQxxi8^uɭMuV ~?8 la[~ ӛwKZ?ifxswD_AF̩ sQ扌`h4ԥwjȚY*9;CVyY_AfqY_RNiDީ1ʝ73ɞUtlm mK!Z+,KCfxE&O*VB *يPϨ.~3k/jˆCBARFazAb0 cUK3 t_) {)vg0uĹUw+ᦀ( ;"fN'VΗ?|\.疝nnzF(V8;'Ptᵡ^R?!D>1w{ YMjdpt5Eewu9r+q.r֯p} 8Q| ) 8šȴ"-bΎlpG'Abs |g~ kr\#7g a'jPC"BMfSh>Ѽ] AgI]*EpY$9 4d[pR.mm}-|32gRȢ@'Ta62EEpaf@[^B j{N j S[KޒKw­inJie-Ql[.s.&9}dwSv"l04n6|vNM*8.NSnuOt?mVV1Ym7`hje]_GCMPtj ڂaI:KNYg(8>1`I5zhK\^O ?~ne~GMKW/XCq\M%Mo^ ,b8myd Ogep;e;Łvf#hx};`=@Rn)[握0f݃{L[{aǦ;ցwYHo(\lo]jdkb- h0Řn΃" ]pE"`I饫mK?\~|y48;9@DY}9Z(W\4Ӹ 5^4lM0[ruɺP8pJw`y:'z y_;Nk9},XJYXF}Y.e~GPpw%h 7aF(* )>IoZS>W(#v_/ރF_m4p1x ΁_|q߃b/|Ze*>w%m7/QttZg/ ^Mx8 G>]}?%D UA>䃷=hp" g=RN; ~A~795uY,kk )R6s Kʒ\ ?9Lu)| (<979R =xbSt4574A:IHM He~e|E0u:p_6SH$mL|Y}o_߅m.B4EZSuS5S /w($'Kj ^{Vxhps?x9ʗ!m^w PdKE.\@~(?]ڋv7o&NLFɔ>I>׽A C=ٔ\z\mueqU\khrʷlձշ5S[S'S}[w@颥3K>-Ztމ`, wSnFYҷ8J z]/30l\ar.YIV3h0 244d CK<}ާ#HLm7ը I\RA y f(J)p=zubM {x'˟r0Ex&@q!ˮ ;F|B܉'ƸUPPo`Rٯo (%Ah$gSp;v,K `hXi (cxs`&gPGYXp-Zupi =qB'=0 #-i9S^yZ[J9RG 4 r#h5N'pvIkU+ԫgrFu&dMq Gr'=՞jo000m*aBc@J />sOA@L=3ӢK](+~mv011"d0i| r&?!$)eIͻ7,Wr7.m]^^4 ?) kiP0{ǴYrϹqoDI9zZ=v$GgХsYе55yC^g _ `[-eK}j>m WP %r[@nc-Ct#cMa&L@3&zG:Ox7/F;8%=YH ډ6 d9YNyI^퉓L2(K5EMQ5V} xnÛb(&o |}PxAWA;y-ƥ[A޾Y/+oۛVZ/fW  )hGHpn MIWi2]fF1b9[Ζ&aYY9^kߍ c1J(*„ d\,Al'E"{nԷx+d@?ib_988ޔ1qcz=zo&(mJ b`d ʻJ_,1YBg+b*]{x5h ΉGЕFx+Y2D,Zn !AH0v٦-Xn9A{EA8aqr*7 :Jl,+K>A?$.sK.9.1.8q"șrO%Os\4Q&\-Alŏ w=rx/(-J BDQ_ E*pNrD{^,RBa%sd!pY\l:ЛIt )jnr\#׀,S1DH ʱro% j*l[@(oț .{8  c "aOYJe@V=eoȪ8xяp`C/ oB$)/ 7@juZ%M>N)8kgTSuzx%$5I5;LWF hQ+׮j8Q6װ׶7O䯷fRx ? 4Jh ^ m@#AE⁨RAt%D#V & sAT5+ޛ@Q<V8>(nJ"YhE7(E(JD@4qG$z " >qITAE@<D[MZKyHDꠄ)DgP)mDIBCSHJX5.)O@0IBGS(H+hmAJ@t{.?KFQbJc O C zMoP){O r@l[OAf%([MH]Pr,%xG@81EA$(ABe@rX x*rr9AP8plW\W)A.$+@  (xۃ'z.طٷтŎ΅E=_>Zp;E@4MDg v8= XWmUroozPG.ٶtlw~{<;wV쯸|Nmﭺ~#Ce?OU!yv#k2auoiJw&D ޢ֩֯öl\ --@^]n#U9z}N2I)•++`aE+1cp g3`)KZ¦ћ͛@L2@7ӱ_VH+Ք*+J"(zC)(Aʇ*Z3Dã~g JwF)9`9ijL̰pG$v¢LSR&X!VT+]AHPW9a9,VV(/SyJv2|$~4,(u5W4@ynp(P)Qe o`^/0){p Rn(|ź裔!BkH=/h,pYVٗV톴) C3u=m%]u yEfZ(B\I ED(RxYŗB@rG= KHxZ2xKpj\<2uM!DX) flt.QofqppE#QRkIC25j 7Zܜu5-PxŹ+NjQ^QAKZuZ~8u$dH-i Du7$~%/Wg*?W\ !-buCc@IUR+罜J*UgxRIקvk PNl\.B %WMM5HOM '*c+7ܟ6W$||~ *TeR6I@ꂾaT}bI1*ZPN(Gpmȵ3w^ PlrzQ/~KpԾrPP͒5!(]J+Hz?Z&u ׅQã[^>*~<33/}ىXQFgTPXLa88ඃ]jxh|ej}8IZl<)OšWuݯ"tP*膀f dTMp dJ/} EJ 03CQ=Nm Jf;l-c=m]gCws~ veW@fٌS7k%s~߭_DžZ)c_dF̻\@C"ߚ!l.!! 7mJP8Ӷv~Y<6ÒأQr w ` (VԿ;.fǂ[FnN ɯ7PS:mJueԢp^tDPdNR pi"i`hp}앺YnpcYE!`JpO 拵 hhthY~Vii4 |p׿압 .?x,%Rv y8&w*'*\% 4E4>W6z}b((~/w ˢ2/  w1;BveQNpǭ4ovi3{] {8/pe #Agj r;̭K,MY6  2Tz)BJQ2*6dͺ /lq"F4|PXfL(qátTeJzvXzziವϡD~%nްtcuepj o^2 u#poC&ḑRV(K/6=DI]0w؏UtҿB!`ũ5VցC9N RI T(>fa7AH&!+uŀ.uMPP%}~98<#o(xu^i;޲rOo"L?^|fЃ{܋}n~ +]| N&>GJ-k^ m8}70M#cWZX9fSEKHr3- W齘yJZnZ^H9]X总<[gFȝ= M\k~U ) rk*fv[xAnmK5{Ip&Miӌ7BfTLx!$\ILNYFAE4442fn̼j~`A =222S{NA2 =K/ Fжv6yA^./{c@'c`DHޛ|3< 8SSC 0ݜ^3_FC¸΂5ZHg(Zhpp 7މ#~")En5ܪiA-PUH|˒PXV:8hioi<(C;!W ?([f+:vU+ /lF|+Yj8>2S'A#2hQe3`Y*_=Nݫ#{Txp _΂C}v/u~<*AT.4z*_Ϣ#<ڗaY ӠRT]_p_Pe7P!T X*ecd?b133P_,[5#hi.]z^1<\A ^;=g{~z#7q1nIc6w)Iuj3bHŁA@-V?(j ꢺ.G/l@U.7].\;xj\s c&swUO8`h;[ ti7A3&LKF*Y[VﱲgATBtHuPTK꒺ l,Y)0Ă TU H@pqr*ĸa܂SK<]=5ϗ`4B`004fa.GkW F?c5FM{j. *}XKŲj42\r圁u >Q@2??kZV< !jT@S)naк1TYa\PZ{ޝ=/|6O,xds;t+xV s.m ?}AUm_V<z4@=Tpk Tj*},߲`*nFPAِ[k ѯE}=;Op:.6ذc4xQzm鑐5Ì8AsfxSN(jNXVg kJ!/,N(ɔhDfxtTb_DAIB _S቉b≡NAX{-J҄HAT-Uk~dǛyUVuDmwy dLI <Y8s ?r r#wfYK,1K|r|C.Se+b/Ɓ+kZ d+sd;x" -i8#NS xGY d9 Fڢ'o̓9 V mb+h>ZwAqAj!d̐ b?o6M‰c'8=t3DOCS!1MLS`PЂ p%`k "E$A}B2hS$-vQLR DH'7r6٠5 Zv!*kā6Z{dq"|zi=IvDdOrAHkA4Aۧ!z[b!2ⲧ|zW1 SObx hLkL Ƒ ~w|$BVN# AZ @sIv_" ]yLЎiI_#$1 h3$v]B< y{Ă9Mz_ѐFgx"AO3$m֑y .` A7(m$жhIYK d|I: bſ)fR _[|õ ]P}~u233'j&x~"Xz~Yevmۃ7MESL]M}LtX2wwe7 Sި+ʭȄl.<18鶶 j%|/l![B)V)6)`cs6eܭ!_F) N `[aEʖ+@x7Wz~ps55^vsN rnݭ,S~ !n]W5q.e]ty3,}Ұ'Ǟ?^LⒸ(~+ޫ2Wft-R\Xέ|I(h#aڛ Ab H',@;~^ ڈ֢5$@Ү9Hk#H1ߧfiY:l+[p̃qA˂@PWH$W=iw@ ѮhA ڊ h ZvJ\.y428uDmQ`1_0Q DHRV1蜗}8mdϙG.F#mz):BMRw8.!q), jbTcswHJ$3-vɈ\1kENhU9 _o%:CI!'5P$-q"_1ţƷ {0p*Ea)!Y EE}tVEN^Er snP2T)[[b2T`>D-69 "E-8eE T.:31,Z8DE { 3"F=`ϸ=_o 9g CɌ=/CF}Uz6wB+Pl5Y OXˑQvymqňO;U6j48#1<7@Vҩ(\G'`%g~k\M쟺Y YEbI&tݱX'V'O:j`0Mhhv"jiAĤ9 S]'ͼyP1bJp{ꝥwҹ!`mf(4333i2Y&˱E;w=ցM4 ./45yRݹS#lLi :w>-?~p{ٷ խBCw''NvC#oׂ.>u}F^S/gO?N%(&씃r@}x*pF. .k\ރ]k'Com=wwލG'IUTJ-8)ek_w-4~#w8uԅ@_TmReope7փVIZk:hk1:05o r tf:.?OLF\v{Z'O=V+ ?~ -ݶt<ƫHM-NV8{IDAT<(Z=UV['oy BnUUԎV;8PVУ 4Ԭuӯ!O;A13+70Hwt9e~Տyڷ,V|vFxoC9y3M4ywT1E΍.PFMØWvR?:.V~=!Q| kx<|01׮Xh Nsdy+JH iNji3ݔ72oxzDIWStv"P+p/pe!l=vnOK0"EF0TʆG.%P`qrϪ8̷&:EuKYgd8vRzB- lO~VkH$BT9FçK7x~M 6|gԗS߂GUJj(-)C%VemO%^|+[J(c<^i\]v< G;+{Aa)lo1oFF&(;w偗 +V+V΀8`fff ސ|#)>JWj:4iTQ.}zȲ.U8INEkQr3 _.>.R4 D]t1G!Q^%;opXsl1 hD@_%܃'}Xi\;6n{u,e) >*+PB~$hdt/!JN {PzJބFhD}6Ccp|i-j߃vA;_4S? ~tQ4Uc,8]wP|#tѸ;8sEpf&FupN 0>U&Uf|i"YQQ E!$^I:'JOjL7f@;ciU%k_u J\ZB TbErL #2!5ȀA}]{$B d d>kQWQ9c2ϴ)ڇ@vI33336`²El+Zpj㨱M\.YLL#M!6-&#~u#ܪ=y6~mݿp><{  bH|x!jY\]s ]g$&KuJVAL!F|L|i؟~Ձ(/_3-I#-mh-ZCg!an^n>XX&Z֨u!Woe/{G^n={L 2B•!]޼`5%@ zR1d+B6[ hG_)n+RϤ&7l:X7^6 8w2=[ "}YVU!#1# Rsh޽Zjסba`[ͦ?4l~p8:dn< ~A[>yr޾/s^C[<YmQ;~z;Qd9mPGA6xeC7!rRX2{ ݠAB v}߳k<RU?\rrPN\q:bj \2 _RsөX e ks֍J*hAG!XL]{^)Q6*儂kj5KM+yp|ݡZey]yYs?{ޔHiם5[=9֫Ox6 1~g|F1ha"Vl] Zx a=fF`_V톱1cGO!uV1uu[?Dߏ.c:`e ݤ@ƥZ2ZXh:I5Ȳj/&l(Sp'>b+T`2Di(EwTZzr"f(WRyw`ݍ\X~zz\%BUת7<I -#auy'P5 VEmK0N<{uXAGUWawݛ+cW{-  +=q8_1b 8 PI*x9B>ק -W|P๦,\Voq=- ,zCV>N$G%W7Il\ 65mfffFa2;E,LX|pppJM-5u8DxZ$~ s3^ӞyzGJ7}<}6o {4`?8 s"Enpr/GocFQI;*X}ޏy 6d(CK'j[o]8vXP+3 cLƴU{|J*U. u]FW̯x2d ,|@pwݏ/[ ~p|'@bLbqbe8̼>pY`Q-5sBƉ %[`kibTL7[mǵk$8r yyzŀkmʮ *mԾlݳ5r7hypک /cX|뇯9r>Ae5X#0?p3 UGAk@/[ZX>a  F SSSW\zs T]>Ek ue@~Z~*-epDlpvkk FdFQgRo@fKADZ&Ȱ˨lŔ;֝X7Iݓ> \~}SW=~(&Z6m Va>ɝbtņO6 YH}+ԕ`v`ߦMs[3$/?@L]o`-N"F`caL.ӽLEHߘ~,DߎN޷{OgΛ!|j!@ =k 3KG,qPzA7 M`xnPjlAT)?ĕ7J7-Y:,{oqj:Sk@7(5G}$O!qy 06cN J-&8U؂,D ) c#x-JKv 97Drh :&TnBr=CM:ba7 ocTO+XIIF2.~D@'z;':_Mo1z P<%5mgff*[r VaX9P!>UA b.)ݦ%/QqDNW8k5A|#AVe 3`7OAS)=k2J3VmAVA<"5 'A4ME#PcⒼ"Kd 0۠Fjqrb iX֠R [&Wi or[`jaUڲ K20 TMUKREH@"WYd'^5PVd ~rF z6W@UD%A VC0PDO]tkc1fk a(H<8x8f d8/πaej 798ܷԇ ۠f[(2OOPrFUlc 紅۷n^ fxhU/s jcp5C}/D. + *WgA$; jڃ*6O<`2V /y|O|nE ;a QΨqvh 9B1QLCP*)$܅( Y]:ʫsn_y6ҒګIP6TIt N%IԌK?ih~:hlȇەo}G7o)"S2L2ַQF{qb GWD@!i3wO@!O <*ϛ, ">W\Oa-Csv1Ļ%4MrZٟd('ʉrYIK)An ˵ˈ4SI2^ƀvX;=m6$ -{vCZk51H AV #Qh!Z`;zSNāR|IHA5ĂV,Gޫi -HV Xj-n[7]z%ѣLTEt2妗WJj0z?x`>|D]QL[L{L?¥kl/OV^589(xZ[` 5H#ra†=& 7.uE#$+ @EHwMfmms 냬 Eh*@Q~QnG_u>v3*H!T8KOaǪRAԃAOkXlUUJ+ͮ v5Xjƪ`$@NX) L,%H"hz?4oSsUv@KmM]Iz6'DM&z=ih/z d}>M@ b,^F]AW_l+mFIyvme@#WH 2SXo}er\z6L{K$}>rT2AH8[x@? xsEȄԁ#g /%/YkM`ǔQaHP=.+iip!eQ~qgwh8řiV^^6ñ!Ǐoc/{e&["ZD_\|#rlg`@pVpe Wȥr1#7!2/5G'_l/ƽX*XxV#*L˳*"N>r TPŹMލ7 o䘨kڿmH(X.]v=*hyҚkM0䓐(p_@;;.րȗɑa7]kOhN⢥^Ҩ)Lpԇ_Bq~qq~1T5 xk)w7nap6 8Lęq馄MG nAToocn=e:vdxmg1mк4l䓓`  uW[z< JRSjBq ߱%L^3!@ CE OW_^ B[({ߔjpi{21 kPF AM+JAD "|6|JO%CC)DW+bE?Y &m'e\a RPJI|" xkpLΙptph[y|Ptg/Pw8~9e XEE&T/kP Kjt; FwV&8SCO9ph"9]D?ipL;Y$@HU2l,iw߼ke; >3}G633?39V~ ߅O%]RJYCf-Z}6K Yg9XJV-A3fϛ }cSkť(^U[UVr³m>>sGAhhhOF kp ږTNP`UTP>9-(_P痯SdF1U[q{QrM ?z!zD@Y E:J[~-fxԶ]h>y/!Yk Z3 <=J.}[84Zc<)--ZCZ-ZĀ{H:6Kh| o_#?kE;'t_ :y*/d#dVH:de=N/քwZ 7oֹܹsw830~p2݌7ol>U|4? /&X9=(_TfW#I`_W?0k>%yF?zn#ysHٙz= &u-O_vaYp p8xa 0ŶE fBµ+1CҌu5Tlpv(R87pG,C*:umFAP *2z@b=p!C c{@tԭT' M=[F#]ŻԵTCPUG,ڢ9;u"3ȫ M7[ld.ߥ*<^SA,q .-(cBLX,N+y&ba{.].Ane/F37er_ޥ-lr  Bf1·lj2[*Wmi4AF1LSLhФ宕;2!sTBj׍1hKi#vhԣ9u*҇d,e.4 kxp .r0{[%A}u [= z1;@Y)ke / 3Ţ 2  t(H*PDN間-ށ¬"".]#zF')XN`1 ,9~E >.mrjeU{ P~~'AMV LLMӤW "k=f<4̮TV}2,2, j:#dZ4԰{'P:3Cac#£EXv,~z<.4հ| |nxnm۱fm e6ij(G9kXΆ&!m 4tkpcj: %Axjx͗!vJױvMTtqr=vP&U\rޮ \@Z//FFjW/]=.acM=4Rj`8`,l!D+Gj ^-Q0+YzS1a"Ak Q @ HTi #~m//Hr`f;!fT܂7 7Mdd/iAs o_m/3E!޷*OzhaΊ`{u>p5&vBvR!:at#~1֩(].׈S) t=SOȾ?g% jW%[}Z_7`lNXʸ5RkN5 wz]uu}>N]'AU'pf+'lVϔHeWm\O ɁY*9Jh88*@`8[bd&S cP A7 jC[V3LPg29en*W~Aў0 rD ֶiXBB@;4;>|^Fz,Y~ 33Dam؃o?ߪِ lF%X9}U}綋G[8yډS4iga~a߄WD]bZ YIGO'dA-v9вaT:)$$%qv<kS)ȃr/bPrt0e` KuF=?@ جGj̩>2fΘ^߽nwgTPdp/~}_X~}#A !lðrJVMvv9#?0O̵+{ܷ d.̘ 4q)Y !7C] , 20M P9_*79yڇ]_G,Bg. \'gƁ{3=TBXfoۘ`ߛgǦӁL[aY3Y?f> @{=Ӟiii;k\*(<1`MFBĖK 'Ire +-B2NFQO\u^V3܀^|ȧk[ [nWE);mMz5 G>ΦaXjF p.ᦸvS`Sl(ҋl nܶQ n Ο8K,N,^WMpHˉdCjbZ1߁gApb r>pxJp8]}v춰 Wk!\|Wq \Ld XSJj{h?##p|iCf-2$LIXq<US 6!U<>#KMU|HeI"f*olٳ˲)oP)! rʝK "|p|J̀5;E D֑XpKwUe1MLGOszBc T ?o I%]> BA0 ㌯<<ݽ $8p<;xTSN*XU9 ~~aKa2уOd۱ B$@L\}l(u75˝@€ MK~#<-Ν5L)PJ]*@ׅ_(k[vVjsW!<䓧AP*^68ٸPs\Cݡc鼧/J$_`%0r *i3{D썸 I>T lllCy38ؕPV>έ!2&"";,-3 ɣ"1Zo-/- xd-vJTI* L%A֓uI6b0>4> Iq"d{RAuQݰImhF3 P7-lh@.8ʃ K:ȹrY`8H}% L`5a/Kt36? %0+A>4T- !db SybPzXBL' s[P,WSߝrYLfCܾ/`?~ "BF9<+O2X^ jFMjt sA^WiY\@9ZC\NTJCV1؀$6R7%YD0"} |U˃.x H杚i[`7A8Q8@5QMF2b`k` p!L,<n8&S_}fӽJ%uY `6xh<4's@ WՈ_Ҍqs$@@ ޠ/Y Ͱ4lX`,0~Z@}&?pn`44To[B AtE7ScxRc pWbj1$f(%ii:r"7<#O`3NN҈P j l F\WFT>*RD%sc|k,cX8QIΑWT*8'ΈӠM>Ɓڦ`,0/,j$R`<2AEOQڀkjwI#1 T7fI{FQ^g8 ܏ = &$σm ? opn\5P bX)ꮺ 6b5FS 05B"\D` 4馛Pp'o]N]zRHws[X򠼔7V (Q2@-B{^(drYڨX*c 6^/3E;@`<сR >UAeo633333n%Sy7c(T>c XnѾ$*Se /QƻFu(W)ܾvlH?&~67@WOFq0>6>)_V/+ ,Ly1jf333335RT hhhU먵)Nc :#~U:Y-ߒo&@2$vLn EtaQʺH5ew:333b&BfY#(((]fKFI.<;h~$#}H_K_Q8jO'@@  mr3VZ\Dt=A9A~( $/<ϓpRK!^9s|㛭@{Mk5B, fV~PhSTX&m-a#+ Y㲦eͅ; '-i, n>Z_'@(#|@J&l}*C|kn19=(: Gq^ԡ_UZ$XǠч@_ρ%iѠ]׮jAП@dD-Q [Zv'&ȷ0&hZWfx^,4ĝ{g sGN)ŠP6hk *ʢ2?2f(+ʊ2>VOZDe<^;<*QdHC5k]'O}* ,En* O6th ' 9 zu/qv݀-#:6y7D}tN@dnܞyvU5vsػ~} sQ(v.jPl[lU SN/om:"<ZkuV^²dX{89yJ'rl5m-N:%O3U?+a˸-O~ !:o)T ξw`ˣ=CɈ iVv|P bn/vmH9r,E($^~rlԶn,]< &g4hG??q]\W p[\vȬ8pgP<ِaNGW+ ᗙ $@LSg]_Tg8sL!7$ה[7^LfQ91Ó' knݷO$K^+Ot>]v54w*ߏ2(>95.]|y5DV&x:gi !r,%!nO\T|\S:~rKȫW+D}3l;}-}O(4nA ^unjmaQ{ÆG>< sfٙgu眲9@\vNPreM-^,Z7}iBHa;:d}WCeqٿA!&|\?8z8cCT7VS7םhwީ~A\V>d u}=ܙ|/H eVʏpɵoBõI!{B,Z=~,l-,By@ϟ9[~%?ڟ) ! eD(q~B۾mۜN[2Wfb ,-k^k No;5AKR)=-ysڎi!ѡTUH6$ئ;67 Fr˓/g@xPxsog TGqY8CU~*s nԻN[r&^ zx_0^<v~IW%$mLQWU6;pM5J#:`Rŋxi_vl4KWף!+ }wXv- ahPﵺ9uSScx dg'd ?wPzscazJ6fO /<ᒇVXglY oA,UfQVnP@0]:NS8|piͥK!>[^=^\8>s<5+:@ޘ)߃5nBmnufyL&dKZF5m-YkS<$:pe禍./doHKs0R0ãywTm6\z_ЧO_)c?+L2AwmX6(6C,#c pCw]rW/ noy@+QlT0Yzlxr`zd<5ET\rWw.]#:0Yre!;-+:\v05357;vww'w{w1bccM6m@⻉bg]OK҆jC!!ؽiҮO^}0w bz%PV}\ zlۻÑ#׎6 ߅6ef,d#S*a -Vn8+CUͭ[CV)`xj>k,SA4"RJEѝ23331ύ??s50'^w8 NN6-ˏ,߃Q=H25}6$C\ )RΦ*e$VM,&Y4  n@á|y/];n|tQinSy:N@1_aq ܔ[ m#|m V[N5Ck&\vWNnFts߈] SP ;=A^ǃhU7ڷ;~T@JU"+I d1T2A "`ƣo9;DV*ʧp32 _h8Pvte۹nN SC*'|@oީ&CԴQ(hx Vj<9˓5>֐X.1 =hf4S͆'9N\.OyRu-fA&Pl+5W@S++8?sviϵGI="<%pWIpvq9'U6S_pn~@GEjV6^0ӸcO LO/o:'tO+NNNPJ/˔IO.=y fժUs% o f'q?\Lsi Mj7o<\=ݻc B=_.U*n҃PGVo(h kp?qNFkR}"ޘƪj7o;jt72wCo :6I|>(TBŢW+N(00574y!PS閥8,q`lmv޻@%882&C6j1xXjڬG/ ,@BU>3|>e c1LόFU9s{"F_60Cx'`r2ٚJPmaec,={lwCk<ʛ\AT-U￿;jZVE:kz40zk#̯UeB *4h+i;P~{e΁o;_'$0A%F!ٸYECo2t0301{mĴ'18zC TK8Fc@P]UMFpOD'"LKI?EaCA ƈ1%ͥ8L^),uDQs`3?RH-dWO$$= "RD\O 7&ln1oC]E F֫} .q$5OꝻ/(MO+hU т8P  )~yyyE{A߈V ?=$x,OCHȹs+'?-J-xXV^ |A' ɅrMQZYG+ zvB;D4@Vdo j'AuE]"[dA_/~@MI rO>bX[7ӛA|*>Rr?-U*h.O' mTVY =B+ٿS+2У3м4O eL=Oԓ@KH1y?qjZh%dљƏ6LF:@VQ+w*}qHIfro '+aK5W.fw;E`dQN+k l :h˵%)κH+]]r*< m9TM we&HX3;1^r.X6Z{OV=CO D1G}D;D1Q4@>O?E%iR~!2ԇɵozH]f/m뀜-g_ @AՠW`QۢT ?D-Hm=[9nk)# mr<UIQ'U|4/!(y23ff^x },\-u Т hu(ki|MQVeAѮn l/ہ@XLd Q|!Yv\$M2,J[Y؃E۠-75v] wP*8㭫`˧[nnm Ϛ>|gx`t1z攛ny/7-y>|o(Z^$ĦǺǎm]>΍=-.X<&JrvN{ω/Q,?p9q/g_ʇ r;X)V D9&L&%}aʅ={{{!>?8"L<[ƭҷF[ϋ7yTg]- C5UMU3m7SO9 yo(&F~?p1Vc@ cq8s?̝K khaGNOGAk5 hjk~rA.:-A{G B MHgj, va̞x Oi w@*Zn߸mJP .m,,u@( Nϝ YrFiCqc,#`iMkE^mɊ̈́7;Cき(<w|TUϹ3J  ! *H[>6Q@PTD"(UN 5N]g^ܙ{%yg8}GEr#}"\pP'/hPzںI \|j|\8 ,JӐQٵl>ݛ`nbĚO7Z+ k{gOxd؆AL~ѯ |2rs]r5B덇K̹& zk0߻y,Vжiq0|, %8}tf5,n` OӲEZj_nAAIaLųWc[ni)jt'Vi EYE֢P8ܫoyyy>ye93Z>Ѫ''JOv<rrA-jyn@ĆtS2$N81h?@%}H_96:;hth香`3b@&$yGK(i7ѷS귄]t ''S PnW!{z1pDK.Ջ.~Uw1A8iM,]"C?ɮ\sc3AzHO Kx /.uB ȻD1"j{x8+Sm5]́ <~z:,QGG]%^\;ymХNq&hO't oLQ\Έ 55 jCa}Nn G_?X(-z wp+V­nJ9?=% ؛Xkey*.tuebh8q&: ҿ0`$,^ vSd1YSjHaݳGe ǓsnX[K>p  ?+ߡ 4 hؿ'DDUEΎ@M˨w[#L&P9b=!v_ ;ps\O^\ }x|c;i1 :X m*Ïx*9=e1Lg7::T"B yeMpʑ;=:{CE+vem5lNǛpmyұK=73dO鹪VPZI88p`pYi [g:8 rcxl9cU骃8$9Ŀ^Gkkkv%BPd L>㽻L z*?µH2#1(+W k-^;L |>~S!v+D(xx1PyZŏ0)0Ǜ/]APesXmqeܴFlVbX,jEZ) c ^61쪳N_ЊCϢG<.n }Cڒ%߭"kE[[?߸j+PV-XTD-ȿ22ݹmt06l 'q)CXڳkBAM~< Rǥ.} FW;*MY&[oSVOk1c7 SL4<ž`b5S/d7MW}J9 ( esP DB9B.VXe;n>Sklvo?i3'{x{ eGX,e#(D LNSoޱlqƎA|gÅ^OBU=Usww/LY5%pr{[3dc ds9V. K ;]!XŸ!k!o~^L3PVQAw sep~@tEpH|e^,%x]4D}U c/!-UfhЭ3~Ӓ )S޽0-NjxoK!]e76.`i11RJS!a fW8;)T& CY*0i p5Z:[(QkP|mI jA ݦw;T̓An 6w .(*}X,Ml<3ȝq4xS/zs[Pru+3ٖiiP8hVE4t9j_Q4(,2D~Ru:oA$qf}TJ{.}Bք iNq>_y N֦l9(s!.}=9\+Vd(XX.?7o87{/~ 2=2CF""כ鏧?ԘS`}:׶ 'T%< ...b~WA. ?U EȄ;Y~8~3uCahp5z]40%K6ܞr,XXږ?q+'hrɇ# ʼ"!B eGʎ! C@~/whwOwO* ;b(&1/~j0|Wއ.m8 mde2:CuluM @N]]+gAj'BcZӦMwo#u7,zj>ԉ]Q %E-q+$e4 hP 7_ k _-dگ3qF#bE#j aaa[akvbVzfOk~/ز.eCʷW;|nxg()/(>;iz&d.r._{Yp*q ~'vUS)1Уq5į?L^v|b'vy`kk2H' dGmGk e.$F\u! j n|?{[P__Y:d]T{rzgt+fCC Jcj݌cKA{P0 1 & = {2^JbғA{F+߫4W w\.8?3k(RJʊ zsr\9P]UAL( jZ.xL<*}e_ҎuR@Bg05NPTI80a6j{ D\5wb"dP$2Ak0qzLem@xǩ hR3)1~t?<ıZdtZ)e%n REH-Nk5X)R@ޖ_OAм/(*3i `PTWd,Š|lqDA]nTrQ9 2Q&ɫ EC&Џ \mr\-8b7Vƽ%T RjW jm (וkJ"  AdLdE6KUe^e00 WVZZ/O' rC?:N'&lʽ'A?(4̆RCp+\5TF~b^|A:JWP&(A}EP 12FƂR+e*>_2mI8e,([-&Юk7[kZ(9Jц O6^M]m$yU^/sIDMQSES@s579 bq:i]M)K@100WvVK aRϪA| RC!xZ=-Rkr-Gߋ d&N[}|&6e i,G|Pkj yr|{3%B@;AUӊ ˻rd:A6qZ:'l@v~A OZ-V9Fl_gEG 4[*7U~D-Q 7p#c?5~yZYzNt( nVVw*~SǾY2\ Au G)#z95bw+K[ay'p0"i@hKk:Mr_5X,'" ϹE:O=f*s yNܫO쭭fwUVRAaL8{u:NyP„H)^Y[lN*]Q ߐ ԣ\s*|1rB $pa28uz{;:-u<1р/|@L◊^//[^0XlGSHÝpY~(XVsmp9TvA[~N?&;6i9R/(hFKPUf`2Sy{~聯Nt.p3d021#=X0= X,~TCS!mCp@)Nӝ.αHR|^$>e 0aX(Q:@餒ȢGo`x0 5 Ad'j+دا$KϮ\O7ꞥ_@tw:e.qDYAv l2ADE$Lފgr{9?GW-'σcW}3'ۃQ8mtql`=뫻:N~S8 )r<4[ֱ3k┸"Po&,=:eJzͧ!z4pobz 6I"67B923D+MW,,OJ=kOx=j\+X  R}FwoL@5.e"Ce~8l895nu^t:o76ikW%eUfZ]b K56JrN9 sj5.Ğq8*"*f'C_4}szH-^(SJS5AlA+k@}9E۶>vwëJtJ (@W }lS6 el Z -aP|Y{}}% VIuwW,; {g{m(ߵu[몍9AKu7^t:o~Jަ?h2m&W[ Xaϧ@͠_Y&ӏ ?tCI+0}2" 2Z@w.6{jX6X,>Q'Q\yD8Wh|-yTomM,su'0rPWb 8Y\ jiMp`6ƁzQV·P7 unƖq/le"Y%Vw/E=K@DPIX; (V 2PvnB&P#:NzP_j:/R|8-ϔka}MBtAV wIE IÀAEP  Ae4(c V3^4͖K@sRՙj1NWH7=QBA<+m{{G2M..!ǠCe_m,p|ˈs97 `1 L4;ʮ $A>&+ȩ_;Z}=a£gW!PNJO $AgXD4A6\$V k$t%ݥ\mGO- - i߁lK[`!/ d\"JL&i@^t:) HuM'Gh3DTs;: ;Ja-hOWk@/iMA! u9S n߆∢7ɥ;([ OL@<^xBe'fZڀ)%|F4 r3[f JJ!(+;/WA$_T˓ =Wv1DsZif$#5OwOșr4r_ѕr /jcHPeu d0+)TvX)kYu7^t:o\HM+y棐jvQ,ƈ@'Bj(82g%ӜU $(UD{ PUb0~kH9`Hso sMt3TL/f68$)@,{eT]x,@YX\jD2kVE?McQExX?XA^fz{4`qTݝRE%p lD4#ط#AzX [ nNt )IDATT_Or/ȵr</g|Nrz_Igž&Oh,b.3L.yS[cm`t(7׏﷾֣oQ}6m().yK (t7pxW'f}EG?oLoĈKM^tu:N`A/@&q 촖G 6%D'y3-_@{\-퇯7n~ÌGsx"; {πLD?8ս_dܯﰺk#.*Q:N݃!xA@xuF9be8Hܪst:NtLx'ft:NX`u6K[ƌ3 sut:N=TQċbxb% ߬jm <>*侇o^Nt:ݿxUUUw]u58k0H3V^Nt:[>䃼 ˳ 31ĂrǃN`61RW,ܥWt:NbŊ1N< 2U;`~|jB}dm,`m]m UU"AH,qNt:E<'FP'k hH5/𹱷A/ơb0YM+ځh`Nt:? w"O0q $YLR/٧-9xQO d $$qwi;TZڲln-\vX]VyuZt:NRhS_ LvP;Pfh`&G4e Foj-T ?/(9s;$Evan|~'hPq; g@S;6{Nt:**`; rs "^-((xC|>܂Ӗ7yqRJ)aO&S׍~3/8VܔH|EkNt:pCc޹3maV"ЈH'zTXtCREATORxKM-HK-N.JM+/b3ğIENDB`sqlkit-0.9.5/doc/img/mask-demo.png0000644000175000017500000006661711714210425016313 0ustar sandrosandroPNG  IHDR<:sRGBbKGD pHYs  tIME < IDATxw\V׹7\(8\HJ-[feٰoۆZ6 iSm5g.P9T~<׹}s3QM/'zB!Jk6BQmy泍$u]jF!U(?<IcjB!Dug*u!*s#qG!U62 !ү/pXw<"QY<7oͻk ԫ~WVM 2 ?x2W22 J w~|V"R~cv17 /WopyzjiuM:]+u;7&sKw`s㸲[5ѺWs#O?^].xTWieqwrpPEYq\u,_%_/;3sN]0a) R8[MZ1TQԲy!%9NuT0*GO_rY\.EL0z2^r81\bέ\)ӆ93#_|M\1\u9 }t_JZNdAs[\ɨwȦ?k[" q,1nKDA4MTriՖu'.Lӟcwnzf ѸNϳyYy(Nx{msfAo/s%:7'sUD.3S+&q@DT[Zw7egoG"f|';?_7rϭcomN+*~I8G9ڗAd.-\q{ynOW^i#7}} x्iyˡP{%mAu2|̼k_~?o&\6c[ >.o#KLdN٫7ct+;1sߡ`;x>+K6k{ ^31 ;5۶xz>9q+x)*Kg'\1}cL)jgo;ൟd.sk+{?_l_e֕|2JQr_|z$Ӂ1Og2>H:ձOM9e&FNK>X"t73{{Qb߈"lr]D=r}80;b ZI'%8^:0rL&Ŝng>/gk?'{v7_mώ{_,֚ty$}™t_4Y;|#y[Vηyr G)Ӆ%|ׁ ƈS,kzeAˠo<3.qVYA@w* jHg[|5Jg`Y#CY], ab_|j'rkзp9Kp3jn'MՁtѧ:sAi˩I`s)rOx{ gzȾz{DZnć1j7?26"|p]Aq@sr*v+~ t64 2^9 0W&%ǙTZ^:S|y|eP+$3GS}ϴ缯=xmR׾BTsui3tJi:*>b?iҒ޳1oޙ( ~n!M. zV2Ie,8pʧ o]\`|dg)M*Ng.x|!l@3j[0,]j䓅[IvIfx\:oy3\,_޲O>L%ƛ]1|" vʪYk_eAeot+&Վ>?m{2vN_/2wxoۯ;m=O3KG၀۷;m|MÈgy݇3M7? f8ҍtB/`zZ"yW~a1uDlb3o&nů2OZJ`~5H[w{в9_mԘQ/MOЫCh{y\-_޲aѼx:K3oq`1퉝c~.gݔZβ^2 >M_~#i)#y9%B*j-8OzX*BB!GB!}TBQ!BT#-q,[B={yxT1L%?,ϤIϻkg{jx^y9pG?Bv'|h W !f~u)/''qX/3+Y|\~!n"9$~~v=r/\ew9 LJruUb]u4MCuq]f|=?k;wsv^ïViѷhJ`@͚*53f~D;!^uW EDrrקIHn_{!nP{[^4Mf-|FAQCcc9q"$>izjꚎ+Gut@U5RN&ѲEKv#?/ktIuxx٧q0gd3f~iݦ7t v˗/g")) ፗ^shMusunıh)yVF ł|8ԬQ UUiݪ5!!>>tЉ#GqԫWHD4nܘ222jRn] XNzίwhQR?  QP5Ĥ\bwr9/i+$< uRue80Nj66rh+4"-=Bc6ps~kU[II躎}`tl66 + ;. t,v6rE[.4r0͠C`P c VYƭz/N0 ? 7*2l/.K4 o(\.* 6BUUrs1JaϾ}dgvwBN૟vP', {/kp2%28yU` !1֭䙱ͨU&fV* ;t0 $%@/*yfw*4J8r8<Ҙ6P (h4EƭQ¯#i9bMUuq yE4Iɰӑϙr8}4bZpU4ՍD&!v;@\.޹Mî+٣+GsjԨ(rCRI230MLLd18dgɲm$kЪ~mT]U'd&vPv.aVQh=a'2%ֻd;X_8 faGuutt NzzIOOZ^%UOXPAW 4pfdzl.?Buzk^~.yKgźf+[lvcYa'Y zmz. 8k%~#?ĜrR)z!$0W hJxhʣttؑDn7Yل=`Vq:lCNV&&q:G-QQTH>]@q:Z}C_<36_ ;D)t K(x<p wM/,W^^-cǎe\h!11y!niێlVͫ[WUs^?n5:FZ 0Fݿ}0|]cQW+;n6oSN$&A;vli֐VQ_hѢtlM^ɩ<V+n 2s\MHJ@USL_v% ;9vcPZpWh;|45'? pxP@9rgd)| <#S1%9;:ÇmsPwl GSOU|qX]0zIPU]1jVW3s^ aG)-\3=F)z >*1{.@(q/*(p BLJr*9yK apfc#'3#Ӆ. SFh -QL>䃱NEݷ =+Ҏ]TuG~G@@A5[ %M]V~(2^ da Cs#Vq# /4?wIJz NJ) wV .ȴT0s#Mu_Z4=<Jkʐߕe0l,MWa2ͨYtg !$xB[x.] ?̝Nt F`@lbPtU3p)Jj*(T9(zz"V`8yCH IC8sݹ;l[5*J)1 bɿl.:W& 0?y.WOI9`V\}f mm9b00I֛}{6`ņC,ݩ^~]S9K&VݓГj`x^= nPhZ%k:s0"q,֢1* v i]I[3:F]qi* II^1k¤jŞx.WQ' sQOˁ -n ?1uk뷞.{0#h /89Rӵe}vFF:~~~LKL5K[jZ0( Jŀb i;ruN^ԹD =i^>?[R{HBon#iҕ$<&ZzW0=}(Ƕ(fOŽc飞s='6F~Etb( jaϿҕ &E 23N%~_dѤ ];EB``6=MQl6!!!e[YwNgv7&6?f_]Spөs*Űv IDATufJ =*ʑc2$!v)6#]S J =a({~#I'(7XW{YZHڵ1Lc1wc[bAvvNR _:\ѣ=uNݎt*ݟF>Lۖqc#$pZ͕@-S"i>͚4&$$? =R&]@"#[b2#{޻G7}${5kg?H9u+]ݺvoޗWgB1<Zh?-]u4s#b82$[uht ·54 W !xgYE$?A>yxbf_9m /F+H)096'/}r=O=ݙn kٟ?^iǴ[طk s$_p d핱lO {SQMcqVPk+;+?15y !Uܝ;C)L9ߚ9~v3+Ҙ+q *j4gғоvn嫥&h<X }e~K Ŕ#jr;o, @[Q42q ʂ]~Gnwnoɋ]! amz+w׿R|]MN͆fTNmg_{RpdҘhTl\˩E`K4Sn,\f+&ͅguN4-/َmk)Et#SzB\*ec]ؙ .7o>>P`1ɪؿC_"O/ E) }O7.d{e'V9-z4vdB'y !|B`׻_(D [7mggu:ż>bX;]\b5YI%7a[9o 5{l-Y"B!*tK3F͢V9q0 z|;ԾZDzZ\g5_#;Ң},KsrザG}x:Ϧ?Сx̄/v)}: 6wzom׊^_EԽ]4@+iB!Pz覯]D*/r>ԫ[S*^*!cqxP!՟!BHB!#*hDަFQ*B!$ʠN2"ĉT B <2٧?7'.. UqqIs = Bj$UP5oF߁INڌ#.hNp= äBBl!mGv2ˉ唊B B <s\n!E>>>R BH&wR!Htދ&ZHg͊0Іa r!՟8ڕ`}*jڕ4Je!ԯ߀kn9\}x֗BqYj֘Ąr/RUUB\F?:UB!x2W0h]J_5`Qz-/BHU*!x.#zq+g}iIxd/?5ɮy'y4$yoO[ʉ7@.k8Ux#H!Q(;_f{}s_0gck+t,8'5]!U:'`d|{R={|ϻ>EN2^3ȈH#uī,;K'10:stG{H#3fVҴkl;k:EIԠ3 3oOk`Ւ!O""wbq~]x>OCP_|C{1^z\^]o{TЂsdW cuj)(9ƕGGAQiqt6jB!8co(q\ <tgo[ػa7denA^iǴ[طk s$_p d흹WbVrq/m8SBԷpߦ] M1g9_jCYCbhR`.1]}0goyyL^F.o!x(v:l.$ۙ󭙑wޏYKԴ17&giΤ'o}my{j;^O~Xkdů MD1eH:a;i'BWN֎bwt̀ e!V4ŕC+tǔ*Pv>Z`À0;֠V_=BQ\GKXoeƪ:_WӫfS>ٗ?gŞA+Y` ++c9l9G?uE7SVL .`{$a|sk~zb7;I jtf;V@Y+)W <["^AZ!h{d)Y!xN>̀ڙ4hcT*~GTdŇOc~FDB!ą_Ϡں+nՍKMө; QwRN^n Fj׭Kמ}ؼnME7wBxYsCdTEQ0 lҚX[՗І0AU5GjX,VzPauQP~jڠB.t~ fPMSiب1F~ F@FxP cEAY\3WwBSH|;TP$].[7$I.ꆄxBqB!^+eI1ͨZ)ꢠ,B!D !j3n]Е9q4KKY.{_c8o3_ޖɌQW3~UO~'mI=ev3+wrpLϯ#]/=yy>Zb ~,7Et^t)>+<3)?|ۮKt^t37^Wݥnz/fjilEt>{Ep`ۓW/ d>g{hgX^Dw{UNWτҋ+̵IdnRlUw _CĒW<zε>9G!Duh)/>7^ Xj1iϏ{qOn".OܿĘiތaN;4U=csݾ('6Jao8?]\pgojC.fӪ1$#ش;>̬i[ΜX~8ʹo~e◹6e6lUESs)=~d{ffq\'_?׽E+{Z5s&rdgsaV>ݙ[KW-Q=^>L/O1uQjGÿ4穧M+,B!D ousE!RX(^G-iJ %?[V/c5׻,OEV嫹/UQ?P88 NwѲjie,_:W*n@OqƓSR?|Q J9ٞu ֏xa<98 $|J?NՅg\( :{c3}Y1K_BQ\Igt(f-܆MG4S?Gk̙^gCsy_|ShKthwvZahRҾf,_-S.ܤ|'˥|˟k8].{X"r_En7hNl9s+_66 {z)[*ї4%9+,Z{ymʨAcy`}vPU!jv#A:NSm`Yr3w N>W:V1Yqwŧ<'MDS}Kۘ!ۘzˇ$8}p7o?EDss(CҞawMS"z^'ٍ^{nD)sk}yduR/o|M9*S]ir@BxUynyr_iC/L/BTs9Y7|]Z5 !{rsd9E!՞!BHH&[ !b\Ca%=Wf܄{L9BB!(Uvv6y*xxhmNaGZxBUiOu R%BH~!!s#:fYf oB!/!BxoxΙ!A˼uD$0ŗ]t z$<gZ FNNGD5fBGIx,ȉ]kcIxDxe'ݥ#BTSV)+ ļvdļس zp<;-ޘ;^@zA˂=혶x vad{ ;˹|~wphߧ\ZqYnl[‹xz R4ބBQϹBOyNW1mV=p[y{Gw>w=a hƐG&y{rbԑtb w"O\ާ9- c֒w85-FL YƁ7!BTKg̞=̰S|8i oms4KԶlJ%>_^Ӗ@Jџ:vc$B- *S>ٗ?gŞŖRބBQ=O3bROP.MaH0ơyB3)A4 ϧ f>{Eel+Jq;|̏dԱ[0$? !Bkֵ ZSiM}3/ٖ^؂#[_b٬9ÕKxﱗPG}]\?vlj-̛6۳oB!ށǧ?:?>0{4-NnIe9o^;-:{җ~ ʳ X޷M篥MT[Zy_M!J>-ݱO_MBq9~OKo,aOܓ[$`W#-fs$'T8hvp]:wEf-JLۿo֭YɤDTw^?FuC  T9ޏROum+GYb) &aF[ IDATQ*EETNg_Zmo@|\,~ao%iFwM6ASwSIejڕ4Je_um2r,+MqfsH-鶑nq~~~rTyޏRO' <֗ 1ɉ '&A%^$&#++K((TzRG49^ *X܌o㢪, 3ZlH.eT.Yjn/KM24{*m}4++r'\L3A~ :(g˹~{玦}ZMUeۀ>!t51)V7/z>| ηx}퇏D"z+GZGEAQJ0{;G((`{)RUXMEI|9+!*EBIk8_\_ºVQPIu:|옴6491im\gRI#85/$ 'OUD[ʸ%qq'#X0ZFD8 G-`S`D1~=:D8,OVQ4M'yGs2Faj93JC1kSh= &[_-;JWCynQ}E u 7.g4ǯ}yHSݵj4F{6PFOFt2 @l,He{qdn|MJa҇iz{rao@zi!3OOmͯ>/KɀxzX^/ؘd,rs׾ܕpO̝2X3rlv-"%5sP#ZƇÜ9{q v?sX2NyM#51D&rbE}P)&:<0+pЖe_0j|[gW%NˌPpfWYԦw`ꂚ]8ߖ1s'fW4Бd;KrJnkk&:}՞f'm4n[W7 #*~o2 ʀf'V9Ud ?.'$bdԘ u*-|})W284یi yW,G6*!A:~=k1֢iSi7 GV=nJ;{/O\HxT:4|* Mõpl :ٲ_"FKgxy'3~ppqR؉c=L-cǏ张>q9bInWs$,]G%5Or=^Y<՘5U$<^f`X\V=ye9I*Lx\vv:_? *Rvќٗ[lhSߧXlρgpwLn㇪h$ޙSdEJK?ЄwjRY<8wjpV}fZׅ]}ܕodD˝<!v_o6 ( ZNJB3Ptzz=_фJGW-K|y14eXp3z㙨8콌5W=vvm23R=~ f3ŅX=/m _b” BČl opޮOxg&2̙5^W|͆ ݛs:E!'7jW  dPuLןͥ³LxqklJLqOӥe雯2y _v< ȗx8Gx>Q5'2jh4JMbaBCRTJqx3*5-" >^N}Bq3D[*U鏯h]`KE!/ϊVl|v]%`X/O!$B\uyoC:E! OUXqj5N<0CxᰣV5j6jMՌgv;jM͟&q+ǣGt%] vt$Co cI\*YK"$^%TBTM:<'Ix˶M;>C\TuWҭgߢV֭ p9^Y6'bݚ/ԵF_y)SG'J!Z5TA#zǏ~MTRУkШ¹_-fSO{gQkҭgB` I8x~$h}hٺ=-[ۊ;ykЈ+'6 SO-Ix,\,鈪5î>l6vU*jժ}bCNVWd%$$ᩁZ-ǒv2O%˝P>(.i4TY|Avl!T/tه-Z|ZuH%D]HS 8MQA#yer:<̶E׍iw z7MySG#'v &Mb7n3!VIxj떁$%'((`o#EX_lAӈ gݰ#F_ j44d= _3Sd`&U"=5#~0SS~OM9Na\U^0RSsфGg\UJmFΖVIxj!ū5vG>DV=/H)O#咁U7` -8+} -}}O3يuӮiX`tjUm+nObE$jXj#DM48pB捱 S!9l#IWbB2!fF]"d*3Po"+VDQ.~sB}A}궕BT?Iv #DZ)Ydԕ ۤ5-| ss}t ؘb+UutZ4.prv[߭zEAo˘wpYN}fwE]Gz4WQP_?V̽SUjYSC#xLmdقGa<{W/d(g-y(wA}«ϕY$N ! 5{__VVflփId'Dl?1ݣvgY1|wa[q'~NiapS`ٿ \ߣkI̶5=gw׾ܔC&{G^柼c7.<;1Ihk>3{T$:~|L>vLZ~|j霘6uN3d( ҇uYURu':hi=xKfn1s%~Ul┛ÁBk!IeGsoh԰?-OH2Faj93JC vM6Lo3{ FIG>L~UzSݵ<(2n`iSn2V8w[zxrȳZr+rK–lfMvN6NsX9{i].\kPNy@ofp Dyok;m!n^1."6ߓd<+o=UbW@HsU9OD(qK~LNG6`-qD:Z4{cznuqDP ?hс&.cOMe0.y]8"ț⍟ іQ8K^F˙*5_ӧO} I>mIO9V"|y.o°(3`F>Nk?E#bKlN/CT}wמr}7lH ۹D~XwrϡRJv:|G?AmCq Gh 5# Hn[Xq9ӆwI=>~cDFŃ\|9M_[s2%]Ѫ qOf%kd\=LՌ|w8α__x${;O< $̎fƐa˪K\+v PTo<.q'>QϘ8gLC|WDܟ[hx6[g;s++~X]А7WT叶 f]q $z8V;c Az A?sVYV׈VS>1K}#ytoGw=퇏1̸,mY w+g'XuJmA;idO`( 7_?j=Oe |y mףFƾӿ-cO͠(YW:S3lgINI`mmyߕd{"` EC5d*73>j e$|xϡU$UfY@HCE;Tmz0߀cܧCL~]bJFfL6Gv֧ehkgi2Ƈ}V{Y9mGܴI{-ׇ%/^_l_]J$|ZEAc4QT & ???-& Zƿ?ƂlL}W,Q(.(h71"c8l)0RKǎoرC{k6Ny6O‡<|z֮$t ǁJ0D0w.+}ž<2`Ŝ$&<.;V ~y)h+\=K|xG2|Y,Ē9\67jig}05CMdoe[8};&CU| s.CC+!0vg^X1.ھӃܔx8|[pZ󧈺+JEAբQTtzjN^Gk0b4PT| 2?U\ )8iW%o.i6rNӗs{'%%߹B?WoZSno헞BoM[L~iFw{O=zJ?d;h= p=wa

abP_؞+ѼktRF| ]Nxl:mwmzO#Z w-)GA˗M̗:MWyX^̀W.o_.ھ]\!|U1..2339w.|RRN]J=j!X*I>T u ̖n Pެ̠Zyo2 C-` ׽ شp@m\60ep8- ݀ch-p&& ˣ%j4Sj@~0ԉ)?36DU@үǁSzt/dfz7f AT*/| S'44 3Ȼth_c~] D&=39K7 s"*5jĤÄEH=BfUj~7nlbWҧ93 pk&^Ͽ8_bڕSBT2~e雯2y ߗv< ȗxhW}~۳Fu_ PhZ@fg׮3?9JjʷGWiV@q$jIoC:&rB! OVq:^pQg`5v jݎZ#3>KN/ QdY# IDAT5!KxSSǏ%*Ӂ٦6_&Pt"fw~)7z$<5FCiٺtmmE7oђz $KprRSw$%' st D5JSCOʢjF9q\]sfMQR[oFa205( r2ؐ5?J!'!SZB!G!B!BIxB!$B!G!B!BIxB! B!$ӹSG>CԩÆ 8{lˆ SΥ';B!W#)LzZcǎeǎfǎ;>qO7-9KZ.!>yb74$<cGqU?w=*f鑥reȈd ֥FØYѭ] |AB[GRHC>K_n12nX ^ =,/ݺ KxG8u|hLa{An"zBqTu% &W.PShuh6e_g䄹6E5'P۳rYSXdJ'\B>ѬD{>͸μL;@'-uSj}ay n_2sA^K߄BIx.LzF;joPP, 6(H2H\@Hz~96^X|=tsƷ.mIMީxNB dn-Xԧhx77'UW uB!j+Ν:r=Ui,CzἏٓG^,Iy[2rg,lnb3s9ɚJἏ1B`}aK;=`GL OKr. ?B|45A B#C`wxKҖhn\ jA݅-c*tN㥮R37ɽҢHAu&:N']Ң#l;`$7ێgf"iղ57 |_a@!Dͧٵ_HMo,b5H !e雯2y _v< ȗ !擄G!!BH#B! B!$:rqrrpĚyMpptR6oƢE\\.\.NgA1<󴊊X#%-G&,yhщO~NmgaoE0uuKuvݎ΄z%9Ds=-7!Aj)ٳ.1lyyyEX#DuOxrPG^wn;3`+{1y%?ńmop׸r=*".;8y\\N.Q0*FfBjryl^\}[`)L[EztMQ] _#F1DgQc&XXȸc2`0l,ڵ~~TE,ے'f$:Ԉ>#deOފ5nEq-$<>E庠\oIF]&;.hGQcHI]աqF.ّ!'?Ü1_hjQpS6Iy%<΋';2#D j4<[qBI' fC7'K#DMHxɼ?d䑗>N" !!=sXJv瑗;gLNA,o=hr:@/TIϷcN͇/.كz)H#DMJxӎnX(yF8m/#l~e:`F]Ov "!5}B6ZAs5HwɎ$77x&;g}ÇJh,}U&OYkҎ'1ay'j T%Opp9:91@VK !.Fl6=x0)8Nrrr$QK3o"ZBM^QE:MQi}z(SbxWϾw՛ԴSn+BHptnۛVQk Ok5Mpp|g FkD(!BH#B! B!$ZOmm?ZkcUnE@MB’B'~ I ! ܹ{g}sH"   t|   p%џcUEemU0~UE0Nj1.@ UQZ=O75nDeH+|gE:^eKK}̰2|:TW[f#⻣@^* )9>(m|fΥʞq#$әtUUQUW$IH,)zs&IM`$Iב޻﹪1S'y-v[qWʇQPfEU#o/UU i4. =5YlgE$Hv;p&w$#q@(( UEއ:h$8(шN՜,X- *4<⡱É:}Uy_(:NHLg0( JALD.Rk%٦*F+9dYgD#!iE "Kr/].'No <}A|y׼M}TUr:]Ӹ.AIYk޻vz37 ӂz^"Z5/[kw[=tO6{A ?w =-n;RӍAPR7CP<l E"I:C A.h7R \TB#zuqSv[.0czAAQNtmyAA芈>:Q e jAAhah> JAԚM BF{! HgzR:\/m)AJ'q3 pQ,n@=QՕ,IsUUt•" t/H7zϮckٜԁEA.RA ?$r8r(" T$IwdEll ,У WeeH$Pw믲oI|\eYAHIn9RAPAbi *X${Ix\+^f3 )N^HKK{w/IrBBC75VѠf?{z_N<zO]=Gg$͆քA$ ut$ 8w^;LJeUeeeLG,_ yD >c aGO[o(A1n 2:Y>ǐ x|:Nme+AUUz}_UUEAUUdY,wO/\.$Im󣤤`jjOJmjFQu:t=nc!KRwn7!ux|ABgSop800av[].7GשNbx|yPs( . Y}`bv**~FA/3(xl˲s(JˉAo *:(>3vAA NgO%t.'ֺ*fw z= aP.G/`ǎ$''3}t2beQ MUUʾ}Φ뮻cǶyvؼy3dff-;4mϳ`@@ۿu\=zM6qIӹ曻8N֮]ˊ+ vČ3HIIR^ŋSTTD^6lX]__Gvv6v"((q1h λ/edYt~zF>*FرcիW_ͮ]HOOgԩ$%%g-[ƩSx:th΃pBRRR[HKKC$dΏZshsYX^ o'Xx1uuu2~x;]z56m"223f9Ga̚5!CzKNR 88I&qP]]͛nr`̙~]UU,_R&NȬYZ5k֐ǠA?~< V++Vp 9Nx)Kt;tpiL4 YW  CA.]JP+4YezwHLL_~-.T:G}Ŋvjߎطoɓ|$wg{7oǎcԄu$I455k.֭[j{NJ+d׮]st:}[nqi=Ćؗϕ2$QAat!0 &𵅼9F$q/\JUmկGx엖3b{yy9k֬|撜LBBBUHHse|ϧ5QFsA]eFo… [];].3m裏2m4_OFƎK޽3c nF#V"''rbcc;[:k,۪l'|h$##ة@'!!^{O?%K$H8p {&))$}ҿ[lwٽ{7ӻwo;Cw' g< Ó{f00`|߿qu͕[tjkkٻw/ddd wo>|/ b…D)±c%DEǞl^뽙0aaaaP__mbСC$&&vŵb뮻!CP[[˧~J~~>]*l63vX\.EEE(3z=YfG??nLR[:;=2t.gx0`F۷@UUѝ!tvMMMl޼_Mqq1Vb„ ƞsO>Ȳjѣ9rS6/: Ƅ (++R\.Wʖ$P<v;2>bIAAѥ"ǏS poZ\rrr;v,#G(PZZFSYYh4ҷo_(// ՖJvލfj$IjBIUUϒ%K?煳d"&&2YVnwf-,))nUmUuu5)))=#GR__OEEEf5js!==."x<NownQpp0]wR԰}v|kօ`޼y455? ^(І|w޽MII z뉌``oDJjڻ #99<>1DDDtKm6DFFvPҠ XnG8vn;v ^O]]111H+ Enn.Ww}T&NHHH#F` غuiZ&==SNKII 6mbpwt{@UU9|@^^L:P-S4ulڴÇqM7u8ȑ#^"0~x&Nh)$'OMTTTK;wd͚5cX0͌1h^ d\{DEEu\np|gٳ-ZĴi|>r$QWW`ر.ۛUc͚53h &L@PP3f`ҥ|'444:5f͖-[|#NJ8R߯:յ, p\p9tE% LKcڵ_dʕu]Zl555\.ԩSJ{>~~~AUUSwM,Fyy9viӦuP>omhVpp0^pRRR(((v”)SZ\J~~@EFyM6n&$̘ѣ,;((tٿ?&QFu)bs 2=z4`gΜ9]@dd$Gxƌ{;KQT_3fPXX5\Ñ#Ga 6S 8<55581LTUUvl1n8)..VUU_hrr2. BX-6{/r{Vm1ی ***mONNl6 ..AT]]N騨pZr :.bf3ƍۍl&::ǏSVV'Nd̘1.Vłdd2QQQ;dffi&_vvYQ*++Ae|UU[)1ݚmTAz^PWUnT9Rt??DE`h4RSS^;͘ԩSBƳ#:kb!((թ( vY%3v;@:/_ή]98:szH^cb<\sC`A@HBC.z;z*&@婓Vr21}1wpֹΓ8v ,n#4Z;,1{o X[NʨҫW/n,])ro\w~DEE!!!]M,cX66Zs3:pPZZ(XX-L=25R]ScZ hԩ ֯[Ka|_fFm?|,kCTq^ Yӽ5z\~KHH߯cQ:UyVNenncj4R] \X`YYY\#w(Nl IDAT v+XŚ/=LQ"C;FqP=)/DAjL*KU!WC=߆^ݻ •jԩ].cv;p-he˖f~ӟw<C_]ީ<|׌={oɽ{t:]ݻw~z=^[[K~~>=z3gI`` uuuֶXgЛ92{رc;voP!d2ά(JuvB$ILsgSU<m.b6e3{B^[d$Hppps9E-=w+ipƞ #/AG?رc?_|9Ǐg9'|{o |Ax ;ƒ%Kg7$66ŋ[o_/b2:cUWW?̔)S2e۷m6nvF:lʕ+>}:z޷f?zÁ`RPP_|biUwΒ%Ko|bVoeCή8 i^wOi7ڿo;uO^ H\utq9#.uAI'$]f'˲o/Nff&dffr-K/1iҤeL0Ç~g|Ȕp7vy $??v?ÇSUUŮ]رcy͛73o<***| gҤIEEW^_8vK::9s搒֭[xɓ'?ѣGfT:z̙Ç~ȑ#GHHHhQwOj7zx Оn]Q \rF\.n7lFE'--b enW^o1i37n'iii̝;NǦMmw,L6ӧ3vsGMM >,<# 2ķ"HNN=^\\LCC8p8L&cь=x_~el6VZŲeHIIa޼y-.**b o0fZshUwOj?Ĩέz/ ):&R7AxKQQ\222m`455Eqqq8~8ǏgƌV=P!l 2m۶1k,jjj8t&,Z |I#̙c=?p N8#;ݻf&L5=z4޽ bcc!//78̙Ӫ\V\``L:׎ѣGSTTDnn/+m^x_W8^~e !##,~i$IjD;x<\.o&/ت0BCC},_|yyרodTTTw߱tRBCC;;[{nvE@@;vv̟?|^}U$Ibȑ̛7믿VR*++[ݓt;KAAl6@DDzDNzdYfРAš*~37'11)SR{K4enV$))3j(~'&&0N~Ϗsw0rH)))aȐ!-2TWW3~xsHbb"ЧOZRSS}s9s&$$$v ko_~Opp0̟?*f}S222HOOop뭷x;EIrr춀jsj%77M7!dE!33~a0Zs lۺ EQ !..UU}p)3fo˅Hk[ee%YYY3e_(-춉 •NQ m0  _/^ ІYVv؁fl6Ldd$:tPILL$,,|l6&L 00=JEE111$%%Yt)o6)))dffMdd$TTT@bb"111ѹk.J0F"##q455*MMMTTT*6  ΁6g {{6~-III0vX\.TTT`0%:&Z$A*"A肭[/Dll,7t3f 00<^~ebcc)--eӇoz^x ޽{O(((@UU cy)//rsqwǒ%K8x &koqtSetzz}2uT8:DVVVF-ß=zş-jb28q"lNYiMMM4662vX;ҥK)))ߟ4N/+" =OA@.t:P]]jEQpbJKKϰll߾[t: N:ʕ+ٶmz|AXM6a())᫯b۶mmv:j(S^^l&((jmƮ]| 5NPTVVQC}v0 z n֬YÆ $ Ɔ (.. AA|8_[?F?~<;l/%%>?8q3gzj|CfΜ /o[&LhK\\ӦMO>n`͚5\. @||<{ocQUÇcJNN?~. EQ|{+nmŋ}ss$oOB$=ʩS@#Inw(_~fv;N% x:ڿ?˗/d2!IV/lqL dee  0 ;^AAl J W_}ٳ㣏>`0@dd$O<۶m?fÆ L:EZ:!o| ˖-c޼yőjv<&!Cp 7зo_N' EcIoTb ƎKll,Ǘ<Û%B3s/_b!==r$ EQPUJ]]/h2y睄p88pXXWA| m0`&Mo?g?˖-###'t:Yl k׮PBBB|i˽nwޔኢ 2vKgQSSCmm-lܸ_1|p999lذwf…-GM6>}O~~>ۗ)++_d֭(ÇYbncZ1 l۶E 7܀f#??<8 A.%_~ɴi0׏g}s6UAjZ;_!:{ `NB]~A=OLL {ɓ̜9z4$I"66}w^ }ǠAe^z` 88$_&)Sn'993f2o<|)((`Æ jkPu׳~z^}*C a֬YӇH&Mѣ1 8P߰Yfa2eQFѧOꈎfQPTT,z뭌7)))`0(p),[,?P^^ζmۨOS2d po>t:=/`H:w5IJ2HKKV \&jjj())bccIJJjݻw@XX ( 唔0rHjjjcİm6(9ɱcǨ&&&NGuu5ÇGUU9|0ЫW/)// !!AO=޼ռCz=7t!!!rQUUnߟ2%*:  fՊ$IDFFȩS'55$.++CUU"##b`Z `c _N'Ok$33)UUDDF\p]!(A. ~z|ANf2I;嗿%d2a裏?ABB-l6.nSO b@ H@޾};?0{%<<{y|/ fcĈ,XZn`̟?Ea&//ok'o`SOmu%عs'|'77'EQZZҥKijjg7g>ؼyg[v- >Ç7z~A UDdAh[pp0ӧO'##rssٴiǻ{ȲKq ֯_OUU6;w^Զϛ7;wo>Z$9طo_TU_~m999ȲLSSS7n?';;iӦ >\miAA͛xsQQQ^1cưe^}UHNN=<%<гms`ې8zv$Lu@ƭi Ƀ\,5䯆b};~FvpݖS?^RjyTNѨݥA}%l&88Á(0j(f̘Osτ HKKCeBCCt}X0 cZ4it:bccIHHDe0LL<nH㉉$&&Fuu52}t+˻fo߾ 6AHJJ %%%X,RRRZpL& 1yd_omnrY\)7t)WX\y\ErG?$]u HmdR/smoKc 6~ml,O^o`O޾v޿}BўgG£ 0?1<]=G ޅj` 7n+Z#6j?.ښ, BCիH~ \f ?us[e\U iO)A)8Rݠm;Q_!"nCTWo i-`:'ez^n% &бos9n'v(٭^ &j>%h׏~ؾ7D빺kJ`SPW!1ZۃӳW}L/ۧ/ym[]v\PYsA3Ϡl4/ze_P^|Ӄaϡ` T/WjZiHp\<;i5.:< >O ^3wj 5v>k`b&ggP 7 l!Y`Gim;Hڿ˘"xTA+S \*aQ_,/KfxK3},8j+UUvM'k߬iAkᾷa3>jP:@ zGw;tzZ`gEX(ޡm[O-*?y;n[v>o7hC:+Z]3ly~}aaSAn%zzv}#nӆi #o׶~o8z@!޻ߙ9>)0Z]R{qCAQw)X5t-oyIdd8< IDATO NNS-ߠOZʍZ9i9^&]˦ [ O`oٳL  ݲ>OGz=utk6O*2E{7dֹRK464  W+ GÙ sҒ]m_ ?sZJ궷p=uYk<%8xb*mq;MqpS0A-BGpZOcOQspǒXshzv  5oעt6_4[ v.z Zm 3vmMSVޝGUއg4wy ۀ &,e1|MB$]4i~_$-I$!iI{`bƀɖeɲ}Is HX6&xş9:^g>y&0z@4'=,K?άMsF:ϝ4ܖXڔF69DqkO };҅4ZN"OgMW2ؖμo?> 7>\& X: C1wuH^|C!grLgSa*59K!B\t. p6O~/=gj5&x6hސS3O{ô =.*lag?b3̒w )1!҅ }-\X4zSܜ0aһ̯zwM[,[9Kk.K}*o2}oW]{{+n R&)](Xާ/Nв6U+,'N@q 70gΜSrqq ;,B\Eڳg/'ۿ?O<).7b8p7=z뭷x뭷Ν;y ֦]͛@,#ꫯytP(EQ~R#BV~S)B("LjBk999Xڵi_)PV^e|(---|39}߶m\s͌F nb -ZDNNǎ㩧b͚5s=c=Ƒ#G;+}AT8fttdR !ޥ( hOwMg !efѢEرfJrrrhoo筷Wc6ihhCK/XiayN8AYYDZX,<3>| ,Xŋٿ?/2 n{hlB__f644fc͚5466NҥK=ݻwo}K{?7Lѽ(PAL&|,FQq8'?/NG<,fɒ%߿뮻媫:m꽢(۷og޼yڶ'xp8L2R)i Jػw/6uSOfÆ R|G'h'[ru1::ʁXn7ž}ؽ{7O=iqxx+VBryyyھH$"9 AbѢy!* 5lD^gbbBxBWhٝl2~?Bmٲף( @ ׾5mqq1\s N6x z)y뭷xG *j*ioorlٲ R,\L@^^k׮%''o~:tui}e\uUX,ơCWl6kUU%85>?䓍QU˓UUEQ >7IQQbB !.?999,X;vpavASSӴҙ0̟?nA~ݻwm6:::=鼽{NG(bھ*~߲sN֬Y٧XhllԆNU]]Mmm-Fld:D__ŚI* s}`XH&z^F A< G("77 Δh4bXP@ 'ɞ:A<|^~_SRR7|Ƴ߮B36jiiaΝ)z-x0LӂNi00O~d"0:mjII 3:-3u0Rq^x ~^x7l߾]{|yO{N4f {vvl6ȳ7m-G&mlww7x^)++駟FcXaɒ%X3+@RKvv6.BwG!8Gn9QB󢼼/~ 8@}}=-}s^uկ~G,*2::JFF;v젰ɤ-09F|r dVpR<gժU8s=Goo/U'W/..F׳w^~V|bݘf&N!LO3I;&~b8Bp88Nn7HQ:::ذa466( {% 9gΝڵkioog477swկ~@WW?@:2cZq9M8Css3ccct:x< ȭJii);wd] t:1 ٍ" Bzr4f}"B!yx}D ! Ȓ%KXp!vynJFFgX,,Y˅fl6L&fΝA4% 166F2Dk+**{8qD~H2$33|-`~x"77i%BˇQB"DZZ瓟ϊ+x>iC P*3LR\\Lee% ,j.K!';;Rønt:ݴh,\|8LB!>R$B'NW^ntR233AV\D"vE{{;zl6\q@lf^O `ppÁh$iY*ʑ#Gط\{aFFF0L8Nt:drZeVXba||_|g}뿾PS!QB"77BnVmka (**r166FGGǏghhEv<|_]vi&.]JVV\.(ŔוǾ}exx5kPSSFqZQ`0Ȟ={$H( > !“ Jq^deeqWb9vdffl2,X@4h4RQQnbpBf͚lfΜ9a?NYYs#J100@*bɒ%D"_<' aXD"b1 XbFp8?>999$ ҆ bۙ LRWWW\q!oB!. NAZa0èJUU^{-D\d3{lTVVr뭷zlrje'TdddAvSTTN#77ۍd҂LVXA(pHUR!2&AQӉq_FF999ZiL&Ln0(++mrͧӁ֗ LfyRDB!7nEQ[n`0zdeetR̙(7ĉt:馛Zlٲի477qS^Dvv6F\t&\zYkS\|$B\F# ,HW;CW^#(.@tr{sBC0(¦M,_W^y>hooct EQx1vmZ?+dZX,vsޫW_} mĦMF|c;k@˃QBsFQ3LF##L^ 3YI!>|VZE]]7n&p}1oz/M{b FFFd21w\L& ⋴טL&nfx^z W_mlT$Jqqh4n:֭[cN~?z+wGcc#ׯgΝ\.y~iQ>OC8f…_۷~3FGGٺu+W_}5կSn$B3X wVǚKpH$2|g>>Op]wŴoojAd&|祗^ⷿ-_O~8;✱fLGLt:^/`BwEq a~_裏N~/EH$X|9Ǐ׶M f:g&EEE<<G?b˖-'?S7L&%kZ뮻zRF^[V_}ھ-{饗x1vmi,.^D !)ݴ_IFН\rDdX,׾5~c2Z$ d2h"V5vrd{SoQRv?q-a߿#G|E;b`Zglwdd\ TSz<mfoI(!9o '?gUEッ !Gyy%+rrr vFvm x^|_eeeLLLhs*N<0455cnf>pwSO5388$ zVV~_;.++댟C\\.%BJQ`}8x/G>O裏N j*++& qBTVV__A"kx紿?0?0ׯڛSaFFF?{vLKK 7tܹ{ڵ<ȗeooYlDzz=V?i~s֬YC?Lgg'N{w|;1?OYz5/@UoO~nOyc=]wWV^}/4ߺױ}}}M !.O]سgmmma0(//'??kz-`m۶yXjEEEޟ0̉zr]!gb= Bs###vihh LpBn n222QXmbTtwwDZc4I&044D*M[7 I$!++T*E4edd@ @0tRXXH,^OYY6y9сrQXXnK0YfiBq Jq^L&f3`^OyyVvpp_,]@ bΝ `4q\̞=Zl6(w羚X,F$!##rFGG9t===$ Ƙ5k8tPH ΂ ZU]]dhh{( +J{{;[l+++)//fR466R[[I ! RXBqPWWG0N?>innv`~?,X@FFccc'cbb:.]JEE---q|̞=۶m BD !ι@ %##IYYg>k׮GAz+V(̈́a/^Z(۸n `Ϟ=Z 5uq_^?^*~7 >EQ%HPPPʕ+ɱccҥ455rTX,1B!.QBs~rQmndAV+gʕ8x<ӎkiiyit:z^DY,"`Ǐf,[r(((FN'dT*n+VPQQAOOG%RZZʕ+q8p{B!.,D !ι\*++ٹs';wdxx2 d2I ЎO& ňb,ZݣV IDATN$駟fڵֲg~`Xp8NL&g֭|FUUƘ;w.˖-cbb@(..&LFZ_V\ݻ7ӺP~6mD,#jϮ]H$LLLrJvB!Y~=YYY444M2@uu5֭mjj" b4ihhjrqL&NSM7DAA~2C[i`|>jjj={6K,fсbr***bppAccQBqxY~=>,ԧy122rz(.i" ן^ !>Z)MMzzz z~8#:xzdggkAB俐 řf$uZ<2ủBW%%%@J!E;6':|Im>?30L< Sq*=%%%n>O}, J!Bwt:6o/~ yW}㴴Os]wh٤_yӶ1Æ x嗉lڴ]8[dNR@"jP{+!y<q:lܸGr7j*222f`M:z(Tm'҅jkk gY?!))V2a螸 !.q:@*2@al(& u(x^BЅ2e-+ֆ^vb?!^ϭo~eɒ%l۶ax7Q?(шf;[B|bf fyJ|#D޷JB W;3i/^̶m0,Zᇵz!:::'s~M7NQQ555疬%%l2|ezR~bWŝaJu3b~BwₓuB\udd?z21>NOOLJKK/`fFgެ G(.%Yl!B\$⒐5=*J12<¶g*++k(,,ƅ,_B!%rO#%o3ٷotbϞ=tuur͊ -Hˁb||ck?68'BKdļ rmqQ1LtttK<b0::JNN.!&8N233Ocll @aa!f@ xFff&&H$B4*Lgx"瞥ob$ L&D p8`rcg鬜! ;BUUxd*N"h4D8999l6R`O"nt8  YYYXV<HۍnGQ$@@ |g}!‘ JKfv( DJSSDT*ţ>ʒ%K8zoYfq hllddffN[i&Z[[I&4-lojjۃ ve˖1w\ؽ{7hP(W_͊+،7dLLL@Oo/dϞ=A, \ M&x7pp˭Ɏ;8qv;6KRXP@{1^:ųfC `\ z{zBݙ\q25kpAb̚5c SUUcll}Aii)+V 33¡, EGOO7oڵkD^^NCk; NEEr ###l޲ƅaZtڵcTTTIoo/7ٟ ٸimpD"U yyyrX;GqFBqnI%%&??+o-[-Xܜ\Y} 99N#Lb4+bX,iYT*śoC=㝀ۍ`oDp:Z׳xb|MFFFAh"]]]TTT`2tAH?F z=T **+)//pzGZ[q8M&V+*vz2/X0:JQO^^.Td2ydvIbSʳ+!K.e||͛0<\oC! ,\uVZZZtgr͵װ`LfV&|R `~(((`0pa"sW_e||D"`֬YXVz=< &@ Nx/I<yf7ECyJKJt uDx`0hkMtLz=,z;r(-FQ0L3VSlk*=1^L `TQ:@qq1F=J<~ <'N`bblX,҂nl6E^^VʻAjjj(**رcfHR\qZp8XhFqz{{7oCCC  @ϧh4A `֬Y444h$p8(++#33^18qf͚Eyy9VD" *++;o'!B JqYV\.h f3N5k022?UW]E*bllf&QB Z ( ǏСCgllD"A8& t$IZZZ!ՅN;wק׳ f3DFGGtttY|9`vzzzba/^L~~>Xa"v9pp 0m^tvvb2x<$IJy7?>]]]}  b4Z311A2$ ݍjd2===x^̙ 33NS RGFFhD"!ABˊQBs.33=Jww7,_UU|>dGQĴv0###,_Rz{{ٺu+W]utww999\.ϟO8fƍRSSsΥ6FN'ͼ,_~Xr%GH$Binnf`` 8˗/l6kY(EEE455 M[[)( :ߏdܹo*+WDQ^|EڨrQ[[jĉZ) Xf a5k7o,q8TWW ikkCUU(!A3 ̚5 EQ匿S]]MNNp_QQ^>p8l6k&qʨpviiiB4X,F^^Q&&&|nt:x'HPTTDVV`a*%%%$ ̒ѣJvv6Gb4Zf3fYtRmeT*fbf32o< Plvg&h FL&'Պł2|]]]~z{{QUB!.jD ! DII %%%F233Ƚދ(L&-339hhhKff&Lk3Jv`0`ٰlR)m9Çl,[ q{J0Ll6 .K]דL&s:;;o||x<:-Xs\ڽl'HSVV֙B cttӉ`rQ__=^`z`0tR<L&ldffNk[ӝv(R)8qVa``@8:^p\ԐOKKSTjڹTVVRQQ8^{iT&j233_;c~odǏSPP@QQccc9rD {LR)-;0B!BJ !ιH$BGGabxgll\/^Lnn.CCC|iǝ邽TJ+PWW |A 8ڴ*w#CCCjYVN'f 7 %JQWWǼyسg?*d):R)y^|>ߴY*VÁdbœB\V$%8N']@ &M+L&1 ޻w/GUUgS9(7nDUU&&&f&IRΝ;X,Z:R&&&xx<A-$''.:::|\uUPgg':,j)++І,>s\.<ӮNm:X,od*^Mޓv^ӂSB!Grokž>~?gWBޞ%3SEף*hW^y˚5k9Nx@ 0m^0$ H$0ZL\.H` -0FzF#Hϧ6@,#c20 a-@B!mP2d||P(ӊA$ p v;~ɄlFQTUڶl9וhԎ|s$FѨO5_0JB:NԶl6zq(2Qv_vv|=:])_Kvv6.O|e !z$%8/EbpuסJ{&ȘTAL#e0wktjLi& ULX3@ =QbccS) sssZnMii)oiӦַh@ b%J nJ<>gt@p# m`<J @qGFc\@ |DY9*UYifff @ sGF9999=zTQ&T*-oQCj:xxxC@ [Ȏ9 IDATtӺukD@ /X * Z͵kׄ^o233S>@ ԄJ[GfDs@ @ %%@ 0@ F@ @ ]@ WDGG]bADDD)(@ W [.k,s;eڠRDȃ<.s/**:7;@ @ ˆ@ [@Q@ @p #J  QQQTJz*'NחoRT*;w/T*&NիWoFZZÆ [[[zž}CRѽ{{ugJoˆ@`BTT$tR}]$IR6ggdd7h4sNN< @hhh݃fbbbPxF/¢EHHH 3gϞ_zӧOsEx9r]o?33Sq\%F c˖-Hd_F@ *9vPڵ+[nѣ 2M6pBe$OLL ZUVdDmڴwyC!IdffFTaa!+Vg͚5˼y(++#22+_Iꫯr/yPtF%~ +++\$sԩS駟Xp!vvv>>u~پ};6mo߾4jHޱcG … ^#++/Beر#/"/2 J9wwwzMdd$<ڵcHom۰ccffFRR۶m3z WWWMԭR9sf$Ij*@֭여,Eư0"""Lt6ѝ0@ T"33TpuuUkZ233x{{ӽ{w &22RSSquuߟ^ϤI eϞ=IP5onݺѯ_?dddjh4$%%ѸqcZhA~~>IIIDpp0=.]IQQ 82> yG1cAAAlذRRRBFF4mڔW^y{fINNI&&sssSt:#==R|||ppp`Ѽۼ[yf@^)((ۛZl˗INN`рlоꫤ憯/m۶5QFFaa!Ze˖ǤI_σrptt2_FFIIIU XZZҫW/^yyLt @PXt:۷7EFFnnnxyyIZZ{VV+iӦ ֬^mFll,=DGGsI^u|}}͍ (//]bmm)))K.ڒF@@dgg+ݻw={bffƪU$~___<<鸺OOsLqq1ŊdĉdGV ]UVH???QT=z޽{N#88KKKLEJaa!ќ:u3fЬY:1zh&NXeGRVVV_|||'==mD6mݱ7ѝ0@ TPddd具7ꫯCegg}މN㫯B׳g._ٳg)**REЫW/8@.]9:]۶m TӴiS0x衇^^^hZZ-899mݺ5/n ͛7gT*ƍ˗' d*--+WҢE JM@@-Z ++ PVoCll,:tP~qZ͂ hҤ6X6P_|||]ի]Qwi(w>@ 0QƉ'Miii T6ڷoO۶m;v,!!!8;;|D`ɓDGGӶm[q41yӦMٺu+ׯPէ၇PG ʊGE8s /^cǎtЁ V& Ӿ}{?NNNqqq̞=m۶3d@Bvv67o&>>RRR믿8p C AAW4iB&M"++ WWWŨ0Q~cߠq\㉎gUƻmP cGхϚS\\l; %'PTo$8i޽;^^^booҥK3gN ޴ioO"00={{~VeҤIL:dzד֜={N:er旽=?~\1u놷hDM6>s䄽=Ç}Z tʄ Xf }EEE>},F'j㉋S6mڰzj:Dnn.gϞq&}qAquu777Xti(o8.;v͍du :Ylf\"((ӧOWOFFTQwڴic; aD @ ĤIt\С? ͍^z1k,ŕk1-[0l0j5999ݛ=z(@P"##M2j(СDEEѢE )//f͚EFFVVV&Jv툌Ttfȑ :tcǎ8::Ҿ}{LQCٙÇgϞ&{~m iݺ5Z{tޝVZht(lmmL"447x]*ybccQT<3h4ZhҨQ#% hڴ]՘oԩtQY3LuW_}UѝX7 2(@ 7#77k׮v|ۍo>+׽eڠRHݻVˠAT`ʕ+|9rDWUs/ ]X@ n>}Է&|g|Ԁ>ˆ@ f̘3[O @ n%ٳg3{g@pxx܋:%HXB %&Ͻu_O @ naD @ - (@ ] 윯 jxʗ rp }}*6% o_\6 epן\/wW aXuz4'Y5ylZG{ :gƍ Fʕ+ G6tt4ЬY3 DFĂÇs rssM6Qߢ ̟?ZMHH}QF$=ÇӓaÆѸqz2%%%l߾K.QTT >>>?0}DDD@AA'IIIcffڵ#<<,Vhha+o45<|q;/_ǫN lMl?/_Cyk6Bޕ뜴^[g3,`ڗuNLL =z@$bbb޽{+j_!oȲ drssYv-W\|L֭jmhԨ$!!Af<[%::ŋs ILLd;v ;wj*Z-Z+Vԓƍ9rZs)i{vء={6+RGaw}СC gggzA۶m6j,M8vZcǎq|fF^^eeexzzbkk\wqqxxx`aa^oP#GX`{E?m6FY:ҥ nnnDpp0o&ublՆ[AfŨO|w? }^N`4,z-#0I3!򴼚i[XVqE2ܸ"ci#ɲw|3 ye35܎Ee<Ɇ .Jբ;sU8Avk2[6tb5dUZ+wAryW y4Gв+W% qy7jQ ˶zsjo{P@yR)!,D!t}ht<-Tf?6B [ Sl@ u\\\9r$Ŵiޫu=Z;vTe'C%$$TϟϱcӧgȐ!L8IXt)E:tdFMΝINNV?>gĉXYYfE??~5k֘/hҤIU͂ 4hPCU[СC3~xBCC9qgΜ{򰰰ӳZ#ĉ̛7I&58C*## ^z:OV_RuRԏURwl( t'o@'ŷU2@nXHD_Ng'^wdCps#Vvɽy} 0p&Vs[gjY9y9YTߌuYO-Wf$?U6+zd_1ΥnƸ… jضmDEEVkGEEIHHGر#Faa! OjW\xꩧXtRի899ѹsg5jD֭}l2elj" c׮]B/ ꃪ&2Q7ɓ-3:uΎN:Ѯ]-\ \ň^ώ;Xv-͛7^ o֭[3nܸ*{1V^O?ڵ}fD t.T]7t}N Դ2M62TZ'1Z;@ӮJYv@{wpmSXTvi9JܫdCr+R{Hsgݼ=Ֆ569w}Qz4iI5SAlTx+hd?%SBйܛ:WN^.@pDDDХK>SfϞ͙3gXr%֭uR~>Rl'??_ٳVVVVRRRR)򟃃jhm5Y헔MC%66nݺU^&2U嬬h4`ggGII]"55++J/ 0`㏷|ҥ /"iii >t^xmF.]ؽ{7u,)'px,f ~/#GCy:c ;, Gރm{!9`Y._sgOk pr|3Vse$X8> [Gٸ0UOg /<9X/׷x^!t>rsgn yyRNi*4] 2{Fhŕ3Dn ߤUd%-?s!=)))HJBߖٳg={6'OǎԩS"++7x%=444V^MVV7nT&L)SHII!??_xUlڵ/?>+W0o<֬YT7yKRc9bݺul߾ݧQѝիWyxظqc4ۗaÆgn:ʸp>lʟ9s󉉉 ǐsSRRRdfժ.I:E ;onɑ ɮ Ӫ& %DsB_.IE^~ҕݹ\TV^*I[`eחߙ\}JNN ƳDFFJڵKzwiРA_IRIHF>#ҪUVTT$M8Qrpp'Jeeןi:N_E>|VVV&"͝;VmenݺIk׮5i_po8&F.\(J5SJqqq$U7EEEJي]{YYY˥5kHaaa4bGRw<5kH$Iࠤ?ϟT͞q|i„ dff&I>Y^E*EJ7J*c7 GJaa~kJ \v 777j}[;vpYL5U /^I&76չ[ y\n|foE&!+gOTML\^>"88 &j+h<1%AU4ַ@p_"IZm Y_ӢW>M6Za}ѣ]J gJ@ nr p IDAT\ƹ )\Jv54E% *6VhGf>lCMh٢ N7waذa̞=y;ut$ߙ;2T*CZK@ ΃wvڢR<s׺lqI%ed\Rb:wSZh}pDPK0>Asnu֜;]* Fs 4 ~5Lq1}tt4u^Q8q䓴l}#+Uʃ<.":_.󥦦j޽-&T*6oLaa!=zF!I;wM6xzz>ɆLBBvbҤIItt4ѣZ#J7$IS:[ӵSڴWPt: MJ5K!>ЎlS8r\ve_OSP^Cųy_T]=<y85oۥKk}Iw9D{wc b81ׯg_矿2UEtt2fܯn7uk;u1I87n~1F7nenn033M$It:%]ub_ iJ[{1NSz= t%%e8Γwkcɐ\ຑjIKKExuG-=%vx9vai۔ Ovnk'22k|Sߟpr潍a05j~~~4iZjUe}mnb4,,,LJTgAN޿?;LP'̟?ZMHH}CI$Cq! eȑ8;;ףĕ)))a\t"llla@e8""\~8'H||<&QС矴iӆqjٵkM6m۶XZZnFa޽lْZ1[ZZΝ͍6m`cS=*7$IBpI$ WWWڵk]7ƛ99r,h\2[KjKpeWhԸ2Ŝ;w$\/]SW|c$ƩdX^N x$ۺӣYⵄ""[bN9q7@$=_|QsȈJTiٳDs5233wӧNgϾc$S^^ ~~~>sR?冷>uEyy9w&99]X///,--ٳgZ_Lii)|Gt___rss9tbDVO5 駟: DRfƎرcy=z4O?4'Of$&&2vX8@II ǏgΝpE~iV^MIII=ߑ&$I3'l2233o^oKQQ6mbʔ)X»(ysf͚Ey'=z=>>zϊ+yNʛoFҥKl۶YFv5-ɤPl޼ӧ`nʲe(..3*bnnK'll4Vm<ŀ5eU*>).ϢR6lfff>}#FTf$&&2l0hݺ5_5Z<4jooo8W ={6|IټPuevwwʊ;* vٙ;rj>ԯjŋ+}1p@{ETTDFF2gϯ֭[cx5@*MUYZZZ[n%!!Aq.]Tmފ3a\\\9r$<~=z+c~*뺙T*͛"V:ƍ]v4m???zvv6cǎ3g|/fƌ(meo̙38p K.e|駬[NATTTqW#5@;v,?3~!*g}7dׯW^zII0UYA# eLk|AIyٓ|rΜ9àA$ 0a„[>"Ceԩdee'p1vQmYfMRR$%%UckkKXXX%wɒ%GV_ 6/2sLמ\YjJ_jՊiӦU>ԩ$&&ǰ*nߨUݻiżtI8|pe֬Y\v .0x`er{j#G/VYtСC?~;2//sss<==M أGK/Ѹ ~L<'OG)nο@hhhJkpkHDfh޼9666XYY1fo֭[ɓ'ٱcvŋ899퍳3yyy4jزe [l!//:{ $I?{ҿ233̤@鈉_k׮ݻE1yd/_Ξ={(..Q9Ts%># x {zj3f ˋK.?S^^;ϟo/Bhh(Gu֡V5j,_'Obccøq:t(cΝ899aiiI\\...<$&&syZ-_|>(+V[n̚5'O"I...h4N:Eaa!~!vvv\5k0sLzySe+~`pwsdȞ\rss0a׏~+WFQQ?#ӧO$$$pABBBܹ3.\`ڵ3l0/_9HDxx8͚5CRİA]Y ~qZ׆ qd^_ ( `L:u?11G}={ЪU+ѣᤧUٳ$$$TժUDGG PD{csssŐq &3>>Wʛȑ#GXnZҨի?`ԨQ:uZO·~Xe=7szPB#Vϝ;נtW_%++ٳgיq7g@~cn~ #`|k IIIIӺuk%]Ѱcߏ7ǑlΞ=Knn.yyyĪUIOOG$mF>}ׯal~),,dÆ i&#ӧO{n^z%~w6nȔ)SطoX[[O||<5LWlmm|77^3/((`Μ9L_d;>3/b0F`z o&FKM~R^nggJ *mggW7"UQjd> iӦ5\x1/ٸqm˶mhڴ)&L39B5 $I"##;;;?eeeҨQ#ON~ a|V#;;ŋcee_Ͳet;vL׍-[ұcG/VK^^mڴQކ'''ФI֮]'O槟~k׮deeaii#<&55ŋŴiؼy3ر ),,ҒqƱ~ziP&쬬P^^7fl߾ϓJII :|Mz-|}}v̌ƍk.>S㱳wތ1&M0sL G׊?D&NHo:i+*.%3+k?) aĈϪUԩ^^^9s۷ӯ_?LB˖-ILL$==r 033S&!r?b<<<=z4 &[OQko/Hz+Kz.aqJ-FìY0 :u͛#Iƍ3ɫػw/1C 7-^`ҥ&ƌ믿NAAż5M6qϯ6oݙ?> L>T eW\nKWVSVVFFnK[k׮)ni~M_~ʕ+J_kh?~|e.pօUgddd|%%%$%%)ӧG1ŕ+WYk<Wn\E(Lk&O|EW_}oPf͚$Im@|'KQ ͊hZKKKe2٬Y3& /rRRRXp!ݽHPvE\\O=4jHYU,**"''d5k3$&&x{{ӥKڷo$IӫW/Mưad*._?Ό3xG `0(JKKݻ7&MO>wuN(**B舳w !!ڴi=GQQW^&M`eeŹs=GOOOpuueL4`I sq%oooh׮aooυ Xz5-ZU\{`@\)++cǎbooϖ-[صkGAV3l0ә:u*mڴQ1* '''4 )))x{{IAAVVVt֍AR)JOvIr6%=:/,%zYl3h[<1HIIQΨ:x U,77N WWWF-,,4SO>$ӦM#;;,o^mf͚QPPEclllHJJQPLBJJ TƎ˺ubڵr[2*;w&==˗CzzITƕF#.ƾnM}P^^իWYl1NNN<*s7Uڵk+]z5ݺu3qsqqm۶q̙7n׿aÆ 5Lu||q)s=GIҪ۷/Æ >cݺu){/oD~n ϟ… &>:xrrr8p ǎCVӯ_?<=X\z~Q^^FW_%''?C9PRP?z:ė_~cѢE\v [[[*!!1c`aax7ߦuִjՊ\Ο?Oii)aaapٳ x"ޔsyҥK!wvv]ٷs5ONضm,Yggg q\xÇcggGpp0;}gޞ+V0l0ڶmW_}?]tQ\gΜAR1zhJKKr gΜQ7\pA Uo0e˖Ezz:3f̠}FZvw V\+<3|l,,,h4ڶmLRSSIѣG9{,]tߟǏ۬ByEj׷;E]fcٕKٽ3oM[ZZ*ROOOLA}G%??aÆ1gvA||<͛7g̙4iҤrsa˖-,Y+++f͚DӪ*ٲe _}+UxmرO?eLz6f%O?~`ҥxxxo+gkU%Í&|={ߟ.]9m\?/~}wb[/111J_t$** sss在 u(t`ӦM6sJ٥KJ1wJ{1<<<گ8| [loÃp}6z`t&)..‚˗A۶mx'Xjprrb…Uzs7̜9;r%̙hscmgg{1b,,#DD IDATjj*iiitIHIIAТE RSSIOOĉٳVQ4nXyݻ777RSSپ}] (9ر4ƌCPP%%%\|cǎBaa!\v;wCLL `aa)..fΝ\]]IHH 7ϛongΜ9DEE]+q#|g$''?Pf3455Hgg'KG'88X &%%͛7&:T*ihhח<==^uT*7t\s 477퍿ek׋/K., jZ ߻bNvv6;v <<^O@@>>>`2HIIAT"@P`2xxxHKKhىNO?:;; wwwN<ɶmۈ#** OOOQ*,\pȀ! !XhÀf͚E]]`|?~}´iA.憿?=zgggz=~~~# C*Q Hd;1–-ܶ6:-  M_ W%cǎ?<m +C~jkk;v,&M]8o$%2".._U*SLɉUUUDDD`0?%$$~3ƎK]]*Ex$̲eHNN&66B3vmL&|||pss?9QQQ̞=jjjD[{qDBBaaaDGG;Ǐ7 899qOyy9Z%KIuu5˗/Օ^R(_$JJJ AVk؈dݝ\zSB@@@׋QOEhh,]T ?f#((^Ϻu?>s= J8q"3g2P,n2UL;Ω"‡O$Mpp0$''6111^SNA\\cǎE&5\CPPcƌIcPx{{D [v9ď &x1x2I8Sk.}<$;#+PX/H?ZҘ4iӧO 2u1m4ZUW]ܹsj@LL 6 BJϏ(T(<< V+rcA&#S:UV!|A\I=[n:6nHJJ }ŌMň<<<9s&nnn/\/*SB@Pt:ƏOllX&!!Aչsc8{r_y i]i٧wqqlz+nÔ|Sǟۚo_x!@0EM7믿~mKHؘ6mӦMݐHJeJ%o3w z_+#qiy:ܾrpV(C`)CCɂB8띑\>z-Z|7Vgc̿]\\}LJJ wf,[@*yc?~c\ddʖRTdbKÅ=_ΆMLL g8EEz-EZ s7rw #QTnݔkS$*/,,|]0Jz_z6\ןvQ.5ldJ%xü&Lx3a rra}qg-fr;;ϼ'yɣ?vuVU CƟGƹ:}UǴv-S;>&3f VdM`Q$.=}8[.%fx͛n2Au:q-xurѭJJx9S$.?{Í7x?=t ]ޟƏv_Xœާ3_5v;5v /O=kF,{-8lc7E»seek#1o<_8[˷TSk:6o7Ͻv_~KiiؿX#JHH\@];QK崶RSSC{{Y455*,$;.[n.ynىPȓP>4>I$$zdl6)..}d$.d2twwS[[K[[ۥL&),, ?[=n#Q-wnw.2eMg N`v^KWgMÖUa2l&~w=pp^8ϸ@.-h91Ak'+V_>%!!!!1R\\\ؾ};1ƀv;mi!nT*)P*i˩Q~v;zh  `?v#[LsAW$ `2Ġ fHRR6>)W26 ɄRgr>uMD[WJm)cG9CFC@yǣu.mwG9GXaJ~L&ٳg~V]z%y\æxUmMśxmk4q??įN9yy8WBBBJo;s缔6.z뭳*tw2N'UW<|qG۟~Jb:y0b ЯtM ^~m/^,潑8r> w]$i&BCCO~2E%<w>O@^je{v|@T -}>?jbV[Dmb޵!.!1 n*ⷱQGCt'!!!q_h}ׯ_g:y$֭# ^ڵk/Sx{{᮷ohxe_}5}NP_Nս4 s ˋ|)٥)deOlbc·뉿RU1วՓmQɇ> %$$$O*CWj|^~eZ-{͍۷SZZ/Q(|߿n|FロGy^DmJ S(IMcǠ,X@O v;W B'[{{;uuuD޹T]]d'1Ahiq8xyń&ću+wQVLjںDe?UJB `hfNEtavwH#t?)~7w\ ~N#?ݶnO7GG?qj)dI1L{|ZJTFx x q֯_OKK |?0;\E{ʝwIXXk׮}%%%JGGXf*o}O.'p3 9{>xr F*[5+ -8*߱+^z%aillbM2)YYYL0_`[mdt7}|/Z?d,&f:m_/%FO?V{NaGIVLׁM2vb`ݽ_G?=g(..]]&:&f4*%:g .ZuZ<=\ #(/_I1IHH8YYIs]wWQˇy\szфG9f29YyGA;xxgW}wh6nHII fqx{!Z~x衇hiiח_y]]]ળpϓO>V;rє;v 7@ׯ[ʕ#?FT3LhZ= www:O>RKDl;w$//O=:9QQ^ɓquuK=J[[3goocGna˖-WV+ՊݣvE. :;8r055eO: &,< wpVCX,MR.9q۶O+++Q֌B;z+⋘f׿}vyPNJww7Ņy1iҤ~_?ĉOz=(\2455QZZUVÎ^^h}tT@aa!'Od +b7߰l2J%A7tGEֆ^NGee&S7닫+pzz̘L&ǜ9sdxyy@r(UvCgg'F3jzɽ9x "˩w77v]]]l6\]]▖4Z-ʽdj!W),,NN"W(P*؄Lh5ZrG.n 8i 544cb6ꊋhE~^ꂷ7555eXhkknjZBVRQ)UTUWZ>>v''mm0b%;;;RHJJbٲe{P(j5-ۿRF#ZrP*S|[{;]hWCP` Lqqvd2щSM`0{]]]L=V'F#l6,r3o澉Pn,)_趚[yK-''Xc,t*!!!#رr9vK-iooq^%j޽І Xxhv!=(\d|Wh4B? ӫ̘A#\wu##5558qa(NNNK,@V?SV+]_UFss3,X> QUTWW;霹[hll#*DILL"::NGss3cY%>>gmFPPtvvĄ /]QZV?j\Nbbӻ(bNȢWW7Z aω'8qtttOGG;#9D`A"-- h1{ R}}=YYhZ||} :* ;3Çs<3x" bX(,,$++D믣Ʀ&V@OOҌ\.G׳xb*Hz:ed2J%'o03;-'??kniŜnMs K|F :u*6m6nԩSP*FF؛Ekk+O====#|_yl߾)Sxb4 899ˬYb`2jCネ=Wt:cǎÃT]hDlݎ v J{\L&MV͆L&(yoA6vl6GJKK)//ooo4MJKKiii!&&3~~X,t:]?FYy9Ǐ@&@GG'yy24BDՠRhkkɣ{WEV+ TWWP(quu㸸8Ovv6LII1cǎEI`` j]8}N#MnsIu%??SEEQ:&qơT*9~8eeeL2???\\PbtXftwwjt8vjkkطoAdffؠhZØ1T*tR)iii!--MTjqAh4TUU؀7W]u(FMm- [ww~~MEE|tLL2Vۇfgy(JjkHOOˋ1cNѣGٷo/|3.Δ״jqa>s jگ9YL>zS=g?kyi}\K|6^BBBJaݺulذZ XnرzkyIAA @B$9tyyy(Jd2s%''ll6 P__~;lZZ;Vc޼y}y'7oEۍJ 3UVQVVٳq}a:_çA(..JQ(DEER(--]U@nn.v777ƌ&&NKN--|7ÃjjjXp!b  \phWX̜*x45Jw+ d2;ʵ^Rf I wޥyi%~̘1'''>#  RIxxsU2~{{FVV61vl)qX,f222P(̟?OOOZ[[FØ1l6&$L 2"D~~>--XT*%vۋcǢbN@.ՅAo0P(D% 蠽O|]G`Xr8q|@t"## FPT*d„~8c2MA~>SMӓQV{zzijj&)) 4 9(zj5<,o濉j0Wcߜ u{%$$$~*\{l6NtD7]f .IAD<֬Y(J;+/Y<F׳tRfMLL ݻwvap"))kf@gP\kJjZ***>p/{M118hĻ^ÇdTVVRSSCGGGۛu֡RHKK#''Orq*ˢEML  fOPX &N v=V"ʿj1+XQ%͔t@bb"!(g޽Jj[TWW/?%ijll6 WT456ή., AT yOOL&O#u"!a-477Q[[˞={hĉvm\W77hinAV;E'mmmv***8q87|3 SBDb6Յ` *2@rr2F'OB8i{ې1axr >'!!!qqRX|9˗/pnpp0,YDtuD?|||Xx1/߰;}wFž DsNvM}}=99Ĝ>Ç @\Npp0!!!xxxT*Ѵ~ vtCCQQ 09yOхUTb1qa$[]5OF]]=8; O//qK9 \FAAkFڋձ<.L6@(iD!w .>H?{؉ "::V7ǏGl6S[[Ǫ2:&N<<_~ {_G=ve2gfl6r9 ,`ܹ]`h4R]]ի)Esgq :gg\LT֌.\]]P((/H&zwlvjpt "0 6 @]d2sstx{{Ott4--tvvVZ[q:uѿۛz:;;1LЀ ZVjpϯ/ 'nNLQQAA8vU$.ӉuZ[qqqlvA0fi:QVV---|TWWֆ#S.vhhuub0XzgJ%3g?4eXWJKKQ5z.& NG{[^gA{#88*+*'|zK 8;NѩS___i#Z_ѣ(Jf̘Cfq]oM\VT"#hiia=m2 0n8K򤸤&<qNѩS^}5 ܓ]= Yhn}Dhh(k?B RSSCLL .$77Hqq---r…(ΐRRvPxVٳ DŽB.|8Q&s|NLLlRRR th4xӍ477Tw:{#gU*'<6ߐJlf„ DDDl8ýf|@??P*L2esACV=== L<l6۷FUUϯ7w7Z[Hl ˗#10m4),,Ņx"""F}ksI^n2y2GSVVJBb"Ǐ2Ǐba}`$((c(+-ߟ3gk?v\,.bBtF#K,! @K %88n(U*r I0ጚd\u˻4zx~B$$$$$J3/F 0&B#+>|w}wv+ -[F֭!w_:l;Y_Hcqd %۶ar$?)$%&SLsM7d`0{~߰&W*YYY;z$.=,{2K#ꮡp!/f]`:_Ji ˃ e^s>U@J:wMNNN*n#--- Z>N;co.9}-^k.F3!-<_̾^gbYbx[yD [vB@R p O)]C-q rbQ%t(\*FZ-ɢЙh4VX_' BB6H ︸8VeWOiB$y z!^y♕Er4JE~~>| ]]]C''㞞+F=JGG}%joсZLv}&v OGn]AyYY}SMGGǏDvz rYmWY=455bQBg>f͚ō7ވ Y &?{М umkǘddd Hz1+PzIZ:K崵QXX8Tr,9~ła6 i ZD~G]]ǡyzJ05uNku2w7$E/ h-GszeS:Ҷ_c *UUUUoBBG!77ZJJJxZ|ŋ夥 1*xZZhZN:\.'11cǒlj'0 \___GwwwMƄ PdddP__ORR>>3ȶ6q=1Mvv6Ge@]}),,$556mvٿ?sk)RQQM(--ۛ3g2n8v;| fYbŠ׳dKGG[osׯ_ϼy7oޠm-]FCZZh4N:ә;w.l޼ycuVl68F3^t)ƍCTbc߾}455'oP+k}ewwwVXߐwLټy3999 ~{3f}&ׯjSO抲Ì31p:Dhh($&&P__OII SN'/T__/˙㔓þ}hnn&$$LPPА|`̙ G|F%#Iuu56mbƌL6mrtv܉R$99I&" vBBBXr%+7gyLƋ/ȽτW^y￟rOUUՈ2x9s^Zru׍TY7bP!0U~-[]ϳdҤWKKRjJ eFP(P$z屦Akn_qr* www $X1c >3իW_|ƍŔ111DFFD=*Q̛7JECC999xzz ٳfy^}Ur9f<PT$$$LJJ %Km6=ӯ#GgAb />,j^ߟ"lBEE^^^{( -o ߗ,j(*QwfÆ ͖-[#66vq'xɄNbk.bcc;w-\ 7@ZZ;v`TVVƶmhoop~yxx1m۷oGRKQQ[n ѣGw*Q_5GPICC=ذuɓ&ߣSWW7,9r455h"ygxwh4`J_… Ey9snJEEcǎƢET6n܈`Fj5#>#;vƤ_.f̘ \?VKCCeeed2Ν+ 6 W_} vmgUww|wƍĉźKJJ +V>wws˅Rl6>>>jLDNOՊ;xeHR3pTk?]w]%$$$.&deeLrrEMG^ R~K/Ē%K :thė0 ,] &P]]O?Myy9='NdϞ=]GyOOO-ZD@@;9r8tGeɒ%455oۅ_ɓINNu@]}پ};< ˗/\TWWzjyy嗇Uz=˗/')) >CK/Ĝ9s;h4|:thT/Hqqq,_Vݻ9z(sZWRILLDVi&x'=l޼qG#<"^C[o%11JҥKg]vP%j~-ݻ/K x?ׯxWV;Ƈ~8frlٲEw^RSS9vӧOg׮]\rqEy9sHHH 33SuX֮]+C;=zt1ɉx`/Cgp<37x#UUUK3;7̟?Fjj*O?4v۠u0|{wygڵ,X@{7/~ѣG)++n`#yXizU{uf\RB7ҝB&'%JBBjl6_TtAhmm~;mL: d2h2tA}1իyϹ8QTfcĉT*fϞMee%˖-#55*GrG^^^$''gaOc֬Yڌ&##ue͚5?~j:u*zĉt:V^=bbx$v!۵k tuu ӗnI4;w.JOO7Ѵ!_f ~)h4m;v ooo`N8qT߷IIIT*ΝKuu+g}ܯߣu%''* c޽x㍼{L>}~tsstILL4nt&?Opvve; 5&"00z_ʡ̄+滹>[,Ωn?#;K,W^!;; &M}}=]wojI/_W^!hd*;w3f EW]);{ptrEJDgW.6V*++ꂷ)*TX,Z[[黜PB\#O#|uwc2V`u]Q;7bcc|u8Z,˨T*T rw*J\x|jb1͘fdz=lذu։l۶!wSL>s̲ rvvMFJqaapjєAM3 _޹d2M|{wyG׳l2Z www}E/.筷2>ESXv;|CȝNˌфcX0c.+k^L99vDd%$$.=dffbncQ*_?hNr9NNNgC̙3ϻfRN8AMM :iӦ\։6WNOO6RD^z\ZDV2|󋉓3Si&֞NqFqՕb 8ܑظq#W_}5yyy;ͤIx'ru:++V>BBBpW3qDyXt)>|cǎƍG\ !##D233im.󉌌[nÃrT*1F1`mٳg:BCCINNfݺu֬YCuuu1Mb*1^k `~~l6rssMp&---;vΫB`ɼ $''E\\/"SL9hmmmb rrr>gϙ33g~{֯_OjjQ3Xh]]]{S۹pBx7ؿ l&OY[JsFjk0Ph4hzC=ƮTFFFfxWhkkBvvvRZZ 22SbZя~6&Սv $cڴi.mMcc#o6|Lsٹs' npIOOgƍdf|D?OOO111( vɔ)SXx1^^^DFF"6DDDKYY{ӓ4< tvvb0v8}<==Yѣ\{Nds% M&wyן.~ixᇯtudD&7a3e쁯L@vM\aO=O=5zf\QJntqq- ,##sB`XX,RPz"|'""B9w ۿ~~~477K/Q\\;3kpS( s&// V\ƍQ*yX` ~vEAA4551g"##СCݷ~;UUUV)++c̙\wuhZΜ9Cnnȸ" 77C/^|mqj.]G}D}}Zdd_!jnnMC5S|3y뭷Onnn;KIKKnc\zzzj@bb"999X,l6v}5泥Cw?)A3ddddddBg@`ۥ(iNAKPH%Aaa!999dʔ)s7LPXXWf1}tҤdxxxTj,Y /mƱc8s )))tgl>Nӧ;`0 `޽;A跕oJ og`ż %%eu&fu8VINNߟ&=B\\TUUݍV^xFkk+hN4 eBBB0͔K233rKaTxbL`B)Qdi޾-|2222_}dMxxx-=3T.fΜIGG<#DGGafΜIgg'?Oh4 T*=|}}/Y 妛n"33~qqq(J=u Fgg'1SNeΜ9jLBQQuuu$&&tRV+gϞ%//B`ƍIqq1dzxb,Y‡~VEP`jxyyn׉+!* P'###3,DLy{)))pͬYPT̝;^OCC>>> IKKC! !!Yf,iq֬Y# iBDDׯ'**EEEϧBArr2IIIc٘9s&^^^ Rdƌz/22???`4X^`j5,YnT*.BSrr2j RBjjj8qqףj cRQQAyy9*%ܼy3ׯɓi&rrrɡooo-ZDJJ f\}l61HDD󾩩TVVrAF#+VHM*++9qmmmDFFl2"""p8cf̘W_- c'ǎ@n&BBBؼy3c2edddBō;2Q`` s{~\Z8bccٽ{7s̑aNMԔ)S]DRRҸDc7o57Ϲ׳k.jkkfǎ(Jnv;*ӧNOOE-[/E4HOOGjNj53gwy#77 Q_h4c2صk$Dرz(++#$$ٱc曨T* Qf^j%33N~^e!JFFۆ,D|K&Ԍ|9ur-̟?;v3p-puO?ƍc͘17J;w.J^{5rrr&srrY.Xݻws 7R̔kllog޼y466JSNŸgOcxt:{o+M%##mCdddd( ,XpqE'//FCFF9sFx=pwex̙yn66F[odj+ɓ'y7`Μ9Hu]SUUE`` 6lf޼yxxx(ojjGw]6m5mNMʕ+?SEYqIBBΦ.###s(JT*ebn/RB(ÃGyD~``AXXVUx1 ߻vBH GFF?%++*++)..^.6$U*V8y)Zv w/####3% Q*rssZU'o &q8dggƗJ/u.&&sr50w\9z(###$&&Gꫜ>}e˖CCCޣ~h(--๋-b۶ms=QTTtA-ɓ'Yf 8pOL&gΜq1狋#<<ӧOK>Qo6 .4Faa!%%%NȄ!D|۸$!JqBDRj)--ܹsW: RIcc#V`ƍSO)So~~-[Ɩ-[馛عs'X___~_]4V%-- ;v?!l 0W_}˛?d!JFFۆ";3PPP/8FNVV֭cŊW*WIM l{) Ȝjf]&//9*`҄o#̕D痌WIrrr8x ұ^;FNNEl PVVѣGinn2?':Dnng:J-܅Rȑ#瞣 {~***Ohllp###9r?O|I-Immg y֒Auu%us5ڵI)|ʖL|YtZZZy&, c}3|a!wޡ@{RWW7)_EE>tuu]4ߌf61(///c ==ǏOjNx''\NUU識:!l6m۶4&b֭}CCCBe-[P(G#gBe͚5qa<==Zl2.]fܹsNRRXVΝ;Gtt4@҂SLqOqq1łhfCll,---tttP('&&wwwf3tttUW]Epp0466' Ũs]](t:f3Oll,( Z-jd2VdԩhZ۩OOObbb q( z=ꈍ%** OOO`tHcc#jiӦI9zhkkJ?6Q(>}/n:bcc fddDj%990J_HJJ"22Njjjח 044N#66&yhnn&$$$ &, $%%-:zzzd2h& _(9sF{T* !:://qו? 6`4Eӡhiiѣ βeˤ$҂Vex{{{V6۷S__ƍYf SLAPHII Jp"##QT鉛jڥOSVVj%<<(MMMt:qss:ϟ !gϞ%&&믿www455aXfxxx677STTt:&j^Ouu5zΝ ahhhjDDDkh4RVVFoo/L:4 hZijj"!!ooo*++CR6}PSSVXM__ ԩSGPHꦦ&4 ӦMcʔ)yf0 \uUĸSTOuu5fP Hss3 ("""Zdo۩!::Duu5 bbbi&9:߷M,͏Mo|x5sΥ<HJJ̙3fHWWr-L:^v) FFƍ  ٳVIs=|z=dee҂rn6fbpwwgѢE.#22vvލdBTҥKs֩]b0gq8?~t̟?KEKK Ff3---|R8~8fX CCCرCz9rmp8(++ܹsa6$55///|MÉ={OOOfhmmeń_p,Ȍ2 +V*رch4V\ٳ qhnn&??Xv-!!!deeJZZ/\"&&իWMNNxzzRrt`]jj*~~~>}uRI__Ν#<qj顦ZŋIMM={0m4MٳYd !!!Ekk+ӧOgŜ9sCP($Cll,k׮%((gϒKpp0˖-LJ"9qvZVX &&ӧ%ALfϞMjj*-L&IMMedffR^^NJJ ju֑Fdd}bZ`ʕ[iӦC[[CCCF<<f͢ӧOV%==ݥ]vNYY'N`0l2¨F'L&n#66R$""FC~~>p 000@EE̟?NbBT*Q`v.\ IDAT hYv-fIBىRddd&HKKcΜ9ӃVJErr2.Za2jR]]MLL gϖBHZ:u*jYz5ttt+PYYɊ+\4_BJ%fb f3466T*)((@p8h4PVVƜ9sRDբVQdzj*RSSQT@LL $''h">̹sncXX,۷n 777,YBZZ^^^K]]SNjb646 ZMpp0DGG%5k֠T*lRRRBrr2CCCl6fΜ9b0XjtvvRUU>yz^꣤$6l@HH!(,,8 a,_"Xlk׮ӓ~a`Ԅȑ#y "$RRR\wss#::V^MPPow}7k֬jj9qdw7j!inn~Yv-GeŊ9s3gJ}/ZMtt4-bʕj+Uپ}; ,`ڵ8ʺ,,,k0, hmmd2, sInF9B`` ]wkӁQ T~~>lذYf6 OOOjq&mQQQ( V^-sj.]ʪUPTzvAKK g RI?RWWm݆Z&55^?SzM71sL`Tϧ oooQ*&PKK ロrwCOOYYY\{۷ LȈ%t\wu|F2_-222 55 'D裏^t\Y, rJ-[&?/X4_e..DB@ӡP(BJۛD#NGX,L>`Xxxk>Na:ǓCWWV6BCC 'QSS.ȃ///RRR8p& ^Obb"AAA_AAAxzz;HR1uT BFBA?Cr ((hB5'کP(c###X,Vt]WWCCCxzzRVV@pp0!$&&J~D###tuuaX줵ł/~~~$$$I^^z̙ZL5K!fNJHH0}[2e $22O q88ǥfsnKDDdKF#B?.NXf]ii)˗/0T*&Bdooox{{c1$&&CEE̛7u/RWWGnn. .DTB???j4ߝcO憇p/iZ4 X,b0Q7##F}}= .$Zܹ͛s+V BBBcGBR +qssfIii)$9g.vN' P0*{zzJBOee4Z$\̔r;::X,L&Μ9f`0z|8O0J~~>J`` lٲD}\oB^~e)85\ >WolܸiӦa4پ};>>>,[rT#ٳdffG||X`h4Dj4駟&11͛7Kcdxx'xիW3m4ib{9nfI;w.0۱c… IJJXf$?,ovASS!Y߸źufxx[h m---pzill$::{Ww?ngÆ ,Z}Mbb"Vu3ZFIee%vfQ]] '33.]*իeǎj{* Ebaل#;;jt$''O(Dt:8t^^^4551sL"##Q*$&&RRRQՒDJ%:FZFպ,t:IALL eee ͛Gll,Fr)̛7Nl<==9s&Ǐȑ#R7Z̙3tX`ze޺IBBB dh4FF}ܹs%-' b\};OOOΜ9Cff$ԷIY@ s5Zf֬YL&񑄵 ̕iҷrJO<'ӦMzpFFFxOl6sAy$AKر?8}QnV B`4裏xfٲeL&vIzzdRQQQR:VZ[[_8233IOOgxx{{2m4x뭷?))){n}YfϞW_}W2vEWWr 8pv`4cnnȖ-[fp, tvvꫯRQQ{زe D|)&MMM_08~8%%%wc͜;wg}8nΝ;{ CC'|'eeeEEET+H444.F:p8DccfY׋7|S|ܹsZTVVJ777ob֭m.++xEqq(--uuujvQSS#z{{ؿx7Ş={DooTFOO8yxđ#GDGGDŽlNSN7xC ~m6QPPXDeehllZI744,l6Gǎ8r###"==][bǎ^YYY"??_Iɓ'Eooʥ;;;Euu`_fdd_UUUG8\o721444/?Ck.QUU%l6lITWWA)N@l۶M"=:[X,QQQ!v%}]- DYY5MMM⣏>gϞ裏D^^rG}Tdee]{lڴIlڴ鲕e%wB"))Il۶M!޽{… … ž}mC?صkbt<7*J|spss?τh߄xBlٲE?lb֬YiۅNjZ1:/q뭷 !կZVOg'oJg͚5رcRBBB Jh4"==]19s\v]lٲEh4=;jM6Mޭ[ !(CIx"99Y|. uij}#Γ&]%|Z-3f`ƌ^@BB¸yn+dN"##/޽{$55%`' EQs~ aѢEjR"** ;B5cqww'..Υ>r}˚5kƕ4S1b…?&&f\~KtRL!jk^,YӺ!K}WTHoʕ+?>֭s~3Qݜ-2y欻ႻR bg0m4ZJ+~~~Ɂ,bbb S___?/υ!pssYXTdlض^___nV:sCAPPwqDŽt:•k,t EsIv .F˗/CMM .BMee%/!3eo6i<̷$I0tww+Uعs'RX8ӜBٳg)**BV /H[la޼y޽Yfq VZ%qawO믿;JM4 O7tNW_;ĉl޼ 7'?ڛn48u;v̎^ZZ:n_i&5ב@qJFF泳a6nw]wErrWTFS(TUU~~~Bd2a2\/D9w;dMHZ&'O~A'? ?8>}P}YIh;Vh: g_;D-^4`xqBkƽ 7@tt4>(.2o>vIBB8reII IDAT[[\}8pkcӃRtD?LJd Ν"9v!꥗^ټy3^^^<hcc\4N)DQqqqw]owysi&)Os 4Q;wdR5J/KmO?;wfƌfRF8̿/`/vM'&jvuu?sӦM\XF1-q>ӧy'V1c:**jH_&Ǒg=ʵ^+L6˱,flz/o|mm=p5Qc/=b\Q'|SO=@uu5۷od2}2/vK'|r\+ˤ;(fl}}}Rb5MRQUUŶmۈ'880z"9 R[[lh/u蠭 ???f3CCChDZ-f)yn@@AAACRGGGKjAZZZۛ<<|{{{bbb8v&ʈjb޽{t 8*++9p#F 88zV[~6lذqRSSo~67T]?u쪬Q&Trss0a &))[AII ,\"""g̘1T*@*Ʉ PTֆ3r\]]Yn-2F#O2h rss9tz)lmmEPPP7xzz҂=EEEddd0m4ZT*%''VBBB%==g^d2]4Nz^L&#,,qNee%_|J &w^ƏO[[hD%''w;6lذaÆ 6lq͌('DVc6DXX_ ̙C߾}C.$'zs,M455N8ѥryr9qqqGRR)\" Ϗ :?Kss3Æ h%J)--ERRh4rYjkkijjB"\b(HJF!88BJ'''R)JR)o)jÆ 6.ȑ#o]UW_ Vvzu͌(ш`` :VKѣ fxQ9\n]$--M T(d2>.+t:RXON4:Ø1c8~8UUU=zӧO3bz= yTxz;;;ȑd( ZZZD}2x,r8d2lvlذaÆ 6lظ\ zСC8q""(++337E?娪Ç3yd&MBҀZ-@Gӵ싚tF\\\O>M6a4 "!!SrJx z)> &Lh4#b HJJߟ3g"=x~Jcc#:N,ns,wwwYd ˖-~ۯմٰamhdǎL<wwwՅۛF/_neZK/QZZJaa!w裂XRR \.gҤI߿_|=55aDuֵb6???-[vU|W!ׯ=@&Mikk YF<$o*cNJ:]w}w|MsMK3gRSS)))G$&&"J2d"sXp!7od2P(cʔ)b<{HMM'''^bcc/ s///N O&..3gJDD3g$<<\ +**BT@BB3fRF \.gϞ̙3}QYYI`` IIIDDD zbѢEܹZ<==0aθBpp'F ((4DƎߵ66lظٸq#?wu?xGxYju3LTWW#Jsozz:}X}1tP0`5 usYf̘_WΝ;U{}888:c4?>>ER?v,f͚żyhoog٬X//oƲRRz \f={$,, ɄD"A"`6 g…DEEYـ0`dBR)III' ٳgc6/*Jhh(,6m'O@*!}%::ZR$,,wyG4R)|rMO?M~7oލVwV/ҽ,+O>}\#ʆ 6l ŨXIظ}1 !z+#*//={ffjeDՋ{[h0D9-2vV@*ҫW/opKy:SVVeC{w߱yfx 0`[lD]H [*,!(((`ݺu~m\$ ª%d2ݶϟEGˏ dz.C#GP__߃FՉSSL/ 44430gΜk~݋'OAMM @P0rHƍOONZZ{E3wܫU-@>}5jTPT >\0|p0ELL  &͟?_ {غu+888лwoQ*ǣQT]8{lKP(!11Q ۋw,^X͙3={0`Zطo_Z-gΜA*,2b0ekw%::Frssh ٢j,̝;{h$..>}Ftb}c< ۷o`00qD3]aзo_9y$ޖVA+olڴ7|^m%qKP\\3<ڵkoE|w;wرcoH$deeq!oRmF`` 'NmH$8ps1{lίe$##2V3a⦤[+VtvAn移byήG:^#^^|}sιs(..&(([9.//7`ڵxxxlq9b٘Fg||<^^^7I26lذTTTp݉DTڊ7ϟ?E#*;;/~JM6oWwl6s ^}U$ 7-5 WёXшڸq#GfW_}EAA<$$$ ]ׁ __~_~xzzRRR7|C{{;?u_/lذ(1@XXAAAW<۷~fΜI=sl`2h4338NFF$ !,,WDpڍ<{ft\.\xcÆ 6l\j#R޼ylݺ4z]w݅F???#"{yΜ9CLL Fjkkˣ"Ż%Jol6ݻߟǏS\\,Q& Aı5bxxMM $''#1 HRN>MSSr\ 7H$]ΓeA,0™3ghiiٳӯ_?\zWz[ ǵsxTdd$899a2 77wwFu)t:$''s䄷hTYd1V&j5b=-ϲ`ou} 1PYYIA^.GӨ9ɜ |=Lj B/բ߅i9y+)fFTJE wz]=cNhZۙ9s6I%55}vnSSS9r5nZ`Aٙʨ͍ҿ`ZD>|իWSYYÇիQy'==: ĝwILLE]mt l߾˗#/0wy)S0zhػw/?f…111=^zYҥKgٲeݛwyM6l2BBBDOVeժU| >,HOOחz6gL&Ņ3m4zy ǒkZ7MFbb"SYQɮ]8qRɀ4h޽{{{ƌiii_+L&|MR)bcc3fuϴsޤ'-ͣ6xW j=9|9]ظ4mo0n>Kj#3uuu@``UN]]ׯb@Dr9D"!77^xf̙C@@+VGyߔJ~~>;wGy7j9<ϟg۶mblH^^9\q IDAT١ݻpܹc0 {{{۷/c޽8::h_ٳ̙3(6n7?SQQ}8uÇg„ .,P׳~6meeel۶JF#J;wjyQ(j* 770ƌJܹs?^kRPPN`0P^^Nkk+T஬$..TJmm-& \=J>#zINNo zAPPDDD0{lQT@G=܃nnnݻӧ@[[[ z!®Jؽ{7%%5 \.ΝO>AT2f멩F9BPPQ;w999bdž:III$z7/;D [O>! 6lذq#QG]]T*z!zcVIJJpttdܸq >fxyyG}ĦMx뭷;PTرcm3lСCdddhTUUM6477SWWJŸq=zx&f̘''jF+oO?ҥKH$pBptt`00h qqpp)u)j5L0},)--HKΞ=ʕ+ח!C`ooT*%::BN<`رcTVV2i$<==t455!dUkIuu5v"??_4zͼy8p ӧO>-x g͍MF@@@TTd`ܸqr1ɉlΜ9Cmmu7P98u@G2gd2k֬hhhh0RXPD"innWWWqL3z)x$ ---`6qssC"`6l63i$+,[coo]f7ԣ>gRXX(+۷YfRDThtsszprt{êj3NVE@wXߐqi^y6np'6lvl2XԠqww8//NGttg2hjj_\L755hӓsQPP%KxWyyyIHHyh$++ z=;v`żS__OQQUUUFݑJ466RUU\.LV???|||}NN~~~b6EcRd2ODD=zeC.D}}=vvv ɺ]_|mFEEk֬aɒ%^D"aРAp1֭[ZOl޼w} f͚ҥK>}:!H ** BAuu5WWW\]]ioo Fs]z;v mdСCBξI^J~.i94igggYfxNmm-IIIVw2n K```Ka3/**B.hrqww*l4'00f3䄧hpىI&HDDW ??;vxYr%O=K.%99G*ܑ'QQQA^^Rwww1į^AǏ(V4؋ʲ$ ٌ+ [yM&[l!33e˖|r.]c=ƈ#hnnhfķ~˗_~),Zk1a?0x`<<<h4 Ν# @jݝtz) 'N󚺢~'OH{{;s FZ[Wppx-NGjj*,Xɓ'`@.bUZc6ioo%@``c5LpK0ƤR)UₓS'J`gU1x`6[q+讫fqq1---瓙)?|0IIIW+nLqq1YF:rͮԩS1͜:uA_gϞ%+ ڀ;wh0Qaa!G~E`${9y$}ujt֭ 4-[k.yǀMٳgbŊn2`eƏϑ#GXn]Ka cPTT7111:*륥1zhz-J~ikk۷/:`ٲeb7$<<ەNֲuVN:ŬYXh(,,dՔ֭[Fhh(gΜxT*KQQ+VՕ?:t8z)zpww' |Mls=G@@%%%R݅{3d,XO?2;w.}>>>Zp#'';wR__Okk+iiiKvv6yyybo%hF^K]OTFF---۷ɄBחV6oތNO>ˋ_~!C0e AVqFN>͈#S Pȁg>s|||8w'N ((Hk%5''Gx?ό30 ̙3 M0 ;vp< ikk#88777#LC q^;FolPЫɓ'ѣ=z@VgnYz5uuu8::wADDogΜahZJJJcݺut:{r:İtRT*JJEZZt:L&'OfرVj/D"e>|8>>>gW;g0LՑ̟'= t~!,Xwww.]zsu9y$f^SɴiXx1#Fё>}pKn:̙/>(+V 64كNl6STTD|||ײ,#M=ӧ>}:j;vg}dbь=l^z%+T*~IFFbꄅ0?F#w7JJJ 2*~ ''8;;@~pttٳP('((۷uVVZe]ȸH 篽[_W\|ٳ4mߎ6#}E* 55Hyy!ƮgOǍq$$oŅ#F鈊*;ܹsz ?0eeebNJ777,X@hh(L&T*qqqB 2w\vZR)Æ (<==iiiRIBB>>>+=bHdh42a"##ƻ?@l:m4<<zhJ%( qgPICCgذaXFGG3o< #̙32f*++`yvpp !!;S΃>H~~ĉ𠦦Dd"::z=z4z^\$%%zBXXhFϞ=Vh6:u*rԈ=^^^8::P(HJJ m\F\\...DGG888+x 13w4?Wŕ!-.zyy9 0yd~'z=rGki&x'IHH999]Qnnn3j(Q{޳mv8j:Dpp0/FѰd 4h^K w7L&%%%߿f]@BB:u Rz聋 vvv\L& gܸq <^ qwT*EX.rcǎ ш...( d2Ǐ+u/(qrrsקOBBBVA0`(J1yCaƬK,YӻwoJ2o]]{]p߲..X >K]EW}\/A@.ʀ0]4gg}s>>ח3gRZZʇ~HRRW^`O?~7yyy̟?e6oL{{;3f̰W]F.?QL.eLxC FWP@MT>iڶ]}M.noÆ B!VvUZƿߠЂԎ/`~+xQQQd2hkkC ٲe }ݘf9::c#Ì9b6n܈ Nj($11J(**"((RYF Xj 'OF/^cĉV,zuuugW=C&h@T~z󉌌ȩSصkwww1Ǭ;QW*Ԑg5'c7c.JOOo@ZZ;wGۗ#G444p1L+Y/4ıoiiaϞ=h"@II 錌 j5+WD"ˉ'UcםPyhHMM%%%I&1sn|!Vk2`h4ܹ^ONV>3;w.=h9s[>y<<<5k o67m154[`שwʕPx,YBW_QKK)Z#oz~tRMX*ظڹH$TIJń ;֏X0a:Z EhUZ7 8;;ϲo>fvuu%&&k#fߏ^G"0j("##IIIl6[-R:ă>huDquuEY]GVy, bggGNNvvv]z:oua>|꼗_~{ƒ{GHHgΜ!((gggƏwRRRpqqnURRBFF555ر(4rH/뮌KOO'00W^yT*7n$&&RQQ1+#+Y-m": ׋rd2><==qss~~~GP(P*ӓRb_w~ע/)) 6aÆn97QM;vp~:RWWNq 3\2cE Ҵu+BѼyr]V&!ɺ]9k dnWZcݟ%[N_ ={e"#p5vs߲zZ -H%P񺝝*^zQ[[Kxx#>(z+lܸQ 8޽{P(G[dϟ?ߪ2@= IDAT~#=zX]gV^^xF#aaaDEEeyŋ_M?>cǎ% Chjڈ!,,L j| }aԨQb pN,:r9fϞ-j; xr9MRRR!їuСֆJbhZѳz]whwbްaÐ?~JE||7Wܔrv"BQzQY0SY**!\рR7ܷ|-yyv}g~|BB8xy?{Gu/gfbuYdY^  b/=%$$7!@\H $`B3X-7Y꽗ݕVwbaK1yvfNsg6}xDŽ$H c9+!׿_G;BrQXX?uGꂹ~޽,>zz P?1:::i|.ȴ}{i,^<>fϞӯ6tvuL˗/Gv|U)~L&ݎn^k|W^e_8J%qW]Eŵ~o~Z=== A rYsF!N/~@]waX,$I z9jf+ eFױ +'ȅ&4ɇ:bRW3"$zu cum#@wCmMHe:_:;5;ǘ\<6/^[>L*_2weŲe©ŴZmP $H 3 QnG'dkrr7{i,?}$2_'͖laZZ$;^XrI9^e!]&/3v˲L[[-,8!g YIҩ1-2klD0 $ Y>{/Yq8 9ex II)ߥ3ى ..\]gxd0PBַyUly׎ZT:y5_W8} A Rqf!0~s(I:; 9zMĽx{{nJwt4kkklMCQ38hd> @||  ӃBqa{{!**XnUUU466~fF:_A`fkR/m2$ł8)ʹ CTW';'Ivsb?٦g[XⶴW\yx|b+,qoWznx.Ӯl8F ]{7m۶Djjj 'z&..ą!NUUŞcb={6jT*'1) 2<<ŋՆ00`4J%.R 21l!244ܹs7+~a*ttt頿s97U_prVI|>fLr>C%%%-)DEEyʎT)ɻBr!JJJ̢eZرc111.D:̏|4Ex;3$ErX Gb#M95|`dO?Wz']\55uۿ~^/III|>۶mcڴi' QVdtzzaxx R^\LZZ7p6(+;ʁ gJjzz{1<<̶mۨ@ o#>>>{5Afb0466HEEB%-[NatJ%o+I-;;;e6Ν;c׮̝;˗344Dtt 7n$֬YhvSWWGii)}$$s[Z{CSc#_ӎ0̜5C1nO288Ho_gIFIJJ"229sf<@II zOرcTTTEee%NlfӦp {˵]7Ya_I mm,Y>8zCAT A!P(x#̛7>J%W_}5:MYQ((( ..~RS׿;߹cǎRI҈M++rxaΜ zJJ!*DrNpa<73f̠;vP(رc'IȲLEE%S$\nZ-J3f0uGooh4ȅ6L"##h4/QXX9sfSPP~Hk[QQSIMMhLr;FOΔ)SG}$IbժULu)Tˮw9a}QZZ(̜9s%Ibǎرc[˖-;Gz\.ٻw/ ”)S7oy{nn7᭪| _KW}ƽ;wڊ&66 0mڴs.g'U TrOxr{{VÇX,zgΜ9?FAeHe…dB^(((@T#;;xP*a6III!;; . FͶm%::ՊBimm%%%IBPp(YU*@`#j AKK3LNjFTV0ǓFJ GT?jex؆%7w*iii"""xhnn&ʬٳI>}}Ԣ A!q8̛;!GˎqCOxx8 -":* Nm]t:z{{hjjd2a0'=wa! ޞ^fΜZ2(**BRGccHh4xj\wuD{KssW]5Dt:==L%,4Vb'2N.j5zNKXܵ )(GQ[WHLOO/HHH$44ϋb!22 rHO 66~o%Ԍ$Ih>c<>C~=tg/_8vAf͚u ́l6vEGGk֬!==uQ {^y1ZNwmww7O?4!!!T*|>uuuġC&ϗSKtt4+VLG}D}}=XV~mDQ;r>=zbf3,YdR^'YdӦMf"##߅oԜ`ҥKIJJRUUŖ-[PT\wu~~sW_};#0]NHٿ?pwb4*JJJhhh ::o90]V 3ܕef=JTT---\.l66""IGMXXHrDGQ1 car$DFF `ާTr91hDFF`jRimm""LOCBHUbhhBAXhDZZH0jbcc/=|>& Rcʔηo$dhMll))SϙV <ƐJua/>fvٳ1455ͲKIKKghh0("aaĒ ddd~CFFznMfWZ޽{; Grr2fcttt $#Iall,EEE$&&2`ȑèT*Ba6Δ)Ȳ." gi% qXVz{{U&$ Fq*5 }hmi!))y摐R && i̜9^ tvvQ]]CRr2EEEtZ$9Vr#".]v]0l6~ӟiӦ1ҥKYtfr}WUٰapBqTWWsg͚5NmmmT*>1<<̞={ؼy3CE^|Eo+\EOOO@ڱc <8~k.p8dt:}e˖1w\mۆRdѢv?ȭJFF(¡CBF#se鄄088ȻK]]jŋsAϟR_zΝˌ3 (~: D555E1sLZ{5׌T|_믧iկXh}+_a֢֭ۑe%K 2?<7tTVVRRRw|0؏n0_ix<nJkkIn 36cǎaZz:t ֯_$I?oz7|K{{;/y饗zK]lݺۍ$I8p1ޛoICCFcǎ1<<̷K\+HՈOlFBxȊ+(**d2188ȴiӘ5k _3T*|+(ʀd]w݅n'99HB $--fyLIHfNɄtcpp) ZhԬ] hjB&\YEPMEE*n0jjjlnBB244DkkFf-Z0xl6T*n+ D) Ncvv.6ȵ :SfDQlBHJJf|Gt:JJJP(,^r"2%JZFաR0 X,V|>! p8IN||< Et2:::hgppP*tuucimmEdbccIH/bu:6Z IKKd EFdYt fjjjhootlHtZ!DBNN t:*:;;?TVJaj"333vNOEE9>G^n.&h4b/*ߜ=ŏոiN^i~]ATs m۶ӧs׎ljj:0LH͛Xv-%%%l߾/,~GX,tV عs' SZZJ~~>JÇS__:$&&8< {/?~[?~e˖z鱗E}}=CCC \.[l Q' Q555C333f#I*.ۑ$Yf~zt8N^yJJJNˎꪫN\(sn:fl6oތ륨h믿>ny)))|6<mrpp{~L&|>O?4fH{{{/ꪀŋ)))'`B[[O{Y&O/$%%ֆV%**RWHHH6zpM7~zG7޽{'y!2ouuu|[ߺ,PU3gSRR|ֲfΝ޽{y!jDp֢-:9S( Brr2 XLBnnn`I_aa! rk|ȲLoo/H55Dzz: 3 /"""p:8]NT**ˍj%55ud@ʔ)X,f$YFTKL6f^MȈO7(JIKK#,K f3vR餡&rrrx( FbbIOO#""&q:tvvz1[MM-q\n4@\\BVKWw7*Ev3mZ'AS &S#im襭ԘT9d yRwSq~l#Ilٲ/}K'B|_fڵr0f&o8v7qv ͒R l裏2m4DQիWi& _=dee100n'!!˗b?9./0̞=3fobOII {/Yg$==}̆ ̽uuu2k,/^Fanv]V g`} QM O}R3#-N80z}fet֒J"&&lä$IDEEcwq\8PbeJ :mHI{DĠE<yy0ՁAzX qLt4))) 9t0]LjSs8r_КP* l55ܵThinvla䖛R>4͈Ip:]L餿A CQ HP 9r&̙B;=v顱|'Tl6Crr2FКFL^o )]; IDAT)A`dSFF#2UUUDDDАn"h4fM/o$2PpA{{;HKWW:+$΀_hh2Ն "$C[[YYYw r9E ,eJJ:hepphv;NYJ1MH^BOc3L2=ٳ\Ϣo40|زeI9N:::ƘUWW×d2a4)--d, uuu[Պd# ػw/.^`BD'^x,ӟZ楓R֭[ǜ9s瓑hʈb޼yv^}UjxwkNQ+Qb4QYYkj%""+_p\#|!+++Z~… ٸq#z-[rwO4>|zA@P0{+G__ߧ D j5n׿bݰs^PP@||iMx sFE"""HmiAx RS7o 8|]]DLGcpʢ~!6&RŊ+(++BAdd$aa(?1EH$*+ZKVf&* Iij2^Faݻn -Bkk+f&Sy.F&%e qqDGE$Z["<<̬L YYDFE~>$MMHLkk+6 } OII o&_K݈Jjk.n7SNERkF__?7xEx(+;FeU hB4 $''FSYYASSj AZZIIITTT(,,$44R%6&6tvGNF75 an hllł6h@1uj$a0詨GΓB]w|c?|>cĤrrrho>h0PIbb"|GT*DQ pvCq+ Q]]]1䪫:&!!ZDQ я~4n]|>9Oeʮ]Pqe2 js1uT某fdTߏN ttt$ Q,6ґ#GGkk+>xڸ{? d_ ҥKYd)˻PO9.7x#_36vSٳO*ky|G$:;;ͱcǸgM#G{_:_hlld˖-O#QpXp! . _>``A㤥 ^Pٽ{7r˥l)|{dffсR#ڵ3(l.'(9qWM 2s6R|ta %wq Ld޼y4$kd'l9Vx ?&#״i+LkCR2k,LѣGYl-ĂY0ĮOyʕc}=wLL +W~ii)&朜Ρs%Ғط?PDGE ,$++Ɉl&*2 ZM^^X,fG)tvvr]_ &DNaŊ ۱̙30ߜ*""G+ DGG~p"N'FA%K܌OhF&&˗? b0Yd)!!!*H*DfHyy9䐘^o j*f3$a2 Ƙaj4k23ӉV,T*9s&f\n7J)pzg=28~ƌcoָffÙ1cF~' qqq477fwnYɭJTT/2\.DQ el6xnz{Μq1Xa$j~4L֯u8 䓓 291w`˘QFO2e̎( ŒK||<JEXѨeԩŢYȾK̜9YArrQQQ#Z@EA III呓Mlllt)66v$y5FZ6[&''qq1$&&0uTrrGKizF)))b4EIOrRQd;5 DEF1N 8F;a4IOO'//J222%;;KcFP(: Ш'5QEYKLLdڵ,^3o<1ڔoKAAze˖~֬Y_E V1}S@?oN6`7A0ho}[;DDDzjV~b#j WfӦMss5`0OC6mژDvqe'͌3?en޽4AYv-7t7Cp'bXؼy3&S(7x[oͦ _„LG(;zӧupp(QB{{ۋILH8?n˅JBd˖-摙q;\|"|;w?):D1s0Are iML<}կ~ţ>? o^^>EBx;rO?47o&""uP(شi?O8v3f`ݺu~{Yre@/p8馛|Af14'#22򗿰~zl6\s ʤZLP$;+GtRWgҐeOK]ݨT@ A ybի)񏱽EB>8O4D>{cʴiM6rItѐ #΋\'葘<'| y,_yJ6}VmMXJ ډg۷/t(..FKqq1p>X\\|֑oυ )cw>Cᅬ{$<8@h/Zk1^IsτوiD![ #l|Y"璒! Gߣ0&R>T(Y%FY+wҨ4P* 8g4md,N/'gS 7DHR9Ok+==xZ11(cbP#t]A<9 I>n7,# JQ/AFvX/$$ Q@ Iz>JjNJw1Q!8wuuRG8{,xcr:|Pd $1y*DIBp[@?mm$'͛.u­R/+'#qg>>N<}t+vW5H A=5LʪMY/h _MM 7ERa0ɡȨ(D磳Z( 9u6RY]FikSr뭷RTTZfppb*++eժU_`q8ȲV $xZ-zÁnGQ}윈_Bss3J-]DFFs\^ EB4!( \nW@EFrlz|>h4 nD__۷opEV(XV^/jшb$ "*괚$YߧnZ-ZVHFE<,c2eÁEEj5j3˅DT"I~z[RVa].EBNCE\.ޑ$)P^gWT#!q8 vjT*$Ir$ R(vP(0 2dYp8B蘎gu=Gep\t:j$Il=tuu1\.<O\Imxnj \CT".+) juA>{ [F,fΜI||YD 66=r|g…wef(̜9Yfa4o~sƗNcҥ̝;%%%FjǾ}$))o0/))Füy1cF`Nomm7߿5^'ֿ7[,lܸ4 sΥhl߾!M^VVF?DEMy~+MҥKj?SUU1Lyz%էϴeXlDFFRh4j*fժUtttgq$&&r7j1 4550{\ṙmǎ$&&@MM GN7#V[[ˆ 3g&22k׎1bv ٳgdF#GrAIJJbjvݻHD}}={졭 iӦq@ee%^v;'77={pqtꪫ9}^/===X,"""`JJ  ?!z<aZ ѢT* ᠡ^DQ$DBBbx=^**+ho$P)UӃ&55tTus:qFBBBX,0o<o660/_N^^(˱cFsR(//wk!66H-[0%u\qABVb illdhh!f^#G~PTtwwhdhhBXXEEErصkuuuȌ3"h``IFE)..f EHHNNFE۩; 2|NJWWtvvt:h4dee0^@X,}.0z<8zh. ̙3 h;Cpq<h4|>---ٳϿIv!++0JKK`0 2$ԩS줺Պ!44x<MMM`ZsFFiiix^JKKfKbb"s9fE+ sw里no| eT:VBBB͝t l6sAzXV|X;v젨(pw}7 @m۶W^yXT*}|ߦwy#G`4 b@(?Aii)Wf֭n,˸jjj7ߤFcF#tuuP(eUVV?H4 ;wf?{Nvv ̞=iӦ͛7OT< xb'\\Ȳ̮]OJFFK.| ቎FaZgeժU|_Eey^6mڄ餻Dz=w ''={~?r~Ò-ZDjj*_kײm6}Qyo`2Nqq1!!!|_g0VX(mݺzo}k ? Hoo/GFF{/YYY( |+H}< ?c$vNK)ظX4͛7 IDATƼgx}^srp:UjV,_, نصs-\h0MM)QޣYd5kLiӦ1sLrrr۰lܹ={p=֭[yGͲep8t!d륺fn݊#** NGBBj>JKKyWXf 7|3qqqW^gʕtvvRQQ( tk7ޠuqSOeΝ˚5k8|0/"|[f144իگ ~dzzz8x ˗/g͚5Mذaw xMF 6{n֭[fW_%22ޙEy :. nW\h554hYoooΟ?OII *իWsjTUUqQ*++IJJL&%?.֯_ٳYx8VUU믿'|c=ba<1|pѽ{wqOJJ V_|QXxŷWٳQSS=Ù3gd޼y̞=Jŋ/Hrr2M]]%%t,))CQ__ٳ?>m۶ 7nڵkΦ^xŋO}vz)f3GfhZF#IIIw}߿9sˊ+8vXh:u*SNڭ=_}wuO>?-+-[h6m̘1ӧO l&&&y駟RUUŋ /Ν;:t(EEE/@1r3z=+VϏ_|S]̘1N',ٵkPx֬Y#BՊFqSl6,[ 'zj/^; |c}VǓKII ꫯҫW/fΜ)̎;78n3eeNqq1_|һwo-[[O?d"++;3hkkJ wyǏ_hŝ{…޽ V\ɖ-[~Y2:uSm6$R^^Nyy9 ,\ZMjj*/J-m2"Rl6{xzztҫa:`񴶶RWWFċJBVZؽ{e2vXZZZhjjb„ הb؈V%--ML`ӦMl6z-QG>}Z-^XXHMMUfEZZqqq>VgyIϤT6Zm~Ӟ3rH9{;v`_z̡E ۛ2 (+/hq5NkĈ477DTT^kߥKέ @\U֬V+/_CxhW5LJaÆQUUŞ={ٳg'66V+iii{ov._ٲe  `ڴiOu &Э[7fhwZ~=z^h4$bqIDDDKkk+v"''fW2|pf3ĸ677p8&((cǎa8y$ 994 W^wj[[UUU䐗'&J|V.g&..N,l6QTD"kMM >>>8jt:Eζ6l"b*err29r\"##5jƍ#::1c_/+V]322dL-n̙3dddZ"6q߾}C G1a><x{{FChh(TVVIJJY[yFŔ)S;v,GҥKFN';v梎^{w LLL 臨V /{?__BNe"''ļyDy,**"%%w=/'O/#??_(Mv`0裏amX,9r233tpxpA~~\,|'Z{`M{[Hy# S"11VmWZ[[Gkk+|(k#&&={ޞ0Jxx L0AXhrssϧYf1dF#W\!44DZZZD\2ɰZv?aaa466ꫯbXXp!iiiuV=*JVf3MMM$''R/Yz5DFF~>S"##QX{])9|%cԩ???Ю>[*jЫ=<<8<Çcٻw/;vogԩ+BBB*rXVt:{Dh4KLL fv;QQQ=$dΜ9_$P^Zm݆FСC?Nƍq8̘1~4N>Mqq1ΝcʕhG_ĉbvzRR/o5ʻD)qUUU+! (//gժUZfҥFbccEuu5 wA.A׬Yө|_|=Ju]{nߦKW石b ;Y}Q~ōMywL&7`z5m4\Œ3!==Cjy뭷bU|I:e2rKZWii,477$],}Y2dH.8N![aa!squl Jluu5[ne֭hZ&OLϞ=HMMڟlv_-EFFg#---<#,^FCLL 111BS,Q*;u^^({˪1~xq؛#G2rHĉ9z(G)iX,f$d2 |JJ ݺu #$$h4Vh4"×Kܽ*VXX455 }kk+555"V+)...B7nÃ=zEaa!!!!V1LX,Z[7VB) EoN';w$??<<oK.hHFFcǎ%!!ñZXVBCCE[)--%==9s`҄RJ^^Vz ~6 Npp0~~~fL&h4l6_5 4@z=#** J%\&l6 e?&aÆ@UUyyyfHCRQ__O}}=:tO>0bbbDWUU6֊L*J(Qo?>>Aii)Zٳg_ f1yd/^̒%Kӧ&wMrX,l߾mFcc#=Ⱦ)SN Q&Gw7HIZj=ᆖQyvUZK%KN(_<V^-dEEEӭKGf3o&:{/"00 .0c oLUU!!!455ƈ#. ==tRh… Ÿ`0f:t(/v/)n6 Ç)S_`4innn26oLTT՜:u@4HcGTTqqq>|X(b&IܸI}}1V\I^AӱaN8sp8<QYY|>(kp88{,'NG=z`1W2Ls"z˅QdKII.Ǐ*^Ċ£(A:]{DBCCQThZΟ?ϻKxx8'ObZ,QLngƍFJJJ8y$*wN~8z(}p8 %..???FG}$,fdIHH0XVzyyj%''HMM "ˋg|2Νʙٳgٴizݻw@xxsMABBf֭ 3g֭[ )+**HIIo߾TVVCxx8jZd(:th-[Ç3}t}!pcKnؽ{7}&Mŋ;<hZN>ܹscTTT㏻=d.]صk*quJ۷OOOC]]k׮%11#Fx 4[t)rhZrrr/2dWwСC@SG7$~3f .]rWY|9<35Kwyf7eɓ"Skڄ^~e-ZDqq1?0gϦo㮻BR{2sLLFF|GfO^v^hWz)0;;{K^q&NHFF65kְm6L¸q;&j#ɫ\›oѣG|k*XVtx{{V{ *MMM aCCCݻ7ZJJJ%11^OCC0F J˗)**JE||<)))Tڎ( )1` .@XXHmm-n+X'N`0 --ݻS__`Z֖ѣ߸Q\uS*))ĉXVN%ϣ %((K.v466 ja}Sd|2z"##xgPϝ;dfR]]Mnӧxql6j]fk%]IDAToT58 EPYYyyyl۶xdu&Kռ&1f2<>>^dЫFC=0L+b, .\ &&hF#:N/bf%xZZZ$''z" @&pv;$&& e`0xw8q" ,I(.^`p@TT111Z_~t:9y$Af0 eooo'ŚhDRBBP ĕ+WHLLGl6"OUSSΝm Aբhk`Inwh(--͆'aaat޽˘CWx]_d۶m|WL4;RwqݛHIw߻wo#63gpqjkkeذabK/`T*El}q ;)uuu|t:ݻ7 "**Jsw^Sb4ٳ"6p&W;ʩK=zJjjHTl2͛/Fh~qСC۷O( Ce~z4[Zq%+55jPQQAHH :T|KN:Ekk+dddRXr%fv˗/l6_S..e)OONuF6n܈` %%L:SSSCXX "33d6:PVVF߾}صkv&{)**BV3rH233\dٲeXVz)8z(里=z`۹t=S2?N")+]vQ\\/KfRR&L@Ѱj*~߰dYFF~_Y]ۻpBO'q/{e޽=}w^Ν;'5]Y*Q+{̝;W(O]26ytUfI}U~׍b]פ e*kǩRT*pEC:7tqV~716+.\KV>Wu\\qAyn;k?vlkK/DI~ܹ۷3vX&NkI[n%;;b̙ի9tP%[֝Oɒmu+efd6鍯Uǵ&`תzu0;W|x̘yꩧIH~,|/% >%ZHI'H$ODyyyq֯_/-H$u //W+KH$Dd*^JV%,,'N]%D" OOOjkkvtH$Oz|畕444гgODI$xzzRWWGCCF#H$ɏl~|tD"uD"H$?EdtD"H$D"RH$D"H$@*QD"H$DrH%J"H$D"HnDI$D"H$M (D"H$D" %H$D"H$7T$D"H$&JD"H$D"RH$D"H$@*QD"H$Drx_\nFRpH$_<==Q8D"|' dmIENDB`sqlkit-0.9.5/doc/img/menu.png0000644000175000017500000002442411714210425015370 0ustar sandrosandroPNG  IHDRI\sRGBbKGD pHYs  tIME : IDATxwXS]A@D mŭ8q8nĺWujkUܭuU "R IL$ y=y=7q{dd@RTA! $:&fukEkS^!A@RsqiM魅y!$]?=Nz#Dy[NŋhӾ3rss!Hfzj^6s/79%&OKx5Agf*(]P"[Ϛo'pu]\y+\4+[%ϥWЫx4n~ݱ`D,e_LR.EEEXr5Zim/֛V[A*ʷm޺cF Gp5x ͨ+J*,,D.q5jo\zUɯM^uޫW2*},]>+O]4qz;;2"ʾdR._⬴KWkX:6mEФ 022z=zS؟b{VY qh  :6P&?TF(]>Wٱu3lہ[ YݺVHt-KF_ceHݰnf|n݋5a668qOi3hM?;a\|s/­wWΗ/o^ml)4//zj a}db$>z3x 0`!%5 Y3|+ 3+ L,]Jq !?fx6Me+W6s `R[.C#|rBNN.Xtp8pqv ۃ&MDV-1rX4ngg5+W`5hҴ7*-[6k̙5йvm(kϤ 1p0Me+Wݳ;NCXZ_]KSٗ\fL 7W70@֛փt4?敠z "Jq 0'%TDA+y6Alٴl6|h=RV$ʅW Ե R SNm+}aLyWX=EgQ+D"K\!j+**!Rtw^%̀ǣ!.*a'!.5Jf%0`XɅ QJgO1rl<O>E8'Oh]v D")~W,ɓ'HOOGfЬY3όE߽[oI&07;8eZJuk5|qVqEjԝtٛasqqйsgx$<c /&>NaU`r!;8KQFϞ=Sd⓺uP(*?Fli:Ż}hwwyO@{__wU-FMNuSc8U 9k6\=дYZMIJJF@X{x_ >gѴ+V§Mŭ"W#~'h4TP􆇧7΃P(Tcк/:G>xr2ǏPؖ-sM]56y69{{# p,^gd_az0 ^ƥ{(5rW(ϐ *\g~u7ƍ ]¿~-233qz$Ο;W^18qbcsnXT龲˴Gdl޲ܙS #˗Ph;s /RƽzHOkWpeRWQQQ<~} $tr1}֭ߠb߰'ƴkS,0ѽ;s1&+3IMMf&3]Tm ?Wg_|旟fRSR⟏1d֝elO&+;}TU0dgA~.gܘĄx>^^^L|}RpM,kMexJ~W駟2cߓ?e5jOS>OOOXyyy)2-E!?ZϏ^F<x{{3YZ]S9jҟ{J_MMMzJW~üj405c1f՘d hVpzكo-*Eff&]Km߃oY@Æ-NN͵ܾ}V@\\ *+O錠 [[[q|<995Khii)bX͘> +Vu<Z]rԔ7?ڶU;SUby p9fŬYl9EGçg(,,D]kkEll8?͛^ÆiO #,<X$LaMfxFF"99sCUH9'Oyb1>DPT4h /Y>>o/ I3ŒӰ}9c N{ec 2t~m/5jr?SSSH$Y,4j>>>߿?1Æ ðaC1bp1puuEbb"l0%k$>z4]F`n]@u`^h,,,ѪM;t*)4֭EXx8ybѺU ))Aӷ,SѶm|1K{ 8k[x |woйvp8`kJ{e\"R(88rZlv]0oj;pJfw}OD"!,{KKK|h! HzJq4Zp^>VBehزe+Μ^^^J1C$,\9Rm|]uE&M ~~~011A~~>YHRc?={]*U M},23`jj(ĉwį*07Dsss̟ T 333:G'8:6;sR"kT%* ǃ z?\={B!8  H A!ļI=1'b^^TOZZ6mی4]p5&,ĞiO`Qбŗ8j.j\+>ʖбØ5\]1%k:vlIфXJ hύs#r2 Eb!FH,DiHH,$&ǡ*~a̚3S3s.**fc_mTAq$ذi-OwlMk$Ś1u:fLQwOlD0Mhp"vGCgWn~@WT%K3xz7;wôQgNdKS\>cgRl6ӛjY=yFFT$F Nً^FkW\m.rbYXX,cK Kisc[8,sf̈́H$BXRӇJȡG178whL?|K+޹XZXԪ_4@!W^ !TWkؾ}'Vü.s2? bDUw*u`HIME־01+~޻HCԞ.eP+Tcu8אiA@B[ 5 ~'Z ^~)H)`-0QX5y|%\0@Z[*m5թ1۷9XK0ds&;jXD7d֤D^Rׂ0""''G-ł :9_MTakW'$ZRGn[X@,VEj2kQXX={[w$Y`9E`9uO?Ų_bw4Y5 Z4%a! aQ$**F~?\rE]Y@?,BX$cz]b‡>YXMXׯaee%.Qd'i'6l܄ Kѫgy\T 5 XmPlzlǓic?ZΝ:)('<;;;ˬ1kvb 77W-x5!rqqP1@2IJb9{{# p,^gd_q:fL)k~q5?S&g@ @\\/ þ?#tWR5Y5 D'6m܌YgEZ bcc`aaѣƨ),0شy=rrŦ1ƴ;8~̧fΚCCCsBCY}|DBSU+((]}(ݹM (9om [9@Ec~ rykV U|Bg0(vM5 Mw11PO^^^fd"eOz CX._Ƹq}hZ-vo[M?t*FaD!4nL6D-bmܴ[lřKk!"0jHB$4tRRRԧ9 yl0%9q Aaa+LJ 8{4Q{XA!A@rRѯ㫮[nMM8QO.?0Lzrӣ)dCQ`85,zˇ`r20̛4vQw ̓,'; R$l >̓k`$E`}-X~ gD5L .jD"sw070 0,?07@zl9 WZ[Վ&+e;8~WgSr_uCT "P􆇧7΃P(zJ/R>6):ˡҨ Z{B+v2^`wk\N\Ç0>C QqD؏Ą8s -Sd[ܖhh )rn Y <rNڒ-[ȡQ“'Oѱsyx붾copw+^>##{8h3wpDƒr@HIzoZnSзOo۫OVmq`Rf-Zȡ5lܻscJsX3@ JI]A:$ҳ7B}9U%MV;Xh:*: ">|>T6Xy64dz'p`^q b V`g'k)*: "kkk|%%%4r͛7`ƎAB@CQ/B PPހTXXid9{Qg9TuD %K%4ƍc;! H Z4IDFş'Yk0v|MV;ZO B|ZPiY- aggsƳ58u <cءG';H'ӳX1v21B!U佝F!b H A!A@B$mv?$ hO`In\Ч믓zU.K5 Oa/йS'D^Gr2;q B]Ӧ#`hXYYEǝ W -ZAlkIJJF@X{x_-[OEQui1Ձ>}׮aРsgNEj2>c1q 6ݰ$l)V?aκGoԥ6";d[]8ypWQgOG߽ [[[6]}ZE\lWFEź,i1H-c8_:^zw疼2-E0.0dgh {eV?aɺGo4Rquuh^o?P]φukFpnJʬ~*.HuhJkmfN-ATWٳݻ/-`g_c:C: Z%M7cxXXX`1*_F;)((]}d+ 1io`A?ڶU@C -5bNH(N ټ<B/aUӂ\|CEFTgjjJ5CԞA:DD˜18x`?ZVqcoB.MeV97c3HXpܨ#F ;[˯Vn :)))h3{,,,WKfȫW xaG*_Kisaii)ë1R)o4q<{%Wt*Nً^FP ".ٓ(((StaCG3ue_*m!pk{G{ ++Kis<8bXe|3O!Hu</%'"6mBX:A], X,ap\qCB xoB͛~[rٳTX[[=VU/V㩣w000Uq޶2#TC}'DFF$ ^x+WE7ƎvB  ==|OXx8233%aa2_4,ӧa0s ʻ< D=k&8N] GAA~a|u߯g F:i~zu[_XY}й!0}X*MÁ 1La{yӛ ҉8v *WKo:DJ~<{hI!ʄ``$(3 4H'A@B$:T Q .j4o^Q ?-~x3W+4qo;,6Q&z+;kS|bi7y8+Z&j@;h&(K!e6 o_Ύ6`8LGp ;vTu|=}^xQkn qnn?D綞H"π #C$R"8\;pnZXby2d牰p,|:GP Cn0uwA`7gqadȁH<Z7u†ñUȉn]je9u!fqPP(͂,g Hpt+$ Bt@اY0Av?HQ~ aO3QfţNi{G޳m} ~][8p0:u7CyC QqD؏Ą8s &GEPI*6 UF!"@>#GW|缿yOC.uxKF4xR$Q(¹1^ǹ5T%{YߔJ}#baee%?YhcSr[E[|̜>տ.֩SՆؙ!/f\$IQ P !F^bY,B8ۙi]oQf_#ʶ{;m KNNkh]`c? Z9Y%[*ą5ec.j!w@eVdZ(o+mTcux\,9n?@ĥg QD. /G+'): path = os.path.join(self.standard_include_path, path[1:-1]) path = os.path.normpath(os.path.join(source_dir, path)) path = utils.relative_path(None, path) try: self.state.document.settings.record_dependencies.add(path) include_file = open(path, 'r+b') except IOError, error: raise self.severe('Problems with "%s" directive path:\n%s: %s.' % (self.name, error.__class__.__name__, error)) try: include_text = include_file.read() except UnicodeError, error: raise self.severe( 'Problem with "%s" directive:\n%s: %s' % (self.name, error.__class__.__name__, error)) if not 'docstring' in self.options: self.options['docstring'] = '__doc__' self.arguments[0] = self.get_docstring_file(path) return Include.run(self) def get_docstring_file(self, path): p = Program(path) tfd, target_path = tempfile.mkstemp(prefix='sd-') f = open(target_path, 'wb') text = p.docstring if 'verbatim' in self.options: f.write('::\n') pat = re.compile('^', re.MULTILINE) text = re.sub(pat, " ", text ) f.write("%s\n" % text) return target_path def setup(app): ''' Extension setup, called by Sphinx ''' app.add_config_value('templates_map', {}, 'html') # Sphinx 5 support if '5' in sphinx.__version__.split('.'): app.add_directive('docusage', IncludePyProgramDirective, 0, (0,0,0)) else: app.add_directive('docusage', IncludePyProgramDirective) class Program(object): DOCSTRING = re.compile( r''' """(?P.*?)""" ''', re.VERBOSE|re.DOTALL ) def __init__(self, filename): self.text = open(filename).read() self.docstring = self.DOCSTRING.search(self.text).group('docstring') sqlkit-0.9.5/doc/layout/0000755000175000017500000000000011714210425014451 5ustar sandrosandrosqlkit-0.9.5/doc/layout/contents.rst0000644000175000017500000000242611714210425017044 0ustar sandrosandro========================================================= Layout GUI built on the fly - A GUI description language ========================================================= This is **the key** of the mask widget and is really what makes writing a layout with SqlMask so easy. If you know what **glade** or other gui builder are, think at this part as the "markup sistem" for html language. Using this description language to define a layout we try to use as little writing as possible, we are asking sqlkit to guess all the properties that it can from the name. What can be guessed? A lot!!! Imagine you have a table with movies, and a field is title, anoter is director_id. The database knows that the first on is a varchar and the second is a foreign key. It **can** guess how you will want to represent it so that you will just need to you thei're name in drawing the layout:: title director_id The biggest gain we have when setting :ref:`relationships` between tables. The code of the layout package and probably the definition of the language itself is deemed to be rewritten, as was the first step into python in 2005, but it proved to behave well in these years. After looking at te ReST markup syntax I am tempted to rewrite the language in a simpler way. .. toctree:: layout sqlkit-0.9.5/doc/layout/layout.rst0000644000175000017500000003515611714210425016532 0ustar sandrosandro.. _layout: ===================================== A GUI description language - purpose ===================================== .. sidebar:: Note this page needs to be reviewed. While it should be mostly correct when ported to rest format has not been reviewed. A complete tour of what can be done is available in the demo The class Layout is a class that understands a GUI description language to create GUI on the fly, with minimal effort by the programmers, that doesn't need to know a lot of Gtk details. You cannot do *any* possible thing you would do with gtk, but methods are provided to refine the layout and it isn't difficult to add GtkObjects or features. The purpose is to easy things with simple setup, not to cover any possible graphical layout. The main purpose is to be used in designing layout of record editor (:ref:`sqlkit`) for db, but it can be used with any other program as well. Layout is not an interactive tool: it uses gtk.Builder xml to create the GUI, ie: it produces xml for gtk.Builder. .. note:: gtk.Builder up to 0.9.2 sqlkit used glade. This dependancy has been dropped in favor of gtk.Builder that is directly available in standard GTK installation. Care must be used to create names for the elements that are unique in the single layout, so as to be able to reach any single object, not only to refine configuration but also to interact with the GtkObj in the program. This reflects in the fact that the name is unique. A normal GUI is plenty of labels and tooltip. At the moment of this writing no localization is provided but gettext support is in program. ============================ Description of the language ============================ a starting example:: layout = """ el=first_name el=last_name el=address - """ A layout is a sequence of tokens and display modifiers. :token: describes a GtkObject (Button, Entry...), may also be a compound. An example: el=first_name. They will normally start with a description key, followed by a '=' and then followed by a string possibly separated by a '/' and.or a:: key=string/variable:wid1.wid2 or in regexp notation and more precise:: (key=)string(.[0-9a-z])?(/variable)((:width.)(width)([-<>]))? key txt id var width1 width2 align no element is needed apart the 'string', the key defaults to 'el' (Entry + Label), the variable defaults to the string, wid1 and wid2 (described below) use GtkWidget defaults. The string part may end with .[a-z0-9] just to be able to refer in a unique way to single elements that are apparently equal (see :ref:`names`) an indication of the desidered alignment may be given using one of ('-', '<', '>'). this take effect oly if the element is position in a gtk.Alignment (eg. 'ae' element ie: entry with width). in that case: :>: right alignment (xalign = 1) :<: left alignment (xalign = 0) :-: xscale of the alignment is set to 1 i.e. the child of the alignment (the entry in the example will grow when more space is given to the widget) .. sidebar:: Automatisms with sqlkit Note that when used from within sqlkit layout definition any text field is rendered with entry within an alignment (``ae`` element) and an alignment of '-' is added to any text field longer than 20 chars. :spare token: A token may further be: :@: it is equivalent to a newline (in fact I figure it like spinning ;-) :-: forces the previous element to span one more column to the left :^: forces the element above it to span one more row down :%.*: the element is a label for a notebook (see 'notebook' method) :display modifiers: A modifier is an element that modifies the way elements are arranged, mainly it modifies the packing info or the container, but can also modify the display eg: label and entry on the same line or one above the other. A modifier is: :{}: a pair of curly braces :[XTHVbNPSvhpOBmMFA](.id): (possibly) followed (w/out spaces) by some opts as in {V r=man/sex r=woman}, this will create a nester layout with 2 radiobutton packed into a GtkVBox. For an explanation of the (.id) part see below (container naming) Possible opts are XTHVbNPSvhpOBmMFA: :T: gtkTable :H: gtkHBox :V: gtkVBox :N: gtkNotebook :F: gtkFrame :M: gtkMenubar :t: gtkToolBar :h: gtkHPaned :v: gtkVPaned :S: gtkScrolledWindow :B: gtkMenuBar :M: gtkMenu :m: gtkMenuItem :O: gtkToolbar :W: Window :E: EventBox :X: Expander :p: ViewPort :A: Alignment Elements will be packed in a table widget unless changed by a option when instantiating the class as in:: lay = Layout(lay, opts="V=") Other values for opts are ``[>=-|]`` - to be confirmed :=: implies that label and entry will be displayed one over the other (this is the default) :-: implies that label and entry will be displayed on the same line. :\|: will draw a line around the widgets, using a frame. That means a GtkFrame and a GtkAlignment are silently created. You can set properties of the silently created expander using key F.id :>: will encapsulate the result into an Expander that allows to collapse all its content via a click on a little arrow. You can set properties of the silently created expander using key X.id. A double '>>' results in an expanded expander. The id is used as label for the expander. so that '>>.clients', will be expanded in an oped expander with a label "clients" a longer example ---------------- :: el=first_name el=last_name el=address - {V r=man/sex r=woman/sex} b=register Comment -------- A '#' sign starts a comment. Anything till the end of the line will be discarded, as usual... el element ----------- the most used element will probably turn out to be a Label + Entry widget that can be constructed as in el=first name. It will really be understood as:: { l=first_name el=first_name } or:: { l=first_name el=first_name } According to the opt modifier = or - list of elements ================ A partial list of elements: :l: Label, :L: EventLabel, a label in a GtkEventBox :e: Entry, :E: Event, :b: Button, :r: RadioButton, :c: CheckButton, :TV: TreeView, :TX: TextView, :TVS: ScrolledTreeView, :TXS: ScrolledTextView, :cal: Calendar, :sb: Statusbar, :le: ComboLE, more symbols are in the source... custom widgets --------------- Occasionally you will find yourself in nee for a custom identifier. You can create one and register it. Have a look at the code in layoutgenerator module. Calling Layout ============== :: def __init__(self, lay, level=0, parent_container="W", opts="T", elements={}, container_id='',title="Window",dbg=0): :lay: the string describing the desired layout :level: nested level (used only by layout itself) :parent_container: a flag among the container flags that says which is the container: determines the packing info for the result. Default is W (Window) as a toplevel window. In this case info for a toplevel window is built. :opts: This string has the same meaning of the modifier string of a container that can be put directly after the '{' char. Mainly this is a way to force the use of a container for the outer call of layout. Default is to put all widgets in a GtkTable but any container can be chosen from the container flags. A special component of 'opts', is 's' that asks to add a StatusBar. The method 'sb' is provided to talk with that Statusbar whose key name is sb=StBar. :addto: follows the name of a container that will receive the resulting layout. Must be a container that accept 'add' method. methods -------- NOTE: some methods can only be called after 'show' has being called. These methods use gtk directly, not glade. :xml(file=None): will produce the xml needed for glade, may be write to file :show(action=function_name): will directly call glade to display the GUI, returns a dict of Gtk objects whose keys are the element *keys*. After this method there is no point in changing elements[] properties. The action can be gtk.main. :elements: a dict of all lwidgets. The keys are the element definitions. Any element can be configure up to the moment show is called. :conf(el,property): will set a property for an element of the layout equivalent to:: elements['el_key'].properties['name'] = value :sig(list): will set handlers for the signal for each widget. The list is a list of tuples of 3 elements: * el_key * handler * signal. If the last is missing, clicked is used :tip (el, text): a tooltip for the element 'el'. It tip is called before 'show', it will end up in xml+glade, otherwise it will call gtkTooltip directly :sb(text): will push text on the StatusBar :menu('el_key', (entry-name, handler, signal=activated), 'i'), (), (): Will add MenuItems (Stock ImageMenuItems if 4^ element is 'i') :notebook('el_key', ['label 1', 'label2',...],position): Adds the label to tabs and allow to set the position (top,left, right, bottom) for a GtkNotebook. There is another way to obtain this effect. You can add %label as first entry in the block that will be enclosed in the notebook. See below: notebook :frame('el_key', 'label'): Adds a label to a frame, and writes it w/ bold face :prop(el_key,property_name, value): Sets a property for element el_key :pack(el_key,property_name, value): Sets a pack property for element el_key .. _names: element names ============= For each element of the layout we need to build a key for xml, so that we know how to refer to the gtk object in the program. Layout will build the name starting from what was written in the element description, if that results in being already used it builds a unique name adding a number, but that may result in difficulty to interact with it from inside the program. If that's an issue try being clear when creating the layout. Use ids, the string separated by a '.' that you can add to element names and to container flags: :container: :: {H.Z {B.id0 :element names: :: e=name.id1/string stock names -------------- if the name of an element starts with 'gtk-', use-stock = True is set for that element. Notebook -------- A notebook need some labels to identify the tabs. These are other child of the GtkNotebook container interspersed with the content of the container. You can set them with the notebook method, or you can use a symbol in the describing string using % trailer:: {N.0 { %first_tab_RIGHT TXS=one } { %second_tab TXS=due } } will be equivalent to:: l.notepad('N.0', ['first tab','second tab'], 'right') Please, note that you can enforce a position with a trailing _(LEFT|TOP|...) to the first label. Note also that any underscore will be substituted w/ a space. Automatic name mechanism ---------------------------- A normal element definition is of the form:: key=string/variable:width.height * the :width.width part is discarded * the rest is used as key, *but* * if no 'key=' part was explicitly used as in 'first_name' two will be created: e=string, l=string container naming ------------------ Containers need a name too. You are supposed to know which one is used: it will be GtkTable unless you asked for a different one. You need it in case you want to modify its properties. Their name will be the container flag [THVbNPSvhpOBmMFA] possibly followed by a dot . and an identifier. The identifier will be a progressive counter, starting from 0 but may be imposed appending .id to the flags of the container, as in:: {V.my_id first_name last_name } Functions ========= ``map_layouts(filename=None, buf=None)`` This functions fills in a dictionary widgets.info whose structure is: name : (label, tip) when a widget is created w/ name 'name' the label will be set to 'label' and a tooltip will be set to 'tip'. This can help a lot in many situations. Db field name will be mapped to user friendly labels, translation will be as easy as pointing to a different file. The function can point to a filename or can read a string. You can write directly the info dictionary if you prefer... Layout class implementation =========================== Each layout live in a container (may be a toplevel Window) and creates a container to house its children. This can cause a little bit of confusion so I call parent_container the container in which a widget live and container the Gtk container (Table, HBox, VBox...) where I house layout children. You can think at your layout as divided in blocks, each one named 'cell' in the code. A cell is or the definition of a gtkObj or a nested layout. You can see this list for debugging purposes with method _dbg_parse_layout. You can think at these cells as displayed in a grid, each one defined by a row and a column. The constructor of Layout uses a dict named 'cells' with indices (row,col) to store the name of the corresponding LWidget or LContainer. When the object is instantiated, Layout 1. creates a container (LContainer object) 2. splits the layout description into tokens that are or -a element descriptions - nested layouts (starts w/ '{') ... For each token that describes a single element, creates the corresponding lwidget object, for each nested layout creates another instance of Layout. LWidgets and LContainer ------------------------- the difference between an LWidget and an LContainer is that an LContainer has children and must pack info for all the children when producing xml (apart from producing the xml for itself) Window creation ---------------- Each call to Layout must create a container to house all its children, but the first (or outer) one needs normally to create also the TopLevel Window, that to us is nothing than another LContainer whose (only) child is just another container (the outer one for Layout). Producing xml ------------- Layout produces xml just asking to the toplevel (an LContainer object) to produce xml. LContainer xml, produces xml for itself and for its children, if some of them is a container the process iterates. sqlkit-0.9.5/doc/html/0000755000175000017500000000000011714210376014105 5ustar sandrosandrosqlkit-0.9.5/doc/html/.buildinfo0000644000175000017500000000034611714210376016064 0ustar sandrosandro# Sphinx build info version 1 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. config: 99151b4463c452d4f27a37da35f0a250 tags: fbb0d17656682115ca4d033fb2f83ba1 sqlkit-0.9.5/doc/html/sqlkit/0000755000175000017500000000000011714210376015414 5ustar sandrosandrosqlkit-0.9.5/doc/html/sqlkit/widgets.html0000644000175000017500000003430411714210376017754 0ustar sandrosandro Widgets — sqlkit v0.9.5 documentation


sqlkit-0.9.5/doc/html/sqlkit/completion.html0000644000175000017500000013214511714210373020456 0ustar sandrosandro Completion — sqlkit v0.9.5 documentation

Completion

../_images/completion.png

Completion is the way you can avoid writing too much if the system already has your data. It means you write some text that will be used as filter in a select statements of all possible values. Completion is active

This operation has the following main actors:

  1. the search field: the field where what you type is searched for. In the image, ‘f’ is the typed text that will be searched for in the field last_name that is the search_field. The letter ‘f’ will be used to filter the output. In this example the first_name (Roberto, Federico, Françoise) is not part of the search, but is used to better represent the possible matches.
  2. the object that should be returned:
    • the string to be used (as in enum mode, or in completion in values used in the same column). This is only available for char/text fields No validation is done by default on this field, the completed text can be further edited.
    • the foreign key (frequently an id) along with a representation of the referenced record (e.g.: the fullname of a director). A check is made: there’s no way to input a text that is not matched on the remote field (see foreign key description & search field below).
    • a complete record as when we edit a relation and we use a filter on one field but we aim at setting a complete record. E.g.: we select an actor from a list and we may be filtering the name or the nation. See example 40d.
  3. the representation of the result: when using completion on a ForeignKey or on an m2m relation, the representation of the possible values needs to be taken. In the example of the image ‘Fellini’ the the representation of the referenced record that was referenced by a numeric id.
  4. the operator used when searching: normally regexp or like. In the image here you see that ‘Truffault’ is returned that does not start with ‘F’ but contains ‘f’.

The match of the completion has these modes:

start:partially written text is used as a filter and completion must match from the start of the field. It uses LIKE operator
regexp:the match is done via regular expression if db supports it or via LIKE operator adding % on both sides
enum:any value matches. All possible values are shown. This mimics an enum field. When there are only few values this may result more natural than the others

Since completion implies a search that may return many records, it’s only triggered on demand. Normal binding are:

shift Return:triggers ‘start’ mode completion
control Return:triggers ‘regexp’ mode completion. This is also triggered pressing the down arrow of a foreign key
shift control Return:
 triggers ‘enum’ mode

Pressing Alt along with the other keys will prevent any Filtering completion

foreign key description & search field

When displaying data (and data shown by a completion are no exception), a foreign key is substituted by a more descriptive text. Let’s see how to customize it.

Make sure you understood the limitation on ForeignKey expressed in Basic assumptions and limitations

You can customize the way a record is represented by:

sqlkit.db.utils

You would use module sqlkit.db.utils and create an object Table Description:

from sqlkit.db.utils import TableDescr
utils.TableDescr('movie', format='%(title)s - %(year)s', metadata=db.metadata)

metadata is necessary so that TableDescr knows where to go and auto-load Table to introspect it if you don’t provide the search_field (see below)

_sqlkit_table

It’s possible to write in the database the format string to be used in the table. A table called _sqlkit_table is searched for (firebird backend doesn’t allow leading ‘_’ in table names so ‘_’ is stripped for it).

search_field:The value of this field is used for the search. If no such field was defined, the first char field of the tale is used, if it exists.
format:the value of this field is used to represent the record, e.g.: “%(title)s %(year)s”

You can easily edit this table using Sqledit - the standalone program to browse and edit data

format and __str__

When SqlWidget creates a class on the fly it looks for the ‘format’ field to add a __str__ method to the class, so that this representation is used whenever suitable (e.g.: when a filter action is performed in a Mask)

Autocompletion

You can force completion to start after n chars has been entered setting the completion object autostart value:

t = SqlMask('movie', dbproxy=db)
t.completions.director_id.autostart = 2

Completion will be recalculated every time the written string is shorter that the last text that triggered the completion.

Take care not to use a little value for autostart on large tables.

completion and Return in Tables

There is another situation in which a completion is started automatically. When editing a foreign key (or an m2m): if a Return is hit, a select is issued to check if it’s a valid value and if not that value is used as base for completion. The difference from triggering a normal completion is that if a valid value is found, no further completion is done.

Warning

Since completion uses the already written chars to filter possible solution, if you further delete such chars you are not seeing all the real possible solutions but only the already retrieved ones. You can request a new completion...

Group_by

There is also en easy possibility to add grouping of completion via a foreign_key attribute. It’s enough to set the group_by attribute of completion_group_by:

t.completions.director_id.group_by = 'nation'
../_images/completion_group_by.png

Filtering completion

You can programmatically decide to filter what a completion returns in a very easy way using django like syntax (the same used to set constraints):

t = SqlTable(Movie,...)
t.completions.title.filter(title__icontains='love')

this line will instruct the completion to only show titles that contain the world “love”.

Filters on a field that is a foreign key will be relative to the related table:

# nation_cod is a field_name of the table
t.completions.director_id.filter(nation_cod='ITA')

will build a constraint on the director table filtering only italian directors.

If you set relation on your Director class as in:

class Director(Base):
    ...
    nation_cod = Column(ForeignKey(Nation.cod))
    nation = relation(Nation)

you can set filter on the completion based on this relation:

## nation__code (note the double underscore!!!) will trigger
## a filter on the cod field_name of the relation nation
t.completions.director_id.filter(nation__cod='ITA')

dynamic filters

It’s also possible to set a “dynamic filter” i.e. a filter depending on the value already of another field:

t.completions.last_name.filter(nation='$nation')

In this case the value of $nation will be set using t.get_value('nation') In case you have a related table you can go back to the main table:

t.related.movie.completions.title.filter(director_id='$main.director_id')

enum mode with foreign keys

Enum mode is the way you can mimic a standard enumeration field: you see all fields independently from what you have in your entry. This is more natural in some circumstances if you only have few values.

You can get this behavior all the times just hitting Control-Shift-Return or in ForeignKey fields double-clicking the down arrow. Since you probably want this depending on the values of the table you can programmatically choose to serve completion only via this way setting force_enum = True on the completion:

t.completions.director_id.force_enum = True

enum mode w/o foreign key

There is another way that mimics enum mode, i.e. setting directly the possible completion values via the method set_values:

t.completions.status.set_values(['open', 'closed', 'waiting for input'])

or:

t.completions.status.set_values(a_function_that_returns_possible_values)

the signature of this function must be:

my_values(value)
Parameter:value – the value that may have been written in the entry, used to filter values, as usual

more customization

There are normally 2 possible completion according to the filter level. An example can better clarify: suppose you have an entry where you are supposed to enter a username. You set a filter on active users, now you need to fix an old record that really has a user that is no longer active. You need to loosen you filtering criterion momentarily. You can do that Pressing the Alt key along with normal Ctrl-Enter or Shift-Enter

These two filtering criteria are stored in two session.query objects and are stored in the completion with attributes:

filtered_query:the query with filters. You set filters on this query with filter() method. If a filtered_query already_exists, filters are added, otherwise it’s written from query
query:the default query, used when no filter is desired (Alt is pressed). You set filters on this adding argument “main_query = True” to .filter(main_query=True).

Of course each one can be customized with the normal sqlalchemy syntax also.

Note that if you have both filters (with and without main_query option) order makes difference as filtered_query is built based on query when .filter is called for the first time. If you change query after calling .filter() you end up with unrelated filter condition (that’s allowed as you may really want this).

Remember that Validation is a completely different mechanism than completion even if it’s not possible to add a field that doesn’t come from a completion.

customizing description

The query attribute of the completion determines which fields will be present in the completions list, the format attribute decides how it will be represented. Default value is determined as described above, but you can customize it as you prefer, as far as you use fields present in the query. As an example:

t.completions.director_id.format = '%(first_name)s %(last_name)s -- %(nation)s'

Behavior on completion with m2m/m2o relationship

When the completion is in a SqlTable that represents a not editable m2m relationship (as actors would be for movies), the completion does not simply add the single field but substitutes the whole record.

On such a relationship table’s completion you can set filter that will act on all fields. Rationale: if you have a movie/actor relationship and set a constraint on actors so that only female should be selected, you probably want to retain that filter independently from the fact that you select the nation, the first or the last name.

Public API

class sqlkit.widgets.common.completion.SimpleCompletion(master, widget, field_name)

An object that hold all the information on how to retrieve possible completions in simple cases

autostart
A possible interger stating after how many chars completion should start automatically
force_enum
Modify output: show all options regardless of what has been already written in the field
group_by
The field_name on which the output should be grouped. field_name will be added to the request and used to group completion options.
filtered_query
The query that will be issued upon completion request. You can customize it as for query
query
The query that will be issued upon completion request if Alt is pressed (i.e.: not all filters applied). You can add filters width filter() or via SqlAlchemy syntax.
filter(OR=False, main_query=False, **kwargs)
Parameter:main_query – add the filter to the main query

Add filters to the completion. Filters must be expressed in django-like syntax Value can be in the form $field_name (see below):

t = SqlMask(...)
t.completions.field1.filter(cod='$field2')

would request

  1. to retrieve the value of field2 via t.get_value(field2)
  2. to add .filter(field2 = value) to the query that retrieves the possible completions

Filter conditions are relative to the mapper of the completion: for a completion on a ForeignKey it’s the referenced table’s mapper. To state it again and referring to example 40 of the demo: if you edit movie table and complete on diector_id, the following code would select the nation of the director in table director:

t.completions.director_id.filter(nation='IT')

In point 1. above t.get_value() is relative to the SqlWidget in which the completion is requested. This is relevant when related tables are present in the mask. Field value of the main mask can be referred to as $main.field_name

  1. each token starting with $ as in $title is stripped from the $ and the remaining part is used as field_name and t.get_value(field_name) is used instead

  2. if the field_name part starts with main. (as in $main.title) t.get_value(field_name) is not issued in the active SqlWidget but in the SqlWidget pointed to by relationship_leader

    This in general is the main SqlMask holding possibly different m2m tables. This makes it possible for rows in an m2m table to complete only with values related to the referring header.

set_values(values)

Set explicitely the possible completion values.

Parameter:values – a list of values or a callable that will return a list of possible values

Can be a list or a callable that will be called to get the real list of values passing the (possible) text in the entry as parameter

attrs
a list of attributes that will be added to the object retrieved to fill the completion choices. You can add attributes if you need to have more data e.g. in completion hooks
class sqlkit.widgets.common.completion.FkeyCompletion(master, widget, *args, **kw)
A completion that follows foreign key to get values to complete and to substitute for lookup. Inherits from SimpleCompletion
class sqlkit.widgets.common.completion.M2mCompletion(*args, **kw)

A completion that completes on the whole line: each field can be used to choose the same record. It’s not possible in this mode to compose a new record field by field.

The lookup of foreign key values needs a different table than the one used to complete. This is called table_lookup/column_lookup.

Inherits from FkeyCompletion


sqlkit-0.9.5/doc/html/sqlkit/filters.html0000644000175000017500000011023311714210374017750 0ustar sandrosandro Filters — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Filters

../_images/filter-panel.png

Filtering is a very powerful feature of sqlkit. Any single field in a SqlWidget may become a filter criteria and many filters on a field are accepted. A filter panel handles the filters that may also belong to related table: in that case a join will be built when selecting (read the note on selecting through joins in Relationships)

As you can see in the image a filter panel gives also the opportunity to limit the query. Any filter can be enabled or disabled by clicking on it’s toggle.

The result of a filter operation is shown differently in Table or Mask: Table shows the result directly, Mask shows the list of selected records in the Filter Panel’s Output page:

../_images/filter-output.png

Each record is shown with it’s __str__ representation that can be set in the way described in foreign key description & search field.

In this Output Page it’s possible to set a field and have records grouped by that field:

t.filter_panel.tree = 'field_name'

Filtering Foreign Keys

Filtering works also with foreign keys. In that case the filter acts on the filter that represents the record, what I call the “search” field of the record. In this case the operator used for the search defaults to regexp/match that in turn uses different operators in each database backend: ‘~*’ for postgresql, REGEXP for mysql and ILIKE for the others (well, ILIKE is not present in sqlite but it uses the sqlalchemy implementation of ilike) adding leading and trailing ‘%’ symbols.

Adding Filters programmatically

Filters can be added programmatically via method add_filter that uses django_like syntax, of interactively. As an example:

t.add_filter(numeric_field__gte=5, boolean_field=True)
t2.add_filter(date_field__lte='y', string_field__null=False)

more examples can be found in the Constraints sections as they share the same syntax. Note that a filter can be changed by the user while a constraint is invisible to him.

filters and join

When filtering programmatically on a join you must use the field_name as known by the mapper, i.e. composition of table_name + field_name. Look demo on join too see how it works:

t = SqlTable(tables="movie director", dbproxy=db )
t.add_filter(director_nation='IT') # NOTE director_nation

Here the field nation of table director is referenced as director_nation

Expressions

Filter work just in the same way for real column as for expressions. Example 30 in the demo shows how to create a mapper that have a column with the number of film of a director, and you can verify that constraints and filter work on that table just as any normal column:

class Director2(object): pass

## create the mapper as suggested in the SqlAlchemy tutorial...
m = mapper(Director2, model.Director.__table__,
   properties={
    'film_number': column_property(
            select(
                [func.count(model.Movie.__table__.c.id)],
                model.Director.__table__.c.id == model.Movie.__table__.c.director_id
            ).label('film_number')
        )
  }
)

field_list = "last_name, first_name, nation, film_number"
t = SqlTable(m, field_list=field_list, dbproxy=db)
t.add_filter(film_number=3)
t.add_constraint(film_number__lt = 5)

Date filters

Date filters deserve a special chapter. It’s very common the need for a filter based on relative dates (i.e.: the beginning of the month, the year, the last month and so on), that’s the only way to allow saving queries that will behave the same all the time.

Simple relative date algebra

a function that implements simple relative date algebra so that we can use it in bookmarks and queries.

Differently from what other packages do (as the very usefull relativedelta that is used in this module) datetools tries to use the term month as a period of length that depends on the ‘current’ month. End of february + 1month will be end of march, not 28th of march!

Allowed chars are:

[-+ diwWmMyY @>]

letters refer to a moment of time (start/end of month, year, week) or period according to use: the forst token is a moment of time, the further ones are periods.

d:today
i:yesterday (‘ieri’ in italian, ‘y’ was already used by year)
w:beginning of week
W:end of week
m:beginning of month
M:end of month
y:beginning of year
Y:end of year

Math signs + and - work as you would expect they add and subtract period of time. When used this way the following letter refers to a period:

m+14d

is the 15th day of the month (beginning of month + 14 days)

New in version 0.8.6.1.

If the first token is the end of the month, we try to stick to the end as much as possible, till another period is used, so that order is significant, note what follows that is extracted from the doctests, assuming the current day is June 2th:

>>> dt.string2date('M-1m-2d')
datetime.date(2007, 5, 29)

>>> dt.string2date('M-2d-1m')
datetime.date(2007, 5, 28)

You can also use a short form (compatible with previous syntax):

m-1 == m-1m

You can use also > in which case the string is considered 2 dates, each built as already stated:

m-1 > M+2

means a 3 months period, starting from beginnig of last month to end of next month

Periods

@ is a compact way to set a period:

@m == m > M
@m-1  == m-1 > M-1

New in version 0.8.6.1.

@ accepts also a quantity:

@2m-1 = m-1 > M-1 +1m

that means a period of 2 months starting from beginning of last month.

Other examples

m-1:beginnning of last month
M+1:end of next month

FilterPanel

The filter panel is the panel where all filter conditions can be written (remeber that constraints are different in the sense that are filters applied w/o possibility to remove them). It opens as a window separate from the main window so that it’s easy to hide or keep it at hand.

Each sqlwidget has a FilterPanel even if it doesn’t show it

class sqlkit.widgets.common.sqlfilter.FilterPanel(master, visible=True)

A panel that manages filter conditions of a query: number of records, field names and output to point & click in case of a panel of a SqlMask

hide(widget=None, event=None)

Hide the Filterpanel

Parameters:
  • widget – not neeeded: it’ here to allow using it in callback
  • event – not needed: see above
show()
Present the filter panel
get_tools(field_name)

return a list of FilterTool for field_name

Parameter:field_name – name of the field
reload()
issue a reload operation on master. Callback of reload button
set_page(action=None, name=None)

set the tab in the filter. Tab name can be ‘filter’ or ‘output’. Sqlmasks only have ‘filter’.

Parameter:name – name of the tab in the filter widget: filter or output
tree
The name of an attribute that will work as grouping attribute. The output TreeView will show records grouped by the same attribute as parent/child. It should be improved as the parent is a the record and not a row with the only grouping attribute.
replace_column(field_class, field_name='__obj__')

Replace the column of the output treeview with a customized one

Parameters:
  • field_class – a subclass of sqlwidget.fields.Field with a proper clean_value method
  • field_name – the field_name of the column, Default: __obj__ (i.e. the name used for the default column of the output treeview
add_column(field_name)

Add a column among already defined fields in gui_fields

Parameter:field_name – the field_name of the field
clear()
Destroy all filter widgets matered by this FilterPanel
class sqlkit.widgets.common.sqlfilter.FilterTool(field_name, panel, master)

A tool that handles the the filter and provides a mean to modify the query of the master (sqlwidget). With the FilterTool you can programmatically set the filter active/inactive, change the operator and the filter values.

You will normally do all this with the .add_filter method of sqlwidget, but you may occasionally need to fine tune the filter in a second time

set_value(value)
set the value of the filter. It can be a string or an object (eg. a date())
get_value()
return the current value of the filter
set_operator(op, value)
Set the active operator entry in ComboBox/OptionMenu for operator choice :param op: the operator :param value: the value of the operator
get_operator()
return the active operator entry in ComboBox/OptionMenu
destroy(window=None)
Destroy the FilterToold and related widgets and de-register from FilterPanel

When the filter is used from a SqlTable, the output is displayed directly into the SqlTable. When the filter is used from a SqlMask the output is shown in a special tab of the FilterPanel that is really a View on the output that can be customized to a good degree. Default representation is str(obj) unless a you have defined a format in the database attribute description for that table.

Customizing the output tab

The default representation of records is a View with a single column named __obj__ that is a field that creates a str(obj) as explained above.

If you want to change that representation you just need to substitute the column in the treeview:

from sqlkit.import fields
from sqlkit.db.utils import DictLike

class CountMovies(fields.IntegerField):
    '''
    A field that counts the movies
    '''
    def clean_value(self, value):
        ## missing a field_name attribute on obj the objct itselt is passed
        return len(value.movies)

my_mask.filter_panel.replace_column(CountMovies)

Alternatively you can add a column to the output view after creating the field and the column you would add it to the view as follows:

count = CountMovies('n_movies')
col = columns.VarcharColumn(t, 'n_movies', 'Movie Count', field=count)
my_mask.filter_panel.view.add_column(col)

At this point you can sort the output on each column and even get totals in it.


sqlkit-0.9.5/doc/html/sqlkit/browsing.html0000644000175000017500000002700611714210373020136 0ustar sandrosandro Browsing data — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/sqlkit/table.html0000644000175000017500000013677211714210376017411 0ustar sandrosandro SqlTable — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

SqlTable

table opened on movies

SqlTable is a widget that presents data in a table mode. Columns that are foreign keys are presented in blue and a foreign key description & search field of the foreign key is used.

class sqlkit.widgets.SqlTable(*args, **kwargs)

Main widget to represent data of a sqlalchemy selectable (SELECT of a TABLE or JOIN & similar).

A SqlTable can have different views of the same data represented in different TreeViews.

A SqlTable can be viewed alone or as part of a composite widget, that is normally a mask with Relationships: be sure to understand this part as it’s probably one of the more powerfull features of Sqlkit.

SqlTables can have both ListStores or TreeStores. The latter allows to represent rows in a hierarchy.

SqlTable inherits from sqlkit.widgets.common.sqlwidget.SqlWidget

views
A container for the possible Views. It always have a default view named main
totals
class for Totals: see the specific documentation for further info
edited_path
The path of the edited row (a tuple). It’s not the output of get_selected_path() since when you change the selected row the validation is triggered on the previuos selected_row that is pointed to by edited_path.
create_view(name='main', treeview=None, field_list=None)

Create a view, i.e.: a list of columns one for each field in self.field_list. Columns are setup according to the type of the database field, introspected from the mapper. There may be more than one view in a SqlTable. The first one is created mandatorily; possible further ones are created by hand, passing a view name, a treeview and -normally- a field_list. The filter panel output page is a view of the filtered records.

All the views of a SqlTable share the same model.

Parameters:
  • view – the name of the view
  • treeview – the treeview
  • field_list – a list of field_name for which we want the view
set_field_list(field_list, view_name='main')

Set field of visible columns. Accepts both a list or a string (space or comma separated)

Parameters:
  • field_list – the field name list
  • view – the view for which we set the field_list
set_editable(editable=True)

Turn this table into a normal editable table. A tables is not normally editable if it has relationship_mode = m2m

Parameter:editable (boolean) – Only meaningful for table that represent an m2m relationship: turns the table from non editable to editable, read the explanation in Relationships
set_opts(icons=True, scroll='xy', width=None)

Modify default appearance of some features:

Parameters:
  • scroll (boolean) – eliminate scrollbars (values: x|y|xy|None)
  • icons (boolean) – allows to disable icons in a m2m relationship
  • width – set the maximum width of the widget
select_path(path=None, obj=None)

Select path path. If path is null and obj is not null try finding path at which is obj

Parameters:
  • path – the path to be selected or None
  • obj – if path is None, the object to be searched for and selected
hide_fields(field_name_list='', view_name='main')

Hide a column and add the entry in the menu to make it show up again

Parameters:
  • field_name_list – may be a list or a string (in which case it is split with, and spaces)
  • view – the view for which we set the list of hidden fields
get_selected_path()

Return the path of the selected row or None

NOTE: treeselection ‘changed’ signal arrives when the selection change has
already occurred. Use edited_path to get the path that is being currently edited
get_obj_at_path(path, all=False)

Return the object at path. Return None if no selection. If the object at path is a total object (see Totals) it will be discarder unless you use param all=True

Parameters:
  • path – the modelproxy.modelstore path
  • all – boolean: only return object of the main class (no totals)
add_record(obj)

Add record to the model and setup all what is needed

Parameter:obj – an object of type table.mapper.class_
set_fk_layout(field_name, layout_nick)

set a layout to be used if opening a mask to edit the foreign key

Parameters:
  • field_name – the field_nme of the column
  • layout_nick – the nick name of the layout to be used a ‘layout_nick’
record_in_mask(widget=None)

Open a SqlMask to show self.current if any and to follow selection.

Hooks are set on the newly created mask so that validation is retained Remember to set any possible configuration of the table in an on_init hook so that it will be propagated.

If there are pending modification, a save dialog is opened. This function is normally invoked by the first menu entry of the menu popped by right click on a table view this record in a mask.

Parameter:widget – the menu entry that invoked it, can be ignored when calling it by hand
Returns SqlMask:
 the SqlMask widget
fkey_record_in_mask(widget=None, field_name=None)

Open a SqlMask to show the fkey of this field

Parameters:
  • widget – the menu entry that invoked it, can be ignored when calling it by hand
  • field_name – the foreign key attribute name that should be displayed
Returns SqlMask:
 

the SqlMask widget

add_row_filter(func)

Add a filter that selects which rows should be displayed

Parameter:func – the function that will be called with the obj as argument and must return a bool
../_images/menu.png

Column headers

Each column is clickable. Clicking on the column pops up a menu that enables:

  • to add a filter on this field (see Filters). This same action can be done by pressing f on the column w/o opening the column menu. Only persisted fields can be filtered on.

  • to sort on this field (database sorting). Note that sorting on a ForeignKey will trigger a join with the referenced table and an attempt to sort on the foreign key description & search field (In general you don’t want to sort on the id).

    A “local sort”, i.e. a sort done without hitting the database is performed when you sort a related table (e.g.: as explained in Relationships) or when you press s on a column after clicking the table (to set the focus on the table)

  • to add a total on this column (if the column is numeric). This same action can be done by pressing t on the column w/o opening the column menu.

  • to toggle a brake on this column (see docs for Totals). This same action can be done by pressing b on the column w/o opening the column menu.

  • to hide a column. This same action can be done by pressing h on the column w/o opening the column menu.

Cell renderers

Boolean

Booleans are represented with CellRendererToggle, if the field is nullable, the value loops over 3 states.

Text

I don’t have a satisfactory CellRender for long text. Any hint is appreciated.

Export

A very minimal export function allows to export visible data in a .csv (comma separated value) format. Follow File -> Export. That function can be reach also right-clicking on the table, thus allowing to export that particular view.

Shortcut

in the treeeview

Control-x:eliminates a line
Control-k:eliminates a line
Control-s:saves the record
Control-q:quits the table
Control-n:opens a new record
Control-z:zoom the treeview
Shift-Z:zoom the treeview in a related table (nested in a mask)
s:sorts the table locally (toggle between ascending and descending)
f:open a filter for this column
t:create a total if the column is numeric
v:toggle visibility of some rows, needs that you set a visibility function via sqlkit.widgets.SqlTable.add_row_filter()
b:toggles a break for subtotals on this column. In addition to this operation it also sort on the column as it often happens you want a subtotal after having ordered. Adding a break from the column menu does not order.

in the editable cell

Control-Enter:pops a completion (regexp mode)
Shift-Enter:pops a completion (start mode)
Esc:aborts editing

Signals

button-press-event:
 
this event is emitted when clicking in the treeview if

there are no pending validation errors (in which case you are forced to solve them before). It is mainly used to handle the default menu. Look in the example to see how you can manipulate the menu.

button_press_cb(table, event, obj, field_name, menu, treeview)
Parameters:
  • table – the SqlTable that emitted the signal
  • event – the gtk.gdk.Event associated
  • obj – the object currently selected or None
  • field_name – the field_name of the selected column
  • menu – the default menu that il popped up by this button-press-event if event.button == 3 or None
  • treeview

    the TreeView widget that was clicked. You may get the view the treeview belongs to with treeview.get_data('view')

    New in version 8.7.

context-changed:
 

Records have been displayed or selection was changed. This is used to track any change in the records both selected or displayed and was added to be used by RecordInMask below

records_displayed_cb(sqlwidget, current):
Parameters:
  • sqlwidget – the widget that emitted the signal
  • current – the obj that was current in the row or the first record in the liststore.

RecordInMask

Clicking on any row of the treeview gives the possibility to show the record in a Mask View. If a layout was passed to the SqlTable, it will be passed to SqlMask. In this case all hooks, fields possibly configured and completions will be copied in the new Mask. Same for the hooks.

If you need to configure this Mask in a particular fashion you can put all configuration in a hook named on_init. This code will be executed both for Table and Mask generation that allows to use the same Hook for both widgets.

Signals and callbacks are arranged so that it will follow the selection of the table. The newly created mask will inherit the mode of the table, i.e. if the table was read-only the mask will be opened in read-only mode. On the other hand the mask will be have browsing inhibited.

If a layout was registered for the database table it will be used.

You can programmatically open this SqlMask using fkey_record_in_mask:

t = SqlTable(...)
m = t.record_in_mask()

RecordInMask for ForeignKeys

Similarly, if the column in which you click is a ForeignKey, the popped menu will show an entry to edit the referenced record in a mask.

To be sure it will have the customization you want you can register Layout, Hooks and Class.

In the case you have several possible layout for the same fk, and you want to use a layout that is not the default one, you can use set_fk_layout

You can programmatically open the foreign key mask using record_in_mask:

t = SqlTable(model.Movie, ...)
m = t.fkey_record_in_mask('director_id')

Views

Views (added in rel 0.8.8) are a way to view the same data in two different TreeViews. This way you can split a very large table into different chunks (vertically), leaving some columns to a view and others to a different view. The same column may be repeated in different views.

../_images/table-views.png

An example of a table with views: numbers and dates are really fields of the same table in the database.

Adding a column

You can add columns to a view if there’s a corresponding field (sqlkit.fields.Field) in table.gui_field. Examples #31 and #32 show how to do that that essentially boils down to:

  1. Create a Field class that has a method that uses as input the object and produces the output, you’ll need a clean_value() method s well if you want to be able to create a total on that column (see example #32)
  2. Add an instance of that Field to the fields used in the gui: table.gui_fields
  3. Create a Column and add it to the View
class ObjField(fields.VarcharField):
    """
    A field that presents the obj
    """
    def clean_value(self, value):
        return value and "%(year)s %(title)s" % DictLike(value)

my_field = ObjField('new_column', {'editable' : False, 'type' : str, 'length' : 30})
t.gui_fields['new_column'] = my_field
## create a column
col = columns.VarcharColumn(t, 'new_column', 'My New Column', field=my_field)
## add it to the view
t.views['main'].add_column(col, 0)

Note

Adding a column that is not mapped to any db-field leads to a column that cannot be filtered on. On the other hand you can sort (locally, read below) on that column and also get totals.

Sorting columns

Sorting columns can be done in 2 different ways:

  1. using sqlkit.widgets.common.SqlWidget.order_by attribute of sqlwidget, that triggers an ORDER BY clause on the database

  2. using order_by clause of modelproxy, that triggers a function locally. This option is faster since you don’t need to reload data

    This is the only method you can use to order related table, apart from playing with relation’s order_by attribute in the model

    Currently it can be done:

    programmatically:
     

    by modelproxy.order_by with argument a string with field_names possibly prefixed by + or - to define the order:

    t = SqlTable(...)
    t.modelproxy.order_by('status -description')
    

    it can also be used to order a related table:

    lay = """
         director
         o2m=movies
    """
    m = SqlMask(..., layout=layout)
    m.related.movies.modelproxy.order_by('-year title')
    

    when sorting a field that represent a foreign key, the value of the lookup (i.e.: the value shown) is used rather than the value of the key

    interactively:currently limited to just one column at a time pressing s when focus in in the treeview (i.e. you ave already clicked the treeview). Sorting will toggle ascending and descending.

Duplicating a row

The row menu (right click) allows to duplicate a row, it requires some understanding of the related issues:

  1. Only visible fields are copied
  2. Primary keys are not copied
  3. If a foreig key is copied and that corresponds to a relation that has cascade='delete-orphan' that relation requires an instance to be attached. The field is able to retrieve such instance and attach it only if you configure the model’s class properly. You can read more info on the field’s method add_related_object

As for Save as mask function, there’s a hook named on_save_as that can be used to configure proper action to be taken before saving the duplicated row


sqlkit-0.9.5/doc/html/sqlkit/defaults.html0000644000175000017500000003704711714210374020122 0ustar sandrosandro Defaults — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Defaults

Defaults are a handy way to avoid typing too much. Defaults are handled by sqlkit.db.defaults.Default class:

from sqlkit.db  import defaults
def_visita = defaults.Defaults('cliente_visita', metadata=db.metadata, local=False)
def_visita.set_defaults( {'data' : def_visita.now, 'telefonata' : True})
def_visita.set_default( title='Write a title...', duration=90)
def_visita.fk('director_id', 'first_name', 'Olmi')

Defaults class requires a table name and a metaclass to know where to autoload/read table definition.

Defaults can be local to a sqlwidget or global to the application.

set_default

The main method is set_defaults that requires a dictionary as argument with field_name as keys and field defaults as value. Alternatively set_default accepts keyword args.

fk

Default in foreign key are set literally via fk. It will follow the reference and find the correct value

now & today

attributes now and today will be substituted appropriately when creating the field

SA defaults & server defaults

At the moment Sqlalchemy defaults are handled only in case of a fixed value (not a callable) nor defaults defined in the server. Adding a new record with an unhandled default will result in an empty value.

Defaults local to the application

Sqlwidgets have an attribute called defaults that is an instance of sqlwidget.db.defaults.Defaults already correctly instantiated that allows to set defaults local only to the instance of sqlwidget they belong to.

You can use such a default in any of the following ways:

m = SqlMask('movie', ...)
m.defaults.set_default(actor=False)
m.defaults.set_defaults( {'title' : 'write your preferred...'} )
m.defaults.fk('director_id', 'first_name', 'Fellini')

sqlkit-0.9.5/doc/html/sqlkit/printing.html0000644000175000017500000006507711714210375020152 0ustar sandrosandro Printing — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Printing

Sqlkit adds printing capability throught OpenOffice.org template module that in turn uses templates created with Openoffice.org and very simple syntax.

In this module ‘print’ is used in a loose way. In all situations ‘print’ means producing a printable file, it can be an .odt file or a .pdf one.

Note

In a network environment you’ll probably use a remote server that means the file will not be generated locally. Openoffice 3.1 comes with python2.6 interpreter and uno module even under Windows. So that there’s no problem using that interpreter or using a different interpreter but pointing to it’s modules. If you need to have uno modules under windows you can follow instructions in this tutorial or on stackoverflow

Sqlkit printing

Each sqlwidget has a printing attribute that manages printer entries that is an instance of PrintTool.

A standard way should be as easy as just setting a directory where templates should be found and adding menu entries via add_menu_entry. The default template dir is a subdir of the current directory named templates.

Templates present in this directory -if named as the nick of the sqlwidget- will be automatically added, but you will normally want to customize the context or adding variable to it or adding formatting functions (e.g. to transform numbers into monetary format), that can be easily done connecting to context-ready signal.

The default way of filling the context wraps each object into an ObjectProxy` that uses Fields to present each value in a human readable format, that means also that it tries hard to follow foreign keys and substitute values retrieved by foreign key description & search field.

Tables: iteration on records

Oootemplate offers a powerfull way to iterate over lines via the ++ syntax. It requires you to define a table’s name matching an entry in the context. For SqlTables the default name for such table is Table1 and all records in the table end up there.

For SqlMask the default behaviour is to add an entry in the context for each related attribute (eg: movies for the director‘s mask). Each such entry holds all the related records as per sqlelchemy class definition. In the director/movie example the default context would be:

context = {
   'obj' : <Director: my displayed director>,
   'movies' : [(<Movie: movie1>, <Movie: movie2>)]
}

It’s up to you to prepare the openoffice file to be used as template with a table named movies: you do that interactively editing oowriter’s table attributes. If you prefere to have to have a different table’s name or you need to customize the content of the record, you simply connet to ‘context-ready’ signal and prepare the context directly.

ObjProxy

What stated above is only partially true... each object is wrapped in another object that allows you to write direct_id but really returns what you would really want: a human representation of the id exactly as returned by field.lookup_value(id).

API

class sqlkit.misc.printing.PrintTool(master)

A print obj that is able to create a default context to handle to oootemplate.

__init__(master)

Initializes the printing tool setting a default template dir and checks for availability of module ‘uno’ from openoffice. If uno is missing the printing capabilities are made invisible.

If templates are found with the name nick.odt where the nick is the nick of the sqlwidget, entries are automatically added. This way you can generate templates and use them w/o even the need for a single line of code (unless you need to customize the context, of course)

Parameter:master – the table or mask instance of which this is a printing tool
add_menu_entry(description, template_name, server=None, port=None, mode='pdf', output_name=None, accel=None, tip=None, action_name=None)

Add menu entry with ‘description’ using ‘template’ Both are fake printers in the sense that are ‘printer to file’

Parameters:
  • description – the description that will appear in the menu entry
  • template_name – the template name to be used
  • mode – the mode of the printer: pdf or odt
  • output_name – the output name. Can be a callable that will receive template_name as parameter. If self.output_dir is defined output_name will be joined with self.ouput_dir to retrieve it from the client
  • accel – accelerator to be used with this
  • tip – tip for the entry
  • action_name – action name (defaults to templ_ + template_name)
prepare_context(context)

This function is meant to be overridden by a customized one

Parameter:context

the automatically prepared oootemplate.Context. It contains 2 keys:

  • ‘obj’: the current object for mask and normally None for tables
    (unless an object was edited, in which case it will point to that object)
  • ‘Table1’ : the list of record currently loaded in this sqlwidget

You can add any keys but remember to use the correct syntax for tables (a dict with lists as <values).

This is normally used to set totals or arrange so that related table’s record are used in Tables. Read example 76.

template_dir
The directory where templates are searched for. Default is cwd/templates. It’s a path significant for the openoffice server
server
the openoffice server to connect to for printing
port
the port to use to connect to the server
output_dir
the output dir when server is remote as viewed from client (may be an URL)
remote_output_dir
the output dir when server is remote as viewed from server (a local file for the server)
pdf_viewer
the preferred viewer for pdf files
odt_viewer
the preferred viewer for openoffice templates
add_to_table(context, table_name, obj_list, master=None, reset=True, format=None)

Add to an openoffice table a list of objects wrapped in ObjProxy

Parameters:
  • context – the context to be manipulated
  • table_name – the table_name where objects list must be added
  • master – the master where to retrieve fields
  • obj_list – the list of objects to be added
  • reset – boolean. If True the list becomes the only list, otherwise it’s added. Defaults to True
obj_proxy_class
The ObjProxy to use to wrap objects. Can be changed any time. You can customize it so that __getattr__ can return fancy personalization of the attributes
class sqlkit.misc.printing.ObjProxy(obj, master=None)

A proxy that returns a “human value” for each attribute. The default behaviour is to use the fields defined for the object in the SqlWidget i.e.: field’s get_human_value. You can customize ObjProxy changing __getattr__ or simply adding methods whose name is class_name__attribute_name as in:

class MyObjProxy(ObjProxy):
    def Movie__director_id(self, value):
        return value.title()
table.printing.obj_proxy_class = MyObjProxy

in this context value is already the value returned by sqlkit.fields.Field.get_human_value()

Signals

context-ready:

the context has been prepared. You can connect to this signal to add element to the context.

context_ready_cb(printtool, context, template_name, sqlwidget):
Parameters:
  • printer – the printing.PrintTool object that emitted the signal
  • context – the context
  • template_name – the template name that is rendered
  • sqlwidget – the sqlwidget

sqlkit-0.9.5/doc/html/sqlkit/editing.html0000644000175000017500000003002711714210374017725 0ustar sandrosandro Editing data — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/sqlkit/relationship.html0000644000175000017500000006271111714210375021011 0ustar sandrosandro Relationships — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Relationships

Sqlkit makes editing of data that have relation as one2many or many2many very simple. It’s as easy as adding a field in the layout of the SqlMask:

lay = """first_name last_name
         o2m=movies      - - -
       """

One2Many in a SqlMak

This is a very powerful feature of sqlkit that is built on top of sqlalchemy‘s mechanism of properties and session and on the ability of layout to let you define a widget in a symbolic way.

In the following example we will use a SqlMask to represent Directors and a SqlTable to represent films, connected to the first by a OneToMany relationship.

Suppose you already define a couple of tables in sqlalchemy, we will do it using elixir that slightly simplifies the syntax, in the package the demo uses declarative layer instead:

class Director(Base):
    __tablename__ = 'director'
    id             = Column(Integer, primary_key=True)
    last_name   = Column(String(60), nullable=False)
    first_name  = Column(String(60), nullable=False)
    nation      = Column(String(6))

    movies      = relation('Movie', backref='director', cascade='all, delete-orphan',)

class Movie(Base):
    __tablename__  = 'movie'
    id             = Column(Integer, primary_key=True)
    title          = Column(String(60), nullable=False)
    image          = Column(String(250), info={'render' : 'image',
                           'base_dir' : './images', 'thumbnail_size' : (30,30)})
    description    = Column(String(512))
    year           = Column(Integer())
    date_release   = Column(Date())
    director_id    = Column(Integer, ForeignKey('director.id'), nullable=False,
                            info={'attach_instance': 'director'})

To get a mask that:

  1. lets you see director and films together
  2. lets you edit them adding and deleting at will

You just have to start the SqlMask like this:

lay = """first_name
         last_name  nation
         o2m=movies      - - -
       """
SqlMask(model.Director,layout=lay, ...)

And you will get a mask that looks like this:

../_images/o2m.png

delete behavior

We leave to sqlalchemy the responsibility to decide if a film must be deleted from the database or must just updated to set the director_id to NULL. To change this behavior you must act on the cascade property of the relation (or directly on the database definition). If yo want to delete a film in stead of just setting the property to NULL you can define the table in this way:

class Director(elixir.Entity):
    ...
    movies = elixir.OneToMany('Movie', inverse='director',
             cascade="all, delete-orphan" ### note this line
                    )
    ...

Filtering

It is now possible to filter on a field present in related tables, e.g.: filtering all films that have a certain genre.

Note

Starting from 0.8.6-pre2 filters on a related table are aliased (see sqlalchemy docs). This is relevant if you filter on related table via different path (e.g: filtering staff and manager of a table projects, where both staff and managers are a m2m relation to User).

There are cases where this is really what you want but in some cases this is not. Here you really need to deeply understand what is happening underneath at the SQL level. As an example if you have a sqlmask of directors with a related table of films, if you filter on films and use a filter on title (containing ‘la’) and a filter on year (> 2005), the two filters will not be related to the same film: you’ll get all directors that have a film with ‘la’ in the title and (possibly a different) film produce after 2005. This may or may not be what you want...

Many2many relationship

many2many relationship are as easy as one2many. Once again sqlalchemy does the heavy job. Definition is as simple as:

lay = """year title
         m2m=genres - - -
         """
tm = sqlkit.SqlMask(Movie.mapper, metadata=__metadata__,
                    session=elixir.session,layout=lay)
lay = """
     name
     m2m=movies -
"""
tm2 = sqlkit.SqlMask(Genre.mapper, metadata=__metadata__,
                     session=elixir.session,layout=lay)

and will pop up a couple of windows as the following, showing the same data from the two different main tables: movies with their genres and genres with they’re movies.

../_images/m2m.png

adding & completion

Note

New in version 0.8.4.

When using Completion in a m2m table, adding from completion behaves differently that adding from m2o in that it requires the field to be already present and does not allow to edit it

This behavior can be changed setting it’s ‘m2m_editable’ property to True (new in 0.8.4):

t.related.genres.set_editable(True)

Many2One or ForeignKey

Many2One is a simpler case. The table we start from has a field that holds a ForeignKey, we just need to follow it to know the value. This again happens with no effort at all. In this case it’s also possible to use this field in a filter selection.

Options

You can set the field_list directly from the layout as well as the number of rows:

m2m=actors:5:first_name,last_name

will set a 5 rows table, and a field list of first_name, last_name. The real dimension of the table depends also on the expand attributes of the containers. you may need to set them to gtk.EXPAND|gtk.FILL by hand. There’s an example that demonstrates it.

Behind the scenes

The way sqlkit understands that movies is an entry point for a relationship is that it analyzes the mapper, looks for a property with that name and realizes that it’s a PropertyLoader. That means that such an entry point has been put there by a relation.


sqlkit-0.9.5/doc/html/sqlkit/contents.html0000644000175000017500000002774711714210374020156 0ustar sandrosandro SQLKit — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/sqlkit/advanced/0000755000175000017500000000000011714210373017156 5ustar sandrosandrosqlkit-0.9.5/doc/html/sqlkit/advanced/views.html0000644000175000017500000005027711714210373021214 0ustar sandrosandro ModelProxy — sqlkit v0.9.5 documentation

Table of contents

Questions?

Subscribe to out mailing list Google group:

ModelProxy

A proxy to the treeview model that can handle both listore and treemodels and allows to switch easily from one to the other

A second purpouse is to allow reorderable treeviews (i.e.: those wehere you can set order by drag&drop) via self.order_store attribute/function. If position of rows need to be memorized in a visual way, a field must be set (order_store) that will be reset each time a new record is added or a row is dragged.

Clearly if that means something at all, entirely depends on the application. In these cases the order is set in the records currently handled by the model.

Sorting

A modelproxy can be sorted via it’s order_by method. This sorting is done locally as opposed to the sorting done setting SqlWidget.order_by attribute on the table

Signals

a modelproxy emits signals:

model-pre-sort:This signal is emitted just before sorting the model. Used to get rid of possible “total rows” in the liststore.
model-sorted:When the model is sorted. Totals listen to this signal to be informed they must refresh data
model-changed:the model has changed the content
sqlkit.widgets.table.modelproxy.modelstore
the ListStore or TreeStore for which this ModelProxy is a Proxy.
class sqlkit.widgets.table.modelproxy.ModelProxy(master, tree_field_name=None, treeview=None, order_store=None)
__init__(master, tree_field_name=None, treeview=None, order_store=None)
Parameters:
  • master – the sqlwidget this belongs or another modelproxy to copy
  • tree_field_name – the field_name that will hold parent/child relationship. Defaults to None
  • treeview – the treeview to set the model for or None for later setting.
  • order_store – the field_name where the path should be stored
copy(class_)

Ceate another ModelProxy of class class_ with the same other values

Parameter:class – class of the new ModelProxy, a descendent of ModelProxy. Used when you need to add a function to create a Header (see example 02a)
order_by(order_list, view='main')

set order for the model if it’s a ListStore

Parameter:order_list – fields to use to order, e.g.: +status -description to get an ascending ordering on status and descending on description implemented just for one attribute
tree_field_name
The field on which you want to group rows

View

Classes that implement creation of column and cell renderers This should allow to be as generic as possible while adding new renderers

Each Column widget need a ‘master’ argument that is the SqlWidget in editing_started_cb and edited_cb to set state variables on SqlWidget

class sqlkit.widgets.table.columns.View(master, name, treeview=None, field_list=None, cell_renderers=None, ro=False)

A View is a set of columns of the SqlTable’s that offers a (possibly partial) view of the model. Each SqlTable has one mandatory view named main and possibly other views. Attribute sqlkit.widgets.SqlTable.views (a sqlkit.utils.Container) stores them.

It can be used to provide different way to look at the same data (e.g. different cell-renderers) or different columns for long ones

add_column(column=None, position=-1, field=None)

Add a column to the view.

Parameters:
  • column – the column to be added. If empty, a field must be provided
  • position – where should the column be inserted
  • field – an instance of the Field that should be used. In this case a column is guessed and built on the fly by setup_columns()
select_path(path=None, obj=None, sync=True)

Select path path. If path is null and obj is not null try finding path at which is obj

Parameters:
  • path – the path to be selected or None
  • obj – if path is None, the object to be searched for and selected
  • sync (boolean) – syncronize all views but run just one selection_cb
start_editing(path, field_name=None)

Edit a path/column. Column is passed by its field_name

Parameters:
  • path – the path to be edited
  • field_name – the field_name of the column, if None, the first editable field that does not have a default

sqlkit-0.9.5/doc/html/sqlkit/advanced/fields.html0000644000175000017500000015224711714210373021325 0ustar sandrosandro Fields — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Fields

Mask and Tables display a collection of data that may belong to the database or not. Each of the data represented in the GUI that needs to partecipate to any updating/saving process should have it’s own handler that is a Field.

A Field is an entity that knows how to handle a piece of data, be it on the database (a field_name) or just in the GUI. It’s a layer that allows gui widget to be simpler and easier to interchange. As an example, both varchars and integers are normally displayed by a gtk.Entry: the entry.get_text() will retrieve in both cases a string, it’s the Field (IntegerField) that knows how to turn that string into an integer using the Field.clean_value() method. In more complex cases clean_value can return data computed from related records.

cthe field knows:

  • if it belongs to the database (attribute persistent is True) and in case how it is defined ( i.e.: sqlalchemy property/column)
  • if it is nullable/editable
  • which widget must be used to represent it (see below, not requested)
  • how to produce a formatted representation of the field.

It provides functions to

  • set/get value
  • get default value (but will not cope with server_side defaults)
  • tell if it changed from the default
  • clean value according to it’s logic: i.e. return a value of the correct type
  • validate values (possibly according to other criteria)

The association between a database field and a Field is dome by FieldChooser, but you can force a particular Field when defining the model class setting the key field of the column’s info dictionary:

class MyClass(Base):
    ...
    foo = Column(..., info={'field' : MyField})

A last method is to use on_pre_layout hook that allows to set sqlkit.Fields even on non persisted fields.

Relation with the master sqlwidget

Currently there are a number of operation that require that the field know wich is the sqlwidget it is acting for. I’d like to loosen this connection in future but at present it’s used in the following situations:

  • to keep updated the list of field_widgets
  • to get default values (that can be local to a sqlwidget)
  • for validation purposes: to issue master.run_hooks and NonNullableException
  • FKey: to handle addition of related_obj when cascading policy requires it

In the meanwhile I add master to the Field via set_master() and add a widget to the Field via set_widget()

Db attributes

The costructor can be passed a dict of field_attributes

  • field_name: the name of the field

  • nullable: True if field is nullable or False. The related widget will get a background color that

    reflects the fact that it is required (currently yellow). This can be set at any time.

  • editable: True if field is editable or False. The related widget will be set unsensitive. This can

    be set at any time.

  • length: the desired length in chars

  • default: a possible default option

  • type: the python type

  • mapper: defined as None for field that are mapped

  • table: the SA Table object

  • column: the SA Column object

  • property: the SA Property - if any

  • fkey: the SA ForeignKey or None

  • pkey: True is field is a PRIMARY KEY or False

Other attributes

  • widget: the widget used to represent it. May be sqlkit.widgets.mask.miniwidget or similar
  • format: the format used to represent the field (numeric or date/time)
  • locale: the locale to use to represent the field (numeric or date/time)
  • DecimalFields also have precision/scale
  • Varchar/TextFields also have blank=True/False (default: False). It determines if an empty string is a valid value. Empty strings are differerent from NULL values

future

This should provide a way to set the possible observers

validation based on the type should live here

possibly more validation may live here

formatting/locale

see decimal to have an intro on formatting numbers

FieldChooser

This class implements a way to determine which Field should be associated to each field_name in a mapper (used in setup_field_validation so that it can be easily overwritten). It’s important to understand that it already receives a gtkwidget from layoutgenerator; that widget has been set by introspection of the layout description and of the field in the database.

You can overwrite the decision of the field redefining the gui_field on a derived sqlwidget or passing it as argument (see code snippet 34):

class Movie(SqlMask):
    gui_field_mapping = {'date_release' : VarcharField}

t = Movie(table='movie', dbproxy=db, layout=lay)

It’s up to the field defined in this way to be able to handle the type of data. This setting can be used to add field constraints (eg: mail address validation) or to completely change the widget that represent data.

Widgets

A Field does not create a gtk widget to represent data. Layout definition is normally enought to create the correct GTK widget. If a gtk widget exists that represent a field, it is handled by a proxy called Miniwidget that offers a common interface to possbly different gtk widgets. If a MiniWidget exists the Field will instantiate it and set values thought it.

A notable exception to this rule is represented by any m2m/o2m relation, that in the layout is only present as a gtk.Alignment widget to which a children is added by mask.Widget.

Miniwidgets for all main types are provided.

Global variables

Varchar fields will try to cast an empty value to None unless blank_ok is set in the field:

t.gui_field.field_name.blank_ok = True

or globally:

from sqlkit.widgets.common import fields fields.BLANK_OK = True

Default value for BLANK_OK is False.

This is only enforced for NEW records, for already persisted records the default behaviour is to let it as-is, to prevent a very annoying flood of dialog “do you want to save?” when you just need to browse some data.

Available Fields

class sqlkit.fields.Field(field_name, field_attrs=None)
set_widget(gtkwidget=None, def_str=None, widget=None)
Parameters:
  • def_str – the definition string in the layout that determined the gtk.Widget (eg.: c=married, e=first_name)
  • widget – the miniwidget to be used. Defaults to class-defined self.Widget the widget can be a string of a Widget derived from miniwidget.Widget
  • gtkwidget – the gtk widget to be used (already created by Layout)
set_value(value, initial=True, shown=False, obj=None, update_widget=True)

Set the value

Parameters:
  • value – the value to be set. It will be cleaned.
  • initial – if True the self.initial_value is set as. self.initial_value is used to know if a field has changed
  • obj – if passed the attribute is set on the object when the field is set
  • update_widget – if True (default) the widget that renders the field is set as well.
get_value(shown=False)
Return the current value. Look for it in the widget and returns a cleaned value
clear_value()
sets a value that clears the corresponding widget, can be None or []
format_value(value, format=None)

return a string representation of the value according to current locale value is a”cleaned” value

Parameter:value – the value to be formatted (must already be casted to correct type)
clean_value(value, obj=None)

return a value of the correct type, if needed parse it with locale parsers (numbers, date...)

Parameters:
  • value – the value to be cleaned (i.e. casted to correct type). It’s the attribute of a persisten object or the object itself for non persisted fields. I.e.: if you create a custom field to count how many movies has directed each director, the Director instance will be passed as value.
  • obj – the object to which the field belongs. It’s normally disreguarded but it can be used by special fields (as image fields) to create customized property (e.g.: the save path)

This function is used while sorting a column

has_changed(verbose=False)
return True if field has changed after last set
get_default()
return the default value for this object
validate(value, clean=False)
check if the current value is accepted and call on_field__validation on the master’s hook.
get_human_value(value, format=None)
return the value or a translation in human readable for foreign key

All other field inherit from Field

class sqlkit.fields.VarcharField(*args, **kw)

The field to represent Strings

blank_ok
The widget return an epty string on empty values. This variable determines if that value will be set NULL or left empty. Regardless of this value the value is left untouched if it already exists.
class sqlkit.fields.IntegerField(*args, **kw)

The fields to handle interegers

format
How to represent integers. Default: ‘#,###’
class sqlkit.fields.FloatField(*args, **kw)

The fields to handle floats

format
How to represent integers. Default: None
class sqlkit.fields.DecimalField(*args, **kw)

The fields to handle Numeric Fields

format
How to represent integers. Default: ‘#,###.00’ (The number of 0 determined by scale
class sqlkit.fields.TextField(*args, **kw)
class sqlkit.fields.DateField(*args, **kw)

The fields to handle datets

format
The format used to represent dates. Default: short
class sqlkit.fields.TimeField(*args, **kw)
The fields to handle times w/o timezone
class sqlkit.fields.TimeTZField(*args, **kwargs)
The fields to handle times with timezone
class sqlkit.fields.IntervalField(field_name, field_attrs=None)
The fields to handle times with interval
class sqlkit.fields.DateTimeField(*args, **kwargs)
The fields to handle datetimes w/o timezone
class sqlkit.fields.DateTimeTZField(*args, **kwargs)
The fields to handle datetimes with timezone
class sqlkit.fields.BooleanField(field_name, field_attrs=None)
A field to handle booleans that does not allow NULL
class sqlkit.fields.BooleanNullField(field_name, field_attrs=None)
A field to handle booleans that allows NULL
class sqlkit.fields.EnumField(*args, **kw)

A field to handle a set of allowed values. You set values in the info column dict or setting values. Setting info column’s key render to ‘enum’ triggers this field and a widget based on ComboBox. It doesn’t currently use Sqlalchemy Enum type as it’s not yet supported in sqlalchemy 0.5.

keys
A list of allowe values in the orederd set by the programmer. Used to set the order of the Combo Box
values
A dict: keys are the allowed values, values are te corresponding descriptions. It’s up to the programmer to set this list appropriately.
lookup_value(value)
Return the value to be shown
class sqlkit.fields.ImageField(*args, **kw)

Imge field suitable for VarcharField that hold an image path and should be rendered as image (icon in tables). It’s never used when autoloading the database schema (no info on the database tells that a string represent an image path), it can be forced setting info values on the Column:

render:         image
base_dir:       a directory
thumbnail_size: tuple (width, height)
default_size:   tuple (width height)

as in:

image = Column(String(100), info={'render' : 'image', 'base_dir' : '/path/to/images'}

Image field that can create a thumbnail and resize an image into jpeg/png format using gtk.gdk.pixbuf* functions

base_dir
base directory under which all files will be saved. Default is os.getcwd() unless is set at Schema time as shown above. get_save_path() will use this to create a complete path. This can be set when defining the Column as shown above.
thumbnail_size
size of the thumbnail used to render in treeview. Default 50,50. Can be set in Column.info as explained above.
default_size
If set it must be a tuple (width, height) and all files uploaded will be default to these dimentions if bigger.
get_value(shown=False, complete_path=False)

Return the path to the image

Parameters:
  • shown – boolean: not meaningful for this field
  • complete_path – boolean: compose it with base path
set_value(value, initial=True, shown=False, obj=None, update_widget=True, new_name=None)

Set the value and -if needed- copy the image file inside base_dir possibly resizing it to default_size. When resizing the only possible format is jpeg and the name is changed accordingly.

Parameters:
  • value – the value to be set. It will be cleaned.
  • initial – if True the self.initial_value is set as. self.initial_value is used to know if a field has changed
  • obj – if passed the attribute is set on the object when the field is set
  • update_widget – if True (default) the widget that renders the field is set as well.
clean_value(value, obj=None)
strip self.base_dir from the file if present
get_save_path(name=None, obj=None)

Return a standard save path. You may want to customize it.

Parameters:
  • name – the name of the file as picked up from the filesystem or the desired new name for the file. clean_value() call this also for already save path.
  • obj – the object. This may be used to work out the path. It’s not used by default.
scale_pixbuf(pixbuf, width=None, height=None)

scale pixbuf image with the same ratio so that it fits into self.w/self.h

Parameters:
  • pixbuf – the pixbuf to scale
  • width – the desider width
  • height – the desider height
scale_file(image_path, dst_path, width, height)
Scale input file into output file. Keep width/height ratio :param image_path: path of the image to resize :param dst_path: new name of the image :param width: the desired width :param height: the desired height
create_thumbnail(filename, thumbnail_path=None)

Create a thumbnail that will be used when rendering images in tables. Uses attribute thumbnail_size

Parameter:filename – the filename to create a thumbnail for
get_thumbnail(path=None, complete_path=False)

Return a thumbnail path and create the thumbnail image, if it doesn’t already exists

Parameters:
  • path – the path of the image file. If missing it’s found using get_value()
  • complete_path – return a complete path. Default is to return the path stripped from the base_dir
get_thumbnail_path_with_size(filename)

Return the thumbnail name for filename using default size. Place the thumbnail in a subdir of image dir ‘.thumbnail’ used by get_thumbnail() and create_thumbnail(). The name contains the thumbail_size used to generate it.

Parameter:filename – the complete filename of the image
class sqlkit.fields.ForeignKeyField(*args, **kwargs)

A field to handle foreign keys

lookup_value(field_value)
retrieve the value in a lookup table in case of foreign_key. It means: “given the foreign key return a value describing at the best the referenced record”. This implies some guessing of the best representation of the record or using information given to site-wide database configuration via _sqlkit_table. The details of such mechanism are described in Completion and Table Description. Since field_value may be incorrectly casted (it is used in completion) errors are catched and None is returned (rather than raising an Error)

Add an object to fullfill the constraint on delete-orphan

This is not meant to be used directly, it is used by set_value() If you have a relation with a delete-orphan constraint that would complain that is not attached to anybody configure the Column adding in the info keyword the attach_instance key pointing to the property of the relation to be added.

In the demo you can find this example:

class Movie(Base):
    __tablename__  = 'movie'
    ...
    director_id    = Column(Integer, ForeignKey('director.id'), nullable=False,
                            info={'attach_instance': 'director'})

class Director(Base):
    __tablename__ = 'director'
    ...
    movies      = relation('Movie', backref='director', cascade='all, delete-orphan',)

Attaching a director_id via completion, requires that you attach a director instance as well.

class sqlkit.fields.CollectionField(*args, **kwargs)
A field that manages a collection of objects Used in OneToMany or ManyToMany db fields. it’s default widget is a collectionWidget that uses a SqlTable
sqlkit.fields.std_cleanup(fn)

A decorator that will handle standard cases: value is None, is a string or is already cleaned.

This is handy when building new Fields as it allows to keep the .clean_value method as simple as possible, i.e. no need to check for standard cases:

class CountMovies(fields.IntegerField):
    '''
    A field that counts the movies
    '''
    @fields.std_cleanup
    def clean_value(self, value):
        ## missing a field_name attribute on obj the object itselt is passed
        return len(value.movies)

sqlkit-0.9.5/doc/html/sqlkit/advanced/contents.html0000644000175000017500000002776311714210372021717 0ustar sandrosandro Advanced configuration — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/sqlkit/advanced/dbutils.html0000644000175000017500000006624211714210372021523 0ustar sandrosandro Utilities — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Utilities

Hooks and Layout are the core for any customization. If you customize a Mask, chances are you would like to use that customization, any time you open that table, or you may need several different customization (eg. employees, manager may share some fields and differ on other fields).

The following utilities give you the possibility to register Hooks and Layout to make them available to any sqlwidget to which no Hook/Layout is passed.

Each time you register a hook/layout/class you can specify a nickname (eg.: customer/provider)

Hooks

sqlkit.db.utils.register_hook(table, hook, nick='default')

Register a hook for table. Each time a sqlwidget is instantiated for the table a hook is searched for here to initialize the sqlwidget

Parameters:
  • table – a table for which we want to register the hook
  • hook – the hook to be registered (a class or an instance)
sqlkit.db.utils.unregister_hook(table, nick='default')

unregister a hook

Parameters:
  • table – the table or table_name for which you unregister the hook
  • nick – the possible nick (default: ‘default’)
sqlkit.db.utils.get_hook(table, instance=False, nick='default')

Return a hook tor table or None

Parameters:
  • table – the table for which we want the hook
  • instance – a boolean: True is we want an instance False if we want the class

Layout

sqlkit.db.utils.register_layout(table, layout, nick='default', persistent=False)

Register a layout for table with a nick (default nick is ‘default’) Each time a sqlwidget is instantiated for the table a layout is searched for here

Parameters:
  • table – the table to register the layout for. May be a sqlakchemy.Table or a string
  • layout – the layut to be registered
  • nick – the name of a nick to use for this layout (used for SqlMask(... nick=nick)
  • persistent – register in the _sqlkit tables (not yet implemented)
sqlkit.db.utils.unregister_layout(table, nick='default')

unregister a layout

Parameters:
  • table – the table or table_name for which you unregister the layout
  • nick – the possible nick (default: ‘default’)
sqlkit.db.utils.get_layout(table, nick='default')

Return a layout for table for nick ‘nick’

Parameters:
  • table – the table to register the layout for. May be a sqlakchemy.Table or a string
  • nick – the nick used for this layout

Classes

Classes can be registered as well. If you register a class, all the times you pass a table to a sqlwidget, the class will be used, so that ll relations will be available as well. This is particularly usefull in case you use RecordInMask that can open a table that may use a layout with m2m/m2o nested table that would result as unknown if the table was reflected from the db.

sqlkit.db.utils.register_class(class_, table=None, nick='default')

Register a layout for table with a nick (default nick is ‘default’) Each time a sqlwidget is instantiated for the table a layout is searched for here

Parameters:
  • table – the table to register the layout for. May be a sqlakchemy.Table or a string. class_.__table__ is used as default
  • layout – the layout to be registered
  • nick – the nick
sqlkit.db.utils.get_class(table, nick='default')

Return a layout for table for nick ‘nick’

Parameters:
  • table – the table to register the layout for. May be a sqlakchemy.Table or a string
  • nick – the nick used for this layout

Database

sqlkit.db.utils.get_differences(obj)

show differences between old and new version of an object this is a generator, you should use as in:

for field_name, old_value, new_value in get_differences(obj):
    print 'field   %s changed from %s, to %s' % (field_name, old_value, new_value)
Parameter:obj – the object to look for changes

this function uses sqlalchemy.orm.attributes.get_history but differs in 2 ways:

  • it only yield changed values
  • it returns the simple value (not a list) if the property is not a RelationProperty with direction MANYTOMANY or ONETOMANY (i.e.a collection)

Descr

This module provide a bare simple Class to help creating __str__ and __repr__ for tables, using _sqlkit_table.format field

tables

tables dictionary is a place where sqlkit looks for search_field and format for tables (see: ref:sqlkit_model).

You can set values directly using TableDescr class or implicitely via database editing (sqledit -c url)

Table Description

class sqlkit.db.utils.TableDescr(table, format=None, pk=None, metadata=None, register=True)

Handler for table search/format fields. This is an important component of Completion as it determines:

  1. what is searched for (search attribute)
  2. how it will be represented (format attribute)

TableDescr is automatically built from within get_description(). In __init__ it queries the database’ sqlkit_table to see if any site-wide configuration is available.

In turn get_description() is called within completion classes and sqlkit.fields.ForeignKeyField.lookup_value()

format

The format string used to represent the record.

When no format string is passed to the constructor, the first string field of the table is used. That clearly may be totally wrong in some cases, that’s why you can change it.

May be eather a normal python format string (eg.: “%(title)s - %(year)s”) in which case each prenthesized token must be a field_name or a simple string that again must be a field_name (e.g.: last_name) The representation of a director as in our demo could be: %(first_name)s %(last_name)s

Format string passed to TableDescr take precedence over format string present in the database.

attrs
List of attributes required by the format string. Always includes pkey.
search

this is the field that will be searched for in a foreign key. When using completion in a ForeignKey the database is queried using this field as a filter. E.g.: if in a SqlMask for a Movie you enter fe and then complete (Control-Enter) a query is build on the fly and sent to the database that selects the director’s table with an additional "SELECT DISTINCT %(attrs) FROM director WHERE %s = 'fe'" % (attrs, search_field).

It has the same default as format above.

__init__(table, format=None, pk=None, metadata=None, register=True)

Handler for table search/format fields

Parameters:
  • table – the table_name or the sqlalchemy Table
  • format – a possible format string, used to represent the record. See format above.
  • pk – suggests which pk are to be used
  • metadata – necessary to pick primary key or when autoloading is required unless a Table object is given
  • register – (boolean) if True (default) this Table Description will be registered as default

sqlkit-0.9.5/doc/html/sqlkit/advanced/field_widgets.html0000644000175000017500000013102111714210372022652 0ustar sandrosandro Field Widgets — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Field Widgets

Each field is represented using a proper gtk widget. Table and Mask need two different objects, a cell renderer for tables and a widget from module sqlkit.widgets.mask.miniwidgets for masks.

Cell renderers and function to represent the data are defined in sqlkit.widgets.table.columns.

Widgets defined in sqlkit.widgets.mask.miniwidgets are just interfaces (or proxies) to other gtk widgets.

Fields Representation

Field or Property Mask Table
Varchar VarcharWidget  
Text TextWidget  
Integer IntegerWidget  
Float FloatWidget  
Numeric DecimalWidget  
Date DateWidget  
Datetime DateTimeWidget  
Datetime w/ TZ DateTimeTZWidget  
Interval IntervalWidget  
Time TimeWidget  
Time w/ timezone TimeTZWidget  
Boolean BooleanWidget  
Boolean with null BooleanNullWidget  
Enum (varchar or integer with render=’enum’) EnumWidget  
Image (varchar with render=’image’) ImageWidget  
ForeignKey ForeignKeyWidget  
relation (o2m/m2m) CollectionWidget. This is just a SqlTable that represents the related field  
class sqlkit.widgets.mask.miniwidgets.Widget(gtkwidget, field)

A proxy between the field and a real gtk widget

__init__(gtkwidget, field)

setup all what is needed in the widget: completion if any and much more for complex cases as CollectionWidget

Parameters:
  • gtkwidget – the gtk widget used to display the data
  • field – the field from sqlkit.fields
set_value(value, shown=False, initial=False)

Set the value in the widget

Parameters:
  • value – the value to be set
  • shown – (boolean) only memaningfull for foreign key. if True, the displayed value is returned also in any case. The difference is only for ForeignKey The displayed value may be used for completion purpouses
  • initial – not used by miniwidget. Needed for compatibility with tablewidgets where it needs to propagate to master’s set_value
get_value(shown=False)

Return the displayed value

Parameter:shown – (boolean) return the shown value or the real value (only meaningful for foreign keys)
set_editable(editable)
set the widget editability if the fields is not editable, the widget cannot be editable
add_completion()

add completion bindings and liststore

This adds completion capabilities to varchar fields and to foreign keys. You can add it to other widgets as well provided you also use completion’s set_values method to provide values to be used in completion.

class sqlkit.widgets.mask.miniwidgets.VarcharWidget(*args)
class sqlkit.widgets.mask.miniwidgets.TextWidget(*args)
class sqlkit.widgets.mask.miniwidgets.IntegerWidget(*args)
class sqlkit.widgets.mask.miniwidgets.FloatWidget(*args)
class sqlkit.widgets.mask.miniwidgets.DecimalWidget(*args)
class sqlkit.widgets.mask.miniwidgets.DateWidget(*args)
A widget that uses DateEdit widget to represent a date. A button that pops a calendar is provided to pick a date
class sqlkit.widgets.mask.miniwidgets.DateTimeWidget(*args)
A timezone unaware widget to set datetime. Very poor widget indeed. Uses DateTimeEdit.
class sqlkit.widgets.mask.miniwidgets.DateTimeTZWidget(*args)
A timezone aware widget to set datetime. Should have a TimeZone chooser but does not yet
class sqlkit.widgets.mask.miniwidgets.IntervalWidget(gtkwidget, field)
class sqlkit.widgets.mask.miniwidgets.TimeWidget(gtkwidget, field)
A timezone unaware widget to set time. Very poor widget indeed.
class sqlkit.widgets.mask.miniwidgets.TimeTZWidget(gtkwidget, field)
A timezone aware widget to set time. Should have a TimeZone chooser but does not yet
class sqlkit.widgets.mask.miniwidgets.BooleanWidget(*args)

Null value is not admittable

A boolean widget can be represented via CheckButton (default) or radio button (just use r=field_name when setting layout). In the latter case you can safely group widgets toghether with normal gtk tools .set.group() to gain a switch between different alternatives:

m.SqlMask(..., layout='r=male, r=female')
m.widgets['r=female'].set_group(m.widgets['r=male'])
class sqlkit.widgets.mask.miniwidgets.BooleanNullWidget(*args)
This gtkwidget is placed in inconsinstent state to represent NULL
class sqlkit.widgets.mask.miniwidgets.EnumWidget(gtkwidget, field)
A proxy for a ComboBox
class sqlkit.widgets.mask.miniwidgets.ImageWidget(gtkwidget, field)
A proxy for Image Viewer to represent fields that hold file name as images
class sqlkit.widgets.mask.miniwidgets.ForeignKeyWidget(*args)

A ComboBoxEntry that displays a represention of the referenced record rather that the referece. I.e.: it shows last_name/first_name of a director rather than the id of the record in the directors’ table.

It also enforces the foreign key constraint. Completion occurs on the foreign (referenced) table according to what is detailed in Completion documentation.

class sqlkit.widgets.mask.miniwidgets.CollectionWidget(*args)

A SqlTable widget that represents a collection of records. This is used to represent a property that in turn holds a relation. E.g.: director.movies movies being a list. The contructor setups a SqlTable that has relationship_leader pointing to the main SqlMask.

It maintains the same session so that a single commit will save parent and children.

Signals

value-set:

a signal emitted each time a value is set (via method Widget.set_value). This signal does not trigger a general change in value as would be for entries a changed signal. Callback function:

value_set_cb(widget, value, initial):
Parameters:
  • widget – the Miniwidget that issued the signal
  • value – the value that is being set
  • initial – (boolean) True if the value is set with initial=True

Colors

You can change default color for not nullable object globally:

from sqlkit.widgets.mask import miniwidgets
miniwidgets.NOT_NULL_COLOR = gtk.gdk.color_parse('green')

You can change the foreground of unsensitive widgets setting NOT_EDITABLE_COLOR

Image Viewer

class sqlkit.layout.image_widget.ImageWidget

Image Widget suitable for basic image viewing. Inherits from VBox

sb(txt)
Push info on Status Bar
scale_pixbuf(pixbuf, w=None, h=None)

scale pixbuf image with the same ratio so that it fits into self.w/self.h

Parameters:
  • pixbuf – a gtk.gdk.Pixbuf object
  • w – the desired width
  • h – the desired height
set_image(image_path)

set rendered image

Parameter:image_path – the path of the image
set_pixbuf(pixbuf)

Set the image via the pixbuf

Parameter:pixbuf – the gtk.gdk.Pixbuf
set_stock(stock_id, size=<enum GTK_ICON_SIZE_DIALOG of type GtkIconSize>)

Set the image via the stock-id

Parameters:
  • stock-id – a stock-id
  • size – the desired image (default: ICON_SIZE_DIALOG)
show_image(action=None)
Open another window that will show the same image with at a different zooming. Setup callback to keep the new window updated by the first one.

Properties

imageViewer has the following properties:

image:the image path currently rendered
scale:possible values AUTOREDUCTION, SCALE. If True the image should autoscale. If value is AUTOREDUCTION the image is never enlarged over its natural size
width:the with of the gtk.Image widget
height:the height og the gtk.Image widget
scale_factor:the current scale factor between render image and thumbnail

Signals

ImageViewer has the following signals:

image-selected:

The image has been selected for upload. Callback:

on_image_selected(widget, filename, new_filename):
Parameters:
  • widget – the widget that issued the signal
  • fielname – the filename that has been selected
  • new_filename – a preferred filename as suggested by the user if any
image-displayed:
 

The image has been displayed

on_image_displayed(widget, pixbuf_full, pixbuf):
Parameters:
  • widget – the widget that issued the signal
  • pixbuf_full – the pixbuf rendered from the file
  • pixbuf – the scaled pixbuf
image-deleted:

the image has been deleted

on_image_deleted(widget, filename):
Parameters:
  • widget – the widget that issued the signal
  • fielname – the filename that has been deleted

DateEdit

class sqlkit.layout.dateedit.DateEdit

basic gtk widget to edit data

set_date(new_date, from_focus_out=False)
new_date is a datetime.date object. Can be set by:
  • do_set_property
  • focus_out_event
  • popup click
class sqlkit.layout.dateedit.DateTimeEdit

sqlkit-0.9.5/doc/html/sqlkit/constraints.html0000644000175000017500000010034311714210374020650 0ustar sandrosandro Constraints — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Constraints

Constraints are a mean to limit browsing of data to a limited set. It’s a different concept from filtering, as this enforces a limitation while filtering allows the user to change the values at will.

Django-like syntax

A constraint can be as simple as:

t = SqlTable('movie', dbproxy=db)
t.add_constraints(actors__country='ITA', genres__name='drama',
                  year__gte=1950, year__lte=1970)

And browsing of movies will be constrained into dramas with at least one Italian actor, produced between 1950 and 1970. The double underscore ‘__’ will allow spanning from one table to a related one (provided a relation was established at sqlalchemy level) and from attribute name to operator to be used.

Note

This syntax is inspired to django‘s ORM syntax. The same syntax can be used to add filters to the sqlwidget, to the completion and when .reload() method is used.

query

limitation acts on self.query directly. Self query can be modified in any way accepted by sqlalchemy syntax

operators

these operators are recognized:

OPERATORS = {
    'notlike'    : 'not like',
    'like'       : 'like',
    'ilike'      : 'ilike',
    'notilike'   : 'not ilike',
    'iregexp'    : '~*',
    'regexp'     : '~',
    'notiregexp' : '!~*',
    'notregexp'  : '!~',
    'lt'         : '<',
    'gt'         :  '>',
    'lte'        : '<=',
    'gte'        : '>=',
    'eq'         : '=',
    'neq'        : '!=',
    'equal'      : '=',
    'in'         : 'IN',
    'notin'      : 'NOT IN',
    'null'       : 'IS NULL',
    'notnull'    : 'IS NOT NULL',
    }

and icontains that at the moment map to regexp operators if they exist or like with % on both sides.

conjunctions

In the example all conditions have been AND ed. It’s possible to use OR operator adding or_=True argument.

django2sqlalchemy

You can get a sqlalchemy ClauseList object starting from a django-like expression using django2sqlalchemy function:

from sqlkit.db.django_syntax import django2sqlalchemy
clause_list, join_paths = django2sqlalchemy(mapper, *args)

where args can be or_=boolean or any django-like query. Django2sqlalchemy returns a tuple: (clause_list, join_path):

clause_list:

the list of column expression connected with AND or OR according to or_ argument

join_path:

the list of path needed to add to query via query.join() to have all the fields the query needs to apply the filters in clause_list

A possibility is to add:

for path in join_path:
   query.join(path).reset_joinpoint()

Filter simplified syntax

Django syntax utilities

This module provides some functions to allow use of django syntax in filters. You can look an introduction on lookup filters on django’s site but mind that we don’t even share the same syntax. We just borrowed the idea that the operator and possibly the join can be wired in the keyword name. The aim is not at all compatibility with django (that has nothing to do with this framework) but a way to:

  • make it easy to add filters and constraints
  • make it easy to store queries (that need to store filters and constraints)

These function are used in sqlfilter and in completion beside .add_constraint(...) method of sqlwidget.

You never need to use these functions. If you prefere you can changed the query directly with SQLAlchemy syntax, and surely there are situations where that’s necesssary, nevertheless there are situations where this syntax allows to obtain the same result in less and more readeable code, probably just one line.

All of these functions act on a mapper becouse it has info on PropertyLoaders. In completion, the mapper is used to follow the join_path even if the query (a sqlalchemy.select), will not be issued on a session.query object

supported lookup
like/ilike:use like operator, ilike if available
notlike/notilike:
 negate like operator, ilike if available
regexp/iregexp:now ~/~* for postgres and REGEX for mysql like again for the rest
notregexp/notiregexp:
 now !~/!~* (only for postgresql)
lt/gt:less than / grater than (<, >)
lte/gte:less than equal/ grater than equal(<=, >=)
eq/neq:equal / not equal ( = , !=)
in/notin:IN (argument must be a list)/ negated
null:IS NULL / IS NOT NULL (depending if arg is True/False)
conjunctions

django2sqlalchemy() and django2query() connect all arguments with AND operator by default. It’s possible to use OR operator adding OR=True argument.

sqlkit.db.django_syntax.django2sqlalchemy(mapper=None, table=None, OR=False, **kw)

Return a tuple (ClauseList, join path).

Parameters:
  • mapper – the mapper that will be used in the query
  • table – the mapper that will be used in the query
  • OR – if True the conditions will be ORed
  • kw – the conditions as per django-like syntax
sqlkit.db.django_syntax.django2query(query, mapper_or_class, OR=False, aliased=False, OUTER=False, **kw)

return a new query with new constraints expressed in ‘django-style’

Parameters:
  • query – the query to which filters will be applied
  • mapper_or_class – a mapper or a class from which the class will be devised
  • aliased – passed to DjangoParser
  • OUTERDjangoParser will be instantiated as an OUTER join

Native sqlalchemy constraint

The django syntax is in no way the only possibility. If that is not sufficient to express the constraints you need you can just use any filter on the query directly:

tk_tbl = ticket.Ticket.__table__
my_id = self.db.get_session().query(ticket.User).filter_by(username=setup.USERNAME).one().id

t = sk.SqlTable(ticket.Ticket, field_list=field_list, order_by='priority', **self.meta)
t.add_filter(status=1)
t.query = t.query.filter(or_(tk_tbl.c['assigned_by_id'] == my_id ,
                             tk_tbl.c['assigned_to_id'] == my_id , ))

this example shows how to force a constraint on ticket requiring that the ticket be assigned by or to USERNAME. Any further filter applied interactively will be applied on top of this constraint. This is equivalent to writing:

t.add_constraint(assigned_by_id=my_id, assigned_to_id=my_id, OR=True)

At the moment of this writing it’s not possible to write:

t.add_constraint(assigned_by_id__username=USERNAME)

as the foreign key relation is not followed in this context.

aliased constraints

Sometimes you need to have aliased constraints in order to ha sqlalchemy build an aliased join to related classes on which you may want to set constraints. This may be necessary if you want to use constraints along with order_by and possibly filters on foreign keys.

example

Suppose you have an ‘address’ table with a user_id field that is a ForeignKey to a user.id column. Suppose you want to open a SqlTable on table addresses, ordered on user_id (well... surely you don’t want to order by the id value, you probably have a first_name field that is much more appropriate for sorting).

Now suppose you also have an active field on the user_table. You can achieve this (simple) in the following way:

t = SqlTable('address', order_by='user_id__first_name', ...)
t.add_constraint(user_id__active=True, aliased=True)

the order_by argument implicitly have built a join with the user table, the same stands for the constraint user_id__active. Sqlalchemy would have complained that the table ‘user’ was already present, so you need to alias it. I’ll probably make this the default in a future version.


sqlkit-0.9.5/doc/html/sqlkit/mask.html0000644000175000017500000005770611714210374017252 0ustar sandrosandro SqlMask — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

SqlMask

../_images/layout-simple.png

layout

The most powerful part of SqlMask is the ability to define the layout via simple text description. In the examples you’ll see how easy it is to create fancy layout that have all nice gtk Widgets like expanders, notebooks, panes and it makes it incredibly easy to nest SqlTables into SqlMask to build complex layouts that can also represent Relationships.

If you know ReST language and glade, it relates to glade as ReSt to html.

If no layout is provided a flat one will be generated on the fly.

The key is that a textual layout is parsed to see if the token are recognized as fields of the table that the mask is editing, in which case introspection is done to understand which widget should be used to edit the data (according to rules above) and a label is added:

mylayout = """
   image
   title
   date_release
   TXS=description
   director_id
"""
m = SqlMask(model.Movie, layout=mylayout, ...)

will creare a mask with an image, an entry, a date a text and a fkey. title and director_id will be already instrumented with completion: director_id will try to complete choosing the values of directors from the director table, title will suggest completion based on title present in the table (and in this case may not be very useful). description would render as an entry as it’s a varchar, we wanted to “cast” it to text using TXS=.

The description language you can use is pretty rich (and dynamic, so you can add your custom made widgets): you’d better have a look at the demo of sqlkit.layout that you find in sqlkit.demo.layout

When parsing the textual layout, any token that is not recognized as a field is passed as is to sqlkit.layout.Layout

gtk refinements

Occasionally you may need to refine your layout, change packing, visibility, attribute an so on. You can reach the gtk widgets via the widgets attribute of the SqlWidget. In example 26 we use:

Tbl = t.widgets['T.a']
Tbl.get_parent().child_set_property(Tbl, 'y-options', gtk.EXPAND|gtk.FILL)

that changes pack properties to a gtk.Table whose name is T.a. If you have ipython you can start the demo with option -i to gain an interactive shell to play with an experiment with the widgets. By default each field has a label whose key in widgets dict is l=field_name while each entry is e=field_name.

Shortcuts

mouse scrolling:
 allows to browse the records that have been loaded by a reload operation
Control-s:saves the record
Control-q:quits the table
Control-n:opens a new record

Signals

pre-display:

this signal is emitted when current object has already been set but field values have no been set. It can be used to configure custom widgets whose appearance may depend on other values.

pre_display_cb(mask, obj)
Parameters:
  • mask – the SqlMask that emitted the signal
  • obj – the object currently selected or None

New in version 0.8.8.

../_images/mask-demo.png

Save as

Masks have a function save as new record, that allows to duplicate a record (corresponding to table’s row duplicating). It’s important you understand exactly what it does so that:

  • it nullifyes the primary key, so that a new one will be generated. If the primary key is visible and editable in the mask, it’s up to you to delete it or change it according to your needs

  • m2m relations: for each visible relation, i.e. each relation that is shown by the mask and is of type many to many it copies the records. As an example: actors would be copied in the second film.

    If you want to change the new record in the related table (e.g.: actors) do that only after saving otherwise you will also change the cast of the original film, that’s different from other fields as table’mode is to save in the same moment you edit.

  • o2m relations: if o2m relations are present a warning is displayed that those fields will not be copied unless you have an on_save_as hook that would mean you are already handling this case.

    Blindly copying the records would steal the references from the other record. Just to be explicit: suppose to have a mask of a director with his films, the director is Fellini and the film La strada. Duplicating the record “Fellini” with “Fellini 2” should probably be a 1^st step in personalizing some fields. In no way you want to divert the director_id of the film “La strada” that should continue to point to the original record “Fellini”.

  • visible fields are copied from one record to the other. Please note carefully this point: a new empty record is set as current record and each visible fields is furtherly saved in the new record. If a field is not visible that field will not get copied. You may have problems if required fields are not visible. To allow to fill with new values a hook is invoked named on_save_as.

class sqlkit.widgets.SqlMask

SqlMask is the widget that displays one record at a time and buttons to browse data, save/delete records. SqlMask is inherited from sqlkit.widgets.common.sqlwidget.SqlWidget

get_widget(field_name)
get the widget that renders field_name
clear_mask(check=True)

clear the mask for a new record NOTE: this does NOT prepare for a new record (missing obj)

Parameters:
  • widget
  • check – boolean: false prevent a check
set_frame_label(frame_name, markup_label, opts='bi')

Add a label to the frame whose key in self.widgets is ‘frame_key’.

Parameters:
  • frame_name – the key in self.widgets
  • markup_label – the label with possible markup
  • opts – a combination of b (bold) and i (italic). Default: ‘bi’

In example 15:

LAYOUT = '''
 {|.number .. }
 ...
'''

m.set_frame_label(‘number’, ‘Number’)

Introspection of the table is used to determine which widgets will be used to edit the data. The following rules are applied:

varchar:gtk.Entry
numbers:gtk.Entry with right alignment
bool:gtk.CheckBox
text:gtk.Text
FKey:fk_edit (a custom ComboBoxEntry)
date/datetime:dateedit (entry + arrow for calendar + time)
default:gtk.Entry

All fields will have a label that is sensitive to mouse clicks. A mouse click pops up a filter widget.


sqlkit-0.9.5/doc/html/sqlkit/localization.html0000644000175000017500000004531411714210374020777 0ustar sandrosandro Localization — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Localization

localization support comprises:

  • localization of number (decimal separators and thousand grouping)
  • localization of date/datetime fields
  • library messages translation (we need help bu translators, there is a project in launchpad)
  • translation of field_name (see below)

Numbers and dates localization is provided by babel

Numbers

Numbers are represented according to your locale settings as follows:

integer:grouping thousands (format: ‘#.###’)
floats:grouping thousands, no limit on decimal (format: ‘#,###.#’)
decimals:grouping thousands, number of digits according to type.scale (format: ‘#,###.00’, for Numeric(8,2))

forcing format representation

A different representation can be forced using Number Format Pattern Syntax and setting the new values in the gui_field class via the set_format method that takes a dictionary as argument or the format argument to the widget:

fmt = {
   'year'     : '#', # don't show thousand grouping
   'quantity' : ('#,###.#', 'en'),
}
t = SqlTable(.... format=fmt)  # fmt as argument
t.set_format(fmt)              # fmt as method

where the two possible forms are shown. If the value is a tuple, the second element is a locale to be used for the representation (as quantity in the example)

Dates

dates are formatted according to your locale and the default format is ‘short’ for date/datetime and ‘HH:mm’ for time.

forcing format representation

A different representation can be forced using Date Format Pattern Syntax and setting the new values in the gui_field class via the set_format as illustrated above for numbers.

Note

for the time being this only works for SqlTable, SqlMask uses a different widget that needs to be improved, so it’s possible to set a different date format but not using babel.

Temporarily you should set date_format attribute on the gui_field.widget.gtkwidget...

Messages

Translation of messages is provided by the standard gettext module. If you’d like to contribute a translation for your language read the README.localization in the distribution folder.

Translation of field_names of each column

Since many of the labels that appear in a mask and column headers of a table default to the name of the column, it’s important to add the possibility to translate those strings as well, in order to give to the resulting mask a friendly look.

The same column name may be translated differently in different tables so sqlkit adds a layer in the way of translation via a “label_map” that can be passed directly to the layout object or to the sqlwidget (label_map option).

This label_map is a dictionary whose key is the field_name and the value is a tuple with a description and a help_text to show as tooltip on the label. It instead of a tuple a string is found, it’s considered a label and the tooltip is set to None, Each of these are to be considered msgid that gettext will further try to translate:

{ 'first_name' : ('First Name', 'Write the family name of the director')}

Description and help_text can be stored in the table _sqlkit_field that can be easily edit with sqledit -c URL. Each time a sqlwidget is instantiated it will try to see if any info for the tables it is editing was written in the database and add those to what can be passed to the widget as “label_map”.

Tooltips

What we described is also the simpler way to add tooltips to labels and buttons in your layout. This helps delegating to the customer the personalization of the hints he wants to add to the GUI.

In the table _sqlkit_field each row should represent a field of a table, but there’s no harm adding to it more fields that can be the name of buttons or relationships (e.g.: author) just for the sake of translation and tooltip.


sqlkit-0.9.5/doc/html/sqlkit/sqlwidget.html0000644000175000017500000026025711714210375020320 0ustar sandrosandro Sqlwidget — sqlkit v0.9.5 documentation

Sqlwidget

This is the base class for SqlMask and SqlTable that implements the common interface.

mandatory argument

The first and mandatory argument is the mapper of the object that will be retrieved and displayed. Alternatively any other objects from which the sqlwidget infers them (e.g.: the tablename and the metadata where the table can be autoloaded):

  • a mapper
  • a mapped class
  • table name (requires you also pass metadata)

In older releases different options: class_, table, or mapper where used. Now a DeprecationWarning is raised if you use those opts.

main options

Any sqlwidget needs a session as well.

Metadata is also used when auto-loading tables referenced by foreign keys to display a better representation of the referenced record.

dbproxy

Since a typical scenario is to have to provide a session different in each SqlWidget and a metadata, an object is provided -dbproxy- that can be initialized from the engine specification:

from sqlkit.widgets import SqlMask, SqlTable
from sqlkit import dbproxy
db = proxy.DbProxy(engine="sqlite:///model/movies.sqlite")
SqlTable("movies", dbproxy=db)

below you can see some alternatives that would work as well:

Session = sessionmaker(bind=self.metadata.bind, autocommit=False)
sess = Session()
meta = MetaData()
meta.bind = "sqlite:///model/movies.sqlite"
#
SqlTable("movies", session=sess, metadata=meta)

# passing a mapped class (Movie here is build with declarative layer):
# the metadata is found from the mapper.local_table.metadata

SqlTable(Movies, session=sess)

Session are created with autoflush=False, expire_on_commit=False but can be changed when building DbProxy.

Since version 0.8.6 default value for autocommit has been turned to True to prevent idle in transaction in postgresql.

session & expire_on_commit

The reason to have expire_on_commit=False is that if you don’t set it, after every commit, you have to reload all objects and the interface turns very slow, especially when working with a remote database.

expire_on_commit is a recent addition to sqlalchemy session (around sa rel. 5.03rc) so I try it and fallback to default that would turn to be slower. Previous 0.5 rel called it autoflush

class sqlkit.widgets.common.sqlwidget.SqlWidget(see_below)

The main widget used to edit a table or sqlalchemy selectable, i.e. almost everything you can express as table or for which you can provide a mapper or a mapped class.

You won’t use this as such: this is just the parent of SqlTable an SqlMask.

__init__(what=None, mapper=None, layout=None, layout_nick='default', naked=False, geom=(-1, -1), rows=10, addto=None, gui_field_mapping=None, title=None, show=True, icons='', col_width=None, table=None, tables=None, class_=None, join=None, ro=False, noup=[], sql=None, constraint={}, dbproxy=None, order_by=None, limit=200, field_list=None, session=None, metadata=None, ignore_fields=None, hide_fields=None, single=False, nick=None, about_dialog=None, dev=False, xml=False, relationship_mode='SINGLE', relationship_path=None, relationship_leader=None, format=None, label_map=None, hooks=None, mode='biud')
Parameters:
  • what – this is the table/mapper/class to be displayed. You are encouraged to use it as first argument. The keyword ‘what’ is just here for backward compatibility as the first versiona had different keywork for table/classes/mappers
  • table

    the table to be edited. May be a string in which case it will be autoloaded or a sqlalchemy.Table object. When using table argument no info on relationship to other table are passed except those contained in the Foreignkey, so you won’t be able to show one2many or many2many relationships

    If you pass 2 table as in movie director sqlkit will try to join them and build a proper mapper.

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the table as first argument istead.

  • class_

    a sqlalchemy mapped class (eg: that defined via declarative layer). The mapper will be automatically found

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the class as first argument istead.

  • mapper

    a mapper. All fields will be desumed by introspection

    This keyword is now deprecated and will disappear in future releases. You are encouraged to use the mapper as first argument istead.

  • session – the session to be used
  • dbproxy – a dbproxy object that holds info on metadata and can create sessions
  • metadata – a metadata object needed for introspection if reflection is required
  • hooks – the Hooks class to be used for this widget
  • ro=False – if set to True the table is not updateable. Old way to set mode=’b’
  • mode – mode for the widget. See set_mode for more explanations
  • noup=[] – a list of field names that will not be allowed to update. The list may be also a string space or comma separated
  • order_by=None

    order by column to be used when selecting. Can be

    • a list of sqlalchemy order_by clause elements
    • a string as would be set in ordinary SQL statements
    • a string of fields as would be used in filters: user_id__username (note the double_underscore). If you also have a relation user that points to the same table you can use user__username with the same effect.
  • limit=200 – limit attribute to be used selecting
  • field_list=None – a field_list to be shown. This can also be set via “set_field_list”. Operate via toggling visibility of the columns in Tables or preventing autogenerated layout to show.
  • ignore_fields = None – field to be ignores (will be deleted from field list to field_list). Can be a list or a comma separated string.
  • hide_fields = None – Only valid for SqlTable. List of columns to be hidden (but can be interactively made to show up from menu). Can be a list or a comma separated string.
  • single=False – if a table is opened in single mode, destroying the table quits the gtk main loop.
  • format=format_dict – equivalent to using: .set_format(format_dict). See Localization for more info
  • col_width – a dictionary of field_name/width for columns (e.g: {‘zip_code’ : 6}). It can be passed to masks also that will handle on to nested tables. In this case if you have a relation with attribute ‘address’ you may use: {‘address.zip_code’: 6}
  • gui_field_mapping – a dict that maps field names to Field classes. You only need to set this for non persisted fields, i.e. field that are not already defined in the mapper
  • title – the title for the gtk.Window.
  • show – a boolean to tell if the Window will be visible
  • dev – if a sqlwidget is opened in dev mode and no field_list is passed, the primary key is shown even if it’s a serial, if it’s not in dev mode it will be hidden
  • naked=False – the table will be rendered without any buttons. It’s used in layout when you want to add the table/mask to another layout (eg: in relationships). Default: False.
  • addto=None – if set must be a container to which the sqlwidget will be added
  • about_dialog – Pass a dialog to invoke when info is called. A gtk.AboutDialog info will be created by default if no about_dialog is passed
  • label_map – argument: label_map dictionary as explained in Localization Adds a mapping between field_names and msgid/translation for gettext and tooltips
  • layout – string description of the layout as described in SqlMask. It’s used to generate the layout for SqlMask also for table’s RecordInMask.
  • layout_nick – nick name of a layout to be used. Defaults to default. A layout can be registered under a nick via register layout function
  • relationship_mode – SINGLE, m2m, m2o, o2m
  • relationship_path – the path needed to join two tables. See sqlalchemy on ‘quering with joins’. Used by filter_panel to setup join condition when filtering when relationship_mode is not SINGLE. May be a list or a string: ‘genres’, ‘director nationality’, [‘director’,’nationality’]
  • relationship_leader – the main sqlmask (sqltable) that “owns” the filter_panel
add_constraint(OR=False, **kwargs)

Add Constraints. A constraint may be expressed via keyworks in django-like syntax. Eg.:

name_like='uno%'
genres__name__regexp='sto'
director_id__birth_date__gte='1/1/1970'

if multiple conditions are passed, they will be ANDed unless ‘or_=True’

A constraint may also be build directly into the self.query object.

Parameter:OR – the condition will be ORed
add_filter(active=True, **kw)
Parameter:active – boolean: make the filter active/inactive

see Filters and add_constraint above. Note that filter are always ANDed (OR arameter is not available).

If you have a field_name named ‘active’, the active parameter will hide it. Use keyword active__eq to bypass it.

resize(width, height)
Resize the window. It accepts also -1 as value in which case sets the current value, so it can be used to change just one dimention.
set_records(records=None, pk=None)

set ‘records’ as the list of record to manage

Parameters:
  • records – use ‘records’ as the list of records
  • pk – use primary key pk as te pk to show (TODO make it work with composite pks)
set_format(format_dict)

set_format(format_dict). format_dict is a dictionary whose key is the field_name and the value is the format of the column. Format_dict can have keys that do not correspond to any field in the widget. That makes it possible to reuse a format dict.

The format dict is passed to related widgets as well.

See Localization for more info

Parameter:format_dict – a dict of field_name/format strings
is_mask(obj=False)
Return True if the sqlwidget is a Sqlmask
is_table(obj=False)
Return True if the sqlwidget is a SqlTable
sb(text, seconds=10, delay=False)

Write on the status bar if present in this sqlwidget or in the relationship_leader

Adds a message in the stack of messages of the status bar, and removes it after seconds seconds. If delay=True it uses gobject.idle_add to give more chance to be visible (not hidden by other automatic messages)

Parameters:
  • text – the text to be written
  • seconds – how may seconds the text should stay visible
  • delay – boolean. If True message is added in an idle cicle. It means it will be shown after other possibly scheduled automatic messages that would hide its visibility.
get_value(field_name, shown=False)

return the value from the widget

Parameters:
  • field_name – the field_name
  • shown – boolean: in case field is a foreign key, True indicates we want the dislayed value rather than the real one
set_value(field_name, field_value, fkvalue=None, initial=False, shown=False)

set the value of any field present in gui_fields. Uses field.set_value if initial is False, run on_change_value

Parameters:
  • field_name – the field_name to be changed
  • field_value – the new value
  • fkvalue – a possible foreign key value. It’s here just for compatibility with SqlTable’s one
  • initial – a boolean indicating if it’s an initial value (passed to field)
  • shown – a boolean indicating if the value is the displayed value (passed to field)
get_current_obj()
Return the corrently edited obj. Note that in Table widgets, selection can already be elsewhere (as in on_selection_change).
set_mode(mode=None, reset=False, delay=False)
Parameters:
  • mode – the mode as explained below. If None, mode will be refreshed to last declared state (i.e.: reguardless of what you may have changed by hand acting on actiongroups).
  • reset – if True the mode will be completely reset. Needed to make the mode of a related table independent from the mode of the master
  • delay – if True, the mode is set but interface is not immediately updated

Set mode for this widget. Mode can be a string composed with the following letters that correspond to permissions possibly preceded by + or -.

s:SELECT. The user can view the records already selected (i.e. use Forward/Backward) or set by set_records. This is always granted and as such it’s pointless to set it (or revoke it)
i:INSERT. The user can insert new records
u:UPDATE. The user can update records
d:DELETE. The user can delete records
b:browse. The user can use the filter panel

If mode start with + or - the following permissions are granted/revoked for the widget by adding or removing from the modes already present. If no sign is used the mode is set.

mode is a property, you can set it directly: self.mode = 'b'

The mode influences permission by setting menu entries active or not. It’s not acting on the session. If an object has been inserted in the session a simple update operation can let it be inserted. This is by design.

Related table inheritate the same mode but you can programmatically reset it and make it independent from the master using option reset=True.

Implementation

Mode are implemented acting on uimanager/actionsgroup. You may read UiManager: menu and actions

Note

at present it’s not possible to insert a record in a table that is not updatable

add_temporary_item(item, menu, position=0, separator=False)

Adds an action to a menu and removes it with ‘selection-done’

Parameters:
  • item – the gtk.MenuItem that must be added
  • menu – the menu where the item needs to be added
  • position – the position where to insert the item (default: 0)
  • separator – boolean: add a separator (not implemented)
reload(limit=None, display=True, order_by=None, OR=False, **kwargs)

reload the data from the database taking all filter/constraints into consideration

Parameters:
  • limit – add a LIMIT clause (integer) to limit number of returned records. Permanent effect
  • order_by – reset order_by and apply when reloading. Permanent effect.
  • OR – (boolean, default False) as in add_constraint you can specify if conditions as in kwargs below should be ORed or ANDed (default)
  • kwargs – any filters accepted by the Django-like syntax and by add_filter

sql may be a tuple (sql_statement, bind_params) or just a sql_statement

commit(message='Salvato')

run session.commit() and take care of possible exceptions

Parameter:message – a message to be written in the status bar (default: Saved)
add_validation_error(error, field_name=None)

keep track of the error in self.validation_errors/validation_warnings so that a further process can collect them and present them to the user

Parameter:error – the ValidationError or an error message string
Field_name:the field_name to which the error refers. Defaults to ‘record validation’
add_not_null_error(field_name)

simple way to add a not nullable field error

Parameter:field_name – the field_name that cannot be nullable

Attributes

filter_panel
The FilterPanel widget

a container for all related sqlwidgets (i.e.: SqlWidgets that have a relation with this sqlwidget defined by sqlalchemy and that are displayed in the widget).

This is used in all situation in which you need to fine-tune the configuration of a related table (completion, layout, ...)

completions
a container of all completion objects. Needed to change the behavior of some completion (see Completion)
gui_fields
a container for all validation fields. A validation fields is an object that lives in sqlkit.fields and that knows how to represent a field and how to validate it.
gui_field_mapping
A dict used to force a map between a sqlkit.Field and a gui field. Read more in Fields.
query
the sqlalchemy query object (session.query(mapper)) with all constraints applied. Can be manipulated as necessary as long as it stay a query object.
layout

the layout definition for this SqlWidget. This is the used definition for SqlMask while for SqlTable is only used if the record is opened in SqlMask (right click on the record in SqlTable).

You can also set it on a related table:

GUI="director @ m2m=movies"
t = SqlMask(Director, layout=GUI, ...)
t.related.movies.layout = "title @ m2m=actors"
lay_obj
the sqlkit.layout.Layout object used to create the layout
current
the object represented by a mask or a possible selected record in a table or None. In SqlTable in the transient in which you are saving a record in a table (when selection changes) self.current will point to the obj that is to be saved (while the selected object may already be another one).
field_list
a list of the field names the GUI is handling. It comprises PropertyLoaders (i.e. properties of the class that act as a loader of other info - all relation are seen as PropertLoaders w/o column)
order_by
an order_by string or clause element. Same as parameter passed to the class. It’s a property, can be set in any moment.
session
the sqlalchemy session used for querying
defaults
an instance of sqlkit.db.defaults.Defauls instantiated with local=True as explained in Defaults local to the application. Any default set with this instance will only be visible in this sqlwidget.
title
the title of the Window.
mode
the mode describing permissions of the widget. See set_mode
noup

noup can be a set of field_name or a comma separated string with possible +- sign to add/remove field_names to the set of field_names that will not be possible to update

Note that to add a non editable field_name you must used ‘+field_name’. Using simply ‘field_name’ will reset the list to only that field_name

actiongroup_*
see UiManager: menu and actions
ui_manager
see UiManager: menu and actions
relationship_leader
Th case this sqlwidget is representing a relation of a SqlMask, that SqlMask is referred to as a relationship_leader.

Signals

pre-display:

A record is about to be displayed.

pre_display_cb(sqlwidget, obj):
Parameters:
  • sqlwidget – the widget that emitted the signal
  • obj – the object that is about to be displayed
record-selected:
 

A record has been displayed in Mask or selected in Table. The callback will just receive the widget as argument.

records_selected_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-saved:

A record has been saved. This signal is not emitted from within session extension. That means you are sure there will be just one signal for each button press on “save” button. This is issued from within the SqlWidget.commit() method independently from the fact that a real modification occurred, so you are not guaranteed any modification took place. Was originally added to implement a destroy of the widget when the save operation was performed.

The callback will just receive the widget as argument.

record_saved_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-new:

A new record has been added. The callback will just receive the widget as argument.

record_new_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
record-deleted:

A record has been deleted. The callback will receive the widget and the obj.

record_new_cb(sqlwidget, deleted_obj):
Parameters:
  • sqlwidget – the widget that emitted the signal
  • deleted_obj – the obje that was deleted

Note that this signal is emitted only for records deleted explicitly, i.e. records that where the current record in a mask/table. If a record is deleted as a side effect (e.g.: becouse cascade=”delete-orphan” is set) no signal is emitted for that obj.

records-displayed:
 

Records have been displayed. This may be after SqlWidget.reload() or SqlWidget.set_records(). The callback will just receive the widget as argument. See also context changed to see another signal that better tracks any change

records_displayed_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal
after-flush:

flush has occurred, normally commit should not add any errors. This is implemented with a SessionExtension: if you used the default session obtained via get_session() you are assured that it will be correct. If you create a session by yourself, be sure to add sqlkit.db.proxy.SKSessionExtension to the session extensions or you won’t have this signal. Read more detailed explanation in hook on_after_flush

after_flush_callback(sqlwidget, object, session)
Parameters:
  • sqlwidget – the sqlwidget that emitted the signal
  • object – the object that was current when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in “dirty state”, “new” or “deleted”, but current was the one selected in a table or displayed in the main mask
  • session – the session that was flushed. The moment in which the signal is emitted you can still dig into session.dirty, session.new and session.deleted and find which attributes have been changed (you may want to use get_differences())
after-commit:

run from within after-commit SessionExtension. Callback signature is identical to after_flush_callback above.

delete-event:

emitted when the sqlwidget is destroyed

delete_event_callback(sqlwidget)
Parameter:sqlwidget – the sqlwidget that emitted the signal

Hooks

Beside signals there is another way to add controls: hooks. A hook is a function that will be called in particular moments only if present.

Hooks are the main way to customize the behavior of a sqlwidget. Some of the hooks (on_validation.*) are related to validation other are related to configuration (on init), others (on_activate) may be used to save typing.

Hooks are searched for in the methods of the instance of a class declared in the optional hooks argument or in the global registered hooks (see below).

Hooks can be registered using sqlkit.db.utils.register_hook() (and get_hook from utils module) so that any sqlwidget built on that table will use those hooks (unless the table is part of a join or another selectable!!). The advantage is that browsing data (e.g. using right click on a table row) can lead to opening tables that are not configured: registering hooks is a way to enforce configuration (and possibly constraints) on any widget.

As layout hooks can be registered with a nick (default is default) so that you can register different hooks for different editing flavors. E.g/: you can have a table for people and you can decide to open it with customer or provider layout/hooks just registering both layout and hooks and using argument layout_nick.

Use it with care as it may lead in situation in which not all fields are present (due e.g. to a different layout or different field_list)

The following hooks are defined:

on_change_value__field_name:
 

this hook is used to trace changes in the widget. It’s mainly meant to be used interactively but it is also triggered when set_value is invoked with initial=False. It’s not changed if the value is set on record change.

Currently it behaves differently for Table widgets or Mask widgets.

Mask widgets invoke it each meaningful changes i.e. each char for varchar/int field, each time a value is choosen for enum/foreign key fields, when a date is selected or validated for Date widgets.

Table widgets invoke this hook only when the field is leaved or activated (Return is pressed).

Note

Implementation of this hooks is currently limited to some fields: Varchar, Int, Float, Numeric, Enum, ForeignKey, Date, DateTime, Bool. Text, Images, Time and Interval are not implemented. Example 63c is a complete example that shows it.

on_change_value__field_name(sqlwidget, field_name, value,
fkvalue, field)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that runs the hook
  • field_name – the field_name
  • value – the new value
  • fkvalue – if field is a ForeignKey or enum, the displayed value
  • field – the field
on_completion__field_name:
 

called when a completion is chosen

on_completion__field_name(sqlwidget, field_name, obj)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that runs the hook
  • field_name – the field_name
  • obj

    the matched object in the completion. This obj has attributes for each field_name named in attrs attribute of the completion. You can add field_names to that attribute if you need them in this hook function:

    sqlwidget.completions.director_id.attrs += ['nation']
    

    allows you to add a completion hook:

    def on_completion__director_id(self, sqlwidget, field_name, obj):
        print obj.nation
        sqlwidget.set_value('nation', obj.nation)
    

    you can also reach field attributes as dict values: obj[‘nation’].

save_as:

New in version 0.9.3.

this hook is invoked each time a record is saved as a duplicate of another one. After the new record is filled and before it’s saved you can customize at your will. Callback function:

on_save_as(sqlwidget, old, new):
Parameters:
  • sqlwidget – the mask/table that invoked the hook
  • old – the old object that was copied
  • new – the new object

When this hook is present, no warning on how the copy is handled is raised as it’s considered that the programmer has already coped with all the tricky issues.

on_validation:

called when all the values have been collected in the object, before calling validation on all fields and related widgets. That’s a good point to implement any procedure to add automatism’s. Within this hook you can propagate errors to the validation machinery in two of ways:

  1. raising sqlkit.exc.ValidationError. Simple when just one error is found
  2. filling self.validation_errors: a dict holding all errors. The key is the field_name or “record_validation”, the value a list of error messages
on_validation(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
on_field_validation__field_name:
 

called to validate a single field as for the previous on_validation. This is called from within the field's validation method

  1. raising sqlkit.exc.ValidationError. Simple when just one error is found
  2. filling self.validation_errors: a dict holding all errors. The key is the field_name or “record_validation”, the value a list of error messages
on_validation(sqlwidget, field_name, field_value, field)
on_activate__field_name:
 

when Return is pressed in Mask or Table. Good to complete fields via calculation on other fields (e.g.: total, vat...). The name derive from the GTK name ‘activate’ that is when you press ‘Return’ in an entry, even thought in a Table’s treeview it’s really connected to the cell’s edited signal (limited to Varchar and Numeric columns).

on_activate__field_name(sqlwidget, field_name, field)
Parameters:
  • sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
  • field_name – the field_name
  • field – the sqlkit.widgets.common.field
on_init:

run as the last command of __init__. It’s main purpose is to allow to configure a widget in a way that will be handed over to a possible SqlMask generated right-clicking from a table row (see RecordInMask).

on_init(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook
on_pre_layout:

run before the layout is setup. It’s main purpose is to allow to add fields in gui_field_mapping (that is only useful for SqlMask). Note that you can force a field for a table’s attribute if you want setting info’s field key pointing to that field’s class.

on_init(sqlwidget)
Parameter:sqlwidget – the sqlwidget (SqlMask or SqlTable) that run the hook

Note

Hooks invoked within the session extensions

The hooks that are called from within the session extensions, can be called several times if there are more sqlwigets sharing the same session (that happens for example each time you open a mask to edit the row of a table i.e.: RecordInMask)

on_after_flush:

run as the signal with the same name from within after_flush session extension method.

This hook is completely similar to after-flush signal, but is meant to be defined in a separate class so that it’s easier to propagate validation hooks to a spawned child (i.e.: RecordInMask, when opening a mask by right clicking on a table record). You’ll see that on_after_flush is called eather.

From the sqlalchemy documentation: “Note that the session’s state is still in pre-flush, i.e. new, dirty, and deleted lists still show pre-flush state as well as the history settings on instance attributes”. This is true for after_commit hook as well, it is not true for after_flush_postexec, that on the other had has already setup relation.

I end up in some circumstances to split the callback in two phases: one that detects if an action is needed from within the after_flush/after_commit phase, the second (may be a mail, or any other action) from within the hook on_record_saved, so that I can use the relations. Beware that you may have one call to on_after_flush and more different calls to on_after_commit

on_after_flush_callback(sqlwidget, object, session)
Parameters:
  • sqlwidget – the sqlwidget that emitted the signal
  • object – the object that was current when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in “dirty state”, “new” or “deleted”, but current was the one selected in a table or displayed in the main mask
  • session – the session that was flushed. The moment in which the signal is emitted you can still dig into session.dirty, session.new and session.deleted and find which attributes have been changed (you may want to use get_differences), but you won’t have correctly setup relation’s object
on_after_flush_postexec:
 

run as the signal with the same name from within after_flush_postexec session extension method.

As in the precedent hook this is called exactly within the session extension method by the same name. When it’s run the session will have no longer information on session.dirty/session.new/session.delete but will have all relations set-up.

The callback has the same signature as for hook on_after_flush

on_after_commit:
 

run after commit after_commit session extension method

The callback has the same signature as for hook on_after_flush

on_record_saved:
 
run from within the commit method of the widget, that

assures that will be issued just once. It’s just equivalent to the signal with the callback has the same signature:

record_saved_cb(sqlwidget):
Parameter:sqlwidget – the widget that emitted the signal

Registered hooks and SessionEtensions

When hooks are registered their customizations are enforced each time the model for which they’re registered is called. That adds some complications you should be sure to understand if using one of these hooks:

  • on_after_flush
  • on_after_commit
  • on_after_flush_postexec

These hooks are called within a SessionExtension that calls hooks on any sqlwidget that may be using the same session. An m2m, m2o relation table share the same session as the Mask that holds them so that it’s pretty normal to have several different tables within the same session.

From the SessionExtension hooks are searched for in any of these so that you should write the hooks keeping in mind it can be called from another sqlwiget’s commit.

As an example, suppose you have a Mask with the following layout:

first_name
last_name
m2m=genres   m2m=actors

suppose movies, genres and actors have registered on_after_commit hooks. They will all be called on any commit. Adding a genre object will trigger on_after_commit on the Actor’s table and vice verse.

get_differences

Module sqlkit.db.utils provides a simple function that yields all modified attributes of an object, along with their old and new values

get_differences(obj)
Parameters:
  • obj – the object to be inspected
  • session – the session the object belongs to
Return type:

field_name, old_value, new_value. Old value and new value are lists.

get_history(obj, field_name)
Parameters:
  • obj – the object to be inspected
  • field_name – the field_name
  • session – the session the object belongs to
Return type:

new_value, unchanged_value, old_value, . Old value and new value are lists. The order is different from get_differences as this is exactly what is returned from the SA function

Saving varchar and text fields

Text fields with empty values will be saved as NULL. To change this behavior you need to set blank=True on fields:

t.gui_fields[field_name].blank = True

UiManager: menu and actions

Menu entries are handled via standard gtk.UiManager interface. One interface is created for each toplevel Window and for each view in a SqlTable widget. You can see some examples in the demo (70-72).

Standard actions are divided into the following categories:

General (self.actiongroup_general)
     PendingDifferences
     Quit
     Go
     Modify
     Tools
     File
     Help
     About
Table (self.actiongroup_table)
     HideColumns
     ShowColumns
     Records
     MaskViewFKey
     Export
     MaskView
     Zoom-fit
Insert (self.actiongroup_insert)
     New
     Save-as (just for SqlMask)
Delete (self.actiongroup_delete)
     Delete
     RecordDelete
Update (self.actiongroup_update)
     Save
     Undo  (just for SqlMask)
Browse (self.actiongroup_browse)
     Filters
     Reload
Select (self.actiongroup_select)
     Back
     Forward
Print (self.actiongroup_print)
     Print
Debug (self.actiongroup_debug)
     Gtk-tree

While for each table’s view you have:

Table
     HideColumns
     ShowColumns
     Records
     MaskViewFKey
     Export
     MaskView
     Zoom-fit
Insert
     New
Delete
     Delete
     RecordDelete

Changing an entry

To change an entry you can:

  1. add an actiongroup in which you have defined an action with the same name
  2. insert this actiongroup before it’s relevant one (position 0 is normally a good choice

Example #72 in the demo shows how to do it

Adding an entry

the standard way is shown in demo snippet #70:

  1. create an xml definition and add it to ui_manager
  2. create an action and add it to an actiongroup

You can also use a SqlTable method add_temporary_item that will add a temporary entry, so that it can be contextually changed. This way is demonstrated in demo snippet #71


sqlkit-0.9.5/doc/html/sqlkit/validation.html0000644000175000017500000003516711714210376020450 0ustar sandrosandro Validation — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Validation

Validation is very important and is mainly accomplished via Hooks: on_validation and on_field_validation__field_name are expressly created for that with all the variants for related tables.

There are many different snippets in the demo related to validation and hooks: they are part of the documentation.

Validation Errors

The validation has the following steps:

  1. Each field in gui_fields, i.e. all fields that have a graphical representation are looked for the value and a hook named on_field_validation__field_name -if present- is run .

    Note

    At present fields representing m2m or o2m relations (e.g. movies in a director class) are not searched for on_field_validation__field_name hook. You can use on_validation hook to set validation on relations

    Each method of the hooks class can raise a ValidationWarning directly that is caught by the main validation loop, and populates validation_error dict or can feed this dict via the sqlwidget’s method add_validation_error or add_not_null_error

    This operation is repeated for each related process and the main validation_error dict is updated.

  2. If the validation_error dict has collected some errors a ValidationErrorDialog is presented to the user:

    class Hooks(object):
    
          def on_field_validation__year(self, mask, field_name, field_value, field):
              if field_value > 2020:
                  raise ValidationError("Hey: how can you know the future!")
    
    
    t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'},
               dbproxy=db, hooks=Hooks())
    

    that would raise a ValidationDialog as follows:

    ../_images/year.png

Validation Warnings

In a similar way you can raise/add a Validation Warning that will warn with a message but will not abort the process.

ValidationWarningDialog is presented after ValidationErrorDialog if both are needed.


sqlkit-0.9.5/doc/html/sqlkit/totals.html0000644000175000017500000005205211714210376017614 0ustar sandrosandro Totals — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Totals

Numeric columns of a Table have the option to show totals and subtotals. Subtotals are determined by brake fields/functions. This may be done interactively from the column menu or from code. The following example is very stupid but... you get the feeling!:

t = SqlTable('movie', dbproxy=db, order_by='director_id')
t.totals.add_break('director_id')
t.totals.add_total('year')
../_images/totals.png

Personalization and colors

Totals are generated by a class sqlkit.widgets.table.totals.Totals that can be inherited and modified to set different total behavior.

The colors are defined in a class in the same module TotalObj whose method set_value_and_colors can be used to personalize colors and markup of the cell.

Dates

Since date breaks are probably very common a function makes it easy to brake on dates.

Totals... with no totals!

If you only need subtotals and not totals you can prevent totals using option hide_total when declaring the column to sum.

Signals

computed:
this signal is emitted when the total is computed. The signature

of the callback function is:

computed_cb(total):
Parameter:sqlwidget – the total instance that emitted the signal

API

class sqlkit.widgets.table.totals.Totals(table, treeview=None)

An object whose ‘compute’ method adds TotalObjets to show partial totals/grand totals to a table. A TotalObject is inserted each time total_by changes. If total_by is a callable, it’s evaluated to detect if a partial total must be inserted

totals
A dict whose keys are the field names for which a total/subtotal is to be computed. The values are the totals. It’s filled by sum() that can be customized.
subtotals
A dict whose keys are the field names for which a total/subtotal is to be computed The values are the subtotals. It’s filled by sum() that can be customized.
add_total(*field_names, **kw)

Add a total object each time subtotal changes

Parameters:
  • field_names – a list of field_names for which we want a total
  • quiet – boolean. If True silently fail if the field is not a number
  • hide_total – boolean. If True gran total will not be shown (only subtotals)
add_break(field_name, func=None)

If no func is provided, a break_func will be setup to insert a subtotal each time field_value changes.

Othewise func will be set as break_function. Break function must have this signature:

def break_func(obj, field_name, path)

see func_date_* is this module for examples

add_date_break(field_name, period)
Set date break period can be: day, week, month, quarter, year
compute()
Go, add subtotals and total
sum(obj, model, path, iter)

sum values and store result in totals and subtotals

Parameters:
  • obj – the obj that should be added. It’s an instance of table.mapper.class_
  • model – the model of the treeview
  • path – the path at which the obj is
  • iter – the iter at which the obj is

To customize the behaviour of total you can just customize this method, suppose you want to make it sum flagged objects in table t:

class BoolTotals(totals.Totals):

    def sum(self, obj, model, path, iter):
        if obj.flag:
            for field_name in self.totals.keys():
                self.totals[field_name]    += getattr(obj, field_name, 0) or 0
                self.subtotals[field_name] += getattr(obj, field_name, 0) or 0

t. = SqlTable(...)
t.totals = BoolTotal(t)
class sqlkit.widgets.table.totals.TotalObj(fields, sub=True)

A simple class that represents the object holding the totals and a way to represent it

personalization

You can change the look of totals inheriting from TotalObj and placing the new class as class attribute of Totals

set_value_and_colors(cell, value, field_name)
set the color cell and possibly background

sqlkit-0.9.5/doc/html/searchindex.js0000644000175000017500000013431111714210376016743 0ustar sandrosandroSearch.setIndex({desctypes:{"0":"method","1":"attribute","2":"function","3":"class"},terms:{get_text:13,yellow:[26,13],poorli:16,prefix:33,sqlelchemi:11,partecip:13,forget:[16,26],whose:[8,24,1,11,20,5],string2dat:0,unchanged_valu:1,under:[1,11,13,16,6,7],spec:17,many2mani:[29,3,27,4,1],digit:20,everi:[4,1],intervalwidget:12,objectproxi:11,timewidget:12,appar:5,interchang:13,uni_text:29,focus_out_ev:12,metaclass:[23,25],upload:[12,13],red:26,persisten:13,throught:[6,11],datetimetzfield:13,direct:[13,21],second:[8,0,1,20,14,26,5,17,19,6],variable_pattern_oo:26,after_flush:1,edited_cb:19,blue:[16,33,25,26],keyserv:16,hide:[33,0,1,26],sksessionextens:1,introspect:[8,1,13,33,4,29,6],"new":[8,10,1,26,23,12,13,33,3,4,21,20,24,19],net:16,metadata:[1,23,3,4,21,29,6],set_opt:33,widget:[8,34,0,24,1,12,13,25,14,33,3,4,5,20,16,17,18,19,29,30,32],behavior:[1,24,3,27,4,29],displai:[8,0,1,11,12,13,33,4,5,16,29,6],oowrit:11,never:[10,12,13],remeb:0,here:[0,1,13,3,4,21,6,7],column_properti:0,path:[10,1,11,12,13,33,3,16,17,24,19],interpret:11,luther:26,telefonata:23,algebra:0,fkei:[8,33,13],dentella:[26,7],precis:[16,13,5],datetim:[8,0,20,1,12,13,15,29],user_id__first_nam:10,booltot:24,actiongroup_upd:1,validation_error:[1,22],itselt:[0,13],nation_cod:4,"bj\u00f6rklund":16,on_field_validation__field_nam:[1,22],total:[21,0,1,11,24,33,26,34,28,16,17,18,19,29],describ:[0,1,20,13,4,5],would:[8,0,10,1,11,12,13,4,21,5,20,16,17,22,6],on_field_validation__year:22,movie2:11,movie1:11,call:[21,0,1,23,13,25,14,33,4,5,16,6],difficulti:5,type:[8,20,1,26,23,12,13,33,4,16,6],tell:[13,1,6],sqlakchemi:21,notin:10,datetimewidget:12,relat:[8,0,10,1,11,12,13,32,33,3,4,21,30,20,16,17,22,29,6],get_current_obj:[17,1],warn:[8,1,22,15,27,4],record_saved_cb:1,hold:[24,1,11,12,13,26,3,4,16,19,6],must:[21,0,10,1,11,24,13,33,3,26,4,5,16,19,6],referenced1:29,referenced2:29,join:[0,10,1,11,9,33],setup:[10,1,12,33,5,16,24],work:[0,1,20,13,29,16,17,7],decimalwidget:12,loosen:[4,13],set_field_list:[33,1],give:[0,1,20,33,21,6],sessionetens:[32,1],indic:[26,1,5,6],woman:5,want:[8,0,10,1,11,24,13,25,14,33,3,26,4,15,5,16,20,19,6,21],"fran\u00e7ois":4,field_class:0,end:[0,4,1,5,11],thing:[16,15,5,26],ordinari:1,ilik:[0,10],how:[8,0,10,1,15,22,31,13,14,33,26,4,21,5,16,18,29,7],lay_obj:1,disappear:1,verifi:0,cellrender:15,updat:[1,22,12,13,3,16],recogn:[8,29,10],lai:[22,13,33,3,5,29,17],tablenam:1,initial_valu:13,after:[8,0,1,22,13,14,33,3,4,5],color_pars:12,django_syntax:10,befor:[1,26,25,33,5,19,6],wrong:[6,21],demonstr:[29,3,1],blank_ok:13,autoconnect:6,attempt:[33,6],minim:[29,33,5],exclud:25,perform:[33,4,1],maintain:12,environ:[29,7,11],enter:[16,33,4,21,29],get_obj_at_path:33,order:[0,10,1,26,20,13,33,4,16,19],origin:[8,1],composit:[33,0,1],feedback:[16,9],over:[1,11,5,12,33,21],becaus:16,jpeg:13,help_text:[20,6],flexibl:16,gentl:6,fit:[26,12,1,13],fix:[17,23,4,7],better:[8,1,4,16,17,7],objct:0,persist:[33,1,21,13],hidden:[33,1],mydb:29,easier:[1,13],descend:[33,19],them:[21,0,1,11,33,3,5,16,19,29,6],subtot:[16,33,24,29],thei:[0,10,1,22,23,14,3,5,16,19],anli:25,safe:12,"break":[33,24],layut:21,on_image_select:12,choic:[16,0,4,1,7],mytabl:29,changelog:[31,7],scale_pixbuf:[12,13],timeout:6,each:[8,0,24,1,11,12,13,25,33,26,4,21,5,20,16,22,19,29,7],debug:[1,25,2,5,18,6],side:[10,4,1],mean:[8,0,10,1,11,13,29,3,26,4,5,16,17,19,6],show_imag:12,gtkvpane:5,aboutdialog:1,maskviewfkei:1,fkeycomplet:4,extract:0,network:11,newli:33,content:[26,19,5,11],rewrit:14,intro:13,multilin:26,navig:29,situat:[10,1,11,13,4,5],standard:[1,11,20,13,4,5],replace_column:0,xthvbnpsvhpobmmfa:5,admitt:12,moment:[8,0,10,1,23,5,16,6],filter:[8,0,10,1,26,33,3,27,4,21,34,28,16,18,29],mvc:17,isn:5,confus:[16,26,5],fmt:20,set_frame_label:8,render:[8,1,11,12,13,32,33,3,26,15,5,16,19],liststor:[33,12,19],independ:[26,4,1],wast:16,hook:[8,1,22,13,32,33,4,21,29,17,30,6],clear_valu:13,alreadi:[8,0,10,1,11,23,13,33,3,26,4,5,16,17,29,6,7],timefield:13,lcontain:[14,5],primari:[8,1,13,33,21,6],rewritten:14,tor:21,top:[3,10,5],sometim:[10,6],stack:1,mercuri:34,ton:15,too:[0,26,23,29,4,5,16],"03rc":1,similarli:33,euro:26,listen:[26,19],wid2:5,tool:[0,1,11,12,5,29],setuptool:[16,7],took:1,tee:16,mandatorili:33,date_format:20,fullfil:13,actiongroup:1,target:6,keyword:[17,23,10,1,13],provid:[8,0,10,1,12,13,25,26,4,21,5,20,29,17,24,19,7],keywork:1,tree:[0,1],project:[3,20],minut:7,treeview:[0,1,24,13,25,33,5,17,19],beginn:0,on_change_value__addresses__domain:17,fashion:[16,33],"_dbg_parse_layout":5,mind:[10,1],increment:26,seen:1,incompat:[17,18,31],layoutgener:[13,5],cope:[29,26,1,13],contact:7,set_stock:12,even:[0,10,1,11,13,29,26,4,16,6],plenti:[16,29,5],usernam:[20,10,4],actiongroup_brows:1,object:[8,0,10,1,22,11,12,13,25,33,26,4,21,5,20,17,24,19],what:[8,0,20,1,11,12,31,14,33,3,26,4,15,5,16,18,21],regular:[16,26,4],letter:[29,0,4,1,6],attributeextens:17,simplic:16,don:[10,1,26,20,33,4,16,29,6,7],doc:[26,33,3,34,16,29],markup_label:8,digress:16,declar:[29,3,24,1],on_save_a:[8,33,1],sum:[16,17,24],dot:5,nee:5,prin:6,"__str__":[0,4,21,25],syntax:[0,10,1,11,20,14,26,3,4,28,29,6],loaddata:6,textfield:13,exactli:[8,1,11],layout:[8,32,30,20,1,22,12,13,14,33,3,21,5,16,17,18,29,6],field2:4,menu:[1,11,24,32,33,5,16,6],explain:[0,1,13,33,16,17],field1:4,title__icontain:4,rich:[8,6],folder:20,myobjproxi:11,googlegroup:[18,31,7],label2:5,register_hook:[1,21],prenthes:21,report:[25,7],recalcul:4,actiongroup_:1,force_enum:4,bar:[12,1],"__metadata__":3,method:[14,0,10,1,22,11,23,12,13,25,2,33,26,4,5,20,17,24,19],reload:[8,0,10,1,33,16],stead:3,corrent:1,neq:10,steal:8,on_field__valid:13,cicl:1,output_nam:11,elimin:33,mandatori:[32,19,1],result:[0,10,21,23,24,4,5,16,20],respons:3,fail:[16,24,7],charact:16,best:[29,13],awar:[12,6],totalobjet:24,databas:[0,1,20,13,14,33,3,4,21,16,30,29,6,7],yet:[29,12,13,21],figur:5,after_commit:1,templatewithstyl:26,drawn:16,approach:16,attribut:[8,0,10,1,11,23,24,13,32,33,3,4,21,30,20,16,18,19,29,6],accord:[8,0,10,26,12,13,33,4,5,16,20,29],extend:[18,6],extens:[16,26,1],lazi:26,add:[8,0,10,1,22,11,12,13,33,4,5,20,16,17,24,19,6,7],pygtk:[16,29,7],easi:[8,0,10,11,24,25,14,29,3,4,5,16],truffault:4,ouput_dir:11,vbox:[12,5],stadard:7,cod:4,logic:13,browser:[9,18,6],com:[16,26],col:[33,0,5],uno:[26,1,11],assur:1,sai:[16,6,5],loader:1,commit_allow:25,assum:0,duplic:[8,33,1,32,26],totalobject:24,get_selected_path:33,start_edit:19,gtktooltip:5,three:16,been:[8,10,1,11,12,13,33,3,4,5,7],trigger:[1,12,13,33,4,16,17,29,6],interest:16,basic:[9,12,4,29,18,6],deleted_obj:1,fals:[8,0,10,1,26,23,12,13,33,3,4,21,16,19],rather:[1,26,12,13,33,16,17],xxx:6,argument:[10,1,26,23,20,13,25,32,33,4,16,17,19,6],child:[0,19,1,5,6],"catch":13,spin:5,inhibit:33,ident:1,forst:0,istanc:26,readeabl:10,properti:[8,0,1,12,13,14,3,21,5],aim:[10,4],calcul:1,combol:5,spawn:1,printabl:11,contetxt:26,cliente_visita:23,kwarg:[33,4,1,13],brake:[33,24],conf:5,n_movi:0,varchar:[8,32,1,12,13,14,29],gtkmenubar:5,trace_funct:25,sever:[1,26,33,21,29,6],disabl:[16,33,0,6,26],main_queri:4,incorrectli:13,receiv:[1,11,13,26,5,17],suggest:[8,0,12,15,21,16,6],make:[8,10,1,15,24,14,33,3,26,4,21,16,17,6],complex:[8,26,12,13],split:[33,1,5],complet:[8,34,10,1,12,13,33,3,27,4,21,5,16,17,18,29,6,7],hang:6,hand:[3,0,1,33],rais:[22,1,13],refin:[8,32,5],tune:[17,0,1],table_lookup:4,redefin:13,get_oper:0,bewar:[1,7],thu:[16,33],gtktabl:5,inherit:[8,24,1,12,13,33,4],client:[26,5,11],thi:[0,1,3,4,5,6,7,8,10,11,12,13,14,15,16,17,19,24,21,22,20,25,26,29,33,34],programm:[1,26,13,29,5,16,6],everyth:1,left:[16,13,5],identifi:5,django2sqlalchemi:10,just:[8,0,10,1,11,12,13,14,33,3,26,4,5,20,16,24,19,29,6],treestor:[33,19],"39012cf8":16,human:[29,13,11],uimanag:[32,1],languag:[8,20,14,5,29,18,7],previous:17,group_bi:[27,4],expos:26,had:[17,1],prepare_context:11,macport:16,get_thumbnail:13,register_layout:21,set_oper:0,opt:[8,1,5,6],"_sqlkit_tabl":[4,6,21,13],mayb:6,preserv:26,gtkhpane:5,user_id__usernam:1,background:[24,13],tablewithstyl:26,field_nam:[8,0,24,1,22,23,12,13,33,4,21,29,17,20,19,6],opn:6,apart:[33,5],get_default:13,specif:[33,1],deprec:[17,1],manual:16,zoom:[33,12,1],singl:[0,1,11,12,26,4,5,29],related_obj:13,right:[8,1,33,5,16,17,6],old:[4,1,21],oli:5,interv:[16,29,12,1,13],gtkmenu:5,sure:[10,1,33,4,16,6],dear:26,inact:[0,1],interg:4,autocommit:1,txt:[12,5],bottom:5,subclass:0,djangopars:10,track:[33,1,25,17],condit:[0,10,4,1],foo:13,strada:8,localhost:[16,29,6],core:[16,21],bold:[8,5],booleannullfield:13,scrollabl:29,promot:26,repositori:[16,7],ecord:6,disreguard:13,postgresql:[0,10,1,29,16,6,7],total_bi:24,miniwidget:[12,13],slightli:3,commit:[12,1],produc:[10,11,13,14,33,3,26,5,16,29],ppa:16,records_displayed_cb:[33,1],chooser:12,"float":[29,20,12,1,13],latter:[33,12,17],down:[33,4,5,26],on_completion__field_nam:1,wrap:11,opportun:0,gtkvbox:5,set_edit:[33,3,12,4],search_field:[4,6,21],add_validation_error:[1,22],choosen:1,accordingli:13,wai:[0,1,3,4,5,6,8,10,11,13,14,16,19,20,22,23,24,25,26,29,33,21],support:[9,10,20,13,4,5,29,17,18,7],transform:11,happi:[16,15],avail:[21,10,1,11,13,29,4,5,16,17,7],width:[1,12,13,33,4,5,17],editor:[29,6,5],add_column:[33,0,19],wav:1,sqledit:[9,20,29,4,21,16,18,6],frame_nam:8,form:[0,26,20,25,29,4,5,16],offer:[13,19,6,11],forc:[10,1,20,13,33,4,5,16,6],traceit:[2,25],obj_proxy_class:11,"true":[8,0,10,1,11,23,12,13,25,33,3,26,4,21,5,16,24,19,29,6],optionmenu:0,attr:[4,1,21],new_nam:13,maximum:33,year__lt:[29,10],boundl:16,versiona:1,pendingdiffer:1,unrel:4,emit:[8,24,1,11,12,33,19],featur:[0,15,9,33,3,5,16,18,29],countmovi:[0,13],autoscal:12,thumbnail_path:13,exist:[16,10,4,13],check:[8,16,4,13,11],fkey_record_in_mask:33,descr:[30,21],becous:[10,1],when:[0,1,3,4,5,6,8,10,11,12,13,14,15,16,17,19,24,23,20,26,29,33,21],actor:[8,10,1,23,3,4,29],flood:13,role:26,notlik:10,autoreduct:12,varchar200:29,intend:[16,9],notepad:5,consid:[16,0,20,1],sql:[16,29,3,1,6],get_thumbnail_path_with_s:13,femal:[12,4],sqlmask:[8,32,0,20,1,22,11,23,12,13,14,33,3,4,21,34,29,17,18,6],faster:33,scrolledtreeview:5,flag:[24,5],ignor:[33,1,25],time:[8,0,24,1,11,12,13,25,33,4,21,16,20,19,29,6],push:[12,5],sqlmasq:6,backward:[16,17,18,31,1],concept:10,global:[23,12,1,13],collectionfield:13,signific:[0,11],id0:5,id1:5,year__gt:[29,10],row:[8,0,1,26,20,25,32,33,3,4,5,16,19,6],decid:[16,3,4,1,6],ver:[16,7],decim:[20,13],readabl:[13,11],rel:[0,1,33,4,34,16,17,29,7],decis:[6,13],sourc:[16,5],string:[21,0,1,20,13,33,3,4,5,16,6],field_attr:13,tabledescr:[4,21],desum:1,word:26,brows:[8,9,10,1,13,33,4,34,28,16,18,29,6],level:[29,3,10,4,5],gui:[1,20,13,14,33,5,29,18,6],dig:1,iter:[32,17,24,5,11],item:1,dir:[13,11],prevent:[8,1,24,13,26,4],slower:[26,1],msgid:[20,1],primarykei:29,sign:[0,1,5],port:[26,5,11],appear:[8,33,20,16,11],currenc:26,hereq:6,current:[8,0,1,11,12,13,33,16,17,19,29,6],deriv:[16,1,13],old_valu:[21,1,25],autogener:1,gener:[8,24,1,11,12,13,25,33,4,21,29,19],onli:[8,0,10,1,11,23,12,13,33,26,4,21,5,20,16,17,24,30],explicitli:[17,1,5],modif:[33,1],address:[10,1,26,13,29,5,16,17],gtknotebook:5,istead:1,along:[26,10,4,1],cthe:13,wait:[16,4],box:13,discount:26,shift:[16,33,4,29],a_function_that_returns_possible_valu:4,behav:[14,3,0,1],expressli:22,useful:[16,0,21],extra:26,modul:[21,0,10,1,11,12,25,2,26,24,4,5,20,16,18],prefer:[10,11,23,12,25,29,4,5,16],clause_list:10,get_hook:[1,21],leav:[33,3,1],moviemask:1,visibl:[8,33,0,29,1],marker:26,instal:[16,9,7,5],regex:10,prove:14,sake:20,visit:7,todai:[16,26,0,27,23],perl:26,live:[16,1,5,13],handler:[21,13,5],criteria:[0,4,13],add_to_t:11,reorder:19,chapter:[29,15,0],notilik:10,peopl:[15,1,26],claus:[33,1],visual:[19,6],templat:[26,18,6,34,11],my_host:6,effort:[16,3,5],easiest:[16,29],fly:[8,21,14,4,5,18,19],graphic:[5,22],biud:1,prepar:[8,16,11],focu:[33,15],objproxi:[32,11],whatev:[16,29],cal:5,purpos:[14,1,13,25,2,5,29,18],pre2:3,encapsul:5,"__obj__":0,"14d":0,abort:[33,22],occur:[33,12,1,25],alwai:[33,1,21,17],multipl:1,gtktoolbar:5,write:[14,10,1,11,23,20,25,2,29,26,4,5,16,6],till:[0,6,5],criterion:4,attach_inst:[3,13],map:[10,1,33,13,26,5],product:[16,7],book:26,pypi:[31,7],clone:[26,7],usabl:16,checkbutton:[12,5],blisset:26,levi:26,direct_id:11,mai:[8,0,10,1,11,12,13,33,3,26,4,21,5,20,16,17,24,29,6],underscor:[29,10,4,5],data:[8,9,10,1,26,23,12,13,33,3,27,4,34,28,16,18,19,29,6],grow:[17,6,5],man:5,stress:26,gui_field:[0,1,22,20,13,33],goal:26,explicit:[8,4],tale:4,inform:[1,13,4,16,19,6],"switch":[12,25,2,26,19,6],preced:[1,21],combin:8,el_kei:5,callabl:[23,24,4,11],noup:1,talk:5,searchdescriptor:26,cell_render:19,delete_event_callback:1,still:[16,1,6],mainli:[29,33,1,5,22],dynam:[8,4],entiti:[3,13],conjunct:10,interspers:5,group:[0,20,12,26,4,19],thank:16,polici:13,gtk:[8,1,12,13,25,32,33,3,5,29,6],window:[0,1,11,12,31,25,14,3,5,16,7],curli:5,curiou:16,mail:[9,18,1,13],main:[0,1,22,23,12,13,32,33,3,4,5,16,17,19,29,6],gtkalign:5,non:[16,33,1,13],pixbuf:[12,13],initi:[13,12,1,21,11],nation:[0,1,29,3,4,16],underneath:3,m_address:17,now:[10,1,23,25,29,3,27,4,16,17],nor:[29,23],introduct:[16,10,6],term:0,ceat:19,name:[0,1,3,4,5,6,8,10,11,12,13,14,16,19,24,22,23,20,25,26,29,33,21],drop:[19,5],revert:7,separ:[0,1,20,33,5,16,6],tablewidget:12,value_set_cb:12,collaps:5,primo:26,replac:[29,26,0],editing_started_cb:19,continu:8,order_list:19,totalobj:24,year:[0,20,24,14,33,3,4,21,16,29],happen:[16,33,3,1,25],get_selected_obj:17,foreign_kei:[4,13],shown:[8,0,24,1,12,13,33,4,16,20],accomplish:[16,22],"_sqlkit":21,space:[1,26,33,5,29,17,6],width2:5,width1:5,rational:4,unsensit:[12,13],correct:[1,11,23,13,26,5,16,7],tmpl:26,state:[0,1,11,12,25,33,4,19],film_numb:0,getcwd:13,argu:16,orm:[10,21],org:[26,18,34,7,11],care:[16,7,4,1,5],or_:[10,1],suffici:10,intervalfield:13,attrribut:26,turn:[21,0,1,11,12,13,33,5,16],place:[12,1,24,13,21,29,6],nicknam:[6,21],think:[14,5],frequent:4,first:[21,0,20,1,26,12,14,33,3,4,5,16,17,19,6,7],oper:[8,0,10,1,22,13,33,4,16,29],directli:[21,0,10,1,11,22,13,26,3,4,5,20,6,7],template_nam:11,onc:[3,1],yourself:[16,7,1,5],varchar10:29,add_date_break:24,oppos:[16,29,19],nester:5,open:[8,0,10,1,12,33,4,21,16,29,6],size:[12,13],given:[21,13,5],set_format:[20,1],silent:[24,5],bookmark:0,caught:22,do_set_properti:12,add_record:33,my_field:33,wid1:5,circl:26,conveni:20,imagefield:13,after_flush_callback:1,especi:1,memaningful:12,copi:[8,1,26,13,33,19,6],inconsinst:12,specifi:[21,1,25],enclos:5,mostli:5,string_field__nul:0,holder:29,than:[10,1,26,12,13,33,4,5,16,29,6],png:13,serv:4,wide:[13,21],posit:[16,26,19,1,5],sqltable2:25,parent_contain:5,pre:[8,19,1],invitati:6,ani:[8,0,10,1,11,23,12,13,33,26,4,15,5,16,20,29,6,7,21],mater:0,scale_factor:12,actiongroup_debug:1,caus:5,engin:[29,1],alias:[3,10],destroi:[0,1],note:[8,0,10,1,11,22,33,3,26,4,5,16,20,6],take:[21,1,20,4,5,16,7],green:[26,12],noth:[10,5],begin:[16,29,0],printer:11,trace:[1,25],normal:[21,0,1,11,12,13,25,33,26,4,5,16,29,6],computed_cb:24,price:26,statu:[10,1,12,33,4,19],toplevel:[1,5],sql_statement:1,pair:5,set_mod:1,icon:[33,1,13],complete_path:13,later:[16,19],quantiti:[0,20],meanwhil:13,sqlalchemi:[0,10,1,23,13,33,3,4,21,28,16,17,29,7],std_cleanup:13,obj_list:11,gobject:1,translat:[1,20,13,26,5,7],show:[21,0,10,1,12,33,3,4,5,20,16,24,29,6],permiss:[15,1],xml:[14,1,5],argolinux:7,slow:[26,1,6],ratio:[12,13],favor:[17,5],transact:1,activ:[0,10,1,26,4,5,16],enough:[16,4],dict:[8,1,11,24,13,26,5,22],analyz:3,offici:16,decimalfield:13,overwritten:13,reset:[19,1,11],get:[8,0,10,24,13,25,33,3,4,16,19,29,6],def_str:13,autoload:[13,23,1,21,6],cannot:[29,33,12,1,5],requir:[8,10,1,11,23,31,13,33,3,26,21,16,17,18,29,6,7],mapper:[0,10,1,24,13,33,3,4,16,17],date_releas:[8,29,3,13],borrow:10,yield:[1,21],email:6,stupid:24,where:[21,0,10,1,11,23,12,25,29,3,26,4,5,16,17,20,19,6],save_a:[26,1],floatwidget:12,register_class:21,onetomani:[3,13,21],calendar:[8,12,5],detect:[16,26,24,1],review:5,enumer:4,filtered_queri:4,label:[8,0,20,29,5,16,6],getattr:24,between:[21,10,1,26,12,13,14,33,5,16,29,6],break_func:24,"import":[8,0,10,1,22,23,12,13,25,29,26,4,21,16,20],assumpt:[9,26,29,4,16,18],parent:[19,0,12,1,6],dome:13,spare:5,come:[16,29,26,4,11],fieldchoos:13,datet:13,similar:[26,33,22,1,13],relationship_path:1,imagemenuitem:5,textwidget:12,tour:[16,29,5],tutori:[0,11,9,16,18,7],psycopg2:[16,7],improv:[16,0,20],among:[16,0,6,5,7],acceler:11,color:[24,12,13,28],inspir:10,period:[0,24],pop:[8,12,16,33,3,29],add_related_object:[33,13,17],set_pag:0,"63c":1,save:[8,0,1,15,12,13,32,33,26,29,6],sess:1,prototip:26,coupl:3,invers:3,unregister_hook:21,ui_manag:1,addit:[29,33,1,21,13],bypass:1,thousand:20,fellini:[8,16,23,4],doesn:[26,0,4,13,5],former:26,those:[8,16,20,19,1],sound:1,myself:[6,25],pedant:16,record_valid:1,cast:[8,6,13],invok:[8,33,16,1,26],megawidget:29,set_pattern:26,advantag:[1,6],ctrl:[29,4,6],recorddelet:1,quiet:24,set_pixbuf:12,ascii:26,"__init__":[21,1,11,12,26,5,19],filter_bi:10,develop:[16,7],m2m_edit:3,author:[26,20,31,7],tree_field_nam:19,audienc:[16,9],same:[8,0,10,1,26,12,13,33,3,4,21,5,20,16,24,19,29],double_underscor:1,html:[8,14,29,26],document:[1,22,12,33,26,34,16,17],week:[0,24],map_layout:5,nest:[8,33,1,5,21],driver:[16,7],director:[8,0,20,1,11,12,13,33,3,26,4,21,16,22,29,6],unhandl:23,mani:[8,0,1,22,20,13,29,26,4,5,16,6],assigned_by_id:10,appropri:[23,10,13],format_dict:1,markup:[8,14,24,29],without:[33,4,1,6],model:[8,0,1,22,24,13,33,3,16,17,19,6],all_typ:29,datefield:13,execut:[33,6],tip:[5,11],notiregexp:10,rest:[8,14,10,25,5],bool_nul:29,clauselist:10,flavor:1,gdk:[33,12,13],filtertool:0,hint:[33,20],except:[4,1,13],littl:[14,16,4,5],desktop:16,instrument:8,about_dialog:1,pre_display_cb:[8,1],table_nam:[26,0,21,11],around:[1,5],get_differ:[32,1,21],read:[0,1,11,23,20,33,26,5,16,6,7],defaul:1,test:[16,29],"40d":4,grid:5,amp:[29,31,7],sessionextens:1,relativedelta:0,integ:[12,1,26,20,13,29,3,16],server:[26,23,27,11],benefit:6,imagewidget:12,cascad:[1,13,33,3,16,17],output:[0,11,13,25,33,26,4,16,29],manag:[0,1,11,20,13,26,3,21,16],set_fk_layout:33,ascend:[33,19],satisfactori:33,join_path:10,frame_kei:8,django_lik:0,confirm:5,definit:[1,11,23,13,14,29,3,5,16,6],token:[8,0,4,21,5],exit:6,complic:[29,26,1],refer:[8,0,1,23,26,4,5],process:[1,22,13,26,5,16],arrow:[8,16,4,5],power:[8,15,0,29,3],oootempl:[26,11],inspect:[16,1],dislai:1,regexp:[0,10,26,33,4,5,16,29],sqlmak:[3,27],preserve_styl:26,firebird:[29,4],meaning:[33,12,1,13],actiongroup_t:1,degre:0,stand:10,act:[0,10,1,13,3,4,17],fkvalu:[17,1],mytempl:26,on__field_chang:17,periodico:26,unregist:21,gtk_icon_size_dialog:12,strip:[4,13],yout:6,your:[8,1,26,23,20,34,25,29,4,5,16,17,6,7],grater:10,log:[2,25],overwrit:[6,13],start:[8,0,10,1,26,33,3,4,5,16,6,7],interfac:[16,12,1,13],lot:[14,16,29,5],property_nam:5,tipic:26,tupl:[10,1,26,20,13,33,5],bundl:16,realli:[0,1,11,14,33,3,4,15,5,16,29,6,7],viewport:5,"__get__":25,notat:5,fielnam:12,dirti:1,possibl:[8,0,10,1,26,12,13,14,33,3,4,21,5,20,16,24,19,29,6,7],"default":[8,0,10,1,11,23,12,13,26,33,27,4,21,5,20,16,34,18,19,29,6],output_dir:11,validationwarningdialog:22,recordinmask:[32,33,1,21],foreignkei:[9,10,1,12,13,32,33,3,27,4,21,16,18,29],autocomplet:[27,4,6],many2on:[29,3,27],embed:29,connect:[10,1,11,13,26,3,6],crear:8,creat:[8,0,1,11,23,22,13,33,26,4,21,5,16,19,6],class_name__attribute_nam:11,certain:3,field_name_list:33,deem:14,file:[1,11,12,13,33,26,5,16,6],momentarili:4,film:[8,0,29,3,16,6],fill:[8,20,1,11,24,3,4,5,6],again:[33,3,10,4,21],get_data:33,gettext:[20,1,5],field:[0,1,3,4,5,6,8,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,29,30,32,33,34],assigned_by_id__usernam:10,valid:[1,22,13,33,27,4,34,29,18,6],you:[0,1,3,4,5,6,7,8,9,10,11,12,13,14,16,17,19,24,22,23,20,25,26,29,33,21],ooootempl:26,poor:12,zip_cod:1,image_widget:12,sequenc:5,symbol:[26,0,3,5],get_layout:21,peac:16,directori:[16,13,34,11],mask:[8,0,20,1,22,11,12,9,13,14,33,3,4,21,16,17,18,29,6],second_field:29,tricki:1,mimic:4,gtkmenuitem:5,gtkhbox:5,represent:[0,20,1,11,12,13,29,4,21,16,22,30],all:[8,0,10,1,11,12,13,25,14,33,3,26,4,15,5,16,22,19,6,21],foreig:33,consider:1,illustr:20,month:[16,0,24],deprecationwarn:1,abil:[8,16,3,26],follow:[0,1,3,4,5,8,10,11,12,13,15,16,17,24,22,23,20,25,26,29,33,21],alt:4,children:[12,13,5],init:1,program:[9,26,29,4,5,16,18,6],introduc:16,"case":[8,0,20,1,11,23,12,13,25,33,3,26,4,21,5,16,17,19,29,6],vers:[16,1],liter:23,far:[16,4],horror:1,util:[21,0,10,1,4,34,18,19,30],mechan:[14,3,4,13,5],dbproxi:[0,10,1,22,24,13,32,4,29],veri:[8,0,24,1,11,12,13,33,3,26,4,16,22,29,6],ticket:10,on_after_commit:1,hide_tot:24,on_image_displai:12,list:[21,0,10,1,11,12,24,9,13,14,33,3,26,4,5,16,18,29,6],last_nam:[21,0,1,26,12,29,3,4,5,16,17,6],foreignkeyconstraint:29,emul:16,adjust:16,dimens:3,tel:26,diment:[1,13],sync:19,postgr:[16,29,10,6],design:[26,1,5],pass:[8,0,10,1,20,13,33,4,21,16,17,19,6],further:[0,10,1,20,33,4,5],varcharcolumn:[33,0],deleg:20,sub:24,varcharwidget:12,sun:26,section:0,abl:[33,13,1,5,11],delet:[8,1,15,12,13,33,3,4,16,17],version:[10,1,21,16,17,6,7],primary_kei:[16,3],deepli:3,"public":[17,4],simplifi:[3,10],full:29,selection_cb:19,behaviour:[24,13,11],on_image_delet:12,depend:[8,0,10,26,3,4,5,16,19,7],modifi:[0,10,1,24,33,4,5],valu:[0,1,3,4,5,6,8,10,11,12,13,16,17,19,24,22,23,20,25,26,29,33,21],gtkframe:5,search:[0,1,11,22,26,33,27,4,21,16,19,29,6],create_thumbnail:13,doctest:0,pick:[16,12,13,21],action:[0,1,11,12,32,33,4,5],via:[8,0,10,1,11,23,12,13,33,3,26,4,21,5,20,16,22,19,6,7],hidden_boolean:29,default_s:13,stock_id:12,put:[33,3,7,5,26],famili:20,establish:[29,10],memor:19,select:[8,0,10,1,12,33,3,4,21,16,19,29,6],distinct:21,regist:[21,0,1,32,33,5,6],two:[20,1,12,33,3,4,5,16,29],taken:[33,4],beginnig:0,toggl:[33,0,1,6],more:[0,10,1,15,12,31,13,33,26,4,5,20,16,17,18,29,6],flat:8,desir:[20,12,4,13,5],purpous:[12,19],ital:8,text2:25,function_nam:[25,5],desid:[13,5],stick:0,particular:[1,33,13,25,26,6],known:0,set_valu:[0,12,4,1,13],doom:16,record_new_cb:1,none:[8,0,10,1,11,12,13,33,26,21,5,20,17,24,19],first_tab_right:5,hous:5,dev:[1,6],histori:1,remain:4,deb:16,male:12,def:[0,1,11,24,13,33,5,17,22],prompt:6,share:[0,10,1,33,21,6],ludo:6,tabular:29,minimum:16,cours:[16,4,11],newlin:5,first_nam:[21,0,10,1,23,12,13,29,3,4,5,16,17,20,6],divid:[1,5],programmat:[0,1,33,4,28,16],anoth:[0,20,1,11,12,4,5,19],spreadsheet:16,snippet:[1,22,13,29,16,17],csv:33,simpl:[8,0,10,1,11,24,13,29,3,26,4,21,5,16,6],textview:5,referenc:[0,1,12,13,33,4],simplecomplet:4,variant:22,propertload:1,reflect:[16,21,1,5,13],format_valu:13,sistema:26,associ:[33,13],addto:[1,5],circumst:[16,4,1],"short":[0,20,6,13],django:[29,10,4,1,28],mislead:20,callback:[24,33,0,12,1],set_record:1,checkbox:[8,16,6],help:[21,1,20,29,5,16,6,7],second_tab:5,soon:7,ita:[29,10,4],through:[29,0],pane:8,hierarchi:33,implicitli:10,paramet:[8,0,10,1,11,12,13,33,26,4,21,24,19],differer:13,style:[26,10],get_valu:[0,12,4,1,13],popdown:16,combobox:[0,12,13],dbname:[16,6],get_class:21,pend:33,book31:26,book32:26,"_sqlkit_field":[20,6],collectionwidget:[12,13],recip:25,good:[29,15,0,1],"return":[21,0,10,1,11,12,13,25,33,4,5,17],var_nam:26,pkei:[13,21],framework:10,compound:[16,29,5],complain:[17,10,13],bigger:[16,13],django2queri:10,instruct:[16,29,4,7,11],refresh:[19,1],easili:[11,20,13,25,26,4,29,19],achiev:10,compris:[20,1],found:[0,1,11,20,13,25,4,16,6],trailer:5,base_dir:[3,13],harm:20,odt_view:11,new_docu:26,hard:[16,11],idea:[29,10],procedur:1,treeeview:[32,33],heavi:3,expect:[26,0],beyond:6,todo:1,event:[0,1,33,5,16,17],print:[21,1,11,25,32,26,34,18,6],modelproxi:[33,18,19,30,34],get_sess:[10,1],foreground:12,col_width:1,asp:25,proxi:[13,19,12,1,11],advanc:[16,18,30,34],guess:[14,19,13],reason:[26,1,6],base:[8,0,1,13,29,3,4,16,6,7],ask:[14,16,5],recv:16,bash:6,basi:29,capabl:[29,26,12,11],script:[16,29,6],icon_size_dialog:12,perman:1,assign:[26,10],feed:22,cellrenderertoggl:33,feel:24,misc:[26,11],number:[8,0,20,1,11,24,13,33,3,26,5,28,16,29],field_nm:33,done:[8,1,11,24,33,26,4,5,16,17,19,29],least:[16,17,10,29,26],effortless:29,label_map:[20,1,22],stabl:7,miss:[8,0,11,31,13,15,5,17,18],fanci:[8,16,11],director_n:0,differ:[8,0,10,1,22,11,12,13,33,3,26,4,21,5,20,16,24,19,29,7],nome:6,nonnullableexcept:13,interact:[8,0,10,1,11,24,33,5,16],construct:5,order_bi:[10,1,24,33,19,6],accept:[0,10,1,15,23,13,33,26,5,16],datetimetzwidget:12,propertyload:[3,10,1],foreignkeyfield:[17,13,21],store:[10,24,4,5,20,19,6],schema:[29,9,18,6,13],pdf_viewer:11,option:[8,9,20,1,24,13,25,32,33,3,27,4,5,16,18,29,6],relationship:[8,0,1,20,14,33,3,27,4,34,16,17,18,19,29,6],olmi:23,odt:[26,11],part:[8,1,22,14,33,26,4,5,16,29],pars:[8,26,13],filter_panel:[0,1],myclass:13,whenev:4,remot:[16,4,1,11],remov:[0,1],sto:1,booleanwidget:12,local_t:1,reus:[26,1],str:[33,0],arrang:[33,5,11],comput:[16,24,13],img:13,packag:[0,14,15,3,16,29,7],assigned_to_id:10,"null":[10,1,12,13,33,3,16,19,6],imagin:14,built:[21,0,10,1,14,3,4,5,29,18,19],equival:[10,1,5],self:[8,0,10,1,22,11,12,13,33,5,17,24,19],blindli:8,on_completion__director_id:1,also:[8,0,10,1,11,12,13,25,33,3,4,5,16,17,20,29,7],build:[8,10,1,13,29,4,21,5,16],brace:5,distribut:[16,26,20],passwd:29,previou:[0,1,5],reach:[8,33,1,5],"2th":0,most:[8,29,5],plai:[8,33,1,26],maco:16,sandro:[16,15,29,6],new_filenam:12,addr:26,already_exist:4,filesystem:[26,13],clear:[8,26,0,13,5],datetimeedit:12,cover:5,clean:13,sqlkit_tabl:21,film_number__lt:0,carefulli:8,filterpanel:[0,1],session:[10,1,12,32,3,4],particularli:[21,7],fine:[17,0,1],find:[8,1,26,23,13,33,5,16,19,6],impact:17,pretti:[8,15,20,16,1],writer:26,solut:[4,25],clearli:[16,26,19,21],my_id:[10,5],factor:12,hit:[33,4],necesssari:10,express:[0,10,1,26,29,4,28,16],child_set_properti:8,blank:[13,1,6],nativ:[10,28],salvato:1,foreignkeywidget:12,him:0,template_dir:11,boolean_field:0,aramet:1,setup_field_valid:13,book22:26,book21:26,possbl:13,common:[8,0,1,26,24,13,33,4,6],neeed:0,set:[0,1,3,4,5,6,7,8,10,11,12,13,14,17,19,24,22,23,20,25,26,29,33,21],art:26,togheth:12,startup:6,after_flush_postexec:1,sex:[17,5],see:[8,0,20,1,26,24,13,25,33,3,4,21,5,16,19,29,6],bare:21,arg:[10,26,23,12,13,33,4],close:4,hbox:5,dateutil:[16,7],gtkicons:12,relationship_mod:[33,1],someth:[16,19],won:[16,1],idle_add:1,experi:[8,16,6],trace_class:25,"15th":0,altern:[23,0,12,1],signatur:[24,4,1],popup:12,appreci:[33,7],numer:[24,1,12,13,33,4,29,20,6],lwidget:[14,5],ipython:[8,6,25],timetzwidget:12,popul:22,both:[10,1,11,22,13,33,3,26,4,29,17,20,19],last:[0,1,13,29,4,5,16,34,6],context_ready_cb:11,anot:14,alon:[16,33],foreign:[0,10,1,11,23,12,13,14,33,27,4,21,28,16,29,6],context:[10,1,26,11,33,16],pdf:[26,11],func_date_:24,whole:[26,4],pdb:25,load:[8,11,4,1,6],thumbail_s:13,simpli:[16,29,4,1,11],point:[8,0,20,1,11,12,13,33,3,26,4,5,16,17,29],instanti:[21,10,1,23,20,13,25,5],schedul:1,header:[20,32,33,4,16,19],is_mask:1,param:[33,0,13,26],linux:[16,6],eventlabel:5,throughout:29,backend:[0,9,29,4,16,18,7],becom:[16,0,11],user_id:10,eather:[1,21],empti:[8,1,23,13,19,6],sinc:[20,1,26,24,13,25,33,4,16,29,6],not_editable_color:12,one2mani:[29,3,27,1],devis:10,invis:[0,11],referec:12,execfil:6,imag:[8,0,1,12,13,25,26,3,4,16,30],partecipazioni_invitato:6,floatfield:13,understand:[8,1,13,25,33,3,5,16,7],func:[33,0,24],demand:[29,4],look:[8,0,10,21,22,24,13,14,33,3,26,4,5,16,17,20,19,6],oo_tabl:26,durat:23,"while":[8,0,10,1,13,26,5,29,19],abov:[8,0,1,11,20,13,26,4,21,5,16],error:[1,33,22,13,26,27],loos:11,loop:[16,33,26,1,22],pack:[8,5],propag:[33,12,1],readi:11,readm:20,contex:26,itself:[14,6,5,13],rid:19,decor:13,grant:1,belong:[0,1,23,13,33,19],tk_tbl:10,shorter:[26,4],listor:19,grand:24,new_dat:12,field_attribut:13,temporari:1,user:[0,10,1,22,12,26,3,4,5,29,17,20,6],applic:[1,23,25,27,29,19,6],chang:[8,0,10,1,11,12,13,25,32,33,3,4,21,5,16,24,19,29,6],recent:1,dst_path:13,task:16,machineri:[17,1],older:1,timetzfield:13,entri:[8,0,1,11,12,13,32,33,3,26,4,5,16,17,29,6],person:[8,24,11,20,26,28,16],cheer:16,add_constraint:[29,0,10,1],edited_path:33,spend:6,propos:16,explan:[33,1,5],ieri:0,mysql:[16,29,0,10,7],regardless:[4,13],user_id__act:10,scenario:[26,1],shortcut:[8,33,16,6,32],icontain:10,on_complet:17,input:[33,4,13,26],march:0,format:[21,0,1,11,20,13,33,26,4,5,16,30,29,6,7],intuit:16,bit:[16,5],table2:26,table1:[26,11],signal:[8,24,1,11,12,32,33,5,28,29,30,19],elaps:25,collect:[22,12,1,21,13],"boolean":[8,10,1,11,12,13,32,33,26,21,16,24,19],name_lik:1,love:4,often:[16,33,7],templ_:11,creation:[14,19,5],some:[8,10,1,22,13,33,3,26,4,21,5,16,6,7],back:[4,1,6],understood:[4,5],sampl:26,"28th":0,genres__nam:[29,10],imageview:12,remote_output_dir:11,scale:[20,12,13],completion_group_bi:4,per:[16,10,11],prop:[13,5],substitut:[0,11,23,26,4,5],larg:[33,4,15],button_press_cb:33,prog:6,set_imag:12,run:[16,22,19,1,6],numeric_field__gt:0,select_path:[33,19],step:[8,14,6,22],elixir:3,subtract:0,stbar:5,constraint:[0,10,1,12,13,4,34,28,29,18],break_funct:24,drama:[29,10],idl:1,dialog:[16,33,13,1,6],reset_joinpoint:10,relationship_lead:[12,4,1],"__repr__":21,real:[0,1,12,26,3,4,16,6],within:[21,26,25,1,5],phase:1,master:[0,1,11,12,13,4,30,19],span:[29,10,5],textual:8,custom:[8,0,20,1,11,24,13,14,33,26,4,21,5,29,17,6],brasil:6,includ:[16,21,7],forward:[16,1],unregister_layout:21,properli:33,first_field:29,conseid:26,filtertoold:0,objfield:33,line:[10,11,25,33,3,26,4,5,16,6],showcolumn:1,toobaz:16,info:[10,1,26,12,13,33,3,4,5,16,17,20,29],caller:[2,25],myfield:13,my_mask:0,booleanfield:13,enlarg:12,actors__countri:[29,10],parser:13,action_nam:11,repres:[8,0,24,1,22,12,13,14,33,3,4,21,16,20,29],"char":[0,1,13,4,5,17],layout_nick:[33,1],movie__director_id:11,home:6,doe:[8,26,12,13,33,3,4,16,19,29],thvbnpsvhpobmmfa:5,titl:[8,1,11,23,14,33,3,26,4,21,5,29,18],notnul:10,nick:[1,11,33,21,16,18,6],sqlfilter:[0,10],run_hook:13,nice:[8,6],draw:[14,29,5],lucid:16,drag:19,deserv:0,lang:16,lead:[33,0,4,1],vice:[16,1],add_break:24,field_widget:13,column_lookup:4,sqltabl:[8,0,10,1,11,12,13,32,33,3,4,34,20,24,18,19,29,6],printtool:11,obj:[8,0,1,11,24,13,33,21,19],actionsgroup:1,maskview:1,scroll:[8,33],code:[10,11,24,13,25,14,33,26,4,5,16,29,7],partial:[24,19,4,5,11],queri:[0,10,1,15,4,21,28,16,29],headless:26,hide_field:[33,1],tooltip:[20,1,5,6],inistal:16,compact:0,sensit:8,elsewher:1,friendli:[20,5],ignore_field:1,statusbar:5,autostart:[4,6],sens:[0,11],sent:21,unzip:16,gtk2:16,validation_warn:1,mous:8,m2mcomplet:4,radiobutton:5,stackoverflow:11,untouch:13,validationwarn:22,relev:[3,4,1],tri:[0,11],notabl:[15,13],button:[8,0,20,1,12,33,5,16,17,6],"try":[8,0,1,20,13,14,33,5,16,19,29],notregexp:10,radio:12,marri:13,pleas:[8,16,7,5],impli:[26,4,13,5],"__metaclass__":25,natur:[29,12,4],contructor:12,on_after_flush_postexec:1,uniqu:5,jump:[16,6],mysqldb:[16,7],download:[16,18,31,7],fullnam:4,click:[8,0,1,12,33,4,5,16,17,29],append:5,cellrend:33,compat:[12,0,10,1],index:[6,7],compar:17,resembl:26,cell:[24,1,26,12,32,33,5,16,19],can:[0,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,19,24,22,23,20,25,26,29,33,21],varcharfield:[33,13],len:[0,13],let:[1,13,29,3,4,16,6],ubuntu:[16,31,7],vertic:33,implicit:21,integerwidget:12,great:7,set_value_and_color:24,repet:26,genr:[3,1,22],host:[16,29,6],thumbnail_s:[3,13],comboboxentri:[8,12],typic:[29,1],chanc:[1,21,6],update_widget:13,fake:11,employe:21,see_below:1,configur:[8,9,1,30,13,25,33,4,21,5,16,34,18,6],revok:1,appli:[8,0,10,4,1],wil:26,apt:16,api:[32,26,24,4,11],clean_valu:[33,0,13],fed:6,from:[0,1,3,4,5,6,7,8,10,11,12,13,14,15,16,17,19,23,24,25,26,29,33,21],zip:[16,7],sqlwidget:[8,0,10,1,22,11,23,24,13,32,33,4,21,34,20,17,18,19,30],fel:6,doubl:[16,29,10,4,5],next:0,few:4,quer:1,enumwidget:12,panel:[16,33,0,29,1],manytomani:[13,21],sort:[0,10,13,32,33,30,19],get_tool:0,furtherli:8,sqlkit:[0,1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,23,24,25,26,29,31,32,33,34],validationerror:[1,22],on_selection_chang:1,impress:29,about:[16,29,1],trail:[0,5],"transient":1,order_stor:19,retriev:[1,11,13,33,4,16],tableview:16,alia:10,annoi:13,exc:1,control:[8,1,33,4,21,16,29],sqlite:[0,1,29,16,6,7],tar:7,def_visita:23,sudo:16,o2m:[8,1,22,12,13,33,3,29,17],tab:[16,0,5],serial:1,field_list:[0,10,1,33,3,19,6],everywher:29,ander:16,sig:5,subdirectori:6,instead:[29,3,20,4,6],stock:[14,12,5],delai:1,overridden:11,othewis:24,container_id:5,nullify:8,class_:[1,24,33,21,16,19],diector_id:4,notebook:[8,14,29,5],gtkwidget:[20,12,13,5],add_row_filt:33,actiongroup_insert:1,alloc:26,oo_pattern:26,essenti:33,gtkeventbox:5,bind:[12,4,1],counter:5,correspond:[8,1,13,25,33,5],element:[1,11,20,14,5,16],issu:[0,10,1,12,13,33,4,5,16],on_change_valu:1,allow:[8,0,10,1,11,23,13,33,3,26,4,5,16,19,29,6],fallback:1,intereg:13,actiongroup_gener:1,"02a":19,movi:[8,0,10,1,22,11,23,12,13,14,33,3,4,21,16,24,29,6],comma:[33,1,6],records_selected_cb:1,verfailli:16,outer:[10,5],chosen:[1,5],mapper_or_class:10,clickabl:[16,33],python:[26,13,25,14,29,21,16,6,7],auto:[4,1],diwwmmyi:0,handi:[16,23,13],federico:4,orederd:13,date_field__lt:0,get_par:8,oo_context:26,user__usernam:1,anyth:[29,15,5],edit:[8,9,20,1,11,12,13,32,33,3,27,4,21,34,16,18,19,29,6],bind_param:1,emb:29,autoflush:1,februari:0,scrollbar:33,mode:[8,1,11,25,33,26,4,29,6],disregard:16,actiongroup_print:1,view_nam:33,active__eq:1,set_widget:13,oasi:26,chunk:33,sessionmak:1,meta:[10,1],our:[16,9,21],patch:15,special:[0,26,13,29,5,16,6],out:[29,13,5],variabl:[11,13,25,26,5,16,19],influenc:1,categori:1,m2m:[8,20,1,22,12,13,33,3,27,4,21],m2o:[3,27,4,1,21],geom:1,suitabl:[26,12,13,29,4,16],user_t:10,wich:13,booleannullwidget:12,ref:[26,21],cwd:11,math:0,clarifi:4,mylayout:8,insid:[13,5],is_tabl:1,manipul:[33,1,11],undo:1,add_not_null_error:[1,22],book1:26,book2:26,dictionari:[1,5,23,20,13,21],tempt:14,releas:[1,29,34,16,17,7],has_chang:13,get_human_valu:[13,11],could:[26,21],keep:[0,1,12,13,16,17],datetimefield:13,length:[33,0,13],enforc:[10,1,12,13,26,5],lte:10,retain:[33,4],treeselect:33,timezon:[12,13],on_completion__genr:1,gtkscrolledwindow:5,installig:16,softwar:16,eventbox:5,expire_on_commit:[32,1],powerful:[33,11],scene:[3,27],echo:16,date:[8,0,24,1,26,12,13,33,3,28,16,20,29],"_implement_lazy_t":26,prioriti:10,"long":[33,19,1,26],unknown:21,system:[16,15,4,6,26],messag:[28,20,1,25,22],attach:[29,33,13],scale_fil:13,boil:33,shell:[8,26,6],my_valu:4,on_activate__field_nam:1,biggest:14,standalon:[29,9,18,4,6],anybodi:[16,6,13],add_menu_entri:11,get_histori:[1,21],roberto:4,structur:[16,5],enought:[26,13],syncron:19,viewer:[30,12,11],field_valu:[22,24,1,13],cell_bool_cb:25,have:[0,1,3,4,5,6,7,8,10,11,12,13,14,16,17,19,24,22,23,20,26,29,33],tabl:[0,1,3,4,5,6,8,9,10,11,12,13,14,16,17,18,19,20,22,23,24,26,29,30,32,33,21],need:[0,1,3,4,5,6,7,8,10,11,12,13,14,15,16,17,19,24,22,20,25,26,29,30,33,21],automat:[21,1,11,14,4,5,29,17],validationdialog:22,babel:[16,20,7],on_after_flush:1,unawar:12,set_group:12,which:[8,0,10,1,11,24,13,33,26,4,21,5,16,20,19,29,6],divert:[8,25],fk_edit:8,scrolledtextview:5,unless:[8,0,1,11,13,25,33,21,5],integerfield:[0,13],clear_mask:8,lookup_valu:[13,21,11],who:[16,25],"2giugno":6,new_column:33,genres__name__regexp:1,why:21,xalign:5,url:[11,20,29,21,16,6],get_save_path:13,request:[29,4,13],face:5,inde:12,determin:[8,21,24,13,4,5,6],occasion:[8,26,0,5],constrain:[29,26,10],fact:[1,13,4,5,16,17],text:[8,1,15,12,25,32,33,4,5,16,29,6],verbos:13,dbg:[25,5],longer:[1,14,4,5,17,6],trivial:16,staff:[16,3,20],launchpad:[16,20,7],should:[8,0,24,1,11,12,13,33,26,4,21,5,16,20,19,6,7],multi_line_mark:26,suppos:[8,10,1,24,3,4,5,16,17,20,6,7],combo:[16,13],"__tablename__":[16,3,13],add_filt:[0,10,1],local:[1,11,23,20,31,13,26,33,27,5,28,16,34,18,19,30,7],hope:16,subdir:[13,11],meant:[16,13,1,11],contribut:[15,18,20,31],familiar:16,nation__cod:4,accel:11,tbl:8,enabl:[33,0,25,26],actiongroup_select:1,add_tot:24,integr:15,gran:24,contain:[1,11,13,14,33,3,26,4,5,19],menuitem:[1,5],hei:22,view:[0,1,11,12,32,33,34,16,17,18,19,29,30],frame:[8,29,5],knowledg:29,orphan:[1,15,13,33,3,16,17],qty:26,add_temporary_item:1,alessandro:[26,7],temporarili:20,gtkobj:5,cognom:6,impos:5,wire:10,modelstor:[33,19],correctli:[23,1],pattern:[26,20],treemodel:19,setup_column:19,written:[0,1,20,29,4,5,16],progress:5,thumbnail:[12,13],image_path:[12,13],kei:[8,0,10,1,26,11,23,12,28,13,14,33,27,4,21,5,20,16,24,29,6],on_record_sav:1,job:3,entir:19,gte:10,joke:16,director_id:[8,0,1,23,24,13,14,33,3,4,29],equal:[10,5],etc:16,instanc:[21,1,11,23,24,13,33,26,5,17,19],comment:[14,5],hidecolumn:1,arriv:33,solv:33,respect:26,gtkobject:5,quit:[8,33,1],compos:[16,4,1,13],compon:[21,5],datewidget:12,besid:[10,1],immedi:1,on_activ:1,on_change_value__field_nam:1,"__table__":[0,10,21],togeth:3,set_mast:13,present:[8,0,10,1,11,22,13,33,3,26,4,21,16,29,6],align:[8,17,13,5],contextu:1,defin:[8,0,24,1,11,23,12,13,14,33,3,26,4,5,16,6],previuo:33,observ:13,layer:[16,3,20,1,13],dieter:16,helper:[2,18],almost:1,demo:[8,0,1,22,9,13,29,3,26,4,21,5,16,17,6,7],site:[16,10,13,7,21],dateedit:[8,30,12],epti:13,add_complet:12,gettrasfer:26,logthemethod:[6,25],handl:[8,0,1,11,23,13,33,16,19],infer:1,difficult:[16,5],http:[16,26,7],upon:4,effect:[1,5],dai:[0,24],php:26,director_id__birth_date__gt:1,expand:[8,3,5],weher:19,"1month":0,off:[2,6,25],explicetli:16,nevertheless:[16,10,25],builder:[14,5],actiongroup_delet:1,well:[21,0,10,1,15,12,13,14,33,3,5,16,20,6],thought:[1,13],exampl:[8,0,10,1,11,24,13,14,33,3,26,4,5,16,20,19,29,6,7],command:[16,25,1,7,6],choos:[8,26,4,16,29,6],undefin:16,sqlkit_model:21,backref:[16,3,13],usual:[16,4,5],less:10,obtain:[26,10,1,5],connet:11,skill:[16,26,6],simultan:16,adv:16,web:16,nullabl:[1,12,13,33,3,16],get_descript:21,server_sid:13,director2:0,bool:[8,33,29,1],iregexp:10,match:[0,1,11,26,4,29],piec:[16,26,13],realiz:[16,3],know:[8,1,22,23,13,14,3,4,5,16],set_default:[23,27],press:[16,33,4,1,17],height:[12,1,5,13],python2:11,insert:[26,24,19,1],on_valid:[1,22],like:[8,10,1,20,13,29,3,4,21,5,28,16],incred:8,necessari:[17,10,4,1,21],resiz:[1,13],dictlik:[33,0],page:[33,0,5],italian:[29,0,10,4],didn:29,"export":[32,33,1],flush:1,proper:[33,0,12,1,6],guarante:1,librari:[16,20,25,7],tmp:26,selected_row:33,avoid:[23,4],variable_pattern:26,"__getattr__":11,esc:33,nake:1,encourag:[16,1],"enum":[16,12,4,1,13],usag:[29,6],reguardless:[1,6],on_after_flush_callback:1,monetari:11,simpler:[14,3,20,13],costructor:13,negat:10,tm2:3,quarter:24,world:4,column:[0,10,1,12,13,32,33,3,4,5,20,16,17,24,19,29],glade:[8,14,29,5],create_view:33,statement:[16,4,1,6],constructor:[21,5],yesterdai:0,discard:[33,5],on_pre_layout:[1,13],enumfield:13,sistem:14,own:[1,13],easy_instal:[16,7],gui_field_map:[1,13],due:[16,17,1,5],pointless:1,behind:[3,27],datetool:0,pictur:29,from_focus_out:12,much:[0,10,23,12,4,16],"var":5,not_null_color:12,vat:1,"function":[8,0,10,1,11,12,13,25,14,33,26,4,21,5,24,19],relationproperti:21,record_in_mask:33,gain:[8,14,12],buf:5,bug:7,count:[0,13],made:[8,1,11,29,4,16],pixbuf_ful:12,set_dat:12,record:[8,0,1,11,23,12,13,32,33,4,21,5,16,19,29,6],below:[1,26,20,13,33,4,5,16,29],limit:[0,10,1,20,9,33,4,16,18,29,6],otherwis:[8,29,4,5,11],problem:[8,7,11],block:5,evalu:24,"int":1,descript:[8,0,30,1,11,20,13,14,33,3,27,4,21,5,29,18,19,6],filenam:[26,12,13,5],implement:[21,0,1,13,14,26,5,19],pip:[16,7],probabl:[8,10,11,24,14,33,26,4,5,16,29,6],detail:[1,26,12,13,29,5,16,7],new_valu:[21,1,25],other:[8,0,1,26,12,13,14,33,4,21,5,16,17,30,19,29,6],lookup:[33,10,4,13],futur:[10,1,22,13,29,30],rememb:[16,33,4,11],xscale:5,sqlwiget:1,on_init:[33,1],repeat:[33,7,22],star:26,cell_default_cb:25,"class":[0,1,3,4,5,6,8,10,11,12,13,14,16,17,19,24,22,23,20,25,26,29,30,33,21],june:[26,0],get_widget:8,debian:[16,31,7],stai:1,validationerrordialog:22,rule:[8,16,13],openoffic:[26,18,34,11],cookbook:25},titles:["Filters","Sqlwidget","Debug helpers","Relationships","Completion","A GUI description language - purpose","Sqledit - the standalone program to browse and edit data","Download, requirements & googlegroup","SqlMask","Mailing list","Constraints","Printing","Field Widgets","Fields","Layout GUI built on the fly - A GUI description language","What’s Missing and how to contribute","Sqledit Tutorial","Backward Incompatibilities","<no title>","ModelProxy","Localization","Utilities","Validation","Defaults","Totals","debug module","Printing","Editing data","Browsing data","Sqlkit & Sqledit","Advanced configuration","Download & more...","Widgets","SqlTable","SQLKit"],modules:{"sqlkit.fields":13,"sqlkit.layout.image_widget":12,"sqlkit.widgets.table.columns":19,"sqlkit.misc.conf":6,"sqlkit.widgets.table.totals":24,"sqlkit.db.utils":21,"sqlkit.widgets.mask.miniwidgets":12,"sqlkit.widgets.common.completion":4,"sqlkit.misc.printing":11,"sqlkit.db.django_syntax":10,"sqlkit.widgets.common.sqlfilter":0,"sqlkit.misc.oootemplate":26,"sqlkit.layout.dateedit":12,"sqlkit.misc.datetools":0,"sqlkit.widgets.table.modelproxy":19},descrefs:{"":{completions:[1,1],related:[1,1],my_values:[4,2],session:[1,1],gui_fields:[1,1],query:[1,1],field_list:[1,1],ui_manager:[1,1],get_differences:[1,2],layout:[1,1],title:[1,1],current:[1,1],lay_obj:[1,1],button_press_cb:[33,0],filter_panel:[1,1],pre_display_cb:[8,0],on_init:[1,2],after_flush_callback:[1,2],defaults:[1,1],gui_field_mapping:[1,1],on_after_flush_callback:[1,2],order_by:[1,1],relationship_leader:[1,1],noup:[1,1],on_completion__field_name:[1,2],get_history:[1,2],delete_event_callback:[1,2],mode:[1,1],on_validation:[1,2],on_activate__field_name:[1,2]},"sqlkit.fields":{DateField:[13,3],DateTimeField:[13,3],IntegerField:[13,3],BooleanNullField:[13,3],DateTimeTZField:[13,3],ForeignKeyField:[13,3],TimeTZField:[13,3],IntervalField:[13,3],VarcharField:[13,3],CollectionField:[13,3],FloatField:[13,3],Field:[13,3],BooleanField:[13,3],std_cleanup:[13,2],TimeField:[13,3],TextField:[13,3],DecimalField:[13,3],EnumField:[13,3],ImageField:[13,3]},"sqlkit.layout.image_widget.ImageWidget":{set_pixbuf:[12,0],scale_pixbuf:[12,0],set_stock:[12,0],show_image:[12,0],sb:[12,0],set_image:[12,0]},"sqlkit.widgets.table.totals":{TotalObj:[24,3],Totals:[24,3]},"sqlkit.db.utils":{get_differences:[21,2],get_class:[21,2],get_hook:[21,2],get_layout:[21,2],TableDescr:[21,3],register_hook:[21,2],unregister_hook:[21,2],register_class:[21,2],unregister_layout:[21,2],register_layout:[21,2]},"sqlkit.fields.ImageField":{get_value:[13,0],thumbnail_size:[13,1],create_thumbnail:[13,0],get_save_path:[13,0],scale_pixbuf:[13,0],default_size:[13,1],clean_value:[13,0],get_thumbnail:[13,0],scale_file:[13,0],base_dir:[13,1],get_thumbnail_path_with_size:[13,0],set_value:[13,0]},"sqlkit.widgets.common.completion":{SimpleCompletion:[4,3],M2mCompletion:[4,3],FkeyCompletion:[4,3]},"sqlkit.widgets.mask.miniwidgets.Widget":{add_completion:[12,0],set_editable:[12,0],get_value:[12,0],"__init__":[12,0],set_value:[12,0]},"sqlkit.layout.dateedit":{DateEdit:[12,3],DateTimeEdit:[12,3]},"sqlkit.widgets.SqlTable":{get_selected_path:[33,0],add_row_filter:[33,0],views:[33,1],record_in_mask:[33,0],select_path:[33,0],hide_fields:[33,0],create_view:[33,0],add_record:[33,0],get_obj_at_path:[33,0],fkey_record_in_mask:[33,0],set_field_list:[33,0],set_opts:[33,0],totals:[33,1],edited_path:[33,1],set_fk_layout:[33,0],set_editable:[33,0]},"sqlkit.misc.oootemplate.Table":{"__init__":[26,0]},"sqlkit.layout.image_widget":{ImageWidget:[12,3]},"sqlkit.fields.Field":{has_changed:[13,0],set_widget:[13,0],clear_value:[13,0],get_value:[13,0],get_default:[13,0],clean_value:[13,0],format_value:[13,0],validate:[13,0],set_value:[13,0],get_human_value:[13,0]},"sqlkit.widgets.table.columns.View":{add_column:[19,0],select_path:[19,0],start_editing:[19,0]},"sqlkit.fields.ForeignKeyField":{lookup_value:[13,0],add_related_object:[13,0]},"sqlkit.misc.oootemplate.TableWithStyles":{"__init__":[26,0]},"sqlkit.misc.printing":{ObjProxy:[11,3],PrintTool:[11,3]},"sqlkit.db.django_syntax":{django2sqlalchemy:[10,2],django2query:[10,2]},"sqlkit.misc.oootemplate.Template":{set_pattern:[26,0],save_as:[26,0],search:[26,1],render:[26,0],VARIABLE_PATTERN:[26,1],MULTI_LINE_MARKER:[26,1],VARIABLE_PATTERN_OO:[26,1],preserve_styles:[26,1],document:[26,1],oo_context:[26,1],"__init__":[26,0]},"sqlkit.widgets.table.modelproxy.ModelProxy":{copy:[19,0],order_by:[19,0],tree_field_name:[19,1],"__init__":[19,0]},"sqlkit.widgets.table.modelproxy":{modelstore:[19,1],ModelProxy:[19,3]},"sqlkit.widgets.common.sqlfilter.FilterTool":{destroy:[0,0],set_operator:[0,0],get_operator:[0,0],get_value:[0,0],set_value:[0,0]},"sqlkit.misc.oootemplate.Context":{"__init__":[26,0]},"sqlkit.widgets.table.columns":{View:[19,3]},"sqlkit.db.utils.TableDescr":{search:[21,1],attrs:[21,1],"__init__":[21,0],format:[21,1]},"sqlkit.fields.DateField":{format:[13,1]},"sqlkit.widgets.common.sqlwidget":{SqlWidget:[1,3]},"sqlkit.widgets.mask.miniwidgets":{CollectionWidget:[12,3],Widget:[12,3],DateTimeWidget:[12,3],IntervalWidget:[12,3],VarcharWidget:[12,3],DecimalWidget:[12,3],FloatWidget:[12,3],BooleanWidget:[12,3],TextWidget:[12,3],TimeWidget:[12,3],DateWidget:[12,3],ImageWidget:[12,3],EnumWidget:[12,3],DateTimeTZWidget:[12,3],TimeTZWidget:[12,3],BooleanNullWidget:[12,3],IntegerWidget:[12,3],ForeignKeyWidget:[12,3]},"sqlkit.widgets.SqlMask":{clear_mask:[8,0],get_widget:[8,0],set_frame_label:[8,0]},"sqlkit.widgets.table.totals.Totals":{compute:[24,0],subtotals:[24,1],add_break:[24,0],sum:[24,0],add_total:[24,0],add_date_break:[24,0],totals:[24,1]},"sqlkit.layout.dateedit.DateEdit":{set_date:[12,0]},"sqlkit.widgets.common.sqlfilter":{FilterPanel:[0,3],FilterTool:[0,3]},"sqlkit.fields.IntegerField":{format:[13,1]},"sqlkit.fields.FloatField":{format:[13,1]},"sqlkit.fields.EnumField":{keys:[13,1],lookup_value:[13,0],values:[13,1]},"sqlkit.fields.DecimalField":{format:[13,1]},"sqlkit.fields.VarcharField":{blank_ok:[13,1]},"sqlkit.widgets":{SqlTable:[33,3],SqlMask:[8,3]},"sqlkit.misc.printing.PrintTool":{pdf_viewer:[11,1],add_to_table:[11,0],template_dir:[11,1],add_menu_entry:[11,0],remote_output_dir:[11,1],prepare_context:[11,0],obj_proxy_class:[11,1],odt_viewer:[11,1],server:[11,1],port:[11,1],"__init__":[11,0],output_dir:[11,1]},"sqlkit.widgets.table.totals.TotalObj":{set_value_and_colors:[24,0]},"sqlkit.widgets.common.sqlwidget.SqlWidget":{get_value:[1,0],set_format:[1,0],set_mode:[1,0],add_temporary_item:[1,0],set_records:[1,0],is_table:[1,0],get_current_obj:[1,0],add_not_null_error:[1,0],reload:[1,0],resize:[1,0],is_mask:[1,0],add_validation_error:[1,0],sb:[1,0],add_filter:[1,0],commit:[1,0],set_value:[1,0],"__init__":[1,0],add_constraint:[1,0]},"sqlkit.widgets.common.completion.SimpleCompletion":{force_enum:[4,1],filter:[4,0],group_by:[4,1],attrs:[4,1],autostart:[4,1],query:[4,1],set_values:[4,0],filtered_query:[4,1]},"sqlkit.widgets.common.sqlfilter.FilterPanel":{get_tools:[0,0],hide:[0,0],show:[0,0],clear:[0,0],tree:[0,1],reload:[0,0],set_page:[0,0],replace_column:[0,0],add_column:[0,0]},"sqlkit.misc.oootemplate":{Context:[26,3],Table:[26,3],TableWithStyles:[26,3],Template:[26,3]}},filenames:["sqlkit/filters","sqlkit/sqlwidget","debug/contents","sqlkit/relationship","sqlkit/completion","layout/layout","misc/sqledit","misc/download","sqlkit/mask","misc/tutorials","sqlkit/constraints","sqlkit/printing","sqlkit/advanced/field_widgets","sqlkit/advanced/fields","layout/contents","misc/missing","misc/tutorial","misc/backward_incompatibilities","hidden","sqlkit/advanced/views","sqlkit/localization","sqlkit/advanced/dbutils","sqlkit/validation","sqlkit/defaults","sqlkit/totals","debug/debug","printing/contents","sqlkit/editing","sqlkit/browsing","misc/tour","sqlkit/advanced/contents","misc/contents","sqlkit/widgets","sqlkit/table","sqlkit/contents"]})sqlkit-0.9.5/doc/html/genindex.html0000644000175000017500000015027611714210376016607 0ustar sandrosandro — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Index

_ | A | B | C | D | E | F | G | H | I | K | L | M | N | O | P | Q | R | S | T | U | V | W

_

__init__() (sqlkit.db.utils.TableDescr method)
(sqlkit.misc.oootemplate.Context method)
(sqlkit.misc.oootemplate.Table method)
(sqlkit.misc.oootemplate.TableWithStyles method)
(sqlkit.misc.oootemplate.Template method)
(sqlkit.misc.printing.PrintTool method)
(sqlkit.widgets.common.sqlwidget.SqlWidget method)
(sqlkit.widgets.mask.miniwidgets.Widget method)
(sqlkit.widgets.table.modelproxy.ModelProxy method)

A

add_break() (sqlkit.widgets.table.totals.Totals method)
add_column() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
(sqlkit.widgets.table.columns.View method)
add_completion() (sqlkit.widgets.mask.miniwidgets.Widget method)
add_constraint() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
add_date_break() (sqlkit.widgets.table.totals.Totals method)
add_filter() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
add_menu_entry() (sqlkit.misc.printing.PrintTool method)
add_not_null_error() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
add_record() (sqlkit.widgets.SqlTable method)
add_related_object() (sqlkit.fields.ForeignKeyField method)
add_row_filter() (sqlkit.widgets.SqlTable method)
add_temporary_item() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
add_to_table() (sqlkit.misc.printing.PrintTool method)
add_total() (sqlkit.widgets.table.totals.Totals method)
add_validation_error() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
after_flush_callback() (built-in function)
attrs (sqlkit.db.utils.TableDescr attribute)
(sqlkit.widgets.common.completion.SimpleCompletion attribute)
autostart (sqlkit.widgets.common.completion.SimpleCompletion attribute)

B

base_dir (sqlkit.fields.ImageField attribute)
blank_ok (sqlkit.fields.VarcharField attribute)
BooleanField (class in sqlkit.fields)
BooleanNullField (class in sqlkit.fields)
BooleanNullWidget (class in sqlkit.widgets.mask.miniwidgets)
BooleanWidget (class in sqlkit.widgets.mask.miniwidgets)
button_press_cb()

C

clean_value() (sqlkit.fields.Field method)
(sqlkit.fields.ImageField method)
clear() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
clear_mask() (sqlkit.widgets.SqlMask method)
clear_value() (sqlkit.fields.Field method)
CollectionField (class in sqlkit.fields)
CollectionWidget (class in sqlkit.widgets.mask.miniwidgets)
commit() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
completions
compute() (sqlkit.widgets.table.totals.Totals method)
Context (class in sqlkit.misc.oootemplate)
copy() (sqlkit.widgets.table.modelproxy.ModelProxy method)
create_thumbnail() (sqlkit.fields.ImageField method)
create_view() (sqlkit.widgets.SqlTable method)
current

D

DateEdit (class in sqlkit.layout.dateedit)
DateField (class in sqlkit.fields)
DateTimeEdit (class in sqlkit.layout.dateedit)
DateTimeField (class in sqlkit.fields)
DateTimeTZField (class in sqlkit.fields)
DateTimeTZWidget (class in sqlkit.widgets.mask.miniwidgets)
DateTimeWidget (class in sqlkit.widgets.mask.miniwidgets)
DateWidget (class in sqlkit.widgets.mask.miniwidgets)
DecimalField (class in sqlkit.fields)
DecimalWidget (class in sqlkit.widgets.mask.miniwidgets)
default_size (sqlkit.fields.ImageField attribute)
defaults
delete_event_callback() (built-in function)
destroy() (sqlkit.widgets.common.sqlfilter.FilterTool method)
django2query() (in module sqlkit.db.django_syntax)
django2sqlalchemy() (in module sqlkit.db.django_syntax)
document (sqlkit.misc.oootemplate.Template attribute)

E

edited_path (sqlkit.widgets.SqlTable attribute)
EnumField (class in sqlkit.fields)
EnumWidget (class in sqlkit.widgets.mask.miniwidgets)

F

Field (class in sqlkit.fields)
field_list
filter() (sqlkit.widgets.common.completion.SimpleCompletion method)
filter_panel
filtered_query (sqlkit.widgets.common.completion.SimpleCompletion attribute)
FilterPanel (class in sqlkit.widgets.common.sqlfilter)
FilterTool (class in sqlkit.widgets.common.sqlfilter)
fkey_record_in_mask() (sqlkit.widgets.SqlTable method)
FkeyCompletion (class in sqlkit.widgets.common.completion)
FloatField (class in sqlkit.fields)
FloatWidget (class in sqlkit.widgets.mask.miniwidgets)
force_enum (sqlkit.widgets.common.completion.SimpleCompletion attribute)
ForeignKeyField (class in sqlkit.fields)
ForeignKeyWidget (class in sqlkit.widgets.mask.miniwidgets)
format (sqlkit.db.utils.TableDescr attribute)
(sqlkit.fields.DateField attribute)
(sqlkit.fields.DecimalField attribute)
(sqlkit.fields.FloatField attribute)
(sqlkit.fields.IntegerField attribute)
format_value() (sqlkit.fields.Field method)

G

get_class() (in module sqlkit.db.utils)
get_current_obj() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
get_default() (sqlkit.fields.Field method)
get_differences() (built-in function)
(in module sqlkit.db.utils)
get_history() (built-in function)
get_hook() (in module sqlkit.db.utils)
get_human_value() (sqlkit.fields.Field method)
get_layout() (in module sqlkit.db.utils)
get_obj_at_path() (sqlkit.widgets.SqlTable method)
get_operator() (sqlkit.widgets.common.sqlfilter.FilterTool method)
get_save_path() (sqlkit.fields.ImageField method)
get_selected_path() (sqlkit.widgets.SqlTable method)
get_thumbnail() (sqlkit.fields.ImageField method)
get_thumbnail_path_with_size() (sqlkit.fields.ImageField method)
get_tools() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
get_value() (sqlkit.fields.Field method)
(sqlkit.fields.ImageField method)
(sqlkit.widgets.common.sqlfilter.FilterTool method)
(sqlkit.widgets.common.sqlwidget.SqlWidget method)
(sqlkit.widgets.mask.miniwidgets.Widget method)
get_widget() (sqlkit.widgets.SqlMask method)
group_by (sqlkit.widgets.common.completion.SimpleCompletion attribute)
gui_field_mapping
gui_fields

H

has_changed() (sqlkit.fields.Field method)
hide() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
hide_fields() (sqlkit.widgets.SqlTable method)

I

ImageField (class in sqlkit.fields)
ImageWidget (class in sqlkit.layout.image_widget)
(class in sqlkit.widgets.mask.miniwidgets)
IntegerField (class in sqlkit.fields)
IntegerWidget (class in sqlkit.widgets.mask.miniwidgets)
IntervalField (class in sqlkit.fields)
IntervalWidget (class in sqlkit.widgets.mask.miniwidgets)
is_mask() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
is_table() (sqlkit.widgets.common.sqlwidget.SqlWidget method)

K

keys (sqlkit.fields.EnumField attribute)

L

lay_obj
layout
lookup_value() (sqlkit.fields.EnumField method)
(sqlkit.fields.ForeignKeyField method)

M

M2mCompletion (class in sqlkit.widgets.common.completion)
mode
ModelProxy (class in sqlkit.widgets.table.modelproxy)
modelstore (in module sqlkit.widgets.table.modelproxy)
MULTI_LINE_MARKER (sqlkit.misc.oootemplate.Template attribute)
my_values() (built-in function)

N

noup

O

obj_proxy_class (sqlkit.misc.printing.PrintTool attribute)
ObjProxy (class in sqlkit.misc.printing)
odt_viewer (sqlkit.misc.printing.PrintTool attribute)
on_activate__field_name() (built-in function)
on_after_flush_callback() (built-in function)
on_completion__field_name() (built-in function)
on_init() (built-in function) , [14]
on_validation() (built-in function) , [14]
oo_context (sqlkit.misc.oootemplate.Template attribute)
order_by
order_by() (sqlkit.widgets.table.modelproxy.ModelProxy method)
output_dir (sqlkit.misc.printing.PrintTool attribute)

P

pdf_viewer (sqlkit.misc.printing.PrintTool attribute)
port (sqlkit.misc.printing.PrintTool attribute)
pre_display_cb()
prepare_context() (sqlkit.misc.printing.PrintTool method)
preserve_styles (sqlkit.misc.oootemplate.Template attribute)
PrintTool (class in sqlkit.misc.printing)

Q

query
(sqlkit.widgets.common.completion.SimpleCompletion attribute)

R

record_in_mask() (sqlkit.widgets.SqlTable method)
register_class() (in module sqlkit.db.utils)
register_hook() (in module sqlkit.db.utils)
register_layout() (in module sqlkit.db.utils)
related
relationship_leader
reload() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
(sqlkit.widgets.common.sqlwidget.SqlWidget method)
remote_output_dir (sqlkit.misc.printing.PrintTool attribute)
render() (sqlkit.misc.oootemplate.Template method)
replace_column() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
resize() (sqlkit.widgets.common.sqlwidget.SqlWidget method)

S

save_as() (sqlkit.misc.oootemplate.Template method)
sb() (sqlkit.layout.image_widget.ImageWidget method)
(sqlkit.widgets.common.sqlwidget.SqlWidget method)
scale_file() (sqlkit.fields.ImageField method)
scale_pixbuf() (sqlkit.fields.ImageField method)
(sqlkit.layout.image_widget.ImageWidget method)
search (sqlkit.db.utils.TableDescr attribute)
(sqlkit.misc.oootemplate.Template attribute)
select_path() (sqlkit.widgets.SqlTable method)
(sqlkit.widgets.table.columns.View method)
server (sqlkit.misc.printing.PrintTool attribute)
session
set_date() (sqlkit.layout.dateedit.DateEdit method)
set_editable() (sqlkit.widgets.mask.miniwidgets.Widget method)
(sqlkit.widgets.SqlTable method)
set_field_list() (sqlkit.widgets.SqlTable method)
set_fk_layout() (sqlkit.widgets.SqlTable method)
set_format() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
set_frame_label() (sqlkit.widgets.SqlMask method)
set_image() (sqlkit.layout.image_widget.ImageWidget method)
set_mode() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
set_operator() (sqlkit.widgets.common.sqlfilter.FilterTool method)
set_opts() (sqlkit.widgets.SqlTable method)
set_page() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
set_pattern() (sqlkit.misc.oootemplate.Template method)
set_pixbuf() (sqlkit.layout.image_widget.ImageWidget method)
set_records() (sqlkit.widgets.common.sqlwidget.SqlWidget method)
set_stock() (sqlkit.layout.image_widget.ImageWidget method)
set_value() (sqlkit.fields.Field method)
(sqlkit.fields.ImageField method)
(sqlkit.widgets.common.sqlfilter.FilterTool method)
(sqlkit.widgets.common.sqlwidget.SqlWidget method)
(sqlkit.widgets.mask.miniwidgets.Widget method)
set_value_and_colors() (sqlkit.widgets.table.totals.TotalObj method)
set_values() (sqlkit.widgets.common.completion.SimpleCompletion method)
set_widget() (sqlkit.fields.Field method)
show() (sqlkit.widgets.common.sqlfilter.FilterPanel method)
show_image() (sqlkit.layout.image_widget.ImageWidget method)
SimpleCompletion (class in sqlkit.widgets.common.completion)
sqlkit.db.django_syntax (module)
sqlkit.db.utils (module)
sqlkit.fields (module)
sqlkit.layout.dateedit (module)
sqlkit.layout.image_widget (module)
sqlkit.misc.conf (module)
sqlkit.misc.datetools (module)
sqlkit.misc.oootemplate (module)
sqlkit.misc.printing (module)
sqlkit.widgets.common.completion (module)
sqlkit.widgets.common.sqlfilter (module)
sqlkit.widgets.mask.miniwidgets (module)
sqlkit.widgets.table.columns (module)
sqlkit.widgets.table.modelproxy (module)
sqlkit.widgets.table.totals (module)
SqlMask (class in sqlkit.widgets)
SqlTable (class in sqlkit.widgets)
SqlWidget (class in sqlkit.widgets.common.sqlwidget)
start_editing() (sqlkit.widgets.table.columns.View method)
std_cleanup() (in module sqlkit.fields)
subtotals (sqlkit.widgets.table.totals.Totals attribute)
sum() (sqlkit.widgets.table.totals.Totals method)

T

Table (class in sqlkit.misc.oootemplate)
TableDescr (class in sqlkit.db.utils)
TableWithStyles (class in sqlkit.misc.oootemplate)
Template (class in sqlkit.misc.oootemplate)
template_dir (sqlkit.misc.printing.PrintTool attribute)
TextField (class in sqlkit.fields)
TextWidget (class in sqlkit.widgets.mask.miniwidgets)
thumbnail_size (sqlkit.fields.ImageField attribute)
TimeField (class in sqlkit.fields)
TimeTZField (class in sqlkit.fields)
TimeTZWidget (class in sqlkit.widgets.mask.miniwidgets)
TimeWidget (class in sqlkit.widgets.mask.miniwidgets)
title
TotalObj (class in sqlkit.widgets.table.totals)
Totals (class in sqlkit.widgets.table.totals)
totals (sqlkit.widgets.SqlTable attribute)
(sqlkit.widgets.table.totals.Totals attribute)
tree (sqlkit.widgets.common.sqlfilter.FilterPanel attribute)
tree_field_name (sqlkit.widgets.table.modelproxy.ModelProxy attribute)

U

ui_manager
unregister_hook() (in module sqlkit.db.utils)
unregister_layout() (in module sqlkit.db.utils)

V

validate() (sqlkit.fields.Field method)
values (sqlkit.fields.EnumField attribute)
VarcharField (class in sqlkit.fields)
VarcharWidget (class in sqlkit.widgets.mask.miniwidgets)
VARIABLE_PATTERN (sqlkit.misc.oootemplate.Template attribute)
VARIABLE_PATTERN_OO (sqlkit.misc.oootemplate.Template attribute)
View (class in sqlkit.widgets.table.columns)
views (sqlkit.widgets.SqlTable attribute)

W

Widget (class in sqlkit.widgets.mask.miniwidgets)

sqlkit-0.9.5/doc/html/index.html0000644000175000017500000004252111714210376016106 0ustar sandrosandro — sqlkit v0.9.5 documentation

Release 0.9.5 is out

I'm happy to announce that on Feb, 7 2012 I released version 0.9.5 that adds support for sqlalchemy 0.7+ and some other minor additions

Sqledit for end users

The easiest possible way to browse the data of your database

You can customize the way data are presented in a very simple way. The ideal tool to edit your personal databases or to browse data of an application you're developing with other languages/tools.

Filtering data has never been so easy, no SQL knowledge required. Read more...

Sqlkit for Python developers

A powerful framework to create any application from simple to very rich and complex ones.

Sqlkit provides 2 widgets to edit data as form or table. It's based on PyGTK and sqlalchemy to provide maximum flexibility.

Key points are the way to design form layout with relationship, completions, validation and filter capabilities that can be done w/o any effort. More than 80 examples ready to use! Read more...

  • Sqledit

    The application 'sqledit' can open a great variety of different backends as it's base on SqlAlchemy: PostgreSQL, MySQL, sqlite, firebird... You can use it to browse your data or to debug an application you're developing. The rich configuration capability of sqledit can make it grow in a gentle way toward a true application what starts as a simple shortcut to some data.
  • Table's list

    Each table of the database can be opened in Mask or Table way or introspected.
  • The model (optional)

    Table fields can be automatically reflected from the database or set using sqlalchemy's standard way. Here the example used in the demo for directors and the way to set the relation with 'movie' table.
  • Table view

    Each database table can be opened in table or mask mode. The image shows table mode where each column can be sorted and it's field can be added to a filter tool.
  • Relationships


    Create a form to edit relations is as easy as writing a text layout (clearly you must define relations in the model):
    	 lay = """last_name 
     	          first_name nation
    	          o2m=movies"""
    	 SqlMask(Movie, layout=lay, dbproxy=db)
          
  • Filters

    Each field of a table/mask even of a related table can be filtered on. Just click on the label and a filter panel will be presented. A smart and efficent way to express dates in a relative way lets you great flexibility. Here date_release >= 'y-5' means a film released after Jan, 1^ 5 years ago, whenever you run the filter.

  • Contraints

    Filters and constraints can be programmatically added using a syntax derived from django orm. User are allowed to play with filters while constraints are used to limit the visibility of some records.

  • Mask View

    The SqlMask widget shows one record at a time. Any field type will be rendered in a proper way. Images are varchar field for which a render='image' is set when defining the model. The layout can be set in a incredibly simple way w/o any programming knowledge.
  • Mask view from any row

    Right click on a record offers a variety of different actions. The most important is the ability to open a SqlMask to view/edit that record the record pointed to by the foreign key.

  • Registered layout

    A mask that displays a single record can use a registered layout and will follow the selection of the underneath table.

  • Completion

    Any foreign key is automatically detected and a widget that implements completion on the foreign table is used. Completion is triggered by 'Return' and the search on the foreign key is done in a customizable field and represented in a customizable way (here: first_name + last_name)
  • Group by on completion

    Completion can be programmed in a group-by fasion or constrained in a dinamic way so that the value of a field is used to filter the possible completions:
    	t = SqlTable(Invoice, ...)
    	t.completions.project_id.filter(client_id='$client_id') # dynamic
    	t.completions.client_id.group_by = 'category' # group-by
          
  • Totals

    Table can display totals and subtotals. Here subtotals w/o totals where requested.
  • Trees

    Rows can be displayed with a hierarchy

  • Editing joins

    Tables and Masks can show any selectable that you may define with sqlalchemy. Here a join between two columns is displayed and fields from both tables retain the possibility to be edited.

             m = mapper(Join, model.Movie.__table__.join(model.Director.__table__),
               properties={
                     'movie_id' : model.Movie.__table__.c.id
                })
             t = SqlTable(m, dbproxy=db )
    
            
  • Computed fields

    You can add computed fields as in this case where the number of movie is computed for each director, you can further sum and sort on those fields.

  • Printing

    Sqlkit provides a very powerful template system that allows to compose the layout with OpenOffice.org. No programming know-how is required to create an effective template, no more headache to get the right layout using OpenOffice's writer.

  • HOOKS & signals

    Many signals and hooks are available to the programmer for a powerful level of customization.

    A "hook" class can be registered globally so that its attached to any SqlWidget open in any situation, both in form or table view. That's the "controller" part in the MVC implementation.

    Hooks allow you to customize almost any part as are called on validation, on completion, on record display, on value change so that it's very easy to create a very interactive interface.


sqlkit-0.9.5/doc/html/debug/0000755000175000017500000000000011714210370015165 5ustar sandrosandrosqlkit-0.9.5/doc/html/debug/contents.html0000644000175000017500000002462311714210370017717 0ustar sandrosandro Debug helpers — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/debug/debug.html0000644000175000017500000004043511714210370017147 0ustar sandrosandro debug module — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

debug module

from sqlkit import debug as dbg

purpose

../_images/gtk_debug.png

This module provides easy way to debug with print commands. This is not in general my preferred way of debugging (pdb within ipython being my preferred solution). Nevertheless I found myself in need of these functions.

Printing of messages can be easily switched on or off.

Printing can diverted to a gtk widget.

Methods of Classes can be tracked in the way reported in the image, where blue rows correspond to dbg.write commands and all the rest is tracking of methods calls.

use

switching debug on & off

The way to use this is

  1. in you library:

    from sqlkit import debug as dbg
    dbg.write('text message', 'text2')
    
  2. in you application:

    from sqlkit import debug as dbg
    dbg.debug(True)
    

write() - caller()

from now on, each time you use dbg.write()/dbg.caller() you’ll see the text you want to log anly if you enabled it with dbg.debug(True)

If you want the log to happen in a gtk.TreeView window, you can specify:;

import dbg dbg.debug(True, gtk=True)

Logging methods

Following recipe 198078 in the ASP Python CookBook (see in the code) this module provides also a metaclass to trace the use of methods of a particular class. It will be instantiated as:

class A(object):
    __metaclass__ = dbg.LogTheMethods

for each method call a line will be logged (unless configured to be ignored) with

function called caller class arguments caller function line of code return code calling class time elapsed

IMPORTANT: since the logging occurs with metaclasses you need to import dbg and set debugging before importing the class you want to trace:

from sqlkit import debug as dbg
dbg.debug(True, gtk=True)
dbg.trace_class('SqlTable2')  # optional
dbg.trace_function(exclude='cell_default_cb|cell_bool_cb')  # optional
import sqlkit

TraceIt

in case you need to understand who is changing the value of a variable, you can use TraceIt as:

commit_allowed = dbg.TraceIt(True, name='commit_allowed', mode='rw')

and use it as a normal variable. You’ll get lines in your output stating who changed the value of that variable, in the form:

__str__/function_name:  old_value => new_value
__get__/function_name:  value

sqlkit-0.9.5/doc/html/printing/0000755000175000017500000000000011714210372015733 5ustar sandrosandrosqlkit-0.9.5/doc/html/printing/contents.html0000644000175000017500000010304711714210372020463 0ustar sandrosandro Printing — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Printing

Printing in sqlkit uses the oootemplate module that is distributed along with sqlkit but could be used in a totally independent way and does not depend in any way on other parts of sqlkit.

OpenOffice.org template

This module makes it possible to use an oasis .odt document as a template to produce documents via the use of an openoffice server (i.e. an istance of openoffice that listens for connections by clients).

It uses the uno module that comes with openoffice. In the following image you can see how the template looks before rendering and how the pdf produced looks. The red circle stresses the marker that makes the template produce many lines for each input line: one for each object in context.

../_images/ootemplate-colors.png

This rendering can be obtained by code very similar to this example

The template

An openoffice writer document (.odt extension) can be used as template. No particular templating skills are required by the template designer, just ability to create an openoffice document and a list of variable’s names that must be used. Clearly this list must be provided by the programmer to the designer.

scenario

oootemplate will substitute variables values where needed.

Dear Mr. $user.name $user.last_name,

Today $date we  received an order for $n books:

+-----------------------------+----------------+----------+----------+
|Title                        |Author          |N. pieces |$currency |
+-----------------------------+----------------+----------+----------+
| ++$title                    |$author         |$qty      |$price    |
+-----------------------------+----------------+----------+----------+

This is an example of a template rendered as possible with ascii art.

There are 2 different substitutions that can be done:

  1. simple substitutions ($user, $date, $currency, above - yellow in the image)
  2. multiline substitutions (the book entries above, starting with ++ - green in the image)

the first refers to substitution of a single value that is already present in the document, the second refers to the insertion of several rows according to a list of fields that are probably in a table.

The variables defined in the table should be repeted in loop for each book provided in the context, that implies an increment of the number of rows of the table. The expected output resembles what follows:

Dear Mr. Alessandro Dentella,

today June, 2 2008, we received an order of 2 books:

+-----------------------------+----------------+----------+----------+
|Title                        |Author          |Copies    |Euro      |
+-----------------------------+----------------+----------+----------+
| Q                           |Luther Blisset  |1         |10        |
+-----------------------------+----------------+----------+----------+
| Il sistema periodico        |Primo Levi      |2         |8         |
+-----------------------------+----------------+----------+----------+

As for any template system a special syntax is needed to allow people to create loops. We are constrained to what can be done by a simple user of openoffice (no programming at all) so we choose to use a MULTI_LINE_MARKER in the default form of a ‘++’ (red circle in the image) .

To make things more complicated it’s clear that the person that created the template (the .odt document), may have used a table just for formatting reason so that substitution of type a. can be in an openoffice table or not.

implementation

Substitution of type a. above are done using search and replace functionality of openoffice API, while substitution of type b. are implemented as a loop on table’s rows and cell in 2 different ways: one that preserves the style of each word of the cells and one that doesn’t. The former uses getTrasferable() method on each cell and is slower.

You can switch from one to the other setting Template’s attrribute Template.preserve_styles

The pattern used to detect what is a table can be customized. While the default is ‘$’ as in the shell or in python template system (and perl and php...) since you may have the ‘$’ symbol in your document you may want to customize it. See Template’s method set_pattern.

context

The context is the object that contains the mapping variable/value to be used. It needs an argument -a dict- that holds the values.

oootemplate allows you to have more that one tables in your document. That means you can implement easily things as:

Dear Mr. $user.name $user.last_name,

you can order these books at 20% discount

+-----------------------------+----------------+----------+----------+
|Title                        |Author          |N. pieces |$currency |
+-----------------------------+----------------+----------+----------+
| ++$title                    |$author         |$qty      |$price    |
+-----------------------------+----------------+----------+----------+

or these books at 50% discount

+-----------------------------+-----------------+----------+----------+
|Title                        |Author           |N. pieces |$currency |
+-----------------------------+-----------------+----------+----------+
| ++$title                    |$author          |$qty      |$price    |
+-----------------------------+----------+------+----------+----------+
|Title                        |Author    | Extra|N. pieces |$currency |
+-----------------------------+----------+------+----------+----------+
| ++$title                    |$author   | $x   |$qty      |$price    |
+-----------------------------+----------+------+----------+----------+

In this example we have 3 lines that start with ++, that means that will be conseidered prototipes for new lines. For each of these 3 rows there need to be a list of objects (books in the example) in the context.

Since Openoffice-org tables have names, we wil use that name as a key in the context for the value. That’s enought for the first table (20% discount) not for the second where we have 2 lists in a table. To cope with this case, we can put as value a dict with entry an integer that indicates the position starting from 1 (see example below)

name mapping

Occasionally the name of the variable will be too long to fit in the space that you want to allocate. You can translate a shorter name to the real name in the dict context.translate (see example in the demo), blue circle in the image.

This way you can hide the complexity of some variable name. Note that you can translate both $rs.manager.address in $addr or $director.last_name in $d.last_name.

output

All output accepted by openoffice filters, you’re probably using .odt or .pdf

example

A tipical sample code is:

import ooootemplate as oo

tmpl = oo.Template('/tmp/mytemplate.odt', server='127.0.0.1', port=2002)
context = oo.Context({
    'user' : user,
    'date' : date(2008, 6, 2),
    'currency' : 'Euro',
    'Table1' : (book1, book2, ...)  # lazy assignement (simple tuple)
    'Table2' : (
         (book21, book22, ...),     # correct assignement (list of tuples)
         (book31, book32, ...),
         )
    })

tmpl.render(context)
tmpl.save_as('/tmp/new_document.pdf')

API

Context

class sqlkit.misc.oootemplate.Context(content, lazy=True)

A context used to render a template. It contains both the data and the way to substitute variable with the data.

__init__(content, lazy=True)
Parameters:
  • content – a dict with keys the variable names
  • lazy

    boolean. If True (default) invokes _implement_lazy_tables to allow the list of objects for a table rendered to be directly set as value of the table_name entry (rather than a list of lists). See example Table1 is lazy Table2 is not.

    The goal is to prevent common errors rather than promoting lazy writing. When you only have a single list of objects you may easily forget that you may have more than one.

    The assumption is that you don’t normally have lists as values of context (other that for tables). While probably true, should you need lists as contetxt values, you can just set lazy=False.

Template

class sqlkit.misc.oootemplate.Template(filename, server='127.0.0.1', port=8100, headless=False, oo_context=None, preserve_styles=False)

The class template that connects to a server (or starts a local one), read a document, parses the document to find tables

__init__(filename, server='127.0.0.1', port=8100, headless=False, oo_context=None, preserve_styles=False)

only the filename is needed if the server is local. If we already have a template on the same server we can reuse the oo_context

Params filename:
 

the template filename in the server’s filesystem

Parameters:
  • server – a server name
  • port – the port to connect to
  • headless – if False, disables the headless mode.
  • oo_context – the openoffice context (not to be confused with oootemplate.Context). The oo_context` plays the role of the connection to the server
  • preserve_styles – use TemplateWithStyles to enforce preservation of styles in each word of the cell when duplicating rows
render(context)

substitute all the variables with values from context

Parameter:context – the Contex instance to be used
save_as(filename, local=None)

save the template using save_as capability of openoffice.

Parameter:filename – filename in the server‘s filesystem. The extension is used to detect the file type as in regular openoffice use.
set_pattern(pattern, oo_pattern)

Set the pattern to detect what is a variable to be substituted

Parameters:
  • pattern – the pattern with syntax suitable for module re module. It must define at least 2 groups: var_name and match pointing respectively to the name of the variable and the whole match. Default is (?P<match>\$(?P<var_name>[^ ]+))
  • oo_pattern – the pattern with syntax suitable for openoffice regexp search. It can only use the openoffice syntax. Default is $[^ ]+
VARIABLE_PATTERN
the pattern for python variable detection. it’s a regular expression read set_pattern for details
VARIABLE_PATTERN_OO
the pattern for openoffice variable detection. read set_pattern for details
MULTI_LINE_MARKER
the pattern used to tel when a multiline line starts. Defaults to ++
document
the openoffice document from the server
oo_context
the connection with the server. This can be reused between templates
search
the openoffice SearchDescriptor
preserve_styles
Preserve style of each word in the cell when cloning rows. This will slow down the process so it’s disabled by default

Table

class sqlkit.misc.oootemplate.Table(oo_table, template)

table object detail on the API exposed by uno: http://api.openoffice.org/docs/common/ref/com/sun/star/table/module-ix.html

__init__(oo_table, template)
Parameters:
  • oo_table – the openoffice table object
  • template – the oootemplate.template object in which this table is
class sqlkit.misc.oootemplate.TableWithStyles(oo_table, template)

A Table that clones rows preserving style info even if several different styles are used within the same cell. This process is slower so it’s not active by default: set Template’s arg preserve_styles to True to enable it (see ex. N. 5).

__init__(oo_table, template)
Parameters:
  • oo_table – the openoffice table object
  • template – the oootemplate.template object in which this table is

sqlkit-0.9.5/doc/html/_static/0000755000175000017500000000000011714210376015533 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/sqlkit.png0000644000175000017500000002350411530470276017557 0ustar sandrosandroPNG  IHDRjnsRGBbKGDC pHYs  tIME%zV IDATxy\_3`aaYbEd $ 43q +˛\~-ZfHfzl1MRjJ.!(!8̀ 1A <g>s>yfB( E@PP(*8 BBPP(h{Z-r9 1at:X,VT*͞ ЊP `hjj2y{{cΜ9eeex7qGGG|gpuu}`ϑ-[@*|||dӇV4Bt:R).]|h4rP(D~0 ;7fGzs=FضmN> Vۭ477ȑ#8r2220`k2e \.zٳ$M(bpwwDkܸq|Q[[{2h 22&Msu̙3$-""Tpt Bz YYYPշ=|><Q*˷(*8wDVc…L(ap=Æ C@@lll6;! %%%8 8;;4OHp!,MJJ ^u1< III7o$ ;G"??׮]KHMMEjj*-ʃ۷dgyk׮E@@]ݜf_~2e ^x~;XF EEE&遁xZln !!hoo- *8׮]իWҟ}Yܳ q8p8Z3UpZ-jkkP(Lҹ\. ;;^p:MMMJhooF0rxpvv#F&h4hnn6Kwttbjq;wgΜkL'''#<<F\\||| #kjjpar릧Zq2 NBeeٔwwwZU"//d)P(ѣhv|}}=>l\{%̎ijj޽{q;#G%Hhmm%'L/BzR|$::ٙ?6MCFIoƉ'a䯦Ky8v4Whh(9vX?}4a&绸HtJ֭[Gm۶ZmoxzzI-㱷>~kZ9r$ijj"G.p8-p ^1TYYE_E[[7 E]]^J̜9ۭ NիWcӦM&+ 88˗/Gjj*lll:Aכíkt|gz3lll  fM!GAjj6Rür ,XCYl&h4(JhZB dd2=VbضmeeeD\E}cmm ___䷽{"11ӦMKy~Ç CTTbbb Y\\\|hmmŶmحŨ^˗/cŊطo0 T,[ BuMMM8x1 w^xx_֦(BСC-> /^$Zڄ:ddd[[[zwHqq1fT*rypBd]pt:#F2?!'Ob;wwww넇wgϞ5+Èri̠>'xϟ7ۀI,cժUFBBOO.4ۜo6^~e#*DGGXpq H^^EEE&0m4̟?_YJ6͹yWpq:ulxUTEEEضmx< p`ccsOBi&;w.^{.M"xP__5kl(tr֮]SfCVVL&C]]N8a && c=nf5k]XGzz:y~hii7|>Ȭg֯_?HOOk(@\\>^'N8hZ۷o@ СC$DEE,}Μ9Q&LfUFgafb~'O+80|pxyy!++ ;wDii]ϙJ8p>`=:lN;wl6ڭ°A\\Q\\ܫ+ ~G , ?8{=1 Wѭ G $$׿o>ddd ::wyARŋO1i$\pK;655A,[ nHXXXWojkk1k,ܹDll6~z9)SN`̝;eeeFrQQ !ˡVo+$J;Ґ1cֱ\YYiA_!esApY,ZǏ7Ur8bÆ t`ʣ)8&BHHBBB0sL466׮]õkP^^HJwjUTT`ժU?iɦ& (()`kk۫Gq8q">#VMyV\\\!CߺJ\zΝÁ,ctl7q3dյי#r1_nӧcܹTl(;888 22'N{gݻwW_zݻmM[{ vvvwܫέb 9s& Kc GGGDFF"33˗/mnn?ujz'sOpelۛBp lڴ 5S@tYQvhp8fNeZ}OPTnk(ɐ+W-P܂;z)k׮u:$V*dYBu{X[[#-- ˖-CPPj|Xj ;1`C:Lb[Jmmm=d xtD7́d2rss-::zy5O?޸_[KD"߿z!/&uL\x1JKK5ڇJCzڵ ȀH$2ҳ;~~k6*dee{{{g5K?{,6lpWyؾ};;U"08p ۇ!C1jYYY={6 &i())镣^dR b?~<֭[\TWW!7nŋ-ΒABB5uTe2 |d]e}|hc!X,/@JJ%~ ŋXdfR:tO~r7@wNpXƢxk1.!(++C~~>9b/3g΄yaɒ%&Ȫ*\UUUxhgSRR]vaƼWTu&:Xb.]ɓ'[oaŊx\|>111سgIرc>;;;3~~~n] N% / B...prrP(0P(hllDuu5 H J-v0yd3K+Ip9dee\| :A!** nnn ?PRRbwTٳ硌fիW`0ǏǢE|r <  .rN< .8ּ 4WϧoBWbh4IX,mcuքClll0w<ё .ǺҥKg!VVVmccC@ kkkcG}DadV Z Z Fsx<DFF222|W_^60]iii5 d2R)R)rjs,Zsν=,_}^z%zΝӑ{_V֘1c/^໊ATl6FB||<.\SN娬<B||<пm  6`ؽ{7;a1p@̜9iiin[ҷo_,]\.}ɐիW1o<[--ΝH>|Ũ\.F1x<ڰH7j`0bظX,D"Ass3 kkk {{{xyy! @HH{j.\q \rhmm˅P(4]bb"`|N<ɓ'C,\K캺:^MOBW~FPz lZ BCP(Tp(  BCPP(*8 cF#T6.Ǹ Bo7f͚6bgQ(qORQ(JZ BCP(Tp(  BCPP(*8 BBPP(*8 BC zPޞB$᫯Byy1 ӦMChh}laa!֭[Z WWW̚5ݫC8=B( lܸSNvhAYYV\ H''',Xqqq>?* w/ Œ%Kp 8t@p2kkk!C jpq1TTT ^&x{/bhii3TIII(//GMM }N߾}1{lT*\|+Vx !hmmT*`=A^ƐcӦMhnnkfC[[R)P:R2 vvvpqq13.QZZX8::ikk3>L&C{{;`pppd2!GKK d2dVvJ=T*z=\]]aejS*8u._-[ ,, fi4@P@ @ ]WTP(pvv\.GKK \.BaE:hnn\.04+r1|p$$$`ǎ=o.@`Lr9p`kkk.K^ NQRRPbގ'O_~T*^;Nw'gggyůzz^7'J3g@*bܸq5jQ aVqcܸq łRĮ]piBӧ4LV~l6vvvCuu5<<}`]C!;;eeehnnƶm`mmgyƍ0 @" ;;EEEhkk+0d8}49;wĵkЧO,_K&fyVVP[[ Nc"==[UUUhnn-^___OF[[1~x<&BqE޽ * xٓ'O";; 0 ٳ@^T/X,FDDƞ?NNN8q"^|E$%%ɩTDׯ^KNNVX@{ —_~={ݻw#""/"|>VXϛC8;;C\\nj(G),,$111fd2oɓ'DB !`;H$2p@m[YYILL$k׮% BJ"SL!F"ZBÇIhh(߈F!"IDDYjz}>|89z(6my饗ȟi<ӦM#Dכn:B #aaa&ϲ~Db1ڵk.2n8gNgV%9III!gΜ!:zCHNNXJE&OLɹsB wNU*Q*Bz=ٵk%ϟfddDtNG~G2tPrAj` O&qqqdǎ:ۼy36l {n2x`rJKKիMZzz:y'{ezé@{{ů q)̟?GUUt:]`f~@V_7F\p8RłVƍdOOOh45Ϝ9XyA⫯BHHH>" 0!H`mmX,jX, j5 jkkqv 43UJ%;5 `l6nnn7ޗH$|8x|7t8g;+̙///477c=Q[[ cUWW7n <$$$ֶ[-**SNTB& b`޼yhHjqtCB,7"##1k,lذWԡY\\ +++RT6`0@$h4(++ UjIDAT>}` `0Ln~I1Z6agg_~%9K"$$|>SNPD"AKKEVE[[Qӡ , rGtt41p@Lzׯ_G~~>|M,i܌*ёZ.\b b,ZLcǎBp8puu5֬Y#Fkee% 駟믿V ]]]aֆr.]22 6ml H$DŸ]&LgBP<=Nz 555Gkk+b1-[Ph֭[⧟~BXX]L&H$BRRE۷/x |(//0طoF_~h~td{ {\.aoon#Ԡk֬A`` f͚e|<ؾ}; ?Lt<0JXf 6l`y{{cǎ RRRpq̙3ƶ'o@ 33gw31cƌwyǢB}vZ [nSO=P tҥws\.ŋ ///8::"%%E~:JKKQZZV$&&w߅O3 '''oK ׯ_D"Att4/^`cwb!)) à ƍâEL*_WFLL ЧO3`@cc#̞Q;LvcP(F+W Jӧ_DDD=8t|7y5QVV^3f`&=WFRAǧGNQ`0@Vcܸqx  ;Ʈ8;L& <ؘODEEPT4i̙cZBlL:ӦMË/ooo駟6{pp0 QUU,Zaaafe6bcc1hРN{* jcƌ2fXhii@ ĉ-Oaqʽ-[ɷ~KJJJJh!g$ 6lr_?B!4x~j۷ .\@kk+fϞmф:JʂRDss3 @3,J{ #&YYYP(Dxx8x x{{r9sGcc#'xnnn=PP(BCPP(  BBPP(*8 BPP(Tp( BCP?0CKIENDB`sqlkit-0.9.5/doc/html/_static/print.css0000644000175000017500000000031711714165502017402 0ustar sandrosandro#content { font-size: 90% text-align: justify; } #header, #main_menu, .sphinxsidebar {display: none;} #wrap, .document, #container, #content, .bodywrapper {width: 100%; margin: 0 0px; float: none;} sqlkit-0.9.5/doc/html/_static/tour.css0000644000175000017500000000070511530470276017243 0ustar sandrosandroh3 { -moz-border-radius-topleft:4px; -moz-border-radius-topright:4px; background:none repeat scroll 0 0 #DAD7D3; margin:0; padding:8px 1px 8px 8px; color: #333; font-size: 24px; font-weight: normal !important; line-height: 20px; margin: 0 0 8px; } img { border: 0; padding: 0 10px } #preview{ position: absolute; border: 1px solid #ccc; background: #f4821f ; padding: 10px; display: none; color: #fff; } sqlkit-0.9.5/doc/html/_static/arrow_right.png0000644000175000017500000000034311533706144020572 0ustar sandrosandroPNG  IHDR vWtEXtSoftwareAdobe ImageReadyqe<IDATxb?²F.8 d3&(Tg3`@ ( 4n gð2qF0A\A 0yꤙ !+1?B~aX@ahħ  *[jIENDB`sqlkit-0.9.5/doc/html/_static/doctools.js0000644000175000017500000001473211323040551017716 0ustar sandrosandro/// XXX: make it cross browser /** * make the code below compatible with browsers without * an installed firebug like debugger */ if (!window.console || !console.firebug) { var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"]; window.console = {}; for (var i = 0; i < names.length; ++i) window.console[names[i]] = function() {} } /** * small helper function to urldecode strings */ jQuery.urldecode = function(x) { return decodeURIComponent(x).replace(/\+/g, ' '); } /** * small helper function to urlencode strings */ jQuery.urlencode = encodeURIComponent; /** * This function returns the parsed url parameters of the * current request. Multiple values per key are supported, * it will always return arrays of strings for the value parts. */ jQuery.getQueryParameters = function(s) { if (typeof s == 'undefined') s = document.location.search; var parts = s.substr(s.indexOf('?') + 1).split('&'); var result = {}; for (var i = 0; i < parts.length; i++) { var tmp = parts[i].split('=', 2); var key = jQuery.urldecode(tmp[0]); var value = jQuery.urldecode(tmp[1]); if (key in result) result[key].push(value); else result[key] = [value]; } return result; } /** * small function to check if an array contains * a given item. */ jQuery.contains = function(arr, item) { for (var i = 0; i < arr.length; i++) { if (arr[i] == item) return true; } return false; } /** * highlight a given string on a jquery object by wrapping it in * span elements with the given class name. */ jQuery.fn.highlightText = function(text, className) { function highlight(node) { if (node.nodeType == 3) { var val = node.nodeValue; var pos = val.toLowerCase().indexOf(text); if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) { var span = document.createElement("span"); span.className = className; span.appendChild(document.createTextNode(val.substr(pos, text.length))); node.parentNode.insertBefore(span, node.parentNode.insertBefore( document.createTextNode(val.substr(pos + text.length)), node.nextSibling)); node.nodeValue = val.substr(0, pos); } } else if (!jQuery(node).is("button, select, textarea")) { jQuery.each(node.childNodes, function() { highlight(this) }); } } return this.each(function() { highlight(this); }); } /** * Small JavaScript module for the documentation. */ var Documentation = { init : function() { this.fixFirefoxAnchorBug(); this.highlightSearchWords(); this.initModIndex(); }, /** * i18n support */ TRANSLATIONS : {}, PLURAL_EXPR : function(n) { return n == 1 ? 0 : 1; }, LOCALE : 'unknown', // gettext and ngettext don't access this so that the functions // can savely bound to a different name (_ = Documentation.gettext) gettext : function(string) { var translated = Documentation.TRANSLATIONS[string]; if (typeof translated == 'undefined') return string; return (typeof translated == 'string') ? translated : translated[0]; }, ngettext : function(singular, plural, n) { var translated = Documentation.TRANSLATIONS[singular]; if (typeof translated == 'undefined') return (n == 1) ? singular : plural; return translated[Documentation.PLURALEXPR(n)]; }, addTranslations : function(catalog) { for (var key in catalog.messages) this.TRANSLATIONS[key] = catalog.messages[key]; this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); this.LOCALE = catalog.locale; }, /** * add context elements like header anchor links */ addContextElements : function() { $('div[id] > :header:first').each(function() { $('\u00B6'). attr('href', '#' + this.id). attr('title', _('Permalink to this headline')). appendTo(this); }); $('dt[id]').each(function() { $('\u00B6'). attr('href', '#' + this.id). attr('title', _('Permalink to this definition')). appendTo(this); }); }, /** * workaround a firefox stupidity */ fixFirefoxAnchorBug : function() { if (document.location.hash && $.browser.mozilla) window.setTimeout(function() { document.location.href += ''; }, 10); }, /** * highlight the search words provided in the url in the text */ highlightSearchWords : function() { var params = $.getQueryParameters(); var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; if (terms.length) { var body = $('div.body'); window.setTimeout(function() { $.each(terms, function() { body.highlightText(this.toLowerCase(), 'highlight'); }); }, 10); $('') .appendTo($('.sidebar .this-page-menu')); } }, /** * init the modindex toggle buttons */ initModIndex : function() { var togglers = $('img.toggler').click(function() { var src = $(this).attr('src'); var idnum = $(this).attr('id').substr(7); console.log($('tr.cg-' + idnum).toggle()); if (src.substr(-9) == 'minus.png') $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); else $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); }).css('display', ''); if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) { togglers.click(); } }, /** * helper function to hide the search marks again */ hideSearchWords : function() { $('.sidebar .this-page-menu li.highlight-link').fadeOut(300); $('span.highlight').removeClass('highlight'); }, /** * make the url absolute */ makeURL : function(relativeURL) { return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; }, /** * get the current relative url */ getCurrentURL : function() { var path = document.location.pathname; var parts = path.split(/\//); $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { if (this == '..') parts.pop(); }); var url = parts.join('/'); return path.substring(url.lastIndexOf('/') + 1, path.length - 1); } }; // quick alias for translations _ = Documentation.gettext; $(document).ready(function() { Documentation.init(); }); sqlkit-0.9.5/doc/html/_static/galleria-1.2.2.min.js0000644000175000017500000011463511532752517021111 0ustar sandrosandro/* Galleria v 1.2.2 2011-02-25 http://galleria.aino.se Copyright (c) 2011, Aino Licensed under the MIT license. */ (function(e){var s=this,t=s.document,I=e(t),E=false,x=navigator.userAgent.toLowerCase(),J=s.location.hash.replace(/#\//,""),y=function(){return j.TOUCH?"touchstart":"click"},u=function(){var a=3,b=t.createElement("div"),c=b.getElementsByTagName("i");do b.innerHTML=",pyi'))6oOji# 3颔ҷ{BQJi'?)rwJ)w R\\L[sp/Gr?5J' eᥔ~e?l3bIJi潱sߌ\SZZJNAEEEP1c[xKo_tOAo.vA)`аQmSG/65CgtxLM=.+Z{hv(u(@4 *NH(`0&ԋ2F>3]|iPJ%` DyGѱsʇ-HŀB~zYT}b< 7z\?4uky0EcS} ,>{YUо*ǀxDyu4P05I:eGor`k~kyf cђ>S Ta߷-olzƋ01.A6%.޾vnٴQ0tngMF|jYQa3f>*\u^z@I3g/\)Vn?8Ž|YQ0Yս^޵i ؙܼ"ʅˇOvYoR~+wML`/<^;[7zi+N]Wt u#s/O2q)ph /f;qk\m;O]rP]>9CJ<7'ƺKovNK׮6^Eޖωʊ`HH럇v@鞀_Qj2O]ybWl׏x PrL4a>*P45a̗wjf  D(8PH%(/[ a%6&Jz $3~ٳgQA!D)tz`J(UBA *N9C8A' u֍IPYY >5xV(LQ8CJ0(^¡JgdYwU 8CxJ(%DP( t5nL43tQ5x.PMק$hNhUpѨQ!/x%('tlQp;[B3/z¼!r @ Iz} 1 sI@Q/4d;>h0sQ}.JfDƂ`0:# AcF])O %ʠVc0xϛT_~1.))޽{˃ZoD4 aP GRa(h=qBc\VVϤq1it,L-B)u:VF|Ccb DYl5h}(HG@B@|@| >@2\nXXXuu5ǃր `[n]lYYY$HrA]zu͚5 B!Cw߿lhh(aD"@ه`B bcci4ژ1cdee!A >@L& !rرI&h#F f D$ň#D]\\tϜ ZYh<X[[3/SGս!'1@~əJzAwv~n~ÖlI&ƹ!'~2.YM}q5/R/udz`0 #884888==47V{?zbڍ Ѿ9L)*&G.a°Ȫ4/)R.jL:8ORTtUgAP+7IzuGAAѣGy<^@@ޟm۶Xg'i^6pKXu{{;Q\J9+Uڞ"90ItHrO77q`Ō9b@p8booQFZL&;:: M) xF6y?,PGr5ˊ0%4n!a|gwݰa=z9 ^w,B_v0L݊~f?uCKkkkkk+S-DU14Vr//oal48(21o9)/Zʣ)9|C7% =^ȭRwXK91\Yta>>#+ fֳS#}#}<-^8:ksFGN8!E썅GNz݊侉dH y'#}dXWEGBI;qrlJ+qR"&0LwМӹ&B_㬨 hFQn5BVJ0 qޘQ+dP  Mg3/RXxBU ]*ycb ZާByyyG677޽{DDJ 9y$,}=tt4w}+寇fᮒ~j燉WQA1,}5I#(d2SWpx5EZ@(3ؙ76թf]TaZ ;^qǭ*!Z>\v_yolw&?{ɩc6>i(쫸nN=-WN#5Ix5iۯ~7ba'c|ʌC҅umL]ӎN\!Kd)vy^ԋm=foc?ri%mʌC 2÷N?ueƩ+^Ϊ=|9"ȑcUG/KJgS/ã$~,\O};}H^ŝxIمn8/7!֕D_qfAbc"QnXC/U­JWMtQ3l/D09)a#BǏqqq)))[la2s}ف9S#ьy)3ćR؜Fxy?6#6ֵp;YU|~z 3L -G-["ь'Z?̶ݸsw5Y~ܣ}E_2oa- \ݱWUf.k)Uð5=Wp6S45) HUbnFw﫭Z9,1v]4=0؈[p#5`8{Q> vGOk>ߨ3ȫr]Uw]zI<,by?ب0F!^؄uT#dM(zҞ_t۰uhvnu;m8/,^8a{)+i[ ^^AKg5uECFJMzuj׬s7ɘh#O*:735oo-#dP'J" b㜟 =HPN-|´ͺOⷂ|+D"/"Ad!m)8N1h_A>Pރ b3ܨ{ ,*RUƮL%{ YxjC$\sӐ#n2SUjv +MC& #ӢGh8v)Wt7 F{5 $.>"TRڦ$+Tgj:R|ulY;g.-jCC@OJơ,,aDܮF& n#n?ۗ)l㗛ݳkV У`II %ySmXэ)H+mmJJ2ܞċՈ_]|6^r_w-C!3evܮ"i:"6f1>]x]dk+\a|]uSIg~ȿ w Z['Տ33u ]J;(ɸq_z_qEpӫ\8G ium"M(x;(;+j{}6Eڼ;oҠ4 IqX`׋Sl{ zlD]Waج'MXjS0 F#ENعwn~O~Z&/u|tgE4ɫY_{]7Ŀuҿby"!õa1P6@ jD[zrS̫VhiT ٹ?YpuAkBivaڨ{h mM=P 4(Dd9v ¯kIsoE>NJ:s+J7/?B>UҟmB6w2t aLHIĩUvKn|BnEa_mdh [gl^Wp81?4Ug׍T!\σ{*"@bE C^>*up?;w_A2mH!h %GQMy O6@ d*ƠY*bWWLͻ{_S/IYv ɵXI+p4}S[G3y˙eov<=Dژۂip=!tA観IDksFxTq-g|E@W??͏zIH ^gZE5W$ޗ\v':X]UEU"GB_σ8z==.uE 3t6t9dBc4@ׄ?E^WPAW ж.M& 4?OI+)Y";D-,G:bMV;^[tA-,-|Oַ$Oӊ6Fj2BB%Z {/Z\__Tuo>y_j4{um𚋯k½DkZҶv%-Wzr=rg,|͏dS"_$br>1 /yU&;^H <=^EvE`iTU?V0z酄KZ^W_DLlecmV|Ѫ~I6I(mk 56ƞ5Z[;͊@H<>+z;D0Y_9} p"{bNoJҦS+sRA] z!}f++#redMTB(>xe{k%">h#lǷ: |#hzΡ|j$SV_4LGho-SҶ_J?SJ0iNO4x߳e4:?{FqO݈;gƦW#89ƭm08V/jp\OT{\iG!hfjG.H:ZVڹB7|_7AKmDG_S$BʹI$ \<:qǯ[7R/_a#׌5Xe˨O%>^O'tx hfڬi QGͰ-"%礨[|` "8ؒPTݪG0hX\ UL+BHŜR0W㍳ht.]436yZꂤ!DGDr _87AP%~$ʰc [xV94Hp-ӛ9[&+?O97BPX#iR[=r_ Ck+%;Am;ǎIK3Co.HZZm*DGDT2ә?mor{m|9s x>Gd9jq+G˄[Լ7m!:*PC2j:jru=zz$̴M\$=- gQM(x5*Ue #4?SiYNJڬ#ӆ/:v!DP&rKf .>NXk/g"mǿT`/,IyQ&oy:#邤GjZM&M6D}>͏ĺ v2^T~(`ۚ,~b& ux:yf4$q@рu*\oSy~Sh#Քg'\S=WOiGtN^ϵW" I?n0@Z3j= |.HfF`:8@Ň H?i0n!.a\?O#du;:Eёڹ aߴÖ[v7nc1pcنCc`0s;0zFvϱbލW[) oZ+!/0V{Y\:H|40 S oYsJsJnZ:į8)mwY2ڵš4DlQ#knԣSg޽*JA@DJq&l[ zqm}N:թ#[qOE`{VFU]#R@i񶄔vC)tiҺFAĴm9WXvO)\RPcY(v\asy(uܶ2SZ:2RS\5c=qާY[y$%#S6y!]4ufTf?vq2r SՕx-nz򍘚Ν "=-1{쌴$)^@6F$ϻ~`Vfo]>?NPaq yS z'(ūK6kXx:>HoQ4;sN:>9oAO< 59]#[4y01k.>>ΥN=oC:MJqCQhFa@'P*<%i @J{b-WRʃM` REw6o. [,#|_RJv;wB5)P]f !Dz"uM׬....-bkoR׬....溸򿠹k "6~*?EhRDQPC]ܛP57TZg}8yֲ-U/2DWF~3`VPBy&d_ꌉ{)V䨿|X}?g eD%R:O)7>>e㦀lh ld;@ 61 &e뺟...{LD` .#@2.)'/Rs딸ITلϷ}:OƠ9 %.q)ZOY-ӡSN$slZ G6~oavh w-=%s}%\.$j1qi.?N"Y6廍57zGq+WEHRJn_VU+'OrOIkv`9I1,)ܲz-w=;aneץ7:v˾5g}zA"BVf?f$V`NeٱG%3.X۾}nJa/[C0A!cTf8a,WG$BEZj {u޹v䛋. 4/(й}jWbׁȋyzh[CF 9-9(řgt9cF{)M7Pɫ_xCU.{6{?n䈄"Qovs^oϸ >`S5t+I1L.$S̬hnXwirv|goUPW6+[gfpi}\vΓWWsQy9Om l+ NxoJ7o@r~fט[zǶvju$濫z|b;\pش vc[e3/:mPMUs/r{˪ w5-k:V}ϼSxM"˴iH,{.#ݏˋk{3cEl;Uv4ӓ!D8lCx:s ~Rto1"9BMQPRq) \mhΚ=xiv8z3ܩCͮ۸9wMe܆hm--HMlM++V|/>9 =xgكr/?hƳضik6UN(cL3*vF*l{c1޿;Ҍ`73^wÇ :mdqI >Di6ou gGfJť{4qħY(%3c7?nۦ*J.EJ@2S{~>uqukL}Q2Č$>{ʴ֒aulLnϟN0'nݗӓz\B¾VE/_~̘3ZgnY0Rd&2bZ\2oL%뀠eF#bbjFslʐdg&HDo\r:tk勉k;hy-=C #4+#vGȈ7SZ߳[jJrd]8$j U7[pqqq9Ws]\\\:(Rr .... \+A~.pikÈD_1'1R. `wV^B`d)Qsqq5WP41l[ ݎ/${fD@RU46k:iͭUQ%Шs ! кUĄPfl?GJ"y@RVk_W!~kQT@!;Hu"-=,EёLUK5yvTjqJcJ(wRJt 4R~ff.9] ֌1 )FMr@20;vɃ=ޝ:@|eIIIMm..<F`Gp İνbF^(-g UFw[ 7ՅlWOPkJ]?/y X@:!1.涷^~P_sn(D@ ot.Tr#?`]IIq_fD,ںY s۲_~R}vM'܈iBSyU$::!~‘]9"*L^![+7T8>kkkO8ᄢ-[۴>08"R~ӵ19h%\Sx^~e?dё|v6ƴxGMT}.+'^yujvV|t\:AAtOߍ2׽jS1(qɀD""D+;Pضqo9bD]n[y]R[[[nݺ+HII!0lݺz...[sLIN G'%q`w%'?agI(wekU)-}游~xLӢmZ|jXEc-[ N]7-9ȚBMjty*l,b(]N)ܴRzTiJgwL Qz; fځg1߉7_w=CKN[3od#7@D!)kSJž(>|wɽzx~҄O>wީiZ$y'~i˲^xW..G}`0/Ќ~)Y`!&%Y +`X_Ws+j*jZ%uMTtb-9Աc֭s=OB˗ծ^}Yaf~e'{̺57._ZT-괕Uq>}ӎh]jaG!*\R>ǔ3B&Ƶu*S|-Ժ?u0QDC9agtMi] xΘϖh"`S0IT]9KBbb⺂U*J8ٶ}"0ƚ<;OжmNvQ״a!\YUfX0lhpG;-jDԯuU`w/H-+:DL5&M%',)،=#Z1$Rj DT3v~_u7V:UD}Y>c3OK<͹m{?Qku&#R%őC (%RH)ğM8fBTVV)x CG^?QlmBíyL]]TWg!vCC#~B~Z_C´i_Eӫ{h%'`j*rrRO>]ԓTؘq4DL+%`ngKk[jͣ+R)LTǼ(oMS2@!=iSOEJ6i^{;筷izgy<7+HgjmTtث{#FԜq^ xhuwq9K)5-+))b93lqdn!bJJJn?mc Û&z?ы+uS_ ,`3|pMa|=e=E^݊uG[8Q))Vl̨9ڐjDaνf; u? +%G)$s!DCCs~F-QA9FZjPM#&h/~!ol3)))ճGjju|>}`  A1M7ڵkWX\?B̈Ii!$660|nE"*˲lǡ6wN[ƚ_EVIm[\.'/|=|i~֜'_f'_qfnSrѺiMgG2pY#0?[\a;\~JHLV5Ҽ zJL ARiYcn*1i>m=#viBJ ;oY9>32&qĜtK}x؎3tsGRq7߽]:wj/@ P]]z]"bC(TY]Pگ\H$'+z^xZMEdZ'5D I#ivYբ|Qt{ʖ: W~3|n>9CU#.y'(]:mE#Ψhi=Wl_p8p3]7]!b\Ͼ\ }gYߨ=CkiY@PdMYBJ Czo?p߯;lٯ<9sG5]+Vd'&thA"= 3lZLLHHP%D"۶ٶmpwBDC7N޹S۫mZ_ZuTmb~ÆKg_M!›M%Ġe@aDF|Oi߳;s '.*H!(cQV~q[DZ,˶PYI#hʘ(i몪6ڵaF -R u$ELg>߂۞rXA) !-h G`|,$ "^mE\3hnc#^@'<39A@7=9qh!BU՝Zx-V(bF5DK%ǟPPQ@R D Br i.G[X5l* 4U:4S#5 ۊ0LKŵNۺO 9np?6ɱbD HW.#A]P~d4 q*+5 2b 2H5/p,'Ot]L#%w}_~®)\fps1tj.}WG1,nFmhUcBSUʥJQJ&-j QI5%C : ۹&ghʚ*J^.2)957+ 8{b w-}pz}O ##ReB1Lq B0Eu-!m@]MM3Fp9̟im)(GB@!,] '䭬#"Œ 7-g׎J!b&ea kfi !cBS-]55E%X159 E8Dh*ma: D0 q_F.⫈R~7@Qݱ͍F!Pqb%uVDj$Q('T2)Td6ccP\2*GfsAPeT__(,bgDm} }۵x+"_YƏ?{KnrRm믜=[D ܾgV]Q IDAT ?fl\ȤC&WpC0+A.)Q*(u(H5rej`HEsn GAI@ 2ZV, "-8G'v$$W't%Wn6YΗތ5Etď‰M&!혦doAUPIHXe!RÆxP"I RJ( !6CH%PQrJP__KH q\JMܹf,=[HػGwŏ[7"%juJHXAH!uA@mAqO=& J" QWcfTCNodUydE;PIS8I 0B"h &o}~vӉH!yLzQ^ov9 DZb߳&WvSpq1Ca3Ŧ1`H%@PJ "ژF&z=dKN8tTtl70 9UL:a9 T@JD A$1zIb;OP箯|wi#]; K٫@t Ș$Z\&ұ´ #!n8$dr2*N2Tdʘ1qzBkjk4)9H ..[GTt{6$ \_r{k0UV ,GH'H@$}?]c*1#!($HADǃuvՕqPD% EBPR)Y(AWp>Eܱ\;/!f$9wMq~.'-ADU],lK2ݒ6##(0Ic!Qm. 9swj_y๾"C5P]LiJ^nB 0 %ES֖lJI^&z|9NJ֫ <sF%-+B*9G5I2DGfQk[ThBjznH QIn {I3"M4d7S@2]Xn=-n[mq;ֱq=bEBk~r@ɹms6$ؖYTJqt,U)\GdB ҦQP+Ht΄J,nԋ{Lr4RDP:y^5(wy]Qm))$t"1Q.]uɊݎsrɌ9v/%zjG^N9زb6)GVy.@ !-6ć2# VX[\ 0$:(Z8i2BrJ'_|tGZ ayMMz2]z *AB A dH;vE:)0tLiΌ1E9v5(`1gCTB);ZO=!1F;t=lmhDȇ]J,[]6Oc~Ш TP"ý ngD [ pDV$1Hm$X.ƢL9D!xLZya\Ry0/i{0ߟQ)v0/0GiD0 *֮]W5߰w2}Ő|SgPm EjIZx~;GzﶘR v-Y23{! 03tq= z[F) U 7af´K?^?<͡H, -*F|KWA$͒=3*Bx֮Up$ k @Cu0XmW-Q@. Aty*Gٵd2*17 2]1 j +ZգSDz$$0U196+nYi_xgã3DLDB ?vDorz") UUCWk5Ħ@gxjhʿpWNݩ3 Z7<^CcL Axj^/'&m TF*`2EhZ"P^Ƈ`1BG{D7`9FS X^ejզ}Fl_py;{> ]u [wjybWMcF7D2gAD Ogt篼7`YRءuT?D"ɉe6ϮuQ~ ;U`yn[@D0* [LaRϨ1[gߙAȆLwm]tsڀMǭUq|_6eK]|Zb`ϝ\۵`/k]e<)?^Z[jt _8{JYlM2N rϖ|jꂅO26|h+~/ظ|&8ᅗ~6;.Lx?G@}qjխ*Xe}(L}UjUc0WiMImwҹCs)i1_~ 5vˏ_y z5hպ;q_W;JGLѪEFmy[߼'r8!k?ڹW/|<|k<{܀ѧ%6jϧB޼)d`4"$gEӃ/)?ҁK(dau6H:>w3+~k} :u_v՟0#]ZJɓ=ιk}3xgڞWWom2ڶi}縁WpgVSf1Ʀ}=WֿA$w\xϰ}6aM=bG:[﹫`w{t;jl' QbV}h)qY}eJ0tXLz?rϽKfVX$%cۍ땛~N}{b>zu2ՠWS]j\|JxWL@y*9u[]̋w-j2TY=fŝٚrsڏvJkE=w;jk%JΞ.͗COv-BMZs]ޗD y^5ˣd!DFpr:B 6?X:Ueym-^Ue $CBD SH}ˆ }{`;Vqx{{Qi~rw v4 "H!`K.bAEs[װNQ"{CcS;!T `cIo\;DO%B:lxr3W }{p~ڦ}zdzG\F ̜kΰ,{h^9* oT*%@(՝?7R~7!dkqDFޘV/1կ, ¶þv'Vi&Ž>$M ecȉzp=q !q#So~حcȉZ3+yU%v،;o9.z77il>v"e 4#獹oUF\`dˎDB0bZ]Ch{՗f%ݏm)}e./!4-qNC̬388]B,?*c77cԳp}yh'+Kfe0M wo,һ58 /={s7gĞv'z%0-Dؖō,r >˧^TlYYx!Wm{ZeV68nq9tqƽMg^NL{zұ=?nFX'pbiqSUAV풂0%qηFp)Da*ǡ9)x5NĔ)hRa[V];}vxp":DahᶤQiPu]UmK*[P&7<:eDX6' ǖ@vJ"ASaY(ƀ;a7m= b}CڛprW|YQAc>? "xqǜ )aYУ䣔,5wmEBumܸLu9RZU %H(*"8 A9eM۾8OhY&1|]t"ڶu̍'%zz+! w_u5B|?+W8cX!3iX4 Y()Z,%<0T8GUMzfOQU;V"gJS{u=vSm b5UٹTIɩǴ=憂ssu)e(WUUӴ]僇zkѕp="sbRvx8J ]POP\,fq$u5NpML.eKj]?v @]MM3FZ2t(pMP]K<)lǣ3%Z587Ͼaͤg䑆hO+fP +J"gt_Bp*͛2~a j( iK'TU1/ *r恃"J)jw7! PZHr8FDJmSݓ£jCQy4WqmnNn~G_B|LR214?z/?BޅRsLՉ5H 㒡ԀYH3}j^}>/٧ ao_?飊^ Փ>5mI *xsګQٺlM{o&~Qal׆/2 ͛ޚA0gߔٴa>7f8T!~4צ=\Y.)$t҅ lӯͬS[t]6Y}ި }y R]^N.E]8Aٝ>>G>^hYq7>hhO{u~cO#;3L^Ӳ.ݻW8熮RxKW^ d U1rk7PJ%*ƙ?_SO'@2a$??fs}.Iʧk<'G [pOrj^$U|H~i̬DikFmv};xoԩu[Q<<$;'7?~v]:“y7ϨSsW'Ŏ/ssw0J(3[zHJɸ`yit_GbӲ͟Z_fVJ(ʇˤc,y9jB~|ZIHˈ׍h񬙫uϡ Wgp96wݝfܞt^9/*ZfbZ2inT&|e׬]|[s+M]SFϞ<}/}_l[H=&Ž廗SZEl([_ȤYL̙=`r;>:d=?mg-+ڥG7wJsr;7XY7M}VYxɦm3fө{xâlxktֻji/U7ց(̅W0 *Xwl޽:,~E߭ &g2ڧƪYvi'F*-.կޣhi&]=j@ɍҵ-:wJOdDc -5mDvoܩR|e^&n7,E*Ey?k?-j쭽}zOO<}ҹE.Cm5KkyKW,CHQN0og u+P/:>c;շgw9j]=s2?x~WnUneX8ys@+?ytۆ$q7p:׳:e/wê֮YY?=aCj]]9d1Ko ;Է9+ik3ʗ覔ZzazCђn|ԯw4nO_|y7j~mq}~cg\u +əHsFx9Td1}Ɠo7BC iE}lۥܿ0>TttMVE=L,(umg(q 0IO1qUj;kx ( umbuQ c[ڼb)71rm! vUvr?aWMI89GɺsjҬS:R5m_r&'r*?aha:o;N"W3.,9IΈDEL#fs:sΧgDzz"H9,9/K؜) g>oޛ IDATסU}t~e|Qؾ_ EA.I}fM\kOa. mÄZ5&NL8Cr:YfLTn_Mv.))ܞ޵~xі-{_yy: ZUl* %}fOy䨱;ukh4nնOu3Wn*8h%O^§ \s&m)(+\| {E`Vt'}.4#߱kէǿ^؜6BtYu=4C^37TLtѥt{]v+̮ڶ)tfo"%%S:a`G&Lʅ+ao=-^^}OM)Wx᥎%Ʋ937Iyƒ;t @N f$T>/oRmbCN˫:1a@@Վ(:$[s=gl&{(eE`ޱʪlkg} +*Ss׮ֻdL+YQ{P^\QWs-Lm1|Ў5|#;7$gذ՘f ~]e`\8ӻY~ٝwN>kkYz͡ϥ|E-%Sl*?xÇVQ[gU?䡻.gSLnG:JXd4IZm2U4KKKF| ѪIDlܺc zBNN!mԷ?^Mr/X_L{uN1q毋qpSAjj]JϞ 'DBÃ%M{83=`y3\4Mр*ql俤{jCUU M,K!QHAPt pוRq\rgbU3 );۶n)D:ޡsM7w*o1eVTo,))I{n5o__ODӍ(3%5 \(\PDCQMq]f@D""%>qs zp%{F0 s|C.*^mJjU|AABG"z^hӏI.B۳#IK T'okvR(+bOAMQJ(pH)U$Q TPE*:SLhH HJNDZSSu.Xi UEQ4Zq8OБwk(؀+pwR44!(TQ-{,a@% )gRiIE BRʤR()-;mB_g?m`W3T7ƹ`B}>8 8MiDNK S`l*RF]Bp:f OHвJ$>a9"2 Bn,؈0oԧc!LDaRtt*A|~?tHl2+_̲LTσmKgjcw "~?%Xͷ;I (@JR@J%Rjh>d &A$m7R6JIٷgmBcf"bSg-?kHOM7p7qY.k% 'rIABA2f> @@5hϱ,35ޯ;[V䪬^?0hXfاԶ3XqOg׏ɟތ]Ξ6q3}+#+Pp g-T1!>o%wt8" QUײt[ 0sދm}A/nqS i?ct*>6umBO^d/jNS{kCv6䶳6?ufWUbriV+]a3( T]wy쪪id fA(峕Cڴm.G(dZ[8ų$B[xz\IǁȩnF̈I1hw B,H7iYEEX1ߖ⪴k]y>ʦ7rƈnc{ٗv:x;9}O ~iwMx14wWw|(?|wk@}ǫSy9ڠ)ȶU>iz.׼⬇GqDk/]-SM9[,g6nu)H7 GfBW⍍;*X:_*,Oayѭ'_Mm,nMeS'lWוϿ5=뺫;T ~)7_?G"y oibV9p3+_ymѳk3s/>d=:?l;5{\Ќ^K{_F:@F^zF٢Lo07v:_즾uϹi'X;Nݹk}I8ae.፧Wkz'بexxOo=/vS_?|wV?.k_z.~~ӈ;6R/[*^Ebd'jJlb2XREdE ~aF(WHh R"R8}IDѿƾ= %vBĶm&4{׎5s>CNJL*Y6^[y=|V׿1{}?6M2uFL.Ôm'<)%@6۫?U8n[[2PU)<76+;JRQ!XVQgTUUUUm;OAFTU=ڡP^z=׶wZ_|/H׼ΛӖrJ_y/ {k񦬜6o?pmy;uͩg&J-rǘ_lv^>.0ťEUxw~rՙZ|;\?j?'}6~steȳ `C|:ɐ.RH%wl]M %"J) p҇d -Ƕ…@,^8qOfȖesDB T^|絷$r9p/{qwZqgo1nLa}t~쇉cQM|Jw]ek҇^y> Wͷ\sr(!g؎qB/Юk _y]gf%r}G]_h7y;'"\f |z|#emg>4us_vmb/,63|[ʇ\=zm75y7hmE_=nko|_Q660n&`(TEQ%9b[iڹnI0ejr3_lמla( %SN]h3u]LKKkܸ|~] >@˕>pE߇̖TW e!!{W^~c̛  G)0>rsV-øAc4@u k܇ o#]<rbۯ`X}m?._ﰛM ]+ݭj1rgdiĢǂ<n*q%esmK3l2}e9h:dݗ/i @B1|nnDsh"W U44t߱jLyKޑTJuNB>H׶XW@wcﮭY6tU 8gR=e3駪۠TUV9zNœ,̶4Ӱ,GQY񭎁2~B kyeK;j;$4@zjC.XdK5;Kw,ڱv]Ѯ Si.NӒ4 RbB(AO}tyqq1\QĄQ!O@ ف: @z̮v~7` Rl@nv!ı=ǁpqycӚ@ Q.,)t4:qc\ . F# !c!̩sJo{M<T[gPͤ?D~a|;r\*hƂ.4]kP渖F6;mLsrtU΋*(MԄ$,Xxͦio߾ 0jt#zi]7GDuPJ`t);A#`BU򿍠ZAB#clրs\ HJ@)]Z<)j܌2"( ( E~}{ 8! B(MD':V?B6T ѧxP~A,1bڵNp!׸]!X{^iE5PT?>#ִ [ȑq)(D QIaPZmL2Z1˓FFMPU fqk M_D nu3޴nyqQ!A LkѢmet%i&vmx@B\.ڛP3(#"ٳ,ƑHC#Ad !Hs䷈1r,[s]!@(mO4KG͹&$B" P!r FJ;,WR^Z-[wiLʸB9jFPJ@T)ҨCiqSo2,5Y}piRz3:e}K1!n !K"eط07&AG6W"|cĐ3gD`FsxӂaCRH)!% E 3%kWorHls &Rfq]JUkduD(,ά-{o+N7" A'f*ȭ{3(A8$!$8*hykzq s;n'"u\!ڴm.%hˈIjL@v8j /.\-f;\Dt\0ep/Rr4K:RXh.рR@"" @ PN]*5ԉIJI!*TQUPwf~]eo^"=ynPw~_+_F@[Q/ݍ\ܼ[Moe񊍗?v~7~%~37ւ@OaBWFɱ1]A{ye(*|XrCtӽ+kqm?]y?9u]ߍJ}^7ߏq>ar<}?_R'q=yԗ\\tQO^ߞn7:fe '~v7Q Wr JJEizˬ@ikZ~VҞ$:iQuw/r[6yk3m,hl_lJPy k|+#`UI!TwIq8_K@|-cRJ!s9gqιU$>9\U( U")(jWc>)\`屢U,ʊKDum3\Ymve帜sqЫtea@9imӯENn8Mgq򤅔Qon6O k5}lĴik:XvUXyv٤][#Xm8jˣxau-9"Я_ց Zc}_ ,1-#wn{<))s͸/」{\yOs&FȔ@( <,|# B*k2oQ:V]"拷WrwRk](a"g.ŤL{bD 9 G 9H.%%Cɐ7/-ñm)}QrP)[PT%ppKʲ1t{EA1+JA!.MINuAtSj$KUc`NUUB+x̍jdsh[|{u.(E^u~9We?@WLt̂ܟaZpvyUn|_ xC[B2) C7P0{ xLÔRJɤ8Z hՔӁ30,{/8wض֑r<'}6fv,g&%wb~yc2:{wĽ$-iک?=wŵ`H#Ƅ˹rq8S$rgmvO,~8Cjt5KAfRnB a9Bs.p!O4u\;RHkg !j*!y-⽿s}Occ[TT|xXE˩4Υ$,6`&4$X<>B<]o݂U4tLh-(G _Q1|J50:ntmA4S Xvϰl & ײ]] s^-3gSs'\ )7uێn<,ǖCym WÉ"837zkct, (r<$`U7ÊMC32zshri@J$15b3 s+4 8jT i1iv5J&!GPk?n/jՊsQcADDkw!ò ]PB P TUu?4>F,>͗TnSV H%I@3M%P3F5u𛱦STU5iPRJ=K)P7UNd$\_v^aPm Jb׆< 6 Oe~{'{&\Gela?r޵`IǶ_!z,CQ.""E^XBHᅰp,U=ylY" Z Ry`xb t] 8UU"U(}3)n9  h Q+LIP4UQ *UP*JЊMUU9 uUBDޠBmU (*PUJicG{6g洉4F)JQ[Hb(TrgMD)JQҟӁgH)5]C)d/*~m)g)šRaM(Yis RT sҙ- D R$럺s_ou>B]UEL6۴BOWHMWU0 nጤ놏 S7LOSf8Ln[3wmPnǷ[Q4pǮMIL^M:cDO7Tbyl9'ڢf[r1Og/v^dtg|cZq灁ӸG/aS^Fz#m?x`~?h|zf)<ǧiesk9GLBdMJeR,[3 vijE'P"(NE[~%K?t)]Sf$%W;isǍ9mG ܤ)Y%KU^ַ)%MӀI{S򪳠 zzcOiQurMXc= ձ=Iɹ_N\cWlD?Vj쁛<{?dynϾҋ/N\^s!Ʒ>DdQR?RRQ D֮-w dd Pb/KY]"wHL @M]rI)-PzgJ5pE# Lq>Qw_Hi?OzկbVa\qa:(JQ%@s6׭؏YU݆îz&ɗ>iD)xvܸ>zt}bض]aYށꪪp]Hוgϵ-3- O0+ .qs5\dFl}'j:Z޹G:拧"o NJLtn?ʘAOǰ.72(E)J es;2:D +kLr_( Yճ'K\%Qġ4a[xzL\B=4k|*um8 tT)0e\NQnPfM[Emn[CzZ1'h"~,x҈'Cm~ap}{ö<ܱ١ ]FwCD)JQ:6xZ<1>WTHҷG;~Vxڭ(El.Tg,%:u#]7F-o Fbquc{хQRՌGXUQG)JQ:hkc#'3Lw+p`*R_+rʮmUk$Q #`h-=-hxYTj?\X}F*J ՞U V6!j<"3KD$zaJ I)nŏP9߽b}5^)5ňG?T)^HOFERI)2~ y%Pu番x_BbjHf"8&0R<{ ADB(jZ<(Axi"KT!. += &zV3< VQޱ'uZMuR5:-%XZw$զT! i{jcNjOVЫĚ.%Pe 8#oP#L\"'@EZnؖ5ja$D@0ϰ5 H!Eif^'*%p j\4R[.3 v%[a'@^딜H+hԉpu1U4 +"-$`/9 @T]`/9. tmrgv{k8"\W[P! A TJ.Hču*E/;UTE2A5E2GQU9Gv" oPRHF6% 1%4*B 9 !DJzk&6_Ed$RȼWS@am.sB+$6 & 2x Ý]݆i$؝UkU aWbe ikDQ1 @PH F,_]uQC? Ek^B\צT  !1h<رB^LhAMw_tE{5U K1"XM| >+\G?>f) lWVwT^ nBumDK_ޝ0 o__ԺM*΄QW$9fQ0Ԋ_ jN#AMzP&ErU8FT !A{f鰁cÛ03Zlu85E<B'Ѧ.Ч{3䁙sDBBR ױ$5 GHD&:4i߭nq^S746($.XOvy3'nlr,"BU!AU?-{*<#` {UR ׵GRLuraFo5'jWfORaz-ݱ8UC2F)؞ ]B\hL.8!$턐!!Fhtu".&.ӠPXdCĚfwot2OpąI˶/Jj2.л{3AfkUH C+{ NE1Ν=]馻ᅮYzW]aOU+ fY9Ə9ms͜bc^JzLE骼iۑ" vw׎5g[TVKmݣE˹\.8kfs.s&K!RpRH)wnθLKMܼb4.efm.$JL$_Kl[bcI<&)GB!D%|6ʞBr. " ɤ+R )CBT+k4kPԹ$&3ؼrOLUVϟ=ǩ+WoZJhkYmشEMΊ -9b߶g1Ib`5V/X]?M]1tbcіuv잷puZvVնw*wR̀xjaUWiRNT.ݶf2vE6٦?~ɍS.ܴq3o*lڴp.@)QHQm{/M l_&oU[$?MS f?Yq˶2p톍ltSCjPsSq|ISf(>vZAaUM _ص~m~y+SL->s¦90TKk\_U%ڻW}B1.\vs4uΫ/$qEPR0Ʌ<\흿(75+jܼr.l^i2v(XY,\Y+̜jgfL(4efҖ9)ܼz Ib4yQo xlRJ)rʅrJjY+6%fdaIivZaQUKc&MҐ3F6:r명?997g x/-1Ii~ptUht64MGK<Nܖ9I )güWɞ׳7:{~6(&eŲT"snnpFPE*(TVu%gݸu[`{VelS%%ܼ|o V?99VY2NBkۮc; P8->Nw_z} ˞mk}{bu: ߛO+ݞ;±+ 2S -y+i3V1//-"|>$؁X&2oԧ>!˷NZ8Vv `[۲cY}lǶ]8;̵c:1k\Rq SģݲYv7`p~Ѻ go+J}:c>ҵ;PeG IDATWXw Z]mUVSBNuC^7}&E>Z2e͞^!fVS#Nf9iHRu{[q}μ$+z郇?vO]_Q68ץU(ܒd'\0G\}zΩF>;)FG~_X~Ѧ`rf2.9wUYbyvE߲of,q[:6sm I;7,މ7W$sލry7K^ZӃrUpgvp쟿jۥԬF關ջ|VuKM0;wc/ծ˹IzgL\?oiC'/H83r֢}zDe?'ѯ_1  :m#۪?0XqL2o+ra}ݲyv{T}>s> f%"PVl>> JԾ/\:4Y>u[boڻ𪊦?{MwA4ib);*R^"H^HGzPPBB ԛ[OILPK wٜ;;3;gϞy$ZLSvGk[wWc_6 l9DhӖ|tdb!ۘ?&(yn Bh.Ớ ?nZժVLV{ohҲi^;Ͽ}vAJs>ɤ{AlU-KZk;m@YnX3K( (JRi1V s=[ԩ79׭=.=O{ρ4 qJ<?"d&߭kDQo0ƢZbG%ԯ}7%AR)3(" b@Nh\$VgoЧtޫa, &mԿU% (I`ר[!bR  gJ Ҡa!r,{ |jP356`K7Pr%"}c^+{ޫ9 O(JwSb.l;t1Ql;z]xʬ x-Z_9O8_0#ẕ;!OTvbfN* /OY1HӛjURf2`i~.[)n߾c,`k 3$v"fDQtfA4wr-|PtRfʽ"}ɚͷ5 sNQY% K"sEo-+x'lR+1-9IEdQ{pIY.3Rܵ`OɅlR6m,ܯ^/,,pseS#@NNz?0$Hf,&ؤt{ N)gd0# BD#?Y.e9!\0`wUJ6K GzRz*Фy&5P j>W%> c@0N8Xٺ32BEl5r(QT8AiIsrb0+U]]P7gLD$u"-cm P_CiyǢh}DjeƵOcI|4@06y߾ Qq!F?K% D?},߉N)9{IWެ^i%vt$kP|2"Scã$tzo*‚JN򴲍Ba{t 0)l( t/V B>Lld0WTBSW)9E߿ aQUαvSFf~XMC|1덠qd iJ/wdr 8Tt),s]19%ڪAn bƭ6Ϣsq~)HBy_W 7WuڿtJrw:H\]$ƥAؙU4"c Zv=ꁨ qspuVK &b&ԓ>t:78٥q5׳箥hnR򝨻>2 :Hp&]vNZꭻ񴫏'j~2*DP.MuFUs%uhאi4^ŭ(ِO*Ha+ ~d*&Z%U27ce[VN2 [#c0߽?GpRvjꭻ%q̡8P/(k*<RkX4*"uqϒ Kf;7׺hiQYUځ9y^~>>Z ٺlV qs̙/(k{9>MP~*rJ '[UQnf.r6WJu+kNVѺ1ߍo<[\VT1tqvuĘ~tJ c $ʦM(IbsC-4޽amBCcÁc7MBªb\2 eOCԣ{wRm[TJR%u4ոdm!!DTLFH:QPXoT֥#ݾVOPa-g߂H9qbKc7 C>kT)Qjڶ]?Dc.ðwfX&/qL*U*}~>RfX.X'brei"AXlӛRiFP49KUĜ4 ^yVei"W넥du$ bxjYbw( bIĶ r[lplyFFXr)X9兜Kʉ ť*E6V_(#',E&?هT-)xS(?__;Qn+Y Dt:W/ UTfNvY[J0b$IauI]=--.]eKZط"n3iM٨7ER8³v=m'/2VYy;^{ByƳjz7u`1Ewtкxj]<]ёV>o~[ș' Go9|oĄS:2cpbӹ#oG{$p,SrQ?緟1`u{^lؤ̥mh3݉{"{~ӅnET#Wnͺ#m+ډ"_nr`EvN<~tOs~eɏsW 'd]T[hN{y d/[4oCoռEظ~' _;ysI~5N\`=&آU4g~QX9gM:w=qشͨ\xu_~Wa9!OoH8Bh!!]г9)'1$10,˰,rX2&~}d[ױ6zG$5ŒfV=ߠ… Ү Z\:r[#{?7{AG}ގܮ+z։$t`֨vCj4ts};>8*XAE?sQAjA$jgoÇlf̘9m9WOQ=˗3ȋ=z؉!u&v꣪} n>i2ko9]/1 7$m[4mnĞ}Z5doͿ}`fL]vMj_݇n nQ"e4QRX~?ڑqAvΞɘl_y,o{updtMպ;Lvqssp8"%kOvѦT/Vu|9hW+9u;ܯ[TNĤl7mrs('O7y~5s&CSJlm挷|ܩO5uo6W2dݫk19FbQbjΧ[JO$htJވnM"K-x;~DJ-[pߘUv@I뷨 eI;s/OJNk귬 bcG;Wz BmAS#@H~p1S/ > /:M2B$Ԑ #.Ŧj# \$+lW~mFwx۞^h7G =Tcha;כ&b=lh=R8k\.1!jV>A_|m.CQk5:Wt)!"RW] m@WOď~gս9C1j;k%9 ub|9K5:۾&"-Ǐ8w^%˜|/y=?e?nGk5f}O~cC* ~_w׍}\>ʸQgOo_kEV'CPɴzUtsFxka:I|+ǰ4jhƍ:{T?%(L#;Z=~O1lR5>akȏѳpuԻwk_v2$%;r(NXޟД1*{x̋DwmQi,YZ#EHpH?}nĚMĜܰЕ=O\@[=G^}oJvyǹ@3oXe'?4R2Jk7۷R~éVߓ5RFD(~pˎmYɌ:QۭRu?_N{oT1l(hwVBmz}9)mۼe/.s%Gc:vޡa>U:wܧfD"v,]'u|fi8:AhS~/5$ >d87I;=?JUPlX=!!*YzC]\Tݎ nA"Ű4x;S3N4EB(Dھ?~%W1v?z# -8(1("IFؤi'HĹ빯nUf5mhU`UzUYm {/-|$XN9a{o/{>t>d1j~wm/;O>h^5yf +L`3ZݮVbɆ~RCg9UՔNؕkLfK]7mϘ@ vl ;Ҥ?dԐrkR3WmhyDqrZD@׆6`4 }!ce xBXZ LJfAh%KLҨ@ь荒BhJy1(T V27Z q;hP0rk̻:g2D l4+% /Vs`Q* Hf-(*--ݽƹ2BȤgJ h Q)X0-B񜄢KQh.;eA}`2@dhLd)b41b5*!@D$R&QlzGt6vKir,ǹA4 0/@) F!ěE06Y(Ci7uhr˓u|l^o /FDJ0 Ed~xI&"&3, sRս THm Do܂-lfo;٩\B ś\.5 Vv/qV+TSY9k۶sb.xbL&dvʉлHm l~к='P}d;.;9Ǎ?4z±T!'t:f W?!oPIDAT?_zS2 Ƕ,7oUKj.i{OZ "$v([ʔk k0sBmw[oqhc~iN:8uM̌\'Ɂ?͙, @3mf\#| ñu=kǏ?35ɰz3w3s,N8{MGnn9I@)r^d^8wKX1[%JؾtvT/^ɘ a.Jg?8f1-݈ʰ$A'Za-~ΆH?ZA+, :~,IEy(~So;IXq^l6g궴ص{\mHyȠnǽ{??w׼s&"W\$7)ұNB+bM'55K*CFhyLhPBH4BȘA#!#dW d)|wH5YX sڐVޖ>;rhr7M;eB0&8m cԪq<̾[{l>ryBȨ?B.񺎈OZ*ۆ0Ƅ}?1K[ỲQC0rIJc8K{g?Emh!Dq$/zcfNTEǖӽiWrnҟ('޳rV=(֪Wu&c?b!P '_q.,<[$+f5MKP7o:/f~>}1Z4r\"s pEvwn5r0Ieh,޸M})ڂy;6t՞ZC@f +fodMA+p;ũFE(5uүDb9]HaU='lx~]x8\;ֺ]_KeZcZ9H2QLa3%,\Q@ B=@ѻw+>AuDSA" \9hX4RSֳocN_\0H* D2'jc(o{:$W`@jJVtI RV>^uO1JVآHNRHT [n~^M:'7Uֺ8x E3~n4{p=i]1 9QidrBd ё1Bމ{w2;P !̆3-_}W͜!z;4]朡XژwN[:U/JJYE ZR[ͪTV'{BrJEr 2!)\"('\LV[B+qJ)*nwrV*B _&?jėO\Xڶ?Aݜp~& l_7 =| 1KKBwB{Y?uI_?6XXv뜱5ij>8|Ĩy4g3?5(2rΜG  M*lL[0d}M\:qgEd4 %% S4pw}XZ>YcR+'>IfRVt(.`C[}qMf}+Hi}!ŧk:5NkYQuu>H_n{Á;9f>V&E?6n&~D7RrG]WjOФABZոi㭿rT+wcZy,W?xqO+4=\w!lG5:S֠͆oKǞs+.籙E?oNo51[cEtaٯ"-44<.}!gۙҺz׮ݼ%30{,WJ>\ڽˁv=MU+]7_j,O\hd[JqAՂVKKY;?/e'沒eAvb[ͱzouPK?t!^c1Y)Ǖ;tJ-%tࡪ{.EtWX/OG/qqѾOt[>낢qI,"hXn ̠pқgִ67ǧ&v'3xB a5˜C=޼c)@Tjb_z& s;i3o)Cm'8 O8).rSHG`՝ hUX`)VoMS4ժs@Mz}١Z9^N.+nXƏVq.BlwPwLYl\HPN7VO6ǖU:ϘԿ&fǹc~cHѾ&6jtdw-GL ,!@iף0imw7k1ՍacGÒX"{l+" rUҼ@(-?yq6i $0,C`q헇jqԻ{>ln)R!Н{`Q+*s5 OfYcOO Ewĸ1 ],SkoZd%I@∄qαq{^"/ 5+!E!ݫ5aٿ,8,@A2%p2Yl /(HuGPXZ ׭pEHLt4L{ MLGM9R_ͅ6]&izߎrVu[5,4ƚ$:}Dv;Ŵ1cBӖ6RIڮt}f|,KG?trdf"bZ3B")}뼭X3fj+|‏Lf `Ϡ 9v{soIF1]_"HPsi̛Ll 9}'Q^<g0d oz}ض]ǏM+Y\9յ=N=y~2]D_ۿn˩>q|&úv9bgs`vt\ޣY7k vXJBGKXՏ}iI2g.w4jq/ߋ!R Da RkX0XhE F*e0 g62'Ad)Q)hQ"(Zb zҰ4 HEcKe2 ,I`E$^қ$(%Ҙ4,|Xb Bш, 's,-F3O1R.âh d2 BYā)h"y J%ZFK"]@!LQRJR!'D4rd2#3d*k0Ys({P:S:RzlNpBulZB1f2`d2a0 E'ʂaer9)_-(Y!L|bT F@Ӵm֟sfI1 @L'2 M;;oZ(kQ~:!  O-Us?@hVF kNv*7z~Ѓ҃7gmDw"B]a XZsV1ØshI^5^=~;#BF|(|5 !r^M\Pp#(Û)hyuM#sX2,13y= Bn'6ij԰; ݫ)=i߯],iuuUUվVݴAv?{k,[`^}' `)!7othPΙ&RHZ_UF4Ύȉi&yo硽qeo,!b{̵ӛe9O*;:eqn^_NWM1&mfW|skQ.mt7okrm=<:y ?=yqf>>]K̯{~$e_3i'MLǜʋ? >`c!74;1$sv7t`kD0i;vKXN8/򪐥4^LftJX,\=f|S\H$ϫrL.`ڷU2 `L&u>T5n},bZ\ Qch*۱+ok>`';N sIyR@*'rIZط"nci+܂U Vq-#^#VqnھB-J/ʖTe+x LN|d';َ})(IENDB`sqlkit-0.9.5/doc/html/_static/images/filter.png0000644000175000017500000006214211533717256021010 0ustar sandrosandroPNG  IHDR[sBITOtEXtSoftwaregnome-screenshot> IDATxwSUǟsKzf2ޙa *(*Wl^vW\]u׆+k^ւ"(ҋ^2)&dӀ1I~9i!U>`{$$\4$`0]+CtDsղO1ǟD TMk~t eu6`04[֭{LA@UZ`0䷥ :d`0NO8TUy\~$@Y 1#y#zhƚ پyd0.LUj! HH@cBJ,˲ 8<P/sfԶA`4ҙG5w`0N]pU5w@҆+{Ƚ;{]iI۝뮋r-ۥKD:Px0\ "8B'yX\Rϫ{Ȳ٢<^@EbՄ#[SNXO>29x  TeT=H*TuMDH(Te8=e "d" `񨬜=HNQs"DE(s&3<5@)O1[drGͶyEm͚Z{tuF->[3R7o>nptrA$$UWHS'{5`8K$BݟJ3k ㆗3nͯV.t`;f'/9ۿ; Yg,]Gyʿ$2ilIU<<6FulW:drĜoy.ꝈR}f\.~%4ɬW!ꦦM *c~EEq N4|5k9`kE>.!Hˈ6I㼈(+jBV+n*t G*%45pyn64UUXzSNչ@׋8,Wѭ5E@"`ϘjYbOK,qXL. 5?6-wy\â/A "@U 6cI8MiY_e; ˢ߱t颯i)P[?{袄l*]/( /PE2YX^׽{@mmI,vdVV Z Cy׭د_nܵgᱱyeEx\[YT7[&KmUOmebr:ZJ,.mb}q}U<M iDU~y/3,6@ylZÿdǷ?iv׌)>Oc+ܒʒFyҿ^a<[J|5՛6_:rGz6?bŶϟ$d=/IFEsX|ℱ.`4Z(/f0(e4a\}`=+~j6+ fG 8BP!C>/C4lnDZ&[.{`x>'l6dEAb^g1]0M5|,z k ^I~ Qt4A%v`$CUhf;'RRӷo4N06N8Q H%gԙS,*/ϯ~my(x{K${Έs&dsގ6j.㼻/͍dk_6#UVʨdqT&ԯ1BETEnR~^=Ʈ^:7::Z4G9&̖.K.쨪Xo-#݊g9'ԏ:F~[IFp u+/ EJ~"}sZP"1] 5U%#1 ^ Z AqQQ)Mm%qxAoR(&Ѡ\ia;`qZ@3ZӐG$)-5)raQN5]QvKV(oۺDf !TS_$)5Zu:Te@mMh+`4YSHIYyAAQLd6zx:_ӓ@HiRUu:˴w#"Y3bTUd !h2!њG{}ҲrrjFEY#hy :(:Zu:4`0zŲΡ7ٲN -Iq*5|LPl2""RY=T a`0NT&ꂴ`0L4  0d06b-9d`0K`0m&`& h2 F``0m&`& h2 F`ۻ.*2U :#TyUdE?e@n?>ݑז3Uw)P{ .X#F{,7zgĈϽ۶5NiW_?xW^zz F;ih|_?V+ ޟ\%G*/,3[py]~-y$SrIo S$;tR9FLHQSf͚5k8jj"R O둣F$$1y[!<pai#Gxoч.]ZiwË/-8v2&$kjO-\9G'ǜhشU':Ҽa9v#Fqs(+PX嵙3{W:]IUw(t>xl|']]b\G9'C񎁨yH,Y,66f-YleZmO>]RZ:tЪ*;xƍk֬}wvlaÇ~wUU^3W`f>i?2"t&};R* !toY!>Pc9ҋfMb_S8.(&GjZ\׸eYVC4R/k]=/^p]_MTK /Kَ/톌^i@2jk+W߳NEb^W^^VWW111 x>^zB6qi+țb3MϪ'X>ϲm㍏)| _rČ^z÷?|:RSrW3-ɕxs̀l\y{ލ/ Q~UI@]wL7.^e~࣒^6c7#{|\KVQOO|îzr'\;~['ym"Ek#Nΐ;%'Y7uˊ) @|^V} V{Kw(*ʌIU{e_ni?8[gw}omZ?#@y+o!eMin :2WA$xO=\A^AtL_UUm?ߢ>BaÇ\ZzJiޑN:j?1:6 +Ի,9{Ԃu?.uf-eǠ[g/mUL.~#nL2?,᥯"@;>~vTWZxu SŸ7GE= ~W_ #nmʵ_=ᒾ.&m[+f{희PmY(^I(K]7MLi,t?$WV$]P /{] ؁#=6H=5}|dN7Ft Hóͣg}#66ZQa#p:ZbJiMu1g2dtMy'x8gͥ :Yvߔ1)M;wV1 8xQ"32ǟ/7XJFe-Hn՚}Kvr6!GO ko{d͏gܐ `6f i~_~Tbus}Rm,q^wajdI!"/u̞AO}wGIr{D:7.|usEn%ja-ݢG!*% \Np H$H -0:AuUν+=Ѣ~r++k= E O1!oyhO^2h=+ ~"ȣLiXKx&7g2- ] ,v5)Jq M') 4X8;nz:B n)&K ]؄=- *~ʱZSX$Mlq*<4PT"ؚk; .U;GQxeHK4t1]v^YNM/BJRPk.qyCOq6eKiUVm[_b;$YvVf?UJU*UV&IS<#y!$-=5-=퉧6:,>Fz\uz_4Ұcї_l**-9z藯(iIxRiKe75y{өDVmO|Czoea␳2 J>qZGmTkZt;FQ+ I xh!5NYXϯɲƕ,/\~=ToϞ={fqW:z`_ݝ|fw**UU5LZS{'+*R% ^_*,(sݏ<:k9`H:>gƸ ?hCr}Wԏ/h;SKb^wEbϘ4>mg̽|o?3HVe5W:?Y ox54'"_ǯ|1icf_2-wcHnϫ +T'9|Ǒr_d[ [ NhRcݞ%<^ckc|bʅϼZOb_oxGA6shȣs=^obbBii$1,Q@⸸ꪚGdjTC~[IFdEI՚dG+|gĐ(򜤪^EI0DNq 6-h76m|YH,q/3o:z:e_ rq\O<|cZ9 @`5gL*M?\.y/I111G"$\nX-:]qfs9] [>K\ޱ=b*Gz{fA4l6w1r FK^}ittgXliIF)EDD3sɺbTih0)R (i6汱RL/...))qM7tCjJ@%Yի5뮾) IDAT)DZEq{=EQJ[ <ۊ|5URP?0%-Fɥ -,trEY+GO8/)[b/li)>yKs 7p=$YVdEQTER$/ DQ؎ꌓUKꄝCH_BsRc.EԆyk!$򂐘9tĭkWTWt}3{ڿQٽrch;<`DOCvǴ̻dthv2z'٩9]g[G,"mA1fZ/k)j[Jײ8shL4  0d06D`0MhL4  0d06D`0MheY 턐&30 Ɵd06D`0Mhl7ㅪ*A</qA%W~w9 6Μ@UNoIbD T)eubPU15#Խ!"A3 ` A) $ %2 @c@` ]&+MaV̾[]|ᶒz"Uݎ򂃫9N4Ǥq'T$_IQ޾][5U8pB{NL4;EQLMn1[) 6ǠPHt bS^CE& Ӥ4 d!ˍ$ jb!1?@堍(dgPQ;oŦY|tޚ;ᢩREE-k q 6ѹPVo߼hOJC zņ4N_gĀxaDDIuo,0 #>6@Й > M2j]9w AD !AU@D1&YvR? jb@PS+` ]؆U:X8Ú D2hiP Ae0MP/) X@/@exVpHv;HAQIzjrGE@]$Г"e pay@@DJ@1Z V 兢SH뽸` &jiB6eZE@hH51"V6jO(-3o $T pp>3j@REi.\E8±Aʌ٢ \c'zw߲x¼g" #!U䑠4>Kdу`l4@, pG֫K}0ݧ@]C "DK%KWμ]B5߽7RZY.rd?jóY=n2 Wn[6eOW/U< D0QM%2&Cxf(o+cH)?m&<(*֧_ Ҏ&m~ #mO'NM /LL=E0Hy߾yDL[1y6>zU͛g `{OWyni- ?#*㯚%ºc7Y~@_O _|8K+s4Bԡ^4=ʡozQYF&#DPfX뾾:B-㈐f-a7.>8Ѿ1@JO3t8ޕk@IyFB |2U8cU,IPW EiծٷU'MjWZ?GUs?sa\Tc^}Q aѤ+qs^messҢm_w{{~Sx󜺶 G#>2%9ER Y1gz]1湶,hӕ-n;oh*w#TH~.b q/#{Y3&􍊮;(>?RSͿ33o7)"6vN<۰vF§ "Epec@UTWӠb羟nU cO4+ wkFZ4_qCU g͹oP;?hkܢ;&0$p5CKsD Soy__[C^Қ~_ Sx3WIS i̵W xߦ;U7i^'?gJ,nBL7w[55!:sGdՕUZ_g!<0AfO 6P /.I6Q ǍE>Q7p#BH`Ac0@PUrR/=s2GY*׮^1gCMzͧ Z'#:gD>&ѕ^|;w>9IGES_Υ  C2@k}9YwFթ +JwI%{o㰃ɥ.M>Gy;Ocs6e.myݖt ?>lIs4#6aڏg66l߆^YoaRy[֦ *My Z ᚟i3VpBm+.fTZxaPUڹ,"gu(ߟosg MlF?]^e8(}usU+ ACfN8Z>q\OK6w0Q_y"qMiֳls[3ycOk4m!m-zzbm}n$"8pnX.̳FxDPw}vnK-@a)"bd64 hʌU TUۿo9)[?ߧ.z3%un6Bj;u0_}рEN'+7ȭX̱J~| 7%ț`,g̚C/?^7 k '_7=ᳺ1\sySfvіzD)B5g$z<(@}1[':gF|nVSfssTXD؋HC}4gV'{IQfCpl©Է 7mQ~[y*:w㟪{\қxwʘruGV.z˪_܃~q}њ0 lرcn5sT|y uE =_r4g~Q :Һo3g=vfpDzSL@6ڡӑT>ڍ?4'chjS Q%m>d 5vo|Kug{J}ߍgFᙵ~m; =~QߪQjUǕ۠4Jx ӝc[S^[3fb7m~k_?GUҬn\Y:LΘxBHL°~k(0Ǥt`dey]߁÷h6%vc^P]rɈ1gv-D!=pM:ʱAlIkL+/əCGMܺvEMuT.?d䤄 g>&{RN3XVPZamBoI1[|B'gfd Դv)&0<^pb|Ơ1ONF<Rڎ %L4Dg-zyN~&nCtaأ?"qA%W~w9(X9 d?ӛccRc2$y8&@TsWSl."LUQS [: ·&|9=w [&=|ޓA$8oK@Fu+I`L mx,!pĂm="L"7E: ъٷ{ˀ;[DnFnjTu; 8Mckԁ#7a|:p$uڼh*(o߮-*tCmopJz/Ghv>^b j)FcPE  K7 #RCCz@Cko/Vc0YTj\FMH"Be:` }H&#k "P9h,3 [8۴[lzGIfDT-SGj;REE-k †PVo߼hOJkǦL4;Dq2T:}D-_$Ț&׵dH$B[UX}0A@֛(aKndzο{L4 HU@lφ{`akӇBBH-tp$¹ O٘Pa1"G[aG"oDgpYMx_/`ТMI? Ś66]Ԛ1< ""*h G 51"-ɐ@PS+` ]؆U:X8Ú D2hiP Ae0MP/ޅ-<ڣhC洁ւ)x^\ӈafDTh B۔i E"!ƈXA0ڨ=0"ouMA0@mQH@i.̼g':G=~[l&\E8I8 ll}lsg_oY+hW@4PwoͧXS )#V'BLuconR7|]:hms J,A*  ^q[촵M<,j *&B\*2b.=?b@ ڄ9^Q©6_3.vt,y݂/{ˀh)柏&\ԠYw je*\TѬI""U{eAdߣ/,H˺rVnXAqn]%/\y~GQvkom}{ |T@LyT/zە {>4;-!&AkJsrwiAI 5è(y΃@B@RV~-dx1f.GOFP;T ~"A&fYMuOk.;E meL)tgNT Z;]Ujkj%' >{G2GJYڕq I>{6֜iHl ˅~犑 ]W{a a{,g̼[9^ƻK([6ǻ o'47!q"|>M7Lퟞ=;fnkR_sbtNJBJΐIUF~n9ZJYsV7} 0֛>U9U*#*9w$ր>ˎR{UˊPeTqNi2WU22gThSHb?U$HTk#UMgm=Fs{r%T(1Z*XVQ vT0خe .Mo,] Y IDATfK@ePe ҆ץ4C/P%>TzQ'a6 !T&pԀwE[+V MO`3Cm5U).B2ׅW=qόY)u|͵<騧)W/gf!=TH*{%N.uyc9r2zIIK#ўUt FT*TTFP"hy2* 9?ޝ=1-_D IgX;(WPsT2bdk% jnG۶~KntizmHO3p,l`!ǭ#*u;{-j@bxN_wvPxQFv0ة@|;W`ty:#b B3EI{n="ogG 5F3Chs Vhl1lÕ2M՟}7 !òmg bQ)-TY[Y/NBC[* =zI>1W.^Q{,IK718Z)`d64 Z=XV fjP$|DQ1zj/KzI{uJeq+bjuobC՞ºߗr 4 ބ S}ZO}u{ oSh_+:ntE:.~3,kw޲y%yV곟q9;^x Ϲp:g~Q :gv;BLnH2p.c|mѸ9.ֽALxChjLd'Fj}@m}YY tDRu>\T즠k[:~ƩCSqAX$zuׇD #{\^22>e%q`||Ue6azsp=fjQ % Vx ֒rWNjE[)r#4#Z诳RGkSG_wFw_}'y =[[1qB~[68ꪭOy\'ŦS_dLJ|rBvo|Kug{J}ߍgFᙵ~m; =~Qon .}jG "{'s,q/3oۻ^'Ky|Eǟꞙ̕>!w8D"⮲" ^! W8$$Nf霛|SUO=ϲF>=J\inuM "G@FX% uɺG\d&[naց]^QXȪ/O"n qjMM) \,S>O]$TOqy@Tuy ;O(Xwfݨ JӴi K\>@T#.Z(G )许n+6L8 f=Z!k,#i6$DKέ cUh=2׾Ƀ(t5&B uYYS@}_ WW.yT h^b5˳.JRi@0VjU1_'vb m^4vW.ߵuCYSQNw|) L hd K0jQDqEo_ȹV29۷NܢkBm7v}{hmqѬ{ ic#?^IM6?tBh?HVrCw ebpxThD w k"KjE#K@@*E,s؄#ެ T^[oh[X-ds$\4 BR]"(Vq7I zArd]V|L` `lH,ޡ:}ĨDD*9uTi\֏ͺQŌSFĘc pPq׹t&lMK&Q *Q9 g8`yAjjڔ ׷Cu\f m+qjDF r3RS;L%/]8y@,>!DkӾKHxE$)аhŊ(S,( ]rԸ5 8CjRkr'KB]/˂$UVگWTc̡QL4ъUWC4^=vs2tєˇNra^H?5:Y tcDEGYOQ/t:Okg5Y"Y$ K},MΥ}4 "TZ␿j w AD&!GV^3Pu8jXy:'6|o/zdHڟZ`.@yShJ0GM'2GyqD͉XRf1j\ʔDyaxS 2|KeA7+^^ Y~ܪ^ʨQrkR*DwN"aNN@ݧVl5wNM7H-UeDЌ]ǨzDsJv<W0K\ UwD֔oIt9<4XQL7\"D{M AkqY "cveQsdXSG]X.+NyQcoVV4JU@SfcuQ~`\MATrQ!@MܼYo˞VU!b5cTpA#[sipѬ*bC*F.FNQv: =PIs>*R 1fVIcK6F5 255?c ) h"M[`æ# Tۥ&&սk_sHD(J6F ɩ\4JZ }5s}lpd_&KTl⬂bI#rsmv0_ D;"?Mi9 (!@q_j=f|h |:eŚ_'W#Дu[fz0Ot}uGRw5;":9u 01bZ2;WvD]G(B9k3S#QuSN_/w/ws_wߐ;"6ji"c!PRT.923{gzwZp8P%~]?N0=az@[C z|zS>ts`2h_o'L.'n:v]K?[UX[> :7"%:DSYh"uH9{?v`ԪU($'N@LZ\8l7{Xp"IvF _qTpƶ 7wn+x'XThp3F[}pY1>f{%l >A)#TBDl&geʯg9h:CRƷF<5˴IG_AޖD[ *ZIK[42c|H.$Rf/9xwBX`ۋ۳l@*Ay87cmf)C`Ph^ڔ;8CH)+#f;T9"2և[8Tr`3^N;rR.nw7靕WSELý +5/*Ӓc*.ʠ'RdT{29W(i@GrMvnB&{yˁ@at _+岼Fii4nҴ]gnmB:Q}sΜɏ2?E;Ox.{edgSχ SQN}to|{zaNZҎ}lCM v>in'?umbbrKs46r?E?n:$R.q3$/؝~--x|M͈߱0@Z?gYy~/bbG^|h}㙶*洸BM&oPČ WcΑyfmޜtŨs(@ +,.lŬ2)(0P f΍e\>Ez&cXۤSw_Zjw:)3s>g @iSGDܻN={ןu| J?TY;]#1:_ۼ zKFwsiMs<:ǖyT-q n?riŲOkFt&+Q{lԴlM' aly8kcZ/6= W\)Z0Du(D\pn#~g;j>D])' FLr=1+CŹu 6}˔dD7H;[_o\q?V).-+hGH5jgzyXܴ'cIGt5:WYE̔2JD]ID_TURT񬗏_u4-5)ThJw[mEPSȑ)VDp39MȐR,ì>x硴;yuߩ(,Adտo[[_fZE>0v7+"vJQ~_wfݨ JӴY刋9CJ*+&ǺĊ g$YP _KeO'!+?6i0Fk>E!IDQ^ IkLX2@ij7 uN=y咷o@ >\4b /CA}WTECNd1y/;+ںGVh=DEu@Od`@WPE&&CVXiWZ' Bp85&.Shr8N p85@9 ù{U+iyt^p8[v'ndin1Mé\49hQbWGo͛Sܱ{O^&TX%B -]}t,7黹ߗn|3vSBݪ*,: Vz?op8܎DσTc^ޞ@A}G2\?48eΛ-w-={!䳧[++GƷ^^t)^M-4NL4}{N\-ѿ=w_# l™].4Ek җ:W|SN]YQ4w|֑Q3w}(3Z=o+#@ OxU#_ˮ ;d6/|R??Mé4Chqķꋅ>ubN+ş~3 ۵1gv^ÄS(8Ӎ`rZn9L{%ۅ-J"d-!#t_V-q8N%T&Ƙ 0`%{_)<|*1KGB{ ץJURIaW|uᝬe]*ZPQh\*L/I<㙸ޜ򜽮i0yPNUd݌Py$0)I TXѻ[XX|vw~ x1,jj$" =Юb[ɿr~Vnu>NR:M}d2 'TsPŏo3{`wwXE{Jݼ3>_ӇF f_w(6+` *)S/<IDAT 2$r/:jv>z0Ɠs*a+U,ҵsaƇ{Bs](X7`3_2Y3G(M"Ty~P)6PIޭ^,UqLxrM3fu+wnT,:@!Y`͋9_˰3))gSs`˸p ݺnӁ2#} OzیWϞNI9}&A?CyJ=w`%>Rikd/nݝs|/~)@ %RJ C F@>n =0pBxx"NUM pJɢ܅J^OZ`iv0Lo@xAXN)]Ѽ40D(àa*j0 .ڷ/ 7{`n@iE-B}7#p Ws,5MR rԍi ܣR~C ,^B.A G u=zb144c?bܓ0RW' IIiEؔe[XB>bRob`e6\֓MC-n^Η?uCnh#ҕC{}]ѼM>߈R(fҡ5dS?fkV@O;U쀇'B/dy=ob۾_dc}R$b]nd 9j׭`@ )mBsX CEZ(mK1R.ܷ2 /?X o fc;f pc#TOˈrvKza\YٝŎ2L&U~_z~ǜ.:>ݺÊVp3}V)p2H%"l ]| .#8By p4F1QðKqB~'6D%[ep Ȓ$ vqHBB>W x> 0"De\/vs>irB) `E糨]ő2`YLBKv_ zS2mBiZQtLU/FQ| UHisA!k*-SgvK{a۾@ierK8c12ۇ6PaC>zBMAWG75e{R@j ,}%b5uUL7],]G,#<2q"QݵFVǾ\~Ys6/J4׏N㚋?qs|ov">w4~oZ|YTqg?ĤPJ?t-2P()1LQB+Y?lRI\:,AkbLDJIM]3l!$bh!dJTϸH%^O7M!Dzk4 }A](Eʽ}dsG+狱H%;룬dqN4?*,3s "=?E8ps9wO03" bFdBv^alW6SkaT(PuoyN>|XNSD)tqB*J%ɲ.!?%Jc!¦~§OtIiLdY«71e|6~i>p5җh!\Oo_ .L~:Stw }A[gqKxq3467Q \vtm8m tp/|uB 'G2'1`ͣ AwO??D"Y)xX ĈdWAo3mLZeB,O&0R?x7\ ,>ߨ,yTW>bmZdy*K<%YBtm}*ʪU?$/x'WnSB,<"^\ӎG5_L K^Xm c M޷ <ӟY-:Eaس%@_u!/o+9z|ƗInL3㾶ޜ͘jk{iO1EyI  u}x&H´),+cȦ2;ȪB!&.9nmc֥WpI\ߐY`-O7k/e+^]x-af2n/޻COebB : +_ Bə-ŀSer>vSKIIH>EP2NKS_dK`Z)rvBs`+V8)wG #tc))DcfzʉE0 P$gNe|qZdy7\uǬy;+2l Xwg^``D%%a_o Ljq ,y@J;f K&EIeۀ Av"%Hŭ˒mL24u\83 / cZ vs|!*+ʨKwmKw2ry?#1 N#q&8,ul`Xxc<`RV/WNBG2gW1mKOCGQI|:^9cΨ80GVi ijYSSY0 ҭD9/|/|y (?}75̋[_v1,shfEo۶e54Ţd k,>s)Qč>JZNfNE7(a jզdTm#F\xٜNeΘjj*\՟ v/ :7/g T1{Tkx-4v2 ~/W׶m(}lŧ@UE "!|4XTr2Ca,vBaeis|Z0EI+N b²m"(v$F?өÌsSGժ"(xu8%ض H=WP[4oK2 ‘°a`0j뇒ɢQrdț؇.%?IK_=EiN mL8;LrY .>{u}8cT-ײrCy/?" |`,߾6 TM3ˑLo\{,w=gѹzSḓ窯L8A̱sy|3)r%~ޕ%{g@dȺvtd@.%pJ;t7b"4X{OC\d*cʌzx#=N23e<Ï=֎pw?̓MN'ɶf*9 JjLG\zs&cRJ1w0nu͜yD7:amNI4Ʋ|uwP^Ve+))H$i Z5p$tPQQi٘C3O!j6rĉXP& ֓ńe(?OOA(wа[ih(oREw0mBP(>I|j(Juh9C@ ..J#H AL!J<0mАmI 14P"GlTzjW;"/@RxRQGP0&qTf 2BCQ(AB*J0 K`%RdHil @Jt12GH$2QT RK8q1?ɄL ,6iDe)Bn>?5piFv}a%M 24 ˴a!RI6n꠴Qջ IGGMuX)+/jr0!XUN #Q2g;I: S@vp#=8 !hmgg -X7.R]sl[PCvmG8R0PB %0 c<ӢFb<&9a( RtH]iL#Ad P}بB&㨡:b.@RAl eٌqg]L R[OK޼riD,Y7i )?Pfb??A`~#J! w?o][r<pۺcvW;=b;~#\*r9<9d]pնE9RP#߽b[zF 'gx9g+1q Ȣmte *BrY ?])sȮYs~+_`8-]}d}SSrpbK%>v>As k7$mҩ$"2eŵ>Ү US :xDJ˩ > 8ihB ]h'&g +Dr" VUFWb.'njʺlw7<6֊(KH0 f QXHz{D!E *)#Kk kʖ{vں bt8H ,j(ut& tfCJw$d+){Xd70+<T9c(%kFURR.iJK351j(kCj4Xp╌i, ͲYv.ל״p[Wr-PPC8ŪL#ԱT`.P)G6 Ngڙy꡴.P %m1ZF ֬Kqü1!~% .地C; ˨yb>2a~̭)&ܗ3|Y6u$ F9cbߣ@0*wPϔbSwqsS"g䂯NKL}lba96B*F͝gW3c'FY䡤X]/s)eqN"²+Z1,.b!T3Qwd1Q'ڙE4F9l@ ?yE-DLP,ޑx||-̝7SL9Ŧп S2bC;dc @%&l߸}OΈ]j>8oYż/|w(5oq϶OŲ *SFca`JEߴx(`%1uZ:Wr㷗2 0C)P(=v4؎MLI3âx/sՋ께P]:f7Lԙ5XSuY|QE(=(ߥ/s7ȫ@!sZɌi,*%7>TK14=GGPf!Oeh k3;quԕZaջ !5p(auǍQ_j!Tidqq1AF}@ 8ik^HV1YY2:%DGf:$`Y| >_}xVъK IDAT8%;@@jB`#D&2HTGQ4t?[ kg< ajhܭaVnڞVA GXD'װWYL1hşM'6@/B;mH!ph4o[,%Ci${(31knW:jʃ'Hf>ēЊEf6s t_bJhQfo7X Ș戬rffke%\1?o`$Wsoc99P1sxG~*QB!LeKllϏ#L:.XF[{qe(o"bGVeXt-[k}?hjY 'Ҷiwߺ5c@ 2U }Ho|g 33O⼳Ns16*y#b̜b B -!lʠsV͜_^f&h4o[o}[ppeJJJ";(–(JUv d]8 ̩Hq_澧WaFڧ@\XJ"0)K -+x㮧6S;w.U6ж)PM[5PWja(gJc5fcÓqC/)cڬY3)_pr ZW+!*RVi FUܣxdTƙ2gt 1Th17L|"&nNG~XےL2%֎bTz165)>B7F%Ɩ 7={ !LmeEOϬc􌙌6p_7c)$m 4Y56JlX2}Z5[Ϙ9i l8a8BPbR3N(nt'L4B/ˈq4,Mergp1,{axb ǜYGB}[d2O㸩1k5;[ih(/Pr%| ^1i' FR%e 2-BJ쥆bl=|Yt%l&m" !v(H,F˱ Bxa`(y>muiC*ۄ-(ϹxA  Pb9eɷ,(8:ypI`&#wOe(#0_7XHW ]R{GK~xn -4XFhh4FKh4F ,Fh4::Kh4^r] ,>0LIACh4#4M:^ fo $!|F9e! B`ThנFhFh4F ,Fh4-4Fhh4FKh4FODi%NSp]PPF) |(++=rlZB]Ƚ$Eu5S|o,B!Bc a` D @'G9z Ӏ(0l(%OFh6͛DPJuB@*erޜo"QJf5PF֎Ӱ_QC,Rq Rlݺ:]d'SH2{ܱ#}~0 6n\O<* xSB HM Jim`TRj=C&I7P,p-7PM u?/GYF ,}TUWhoo琖)[  k/d7555i:;Ѡ/{/"i mL8sww٦tl: OHJKK}˲Xb9&MbÆ $SƏkzS"KI$G|j ֜(FuW>iKox=Mݯc: ] RQQlݺ7FcE-YD(!bPq=I`U%׮*Ut9e%ɴ;r{㥼_/a翀P=WzۑnsϤsY; :a@hC(01Jae,7z,Zn+g;y' I>aN)E yf*B gM;<s>;yӧA( |EdzRIg>~M0?}?1Q(cio:1+oT5X,S)$[MSyT8kv%=#'VzT &R@f8Fһ&F ]xܐʑKqƢ=Hp^a;C<ȯv y~ a;:]F'B~o s2%'G!2<\ b\"&,-[E Rj*9|E叏?ACe ?!W9rfmX;G%G.mݸԜ|26ѵ,g2CyLʏ=+ipkTB\ 06YQ1J̦2@QY,e#ןP;kTZ˼p97R|>O8fpp @E٢A!S16t.frzD(DbT-M_*[td͵70 FIEyt /B@W%jH}"IOчUX(xysC>>d;cv ."߾(si;B%@2\d6Cl[/Y,oFhX!ǡu1 lf3yxUeTP?ٍuS¬4IR_`8Ce \X"ͯ de&)%555#Mr.B$}N=$<}dj#oHͻ8L#=b駑\7¸q.yW}a~\@/T{OJ%I0 ˱mjLӤ-q˩dfn$BΩ [$f=ޢQ_,ּlZN]iefӒG |:Odxu,ju !(2CnM_*\_s%ڲlϓ-AqȒ‚SY˄~ݸףD '3X?~ ܴv/CKRhK ,H҄a|'a&UUUs$c YcYdrDv&&X֗}qV#D1^hpp83H\+>͒? )aB4P<7}b#ߺc]%1 R)\:]ʢ&O,2L /$ʎ8MQ'0pɬkgU`&a`#s?.SJY1K - V`ж 됣iozj4XŲm,YwɨQhhhrJJJFIaA@.c6.}4/{weU}k9׹{aAf TAł`AEHbƞhbLDc DQTt L]Z3E3{{.﷞W^y%gfΜ95rh+cN2=~ܶM?a栝5?o`\G֭,s | zCM[Y[QX\W"-&r/~ Q5q']0;цִ'p HkmjTJiz,( O\cqM\Ga$tpu]T'{LBF_8{ {]0U>k$`2!C 53gnLR TCM0 IA|9dr0 8 /kZ[Li+!G566CIv`rRc҉y]+yK?/ b2B+W+ROH֔JeI%36]q G^pb50¶7%ϛ)}ͮO2^=U#Z`Mly68| m'IR.YI>EDu/$ شj$V$$[C7}c_Fulv^=2dȐ!#XU$I8/y)o# CWI=~]c 'D.[7 C>7%'\RIKϏ~y XInTTc#+Z<>s]\s=Z?6&r미7Myp:4/מ¼|\.Pq NHEk62o(o<"1˖9aI+-9!|R2uR8 @i9Y"ՔN)eCQz*mI)і](4a}` 5em/C ;~šcOC?FZyT*Jj H ɁyeW$448jKJ R.Wu>S Θ</POT2R'\iY p$V|B@bVSV'k-K*L=$ڂq}:m&@ j%-f]SѶA4`JElMcrtʐ!<=^'uF`exʤE8qڞ63OfaǸejsV}[O먷L-?M!]OM%B,%Omݬ 2dx srϐ!C 2dV 2dȐ!CF2dȐ!C 2!C 2dȐ!#X2dȐ!C ʐ!C 2dV 2dȐ!C`eȐ!C 2!>`%c@Px֎1.,Q.nʹm;*x3u}~/k> O!<>o*Rdŋ3dȐ!CF?ض}+] O!=ߕI -+jR( M4R%Z*q,_v(mtwuĮ`llj0cccKEʥ2QqBTJYQNR1 > i0UhCXJ؈y瞎79d*XBBVi{20 d4R\go_$I]2dȐ!#X()^@p A* AU! DWW]39k\*l۶}E$%kBr.n#̖AtqȚ՘G4#\t^BSqVvJXTK+R(Vt2GO瓽e Vgd`%OLmBt JbؾkB"}\.tvu{'Ƅa8#D7rjU'̘1;{w 6yaBytѽc ߽{399w?usDZom-(v(1h#GrJCĕ bgrl /7pb^ &o䰗{=PyŶ[t͛u5r :Z Lׁ-d筷\.T@v>WfᅡZee2ṡWT[SU=]k8}QcEc$@i)LNLR)aœôw388[8^qZ[[QJ166Fx0_~9BB)\[ QۘInZzzL9 MUxJ;iߵ,k è_(FG 7:}{yyW2g(>}k̘ыlB7{؂vDB`ajEm|ag\ !݌>Ai G7}oj:$#X%MZh>ו%k-\9}3JF3Y)aiqV<|$w&t)U(BkpR?ErJ~$#0̺D *&AC+P$'qOz-oik"*!7s9g#^z$J[nǘ1D䌓|1w|л9ZMb1&pYzh?8Gx:7 UPhi foogd &6ii6?go:پa;Ze? T&nݲO JeCtF2hk4>Q1&iXds2ܾl \7+OZ[r g} [Qdk)$===188HgGR ?_;vchCD"̜1^8X,6m ay 3g$"( Sq 6xXAJu}N!rMo\=W,BNTٿq+7 m˟+!p\eMx~u#]}]xKՑl߹ܶ=,`w)>/$S] Xr=IMm-8A׆TšƑ=Ird~8 % ZT̶7/3N9<}-q-' F|cтٻ|mtww1lذ\P`ƌ3::ݻsɴ`\':ר''IҨէKQT$ODevʻUZQ|x=11wW %5:h+9,b7Hd V ñDۋ-FHbVX(+ݏ W("a1JCbw}'0:lش8XȄF:yBBTS,Q5Dz>>|=|#0xr  F  hY[Bهͷig|'xNFSퟌ0uC r4KZU=K)ETbrrJd=܃266{>cô BsNv~~>3f̠kabb0 0ca߰:PE)c$" RI$I&T\M$,^Z.`1ѷPzW-gqG}f8&Җ*Qh!V8Z$ʀ0[+ VX]i2F11n-&q.4b 4ĩ&/be|! hu(R0巼 DCђG>~C{5rPuZ'u`R$uqE@6Qq3?ԌѾڳ<-냎EIVeAJ(L0G8($J0JXpo&@\+NcWmdq̫/_'N}%ɶ߹gig/⊏;ox',X嵼o~'p]o^.腜52_߿}Ïo>t2 2?׹s1J7؛9b/W>)_Wӗ~ȯ|dI2d3z)-(r`=QTRPa`>G4oAgWBZvl8N=AHzۙ;o}R)ih]Ue|ߣRy@299VHF!BZ@cxXdHh?#|aak8moK?# w~z:"E5wsϡ-۶o~Shkwt0oڂ<y|7w7ou-G׼Rr_|6Pƺ+8dhf JJ*U9!V}2N-|:">'~|#wkd|sD+=Gơ|QfVRڬ!׽O| |ꋗ3;9nΔ&fww/dtvʟƿ^Gp;?M|Ǔ|& |8K,N=׿W_p?‹9-ଋ6' /sp)#ǞW/o|/wO^,!C"|~4%;Œ>&&&طF`rfZuTU|ޡ+3w <4l5=C߬.[99sgZDGG6#~4/]r,>2w]]vTG3&ŇsEv2={-9؞bMR 1aD\.e/:tp~JV^D݉ L%B(N љ$W!Q\ Wv'a7pH++{ٝN:^ZVw{Ce= sB.8a:ִ/c~0 GnsNyK8zn h7y8]w{!?ĖH"8|[$#yȕu) ',fۇ[އO <ټ=?mMF?I¢s^܋󦻸6+Y;|fm-w7XmXX$t{.$$Lc{;?\RS'a | ҉hӰDhmk#B8dR1l޼\@MtO{[+ .D' G=ՔeT*cR!°:D6!4HtBX k|12qZ< tajRpQkٲ~#=-T+1_r ^\ж1$1=,9t|;۶(Ȏ}O%/-yy{{=Lus~[w=z#~qoyǟ 5F^rMMء!&w*%Cw{Q9QM-"ط>| KO8D*Oq*8d)[$ s_WaZug )ABOl{5;#6\d-?Wyc1E-DX/_z*f)&5H8dۏǯi.=fqM<|;|uYf^_~q+y.F5%4A5sb-9:Xmx V13o 9咏𢭗ό?83Z=`1aɇHo~F7l9,RV#d!IiF!9#ҥ'4̗ˏ5]3ر?~Neu|=J(v4G>oVu}\΢zgڗ_Umn3D6Js{vfj!څ:(~a\WաE}cA!.JV8Dn_Ba-;wYʑ^Ň2>n =ՐP^ZXCX Ǫ+v, W?Mɋ IDATgyXz-:}!?<=;CF2S,!q-AX L{"NyI4751<8X+x 'ccS &1J4 !ky\˖21щR$(KhD_X`ppa,YLXdllr%-cY/`mlp? u8vDH J:TVG4(I-m-97$H+ں;AIuS8|Ä$FK l IZGZkkC6^3񱢊/}]kNx'Xo?[E Av.} ^U糢0L~a{&H$Ao!D.yYMW>%1e*ٔ!7s?o`HWr䚧l"^>ϧ%%9Ob,41(?w9\/;ʴ4ss/})7xǭWzwcm}q;f} ^4ldG:c 8崣 cns`4{:jmV7:s@/Wis4M9ƒcEJ$I(8CTJ}a`~ϟOP '|Ev"#=u7w܇EIݽ"  )-p9]tq~({X D]?B&qjR$e1`!eSO'(k -d15*סsrf̝I5ɂŝogk?)Zg}`\B A[/ zRuhA称*3waQYS͚r1-sݽ#,[CAl\Τs}}t<^=oXhig'Ǭy̤ѽm A[V1sƔMmHfϠpS-܅l٥wv>kFΐ Bb&+XKIZueǎq̒%Kx^aYN-3<3IB}M5HӗSE}[bz鬳z; 9Sf]Lwl(~vTg*g %|!Uk ,Nzbzn Èѱ1:ۙ?>Zi$DJ DD*U<+k-J9Ɗv$il|FX+㕯8U4ć!I$<V#rҠDt_˾͌^K%Do*a ֬wV;Ωo4&2kq\W πMtƪRy\q]&qW)p:{d+ó.^!E>S %5JtFӮ;9쌗A5NKniv #)ԩ=1h;lԗEFkQHkՈD'J 1r9>88Sz_KwbhNbK~2AHN}$_SXSά:Atwhٶe=B\*ł5p}:P(LL e)8^>Kΐ!#X/H)ec1P#X)uq$,]4}Z0&i(4Q=}YaASl4b&$ Q1qԒM1BQhnR.S  y&&LYsP1ƦCؑ$.H$BZkRjZ5ȒH)j\,RuRH5VSJf]ʶ-Xs)w{{fWs++  f56%JSB٢v]םDAirɝmY^[ zHXPR`trj8J)$aڵaT GQZ,JfS}r BB(xn1jȻ<%MGhij*0Y8DOXD4!K%Ka@`%()p\';Z TS18K.'iU]Qq*IL*]ZH7N#m(&ii8p^`pxRTF76PՊ@ E㦯i`=@lJL خM!I82d֟ +X}]RDEpB8F 9XS󈬵?-LI3 Jki BL#Z)ftt4UESS{wMDKkS#KԼ4-#sȃ`M wշ:j{UKw_)H诫ruկQq ɪoRólm-8cby‘6r#Pk0bP[uw6uly&_ŋy, 57}s1ԨͣF服c<2[igt}TN8LI%|H>q:{6ۤ~h` sC9x2G ~gw#URԴcCWQGEFT%>}CWt+MH=!(ĥnJCҍУH\|2Tw}< NWWW Sr)K5(MͳTRUW\m|}U'zZYEh7TG$vzX!)~ AN-Qi i\8(J{*ʦ} 9{Qѷv!"fݕQǟgy\Uܭ޻Y;I "*8ofPeqEܕqAd_CX$=魪k9ǽU8#R|Bu>0{\+`}[=y)?ǭZCO3?= n6 Q/;lԬY|cW#xRXbDh8 cF_bOnf>)h]Gk]mZ $]RZxVl~?m'i+3m5HAi6CCXmh4mFFRz= ϫ5CAq2_&K%,)I1E~.mFujt '1]VJJTێ+v]Z%Ai1:(y~פNm}o\CGT[ާn'+83ljv'}ez&qjn+>}mBTXa;_p'Z{򺓖a=8#V-u# 8Ok&kzɚ`sACr3Xt.#&ciky06K*%+u[1u _/n<bOxՑo^6P_-oy}J. Oռ{;oͼ̏q?>LӏL; u_"&}ӇY,|膯31GEҸ68lL{>O^M.W`˖M,X'״mǯM# YkBb"WA$p"d ǡP(Ny??.@))DS!,`H("J Hk"c0A mDe5cOf"PzѶp=bJH;mI,"\ː\\7M۟1޶^m^ H#Z`"!:@X2fTi#mi dQ[©\ Yb k+l1}6/f>3C?CV QǼ #5#i^{l]+dܟmd`Rڶsν14Z˵pч/73d)͗O^LѶ^wKp7>3q;K{|{.a{zE=ߜ/2줐ѵ=H[ūD- 2\ EdzG2kp^v:wS84!3W/r5Xpy1v}g;UCIjF܂8}\3L׌nDfmlv?{v(|TT+ ]\$sNrO ZURSPQ5? Z,-"X/>FԋXB0c >(,Ys4bAoa5;mH+a }tc0'/;Eso l:,3gfvo~{8rA=tN¶.;W:+8# eqKK}\C}q::s#SnF~}GŭSO86NwW6y<@1u/C4-(?UpɃLM7` cV컖\~WgFƒۃZXR⥳̺aSF&%J8]J PoVh[h=Z-I ?8sYv ٳ>Z鄀(l[V &r_Tn!tojVU,ڠU̗>=*~:3d_a爫1t߻ADTDJhA`"ɭ4"$eM)i[1"}K̖kMY.@DN_?ǹҲ_2>`~w]Fz+>b$k",!4H(. 7;^{_0aiG9?[E_2|zo欃soj9o)SZԧ[5NfU坫<‡|r|hU HmL܊{QDm>l'z ZVgZ-i-"X/ASx j ,}2 z=v6׊,b/,)"&a -UAs*/&O飥!"HV݄Q!C! %Fk$4E!h34}Jܞ1o`m[R)qlmtv166NOOQ݋Қl6x|m:sgaejQDqP"Hy399J'e4^kJC{8Z،=~-;^JNMn!nu ۥIzxmLP'POrKZYAWTcwqH3' 2OwXcw0ƶc+K籢Q\|6{Y&<Y8t[V͈k we-#%PfYso# ױ'/_F7}zdIX_ez{?Y4bTgkntV8c0m,~L6ݷރƼ4 (u0\BbԛO"F!/$:&YkZ/ g5ʍ/P xDaYlJ;*!$1XqQ"T$(  nCy\rL|LҊTQbB"A>uO4]=%&o~zB՘jrW~x:'?G70|&mms9q4[Z3ewy̙5YW%N;S8CyI'|rV\gYgL12!CGQv'>ͷ8r2͉>Mٗn_~߹kf5S,彯?~}s\f5zF6/\9Xco{k03O]=Ws88H-A~V-f9tl@aGWO5GU9.^o[/2ro^kk-L-10N fC0c'9U3}{Vr>̏~{ָ 87rwrA|VĪa=d,I.zn h,;::1B0mb7~ZZx孹qeXT3'[XuıxPZ zǟ2c<0*U/*)|>KwOI{{;ŭ9i:^\6M2 OzYjxO}JLRa4N^T,>DX#R,RT+I+EM)HQTضm[StQ. r i:NbT<ץZ"<:;;$Qר>iiOmZzN{{;J/`jjή.H" CN98>&U¸RIGzO^X+̘5M :!)Kht8YлMQ$0LRwib?BD\Liaf0@D8 r*SrxڙٌF&Sĥ_u y}̮eTy_]Gv"ms6mW-&qqAy&:{kӼ 2۶ ->ZslH1\^ڹ(WL#HvvW>GǜW2LјDٖM>߆@-Z](m0:Z D-"X/{8S0,A055I'H0 qh2G[[Ǖlٲ;n0Ccrm%ɑrS(;kZ>X^& Fƍ%a3YE &2e<ˎU$Ny'Z(t: XeJKJk+,[H?8MZ"2d ]Yn/욐0뾁Қ߂6HacrSp綛Aiкn?Gza -"X$K%rٺ};+W"R!k[nT E4TXVY-JSl8Z"ݝ=wtEow¤CRHI(\\.3>6EJ6qc"ϣF!ʼnIj $4yͻ>=(ɕesv Fa징XA4"}RIKFb²mbI75YIH^!fIIh h0i[ۻ&L A_1| pQz_LI,Z*RhoAدLmB`1(Yi0je$㒲)m;6sǝ739Qa|_G)Iuhoog޼y ,Hb\*O\E0}J^ex| uFG,TJ)ɵ룳3lC;㒲JjTbX@xyiƦBĖze˖r]R)j٧x„\c#׍ۑ JDM*$ B0)@ mYLFqx)nƕWh3f{of j2pu a0Vn]Ir=iZ<ZqNB.B@-OwW6mF+={ケۙ3gk׮;ٹs fa8, TJArLRT*199IXjV qB0U.7RJ\q&i}ĵ85X {CdX-\|*F2mz@iWK@Z1 t:EXXKShѝ#qX-܂8؎%g) .䦛~{r1 4aP,cxxIL3}sqmFZ%6 G8d2RTӿʵ-\jFvH)1QL6ʦic\.jNr)'x|+;w.w~;o?or_Wx_ϕW9 %`eK/+\V^͊+ <:[nK./`nvN8~Q)c;-_s9{2gh]o2 x :aGijJSO=Ŷm( J%"mUkKvx+Y~*Zwei8-}TG3p\fV Qh0MV9;v\qmЂY0?G:Ny9<E\7 ,`˦L2R̙30F255Ɍ Y0Yqt21>Hc>) 8ǩ/?[n9sr5Wʤpl3g>[GF+1N8nӍ="R]%No?FGG ÐP)&&':vaࡇزq3J)οaU ]=PչI֚3gR.Ul۲c T|n MmmۨdJԉm@-ZĂ VkHDxi4L '՘F 6˲3xUstY)[ЃjclF+/.t ?[孷w~;3_]n8]G淖{ji꿰"|p,۶V wE|Q!;9S83o^MYR,]9sT""ɑNeHS8%%~dfXE:R.3};۷meْ%hT 8# @;::d``>zzzRuFGPJ!$1ou_ ϷNܗK5mCOsf by!*r`lkTd ϛCsĤ`͏>M۩_[=X22>J-2•ZhUmB ÎjiAmY YܚÜ=(NUr+E ~TTBEmQȷtх mG"цt:M.kV.\8Zϓ#dt0lݺ5kP.,2w\fΜIOOt:6Mb PQAN)CH{+\AE #$ϳ}&AR*Jܵ T66Mx-xF7+qӏl-ׅ :˿RAm7=l{ J\EܱHߒc8e;v?w\Ǹ7Wσΐrxˏ"'9!_T}k~{+yN52\Kx׿.8~󋟲fK}>¥Z>g=e,co Qȵ3dl`)㉿t:M&Re-%$b;vt= ;w.tuuF:n"ӄl3Xp/&DQDPRDZm# !di:ّR6ctďM2ͥ)vw]q.j_}'d{Ix>zL$t0ʥ>p!k7~{?{ \wߴ/r.N:,p1'sAty锽o,tY~Y@3y\Cq?ίĔq# Vw2G,v;^Qs{ث9gs쒐>Ur7?.]~W3e]n gF>-Ъ`=A+-B,ij5y}%hPZ!'qvdMֆJ((*D+zRxdY2:43 6׉"tu/fsXZa"gٙ7wlf͚EPpS)FFFb6$2GÆ1$dX6ZƄk&T|!ҶcwEQؼoUo+7{?:;m_xۍfe}?d>VЯ9lyUrv 襧}{/]oc¢l8\ĶR0k-Gmdz1oLu%9{zlIv}8O&_c҂}͛77tw˲vi#SKHTB:C[kdRijO.C(Z0 cbՈBJTqunz]Cǥv >'kE/.[IrZgɽ~; hi Z;jiXP«.NkY{wq5 q,6}͗}r/t+}l馴[|^V-S7N(Rs387ŕWbh5F%Oiogrx7]3_Ѫy5⛆ k8\>MmgX>z6uϙxbȷ.XhcRb ;jC<,jؖ/F2!M`%cHY8:h GTl߾1tB[Tl@{{;vp RZ4b7*pMbPV`j5(²lt:tI6mh$i:LRͿ\Dg9vpĵ^LÑRJVKR):;:h@+jAclV6 {& WIgPQؚ2|ުQhJl>xɧs#y+/s;A[O[M登x)rH纘`${3/GmY,\"`hi[`)f2="{#eϘYXΓvWOu4,PZx1s+%Q9zfQaY6v5+?"|@ I{>I#muPBc t[Y-'|iWhpJH4 ֚ѱѽ'kFZPhmp\l7R  RnHy.mAxHd}zLPj5T>02΍STuDR-j{ڶɤic;6aY#k(֠WL|M>#_ȣ1瀜yD:Û$1LTbԢ}, q5IUuPzF vN<6?&sk?$/1w?g??C -"X/D%B\%@XClq⩍O0Y@J7e"ZmEkT: "*LMM144˲0ZZl`\.MnJK&! 븮C5Hy5h0 (iHۢZ*STbfYuBܡY8m۸iI+)nBx"' 𩙈0sd'&xǞ@kS ~bTb̝֬9 yhݣ-jB -B`MsX$4#qL# J,AEBXgX,266m8VQA6Bj5ZS,NR,X')p]ZDZ891TT:0kHO\*˻ݴyyxnN6 eY$RZFLVhxb㓄G^_15U"1,'h,a,ɣ^I*Z3V-B - ` ;V3FyZ![hth ^/.38(EZkjZTB09Y۶ۉA4N͈L&T\UhHi0FQ*Հ>~PGzFT ˜CIN6f 8- ULlnd_F˶mO4o*<Ж_N0'6R3Q.QU)OR V*&bJaB -B` %(Rc0(vM#99cdK |)t8}9>E*-[6rwGoo$B vngaocEEi4B;mmm8 &gȧVUTG%SF jOb2 X1 TŒvih8DD@]Zm) |jzc{H&PUIgҋ:l^CPľdBdU -B -‚1*"TU`$Q"¶"4L#8ÐPLAēo=۶d2 q,l*[>EZRq,{֯_φ ( !?|@:6(&׫2STVT*BAP"[JJI0*BXYӇbMTs]D~8( z0Q-ӑm\b#Q} 1Jcа[b -B - _̄-u0¶\Ե1DQm9 Cʕ QadQԌBF*l%F%n l䱆w4ȗR:TԪUtۆ0bԎeR |$c-B -"X/, [XMʲqtsr.&nst'mN% ¦"hdMWu)%044QRl6>.RIww7J<,W4MEe"i(ZכBl+ry*viG"m~"&3A! Y2 WӉU5K#a~6Ym1o|J"l;Ӗ!l aIJ8pvp\(Ś5bq}Zvr-B - _{r]ҙLlivB.~=b9d2U9LOc18RB&fx;fƌx)XDmlE) ;|ߧVVT@ZFzk WGh5'b{ܺ펳H0ĎQXX?dA,+v""Ogo CsN?|\q c,?2BM7!ޞ֮]Y}| aIZ>X-B - `Ex^Ƕ $}l²6BZJbS.+dRh&NcY˜)*Sq(O%AaR}*)U# CޛGKzgy~{V%Yd--3`LN !d<`d!x3c'c` &ޭŋRZrתzg[ZdО{N[uK]_~5u]OL&Ӗa@BpB}EkLi#1{M}Cbۉ -!Um(_܅ y2yY`<.1Oa=ln9Qn|9z{Z_uԩS`]IrS5xDF(Ut,Iȝc'O<ʥKI6%BHl Z'(BJ[ggϞg<)_;wLʖmxjRN&xQ!jϳp8رc2A)~n>)(C.թSN:uLƨuM:' >hHI0&ԩ=zIӴoNy*˩k܈>E>CFA/q-c{a2Ȟ{R2L{8e^UuB0˧Wi9jcb&a٬xUwQUJԪaQI)QZ闭 Ӭc Z0'~@Y$ L&666H 42ޅɡ :uԩ3XWdYU&AqTKRGl*)g5Ӧ5`<'`}c /pQM̌()yM^'_aiiUIcj4aP}ެ+B4R͔IgIO1ΐicfj6nC="ʲl h `:Lpޒ)qc%/rzYcLvMʪ:QmJ[d1s:uԩSg I) ^6ZPB(Oƌ|ꓟٱ O&rʥS&'4:HR ("׭xoA:ǎ#MSȲݳ5quu]S8ׂH}cP8Ӭ1-uYaږi29d2iAHA67O`i-QMt{Йw-a0cB Hn/pZGUVg8('J;uԩSg:`}d0xPWuM/I)yQaS>- |Rj /1??/~ }CkMKpSOQ5% 51ؽ{ "-ʊ f+ eTTU cj>R*'Io֊8<$YNڈ8)JEIcl;Ji0r}&c ┥^җۿ9%XZ^9uB궮SN:u c R,t~ՔDE乡( ,%J~nFE"$RDKs 71c`M&{.^4'S"8xv$RrW#yU`t:mTUiPiq,yp1$IWt[eι`E$qα=c8n ,p>[͞_x-"\3(˲a]y¶uHC(.27jE Ht}}8s46a?1 ӽQ;uԩSg04Q(!1eIUʔYsiN927ZZP*,$MS,--sq~1UY` v%E>MIkCQTeIYV8[#6 FjvOk r!Ym_a'0ּY3363Mi̔3}|vɘ{W= Dle}~|45[xGLqe)*B]F{KeA7ԩ_5^0qQR5Ӡ*G4Tu20tR`pCJOԵen@?`qqU17c*\ŞݻY]^f2K ޱ̞;ٹ<[Ǜx*/4DFD:jle ծ!šN-Nav_A(b7?wׄg"I,ˈY]:)˒N={BF sوͩ8feAd[$IiRެg8(gvit3+&ߝs8S*CFdXi`h 2"uOӒ$a2b<]g2fssڭG8HqΣc (\YLc3b{{ÊS!e}`lYzdt* ð*Ä/|xhI!R5haXjmHXE`(LpɸAQLoo*2A5ylQA*|g~1֞ u}3f+wп^O|T`ϝusS?sMu~{^{Cl{<o~Ǿ%|OUU}Gyo? l~?8Lt?_!/51ͷлWHC]$HwnSN,OSXK%_9v o x?cHcz7?o~OOY?FS|{9y1X}~>kK5)F♜9m~l=Q~x{Oǻ'oǸwE}## 'cL>?{4ɇ/yK_I^u~?:;uNϺڀp.ʢh͎1< 9!A!.{gΞrv<$/*֬mooS% I(S TUݰL{"oB|;^šf:~selmm1O(˲2V s!^p,;Ww/Z,/ؽ{ۍQs|!F)haY^Yak{T-޸{ȧ>|;Տ8?f77Ae;˿6l>Iy%_w d4gS񏾃k+{ã%/5^:V7xʻ~fєԩSg:=7,yLehBeتFyx)zj$.sEbޚŅe$ino-[[Q5i2n՞ڛ*S6n{*ܣ(jMی?3CZmLe(,.9ȧS,DzL 'ZP&>9p4˜'N`c,K!,--I(2cqnzJ&<{6i4J=G)9G{R(1%[&ck-{xHc~|#(ͧyPYGy]NzбMs?: W,cJ̧a=S`QX%9hL~V:u VdZ^Ht74SqTjڠj%yaGi\Ʊǎ*qcu8XxBOߌ]5CuMQԵ1VEQRUY㸝Hͦ`޹(-C]@i1f0R$q<!@xrhBQYUUEm*>ϐ;6lmmSSCxM+MO1zH/X1 g sa[)yN+>ySrlRx!WKqdQErvs ~ٺCvn?i)X0\rN=g%qE:'>gw=|J OٷJͰ:uNJ )uUYQ9ml|q1UspH$:uN>ιKGae [AT BN*sʲl׈yY5yQ=Jl 5(ͦL>t:Kع)2f’YxR2V('S(")h]L'dYw.,JRT%Z0#z=(jWxz(L&LӖ)VWW9|hMy^^[7.@â7F؇!-Ba 'Dk`LXIJs@hukjPM)i6;Oη-qƺ(F(b֌:MG)He 80PHɩ'u`tڅSDQB'`{<+OOُr+{ V_طg/|Jm-RJDQ`01h/uIE0d=eO B:b كcΜ>h)EeCRYOU`UUxq;Jv:6 όlTaJd5uL&1^hHeqڮC,볹澤$~g gfkF\srL]!_h$y\U ,e TuK &P̊"S cff")OcJG$?Z=ŸyRG:n!4IީS`uz4Qd:# $( X,..rwbɔHGxV4fa)-Nb:SR;C9 vH%Ij[`0$IR1T &\~@f9^/c0P5.-5Zk z=&iB)%څV}pRPm<R4>tik$amm S[d81(B\ytZ;^?`{^D`(%gpւoVU*څ'YƯ5RJΞ9\OYPWQՆ}kĜ9qWǎ<6G;3EG\ԩS`]뚋Z)@`Ѥi%8ġeenbq䡇ٵ<7DqY{(OʺJIP] 5XJ)5`RaAB21eKwoW=Ν;L$H PH/:Kt-}KTzҹ9(,^됫X+Jj:q?d<Secj~qq47s\\[CckXWm˟{tSN:2@X%"K3vŞط KQE '\[5{vq<4aU-~8ZSu]RZNcr(:,ˠ@03j3|UUMk>Y1i3cp~BsYtA,S%X*!yQEx4>Jkm g8|H(Nc]Zcni˫|sgq4:ǖLݽQ;uԩSg 󮽑nE,I q uBp'9yInv(%:@OhWu{(l-<[,B\ XAB4JiQfJ=mff+cqk `(B D|+!8AZB D+㝳"c4Ԛq]S/p`DdX&oހ ,&}ܠާ:uԩ3XW!Nh V,cx2[oأGcEUUiqeAvyӀuNbwVyO$*Ac*" $i9W &:3OGYfBs.|Td`4Z (dr3l[\XDx4h9G%Y8dinFNxA]IР)yʺF%/VztoN:u+MJp&yw)!12wkꦃ0(ˢy,xp@UY^!"䣼qx Dwdcd<(Mց^pW[Liٽc7 T&di[H!QZ1R*Dv^KQD1 臙S* URHI<QFG,|'yϻgs/9ӒM]HHjqJԦ["[fZekY=NC̈́( BO`jgn##!cxh0VF*1iz5|^0PRT&J"U E؇5cj=^, $DԩSNLV8mrq#>ɓOhnȇ?\su ` /K)g{{;}s Zx0 R I>hMZKmkfjxY 5> .]yJb /k1Γ iCEf=}bQJP!_6+.˒|2e:YvLhh3 iD(0#X1^PJ#Pl8\Р':uԩS`]a$L!}wΞ;s'xyޡXWiv[3#{u]2hhqߘY4ucfDQΝG| _@Jp8dmc2ӌ01.йWTEA> ^/ce&U$ i"cp ES և,>\ȏ5N:uԩ3XWePHiMUxgΞ}ĉ(G{9xp.`mne7BxʢFˌKkpY1k'53nUQDQ$p $A:fOmJ 571$Yduq>?b~4_rN>lj%yQ ISle-JSWʢ|Y)DS3C4DQDkZ !YTƃO@IkKUz=t ]SN:u늒o8LQIB[  [o}E3Oy5Rd,Bx9rIs..zpAX3 AYjc0Z$ I1cmmGrرg0"P={(MͩXZZ!kLՌ1T" JE=Ά,l 7ˀ@5S~̒7qv/z=>ڵ>[qZ1MSj]P),DqL9:uԩ3XW5m:5'2ٳgkkkr-c>r]'$3H%۪!U{BOk4REy3Dkle1zg~~,Ky'9 3,Xky_?qVvkkka2mj| Ӧvc}Ml),ֺ=i8QJEDBZkhQy Np'$Muuʚ6֩SN:uʙa#N*n=XN׽oĉy#T1h177MŚHP$I2d/}3g1NKa!<{VVYt~~M &M8WT%[[0) xלN4ڑ%1$Z$HQ$qޠl{>iz$q=AV*`2aĩ'LrSL&iJUUhTUDtv]UU,kZPu*LS-Zt:NJH8tъ$NI׃i t0Z%qHuQzC%ZuSN:uTUL(8#G6zի8Q_2y,KnX)~[G)^W裏bd ;VVVX__'Q :YXXhOeIQH%%"+UCFTɧWm+NQ*Rg?ֆm%RFDQM=@I}. @ԩS`uR(y켷儷sE:˗%vd}q<䓼^sȣlmٵ,y/۾G}"pAo&s`8dc}؜L3\uUcf:N&xY\\`4=Y1r W_}5> Ip)*kx+^>03{YFiN eeQI%:j;s}(HӔ~O/{ٚ*P2A!RF 'LŔQXh{ES5[[[,//N:u 1X(#dTsq޽{y߈{ /Ν;+^rGΕŔA~wg>O/s'O_$,ry.llqƴ4Px^hgaaC⻾;%+Kύe)++߿gw|_$MSַ9e]O|#QDE0 Nܤ 1HHI H!@D )ZHH!K#z!I2D6Xp0Bǃj(tL bI$R]Ȁͦl4 Ͱz))Ϭ ԩSN_aL4$~Ԧ[nٳ,,3Ưcz9x2p}{ޞ[׼|~>?y8y1yɋ0A1Yɭ/?>Y$k?,caapHUUp>͙3677)+C^NfծTUNklmQL'5qIӸ Giୣ*,)NP"&0-kbq1ZʲD*$aLsEcLgqIXUcWHҴ}IVN:u_P·G>Oďso{Ç{عc=Nٹc7we:Y[1%ۛ$꫟h!4 vG'?$ш^9 z\{<}}nf`mmz6weȧ|s_EY9UUj3Ql9B(Vh. <42^F$D:ngh3,e]S{K,N2bs F@:po癟LVN:uCsݷ:}{뮻;|h[^t;^MQ\4d gO G#Ξ2Jjhg/vLc!G(!:wMaE+}szqLUU8N<ēlnl]%yޚ5,:jPҬP2R|Sׁ2U(]VCKGm0|;I?S?MY0y:uԩ3X| R,0)/z-xxы^c >{du ב(! F/,=,-fqa]v:$gqY&BT)A(I: d1EI$I Q`nh}}=LWdna yc8nO Iё?ckk4MI`@%QX) 8M5hnp)# ٰ(qOZ%GE8 EQQGmm(oIY ֚͌HP*#Vٱ3.]\'yXkMgnn;vpױw"Vd4z9Xg<,Z$iZ+7)%{BѳEZfk~76(Sʢ c& և a٨X]]a8YΌ[53#{밗eXk }fln6!0`cZ?sy51\etMY,..feƫZZZj_=k+8(˒pdJ#s677 p`D1"`:7RUd&iIpA>ZFmh~}cLKx"8ω,:x":[y:&W@x8lHO|lll׽7L'-onWd{lCZEt VgIY/6;)X%LH%c_ sdipsZ%=(P˽zVwPW9gΜauu5O5E^STEw4ӫ8`P{ٺpcchĎ; δDhֈӆB`q~WFR0a<DVgEQ;dii)JRv}˾*?k]f.K` +6`z1B#@ $rI yr}c\zb\–eɒN^3 {}G93}}s-E *Q2㊜s)aAV0pqq%/sY|&~PU֭[b9gYv3U#Tň]*I+rTo*2V)$CfDUx0AW=$I"MSǙXEh%c]4M( KKg~~M6%*H THkF§jz(Nm۶h4(ʂV3.Jm5R~x!c|l a=)c]w ݼ5S3tۨVN0hCVhSqsN>$2jH0yDf9Xv2:RT)k&K;^xq/99^7. + dr ]Y_ig>u#fm++ X[` 0LZJ\) YA%$mt:#,.4efڵXkv(2116 fff1e CF$ EBwJ QۿWR ]ʒ)FؾcGJFGv:{]VyS$%`pسoF$NF;wD~Qfd= 'GiAc$ޓúuH7)>.:;LQFT!+gBpM7g?\s7O~ũFw]ܴh84wnߠ12Q+j*z([R(k;K4Ji4C48=BUU;{ƙYhƙbll9碿//~ <~ς洇<G)Tp LOOV"B=۷}43===y' 333^=ˮ]fs7~GFeHΠ8O} ϚH%GaTYbX\x.8#ޅT\OOOw_ȓDfffx..erbb56+Rþq69_NܫsĎ/Y% bIJqD IkMyQ`E6B@$҇>-Hwt;]:g|kHS46n<gA k< v-"h]St@yk(v?QKY3?ȳbff*W \uܳ'6Z[񅚔W|-?2LRYԓq$MIDP?oGQ-zihEsq$[v+mOd21TWyȖ-LZ1laۙbjz$tmK6ɕb۶9IOA%)R)uA!BCYe ^~OYd貏3TH&F4vKY9V%ZWL^EvZPx!Br8$KT`u|'S9S^ʠ"uZ; UB.+v\sc˖d[6&A%2 hVi"pW>BPTXrI3nQ{767\ZiHָP;(%Razx)$ٻv˚Jįw1&M>O_8׼>oW=qgEVBfBD H/&X 1PWI)~#feeRtmٲy3o,5cjrQ/.!ea]1$9#i="SkٲxJ%kذy+kLCH9f+cx}sYνeHcǺ!w6IqU/ 6ep5*k;*N>xjN;t. ggY=뮿v{_ xBE# \w_CkkW'cH>"6U]^BnGa$+yX->\b>>&WNG=թS_~-g>"-/ϣ7)T _|هxy,]{sǝ0s}wq0OO>Y w`$,/}3y?I;|+C\\VBߴxk_sN• {,r ^3+17n ǞO~~m/N8F^ytzG{Xj5 ӫYznsT;#Q`E~dYKL:YF2iPHx.k[ .ma֯_GQʲ5RAhK5FcF&JecBc5Y>WHRI9QLMd)kgc⼧, eQh49Sh6Fits.MQ*VMv%}qٷ9֊Wv^ws=R VV:X cCJҕx+?ۼ$gap:x< sӑ&zټEU{ןLw7km[\rܤdXfq{ O|_㒃k~v?3y}CQq~OR2~<$U υz%y/mJI[ z<7ª~}bn/7N$9醋>w9{CFOw]o^~j۾r)o/~hcN8to 2^CëOX[ZZ9uGyW$oΉwogӺL0z+M6nDYTx{sooxZ^Ȣv\rWTTIM7~4m2s߿@l]w8xp[/&j7-pҵ|K/Kv|{o{?x+y>~'0Of=ofe3?ln ?q D(!(+͹η|o ~~&|v7-yEb+rߣ:=OB) &*Aie###TЫBh`[+lY86Fe,.. YjUӌC ~Q^{):eQ=JQ]<ÙUL0uyk ]ֽ^ 14 ʫ3uhzؿAc׮;nۆS%_ذaf1[|]w{Vβn$kP.V/!,ny'y9@>{ϳ9c$y&QةTHLvd2w/ktf:%ɃW/f8]/<‚?ٜ|.x8X?8ɗ_;$Yz(U)$iec|owl_<79ck{}}2+ȏ7G]+J4("5Je<c59D󡳉Ц.:Gii8KUT6i#9Je8mpօWUV0",asn8Y) ,Bc ֚Z)~0T8˕9PA,ˉ:M#HAYHd Z&΁hR>'OE O%Ko4XWIDAT.M0!';$O D"?¨ǯ~F=V>_ؠ6`e, :ëXC 0ţ <28l]$F#jRi#D(jWRuEYJY5@ LMCz?C5 RRJ c" uQ) 갼!K0PY+MUT JJuJ2B@R+ . }TbH>5뷰o,|#S$/(ɍ0y HY:>eIҌ;( WW0<˲Hc:)T($JehoA:1)n뽴P?HO"jp>X_ +K3,P%,CTy 3S,aiy$I-ʪ ] hJJڝ6ceIۥxkhd9JΓ,eld)Uc},ZHwz,FkO`B!KSʪ6Η{4F T15w7=<꫿#հD9g]LTH  ܁S̓uiA)!>t(Xe=TcυhUJQ SwPD)yNi5,,)4X-5-OxPLULs:Il)EO3Qƚ-rP 2kqΣ$xiY:4+LN2??LZ#ٽkq̮9$ZT,o?_zcb6:ƝeWQ`EU!U:L)(be/;HuplHڑ +($FI|Z2'c-1&B*6ax/I *Lk-^-JJ#4IWx)ZS+jPV0[D7,>$(Kd(BTJ)9t6NH@ځ<(q0ZM/Obs%b-8&!>@I!U}zzY^^MNP26ĪHVY2992Cu`%~~UQZ Ɛ& m-a34 -)"h0j c>YJbP$Cb0 όʐ2C#80%0FXA;?LgB(0SF/ߖ+0]k-/h Y 2aIp069"dt=.,-!`y_ŝws‰'07w *$^p-6c,҃& S3i|"bPu|4oaCdNV{p"Z΅Y?dXu؝ EmJI`8*96;uƻ0wV᳨}9 ǓKD"iq"Ptݧ+@$-r$ AݜK${{ h̬t]cH$8!&uyoawmڵiۑœ i9PQ}>]A"3m/|OuwpIbmMKM -Ol$i9\2a,82SMMfRY[I۴ATOW@:%IHb3Fn%{r"BM͍MFD֫O.ӟ,XUzn횹aL3dkjnc9x &LS@fVaLˮu;v+/WIƏ}ѹ&Q|)*޳dvClF.2$@}y\+owIcހk^њI T:GXrIczûbAƴ_<+oLnHt|ɚ/뎖Ϭ?anHZiQ|CGd<''l[nϼ3V4nh狟= P ^w;_:>JmfFk_fRa>uˆCjQ[/iKP=3ƙoV?o]QI? H|p @J $1>uowYbc_ O/{eroWWu,7kqG5" ->[UvP\Xyv?{P}}ٟN+[|bx4FKfTݧ\[#2+9g ZQwwɎ1h#q }a ]ƭҗloƜZs<*J p!mתjVӡۦV3*>@řWHnѢ٭79p1|wO4fΛgm-DHu |誱aѓԮ|43NM+ \<5%W{X~3Z8cjç'N]<- %to ;nn?x L&Aiƅ1qm=$ <wǂdTNhRC@Ug*la >znV -EO=l=o׭? Gײ!T܅8HF}-l)H rfT*kcTcfѷw  6}'@YD ,.:u7^{nV9\`Z2~ά o\yDvcБl^]") &tƒ*`cHih?siYޓ=ɳk4Ŧ\wEKBV]_qxGܑvcXqsѬW= C96}lEz֒~)peMOٙ2ag3} K V=~sՖd[3GLHi7֝JHmԎJ:~ג<{xD}L[g"kR(^q84MGX}w$l2橳/HPuL'>㮏OoXа5Ǵ cgl @4dUI ]=]p{N@"Ywq3+G1@f.rk?{ʰR|7]\ ePefŧzwXg͸z̩쑀3PQx6k T7_\}Q kQNִ8,L)ͬl/?opm^%m+:3ϼNƻ' zÈ%'+tdVB>%IJL=1;I ;:)7pI[ L:+u_HWV2m B5S)@hZ$Z4鑀 Ie?SaOnY*fs9{$iaHdR-#~mEJ%U)@Q!Oe$;29:2Ih21β4AINZ HdS# PB]SiU7NT^LЃޝ-LӣZG.boGcH(ֈDr"Bu W~FBgD_-\fl߾/^%֘g@8|BX^,f*J늾H@ApD3$\TJ($bp癀`ΓKo; t{OrXbX6R|T*]S]}Ks,XʑN(RkYox N~I1#UW-ʞ85zGHa8^nIV6T "$0~|cLwf6i^ 2-M,ˊb' Q!@0 COR8-$Ri "iO>D 41%J56ԝC=` 3yh9 AMo +5*;0miCMdyWܔ>U~]DXPe ̚&c0 : n9` HpiJt]OpN|XR^zbTuÆ # 䓾XsYu8g|+f[T'cƑew{>A}v DD ǧDdo!( EQ2cœY6|U_2xv[0ɪ?sST:kfL3mͿ㑿]G+R5 #Hs9 Q.Fpѷr*dwoN{[IGȘآE~HoV&65AL=Sª~CzRP@S SfO"8A4%mHw/&7M+Oʘf4tuTY2kk]<wcF1d~kힾOcrtolkg&-˨kN[]݁e/01-wf EirI{Kn]uq= Ște =xfA7g$5M 'iȋ^5t6kZtc˾k'UB&ɚW! !E#!]o|DgoƬџ\ܺ Kb;xĉ2 QUޔsJdH"[ "c;O4APjFm:=cЃ.h4h4:&%+wҰlZzٰFB3T*yKDąp &:{ٴ2F~q݇dҟ/m Bm0Tw92ip1+&Eu=N$'xwMzwU/0Գ|>8X2s/=rCX`9Yt]x2)*?J ޓKa;g>pT%&(>}7Ttݤ/ҊA0BiDb$pt%n/.a9$ntVftRSX zEl;\}sodL3upGΓID p%"!MeLeH|Ѥ#T]?ڶ {4iuuuH$kjk\eYD@ wI7z̉gSN̥|uIa-Wi;X?z7?ߡz"2}>[UUf#}²@AurC`k *Y~]9ؙʤ udCZEHmu1/]ɞɁh\&p|sሪFGmTTkC2JE7Gw[%祷e$zj乭!]uWC(_Ru}KDžw~<\,hZ͛eƌPWW"WUBp(81.1x;]@?7ۖ꾴nˊY~ uUDf-k};*@GtHcnnڱ/qvSs 912$ dc|͊:(jqUUU r}}}oZaYpHo( &~ŇHuu(Yln1DZLrlA2wu4⨰@mUcUd|>EQ\ttGJ $92[Of3B}ʴcpį <k0T%*@U5cڼ .l%nciPfмijD R(:C?އKd$}WEBKɕrBTjMrA[vq(3ݧe9TX0͆=htv2]cqd>)D?ȣ~'IKǵ:E!Rh/2/捳B;aK0XeyQ";W|>N䀘ػ6|w. z%:KЂCrF.U#[P:!*T|KiiE44q LZiÒ= )8FTLgڻ[\A, n*xŇs?y #WWLTnۜk}?9v[XplrGIR䗟KmTʕfrAܨJF"Y%-̽8]?tiT-Ds5S4]gթX{0 RMkY4J2R'4.pPiĶ4+Љ3mABJR9'`Ppj1 ǿlTt.PsQ/L~b}Dm}מε 8)v0V(\ؕd#Y[' vUl0Bz68yJq4U:<gE=[vjT>M8\JwBNXJ",neA!^֫oO :r8t%]߼Z[>X))XE>؃ U.S(ιq^ǘ F\؝bPI/#J\`@,CD%yE%%VE"*>ec8gIX2ZUtw!G+#~a;B@__&EUU5'-Θ~$8)4_*l/eCʰfDDR8C9s⪒y~>}1l|W 9b#ζx_mu}kSl:k4x1Qh]c+b>v|7qUѱo>>^7^/]𙗷nnrBTYl_R]p-W91zNi'OFɨlAy]$j%7_|uwJ iV: tN\c[1N[GWjڸ!%}`<i5B'q'z6?&7tI6f ) [r}`ߚ߮j~&2~tv%鴈G_>UFUhnu~4koqٵW^*4;δQ;v n(]cJ㒛{S{^G N ;nzc_ʽ M$~k5m2V7N;r0=OmX1Jc%*wmIOQ. K|޿iӯn]zw=fom\ufychn=Y%XwHκlճ[|Yus8_瓿YbͮUY|AC6}x QUôi D>oFWWY}pYWVCM%1].]ǯOuu ¡5\t⪥WUOeOv~u8^|u9ca C^QYm^r}M $; Dz֝ͳ>uz{OsxOgF&Rn'wm|88%ӣ:+R`ń/T̷ttAe6^sJ̬Ϭys;^1!HwS4p_~Eg(#~dϿ1mmLShXt֞J9HD<_?~^m)Do?=3OS˖1Һ  Zo45QqfeYlҵkܒ OٙrUM>{תg^vf-pP{z-na+g|rĠ'/"R"ꅣsՊi^99}+__EuCfsu}KGw?^9v^gJjtw Oz19gOvxUՏ:0`"Զ4g!9UMj bRH' U2Q#\=*DZ>GH:wrg(I AۖE"ݓڵsXP8"R_+鉟޽u߮zѽ^VksG$z4OW|Ԙ_|c^d;'~T̚5u܉uv7޹g HhSR$"RCڂH{4$r $C$!Y~dMvKSXٱC\HdH‘$P8KSq\DvL([ OFE0*hwvZJUUPO161k 7{OıjB,S2M'R\V,JD̊遾}k+]!vK±2/4cq (C}0B$<#+\cU7}G-.x6ў 7a1w "I$e~Hk7U==:)~_%?y2"pl#Ma@{COW n9&D:[I<8CT*zȈH!t,s봚\YO18HV2'Vr)/f|ιR{{Ў[cO}Cnyn/a7N$Xpe皵{>W ׏uT.ٽԺ^U|a-2_ܴu˧$w7Z2 4)gZCH٣Xu`Kk&5l3 hw(r-mIoe`ŵ;WnZF?R84k9FgN:+[g|ٱҨBʚ)_߶zŖG1E 4M9KGyc ĊokZu}$-Sѳ-NlX'dsj$Ȫ/rN3\?. 5O RHmBɄʖeO>og>=+}Tc(qpZs\+ʫoZ6R۶-._V@D%woێB(/tћo?U]vҬoh},qEnm"r;R;3=AX'O/k‘* qUUc_rUjǟt)jx՗ׇZ(cR2Q;*'r15tUWԇU۲޺|uT0y}n_(}BtX/b1/O>ecjhƢII$VV@UU[9˲ $Bd9s1$2Dr F-w$Tޟ|$3C p4LPquUDR )*-o!w>iM2Й[ -AR {"']bC-.+ԭtuhB9ED~qŐfoJ;ʻ0\ O?vu_qw}?L@,.JWQ]ط^?7)(@w{!"TP|Xuwh` PJjZp:U|r/H:#E]H$p64T$Kza IDAT Q,-|˭]Nڑ6}oo \rvo$%mY\%3> ]lŒGw4RJF] 2ulm߱H!ċs =$w{$B&dQrA(͞/yb cE:gE۾E9Br!{a#桓8&]vţr]͂L;ׯ9h,1iƬ*$ߛp艨rQd,BBNmxѼI50ݖ =Z_LqwL7Ϧ@E"X\r 8#^|qYY.,ASꚾdmo/[{  ۞v`(6MS1FRx6yH4HtRRuMmOW{  Rb"*8`k9ˑdro 6H$E d@xAeZ/&{iI @ 8Emٶ1~ Ͷv7lvlgm۱7-t,۶82Irl۶m:#msm!ciDDwQܲ5)+$$A(Rm v,(6Pl( bқz'Ho{̼?6tGoswܹSN3#_IlAv]Aԡ fS<{{ }ժj9sZ*+/ wBb6F\1oZBrdIU}FRB#̕U%%%XS|C1u'OJLLKRj4/uk(-+Uy VHNww{~(^5OFh<ǜjՆX*> U`dd޽ݧJAnQJC_WNサ1bbK !colYq~YIAey Q1UMܥ1T-TIR.j5z^xwns"9Sl6Zek-Ғ 䧷o-r)fs##nk/ /4ܦ.T*SDXxB>0`n` hU!kf9sƔ9v"sejFo_{^xqpA_?SݮjdF]DQyHN1 La!y']d-,/^xw Zp{``HyEp;q{gʰZL!VJwƍᦷ] @egYISsɎr7ݭ>[]ZaߙcשƥE;*/.q천u=z5Ͱ|J]Z1TY\RTicGrNwtt\B|-uZ^%jpB!`9BqO&smڝS"sopBC'[Kv>زM9ˏ4cIWgsAa+x}2šjtyP 'jM[ټY_|!_vB~-iph᲋Poұy;+ԑr-״wZ jįi;2ʇ/~4;m>]^~U۰0S U$!&YOyD)Lm篢[)~1ztcV}Ƭ*nҡ}Aٮ_VP€bBBߛvWUYeeY 7U-޶U8A^D`4:].vw9Z0/poHוɹ B/_02RcǑ +TGiR?,-On %/+w&=Bԫo`uX0%gǦ,f!μJcU/_1#_YQᗻun?xYNXr[WV݁9C/Θ4r#'ϘvOo_4k릙bB.qvxjݛiۨqV|ײ(g"a~VSYzyMNV?ݿčޣ?Ȯ<לeCg/>k !Vn҃eJs#]~ݳeF\6v urtϓFʃS2g;e4MΟ3Gthau5au!nw Ȉ^>텇@^LئNdysƙP/zePMu{8?Ҹ ؅ ̿crllCks(;?߭m&[K9 PMkѴͽ ;q^5d˹9<λLqr䇺d6JoNfO]1wfM[p !Jfyú6h{mooꑞf?\}~ aQ౗ q}1|r9Ւ'3d)#~;غ!&}cG LܰreUQ ~F#hT JZQi9_u2✅RĩFϯܾ.\; .9 ySN3"2G^uw.zRj>:덏"&eʜ }n=dߔA+.ڴ{]k9Aqe@V:\q}K{h-VΌ3uʢs)Y/G|Eg$y?Oֽ6gMy/% f;"c myO͛gIdAij1Yߴ/|7~>q][{wD->X5!9poYKKWqthܑcKZvG +G=(0POG^e/|sV+hm0qr|o޿O~>m%^'_\b`tYPm!]yrLXhf-;"M$LmQCC##eFnG4|1̕Z1tF8~}*~|o(2+:ᣝHo(/Xz6yy0il񤕏 ]s⋹'Aq얜G1C-%nm7QE;fD#&9i֩z< 4g *s9xj/7׸HEō$7Ni\_宪9]A*+ =Q=R@^+˜E(&}$K ʵƵ뤫L~qhiP Ұ^ҟmG=-ΊӇQ/ fr˺qrز)SL3×>e9GXn0Jqڵ٥[,F2Z^-DLh~/܂*)ڶk hJrKi(@nʘS lۧ?+\';i+: >&6r sEŐtz3@Õ&=[ljYa̋7M Oڿыo0o ha/F5f܇h .G{cP}7bzwx7KrjJԉ-0T."kECfNɥjբHr8nd nOo5 ń*UH85 <Ug>Z#dUʋzroǷ>g}a@N QaNb*:ڍL3ǁ7W1e<6&sϙ {Yvn?+5xs-ӭpz8zM|?E<iqṵ|)2/ ˡzp$Oپc 1nS-(6ܹsZ}7|v\C,_0>**[h`p).姩K2- o\;v{V-,h.).\4nK^PzUr]/1oc݋s~s,ECtRU;}##xYM/Y2K^gB8cdB ƯMH^TIBή[4{8?SQ4zPdO[wgB\?0C@v衣+w}̮a.,[v 0DviGA]ZGP] "X/cLVi#S;E@|.+;UmrAFmN 簨Q,#F%1joK 扼uso2!v.FF.D﯈);QZPPOjEgVc+_gg*@,Wص1@B^~e 34 ڣ(a3ilX #YW=[l@AHHvQZuQB-Y ~,WkȹOOMeB  ZG`;'7ϝuUÂW?tqOl{/4QJ/bOK׭N7&m!Pl'*q 1/=4Sۦ;/^{>7$SW-_DOZZ3n~\G//e4 J5k!&պӓܫNfWǶ%BBBc1Aظq$IaÆ*^84Hd2]mv$Nk`\pHurUߝ1ܖ\)ylsvSD^  FZ`F0D80*~8l/(l6Z ]d=sgu=&+**bgXF#L4RiE5k7oqJ5gΜ˗-\fku*)әs'NԫWJU+ k^a.*iM U!+)l~4cڥMZ%xC]Y)5vPRRb4/ E}5n tc.;~{V]u^b%[0u^vfd2nq_1G$l"ܠ=| ̙3QQQݦEj ˲Faa98NB8swk2er ]\8\_OD TBv:[&N|紉}cLo3B`9r s蟨qDtƀUm< ү?ozX| @y̵kn{_zh?߱]rpnZ݌䂼'%%i4vN700Pe]RJ)UTuq2`TALf|<>.jcLCGs,m_1LkHmf+ *\"UZ/Y"ǒ9R`Z~zC亍Dy!L5t4^?? r3~:wZI;Ox3DI8Uv1eX*/4;ygc<ԴLcW'5YrA0:vBBcJillСCeYvsQ1zNJkRYO+ >O0[~c~< #ح(WnI(El{v]ve!"S B IBA9FBnP*e`w|My!#D)!4UxJ%B/,HyL)0"AxQY& e^sI (rFRIR(C L  "*Ph;;ny9Q\~q<'r.=<.Xvܷن82$Y>3fEyBۯIQ+>cPI&X8!& SdA- 3!T<ݞGk‹\fcH7QBm޴XIs|6*we$?=Ѥr:+kCbY8@jce{wxz;A h(gwe/t\Z4:LX~vNGF6NϲGpݞ*SRɀv_? IDAT)I ˧ ϶,t 6|^.9){߲+РUưN!\EP@Zá{75l1 )عk2;HI'@J{_F̀9xKB/‹X}| h2FZNV),c/0Qg\"K2Ul޶1p87mv-Ɍ\¢e' j(gh8&jdS|lnV9GytjB{TRxl]x| PEfDR_eCq>mٷA ֧|hq?FQ9ߚef<3,Sȗ}{uBsu~>Uπfl2I]ʮᘭt5xޑk8Z$S)g)@݅% r邕?fw0/cS8Z$D{O\97˂r3)a~1-v=o`^R+IF R4^TD5.j̐ڄ艋( 0¢6} \wns}A/p(1J(.R( :^r)VA(+.ҷ־U 8.6:ls֏caTd7%U^ J hZɼ'YJcer/O](CR|0_ .B$J$FeC3ݺ}4onmiQOBc"_h4Yh."u2aLpBa1:U6i[1qB+Ā0%vśd%aRf'.ؑDzA>V2# ;O9QL{9091Bs=q{O [PjCjQF0jy(a9A.$eFh~B#0 XzV?f 1Jg򺒹K w͟ vS`d{Mk;?,!C& bbaT*/WSWWWj)#PۈN%f 38ѹpϧBj  F|'Ľ=,sWDPB!?©0A`Ja*FQZ1 @)ei|zD)JFM/RܻoHm{Ri1#@&}}Ȓ6aeo^TзcxfߎJ/J$ACT3_oж[ A.k]6s.tVn*&:tlOڬZaȭթRRb$|Mǎ )(ٰnOx)l[^̹wS3v#^}#{0ϣXƘfFoqz5&k J-$ Q}tbZWn>RBmȘ<*UT"%.TsJECZ@HyE!Js !Q B)%J"0Fd) RFWQbkS$""8/39KYv( 20&)sdbuc ] a^+"`LAP8yPB]! .t"r1X `F/2JKK/Z{Q̡EEExQ$''5ƹk=???<<#iu4:Ҩ 0N rF ?:08!Gyl @m@V2!yk8yW 3I.~w' (,Ղo;!9?^j~xlZdRoxI4qûN_P1WZdŋ;Rm!,*C@V|jĩ4U\D( *ZNK a^<J$%{u[Uz k%AiP%#}w;eK{rni{2cs{ww{ӈNiSZt0HiJ7)k{7=Q3Ş>g:"W%w4)!FmK?da+{y-5r1WůN_i."s r=_$Igz,5Xj;!SrSG |j.S Q640{[)b6g,?S|T4 \?;?t=fu2^w %!pV9*Qpm29-#_'6! 𺰠("Z9#qfO:u xc=G&;sGm Q <:g֬S2<.VLooOzqn— Ϟęo7̛x.Y_/Wg-R 31Oxc@FxE10 :OLTVIqUgm?np$~󒯷8.}qH{2ujctAkg6"3(+rѠh#'? A`T =NHÃ1%J_]h@ @V<$ Y-FM '\8 v쟵Uyk"cHW<+5k{Nڱ&/4/tq90EQ'dE_F~k(d*:l`v74Z|y{U-)h=/:IFiG*,Fj*^SʌDձf]SF`~Q<־_7KKz4vQ~r֫4>h4^F@ \zj߂J$) a^²ۖs*v1sƮI SUHv˴]b̹薉~ ī>_QEv W:3iu[IWŪVOhӂ}2:rn?wʨoXuw|]R*g{EjAnVgnts7}ќ_R#.MN/Z[vOʫ,y]dUQ%`zvor/}] ;)M1ǶwYXm㟟Lc;j0Fe.ŤKs?ol|ڵ^j]Dީ , <ף-:~~5ss+8Ya W[; eXf%ī z_Crou.6 VTݶ3 ,`;.|82"2AsEŹ9:jIHl.KD'oyÑ-tdy_.@ԘWL LO ~O^a BuVA9Fl+!8r7 L;wN7Vi7Q7N / IDATy0)202k?!Gn#v߰_G%}` J'7A1,>U|@Ɩ;6 IY?DwJ dG3O>իQ$\V 'JO;Cww"}F:v]QL䡑Hjc^ħQvqzC KvHi00ƀ1͋?GPzЏr^ۤc,;.JЩj)aY(ړ}jq۶HUK5Hkv~vmDZ/nʈ8^@s6`<Vn6**fڗzI) Sj5 ZA;v@g䨴 %[)ap,Ld=]5)4L||WڞUe#@8Y舉O5 xrWr'+ > xsSɱBdB8"9DN@Q-.rA#2@qZ{&<-TM5, F.a,1_*TBbۻT"cԾ"i.A`΋oo2'SY^CA%1a`W?WDMȺjܟk&<7\U٬ŔbHoߜb3;@e9jL܊7P^1f>cuYYYxx-++˝N*lULVtq1`o΀]( : R2s´v31 A)mHBdI xO?oBJĎ%?̱żVt5fLD*G+xAx_yqG1"IG,,U<_2cJqFD"3EmG![bw|'wQUM-7iB(;""( RD{DlH Hһtw| ɓr93gLl URZK CBeI늘cօ3#kq*/}X !۴\E/Nr5^#ņxp%%Z `I5ل'Fv{jyŘO郷/i-OJmRkƖ?CCܥ f]= h'N9j ):* 'O4NFQiUj*8([A \VdΠ)d[5kpp{`P`~}ؾtssZò̮2ĵ(@r W?Yy׎CB](D!߽g?GBB qhH)a[@xu[j}vl.#d " LF;V1"]⫡zf:¯E`cF>5kzfQB#"A.,A>jj!!Li5[b>ڛc:ŽU*JEP8xp2L*ը***W!T[[BQ9$6jg w܂(` #0!cGŠ7%0B`.EQaaaMarrSOjj#W詉%DB.3_}J7~Vkņ^ GU3ǻkXAQOk޾jzt QrsO3o\KTȥerƱ J(y£oNR7C84FhpfB ` 6H;O zS >Z*-z>&^wuumB ֕S[̎F۶=ukJ1 2 (^k*kw4C,onYq#O4H%Fe޹;7A{܄.'O^n̯+᭽ejOswwqEb6:uAx8jd͑7v`=N8=jj C3O7{u&X,6ΪSj本\!` -VN8N'Z=]C\}[^Bk8ΠKd4䤵N8ˆ]XP>'olٹ9yvBI#!ƬLFN8N/Zds|FDVJ87N Ue,y3.779!ɍHylAĿ;ϞM:<\~pm\\ݷr1=;'=035dj9 'YUUUU9W y@/Bٗt]879O!#yøG?G  ]OA̜ oIÜe#9dSS6tяvi^{}IB^;m+̍[?z;epB JR[p oۦMbɟm-⮗ppiq[IJC!ߏPĶ#ZeF!+{{֭?)__*B89wDLtdTW/B̛;-EH*88~[N̎ +k?/^b{nZk>:M6m$Ą7{c'E=zDUGv)Y lR<@doNGH=p߷{(ϨmlkU~5G_臱/R|45^޹D^^\$zFujc1wxdPdTZe݃4ʮ~ɔ[xҶ*crY!qsѹL's==r .U1]Bc4{Ӛ]慌6 0e'rNjגjVe uMywƾnͼt In-~xu+˞p9Tmŕcekɢ:ufZ2WV(>e@W f=MkUmS:9B*/-Wr qwTYӕWg9Uˆķp<;rI=cby2&=iNۆ`/Ϲp%(Pѱa^,sѕW$J&ֽ +R=wNPH9;RX~`> Ev1j!D+.\S*!06ey\{v= X|T' wފiYzygMBgzٻv{|{hpYF}#+kn|\٩Sٱ5NQ 9F gkϮVd\K(H-κv18Z]kV9CeQ),JS!{#[zf*: w))///wW:rwt 7/~*%/ =ǯmyks$cלvlo^WTP#CŜw^, tF?-Ջ{:mn2P|8~1_x2$}}ƶiYiIvY-RYQ^Um7􈭶UeI>QƗ7,=,t/n\6}n#O{`H^[Q^^K S[V={Yfn0dƦTqlg_HJpLV/ߴW՟ٗc[!N>HoC-/qd"6f{wO9XZQ\j]#O?Qzܼ`.&dABe9OAx~w{rҘ^MY/xPUZ`bc}L7uRV1dՉǭtz` Ɣ<*M?wG[*AgQr{*=^?YGOtء`p*6ޣ8>IJl+|eFaQzcj1-ymWTA5 k7m\ݻ ^y9 U%oeKE<_Heƶ?'^I Hb[~(VSٽM3xpԌa^nbhsV1o{>^T0y|1&<3I .8*5*EHTU}}}ܽnTqvBT=Fz(d{&vho}DŽM)x/Gw4{؏ƥX3Q]C947´R?߲X+:aȊ]ӳ+v۝hOgǀOO=gh!>c^9x޿U/L:y6Qش#VwYG~tGӼy?z5t"2_y݇<`IncJdn_8G)'{)@sL&%xZ]1# =p))↌ʰzN}I5eėδ|!׉7u;bi hOe1Aq_ׂ5# ԋ퇿6#sSr&bxG7^}!1Jx'Fm諴%oU_efyDM)%n2x,\U{K [^Y=mOw~E}S 9y)o|sm>㛀­U~u7t4[j>Yv #秬̱z:6W@@Gޫ~L v٩vZQ+% eVY[\lWW#Ud z{hxb+AQUŹo@QBn%{~n~U!ÃSGW$FWjָʼKө|P1De-.B:Ƙfl=:c"C@i6 @WCzpW) n\W%EfػV :m})7>x$[SuR(ThR8 +gW.`wj>X[ٓ'd4a={vh 5vțdTԘp2fVQ Uė~x1^vk'$g V\6Ko` _/R{i hfZNB:|>GSY?[o @6c@:QUV;3Z{zhT2CtdC:ńl+O[/$Aw@zQT!KsN^-X.~}F|tСbe` K1+p~oX@*[1b z5ew8ЬP'V"^o @ݶkĞ >|L$oWI,]7!Wϡi@hK v^gh`6NZ<ɆFo[AdtVVxG/mQe4Zk*M-~͘W~.XjM57D1 Jbnb IDAT左ٔaJvs@NjG /\Xr.YVM(\ٲ`ΊΥ':tYb)߾LfT 4= `hJh)uVUp'K۾j{eN#E?p ( Dcّ](;O)E%tf9o'm }Sy q7~ڄ'V؛Z!a,ɌbAQ۾PCk{vܾmt-\BYeBC{<2XLY -k,et(tJ]&fTr*rӖ:Prxɴ2`ؾ+B];x˒3;3zܜIm9 !<#5!/MmٜTwXS?Elw oeP ryLTsiwWz/u7}ji+z\tGq 2p@׾ n xU0 ub.Xx <ę9T_#Z49"8$@ejڸ Y%cX3g;'V9%I~Snm na}wط%}u-C fF~eˊ=N6M7OB!, !(ڛ=VY7EXLov U6ٯ^ RL.ifSzW K11Ad_-zC[nGƫp~*⋞3{緬>#CYdmBI8LxҰO_ ބe`6Cr]ΖʺN^>%Ӧtx+~P_"J,/>A >aZgX1LjsG" ϮxiXa3Y, Y6 ?~z_4N+ a#ޞv oX9ۉBq^oVJc,?Rl;dyf7$um&~66mx{hOZ[KDxb0HY0gR%2m6ԨGpܜ;N8V$ಔ_z 0nG:+zH-"HaG/E@$MKs;%Dz 5I+n}cdlg0Br/۸x?}" tQ:e`;cΝUڔ>y~7EC/MoۧfWb %={^oW/A+UaxYd((<$O\Փ&;,<1,@´eϾڈ_a}uRHV~ 7w%(T&>0ETAU*rl`hc庝oٳ?JZbh /.*ܶqc"##opsƎ]ɟ4yYԄk 7vgA} JBQa|5ufՖW zvźh/u}ߚ>HL7Ӧ0{Ab_Wդ"yN`̱IO݂:b2|lnsθ~:aT`onYs66 U/_T鞝1.^_nb ]'^]<6Q%JNk7(8/=82]vJeCVr71-1Mw+^?YG9gzCGz5u]d]AhtEf>N}_Lq[}&-;8)#ZXX.Vڡi]7^3MU|~Ҽyۄ~Z][\㎲[! 6 i^mAOQ!o?dVݞ/:5Z:vKg NOM)-zI#}7yǢM_[Cqau2[:eYVF$!-~=D?(^R&"EѤ0\ӣ 8یv 4E,F;So$SSRMD (Xn_QmykS&E@(RgE-7 uc\nßMϗ !\}=Zߚ sn]1;5~i;=m!?λ2HhLg޳GIIh+Ew#*͢;]QZRt5V6TBTzjvVfYyzVZZjZryOk¼j6b<*Kn&յӼ2 uPozLnf>A8Ss&g{ ?xmp,U]hs$S+7e:x0E,$W"Z ߳~ۗ+=ZJNK>މ,FOG;Ckp؈O KEa3^>-|TƄж?T~ZUWYJl9,rW*`uYyHqN;V^\ AU&|x<==y?tM./RǯS2nzV%da;{.rm@Lb+K|b k_{*I|je/0l%z >f~^xx߾>[8e^"k=ig{-i3GxE_{3F}xR_NN#co84C}#7㿉\w+| ǹ}w#cshLdYNhЮT= $;xt))/) | rӓ|"JU\]'Nh44M `ZvfۿO]WzBQ4g% ;}(#e Q c\7fdIƒ(J4O3%LBB@$A CS"c 04" "DѴ#c,I3c<tý hb_w_$_[(YiـXdr3M "Mе+v0㠠v#8+++ l6MfSt@k-":, IhH/HAw1L$Q~t=5/%KM,p 'ܣL rh\UеD2J<"BN.$[SQk&#X)'^BQ?$Imx׌(FM M'p‰G j]$VVګm4AR\mpvV!̈́y%XwwXsmF$I"D1?p '-HIHY42UY% Ea%FVZ;ۂw4Bcp+p '켍0WS-y1MӴ((_ jɉ; !ADQIM;D߿aȎE9Ȏ1fn}%&W(~ffWTV P DBVE,,X,ߘxLfd2OZi9q7Lb2eaN/J<EYlP2 (ʯY!tߩ gwZOw~W_4e_"c:M99ڵlJRTyzKd W7{yў,2_=c!09+1( 6029 ]Zd9\]y$J;nKy!#V3 #YBahQrħ/{nGTWZt6Ή̴L.CIRP pvN{SH#W*X `f0$p6h9Ɂ8rVi"6e{s+J1rP5%$Ө"\ۼr.EiBbdrcgL`i$6;'1=H^ۚVUk [9v=T}ђDDG]$IR*Uz^hM"(=722DYSW,\‚2.I:_MLyYGKĞS)BM rhwZ8YTS.y3|ɚϹДU+jO?wt\3Kv\S2I"^{eI!m;Iy<·;~ur@.,[l p`(†Ϫjټ׺M"w 8lXDy# D, |GڏJ 9Za9?|ןO|y*ZNnoNjYfLݰ{8Ӆ_<ɲS >+Ҧ/gXrhFW+&^ (8z\ZQ-ӹj kj͒݅AW#\ht!2<=X(~aia,hwpB`(C AyW:[\I ҌS\'G_߽iARΜ9cXӯ\heeŅEX=X)),exI>e \D+)3w06\9깞Kg/>2ݙ͓X 1;>q%eH4"`QfKivSN-ۭb^#)^|>8l76vE?e8,$l_}$ kMV{tErf]'IgY,8өYGu^\aovo'e">ѱ9quvy }oijvAcIiP:"&}sHm;=:lX6ۊ*jI0oZLֽ+(Id.}j/B0&`I$gZz}{-*{MaZ\0 ʸ]U4vK NXDIeCH<׆oֺspvl׻ksRXT|ȗ%ux^,]6wdeƑ->wjm=5sᗇϝBlefXp 0WgG5R eieEBv, ])7B0qcYgxO{f;V=[Qh{˔@/MLܷgoA~>ŠʊJ&feLN[mFG !jc# DɞNd  `ZnrH ؄%WCQ~]qSt C\n+4}+ɪ*& I7B(95btXp6g(u˨ K`){9QRff*&D8LVB1N%y,bKHu^@=!sޗk_̽2]S<췐/ʊN C:>?hSg6o)OEE,Uw^ssa$ ~8Kk̫FZoPRڈrk̴ #t0R>ԩVֲw@LQ WRk.rfIu$wVzpvEY¸XK? \ݹ|CPߞ|a'E3gOD+]toݥ˥EE3 vU v %b7d\_l"u97ĻZ \)|jU{OWPd&hA8(00TS[ZRTPP(յdj%S(JJ?|D>3&`bߌX{U`L_>hwxǫt [?eއEf͠Z;R*~ 2:AĨ),{ko=?@ͷ#;Ϡe!5 @y5dk(r{Inz$A@@bAQQP TH; !mf7 Hov99gΜlZ:wom6lS1S򖯶`ԦFQ( u|jo#3"Ez[pƌ -Aܥ'p&O{Q"vLk`,54aBc :JAc jd`X8kBhѬOPOIQ>;ෞ퓳o>顦Z9>3_gTrTd-k@yNaydЈ)KjTNu/<3sdͳ=๷6+2^ޞ?|8}ykƼRT|~(-'>ޞBpGz??i! S7VξD w tzIF#L|ݺѨ3}laANTdF ʍv6jYyURųv81,|Mݭ_k TN:ulqE\LE֏yaRɏT!݆voRA~ґs~:=;ߖG,R?ۣijtՉ `uyN+Y?}D%9i5D^`YR*I>W' 1V˥C!*KZ{~ʇ}#XQZLܤ3NBFW/gIJ F]o@eW)'<3fgјSo"w4~$~2}HO?f#ޛbș/=]) T$Bwu]i7]N{l®oz 7']!za2ٖS ~~wظ_EruWLϵ{3ZMY+7>x"\8e3/=OEMzD !"s$IeEd0XFv{iq i|o~wq!Ф gxc'/jѤm{% I~eƷ콇}vnK*piͼLPCY9|9 bQ>>JQqIV]U'Տ/O B1at Ak/d.1^%\)7v5%|;G &k3&u^LLڧ]|ݺQ$A [!&ll=l6LهVd͞s.RJOP ѯheҦtq\cW1v!D^-S 3ߔf͹-ENV&mnH/KQZ?:ym@`t%3sv9i䢃?t2h]jW.l]C0):`1o>^LZܗ:(?'2%֧qn_i'/ڻ1ywoOíHG7ytz_ ƤiwXcSA/,,s|{b]j5ߘ+kb_n:) m]DoGA/v؞bXhh1!}WI+ѧ舖}^ٚJ (>}5~:pRR&Z.^i5xꖇ>ژ6d`CB~t&_A B# ѝO(!F$FQ %b6ogWdX )W iv's :M~?'{WcSV1͙z2F#wto52`;}bC;栀\e0r+,11bIcgбླྀBE_ZS Y7wZaj]?nY:*૱"[i;u͘q:QkN̛$]?Eޢ$(|ݯei+6Wﭥ/0 o?m@VlY1&⻱cۆ,߼ujf l5m6oY॔$9KV蛭۷m_3F,Ψ+9?o'b9HT7~۾*M8Mwn_7.I)/5}IoٺekM]KWlK<(U:IȚڊ#LAm.n4DcwrE|J&~Qv_gy;ȩf}: _ M E=y`Iq187;+97؋8Ӱ}k(]hk[߬Kvb?6Af2mչe EF B#@~i55kg[Q sD^Hx`&=a}h}V '-H@sI=Cu Ķdќ!mYޟ4Nӳg>XQ 9:֜_mHL-F׼KЏ Rl(%UT̔pmS!/>[/g?tjs_2̯EHHM+l5`bߦ,2n{3}c5gC@8M3X7?unGFE*ܑ,(n=aҦ'F*r>@#U0W͌^!O$( Ll>%ޜx>]!B <&Х6{%b1ONp| pcg c7X2ރQ^n29o!* h5sBcώZ/q[|*(}0ױbDeJd221# "cꘝiƐ Y1` LE0,- N(f0%!_A!2vN!$?qXg ǺK SLIgB*,VV*8wflid뭂)),xmOVle"{h @$m9z 7 M٧]8|jXeF̆;qZ> bLzb/vX\7oo1`pKXxOw|3VtMHaA$c  }}}9 :dQ7o܈1@7{rS&Y܅k=׭]/GX';;q>JI҅)a"zlk:ʖI\4kX2D?؇hDI; F0;v]0J@_>ƪe)QP@fpc1 EET)idéSiG>:$;c 4QWQv # *.fx,ɈaL[捆LjM_tty\!sù {`$|OR'mּ/oeaX\ $+Qd ohX|b9hBxIگ@R}YH&ӂ%M%0v|RKSrA,[{юӛ#ߪs^0򝊁U7:,VkŋAA`t @H%0TQIXtD)E -P;/((4ԞzED^.^(@tIwkq_qa%*nQNTGy--WT>2 rxʼ_<{ʤ?e|tpZ>4b`~\άڜS;P@%^q )3|5eAeѵsǿs8z#?(:2N6oq7s/14"")V\Up_J$-Hen Ip{ħC~9aP̷h%codesІ[GMl┽M^r9fTv9˟Zv;\^?[PcF“ ,I 2yOA*l3NQ3l*Ӊ^4rNȝƿ߄n@)bsߌ'DZG@/nv! uS>u!ݹ|-~HWNks~mf;b:}GSs7|^Fy}$v uxl@M_̘#.$ǏM~6fj}}5wK}돟N-=5#怗o3rTѳFH.iz iBh'Ÿ~O ~ud浡 dhNn@`&.Α MOogvmgJ3SBzNjsp(YZo++}9k^Lq: Zj +,ͱ;J=*ɏ pX~6jժUKO k6cq$ZˆrNdf6 bX "p6΄tFmrb[頥œL&:؊U:Rj5lŢӕ8SO') M˅hc.e#mFRv&)[` !!lS?R܆0ⱧfFD9]:ujRhʶ2e$]r+H_pTO%#xgjRRSљ| qslD|.} &Ս5bP p&aut4fAȔ 5|0xRϞ"m'F=_]I92o3dgvbB]2m\ÙtȅgǙ1' uk@J2Ο,&!fL9I3K=Ma1dϴ8{֌ Pəz!c "/^Q̂=3)9PU#w@#@ ;SKј#":cY8Vœq:WNRz:թ~)w _? J2@JY+3vSMNLɗysd\ .JNP$Ps`TpKK9F<ɥ66̙3uԩȥY'O cڵ.$^̓8SDl\ۗ_=SG/ BXbceBRv,}1skF6Q @Ŝ .CĞ疁Ck6υ """I߭nܜpiѢlL=2%(8]ݺ ?.᧒gHWfH6m ,x'SYZ<*s(j:5k҇C?xmt*էT5J1N+jZs_ ~x{*MszpUֽo{{4URTz+eզ+D\n23?F{ZKreꆋ*%/-dnZW|kK6GGGj|TUMzDsV̞С~~DM&^V٨2:[|Ξ-ߨ|O* tU>͆~7VzGՎpbc^zie~uv x巫 JE{⢏{؈6#؃+?[uv_EpZs+IkɬgT] da5WϢVv^*_Sö́I=[loݦ=7GH͙ܲ 6ば{MJJjpS6Rm4oǶki T3^n}m|5,[ŬBjDG O 8VxAd0r;]R8\Z̨HkY^USߕosqN׷xeǎG jk zәe˖|AO %*TPMڂZ#i߯X٪Yu 2 cIW()$q}B *+ۭfצenA#?0-+}˶vD; DUPB=‚R I1FLIQ#NGzF #xdN*V{ckx&PdA\*TPq/8B}Fe~*JB ׬OHLdqF @UPq7BϽ^y(\dr1|f- X,C[m;vLmʳ6TO !`Dxl BH Sʢ`Ӳ׫7 F.K ba8saj=c^!}ZFaNHHn:ky {If]H0{μ5/~dgᑝےJ)j}hQ'Ԃ}?Ķqn~"fQ?tK2A@WkGw9QprڠXuF;^*Vw :n9x_f]hHHIXe{qju@ɞˊ YnhД&9s\b4zVou,J)b8X~sz8%zR .7Zx Ž4vT/,r *T'`'8")d1zkpb3o=^$oh~\*!11Lu `chN[F RrumB>xñ,]TQz0+<͏=+9 {ѥ+RӚY8Y7$k3i.$OnXK#X!G&k8x r<GB c0uCf!5hp>i1Gp:L}/6&!~hJٴz_:v";4J֟nZ@?D&#Sڵ  e)f!\|jyz}ךzSۂq %R w Y`u>g<%>G2?q rWRyKZ`wz{]qWtTrzQ卟>"J* Z&?H?cZ9,̦w>kp Qsʢ/=#%.ӋY~NƳ-69 AtAe|Óu'~ : >,u^vrD2=9Z׳tٳ QS#O)'7n{wmnQc>%f/|0&NrˈUPqWSb/`atP8NռAf2~E?t09AoN_1u61p`Ï{GPQt?E내X'Md#6L H>2 *Eq `X,oJZ7Oq[UPr,\C > ^G~}7G]ebJiHH𫯌`k֭[,+ڳn]x30ð,??rՊ% lx6-E2?Dkײ:>&ɓ"r ċ|ZP!9ju вEX9C%_ƒ޸Kgݾ̞2mؒ:}X+%U~LÊ M ]}-7b닷uU.E΍^yDAѲKkH>`{V~Zs([eR`ε~ٰ1\"r͚5Ek/AjeIJO=la_=ж(iʇkĚ<6n;a#jaڼ0"s볟f34uf+l]V׬M=PYjU_&{%T lAJ!gr`/ߣW!V@x$Z9EB3#}ޓA]d)~s,Pdu~c?|YE0a8x!-f\kR/i[zk)!{|xL`Dؾ]ۧzmaa!@eI,$+ԛ*e'$Y!@*.H(rbB!*KRZX־,W *|֩xSČBo9@A}j/_ƒzϵ׏|af_հUza>pٶ c9ȊI1h{3o 2$Zm .gSkl{tCB4wFd>ڴ/f_2Mw\6>;FhzY} Hire_G0ƢRB8 G+ȸ^ 땳kc݅NR31ϛ#f͜;rTk%:X >dA$ nEit mI1NLLzR7sf=v;D嵲dP #_ H(n,14a :$ud XkJ!ؠ.L(p:^5nO TMƢ*sfHɱX,ZVk ix6Su|EA=6tٛ팢DYNaU[ ,jXB9BaϞ=[v{ݞ0 {> X*pR%+ ! qx5dA.zb,kHn=#BAYp U 4yeY@vU"U$S*)A\},Xu0PBŽJ_@ saC_>\Wi*n<&S9sf*b{EL *DH/QEeGtIooZKW \%[J>SU^`?\}Y^~3!PPĨR~c2wGzPX*c@ `+"E A¯+);$k|\J2Lvѭ`GT22D%NEO\WI=(]}!WDlJRyrB**FixPq_#(*0#ZB F%i)ht FPt)JD649y{] XJD++K> ѩ=DY} D\lբScD﷡Ih-fAXzU*-hR.X"f-]dg\̼$Msa0 P^cHukLż ' j| gmPzgy51<ػ֤PE!TAs߀0Q6.&0E [TuK- IDATdeJ}B.TA[ˑy17zMB)< ˝=ccR(^r_c^C\Nu‘?I- zcT[YIBnp0p it1/ϱDboj\Z v^]:$卍T/HE*ZѠ1˃+ڡ=_yG|Et8stOrP9T܁FVos,-)/b0[~sIYp갫:/_%>kJFf3Fw?k\4EL@OُFIihI)ˠ?pP"KNaPY @Idf~j)eU$QRj>~}l(A R`J0TQ@uPcYf,ʸȲFg,v$ Q0fDvtUVFr׮R)⅋1}zN /(޷(mT* j˜ 9Qxjٳ>vC wK=&-tXEW6#^e ZeuI QY%(v TSd0| u F?0?0,=, Y>T0Q5֫؄9A~6bE=䍛l!ߝlb0(-NˮsS9|HipL)jWfy7Ko7 e@̎:3p 7LF6g ~DHn{; 7mV*6RBU$!fEBJ$ >+7] BBaQ@QzEQ"c>'@-Y^*04~kE30=v KKR8KL=WdQ.C)BR)΂"őO8D)0\DcF%%-]4]SF> P7wHKRDt ~RYâŪz *n¢Cb4"oBohPFJtSpdǣcE!*_ [7:${Z>$+N:?}4T1 Ƙu]+7@{ךt zr@) dgAJ1BxSקyK$K&TPKeXPa^sjonf`eD!gg1,pM=2~V׫tMϽ&'%ubwG( /Dsh{g1tC |&x!ns8`Tޤy~w=9;Eg5rUѢWP`ǓoYo0QJf8$"r.iQ>/4@S bܷW&鰶M5(-9+3r<_.֘(TJ6t Ѯ׸񽚛X_s|iM)cxhs:iV3NTp{#>4AqmfxKDs>mhW&Y4cXǟsHr{_7E[A%$"ANR+^RZM6훑y.01jRJ(õy*),+gr`kq:_Khur" (SJ) ̰J *ȈDr,@d(peYIt)U$;o$8+\Xt+T%%kGn3,Z䤟*gA`wqi▽~ܒp}XukEMu`PXM+T]`U{D\%hVgE)!Fq:-BHdr: KrNկLy+Ġܷ yj?PqX' \E#nNPqmPJj?Pq?R{IENDB`sqlkit-0.9.5/doc/html/_static/images/rim-bi.png0000644000175000017500000021647311533717256020712 0ustar sandrosandroPNG  IHDR=ŁassRGBbKGD pHYs  tIME; a IDATxwŕϩxG(KH $$ LN&`.qm>]{Y1k6  l@d!@PBi&xc:w$FHKG3wvׯΩS0H@@ +.XA&@ CuY " # c DJ@>tcKXH" 2ynW{Po8@C8)PJ"c q\"!cp"Э"E$'>sSJIS3d:") `!sO8e)_p* MðZvĶCӦeEtFxt( B0Iw5G2_]Ӽt&lY# )lm1aΦ*;8Ḓ-Trp((+L˘8tw(kͺc{;J#=vչ)fMx孷YGrOш=E"]WR\FÈTv4q΃8)E֕Ei"2 )1^>0bHw MהVkNҕJrH$7cJJ=%dP!HJS+ `fp4=7b0}AN 4H )[5HT ]+kV![[^/ " #0ɪeq3'ҙ4)l;7'sa'aBX,8:0Č uuÈC!t+׭ͯMQW0MM31@BqCGxH")|' 8ky TFU 8tyu 8 8@>Z'4A@@@@@[n| "LScY0" @@MlH.s K@mUVbn LiW[-M'az^(Bs{[E箶bse]oiho÷o_Y֣fVHÃ;wn5ʃ҂`q{k㎶nml5zokw[:idX>Z K<^nb@[Tz+9O/Hm!!v ,m 1m{_=k\#nt""d<!ô,agt\Ҍ_䵿2βvZ!o!qO{mS[KKuU0BV84N6t6~?eWGO#a65% fG"`ᐥso*U8rSgO eeC:f@L>Qrw⢟*Lh[[N; ԳvAduu/o_R擛JO[[yZr*JE@;|vV~sÎe;Ϲ,No]?b^zOT _u>ʋΟ[$vFS]G;2SN,gM5;g$rwZbO}3iujg1TĘ1`2Ҳlvѹ\rl+{L疿xGK@[ rkՏnݪ< fH' eۿZe;˦e0ONUMm~iS]yj=˵o{/Nw Zn!0]4,:Gsymko{㌙yO}ڴ\}\yȠof,?̳O<íwE/-_)rZn#Uq݂a$WRz%]vۿD,{ۻG_Ml=TL5?~|/>71'?C" 8pk+)^rWc|޲l983psxگ\:Sl@R~޴.%7:׎N#DݼK7ߖšqZ^ {V*CW<<ަuuNK !)}IJד%5˷fNץ3`Qжo{EvE}IH3cUK kZ܅D^n|@e ĤytY܈-6(o|s'/<^;zO_9 v4{Fs̼$Z[^"K ߷gVV@ b(չX(Z5[s5Mf) ֐dx^]_~Y ydJq#AѣSb$}t|Y _B5ot%DY!)ȷESQu8C? Fq"aYIm)IeT/TO-̚;kk?\1SYuZ疭ik.9pU.)O H'c%GOּnISTDE/a5tlAwS~%LW׀C`)Ůw_ˏfE vG+"c ڧ?{ ! y@ѿ^ǎq% ޟ-;#Bj,{*89p#-Fط={Yb {W4'lC`~+GqĖ1Wܻ}~}Kݵ+;'J{au1x%_ܯBnMT{t,Ь 8X{P=k]!kV@GS#=f1 HV@=qk@>s* +-VGE ! ` t+ ЭA\FK6cc a$ў?޿J)R, &"]38`3Jқx1C M t+"*Y>L* _!|!?.(CX6>q}pƄOHyלBzڸux92\Ke2D8'Qt+27oD9|=MӲ?>s#RUߞXfeHBN4~kc qя'_ t rr++uw7m(+;>ۉK!RT2]91"B/,,L +K^Lf`OM3RɄyR@>nTGtr8h *)J)"b1۶}y"!r"kzNNٶ=08hzs>#Mb7߭[}%9R D*&qv:m =b``*tww٦U\\8*d2ܼ|uTv׸i7n2m*״јT:۟KR*D44bt`E+?p``} -"*XN@_ˇ% _ti "CA ڴ}<ʊDN$KT AJ&TȌU MV2_T{Ι8P\RjxUkK3KCJ #1u;;**?V^Ad jȈEa忽'W "B I'_5Moq6wLҺqSxfƇRI`(|iaۧʧH9N8pq`p!SR!tƩ(5tBRJs"Alok5M1=/nbUFEx BH4\QYt5nnBdd''']*4dDRj3!")AJۋDWӏ:ޯڄ` HQ-kZ,wUuΎ#O+G±\DLRD>7|;h}K.[y㥤át&3tKBX%,M>wy' JE/o: =kɩ0%PS=kjهŸ=u1ph'}ĝ\qQw]O:>.vE=;<|'klX웮}!JiVZZ* "^߿q̩8R8N4MRaٕC)i8m);cƍJ_xDDc#+&-1N^q{~%t#ZYU{~*0m{=)4~A""R HcŅʌ!j'Mjij}?t (RY00<4XS7)'M6o۲<8'_~c9hcҺ+.Ь׻v6n [Vm.T`Osgϯر/zއ)g5/3upduv]{HckҚQ`$v6oMJz$wzC o+iں s[|iŬB"Lj붖aX%!𶼽u(VXfҴ2~wG#I&Oȳj-yN+YinԚs̪];;" Ӫ~|,4Ck׶ΨuKJ/ϏUnh^_}mOXW^s{faM=hq|3UPq]|?u6E3}9+^x&n͛giUߛӱ=(гvYPuuglٹOOpt3.wxap/G~g(th㉾x<9)((7}"'kr͛7O2e``0p8|~?+"!d29888utthO,R3޽̣&S8s(5xלz=κHcV4Dl=w.M[&|QVV6{GDym݌1"{3ߗRJ۲tu'r}O#beUUoo<m+tsq@D""G;ϓRJY][9 ާ5Ա8@u\d8H8E$յ\9 ʜuw޵'i}Kwu_{AGyje]X1VAL( ltӞ}ƛOO?xض;‹[;&Q.|]P+\qϿE#kٺGDD-Ӽ+?6oۼOaG[?ʫZ| ӢPN+ej%O\)΢soT ۝"Ο_~¶+[^\Y]m/g;3G6tiguJ}'_v:Kf@Pӏ ?܆O?jr͆vlN#4iQ%%x3LMz6S@bBdW%kiZYYy㌧[XCCJ)!DoooOOOf@D]cխ4a\!ľyv %HԸ &^qUT\Ð ڡcl) epY8'd{veZBDs1ҙ p84n[aI2HJҚg4?q>ꆙIg*3$s}~$X86 R8aˑB?%x }+M;|v'Aas?v},C;?7 _椚}b$Spkl?̛=3 He9pm&ϛ FB1y31RR!|Go741{JЙ?ngLD錔Ԉ/^*4mAd}o ƌU}slasO{3 ڽs^PZM};o(.ŧF E'חJڕS Qr9b֖?/^ ْJ1Ϙ߳bi3j=gO!O/B%D:"t*J 7ΠzWg.9O]0yÎ}kxB"3/}6\~1={/pDOuF_דh0@Y‰>1H|88r?B)holklNZA g͜9%lfvl/8]ͻv5P6crRZόhmmm3֋Sv7#Ĥ \d'rr-t2y$ϋbap9?ƘR4t:Lg\וDdv$dBvv`ٺe[$5fӎs6:/wϑ5H$[-رM"n))|'!7{\FfDwQKca{.nEpw|Y9A Q^B"MtI , H gHJL93=Ÿn"&{aPfD8 Yrz=Aza/"iV5RP<E@ y!f/$2B2Sr8c|'V'lYO=k3ÊHϤ79߼v늏R񬊚#󁁁d2u1V\\i[tw{("0Mx<ZN&i'3.٥ǺfDs y{|x.mg"!beo>ʊhu nЭ+ Vy~tCǿ<7ȟ)9{\>p_˪]iOUKϴc9q!{aNOܿbP{د *-kVWO&FBL[Ϭ1|$漕"W*'O0?.rb_)}U7~`A|uY6&c N#F.G1ݮA|z{qDs)ŐH^lwqTKfHgNi7&q7:EkwW2gtQ ;5ieh@R'3"ÐmYϋ@^q9:Vu "ȚDDmOasEETx ic\<2K|j\C}T< xu? ‡5L'xkkK&yO w.מnwqLfЏ>{{c-#fJ^kՅQ)Y'NG6v O=ל}G7\e+h[/8KPq׮==T/k{Cfݷ "R斯ubćs%6'Nٴ[4w !yJ,2TrAN,Uys6)+;Sokg@ (WP 8eZ!s=O?*p*D5V7u晗^iCQOoJM>2@ MyT!J% rJdfF*_#r҉Q V}tC ˴mx]_{aK?}1)cu(XNM-;jܴDc9]؊&9vu1R2;qSMJbiۍKrp߳SΜO\g_Η<99yя>GT~5-)odͷWWXolҬΘRʋw>k=:SWb^ӶnKcQG0h,+rapl޲ -N79s^|U28w~'(~13f#ٝʕ}V0e.X +6nx ~k7|M7%'0dH1FL GͳrsA\!"S}/Hmwܓ8so?SkWWVK%;PQGxg羼j }^TН^ r&py7޷~% ]bdPlXrchׯ=y턱[c *"Dž| 4Mr]";񎟧ίK˻4R9Ő+ۖٲ#atQJ"[T2e!GDkTD9um /os̹_|DF—pL'i+I!ܔR1d uit; 'H@Į{ul=/>#Qu1kc?ؓnkOv5nYx) N9ضk;.:sF~ٌO: CR},kV흕]h+0Fn_u%j:l3x6!$_YsїϜZTtu˝moSSXP]9*il$+baXڸ-wx g3=Kxxλxq 46-+x~K\6^~ _rR,{f5D6\2Ƃ$ICy{|~UiXq)ɏ;UճflncR[pTDaAy!jD) u,҈yHJe{!*+Eo_|a~5CK@I#("%8}/xc$' zXCF>栗NBOv:ɿ=7}LSvI_.$!\~'8 3cHȏ_X|ݯ>|!Mr-Ҹ3N -DRVdLilclE`8\=or]rg.23؟3gR G~q>Q=HL;3fwޛde34+jkOd!S:/| }ϝuy_+t##@THl.G8! y0!=}tRrTFJRj⇒pe_兇[Z޼!fd"`)5NuS$ Rd)`s]P#'Wǝ͚wrpl@KW6ܰݴzn,މjǶر-ZR_?.9+_ ߬T{ɑ%Q}/)$+^߷-}xnreS!9|j ΛʡpyrJ~m=L{pE+w OsO8 |}6LVNkS0ttF _?'ª) zQM8U' dJ(k3Di+o\e=C 3κ͛/;w ' CGƸEP2d,RR)W TSuz* ‒)B$BBDBpdW7|֥aӎwec8!)R #eUS8)hr‘jJ UDf6+&)$l キ I }E>_ ~?򫯹:m'ȇ < C%茭|_^ ? yWO4Yܹ}KۯXXYhq"hF1ໄ EZ.qBH$5`"֝vz˸iӳ#l-Pm)Oi '#1 \j]iZܑm[i!e5ð J:O!e0$) 5JEʑ#u[epr]O(Bٶ|ux `-ih8"`\,x a 3 :zoqML!I!|WQ3y]C.7 (WOErcمH >FM] ^PpM_Eדr F ,Ң 9.J'N?3/h|_mt#p-JsW{~d×}%]w[~=qfT, N"e2H t{푛qQn;Ұ gKK%8ΖȽsZH~Z$3i9^=G-뻣k7P@q'hGg:(uUwϜ9sT$(HV1#*Fb DEEň`΂@ *AtcvʮOٞ*GDp-%Uq2@cَeGcXXg*@6bI1`6q"me+s Dk%85h;pݨ]<8r+ݳgOm$Qݎi1 w9)G IDAT{\ƥS~v[^E=oޙ2Ho,~ݺx^b(۟5,ZԵC4RLo]c3 7U?ij< ~uˆ)/)жF4c,L1F]2H he#DqEIB-)3KVmNaZ/opB${;"( k#G{Z:~fH%H c;]={~sNu };#}go/mYO3}3#:hѪ w'Y~PVw8Mա7}[^pɌg^d0eZB-˖ӷW̉5nB05;篂6' g 20tF0(H)/˟1ЊFE\DoזYɎٍt?9J&Cn!x z6Z[q`f&!!ZW|4g<{UhqRR\D+PRo|5Œ2SR֧狏?=lzvMmZv3F<~-&$+u:omK0xЌҹ<{iNx[RMI )^1ӎ~WoOJLk 㶝""(32)=*FT!xJ&&b=ۧa.ϲ\ѕ˾? m;qXp;DQe`e0i3Dt9J dښ=ĆGBJ)J"JcMhق%a\E%mG:2-;3+J%7̉4Lcu-Z4ڴܼ~Azʍǝt0Խi}D{3d;afܐb$,3ԡ[m[kCI@~]PX?i ,4LH|P2 ƈ1BB~k]1Mp'5YNNZLZw4-/# J͂V,d$HeS^cDsϻ|ܸn݆_V-HХM4QZzlγ?ա{G fç^Z4gaAIO˗w=5s>~T#]%͟7G}mٗ3lɏp>EXxw?\pu}\rϽP mvS]&]x'ٵ|C~zo8_96~iP"X o{^}Q<*b|}IOӾXL89 /ی`h֤|m˞]&>#S޽+KK) H _{5j^)Vgrf?xv_pGc𘱍؟I 1nH9difޓgg:M>%cxLJw"i;Z&4` A=wr1[><On[ S$HnXr%&CD.ILk-SJ|~ҨLT|K֕)Y.s3 Y> 7o8I>cF7wiy~zݺ[M5N=%?oas4ʪ()啦?ѦU =VA]ݺCM,xp֝;./ݹ}Cֽ ǘn>[ɡ-?-[6sr䡠OC>`_s' !@5/m 04M@)+d>rʹd-.k~州nu$d @hc;ញrK}\0ʎ; ` m ` "/cuz}\\KG\D Z&l ϯ}p۰#3'L t@vW7 iSZq=&bcDB L؎"rI4|H`lM@Mخ`"O$d0A[&) qGn~W70^$) !٪^T 4HAQsdJ#I$$ m+S2 I.haY޾(` oA X1k׳.ddskRu,4C `-oYo7@-9HL2#T+P`b2|L4i.fc]3}7> ˞/gvns㷜^t:ux/}l s{Nz-1|c̴Y֪ǙWW|v a^vh:a2qG C& '< kfqG\@mB)eSuT)DFM@Bi(u4IG(!Kk)P1,!ZB@q`*0"FJDW@3L)( ApQʑ}oKxifd^vYohY~Ȋ~um3f|9 [e%{_`]5NNT)ij;Zyj@W߼&v]=t{ n/!C{[R/SfEmu 6Lý:7og]=Bw?,Ɩr٨_~/Y9soonմY{~+&<7J~}q{f|kst*/ܛ^|dw)oӠ=KF<~Γ&4│S߽8e>lu"(h22 .]B#FE C )nI3 $ &%?07Nx-Abb597PDPPQOhy%IFeHB3asJD)ByKHJ꠫2{<2UM)+ZZɍU1##@psgY?JFA`T)O=6Ҡ 3e^ .YκBϚ>͒dZB:x"I˲nd/mzwid:pWdIg{^;!*˶n%OxqQ_p⅀ĭ^# N"aE )]Sڽjaջ=r r/d.n|mypb euzEXnjtm|vBe7JXeѿXy^/wL]s|pQ+:(j6WJ؍'nJǢqEwnn@@B4CN(2"IP%^ĐKiRAjZ윋.xa[?/8מǦ*ɕ#~ @!Lilܐ#p@B}0BE 9 $j͘Ј%H$ ) !ۭ5&_v B%#Su {_k`AёwӰ'6=njU*K"2#u_~}]~zqTW)K!r"P=2PWR3O 3p*>*@x+ U4~?Љ q8k?ѐ?zۋ^a`(_[{_i֍WvYzݒfѴS߻ C<<)b ȼPI:ap;Hkdx;4id IADWylOg}ۯV M֌*=iRֺxR^t%Y"rF\!/M3'Hff} p g|S7g`O OFeU7[[@7'\e=F2m龢¯|%X0=b[{j3o\x{%48οdZn;*7}/VϏ_XQ׹UfQi/F r0 Azo) ՛Z6SԞmnl4ҫ%;U(k";fOW~fA"=-X~ќťՆrfzϲl" ٝZqN禮-)(%k@#aGbZX,NN5ԕ)_IPRRI.4H~}&PN&Tb:~1C/3O4V<2-ADL!rSph1 9J"Zk̈́@k5h+a!*EBM^=+/+{[N}I?z`Z!Z'#"FDF&2e3@$ B8y/8,7ol'y,=n=5jE_ss "m='7~NgUaJMSVӼQ"[]1`7/͸!YƓö_m/OM8~4j ??+;b;ᗮµu[vC&M~Dm۶|fM?`Ll;2cEDddtE#0"9*NH4Cx’Ȧ2_pjR̆ s[w6`H΄C2۳Q6՟Ii!vR RqT6Cƈ;qX=-N͘|w7oV%@^?Rn|ATZ3 hied9D D\K$×l `ZckhyLoODQKW1ΤTL+JyE4i%pR%7|׹KscWH gJi"G)%0C0DF9&b!P) "ƙ`Eഔ 9i-σG^WZ#\K %qWyއ.HLp NnS4VJ)Mq(qU7X#* IWi 8]E(8SJ!#yjZv}K |XrzѦQc@~ pNN8hSHwhkegr;"@ޞ#] B7 cLi͛B}ʟ*U '߱"%EE"w_?kS>q0>JZb񖊊2kѡQRd[ O2N@ќEs _:0سyۗӌy]Dd N&#L#jЊ@2]"DWdB24CɁ9a#̙_LdZ?#2H 1LNJ҄|zBrKXTࠕ)EFi數& @iYX\zB\RJJVf)R-5w'RF%̄7^N֮q9%s aqAɪگF%UY~5ꋑRdTυ 'Wz DqֲzQu##ٹ`P*sޢEv̱ 8"wLMMap$';q&iGf -U4Y皴0z)(t"95P")eu^cu6m۲m{FS^PJ(Ϝ 6jyI5\T}4ͬ϶9DZUoV].|` bh13kҨPbݴ_M4 Ӆ@L3T! 3\ < BC]ΘF]Û!*OM)()o]#`҈7 .P󲄕 1r2Qm<EI֕C 2B`(v(HĀ5)uO>*C"Hp)TQSO{tGTݒ2g8dHU1BaP?iii{_=p%џ`ӛ_|}ܠeb6(%igIJjoʕl߾= {dd!;7|,I^2{e6m=w}EJZ(&o_"w=s_j4긅&29,s֒~GY~FK+wSeu>2cENU<0}L2)IB7{ρ. #s#)$ "^Y07h jDL!?ݑǹs̏޿;vlݗ_FLμjGu;Vne2*%kv\w)'/u@Hq3V|ն۶ ßdAjWF\BJw |XRGH$]nYŪv8 L)lKiB{ȁ-a/[Qr2rn>YVVּYӺ!R^z9|%X [ 0*vJm"1F9_;䆽'81cGʕGv̘\]jFiĉX2/B>1oFM"-r׽/T"mYpѮ2;xÁ`6?j YGwVv-XGǢc[5vR_Gi yĚw$if!iCuO|rEڥ9+.1uJvi3n̔w&N~IDZo7z1_uY_䚳.y% )/s9[tiلw%fNeߟ}J 4'kz-] 1֞6&wm[[N8ֺ`OΒC{"?ф=c܊pT"0,d IDATMߌjpi,HtKe6ݥ0TTTسDD  !5~;B̞#n߾74=y O_wyy?tDJjيvK#Nr@ 4K]3޳UVa((3@Dp'.oqy`9eZ8P{Jvԥ훹:^*=㈦}Ż?x="lQ#Z&D>cZ[ Ӎנ @h`nvm%wp *秵G4LKjԬIU(띉oͳd\rF'<o!7CppigyLy|۠C9˕9Y']×.裩IZK tjq޲]6G}h?k[!W%Kѫq19&PnZ.v#QH$tpq0 6t;$f?M.+nYe+%­NnwVIi1F8JW*~iֲNGfy3M Fbnw=~UkoOijXn橧n())9С-T_iԑeF=7Md[/ٛ5" J]7f}ZsO;2D}Q8*sTl{ 9]ƗǃIFq(4 6|<&@dHFιԺ݇ª{9l.rVP\$ A۴A.>+)YwGr5f5mۥevu'DtuKO>v܈GڠK:u>Cd<$?xD'ㄈhţŢ,@rjZak'7vh5D ՞Ix^-8a<WRHs;ӻ6YyӆK=CyyaX,na۶e>vGJZ:oؼMWJ)qQ) G-DƙD5=MrFPz534^tٵ'|=}"5 &p,RP. YI ܲg 7M$QeL|&nn^܂u,S"h_]T,D^ 0 ]zS"?5S<^@Ƶv2WIHdN"!i?_,$ 35?<;v==k?[`3uofZp$oS?ػ @ר d6m'D" dոc,Ƕ/ӠIVM-Ɯw:u$¥d6>Wj[G@c]tg/Fహv+|K?m_QͲWn}aڶ@ 23ػ>{~RXv|9`8RN2A֜s0\Wftk }{v+%j%{h7۶I o[/&ծtuvr$gHDK3;fdUo7 vq#[DZ!;Vv x64$ ȔDE,IsI(PFL25b`HD5CNJV\> -פE˻_xb)6lWߍ.ظj]fRJI( ?, &4idOꅿ\v=xU\A|i׍7>*Sq $9zZOHV"IJlTˀ5\tD`uZVca@ JN 5yA>_()dUMjuP*+/G FMҏp/yR*%%qzU.d.rssD$Q޽93۹rH,'9OIO,KQř!w^3 c_7堸iRa# p4{_a +/}=[)HPG8A_슺}eny\fdYJpSbBS$ZHdnZޝo}?URo/~w]aM0狣פ߸i?^8w^؎>Y9_q]ML#Ӥ-$-F Mpk`0jЋjHWVVY9BkSSSիHO3cH`V$:aq.BP8NOO.;Ac JKKc^$uee|y_ GŹ9?=+?c`VosI FrR\rVbݴKgZQG лu]yAk?>8A4-.BIFv2Bl6}ݍ Iq|A"5I cQH VG1˦_J|IkS\`u 4[#!zbZc =i`䖋s7ry{ֻm^0#eҒ30c;t9/RL߫#ꁻy O@#bXwtsԟ" 뽥vMT$B)pBkVViYYNNs΅BƢRJq\וRz*m`c²@'_)to!=AV*M12~""IǍtL1U"h|3GQKIӖT+k $Bcڳtc^K;CBDMZ+V` =گ[ H "bP-O CkRJ(iMpϥJS+N|HQ/@b(UIGc_ ; &''s+@fRcv̇dBjLK_J9s[zy@:u5#`F͝]gsvfyě.M1i\Gbմ{F[y̨_0ms*uCa/|g_r>yA/[>?m}yS\? I g5P42=%M1p$--VauPv1gϾzꗕUXͷz !l...9  LBp=wisN0ܷ/vzN^oxp)I@!Ej̒1+VۖڸaƩY/yģ?Gr&{K\Q ۿOfۖq\EwxԴ>=K&'cPuK#v?^+WSZ07n7-dfrYRoB-r9,][;ּpc{A9ZK%ЉGʢ[+=nRP[!bԊ %^ߢfȘ"8!'dȴF\xRxUk0jY1$I&E}D0툴J"CθtPjoڲ,˲Q"̌c{/c I)ѨRJ)ovRRRj+,~ DFh"bԨmjRJ E&~~j0f Pل6N#m~|7Dz{7~L`|kQyjah MACO8QXF@HMpq45k3#O:G&Re?0V$}N'DQHk@mJ8wPG ]a+R G5 &Z\Ee"TK&H(O5{ͽEJJSk3MAjDR(b+8̛֖{o*:_rSX\k]sifѧtU01~P9{~i~+ؒ KnK]u\sKxògs5xpYC~D >yѺ]8G 8Ԑ']}bkhx]t;iPwxޜ ]Zz+YYQXK{P:gKf?Vſ֬+b{g7l g4:wf>η[)eFƉsnCq!P0s cJJ1v8`m7t×3D]}2DLNNi԰PJvM?ܲl+{>㜵h<778l|۶MpM<IG=$DLNNnjyn2&un8r!V},v9Kr!Z c]iZkN;f- FcHD䁱BOkܞү?b~eP#H!SriIDK~JHxˉ35ը8Psa (h1 K2iٶN &;-(jKj&@X#e)d9 2 Z#W HFוFn|'#Kල ҡR -̛ؐI5uNcVgOdme{EyεBwď==ƿ9أ}_2FAZOkm-0}mw\ud>7Ve:`& y;>Fίax7S3 RSQkiKeNUd;`]̗rzwQDeJ$Q1M#b!x˓{׈;cAfqI1V* O oWm(Wtiْ&Ox"vYv/ٗ75o]cmvdet}j܁G|G5Hb 'xa8 /|^ #_[b>;c[zY}w͗?Kyz$D4i\jivdҎw&qUIdԲ|pVxOy߸FרjMnCIyY *0tDB+,i}&<zLJT8'29?dJZ5J0gǸBR44A`qDb42 U:bLBh82X zVU9$I[9P9\`<`$bT(5~p)-%qe[U J 8ػ0)}Ω²$bB1"*A3W1EĀ5d@QDD$J;gv$^?Utγ>3=SuBF, C;2IȎmkDaF,n$†eV( ! CH KVh$ak.#I $ la$aғud?F\ǞP{2w y@eIbDlLQae\"/Ӛ۾|ZyLȮm{B'Ptse @tl߮jݛ;#Ck]ӛ;U^m;PWJM6N2r"DRA3 zeWLAd2ID.2jT`肮%ϝgZ!$Zk" BvaӫլGsiRh:z~!iP 3$帕jU!{v( >%f P{w1e[fDߗGa(KwD͐G:eB$aXtB}O$LK#8/HHL J-'$@%ox h*QǥC1S1fV2%ȕD~( 5223030)Ϻ{3mFДc?}굪'f‚Zw7_)V}cۺ1^2S~{dsy@9/?/~Uz}v=h%SV6 wݳd7fEj$ M@hv501(\,*`Ǒ2js`^]˟w@^?x`_f Z9.:.:ߎ@ L2xovjXu"DJ$!+}`Q) 藭:aZ&)ìs vO1>G%{//.*ڵu[IV:^|v˗t[1 /}Xp8V:.Rjw>p%gtbK6n}'xmk7dTtӀ|d3KM>{]UoCM[Pk.KCxԽ @ #.%[>4рPV;ܴ|3:^b99fXZێiҪews@mHNHaV满*TH B$UIPb.i$䄠}>.m˖-B1szzG?Kde];vO)m6q-V@9ٵ?m~?=[j{0{XHJQPc KR dڵ'zta0$'$a۪` g'̶B&@o>1AHK@ ksc, $&wYSE5+2 :ԿMDӟVSzG\PZq+nE HFE pȖ(k. OMH "GE4'iغ=zyT"%ROZTLٴu${ۤRX)m|]۫5kq7g3__`[{/O[ \Vs FC^uuYݻѨM  (" (E ʵI!!0Ze"_+Wd2j-~ۀgB][J<{-UR n1_S~&>p\2: XÎxVz퇳 I('OݲnSx^3}֝[%$nrڤ~re͛7IBe,oղm?jP/vŚ?  ?Y|("a)kvPaja;3y2fY߹7;w91k uY(P H$BKìݰU Zֵڻ{C8lbC(³;_yZj6b,Z3 <&(S*i9B) [=?q\ՊS}.k6`qh}\{S;v;p@ON,8rS^CIiPOW j m`- R7qĘw7Tqs F~dMڒ^NwY lfA?~}V.25/A3F dbNHfBКAHfv o6ۯuni8,{ 5G#$ccʚs_ifJhRSes_z>“]b7Cشf!Gi PkEj7dBbBeYȰ 9p]Z0˓L#a:lEc!1 j"`dY}Q{L O.Tcƌs9a.ܹK;>o@U^18ξۮ?uVr'#3uiעYLR{mλ"~-۵i e^7oqդE#Gz5o}d.zܶͯ6 0uO=gF\s^ێW1KHz-[=ek?ҺMg,yك'M9]놽[˛:: Yuڶ8v hX{՝Zjs ߺ3;\zǏ}<=ߙ-uaBDg?^񯃡7]{F/( :9n1BX>`B} 5i5)Eƌ.j`kr#!G)( ,EB 5Fֈpp !6\6FM$FؙYj`fBi#" G"SA(]͈H+X VLS$$ih @ P 8ų>d ҭ}Ot_\6~kȴ] R 5kG/}gNQt],^tOXYҶٟ}CF߿d/^io-mhuw-[t}r٬?{Ҧ>[]̎eOf=tT^W޼BV*VϭO7w4+_bCtPNp/:_جOpO_Kӗ~vc?cԌYӊT;=q˜.CކYhI~VFܾ߯Z7g'3y}׶Ok\zEwqB&TOrsݪ7u fZ?S# ( 8ÀqS̊$-.0<$ր5!wϡ0aV cZ#DPG7qb^)J*UMf ppbMtפDqR4hК 5Q^B`IkN2`AОZZK @pl_S׾<ݳe  ᷇nҙ~W/~# Q|~T}-J:kFi&@izy8R[piek {8nN^`;uzߚ ٮfξ λsvI_]ݯTTp˂5ӣZx~˴/55?d;G*_:aUsVg4muFvu/m> 6owe\ӹǝ{^=uc׽RѰ\ Zl_ϲ"t<|5 ŵ&m @hM[W<o13 eNsvr)a5 RKR"kפE͈(D(8J3IC B`帊5Țv!ZR b֮$F&wiCmuo{啫\`A]e2 b=y:! fQAB0{Rn(D) `Wū300֊aTtxҋv7/}5gM^,_ "!n\8JH'n<ځۺߧwEُIxJT(v5ㅽ]ʤv9iMm%R7NPd,erj\tڅ?q9=-P m@Ҏ@\!3pܓ:N;]>黏 J^ׯg {pֲ^_G5qEHJfwiWs࿷+C%MѪn}=l+B9?kƽme%C=9zJO =h;Έĥ?~i#-D\FBr4"@O8Z"iS a&I5 y?h·/>+2yŸ澳xƔUrYH>x;H}יFϹ5@~:~w{ f}.n2VK@k7_-'KW&mTqs?$a{wk8C̐_pmzc[W,خ_y]zVb;#+yڌuv{֝9k߸̝lC#{㶏c_tt؄~GS_Q߾z}WA y?}]E7=4Q+OCKz.dwݼ|YϦjkmts[ڀڛL\ +£9$@hdFvlD2r2"fDs#W 2JB:Ό2b$,B$Z#UUt3;}zQݺ_?iqv=#~}4OȨ^ |I5*&{?:kdz.ޕOkzk|ǵ=zfUjxj :G{$y.hi*V^NFsRU+Rf"RO4AX*/bT3rZ<8mO/q6l.uTsʴ[ft5t~!S=w˧_V^ !5#=`3TO |t[Foy醝k/niػAIߞo>doUUu*|g/*xϕɀfj:p}mFk{Ǧ6*8~~gmܚ?q  vd˩SRU@mXCZrNJW<K@;%-|s۪:G=:\Cn{n녓fmrmf. λgʼn~?pؒH2#]D౻=zwW7>GǁwRzNFuKGxNF |DJIrla *榔RA"!ĉACq=}h QhU%KtBe&Ba*t RTFf BnoHssFG; ~ns]VԘlzi쨬zbX%j 麛jc+<%'Y%[4w;eIGIǖq̭k+VhXX53{V+fOx(ٗEr>|ԫ'vI:z+ɔ&0b'^-ӑpcwW-?sίk1kF?Qf L6Y+FBr;{z}X0azQGJd@J̬etǭ?l| R 5C.0/6g(A0i@/4M$"2#XD1 XVN0|R!*@o+VxlȯCzqepdo}]Y#pMy]LēVv;V8u>Q(LBJJ?{mUΏ9iQ]"Dr (Qo/8qv!5ѻ"^e_Ӗ8߮uv􄓼s]u^ u*}.9yA} zS^_Z_EzWF J)\t%tQfB|\ RxCM..G:LQRxӨbL(; hvYs,1{~g.%3CKF.ddw0ѩᄤB4fDa^UG?2Gg^Hzrgxi&kO[NCo&h_E=B@WՂ$iHlg`g=(Yڶ"E_g5TјlG2?! tщ+j@!$M>Oo0# $e4(%g!O}]Ө\@w&iA@D9 XHX3CYzl`/t R3@",R ;_)+eG#֚SRQL3GG\?]QϺ|'o1Y"?`E2XVWɉmQ B>'g:ᰖnVPX}f$րҴ C0;J_30Rf3% }7U(0a]6-S dQ`8">DB.# A[}2v XG,)#.}Z/Պ2Ra'jG({$FĈŀ IDATA@Ԡ_TIP9lsIbBRBI'%Tw^}?2"bR~Dcy_D37Xt8D'za.* ud D# ڱT8HtQNdn$tұ&":PA "![Ʈ@'b;e#nt a;xÑ?74;4k!L53z2\. ,<߄s8 B*0IE'R @$`HyM%'<%eI0Ilj!9b QdO#9Z#|t,%E8߅]v@&+D@QV$B`, Fu<2GӿnN?+(*Q|PQ3{ &LtћH5 F& bB" BY@DD $1I# 121 ) bip0_9WO ` QkV#@ԯXJ-nqT5}/Y>S4tB `@Ŋ=(oQ` VB qHDr)p8 $Qb=]J45j#Ndm0YOzdv2h""\ *TJFDDE,d`FL@G= X 4xHlccG製YLkٗ2˦0ƒ2{Xתĸ$}_6~#3qu0YxXXΈ8nuv8TTpX*~SOۧu Ġ|#K6Ku59 _PQ.}h}N*lM<Իキq !dBbҢKkԨmF$" -\/  5+LJ+w޹aC-@VV.ta4"EdGq܊[NqCH8˓eㄧnAR<X3#{Gfv3#7D|% rSO{0k`$|/ԓC'L`ho6o~]p4W >5 5ƘZBLB)QAG}\@V{xH*u>?)/?zKa>xJڠ ze9]c1qQqԷ=֬ߠR8)fZi@\-%jKӢZ3)ogϚ}*O&$SDgf(m է 5k~]w5%d@`XVбoVT1)5P־ukVLMJ+EGU־}Y%!,{1}C;lSX(XjmN~DXE/XBL!Ԯ/?&P(!TZ*nҪp݈^@P!vfexff-qg>dkfϮ٠'vZ:Ã#mkn1`?S\X8l5k0RFMR+VӲQD/vYU,d OM5\ٿOOQjV׉VHO*(Ͼ/_.K 2v) r殝*f+^jzj>/8fjNݯ@um7Z8v8,,O[:IǢ[i hͩ?KmG8aØR%)L D@tޕ]s\vRjV]KXᕑcHG/鯼Tj/jUu6mszu̝s3/ֵi#0Qtw T(Wk}ڲm΂4tB~>""%p]"u6-.(Ww=pf@ʴ˯/Mk֡]},g6?C-v]7jם{WyWo (ݙo0.xvFݚW37ʧT\rU*bl#r<-op ?<9 _-$޳t_BR#AfW<qF8ㆎ;aº5nK te:EbxqCٚYz+>Ù#{lƭimspc}qM4hcCɇ|lQ#wn)R֭[gٮowl{ɜϜ[1-ǯ6P FJk`~7λ$U$:krb#>Ú9{5k-m&=מzls%9kVN+ Eg7.O8J3"I!R~_3ᾛdhszG}-|ߐJ~Į/=w e=47۳+Ilg|pJ޹8E_-$aQVa TXTk=# A.~p乾y-xYY ifP)63PB$G :6c@.ŪYC敓\j\FAl$NPJ"[ `*b#H;BB\vРv 4;o6"\@`$ bB;H_6of! !/ $dɆ`THYf"N!"Z$`nD"[`h Lڅ|&# 0IAbP(0Y] 1'dĢ~ڼ| 1!x׺[%Uעaubg׆U;CFBz֍$޴v>G&6lؽ.+P1sҲc\c -nbmv|0.:7fg#Ȧ J5|NܑO[,LqU<~yO]<}v NmiO?6w&J(y$Jfk2Ygj/<̓%q\j a ARQ"z *d0|dؓ{W  $IDa0H$ DaH@a`g iI' d0AH" aD7!$QH i4HZBBZ(,! !,aBBF@4|BZR- ÔY#h 0W%z2.#E{F=6a%l|֊[H2煏V%+ڝBn`j$ʢБ nyΚG-W/hѰ_wWKdujH5ڄ_4g?0 _>tP0k{[*y\+ic\qV4phujXի$ƊdU/hajmNK[Q>:\~r,a}߮)8 <7量WW]yZ6Nh\ā UCο7v_2xg?n R@ݩZpK DaԚ!?;-ahT~]x1IcqJ2CzQ'QXQIO)fFDM$%y# % =Ŗ?"~ųs,"ǽyT$jݾ݋~|VvjFJ6o_+%Y;sfR+E%;\:RKlG1tAOۼ;Wvz8GrjHY{n?_h}o ԰=Tj>T>מhc_&vzK$^׊8dcW$`r%`w#NK %<#Mj̆0Jt>(eQJgY+Q\'ؾ cq1J쏐=+|4cGd﫜VgT3Pss/AnDVh@ÚiUNrMܰ/aAEiҵQKTN`]:` i5 Dr#L5 fˌ*]'!)=G[Dʱ픖_Z) qN 0z$L,Z/UOKZSvkҗQ?JchP@CZi5R}ohEn:ж}OVZ#ĈCS7vVPر:;>|}WBK qԊƭ"z̚=l׍GC0qY7 K J%PqW[-W ^QbθO<;޷3o /xuhAج@^>?<1hC}`@fQoϾÍ|jRJ3+Oqz]pm Cï}|E>i޶8V.*RҷoCsV܎ qUtܩ!@a2h66nd?0^lxlt0[ѠGuƮB.㛄KIsdiG1ȉ5WcHuEE2vtw **8Y'9Bp% g*ײJXׯۼuco[i1 ԄqW `cTb9CEq)҈"bH$^9WfW  T]wUW >%B)$Q -Ym h ɜ0;/NBj*5|1!ZZjV*i%ֺwvtiƭ)MUB83c33׃*2H*{nɾ0;Ϩ;^.$܍_:8&ƛ.Lwy? *$V@XtdlcB8'>'ea!h*q^7Y_GF(({pEE:9qOXiϕMC_)w-^4@(Cq@hj-s8i"geVVt\#iyxg΁PVsQj(&QĝMM- bM%KL TU\zJʵr U+ebL b\$83rI->WSZ |59*+>5֜4HV w!vN4nM9+"\T6O;+E|R-w^=| rTL'ž*8!Lk"'Kq'3x2aMf& a?@'bLƸ=Z-X-o a88:FuFݐ'/*溸kة/?dQ =4IedWz롧ؑ.ULC$hzCgs46ے0+Q.)Ϊ.Wõ갸J|eiX)v#Y UbcѠj"6E` !ss+Wd5ͳ$X,I-J0&EXDlS]-RFD7* 3Z|Cϙ(FrVD"k IB6 kALǷm÷9Ov+/oܶeSki=1pWˇ~!/ާ̧}w޵j.q> N{ƨ ~-T ,{1)h+W`NB=+N"[11(Ȁ'v'^g3!T.7꒣)8Ae\ twJ8DA+QF$%n&#kl<̐nrw7>pσ؞4\-J>7"ƃb=u.l[(.E6,bD9|)ŊLjrpبÕԍ-  yv>uH\b0c֬BK81AR.osIX-E&T1fZUej͘VlhHlbfCXhAMk,+kڦqkj?@m^;O#(-?wUn ڶ_ָFs `XȣOn|';$8iV;6TtWOcfJ^bߣL$h:Jt?p V%ӵw޵sƂUiⳗws=i^rLOM-Y~‘{ͣ=}%-O=ev=rݫ+ l}^_= =߿eDy6qeVƮzA9 S {\⦵{==\1idbGVm_߷-#fԂILfH1jlإX,q9Ɍ˵L#skc{ZPV ]Ȋ0*!dJ-Z"MM-+rdl胋Tl`@l hl 6$d w]# WEP|E`Ce׶};ϺzND-Dc ,KY%(ލؒ'6(*#}8-W{gn/4 RR4*I!ZS!uw<54bãX:4/9G\IZj-Ա}Fk[G!6mZ5ZK\B4*"P* "IiԨx,8T" % r%j!7g| (45ڑk~mMiឍ;O\4o̶ $4[m,DA.ݹgۮ]!["+u%ii7nlh ΁Z5:/l'_{z͠- T+kwl]2MG-['7"`^$kc౮R$8{!U u?U_qkV쳴ẊN+)6vN';rč%_bc/ñDyoGv~Ge4Y+ZcDw^%(`P"]ܦe %o\_%XўJܐlߴ1Wh;ϞZ:vS.1!_B='50'q+!\d#P6KV. ;v$.B4Z R 3c48kA׼"'U;LnܰC49/ʶX2snm56D4o LDCkꇴf6!+6>mӸ5^X >N1l/ue{B/DtoْrMyW-8=Qs FtFF(H]HEF ׆VJ,Ӥ[ձ04t}jɋZ7*Υ ?{IsX(=-}'%@ǟY=zv68X7H%2KdC[GxhcڹV|+c7ܺ;9%\A,N22,YvXVf,Wыx`ݵkmMb1@DTiƭ)CVFA@yGx!,+Q-}uO~ ܢfb_{ֶ̘s04jɽ; U|Bw~i|>s]4j.<緿ݥ7&i9gWCڛ>u;d^f5m~y/7}sg ~=͑#Ⱦe7YgRL,78\/q'`!䂄2Jb"׵eŋ~zƥGGpƎ݋>D(0MbȐUfNS@!c,!bdP-*h1^1h0Nl"o-c)dmmz j8YΛ1 Η0* ̇lx64T#JHUHl& C45 Ց`)JY4O .hȃ)5`DʐԴ Y ||Vp$Zmզo}~u:PqV X؜W`wqFÄ?? ߃p.2`c&ާPwu{FxZ؛Mj l!\ XT`?LJ3րovQs.(BN A%Ɛ~`l@m1bmd r`\D 6A" 9V%,U QO"PbբI(֠1`6QFJ!lY`0lČ!pFQ$9Ml͚5`ȃ dz 8m~©*"EjV"S8 8/|}lYb%| 4@[9ob$ aV\.&B ,@l Y ! JHh1`qư(j< (2!1P`!JTZba!F1d:Fj\BOuAluNeL4nM9t&2 {Y"x8qmz*!R n?t_rkHFQI@8 QF (E9GD 0 GU$c4XT2h T!Ea`ՐM(2^ $2Q4*\EF AV !b`6Xl"ޥdJ9ݾ{' Piƭ};T}س6ҴŚgs4B1鷾qȡKfd!@JD""UP%"R5 IU]H 1F, "!p AcPPTB9(&"BP`cIE!f%`_wZ,Uų WV]8Pbi9&)66%v+Un6PPqzQcEVfhd?Yee6D$c2h € AEA$f V-"  !"\s"!B@UME&2zb`AɊML#@E=j*ĞO:zϫ7d ({0Q2h zɇ9Hj\hQEC25hRl#VRz]UeETT@ LPYZ 漊CYrU#BR ]8iƭ}H6W[DLqN*w;guzw('Q>5MɷǷdEQI8c/}gV5 ų1 " IUpL8({,F)u!!Dh#jeR%Vuii@9ũy4ct晊"if>8\*55+z 'hՉLSiƭӻ΀!‹(g9}/)0%Ix?|Crq6b @DPA%T `m_~od(3)aEdeX+,cK!dbb >uDW Bq(EFSJj.FۨPlL@j*Qbz>1 (u߲skր^i_6g[&2׃May|h0]9Ku4YsѢh].ZD'¼sJ-SN#K>0t?D:g*zi?|+5R~rW,p`g2_/yG|gd__[yyő}v[Е\5{ddoy/Ѽ[2Q|2XYa @kSK]wQTMe2ڐJ|^J9up;o}/?߾rZ>F# NE UU@$Db'?~1jTU B@PAL8<墜JICC3MLnLP(QbmR!e\d$ilhhIJFRg/"PalDz^t%]Gy𮡁=:K<xСgU<@k=7;~uo;dklhť;]Fj>kf~zݦuWҁw 8*6j }[w|vƬxx}^|4'BU2*W907gN{twWp67{v{Ho`+h7q~ֈӍ;ysjn/As֌ n9+mYf57uEAT)R N$,R,jŋoض9 &6ٱsjŽU!qOjshl{?\'%;w9[h;ɠ i-c!{DEk:J(@j@|^7(*(j UE`*qIH*(΍ f0q<" 1$I*F B.AmtRV]@$A0huQETv`Hl\vFdAFUGչ Zn:hAӓҒE׭ݸoO[뗞EwW\sr_Gm탏׼M.<FmڙM659+_87`Ϳexr]sV%Ypg.1ӗe^ nO.x.OŶ{{?۱j6|0ًf ܶ;[ηm>`߽E o>\eYb*# C߻O>em?(.77 t>ꉶSCkL{/8snwdmyd!y't=?.y^]Ym7[?{w1?o3tŧf=Y}6Կn~?pTIP΀rI7ܸoEVP!2ϗm-`-85Y͍![YU+n8uo{amߒxt冃^7U X@[`[hfrAG3TAu2(>od_}TjT0RPRp<%DDC(k|K#*SP,@;k,ѪD`ZJ!Xc@TI ID*%KlTRfPeQ`A *88 ^|&>l >@0c?78U akvQ u]KZ_nv}ȏ|[Ճ\߸:}cg&<3:iggx$(2ꄟ߻)IP8ֳIUTD؇j-e|R.77Hd ?HPvA\ΐfsy/N~'kK[?GvY/g/[Puʠ+E%9ok6J(FUpDIAM?;\ɻ*s@5C ` (lmلD Ldl$DH z'`ec*^8vmZcppp+ݹmL@ԁ8Qq*4Ng*XWn]SZrBV/ Ko{آѐ U3}n!94 !2N9 {N4,@gl ~~➦6O~np+شY osŒh~}?N;m$FQR19gI+Mn6wKUrLU+[߿TnFgk^yߝ}agc),9Vn[D|ֳg)\aV@D3:Ugv~FP R߉"#ڼ5i[umMaOq"Jq2'W]#;t&w֎|JhiIs"D *8q=6Y΍Y~fQUU0"Ƈ6Zʵ]OWtSwڤR" Pټ,<]<՟*;<_;˦C3r' FDc Ro]AEyUXj@4zmRq`Y[a2Rlj@ҵ2jTc !2Z M鿻䇫$.wǁE)V m~РZѽLmB;ZQ)A蘈јA4G|}[oyŅbdA@3BaDeV6VD|UPlB+"*3DQ%WTRѢ%82 1 Rő$I$"e@ĚP??XF)"(h >kOXx{o:}adb3Ym2jD*ĵ3_~zYsD &(t)hM?^}'Nnu7a̻ܿ]+vZi$#+7]K̟}o\]p, (aq_ؐ@Z +?V#kIPK=<+=_{!?MFmE?CE\-9qG d]W3cWs?Ԏ[j9$LqG?;6nM- !M3p'd[{XhwymW|g-8ĘCҸGԥ?kr~r".M3@BZ˄yo)X3/>ٿy3_uwȱpHc.?k?Պg ^tYG~槳mЫ|eY9Iifʵ/8XW2@y[*0 ̝;[+qH ,Ps*aoV˄.\_pŷ>zÌcp_o8iV B {cL@AD'M U7\qw J-C19! RWc*e+qP Fj\dQ,U 39bq΄ Db ʼ !KYFe@DDd:T{D" jP$S0U@ڰ+^?xF5(f4n@M=?vSn~[ηX|g|x_t==?Cy~o/%g.}Kg_۲Vq{E9!" gn[qC G1q~"Z+>sggyo^Ϗ23uykNOaǝ|&hFK~::eoo\q%={O»yWO_eX=c2h[O ¢H #36hPNT_̞pBŶ7t4N FPdPGz B\7-II/\KkD'yQEPqSE9)ˆTաl,ڈK٨AIxa3pb Y1& ^3p-E /a\p#C7:RL=3湴(j#tn٦'^G>G~껟=uw~xn:㌛/[6x#?(7t?zwi^S5g_9yÏ/l*eCӵ(s{x ^ux^a? _}1`Y[*= TЍRMznBn}[u_sItИ۔ W~ŗ^y(7w=|]‚! >9q6oF߬ʲ^s y4L-AHAų I,"ZFuIt;uvvՋP$IHQ |BV.|j~`I;t<">)=&0Aǵ]KG,]BjZ4"18aMYlU$K$R)>4gțDc#uX!S&3WZc{&VYɐ'cd<1 /Xp팾Ŋo|Rȑ1Ȉkq"1`C "D3>?uDd%5x]Ummu}usb!Ip#!xDqLK˲$s|Gc>15ֹq|´HVVѬ8ǐd(7& ruDI$&c\r?=I)GEUݢjW۳g0W_&W<ÍYm%dUCY~)/M! g+Q)(E ҍK=9Uko̼wU^/s{`+]:Dޭ]9r%E 0hSY03k퇲{ D 3H A"|?]:ܜ\4"YVWW`l-fF߰G@z(m6Ӿk&j0~0BƩ Q߸lG6drZ` !TC2, fZTmY, >MA@B.qDʵ4 2p( HztD:a[D(Mom:IKɌs"@ q:$jH@ 7]'.~!o3AR\'ᴛFx>]fz9ᄿg&l+$ F|Xϓzw{攟yq?>ҫEǼJ[!GOH~cN+<틅h|k\y̧z֯Rxt7\׷:'ꁗr @2A s}A$e TsC#DNv'T3Nc95>yՆ_F,sCiϊXPK(Dnت!Y@]K@ge6EDdt=u'bTCf[: 'L-R"t\W궮1Yb8Y@FHgȀ|'MV(5ABn٘Nˣ=A`:G$yri,pś22IhaX݀Ͱ%N@h[Z 01覅%&K} 'D݃Xv_x ]ADRzޒ@ mm6v:O~δiL̐V]_W &do};L-aZI]G| $> CZ.2 /YtƟoFBxd<T:\ɳF=0픁K{DH#twxemUrmI\B,I`ЅfH# |p2-h (1"d2AAJIuq"y } QQ5I08~c2fһ:>NR "2OM> JyA~:LK9jv2nsJ[݃`{4*2]$ ˉŖ1ι]ߝ:%%u:!e%ἰ^QNl5#h+AP BfrtҶmM,DȾ3>%@jU_[4|C ka2G K:z5PôxNvQ;lu;k~"!8ٚSL׸ISa,$t]tig($iG6|5M^HS]]70l4.o,D"2MO]&$liKB[IDATECuy~<+ե5k4 4 @BSIuy4TP!ayI)M`7it1lBh3k0$$(5kl~z eЅvĔ7H4\J8b 70ȕt`iYz8iYaHΥ^/pՊ\L4<F;f:LJI.J&Hf&$_dLJd Rft7k aLwM{YdqȘ[#pju=h6*~2f4lݳ:t#t$0dX`d d WHHf4I XMe^pn8bpXg^Xgn]@aD&@B|6#V|"vLJH;/-1st+dj djE˾\-$$"d $C$~\2`HH2$ADFCQQ[T|D+Wn?u`P^lG ǜijٜUOhC`F|9lndIF$l ~tfIIt =S,[N23iXHq5>;nXt)"uNeC^nN87 4` M#(pӎ`L L$ch5/&ػ$=D2a3>n /t|[nͿ59 Kh߱"~٧L)ȑARAà 24%fH17%$Y"`[ [C$d,-]3v}ѽK B7tD,<'-e.Ք{k! t al CL8: @4݅[b=G 8cMcD$d PkBBIlvU{iad%Z;r8V8R~`۴[-o(~Pp'@@YGD!'-ѲyN?<#|}+UO3_K|Z![dچ0!4H< O]Mf &{Ro'0`@vVEuQ3+/qe1Ap1 $'2!yK.^jLAͪJ2 Pظ @fѭ[00QY.+[IE瞜\C"QgWTά?LaVqˋS=;h~&qwL3L˲L@@35 ݲ,SP:z3p\A gw-2 ݎD!*&@fO ?!=dX7IߕB!p@P)n篼4bpmvvYv9V~*EPN(pȰÖ2t4m V>`$"⚊D2$5sA@h%v HzA w1)Kg.1䰻\oNvÅ|uO;gK;f H|׭G4%b\2(XLpdBEΝאtnIP0F$GD !JLKW@Fʼnfk+߯B֞=<94wU;^u92" r\Ȓܗɱyտ#|ųx/ =z_&V\(6T+]ߏnR_0@nFDdG:'P( [#YJ;}44 ֗-sT(msquUܳC""k]Cۗ0Y/ٖ+'2>^Vа~שxQG/==g{脡!/^]nɆbя&]tc}>eX/(1!I/N: 5^yUB?Olgº5sFNIƊUc=sW9\qy[U+<9_[_y8tEMz!vuڶCi356 >䙫C݆^utg\kκ7_Oym V/hoz} X5T=0vĎ|?PVS~DP(,5?YyogɯtɕcKJ_s翻%_>y쏮.n`ӗ/m~n OY~~ww݀? _ucˊr?gu 7"vԪ?8rMYoqū^@~`zs_K'^_ܩ|5g-W/St՟.rSׇO\Ԇ'7\;eO+7,ZzWTƲ-}ɓ{+6nېtR(-EDѽf7mCt澷}#8w6N^ʭY6룯pݳ7[:$0n>{utFMLW5K; KM=Gw+͜GSs~Wӯ1Q_9hьwVQJo–l[u9|Ψ(@GњYgoh-kݦ }kǪ9³վ+}:7=~>‡ԥ?`+Bs'ާ *\4USl*q1Yaʹg|}qQ8ݹ]n'\>1Ï;]Nz7 š=Nb` e7Ȗy%_ty `…۴bdD:d"D;WOyW\zY?kpaMp 裸z^",#q_.-jPaf+ʮ#%ĩWW}_6v-&^L{㉛o8wo{veuc6h~nKFA ӱl"28fD5)}܄G*}k];{el6:de7=ϗsdAAvEg3W !z;}t*ISԭիĶ-vI  šoo뺮SU۠G6V7{BG=3-(̝bSk B]GW^PԆ/&-Uc%9UibVǡyf,s`pWv0uk{GwmmU[6 *|鲦r;+꺟|/>{s,ߙ}Er޲Y+krk>6d2n'̛5!ĚݫtW[歫Y1Įk xP]SK=o3>oڽ}K[҃T#vBjVGy'A{-Ɯ+d:K&"|slgig^8ug'OdSƝ uDc zoN9ǿLśg8x㟞BTR?>|ȉY<:C~y̝#GyѯJ@ Di,L8'3`-4ˋ>l7+OԛN9~qqs{EgYϞ>rȨ3.{ u ]P)_ S??NKn; w:xw-*ӭh(jo}]֍%T2ZPx%v(TS]WW_-atAvų>9fg}/))巵eRmFAi eBHxIIg{\-/5j]/J%.ZPQөolkcBP0 ^kc:vrD~ATW_s]}4M!BQdTB!B!$` !BHB!B!$` !BHB!B!$` !BHB!B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!$` !BHB!%B!K!BHB!%B!K!BHB!%B!K!BHB!%B!K!BHB!%B!K!BB!%B!K!BB!%B!K!BB!%B!K!BB!%BQ, D^i29d#@jB!$` !BHB!%B!trJM>)B!.trą&eC*(l !B(*Ȱ Bq Kd>XB!B!$` !BP74. ݅aȯC(.KHvfp8];t4dQ )AHW.' l}իWtrB XB˦.پZ5É$D!9:g&I'%C!$` !,d4 !,!B XBc,ܕ㾒\7#G]ouBHGX0+O,H=*qCVkcΣ;pԻyAnor6'_Iz|$}9g{بؠ6+9n/q9WB˩?vI-3Y\K}y>n H;ߎYÇzK 6pjU` 1!,!nLg4z0q:|[G,n9)<9Gf<̙̥p4|#} +Z^wVPni ߼7YHBH& yYtl$T/"rh;Loi9 sڲwx)><ΪY `^2/ Q]y'[Np+V^lW8k~?6lG]Sl* =CZb~Y/o|1;ײdʜ,ri$?g `Feq$ކlM^|%čʇ/cCL'ҏ^l4dИǹ}+o8ڔ5HX0~'QJ*(cMg9U3@ʕV9=q$yy MiG哿ꤝD yWB;}2*~!eqխLֺ&7m4^SguN7{/fȹޏ;t-BW`ɫ~G''&m3.<}PAqT'n6$Kns|{m6aC,Ty/BXZ= [&wiJmϚ+f A?/&,2wH%uBFF>Ni[+Q41Ad) Tezڕ-K 4*}9ؖc;|FgЩV9ªԤ'׉~$ZińV֟(ZNzo>ZcsV9BBBi֫+}gd+.zEޢ)ì;*gjroABx -'iv Ħv12ʐw^uj.E-MdlN\|&,!sag;u 9Niʶx -biV":O~67yQNvCLP:u\io<&Lhע aޅk<'Q||sF4JWÖ $d?w}zR'qyXTOh9{p$aeg.k]/ozm dON\Kߕ;1]qO5"m}GI+!$` qӼ .ON$s Q4ok~:ڽfMe`pwyzg9FfF?G9_nP&=0aw$w3^FlX#Ug-S'9~8;~~?Q5Q859Y֢[pI™߲ u/˜LXCǎoWNwU2w`)BȚɸ1I?>M` MB\1_ة ~ Pר~ ~Q2;JS%-D,;q_Y 4G31{0!wƢB{xR(_ =;7i8~#>J `-]ҳШ`,_94_Wo>9K-IS `ZL eMQ ];SR1<VKߕfĐi7сpOPkb|ETRO%s_\i=n! T?u.dīl[s%@!BB!D&}?k9hʖB\%R% @QT C~/ETReʑ|*]6BHBMQ#>;>đ#&K!ɭ a?ibǎd $L4%d Q)A],JQP4(*T .W4Mܙ ÐJkqL fb{\Ez mMPQ!o%č@oWG1t(:P5P\M۠WFxonS%lĝ~y\Qx[`M!K.\cq\ѡzdhݍ?w 1"RrQWBQUEɩn07Q<f{mv̌3[IjE @U3%$` qcwt>kbtQ)Tڹ3vzhe#+Z3'Z)Y<{41s"Vv(R[&S!Ki*HV- `&heanOʩRSZUn?F+t'~ިb䙦 0``(gE$<ϐ;8ifkb`z:+Bf'x}Y2r-245Mϔ<ɛ ]-h^J؋*_Yȏ? kTeĶ<ԁI?ciwp;Lx#JUgŮ;iJ`+xDNq{mޚҌN /!gH³},#Ns=XjeTTkLfMryr@7Q>)_њ+; a&(jV@RINs1n?ŋW' *hf_ !K\3|^[tz o29ewI>OgOxlk&.lMP7~YB]!_gi9FMhݴ~üd6<Ć!Xͳ2dm#ks2cܗ:]|V,Em4aFEyҼ4KoȔ Z2}t=A4l5uBtt4˖H۶mm<|}$d"Xyj<=h;Ee)}U )AF?{BHפC PK3F|nBK!n^Y2Sll07zeT\qkf@Gj3%T-[!4.ݙ-ÕJ{bx`v4 J 86z,Q4<5XY)++`١NogR6‰d7uc3$5{8B%PUlyA VЬJnX2ML5Os Ϭo_Il(@AS w=<$wOJcjdVQv焬lmڴaQ gʁKQ2 0sn>^P]ԩ⅏9ϙx `3O!$`k%*CZ1mL31ӓ^<*muHf}LBϛ%)?؉rNPY&Ͻ-;nf`k1yӲ:6_燐nnwաCOpJWDɺix.ln`f5h^ӻ Ⱥ̎{3Mm&%K2^ڟգ͗p*EU缛 xPM-^*{=z3Fg-<[;jL+iTuy0 μcC6HBb|q#gqIf|"7ꤸ퀕f~ w}ddd0'>YZnkLczw_dٳ4dQ^b)##-SF{zG~%k~kQl8s&8pݞslDGGJ1ԫ{3&o(\W1cp:gv-AҞ_C;xj@x~7AxEB\ϝ;v-ciz,3*!!uҿ>2uUUۭV\.*uv}kPɭ2Yù{jdaUpZF׋/`YvHn]INNs<裌3UUiV(բjK3@/]AKa7deHB7TkW7z]5`eՓ[jTI?&MK/ēOi۪^&%h]efu2'}$a XBA+0 tEEw:qMX]|ݣ{wՉwСC:t(&mymv>)UUQܰk/XnLUI&*YUC a8C1]F4y.jřMIjO+U fP&Ő/Htt47UQU,*+ d`f}-K5Y%=l)cv# ?yp6ax$$` q]*ϕJZ}ٺ5zX YWidGecB\qGxbH֬Y͎;h 5]7CVv2UQP4 EUAjVRj)%7`nQ[~Þl( !5:L|*V(򛦉P8AdR% 0 "/WyUbwa]q4Swcdu~4s׀nXTf2:YwUKS/ ~>4(/}5+\id*S(ͅalڭznUr{ҒǷ*F']5 $` XY篎B7 Lĝ<#Q_Lr&Wu E1|gD*ik\bɾfbe5 7)sQύ,M΂V+ V ];X-X-V, bi*h5tݍP䵣fXBbvX,@҉cd8Oc-nĩ<̕ xlY W<,8O#W%dL(Dae+.^Uղ.z/:Wry_oۜ;]&CCm!KqCa掇%ĥp;PՀ)o3_+`AKQr>tN=bʯ,!/m+cx3[Jfc,~ ̔{CٵX%k?3 G(|J4Ù{TvgQ) 8'c.Cwe]&Д!K":GX0d ڼ{Bdǽ`_@2.Wi!x ~q=<>vyE@`>nюcvKݗLӝ۠g*BR;)9-|,;b.L|5XBH$rz}^FhWpr`G:y//aZ`ԫ]-=ãɮKUlY@]`zjLZմѦU$iL:AeQ$|||3BQT|vk0.O_xTkd!K|5~"s?˯ w<' p=&.GAJ^<5Nxiat폟/s0s߈&t8=ػŠ'Y!v'd>faCaye^h=03[>w?ݧ P} o؋0]2hDFET422mnRڠ;^D[`Tv -k k 'cMg_Tk~E @jb\z) #3{:pׁKx9YUOGb ~?@)w>KK5ffi{!Chs6w5:xwTiXNYe:|1(0+ IDAT-DfsQA:0MDZGQL gT֎(x!"ԎpH#_iJ,QTUJW'Yh>>S1k\gf9=Om1c Q;ʬC?H E,ijR2o@13{D5Ifoj6t~ kx;ǥø2/]ۓ7̜3@TlܼͫP^r̭V`MLᩉ9k(Tc?_ܸq:a3Uʯʯ¢qD@ԋ-ׄ\8vCtn\!!TۅgmB/?/BT)ESM/ZZr$aN(Yvdur7\yn:`ٱX,dnOx sJ̌T}Ym돦px( 7O4PhD(A9q7pGx<KEڜSۿaoٰ?nl8:3YυIEtgAUz^@R.t giԫ߈V-jj}ʶx -㳲:#\4Y:UZ77_Iߪ>9=KP4KTs heuK v g3eŧF4kTy X9j{xfW X08i6˚rO{KN_2[(S,9ݹTJٝNϭ1lgw|qDk/Ȫ:qۅEP8]~(әE0 22QϻuLS% VÚ Q`:6/HI矟i`*U6mڔ.|e~쒖iZpT;xF6l̆O^ӹ7y 5.s}6/*Uo‡V:uyw;GdJMF0fQ K/GA}4zWԣb-#면 Sf 3h&qVgfƋ=# /',00MBggDIͱS94 ~Q2;JS%-Ay\j jpr2(ilu>]íX/R׉Xv"bتK08C}c,u%+!R<_9Q`,=yg,ww$#=∨X UUI8OAɤjjUcZi &UxORkX-~C BQg튒یGni™3g8}:]9u* EQq:$BQ22I<(xf] !MBBFv'w3:珿?)T GS5|pr4ahƉ'g'm !K!č!o'X%Bzz{Ăj\.'[nCQΜ9MX230sBR%$` !^yFj(DnYoe䭯ej8XBBUÔ*S.ktut[2MUj,!9rx?|Of<+ j'28*2%[36+v Պfj`hh=EQ)U: o\HE (B3Xբi әD\*GHBQ⵺㾬qqqL4•gݛ|ϩ`QMNx){aBB]FWT@* VC)Z&ShB\;N;D;vʚU{u܅GTz,!B*tٗB'?B76Lܜvy |ճf.Iuv.vZ]O!I pd?r;#x`>nю\ŋwѲoџ)^js=%=ã>Jaڥ0p4 uU/ܗz!DM Z@ժWzx@AMc}[ɾC:(&V}!yg|]yOCo&1z>;,>V_ǽ*5G2\pz7\+3~(zT;ghKѿ'U}rEST OŨU`u嬫*.S,9ݻTJٝN5%!dKXSfswsJ@հM>K}(h Wʺ'\^[Dɨl!ĕ֡"{X%޶5)o#ڝG,+Cm•XVzN,P1܉ٖOHK", !$` !ſHZ3/Gg?K'nZMzch[ѧ^_OZ̾ 33'BN_2cwX9yfɡ#صa ߬;ܒn̛,ݫp썸սt]PQ$ ˟f|7y﵇4`ƝுZ;M$~h50]'Pe37#zej(j~Hcد+X ߝw^j"^W!S.K.HK T"AQ9Ee&vq\8.223HO'--`BdCJ%(4+Vȵ(NB!B!$` !BHB!B!$` !BHB!B!$` !BHB!B!$` !ĥg#͎]‹jl'~L 1a'}cOuB"@Q(f2z< }7hG *EdhYŷH"ͤE8+ n/l$h &,ȭ| "ɹxG(^+3g7t}K_ XBHxeg[i"lg?$Wi0%uno蹟r`Ǥ~ۆ2*)im&i &DW0DӘJ'ĵ])@q)UN͕3;3'Mbs]b$ulNLL 1{J3=3rydnkCxøefd\ְ]_ƭ8;!&1Ld)n"4ٻp4}~K 8gւ2|tlYN&c~TՈ wVN*^ѓS3m#_s:wixyk^z~Fϲ1]~cbb2+ {/3-cq{]NLØbZtُ%Flfߘ'S=/Lc%̛6jS -4/LVn\gX8V0Hf:K2gtwn61D~8lboX8uڥ}ɰٕynoZdXxU6(lXh.['1|Ըm7Jm?Zn֟͛ټy35'y8B}x>>4aD=-7of1U&MBKy0-brW|b_=x筇h`l޾|Cܜ.<V}:+~(}c`*E#ymݫI oп? @H8=+欳pӵ/ ;_|<˷RѰq-˩P.ʞcNOѱI.H-WNpWK:Ϳ ,r_jێK !KqKū\,5*۽ټ>~.kMB~ޒ:YT)KNO#ՇRv'M2qЌGusB_Iߪ>9Y]*bl:ҭ׌ nkNf]½2㷰?ߘg-',>7A'۠(f; !$` !i}|>P,jN1~╧dl]OaTQΎO&fW% 9nY?yzLԩ]x6 L&e4(pꙘfxXBɨl!DqpnWujJd)RMoMRω[~~ݓ7Wv%S5ү/6͟ʍ;7׈I;3֡"{X% 7E%[OHQtPPU4dBZg)snL-iI r\CGkYw3%4uݘ7Y=ûWq/=ۃ;s\76'1 XwUZQZ3/Gg?K'nZMzcWl!4߯gOfj./ ͗r 7IN),!5R=/؜h~[yWjkVřbtЙ/}ɮ E yLוN=G~yQJ[I}#wхǧWӷT??=XCt#]˂8_B ]qv̥oƴ"o}^JnNj-Ïzξ7ug[Zwz?d!bR/rez*Ш2M]qݸ\.NdNdx0AQJ&YTF7%Ŋ+ooop8l6V+M 7'8H6D3t Ňț 6;SB⿢(uQhC&͚be$4qJ2 4MCUs.!.`-m+c[^^1c=ײqEAKAU#..ဠ97 VBHR-iU~Ă0u(p*J! +{AAKq'BRj_2U+M,i"B^M?W;hpd 3_La?8 M>p]k-ʻS7KMx~!F|:h0=ɪQg׷8A_CL7_ zUr%$*ij<ܗm0m3 oԛ}=y,>s#u:S}hde w8F0e v> WaxVb|\⢲b((dτ5N|߂g'KMuޝt+TLy{2hZžy +x{fs1Ro'>9^}Kޤn3w:]EWb^RiG spj6/n) |c;k~~gS57S&ë#Lԇʪgoh/x&Mv+V aP}ŒhVZy:ս2*)MpdVI\g]4i8=y."R.*Cx7{ub^/fh 5Cߪ>PI' a}=5gn.E?дuņVʎL7W[+8\Ie,9?^;wi$|(C皳!ŖY}`پ=37dQ>~4_ҮƑ#^KYДo?P{ؑɥZώdh5iml̔1K +{skownߵ/fYx?ؑo˺-sgWB!Ruɧvϕ+u>GVmp= X\-xygOytݷ"s3z[-[@_oN؜YgJ1tp_ș$9Yhd 4.T*ظY+.YZVJ_ϜN>u2׷wTOxgTOv.oX|s켽Vc p t\:3X?T*)\6,D{RTh4].zQ%&fXՋ +Ho z{h'a Xo_ *, X @` @`, X @` @`,X @` @`,X  @`,X   e]IENDB`sqlkit-0.9.5/doc/html/_static/images/join.png0000644000175000017500000007347111533717256020471 0ustar sandrosandroPNG  IHDR!4ֆsRGBbKGD pHYs  tIME :VQd IDATxw|l~;n[%wl+6.M1C(  5$@@`zi)W{mY;]-3?wɒ18DZ̼f޼7r P( PP(  BPCP( 1 B>BP(P( B} BPNt|]N;!1VW3\{cvߟ6O{R lݩ-"   l?_(1vCS3 -'RDւ:ˡO_C鿿~|xM qK|hrp1E9y7 k]^|xG\ 何+cR~ѰOdpK&vT\1K9z&u΍M{/r)ǨMY(G}Θ!us|| M4Nܶcƌ;k|LZAaUǷ#k[v{H ƕ[3epG^gv:P[=nӐ-Ig[϶U,Ku'M(ǰܼLcLoFLGbϾ`mU̒s/37!2,ܽs9jmㆠue>tKї]1y^hg{;$cƔFH:7>|a[8[7/29"o^咵M!`^x(CPyȐ?fuףko)3[.II_ٺ͟q(sO[z]?S٢iE@fzPzw1, KӆTl^UNqM_6_vǽug׿!c飯l{՟va{7Λ]M`-7MlfQj]+?5vN.}\1F<pD{/1{\4l}jEݴwꊮk`]w?}_Cy7.zIp@חqbaݿK~ߏoصPk7^؝kꂍG!e&uco0/?~igiʝ>\MUCfw $6X3 |e4zpݧr %f{oSB%~.g^>ܙl)_^'.%ңpzճ ?|۟.d\\e>ܸq{K΄1rV-,gՅ]^Hک)n[o}[yhrx+? SRRG>\{wL \Dvl]2G&wF=̄+gO-LI-^p͔m+%Y>nȷaI@z]>,fGӆX9>ipf#)pĥ{ՋG 'Xvll{+=@M+Lbne.ba[ZڲeN9Q #lF'e4 y~yYy9-h={CΛ-v ՠm9E& a7Uye_љEvVW4ٻ堏۝ hcfRkM,4Z^KsF[y13$3Q&;[QlAu;\RN%X A}27SvSl^DRg%°l7 (~`xpCo߱jc)=[0n+60և&>s7u`ա#57~/+gw%GSv&]kYK4!MBIGeҙyU?B57][bdty#,0|V WS('ZBP ]=1Ic.^q}`ۋ:8rQ/܏[ƞ3GnM7 OzAmVv؊յ4!);ƻŹpp9sJ?|ϗWUr71Ebբ(o hIzң9Mso@?BëtO4wrK+]y3_YxkYٳDQS']+QG=feݍOEsb}tn#P('~kBP('NL BPCP(c( B>BP(P(  BPP( 1 B9>7BPhCP(c( B>BP(P(  BPP( 1 B>BP(c( B} BP~p9G RSR&O:bB{4B sZP(^læ-&nc1>o.{g,1 .sysT:쨧ڵňԔ" q7ZTKN SOĘ\( 81$|Qw9`$x?ŝyNj eYNOO̬ B:Rܛ|>!CXmljx|)1| JM%cX[o~X/̶ $莗:B*=;Z_*`㣧|>o,NAt$E7"Crj`@r%;01Hn[tj7䊻n/|ubcwEK/O/N#>IiiO@jrRY37j=sHI`R(SeAAE"JbgN'Ix<>U.Y#7ok[>Z23`.׷vl]gSmz^ޖO?MYp~_姾]`t9z:gDp@RڶȟQL͔B> zaDI!1 (|vf-At:۷8w> v~ Pk9cG=>˛xP(=Lg@6d嗚;BN$)آ稙R()Gs'Iҁ.sO9w!)运Zpq%y67}ΕcB}1m߯lݞinh <(/~1撒~ n իVWZJ2ԵD2xIkZ<$VȞ,V,cHdhCPN8&rwlc\‰1i8FY_4"Lf6}>_Ñެ8/&"ƭjדږT2aS\0z~꬙}>Ug4~"u֬Cn]~9 *`5pti9y='> ˡRMPTR(S3Aʤ$sKs-^AͲCb0$J#˖k7<{tل0(}\ٱ1}1eN/ҫͫv^}/(wxxupp p.$XV2Gwʏ:}uާWT͵"S$c()B?B&]<69j$-7(JۇӍ0aRCZڈ|m,i6a7p}yK}vdP kSqm>)G|!Y -PLcǖUj c%ovl}C_2{-w! @ff&ByK2}Z[~녾1 $:ʫdzfLuWyޘYtԷ_ z  a9kRq(c!DTLh 0'BsNj[,AV^+7.'#6o؝50v]9B˲nwz HrŽJ\b@jYkϨPM gݝaYY 8 Ɋ2QT;ĮgֶBN$)tGB9ϧ1cQՕ$QeYeBaF}͌(NS3 p£v> /zq7)(wO>gQ T~GTWg5d[s D! .sgIУy,!$ G~Nd}9PGEsppkb-z΢gzVg~(ʩc^l޷o_QQ˲>Fd69cF kdYmmmGMvɲp8Z[Zczs9?D?著/eA9'm|¤<^ʐp|^~sBtͶHC!cEBJΙ$ Zy'igY~-&yM6q=T r*בfԃG}C 0,˪eYV}CCcY }޽_8Cz_o4 uZݩ+n{% 1 :ed":0XhȈ;n }+<˪ dV@z Ip)2+6k7<2LP(S|~Nt&W7+<0zOty{[VYߟ.҆g^˝6?`>Rk8! ,k4 (h1IY^R.cF>6cXuzPRHS81<)B95}̻?Z*===55pl6l4z tHl_b(~ګ}sTxV9`H`_l-;+O{ᡏ/M/9#bP9e<0 ~%`<+<۷5q&X&r1bcޯ,a \6.n]P(q8lneՇ]p,kȎP($IՑчqhwR_xȥ7^{}/K!`0$r=-.y d@BXlw"Ǹ^g4QEbPX ft. eEY8>A$Xz rԴ&뮳Z$|>AI~5pQ(уgafq!3#~}r҄g~_WKAA `EAHD3u1HYB!44K4G"a( VεsNͶRKP(5sǶUUy3gvoΔ$I 0Ɵ.W`[E0b($IRנ{M& B :0榅ΪYMΪPtIfK~1dOA't:A<{!{eYf99b%Y}49d2LcRא{ԍsx%gHE:uR(SP( rl) B} BPP(  BPCP(c( B>BP(P(  BPP( 1 B>BP(c( B} BPNUs]eK{.!BB  @Dy !z؛?tV a%j3#Đ2; ؅.7 z0\ο$zqXD#lDBK'P䏱’~HEUie$jf3vx3(ȠnI;!#ڞqJ_DM R²F]Gs"1#f ƗicmP#su_$Bi_C"S΁ vxX5bwAbP$F1fq"a%͐|ig( MV#> v'1XR0aF6Y:AE LPTO&gSuC9μa&6F, ˝sM4͚"B4=X@]C| #s. Ć=H눴F4y]4NzQ<BED&M9 IDAT⋌sVQd@%<7.FAyZ4lԛtf QaUy  xJ!U$Ѽ^4$s5Fܩ)5LS$D:E0O W1Q H۳5d;EG@#PT&JD=d[ 1yP{k)H1QX"M:1=zܨWV;UNK!Q>:{XI<A 8XNkܧHi"kus㫤Mc&]cmkR?&8wWHEgmsvlb, !"R4#Cxl' ҝ1lt#)tk^%#Ѩ,VY$^;4A7H mWD;I1F*9ҭAHff"-Iu'/G:,Ѯj>ț(IBa'̭꺞s]*X>ve]QUO9zzJ5F $$vYk-vm/&܉!ЍOAMPbf)>ڕ$xԔ~g #D}(ިl5 bq[\\W%>it!'!d s*3l$b}xC$^$?Q#SM` X߷V 6E@ly.B|ZI|ĉf5PhE& եFf3?G)ԣ8v5.}"}(Q@'Me܍4 BG7xcŝIAl2,,8E@zEm]fuɿf!1ĜCpozՖШ!"/<$&P7f cb']TбXBUf5hzkה(uHʘV5sY'[kmj uˋ^^ \k'P+{uqwBE̤Hޖ-26ƈYa=hcOHŘ \?@›=eQdE{ږeAY̫1{WC-@l; ObdCpϢizBy"]^4~Vncڝ+13!ҝ>,:!v';6‰uΊQlȲ:ڣfSL1$ӤGyX$VJ$WbPb6\!773D3Fb}%]$x|iCALH]ь&^N基r˚k:Eki;@j^++8&_b@ @`kOR!GnfahO1 ?@ Ҥ5L=s/ф1nWh4# !iK`[Y!|krˊ=Ò >22Hr " EM4`wk[Gz4t; ?ƀNGI4}*f6&NW\,wK)jFL’]%v& 1l,eE4ڝuU5%kĪO,v,$퐣Vnkn^İ,r܂DXC]3H#*@1 8Fպ"ˈּxuɂ?Id׊7?:hri㶯xKιsŖm9{5畖aۚwTUe@AM/!/X#SNX\ټҫsy:]Ά6f5=iD, 7rg[Rʿq-@N+e!H}wwBu;,Pˡ۴PG7 IOP/ĻVz@wAMbc(5nһOعB(%eYq[xÁ&WdLƬ,0/֬?5d3wa]w_/1#PBG>@+iްuwsxŹw7F 0qά& tdCAʌKl";\`@K7ոe/?=M~<2q3g=T(I܄ B9mO? B޿o+h`珼^Ǵs 7j~35*9g,!bѽk~ZU1uYB|▍v7Y{yϙ+}:,{xс$8q=-x WkT0!5C~q f<{?:~5c X&E`Bl?}o'0_{af._ƺ?OKՁ"IϾԮ'^UI>3M}}ˆCmY_D,b7lλhB3߷%:k!DgmڜOGddYG-P}J%s-AЧ,/ ՏUTCt P}_3h ?rCzuWrLJz$D`~;Ɩ_]JrFn'663o?XG_5!WKMkc]JgQUC۷mJ18~bа;pJF^'ַ6x竌;dZo>Zިlϭ yՆx} 7պ`LGZ+[|$]~vw0sI$/z:+n :<agDWX\+szg띫UԮ hT׬໥#*8يAC+5{b^]q%U7_U u  ?Q1c0XQ]R~O!5rﶽNё2Cgw1;jفU5̞RreSy;Q,$~pJ_`kՔ6e>]ɗMjzV ^?PʗvM|rӓ__م:'2lh[@ڤ7OL/:{~笴dbj~q]-[B2V˟ǻ{شՏ4ش cf5 I N>ei_{W\/%}5Cy9rU]xV  )?~bY'(ŒcY'乌Wlj ]`!ؿfaBun]x9&+$Tǩ-|Xt8P'J _,:u63$1cM Ƒ!BӍ"VNmWIT(#}!E`ihP R̈xN^pT#]NiY%@2D^o*8q>&X!@5ǥ@"$X!8<[ +jg*vzg-& c̜cgCt!kݯ\16]OF2p;D-!hދmKx;>ƪk]s{95iTM-+g:(cFvl05 sWӋ(&N- ˿Gv{-/9F:afӮ0njwDV}($\3m:OW] :q*:XRl<(t{֜C.f_Y,Gv0_{Ɩ@7`긒6p25(ig)&e.='P :ka3&0!'G1<^kr{juD)rUEV+`NO y[eg$%/u4lBVMI u|>;j沂W̒ gn=>_,.?mY689I\ 5?*yJyw`s՞0k8Sḕp$"P~q1?$9n1 O DEV}1,6Q9i!@ݧxk!r9';qGmxdJX| lL68C氛/IpyLbحOɉ6?ɡI`53dX|L9SmA+rI\-;Ol'8CFv?u|c1 ¾~? LmPP󣒧>r|l8BGyBECP(cF˩Ѽ4B͏J?sjjjjkjLO-%nGs C3ַ5lY';$ˠIz( ћ{%)iVA >jGJ `yxR;܍m-MW\s B9V77V6׷T6Շ@18.'BPh= xܾ^ohmiOMMwedUlܴeAg0XL~1  ,BPIH[K#1lebՕ5cF>|r~jey HIKE,#)?+ \:EU?336 U`%xN`GA(8hjjw8~ppIUW]F+ ~1:A疬`,Y%Ϲ_*u\z{EoS ?Lt|}Gh~R#k3.O7MNO.v>jXeY LFrȑw~rb9NpK?[v)a4D91oLֻOK} 6=q/flco}`I_,IL_qI{FG#_"$?O8R/ysΜNG_ cx!`0F`@ 9pjg^N{GW\ !EdQ ')͛WW[f{),8bkV س?]:t[Q O1c^pUo *qSM:j~gNo?m-=z8K~?xsM7~5]/ ~^:fkRise񗔕w5s'qw0lR~0%F,O$ؒKcz(ysV GL7g#;D_.79cKKGӍ?c~cB0ƄJǎ{޼dLDIxh1DV+ϰBt 19=[g-+~ʒOLwrYO{lm_/-S2ydwt֭[hDؽ9W-^;Nwr۽7^C>R5~qGw~ߵ"Wov}rH칬w /~w.m]\K˖-o<ۧws8aܭhKGAegiE1nngse :~Xo\=>|b~ٽӭCR1y)Eb107LIK6rd^C&M 999EM=Ӄn>aƅ7ȋnk Ҹuܜrȯ ?:؜q榥fΘ79.nt?zܑpu;>Цڠ}W&sH7qՕbz56):=^k~ǍwED/1% 9o kVT38>H39j,xB}qw- 4|ׇ717EK~̤yw,]礓?)ދFe8ZTNg0r(=rsc-IBA٠G Q0njify. ,KJF sUCyDǀO,Sg;_~p\S,28WfϽÇ=)N8}̱ƞzVݵp«9nX.0%1&N ql氜p$ (T;k{(M;Jq%}=ΪCp;l״s=1es W[ w:|b-Tz*5y+w?Xu8cS&{LU)C\etDf%,{@f"1,z^E`2ut2~xEQ***$R(Yl6hjSO9_]EW\2O?VLݴr^?Ǜz𫘠!~ܔX*!mIx~*r4#Vb9\%!.Ƌ^+z=Gm[ sPcFv/m?^DU#yKNa@ ,~"gO~}YзoeuCNNF `Y :,ᔔY^%ge671R@g9zhϤ˂3rNkQ? `yXsaH7.kdjQXuo]IŹ^]*ܸ4g3Hq:A #8{uDxLU`hʜu W>ONm>tJ۞ݮw!Bc~ IDATzt+|$Y-Ԍ`@ äz'^t)'Lܴ'mڇ13Uu/d!T3Z^RGXsV۰qkysVI~\yU U{.~ m5_{#_Pbݵ}pQw[e !YIH >*x瓇Oy (&T \e 2yl?lߗiᵿp.n=Bseǎf;tft`yavp:v=333 t:=r1nĘV]4.m߾ 9f>o<}3\n,ց[V>}|ڈy}<7_/Xye-:ᏋJu/.d¼[lbWߐ h%I/xWJLڬ̛9)sEMfy7aUSu߼IݳK*0 /bЛϾ~G_$\u}Xx;Xb/vx8􀯛\o[55Yfذa!B` Ç4I{TǓeEdnE: *_]x%Y,~VЯSSN`#IR0ed21 ò, <:33377eY^qg2cEq\T7 Oܭ}>Ne"!a`&33r1 >qi0d  ,nP($|IJ,˲~h4B~Q~ff,˂ Hc (h4ZʱoʁBPfYV$Ij&9NE B ~Bu:!XQQaX C[[ǀjUE$n( B!uu=?//`0G,P(c ANS-j~*y0bi jXAlw@/475cd րC;z( g͉ݾr$IPe[V12e&j5L&幐$z> BqLOnKZxiutt(bX,bEo2&`)`@/)Q( 1G1 rZZZCCjݲeL<-f^_ǥD} BPst&c#vĈӪKGqy܇9t\!`S! Q$cRSDQP(cz;+۝EQ>l)LyEe{{; 1Âå I BPo0Feٛ[h4JTPPi:{>dQ:gDQ>BPNN2F3wzA !Vf!It`$YgaBuCP(.^xvtbGGG(EQtb²hRR S;xxI ͉Ei9׽W#SQP(L}(999K.뮻^{M6ڵ뛯VܱX"x`ߞÕGT(/?!"U10+/͏jA#(s.e}䘄gD KN𚿠۷رh1G2F-=KrP:aI6F&xzɹxF˨kEyYׯ_[n-))ٹm{III3HJJB[W__vP}cCKS3B ,bo*}{Y""""hs+mܦ4́#q/9ٲr׹4/py,dԣ-wA ̘/XP :wRw#޷"O_/}7SqtѮYxMo.|{'tO6 ກyvdϟ=}ji{ HKLS*Irqq8am Kh`)?2{1hp33gv WvcUaKN.uC+%[Ю549kߑO+Wv’0Y:t)>d)+?L)feu?}͖'-}VyGW}ŧI|mъ}ʼn &._٦.0H-x֐|Ʒi|Ngl FeXC$nrKA׃{)3P /!!y8pt>)ѹ}sqGWjXMݹ1F&lTNGDvvvnn.ϷleVA8Pjdj4 eÂ7I ^`T' C yHH}Do%Wg& $wW&ʃv116mx]P~E$0vX#.>>dCrՔ>B/8d?ФӠu.YH+O ?&7G P6̇kroA:ɼꏧ^>0ſC-}ڱJaڄ?SUM}#yYcPWz{7}P(a{VkPPPQQQAAAAAAi#p@@Xm6\xEԧܳzo2TԌyzQ5*&T|ēiO$gus6%Auk$Fߦq$v}3̫b ?X(+SЦ5n-Z iyF6VH)TN;84:;ɪn? D *4ET𛳦It7wUSx;0xW ֩E^Utqk4- *)=X=# TJmTY%4d7%:IrZaC-YZQJ&@Y9uǾ o0̗_~9cƌWSHI(e~ӧMիp6j-v h;I] "_Vsɽ,:dȠv~20FL|kŊV؄=E__au$&5|UfGI/VXZnѭLCY "LV|ۡW+n+ހhRD4OѲR$C)Z 9ӍOz'‹6m&@kJBQwUq n\rܹ:e1VQ$Ă{gj4jeza0T,| fN+)(V*9%3$H޹[*,9wdV,K*`E.ݜQ<ê|Μ}cCvnY\u-V3m_ť.ql):tꡅ,y6/7=x$es| zĽqbڣ2/ljko!ZpJV fw{-UYh5f:bJbb%Kzaƹ{eqq&oE%vMg4upEܥfQB؉ k; WrDoD?ֲA=̘z"GqhQpkđjgqƼ%/_gn`5->d% o.6@9o?2aL|s\Y=JqnQZ5' ْ$g}]bÔG7gT0]m?5//k6f.k =mx1ݜ~zK (goߪ*%JM]hE(u#zSB1*ԑ ٳ|>b| Kb{9;VϞNbYd2jTV tqX6D$N%HKKk֬|xѹ;~putyy}d29sxY'7d(")RL CʗD"]oC@xV 0(@ $M8EQ埍۵k׉'c Ti!Y&6RS]LY$PiLq^ 0AՈ,==100L$=\#EQݺusb1i_)W$K,4zJܥY?I/qsiF^'^~޾~)ڻ/CGeVvw-<GOܻˀ !Am$O.r ߯\~=iAUk9i׍~YqRcXϦl*8Cㅿ8nt?۸?n()+oWN p<kWwax ȑ3Vu|·Ko5hayƄM߸pd>Do9uo=N.O5[6wt9oy߉[=vhϲyܱ)+v_6lqw͔j9}/?̋+ֺ>u´!o_ҿ3j5^PS1/;`4:$TnA%BLz"̵֬ߔt=mZI_ژ9 [\dWAG!+\AQbVq}Z49uvWP.Nڙ,Ŕ}CM59}^[c quLԱr}/]2Oo/>EvhbP=*.=vʯۈlxI::IؖA'n,(ykSDԪQM׮+ْCyjQ'"l__MSt7=|*`j5^PSYcÉ SB8[кm@xgE)uj/,4br//tzmz񼹤 odo剃M x): YH֛]C^YҺSjʻEh`B'?0.8^CYK"$m˪IDATH{0\۶dٮ3ePL&[P08Į[F lm`[{٠B^`)_[TqzÞ&opȯ ګqzH.D"Rfe%H<~iɞ/˱ׂ(꼠9T2c^ "Bخ]@[FgC[AINJԧb[5Vs՟c9$|m}њNBo=9K7bWbģPbTMYˍ7mcوk'Yo=\G0Dm! tupkǫ3<:6yx竧Ϟx 6 ٯHZl hӁwʯxC)\^POU2W((O^F`a*;+/-!_1࡬lyɽ% Ljfs\>{$ A=7Zl9qƥߒAplݫ% NJ*PbrV$ݶt] Q|+5C E S _ڵ3BH*"AC׭2IHu=& ̯Y]v|e&k7u÷?jGЅ|ĩk Q'$FkJ:r\e64tq`_:w ri&ɜr>xDj~gMSTs0gA~70BTNJ=+3K4ZZk˨e>q#k7UMYLeD%a[)cukd\ӫ(Wy_b~쬬ד~[?_1;Ӂ>%L!u0<-uתmǮgTywNg9EZ4Љcfv?[->uֶen_Y< bт#쟸>zDE@gVzreGw^AȼcPIe1y+ ,;gZ„Yɺ]G={̝}EoP$j;4ˍ l-C8=ߍ Ҧ$֗-Y2a^1o/]26ys穌"Te P`K͖OC-8/Tcx'7iT§U+Dh,l#g-/ ֤tIgΜ{#c_mzә?]}0hBW!Nc t4Mm\C^)-ֱxK:5GF4KzѤQ曨3ATgwdYh0˭Ea:a@ /Ɣ"0Y6vk_b'V /kB^_XXH;lP7UﴘS[x7j}H֡."[v {xzjCTw';_g^pvyP(B,7MP7UHbfx|&i1&CWjc6YN޼HJPտá,)#"8ǰPԦTJɗ/1ZnYfE:T$κ@ FD2՛Y2 ؖ.F,9ÄMqpO}@ 0T]gi1X@L'J0PQ$@P[f;O (:S=7X>n6ef'$RA`ÌXN+:l&I¨B@ 1b' ǓH$2GHEf3)| 0 ,gsbq`@ cE) 2@e(YR .3H%6GYǨu 'wqq!I@`*$`B"0 B,0ځgq$.QbX0f@ clAB 1OSVV^ό%$4k0`0H\]n.Zh4jtZH( fZ Fpl9`=6T4hj( űMP(8!5$$$IeH 0"eYqLyLHR!ΟkإKq{..9y&Q! U*h8qx_SKVqF۶mM6J0LVCɌa@eeei&(AX~@`JԥbARVc4/?b\"Qʽ]6_SF8A(iii8zFiٲ%˲zT@&mcb"Dy7A0L]Id B A@ cE&edd0,+ % 0 &&FTZlVWWWVV1q,@ 0a (JuePצfKRNbVS&PcYGQap~@Jl%xn7y cX$.2H!M`"8aX8ORX=^6Rԙe5xEt;݁m=0H-x[r.J@!nץ{P,zg>cӮ:H30k8ޫto c|Ek l׷jbN( ˱j,XV,+kϟL0o¬q^|4jo[q+~OJmޛ˳4 ur+/"=}:as^˷/8D:eÌ ¦Mĥyyȱ_Ã1.aVxZu<$ Yip P ÄA0 ‘ m\AT1X ZGϚ^ۈ^Rtlٖc6/@@ఖ>{/g-3nyl_p&,k9 mhtL$Y33|nZ|sQ$Pgo݉ '%@QhjZVg0V3Xv Ù-6Ap8aYqWmFT=.vu,ݶse )x#a+km@ɓ'|,`KO/C~vfdKy(MZ2غ}Ζ߿>tP]JfKc';?[tnʻwzukm\Ir˯q{G}ZUwzr6VD-,sYБO9mټbbh9s>_u7lͫgT+ /i$t'n24MQVU <<=EH$ 82EgcnXz=aR)]Rg&.Ӭ\ߪ 1I[á8B54(Kgř &S=?=+us)USDw_B@ޡV.x ฑӆ{=x]S~0*ƓD1p֟caRq!fXkңnNzNC Oyu]=a/TA%#^[dyF7v hct.C8b]G ݺ/bxR'\Q$ݭ1t:L[Y%1^̬! q!Ird2 a]l=v1K[6ĔGa%еb ]daq*E  vUJ1ڴbZlK@ueĦg+?'z+lyzQ\kuߙ PXQ`f[NJQ0)^M7|2'!$v-=jMwҭ0rB!Z6„nBĢ`0pUHIë_dzxy(cE!GDBc-ZE-MᙑPl)H 7Ƣ3h ZjeZ/^o?ndF z.2V{۹;tm&gdˣ٨{88npsXiɞoO]xT'*xYoX=7Ӟ_Zwzabu2=-pR`ܺ ݂|3ًQ3 >R '4_A1Ub;1^8Fo3 GLOAܙ3ׯ]Cq0v=B7vͩà1Ke!CPe=4֒J!( Wxg'W(5)s3LVdZqS_ZOB)"hLjoyaѭLC^tzIYZ') LA3$ kۻIwlnm{4co>${4Hsp5Sd~~H*QRcL e$) 0Oa2b5͓?^}݌{W`ܤKSw8KNY00+~;[U3s<; i;xly'/Cj8l{yE5۷߀~]V^}[NS\9ilSf5pSPsm9>} &%P}Bk'c1Ä5nmYܫfo:~#-;ޅL{>=!Bng3*޹:3||D|ɠ'Il0M* +mcY3P c Y@Ѵ ?xķܲkעWīoNzУxB<߈E[[3n{8u⯖~4p.Gy9oVl V9ɫ绬ٲr&(=1r:^g{ܢjN%#ItPNCnfYv_S`릻onPl߰{~ֳn?%V,~G+%ۿœmqIENDB`sqlkit-0.9.5/doc/html/_static/images/director-model.png0000644000175000017500000006066311533717256022442 0ustar sandrosandroPNG  IHDRdsRGBbKGD pHYs  tIME p{ IDATxw\qX84GTDĆшł=!E(j 5PL ADT@:Rr p(Fo̾v͛7P(@ # @N@ O4Sa*L@ꗋS cɿT2zd/hFr@ A{疬bmbmbe7_hQ_5վw.(*~K`BUӌX z̘xM7! VuhZV,B!I200-#_I1waRIԦSsVGU&툝c'j3&+9,G S__m۶իW#q&$ͤ. 0CI+HtCPa$*Qs ^GESa*?& T-W6pbWuPcb_Glkks2H,>iلö!ֽO<",|%/kC/Z)W;>!!êIqiY[ظ:^/e1oAV76117~J) k5R%P2t I(o*6ҡfjA|^cFa%1AӜz PZu1U ^IWy/skH W/o@ >#.P92Il%j+p İ4s"!(N85lVb!;CM 6\w{VR07Y̫ m'nzoy6=oNR &$D~UWW[s1r_)(JJ2qIf2DJǽ4~7˿ܩ$ 5=܌Y@@du)n3">у? y/YNyg9JO?u?-QQWF R6T9褔OO)e>b&v%Y 7=Sí[Ο?uVP_Fb@Ucn \LzAq3 faO9n/Rn6 dՀo*Gw𼢼eʰX, 'Հ[Srz9|h[y2l} C!}q0вVlC':;f`LpZqlݱm$^<)h(0,,w*ZG;90zu/}tc:wEa}dAKCǬcwoϜ_\e驪1=Ϋzq-6e־zr-x隯,79+T[tج,jI}wqs8g]Y Q4i-ruK,{s\qB|YbSSׯr8___yyy7 89Dt9OwðWM=:sYk0p/ƓxlUA(*(2hGӪ/-WkTuA8WEU h\0ph֦+{=o),u]wyFщ+.1nxj 34fLh+!Bּ:O< $I++?sРAl6{ԩ< Ŀ5  .Gˡt|AY1 hQ!-YRo(5;2̌XE>#`w eu ` h`׿(.jh|>,=Z(9cUIw [2< ZvP<6fu1ߎkT=0T}(EVAZҀ" b._~ӧOWWW 't7FtA]M2ӛIlUAI6][$<t .0*]C!MR>(~p#ek}lgX{τϋn])c<:pe^G;OjZH35uXqϓ k 0id'r 1A)lI[F:COg~ֵIJ7' J?~r+IMD͌j{;W+l>G>ϼwZ k=9 ǥsMR?~-GilQo~›3,@ !_T$FٳϟA~861XԽ @Ͷ.kOCc}!} hH\O\2$LQ,(C`م)qBꝩ8\VAl\p*ЙAM';h :!acbŏk){h .w诣!%a 8z-3kC0aQoE??l:v7 JC=W;sޚg;Nuћ&\rʧ)E7߿]0Hw P2[(H(N6GBt嬅H O4UV 2DWWWQQaÆ!!(А@|d^wu p λfL_#U %CWד䫛? @J&keIMs'Bk:K|'(V߹gs$[㫧8Q }md@ts _IK, >T]מ!]I6.J23d܈6⧻Nߍ Av%5I{yLvplkfgמ!<SHs. 0V&%q!<âJhۿ5"dg7>׋,ܹsMLLqqw7660`ERB LqBa6 uBP(dեΈG$[fycBVFI7.)W;'O9\J"J8|TPߢRS<=mƽ'"җ7Eߪ)5#M w8*i8TQW=r\u56u<6q畣(Oq׾ȔUQ2ZVۮ =l6̕YgxP ~܎cUr~_};IHKW}!0 z#vss[bȑ#L&Qҧ'F_]o] wnՀ k6;oHH,gN/.~ ԙ8Ǝ7p8.~B:0l̋ |u'+h [uEF\eJCaȒ~!4$ȩu!ʝ:̸8(Lψ\nc@[(goy~ylhY k'®{_K V-P&W* Ԙ~8XJ/61 k$@ ]RYtlVJ'5Rkjr^t4ݴ+2zrW6S_Ԑ82Ջ@ 4H&!{yMa3N 0^)9.Tcͤm.m~CKÚj) ~w Ts=N|E'`௣;3̶ҕM6/N׋@ 4` jGWIw [2,dǿ~\*yZ5CpĢ٘o5ؾR(;{OOԍYJL PWRZ^(֤>TxzVmdk_l_LV^[L{R-Œ*K30U=9,@|< 8O@8R\n`((߹QMYzoc/xyٞ[O\e٪޾^:M6[ќzgy" p\:$5+#1YYOY[%sZo{:Y~ga* lbe܍Q8&mh?GyVtM99W{?{Y(}"bwG v ܒċqį]nr)w^ՈI8ʃygn_Z2~d@H9Z?iʊf>A[8BA=ن:.4;4U=l;i0럛ˑ,$lMpbdMhRoH*s plz|ډOQW1 @\qac9Mg:.Mm2_O;Ei`p[- '4?0Ec͞?{g46l~ÍckϜ&o0vh*@ @ r@tbM(5$ $}:Dkmĕk@ (К@ "{@B*ӚLR}gnXX:iz$>YCm_>hHuUНu˕%{|kp43tpUL{.+@ , P8ucɻqrC‚H叜*~K`BUӌr[MP}sOO.|Θ'kǷxSaddwa- QGn |EюO]3 1>#Ᶎs_m͞HO#VI?uپZǔDm:UԜ׉{Í[+D&[m%8%nAݻ6{1Ez3&+m2Sg$ '/5lߘ Q4Rѿ:+]maf:a原D 6Q&BA}IBK|Kw⛬HUi8t$sm')\WP~Ƀ1zC[E;l tY[oe \^F`! ,_4d;d~"Y] p9G%1AӜz PZu1bA˼ ,v[%;]>^ MuqE-~&ՔG*"_jQn,gq5cA($SOzQU#bЂw7*roI)qg)6zSJ-eQWpHvS"u'o |/875l<^TH5_=/R9 s{q"g՟q˼,%7rV7z#o.r?4d$ȍaNkӏqsȻXN|/sM*=UFL&J Z}!4wt*;%] z&N/lxtMF(uvvy󶽛ҽdv%iM`x (z񤌣&$ӑ3Uz\`sh/;lO?:3=OK-o8fUx7%߹``:eu{Oft| rv3zSt/}tc:w駶Se.tCNŹ-1HϖJﮭl{PctyUA1P ofv~NcrV7Rn+a HiJ 499KCFTJWebi6@J6 Q #:JXr,eɰ, gʱ]lyV6E2躪z`ʱ$Ӟ*D ꖊSש 030y#M,O}`nhA&aэ+9:Y9U,x:6yJ@ɢwnW>>g;Au8m ̘ ma,#U geWL1TTB?uοU@0eA]e t"w {TdtЁ!?6xu܉`w$|"D׃iiju=5_oQv58WEUhAʑIm߸NDzo7M$O1F;S~GrI{vSXdWRۏ)zelyoZwƉ}F;ErNmH00)v8YK8yU 0HWSר W_OߙΤA ޭH{n3 77{K#"ct,<( "=H ૾~T_ }v8ڰ  Ј7i1b+cUޫ%[ DH/.sID>H1ZH[#%9P>cXrГ@|SN޺qL#@mƙ(̽ uNs2:~Œ>ė7pjˋ_&uވ2_\3+ k<Ew$3yGj[w2>z[=f0I]g0r'RCBR;Ōm0q|G%D Ӏ@ 4*@ 9; wf6mC~%cʟ cYXy}Zd;_4l0_/3yNk0&nÇ)@ :d >];7Z/V%%L>R BݏkK*~K`BUӌ?'Ks3V!P1p$E둩<~& P;m E=W]e:AJCezdW'J]g'5.,$-ƽz IP4dnkDzJReOǔDm:Uܜ%G}lorі)ʵeb{ߜ>w|v]lPY ƌ⬣Sor=Ԭ>㔍o0k̓ȵ1YmsXΐL95o}^/> q5cA($7j3O/jF5vK!3؁ &½A<);_k8VG = ު_= Ǒam$3KJȒiN=AYx] FCF0qrOK($V%[ ~ɜN|"r$V „SwA|;v,GQOSЅS ؔZU1ڍqsC bA˼ ,v[^۵&8ãפ J gwa^&V^x_rMel/OWsHIX/]|n4A(x^17oaT 'Q޶5ZTX2v?Prr!T5[@ >S.mvFħ_ )Y]c>DѹpC1ܞQᣂ*Gwvi7v=+G^5ia1U^c~_Ѿpw/ӊ\2rѵ_@VFI7.)W;'O9\Сtnp İ4s"+TDty=a2#C{'|po*S"R]MO^ߺZ>1y VAuZc*q_A0vqy:`kf^dm&0t;]YFo=f_uB94}UWtzwJ6YTk{M/ ^2؄%]1tV@H' O8Z: -ݺ|79kq];''c4m W9xhwVmßO⣵k'))U= <0Q} Fa)PUxutMs+MrԍJ+nJϗ5/lwK,[x#XXR@~-iGfž r@\ ԿJ ـ,Uc0"866ƺR/sckn<ռuE- 535Vу\88yN+~Yyq^w .9P[QKI0 ddY2l. Ùr,B1@qZK\.[DECFcmdd(ۨ7D6C9ђ @USպY,I=Ws3b 7VyK wT[ ũ&4tN6Nwpܤx4U!][K^˂ʺXs'Qkjմٹ6\MΫq eM3utN4D=݁)7{r/Lr*JU\@|v5 s+ K}U!,c7qrL\HdΓ ۍ=aXW-^ k_4fl 7Mk@뵒¼GC_ҋ}VFHbdNa]~|a@h|FF|hʈiʰ~Xa'W6,ޟg;SU>Hc;?XtPГF^:cg/xyٞ[O\e٪޾^H/_wn1y 钿AL]O7baFXEdӵ%@RaT9<eC%R9Y A02L3&o \s5ۆY4}߿S>0HWSר M ٜ߸Zxmx|aIGj :Gv%,$!pe!d}%Vsâ{9ooQ+.Th)vNl80,炇*L)?ޛnc7#rb28ՇGo7cmnO޽]Fb`/nؐdO5*6rlw  h@ka`k' g@FwP&g}s$ ogoT\YV {t=vن:XvNyމd$E,c3txz (q_ .o{WܩbESׇ^fLqStU@t~ 6B|DY=q|g;sGX Izh尢+!WmH " <2.7}3f9p;~P4vE3Z^FGWگXy g" DLj֍#e5p肭]UH,4RCBR;H6r @  4F %8 d+(AhNnsPy}Z ]JYM *uɼ1N[|]g'N2@/]6[Mj0&nÇezA qg&?N_K w؅o4^JJ|dARǩKu%D(ŀU[zf ?Yzo A%/__]b'.+1vAzN 5?]e:AJCeNGzdW^*I]߮MN?aq{⇧zA |ڴE,ぽ7޾i3|2}S5{"=X%)WxegSTGssVq)^sF?E[,+^A}buk'ˆL՜Kh@,o@,::u!C ϟ3NN\6 wDˆUAzA HC% F_6{鉪_xr(')ZAlJ*roI)qg)6zSJmgG]XJjb4ƚsѾpw/ӊ\2rѵڄJJ[c=܌_7 c*qӮ/|Gz]t,jX#n@k'TҖ;(kTG?A @ fQ\b8.f2iWU,.@={K90Sn#ۡtnp İ4s"+TDty=a2#C{'|po*S"R]':W`Ь*C.Ea}dAKCǬM`x (^g] w`kf^dm&0t;]YFo=f_uB94}Ug :%] z&N/lxtMFt: KH O8Z: -ݺ|79kq];''c4u^ӣ;ygrm}i2SghI s EULp-$q_m0fFkqp0YW\gsRYtlVJ$`,9Ȳd\3XP_SQ(cⴖr^\ECFJ{6l|Q2mR Ftђ@USպY,I=Ws3b 7V;鱡8y>7ZI41bt|*dUkkcqYPWYk$ʳ+hԪit9=mW; 9-$mN˚g)h9zSn7S9B_F^'U POoU&P. 4l. t˿N<9pQUyUP=⼰W=v9W k_4fl 7Ms- PjZIa^E>+?$KT%oGrkTWEU]JP0jB.ywN d0&aPSFԕp4[zuyBPRhqɚWS}3.Q;8o]m5 9K'6!>:s;c|6d#ݍ'1d[1fW@|#1~sN0@2.%޿$_%-6n>ڲP)n6۱y c'@t?~k(MȫD et̚Ɠ18LZ  UMc[04ø=ȫ 6'txZ\M0B_j.J|\NŁzGKm'QSP =xP[+p\ aKdkME DSWt}YaQ FiRҁ;>.՞E Lq95Q-Vv.n;#JJ@50" Z>hڶ>PXHWb'&_dkMtXgԕhe].&-,Jk9YmX<6Qt. P>㫤˳&="FaLE5n}ڵozjRT{sSÖ+.=tYb?F$$5=:EuvBUjx**m~Fv8.f2iWmt@ 8/rSg`xêzkWOҫ ;OM/ -løߖ^ewŔ2lLt[n%iK6YTcuJ`koSM`ǥ{]vvӆIUxRQhy̾1FږncZf+0hVS130y#VuN6Nwr~`FVyui032CB;2l!o+}m--b8\M0q=k(?֑-L(/3z;}c Yq: d@do6n辖-|ýiZ3L)͔giޏU޼ ݈! Y}=C1Ka NsZ9o@7d.F~lѣ wt (h w1H# VoG /UO`7oguڤ7=4ց~J:o>G Ti@ (`VO 뷵sO?_U-J۹zנAVOQYjƬy6G Ti@ >+xlO|U l6j? $#Y% @ =@ % @ PҀ@ % @ PҀ@ % @ PҀ@ @ (i@ @ (i@ @ (i@ @ 4 @I@ 4 @I@ 4 @I 7>@Wk5 hMm72wonl{Xٹܞ*Z_Q Qri#kHg?R˯fQM&qX==uI֪xyǗq\d=1@Cvk7?:4} P8gc]Mbx)q- ._:Z qio懼/$Լeq7pXZ=~&㸺ٰȴj1 / Ǚ3Dk8C@P};PC58鯤*0hrۿ-IM<1_¯OW2@)Ϩ{{ޑ~o\y }46)[{b v;㲹ev#.NQŭ_RquK+%bթ}l㸖WOՙ`kM.COUpϠkc#NE&pEͥaBAlWw[4!ҏ9uqz@}?ŽcvbXXt?ww%d;ҏ} dݷŋ8|툫S#&;8sSBI[i}?w6*=sQO+ǩeG[ő+{rţiwM'ّ~NL e_|iGBcFx~P`y52])=!wx"̚NZukL>qD'΍LMɺlz7ܠD['4.zuMKĽGW%@JC˩үjwj ézݞ_p8zd _znq8/r^嫛!&ǿ߾(r͂ڦJ m-(%9YE8 E=r̄/ɑPXCu9wnm⊗y~_i*(q?xivZcGF뉆!|Wv_ eG3gc+v|H|:yOBF+T}8Eg*{RژiO:+)>8TMAq)F&_rʛL{;g#'Vk+a+r-OX,6=Ӝ)U5bKE aGmq|{`D~ oŗ ^D%kG\^߮}P,c>; s01B#'3vi 3"Oyx^2fZ M?9#d@NNe_Om{az_n2Ԛ}Gҭ'Lwe,nV̡G \GY!Ǘo=ačަ>c-V܈:7;RM;n;x& tV#,cY_*;r"Z5R{m h =8`G'1ͧF/!Ep{|A8iSI`;k%7@\ouW-=T 9QУ1":Wӷ?0jހہ;wzz^DuC2=8Eקr{sRt\9_kڃFXa,*XB,Iy1aHO[hcL8CЦ[ )To>ݚAթrlk߾6@5@j_EIןY~=-c\`5^KV~cR+om Kft oGֺcfχ _Hg^Q"3N/$<^>}o@+ɵCG00u>P='ZIx/HtWxޑ;gZv`:$ MF(4J4hs +;JVm-Tn2vٺ3yK-GW34Nz b}ScT)b1ɕ7HnYibJ02b&n}԰vt)h~~PDq )2H$:帚*LE4{ [%ߋhk)_'3BEW+.&#eⰨ>G ! +;ƼEة'\yP\ox,WثIGRub˖/1mUPP)ij`CX=J8`Rn @Ho'bK#(on$T-2Ly^鷳JOAA+yi/xȏWcto =\DU2Vd t=5yNcL1R@$* MwxyfР[/V kG"i(b2vCgBR2ռ2ښjeZp)wݸ Wb5 qawy--IJ]c FPڧ AZSO? p4P!qC֓Hdk96uCUն{i6AChjZ躦 Rn~YviF[%I{F31>f?^ w:C H猪?Jڣ}O q|rK`)w?Ɉ$z\1U{iOW~$]u>$g81FPR"1Di 4),ied^7*gR @52ս3t{+ܾS,pR&,,sG[N:Ιnѭ ؞Gt*y/ȼ[$й"IbI(*SG}ѕG EFfF i"Tz (bne^iDϲՋN'R5 e {3B$ UeuJj<( S'zx&.&r #Z0sr =[>$PB P07UZR'kpyyVOlD?1Eb?t(nw|K6/UD"'qPxEK2.w|Ŝ7ٕJ--(P_ZF,4 E_q!Ԕd3"_1N[W#|:7^j"6\R"2Ww%5W}4 a;}bϼ-&YSW&@5W֦{(lwY:њˢs \swjZ}Ⓖ{gbS}}K,tPʖKo꣨ůK+yb Y'ʭގK?!K7Wm }%{Gyc~+8p͝CI)"QN U ܬ N>6ҷ40ל͟DuB`ڌ;uFTSVHgAbSN p`J/(._:(C|0zuBUm'~lgD@Fj7o0ik?7xݾE3xm,vB۴SvZ6V۠||"eđe~cokw,/yEl Z;ر^(FÍWtWo* ߞ{e"@盯0Mp'Őm XcmvIȭD=Ê1LU1dL ԟ7auQuEߔu-B1#SpP;\TC <'o LYH-JJQ'0_L Lr(jo}q)М5ν,?ȏ/($yIv0[N1&15A^9]w^rsӎp;Gi 39IDATg4:bWC[A_2ϗ@1[k%]_OZe~~s4r`8ˈn`7og}~K[fvK8bŁ۵ԗq&zJzJx/>`kYTryCwV)U(`VO > ^%$pXV/.~@(/zX5_G ٍ,*KM՘1φ0A@ * % @ ڞ@ .xm@IENDB`sqlkit-0.9.5/doc/html/_static/images/sqledit.png0000644000175000017500000007137211533717256021175 0ustar sandrosandroPNG  IHDRRm>sRGBbKGD pHYs  tIME:ڔ IDATx}wU;ܲn}eH* P5&J4~5)XbĒ+ؐ"MAiҤ)eil[<+=33> R@@@@@@I@@@@@@О=A{ ' hO@@@@@;@{J}Ⱦb}s(i͟e=VO{qSL6֝?NIv|;˪+f}<@ܔ_qcW)8x֕Kkzt8 ~suγ!sЫ] ~SN{◃D @@@B{X4hjoz=G~y`,Afqﶇyvh.y9^ô.l;߽s򗕟z]%dH}_1BdBVj<zր+{5jm |hN)#}ȁ2'^;,/?|7/ lAH𲕟\95qzE.Zgݮu8o$ 8 =oIt<^ L Uu6)IxQ ~oѽu׭%?tG떗,wۯGʖtOqMD/gRswn[wbbB'oٿ}TtW)WQ,E.!Ո&*>7[ŊG^\y78r\F5L=ovVJbNѠkey0nsrθ2~ݺ@UJ''3).-`iNc2\tҵ}+p|bQ?߽,{Tb,+E2{]>wB^Վ% k8K钔oεO"ĢiEmo۳=xhѩ^YxT߯?oޟj?]hB8^ '\Qаn]hWqo6helLgOu2:+v󴜨;3Ld@р=-3(JG%vΔ$&op+|P0(Q2#NuLH?Z@y63ۭ('Wvw^g٧1ͥ,Eλbǧ__I{_i&dvK.I19yz\Ѵ۴Z+rJz i9sL3\}&;wI_x;s}?zνw.޲=pQ@$?ӏ&tCW1.Z5rOa8y$h`iCmuۏ%w&ǼzM/6frJЮwt" 58G;F% mFh~uLk8QJ.\o)1^f: kVk2ߑqy˞ܾf\}Ń^ֻuԙ5[}A^Ñu{tPKL^f+9;jJcy ɞ2ڝkfuZ^QZwi!c~6i#dѮ{ڛ*+j*ڹ/@ӆ&HrBF:v*Z''wZh>+ݳeey,|}:P%)#cxѶƊc?~~ڙ9zN|+9qW֝zASNuaS@ mzo[uʂ_Y6ii(ϘftZȬxkKk\nEʄqY] |kz EE߭/WO{˵ǟ~;ތK)|QO9V(;mLڎw~3 c'zw׭c \ͳk_Y_q&;-׬26/znX,E~<;mlm6Eւ_1 El!Pު_n{.y'&%ɢ ;P5 kom:'6@ģΞWo#o9 y| ? ؛=A{ |. ' hO@@@@@@О=o =9Q3Ddp}Z"UjWٙ|5==.H,;8-A{a[W왟&Q\%@r%*&ťTsr.tK@@@@^(Sj,'J$; QA@BTտChK鎓޲=9M   w8N29Frz% @>ͨC-4HL%(0.1v \%K@@@@^4pD"D$xTR!tB23MS9 54B0}s0BGΐQ" )H"h_A?~!@~˴ô[[!8a#S~re 8jI4QY&t[QQbcsIeYjx'qY"1t 829qko۵>=CP@u#HL1G̹&C$l;iEoimA%豣mmXo.# TS@@@d_u]˝)9D@@RʃT\aLa\ax k^~Dc=8EgQGЌ6D;]DsPaX\rxnm5yF2Bz3BZqxRbRFf,;R?_AA%PV~bN/S@@@=%HnРb\)D"r~GC RN{9cHe$*hz\`"86/NӖ8CꓶWSipdMU͈,))W_rmY97Ͻ_⣄>q)Cr9eKvSF<Nᙿ# %:!`L`z#!9'cE 'QCj[xjl١l)P 66G8ypUxY!1ۿ(c>CzA@Ԗ/DL+ 5z8QV԰e˖`PNIMKMI-(KKK?rP=M`׮cG * 8#(ȒZQTNbb;6XSc < DH9AR}.l^o@sRg{qطۉGˬOE# hdZW'D`}p>g!ZZ@Xům޴e ZZ9 q -MU]?x=o# "W(q8b&^5?S=xud4j{AsET 9AQUTg} AZD|5XMJgN>]v([:8$B8Gs7ԓ\ZpG[s(I,˒$w)B)}yɎ2pV8c㛚3D$`zѥנGʺMMNwX gBМ}&gi랾l's.v;䍉`0Cȃz6JZahZj(1noLL.y!^*JEU9b7 ":9o r䐱2k4uʜ֭z2o!7} li MMn JIYSk$ .a'QfR ۋ_U//;.KN(;0Ƒӎӊ̌g]SLճ+oaiiiivYsssg_pтKgHK* pNir$p~ Xw G8g76)c`39A]@+ ֵ/( KQG ḇitGUU_QJ9!p8yRТ11_K?|بݷ73c99uS'O+//k,|k333 Ko P"` waw)=;x`L :Y#GwA6JJB90ȩz]a[@SD"(%h HCɥr.'$N16K i!GCBZ[}͍~м!z ՙF{qZ#;3k~|m5U陫֬/~аbn sЁ9W_ӧ]#U@@@y{@}~֯(0mzF%s00N25j]ޔN:dD%葃gKfS%aV i@jKeBHjj#ePx\6)c:Nz=r0)%)555666666+#sUO?/Y -^s/%U@@@z{ P"o]3'K|.^QGK=;x{{ p ;ɩx&!=zdG Ubkxcݥ%RZ.K$BHNN^g!U_[qtE]YSiIvt:U{>4.N,f8״(!@kK.{0{\]j.?H]Ͼ2cpѿVV99H'҂qN!>#No^9E9̨nbB !E)uy<JWZd>ߡZ;A B8d*[q4;\dLeqdsT#ĞDi>i*}qqaCϻkkKJKۃqqRSnD|5w1RZ,9b1.!G/.T@@@4)WP&(HJ^rkfN- T~dFAi+ vT$`e8G/$:LPHڸ8>>61>u chmUWU7&:|M͍ j`9N`Oo{#%qެ84+HA< ڧOW"CAAP8*?YSuЈ8jcL]-`˱9xIɖ gXZq_{0=9{|% $␈-y<'ko yQn=%,I%%%zBEvED3XQ?i̇*Q N.Wh YhK]?NOY:#WHd!iɄ${ FO?HA(r,$9%Ǹ1nGq9d iHZR11piV}f } QTHTSN}ʘ=BXO KI1/]xSm%"wH:c"=ȢE `B\teeCG'ƆA,%/y y0_(◃ FHZHMMKII߯,111>nR{DuN|DIHH瓇qQ^.옍39#ufr\.Au:h5AAԏոOH|Q 5Ѩc{!ia؞"$@7w&^u')ufaKK@@@@@^'W\6o ىC蝕AUX8 !\%o#f( AR/Kj[{\4ϕup/RT#&GZBA [Dlh$#WQ 3f>g{6v`rF4΂o}xsݡ- ?#@ ޞWl=8:/( pn=; m9;~5x|<% :X"`{o}cͣ=vsbv<ט"/*J6.ɨm;PcEk@'=mo?{^ARo/ۇq#ZSrvxi") p`z82\=[hQ ,hxO_-h뫂%4c6V1D^U}1S%.3WeCrg]wZV~qPsuc&H5*na7kaaaaaa$7/9oPn [S!CΑsd3 gL/RZ+n<ln<6A]EsE]{yIWU2dh>cxRqe@ѣ/}TQ߶kݡU1ź,"Q?Q/}J;?N/A$_;HKVMۏ.y"-D߾{qV6`0 y_}1"wR~GI=qGpR?ٵ(*u\=Uo?YML:.'K{*荝re˲i"rlH@*y|U?Sp4[O|8NDƅp5eҎ;qrV$0+5Ъ3S!RkzL3U% _yI)iz>./.-,~t3Rňk>2 'GU~u.9%II.x苯'F -{rgգuU}sFd@✂D~@B$P[)D@@@{ʲ @6'I̥C=a#&1H,cǞXoVnWIK~^eٱ떍{\Ғ^sKN5ą '`2lԤ6 S DJNI:rrFvgT rƧ|}WJH#[D&86* "E f|_\3 r$gH3~.; $t6 hhl^v$a n "hE{o-nGY=)#iB3'zj*5 !Fkѳ.@869ؚ. b1DbvLaE3 ~U>КvSwqjiK2rkWhFyLw0{):tQ7 *CZ 0tX ;pǨB+ 1!X\; FjP) i7[̖`4EBحCw-1{S`$nɹfVfdnV#O_hJ(nGK=AU"ZSnf-vn֥aP1-^p\ULa1NuH}Z Mւ݇M2SߠuO[.}uݭ*Mًd7X,4r,ARze[+B=vRš{RTc)F+h&omj2ZlEh!RB n6 5kK7(V*1b4bRoNXOI{b fǢ1 ]zwfdeV%L&EhFnL!c62=X=4K"<-c'Լ2E%X=?C0TրYBp0nS;^A z&4:ѣwJD5 h-NG(]A@no5K,қ0cIpՀ-#-l]NĦVV ggv ǐj7Սk[ՂCkHK(xZYtUCKҞ#?$l΄j\3mfuh0 ¢zQ;Qk,v"lvTigօV@S8Z+FkX*冨`9Z#%UecHF5WOjmNeݢ2FAҭݲCi7Zf]9hw)ӎEQ4v5 fm9u bV 3"0vD,1]s4CQgЮSrrnD5o~.haD6cP`L Zڋ@DQAK`4eYh莾qFl="덈=$Ti#I,'!2uCMb!n!ͫ[yK%5"8fgԺj'O #ўE矘heh_`6gר2V6#!|pal!Vѵ9Ń% 4= ^dZ5IKHC{gi6z5fRW>Pe\]oՆA#%ܔEcJ1_6p",y-BTS7䦵EO< S>"55O;ik` i+Qbh6>3jח`\hwh0VU~mx\I1:kUB3`ÎYRU* bke\Ww7̞QdeӧG%o!GkڻJ)P%&FrHl=*bIح %<]UM'fu$ :)"X"йx4bQbG>D e=d~un#Tgx-+͑0t kn,!m,6&Z#lVSj?zcdu k\waB(u0btv u BsꩵvhxYt"GX{C؇"UqSnZhihF@GqFaNKZ8CBeխ%fA,#ȼslUawUmUc֙)5g~/yVtD-7Іu|jП?9" +fsZAb fhZ-`6bN- 6T:SHY0J{5CkwIf-@K'6[u:&`X>nȼ9Bn:%#vsb>6"Ln?6.4UlmĦ?/k\Ŀ w4Ҹ M4֬3;Ʋ~~39p?F JmCt 9WQ?4G-rgBjҦj|帣5n/ |/^^cr9U4W|!|H ta}X2?2-Fs<Q&+f, JC2o < /z2)Z s5nx,mӚڭVxnY]Yƴ0 k|f;~96a%@cҰagt@rC"օZ@@le)#>g蔋/B(Бѻ )Z:;^𧛻{_sSnBdDŽ*a|чlTgBDaMyU5cK̐x-mu,mRF3hfǢ1v>e<iNhAA&6$ ;o&ʵ*%kP)c0a TTm,\<pE2pDYvDA/>##-IU[VF'wٗ u#_ܴH f?Y" paךK6(9yƴ ]=uz!IؐQri hr P>(􋥻6$A# 2Nu@hݚg^{|[JZ'|(eSGJ!.?Q}%?gZ7Ck^?JmWWڊ 3z{ՐtG`:QejQ g|Fz_7IAl?╷-j[;z+,۱-9cƬUsVtkI93/#}l2eo T RS=֭GQ<]=/aךU<]`OVf-xFj<|һ@{zo5y#c\isUqXܯ[KԹ|3N=oW{jڳ]>^zر%i|oˀMig^bE69MQWUvyÜ=sf ;}v ”]Wu:cڄ1 ^{lCW_Wtnow*|-Q$ffy8r㇫ViGJZWvףtǖIs1Fjͼk7 e d;+j̑S6~>ۯkYwگb ~Wș]>0a[ڠ\;}*A4Zj6Sn㻺7؆~>9ϩz_R'?sZ&̔dw_3k/}ۮɣ*PGDFQM eY†Opo3fOt03T(>D}F(LE*?=RjJJSO<;-7~LwzRjæ%k|{qO0dTQ>pֵ xu ^[w+xR_ث0Cip; %oί( yOweC$|NTe9gĐ7l+i_>\+0S3a?wҗ1<ٳ#a?\+5}S_(krƹVDis@:dto c\b# ]=9^wHtɸayIf̬۽1qOQ QA.lkhwMMR (H dB=nhʜ:)cz ݴ _Uc.387!!!9!UQ\Sc$&%ɛ|A=qBBQK<2CI5}jrhe(E41T5Z" Zlg`kMۛ$Cy%@Ce+7sXvfj搱}}UGuMI>qdJђ`XS5#pTLaLQT4Z-̓~~`vF9m9٫_l57zyGz&șxh}43N92nr?H huShg#fO:>(=: { (j/Fr;ĄdUn[7ަήܢ)uŷi?Ѯ)c X8\0z&MhyO5 ZD^y+r{\nOeٱmͽ 9gz|kvk:eHR ]-pdJ W~0jiS}byJ>m^y*m\]Ι|~ʿo={4bY܈RbVP+HҘ4CkiN\,5#)m/.oF5uI zOӯm+*(m`T9 "U8,}/0ptK{T'c\-WvC+3=o+?=9*@ڴ( LmՄma9Ӟ3Fy9k:F]7<{tEׯіq=f́3M8N羺ZENLrAT4FW;KpimݹَΊIyco"oBB 6,iXp@z7t/B)~O^\*$xTaFэ$ wBzfZrdot[L?5_zs6T [+ɕ\3O(V8M77'UlANpQh7UVC< X[ufJj ݓI#!+҇ U˙&Iٸp>uEGBf6W?Ykeϝ┐d'ݱ-+c_|u%Jlj;kqS2wr׶+Y)]\#z:n){J׵= 39TڇJv rt`H6Bd>E}76-!*P@П *F+s I8+4Ad8u}:Iť'aцPVˢr`:3O(sBB=VG GδxH[(mW#gJ6/x'kjLr22xL*^r5eǶ~5A39Gdz:LjG3{GR>ظ%&mЮ3]wl{i}}ՑnN=2]B丬WӞ9I+Ll..iq"rܵrՍ ' /rٵ9C.e-ݾ!yXXYx閥;jmڄ\gPs:Q,\[SWUUy[Ҏ`đ<ؑ#YQ湪8P|p^ӎ1i.pL̸,Wש3nxٛխDw3,`6bJ?L֎!2U Z#@bd=G5LK~]*SDH^XׯX,[w)WƮud]bwݥT)4>uvw'C!+7&#MhFkLg=J2U͋~(w\wd`#}# g~vU/ݾJ99{ wӺK %Bf=(SN__g8Yf3kywc` TR%ԋC[`j1Pq2Ve,/B5v{/PK$[L* 'f*yZOsVaA!vnθp }W: ȟz#}Ya`I|:Bd^H͆ h' * ka@^) `WfrхJ~!lf$ BаlGGy'&?Ě/7R39%&>:yzJuy rQz*p脤6pBH',kũ-t_48/4EztP7?/ [>Ĺce%Z=oź1}>\2O!hxI 505_)&a[{(1ȍF%თ\%#~RB!Tz{!=BB!a!°BaC!~cFT,2Ӆ Ĭ|ΰ'hKj(>45tb1+0*ӟ빱 CKdnעt"? Ĭ| O,0+P= {@K]X7sAI f[MQS"3X]|'Y<i  cԟbV`k^ Iӥnz.5@T'"CA.3m$V'o۫|L!CKetPSу.IA333͛@PV䫫EPYVFO\q{{{~&B`eJPR`z{KYCsӰuN?k FVgasݽ=VmxpqFg[4i@4R\+>]ZHN# 2al[QU局lFmC m/P JINLT0Q[I]}ř897W^3zEXU{12AP-3nl'Pz{A:ux1կ`o󭌰tTo1cl좭!.[xBnn440 t8;Ys^ҷ-{Uwn(ikGCw2׉^ n1#~߶m<y6"xŀ8|=8p`AA[7wʌ|LՙCWLg 蒮=PU ^RO a|۫KOOrss;vl`` vEYY p~Є/g͸sS/%% 'MRߴpgmfܴW՟lXe^P6Yrdq-TEo^7<=^7s) ban>}Æ ϟ8qbxg^dM]!&Qg;"|73D>gܭ6kjԷ]s]M45DF uzA0xn]O j9Ju2kB8 lz3oʔ)SLYvulEJ=;q|锦:hnΌ>XT||btzh3wBW <*.|RjN$xsyU~ y?zaIW U:ROuu1|X֭xIPYq EAXvNZZ97FϾ|=LU36H߈}@=n.s/R?d4?}~dM0xB#S%aJ&m4zS݋s9UqO׷ =͵)t\:͵-g .$$@6:9d*F3zAe!*!=N!1Q&vM9K%j'\|/^6y2^f⻛KT-ԳЭJl! E*.=j,6բa}8Ř]m!*.i6jՈIzZX{[t-ës2?0 b/\ Iϯ6j 7_)-K~!lf$6BUEc5:} ' Fp:8&j/}QS3 {n5y4H^-~7p @49 m;8|{%'=JglydLL„,-I!}M w{i>"Wk$_ݵ)4  woB:aCh)inx) n3ϥ zF\TqkZFyMtmݺk`C 'B}(x뱨lE\O"hRǼ<[f&_^aꓤ”gN'5 {U {}?E rGP+|#waffvt0PUANqҦn TV\B.%*)qzj$YLGXZˏuVøP '6FDD&^{ALmKG;C?pǍdЬ[ںABpІ÷ @`8z Hi|砳N:?q'eS+gHC=2߉80{űP%R2b4uԢz wEYڮ]FͷYP9vzmuQ*6̗lۆ>']䘱}i4b ֍ÿj{PqNr t߽C8B!TMaJ{5uO_r74he]=.cDD)z)Śli5;J9)cmE\t}kً#B?9 S>gyp$^$mhe`k4T(!%9ɵ +w!Pq7,Y8~ϯ}v.Rh;'lKF jaW*w\o#BHu!'ցRMÄ#~)zZ2G!31ONajmƒvM,nB:ۣ\)ݰտyᓮBU%!0c!j,!=BB!a!°P&M wɲ xל]:+_^iPMw TJX>&j5mmiB0/͙xבzd9:l FJxsbC y ]vj$Я\R)p8!YLR]FB"HIX{{[z&'`AD,L8qqпt2DYs謇j`w%Wwm =0Mv[ J5ibHQϏ}^kh쨯ig]p¿&x$ag$KYSOOqT14n7\avZ]8SR>)ЈuZ5~xJ90#,O?jBKIC Awn߲lm w>\;"Sm+eŕ|7sofw߾Dz{,"đ`7obg5y@ĩS&;\ikBwn^R 8uwUmyxMw;{97wc{k\ݰ aثD}oU< ߤπL3N\7ϣ%mF6h9iQSqCIƭWx36520H Nk!Y܂;{ʷ7!pKR3ۚj뚴[;c;T͈۬S;5>}QciyG56طU[ V T{ANRo^fǢmrm>Q#FRǼ∌ޢ0+69V/Z0IRa3}VCF8V3(se͟5Q-@i4BϣbaR-e'9 +M`odLU=i|s|It{Nn1jj+5'.zE6puM[Јx{Gx=G&r̒,:>{?ʐ 6\cs lATA/jM`x?Z5KܭffvtA%%Y$E ~j+t̅(=is,Oؐ!{{[mhx)Sdeўf@*rOSn^'^̈́F]kc5jc$_H-je(?URR%3 h(L{ FÛ(N!h[g?ϡ-ulퟧc[?|YjQ2$^$mheŒDt`i?uàUZm\8.5H Z  NأRϲD5[k?#I悯chhlX8*g\=}_q@OY5kџݨvh=_e6a/džc= 0.a\Ʉ^gbJ67sC=~ {"~},[6j 7_}> CH@Dpwضm_/ӵl ÛBՒ9(=htBҋK ym8L "57vۨ~Z7>(!%9ɵ +b)"**oxsƸGàUKǚ>grqCAmTJ[Vh;'lKF jaWRD!TNĕ=aFT333LW%khլKNxnl6 q'Q0 0 CHJ&)E(JFd|e+B=BBaC!T]yEuJrs=T/:u^s<3 'BzB! {!=BB!T;4=IENDB`sqlkit-0.9.5/doc/html/_static/images/o2m.png0000644000175000017500000014573611550640765020232 0ustar sandrosandroPNG  IHDRlssRGBbKGD pHYs  tIME :V` IDATxwEf$^ i >"A~**UA@X!$Cn?3?&K}Lϙ\\3#Ry B\<6߈iQoB;М I7($JؘVfLahƦ8:+PvǶdr4 BM=rS]UeLѫw 躎 < ;*$$$$BR PRvCq}TCAѕ@hbTl7ma'ynW!۟ߕPr(P Mh!۴67I9i꨺s)AE| $kV$BqQ*k1H!%JQ.eU]4|Fĺ]QJ!=9E%8F!x:Җ!di}40 6pC)l:~nZ >jR$ۛٶy-=طhE]J+jPJjofuI+H|ؔG;79Sm$۩@(r% E쓻|n5tCR?0WʻCKђUs)saQG|i1_wiۣcSX-,a'ǧ^?vl)'>s&GW=wtJK͟>hbMu˙`SOgbD7MV5ǟq@xN~ܼo}}\ol)gK4̸V4݋n@x.ל:Mp 3_3L+Jm]#ei:o+aq=Xo0[7pK!M*AM]#JIt]^֍vK nkc f1L_>$$< %{Ξ)"ܞ};;dij q ]$VAYy44S]Ug݌Ο71z,&^WrgKo2iv:f~In}$'\`O%0qd 7iDo2erqO(FHɼg6$=+P* O5T 2 ëw})S"ش;"2GlG;nݹt'׺79ŔF"sRjkji;wВrŎ_=H&^À8>ի۹pҝ41Τ} {9_6<^L[OrjVnea+бSIZZ[x(k$S_{4/>$cF\ʤZof+؏w-i0;^ٜlxMk^H+Qcĉc0w_߆vW8yљd#ÛxA׺36V䷯w2byL+ъ#"Xb&.ȴZg֝#N77hcСX?ϯl~zH)A#L;ZPǦdG}|\:ƞ%V^P >\SqnCGY4h؁#μir^δR4 dH} ]%l2kQGk-}oґpOjS#.(/xn~?mGx\3.۟I,D+14Eye1K uV=dp*8F:ggˆ*B, (%,w/=5?Ezk#K&BͥU)C%CU<BN{]Imɑ@^d <߳ V'q&=W6 e'E$Q)Qd'gXݧnV`3R@He{iҙ,\3>6r>~ bUpf+C.B7 fΘƻ'B#&1abF"xV/+62vD M\0Z+&O&  D"TViuvEז!4H$~EURbqL8Nұ cR6m^cgǧ F+XI)OODÈx͕?W-!*8uz%ib_WB(JX#"_VϙKMDܶd,RZeҾ}3)9to=~PJfsdZZ~7kߥb0y~vMY8!zawB;IU"nlG$)zM>@{o!60iX<(6XN'ʑkZ6u>+}43Q^Ȑ"ӶOEyy*;|(fl kO=o"%Cz-cćD EQЎ Oa3#t%JRTTOExn!3N; tvcEXVC&$QT 07fA>d +b5Mthfی~%9Ο48e XEehI'J) ]#!4Ɔ(mgZ]+k$Vg?u_o\νk0b`{g1'79=~CL%WyR̅EjX1ֿp'<gF._y;{z>}8;/@y=31;%B2[_{r.WRZxU ;hin懌oҜk+57'g誕n!M*B$Ȓ1j=a2 m+;NO>I>t|PM6b:_}!J9 httNqq#<.;8ԣ.²z5/:;~~%qƗ.fxcKXl@FZQYU'Uji4Op[,NM٪IGr$ CBBBP.EEE]å5Da ꪂLs\TTDKk ;iKkHv"7!ħG.:e @ÛqE%(?;`4PJ(\WJ!@&V+Z@)' fWM9_+PbHoa6qCI"1G_h;("f恈wM\ xA=>P"JƉ!<%7MbNPu'j~!E2*(E$ư<>'mJ@w \L³"R@hx~|P?&|q#]iQ# <%Ijۇx1>u?#PAGG@ǩ?3٬"<31$$` MJcg3TЄTN SU]MeEln%AHy;UTuw/#Fz%AgxM<ǡ A. EWBeKO8\ݙI۰|B\,+k6GU)]4hOP!nﵜ GPD_4О6v)=&ae,]AKaW~Uyb{|4ægB V,EG4:1{ʄ^(CyBds1!Oi$(=Ӵ].,Qs~S0Mm)AXm@ژz#lB$X$յT#`հ{u e@ E=$$-v!*7PߟWm,A"A٬MG2ǰaC,/i@4OF*:B26c@hn(93C}ߛׄ@|`2^lՍ.‹4ֲeo-,?H&ҥ-5\!=۾v}|ګ?^g)al,K2Oɩ,L1F/r IzTsҙ *\P! 8Tgу8sXmxCBB.q4΍TP>q\*0 TzwN2 AEMGc~VWq œ]ӈ%g[g\`U+ %0B2P۫<Au(T!.NP+rY?ڧ0Q!vnG5P/mzHH?b51(z{}:%C6;d,7ͮC`ngTno!Tݗ =mN!EO ~b%Qbt98 ֭6g}!!!! {HHHHHHH(!!!!!!!`a 'g[\Mro{d__]->}($$$_#J3-bn?:kyue.Ho^;Ѵ(OUؓG"".1P`F,bY}UϷu8Bh+U/46XŽv)t+et߁[Mb)PAo/V0 LXDLiRPi4MѺb)aLeĢ̼nƓ#@*7 ·eW_'|ytdoͣ \?hE[B[+n{p0iWwsե#!fF|/8B \SEMx Ξ0*Q!V;dgkdD$BqQLpK\?$$$vvY~4w$ϳgEhn^B$;6sChQ&#]4?R#„ecYꦍ<`/p-TTZM'F6;G[)pG#ghI}G ->>-ƳʇiAN}x"ksC˘pX~u2l]-gvh6ޒӸZo^ ʶFg/=E?i^)H_^j;$=n]D5W~ua4oy9c37w-+C&;)tW|l(" CBBBaݤeR y ;R΍YĬ޾w|ޝwls@L+px'`r2П[{|?>߼ -y;^\ĴF:f~ 0|Q!rΌE1ؙ|1fqdGSX&o Nox.&+գ$Oq f>hV E_]˚5ǏgXԢ񬳙YCS^%))wHYXZ׳υJФ" n\u蚏2ǐS}p1< TjEӏ}i WX{GQ!=4z o纯.ಛD\(*Gu]R;CDJ)FנrV'MN RmY;\ⳅ6"C*)5!"&X @)>Zg6SY fM垸m큔H/GS'=]^3iVou8BBBBBa5+D9u] Jګ gJt]'gȸg{2 ?ϣ2jh"sdB`r8蘱8tp@ GԢ ,?KhHa}_rїab Mr}O0_g V$8{`5=xcP!t4X*/NV{ RAi\кnYQcTYFbKCTdE_LB݆ ɦ^Eho㽗^&s/Qeb7t$} MVs/y<(iD@qI)ަ- }+KnEe⵶HcF̽&/|1]쾃п2:$bW`bE5v-HҬ(Af]5uL8Oe5q( ZBC9:ϏsU?^3hL.?w,8[GcL},AwD5j _o;׾YE_>K8f>eTsF UG}G#<~S8cU?$Q;qG Vt,=b xGAOsgg'氟?/{}DqzWE?9[nǬN9Q F4[3 "JK<AP6||G2UG1$ݖFF￘^s3 fqFuIYFM UH_-ԩ <@QU0^yVC8 Ӏxm~3ޕgs(?Fdx롌u kv҄񻥔RyQ Bu%t 4 *$97@3͞A-I>}\}K,98~shI}WΑ r~}X\7P(t¶%а".@J!@y ;"{iaMQ϶qB[ Ez8( 4Vw .rO+Пu}Љ, pBpmrf$/JbHw ꆁ% A,jڅQ9B'b=運eY=iH\u[<%t{7imJO>[p]Ji@a^w`m\#ޟ}/p^x人veO]#y{w]]!С[ug$2CBN BBBUdY53? q,LrPR,/YW_(!!8CMM fϞE,aάRV{H'*r{! f3=짐>:b[zkNI{'$$ˆ+,v4u#Grwrs}`2CBnhڇ9V4$$$new[1[n+/z 0x.$$_LR__)† 1|8SLǟq0%%%agPCBBH)ٺu+z*/"s\|1 0 h4vTHH }[!!!2tMg۶̙3)'q73|phim_~TWWcYV {HHȡa\{Wq-`Xj~:/p?{HHWa;v,7&N=~~?Fȑot]tj=pmO sq'H)%7~s'KSO=%g2| UA@ %A 3Qn2 #N% Y2yDx 줸xGp/ Uio$`~]сeYTWRQ^wuamXzAGHVZ9d8֨PC@nߓJ@*j^8T)fxPzT+nkaSPN qOٳ 'Ӆ0$+;[(-/G&=Ku}+n1 n~N6^C*Ayy9\k+vvl㮤uGM{^3%wP D"Nkk Cijj"L1lh# J)40bAXeeC#ر7mk? =YWVt\ei4VE9EخDWQ^d2I t{:B)r*' {6 Ϲ#N'H('gw'yBBIaJHI"aL0UԔtfPuigR %i1'禑R~·I<W#4T*Eee%u6\*p%;s'UGv (h@CS/Pr ~Ǔn;DY{@btMng)QK'nJљui˸$":Tн_!W#-(YKr|S@ɏꛔ=a@,9ccHm`//cտ%QR VIKr+_y` ű**y@4VƕL$d2Q}=N)xh=;X e24\ Ci>|8Y'3~CW<|`D}|,C'r)*&Ofҕd|Hĉj>Kv$Ç2?sy&xrq3A%/iItʢ9'#xJ\E$;^=;u$2fkczF܎Ύ0W"we$x-+4Ԣ[~pC+̢r {Ж]Vmgg'ɶ<mm1njAsFeʂ⤇#"*Uš$IZ;'#fƸܸ[Y-\|^Gs0_=x_qׄ ܕ y>NgLq%YA-YBF3FtH#1|8_JwP\ց|b~SMmyL]PМa8Җyfq3HX:-iO}9S  ۞+<{L%JL/ 񱷬O5?! 'HQ%5Ft;BNTK˄=K.e萡Lr&~n} l&}ţ 1C΢M6a UAX|9C cZ}B !U)&5;>^|b"L'ByeesD#.s}1Ͼg5Fg]{U\\LIJd\5pԘK{ .ݾ /GecG|(s =Pq'u=/JVJU:~ىA_3g޸aH;Ch[ „G"4yh_Hm#K]TZt]'wy2Kh3ٸ1_~4$>ⓩAMM ay.'8k|l sqq1BS߾Y셣rr "zh)eۋR2SI1mj IksYß3[p(sL]:;FSyS/(llkbz8NU A)dK(q r>d`=[,JX4/Zbտ$>xTxLiHHHhR)b񂀹K4_~xy|eί oH ,lOz4ɤSW.+f @z.+L.k;8;zg%= jAEaŊW" ^! =4!$@BHo'n2<sΜdK|^f̙};s j2Zvc_ ?_C͒TϜAcOs.mNkxx:6XJ~qV~4ZFq{O./n$(Ws6g!jI [X}_̦&bu-: ЋM2!TնC-|>"Nօv8XZ""QŁU;9Gko!B类9.w^I&ql7nqhoogݤR^u]٬Ewc?L6H)bEmb1"шW9ҡ-L.=kUZkF9v݄6?k2hy94z2y:+^?%yF;Ǯwv.^Dv͛)Eq$kN^QomU!pƖ 3|$ckhMktKt[ 6wMo3_-5 6Ag3Xطq7&pMfMriZM9 !) K5{ HǮ z!BD:aDa0u[νL&6ư}.St`;ZkRTm׳cz71ZuӴK{:S^0zM#عp)e->#+6P=z(gllNIn%.61G9 R7 CSk)EBjMԲ0ЖBqbg i& HKDžץ|8}ǩPUcCs3MtoqD &޳o8g-HmV(G k?AݔS6 e"{^(7; #+rMG^zѽ{|[D۶G쐛Cݭr\ $bbVtњS~mzGim֔*زM;Y3 aݭ8Z%®ˇuy0 PYUmwmr)#\9_of_D--%w&I'0th4e#B)Q._{o%ObHgRXRhH$rm~g3glU1d2m1Q"DǥbWˌO555d2;z.4 ~R4\nSO9_&jjIGV:WK[fYXd H{8ik\0qaݷG rӾg܋цx}=fLd9Sh18eeeEYvq}+L_kL6ΐ*bànNTͰ j+H+ҵ;< IDAT="-~rgh[ED٨hW} E?7$`*r۲m&?Ůk@V!Bkҳs0lriJ)iڿ͘#νz'tiKY)u[Iv\L&Vw cDTT&]KqA] K oC!h-n/!%Ž 2ֶ#$uW ,2!B8`Y6?fw*CuM 5GhNwxCuu55]إhKJIMM;VR`HR2R KJH_5 X;V4+D!5Q\2XN_[Q4G*1%㴭!BDX"D!B=D!B*!B"DC"Db"D!B"D!B{!B"T!B"DP_9O!B""<:I8#>9!B8E!O`)*kWEz{!_^}رc/K^'ϸl&NJPF;[60i,+,O⽄yiXV(6C񖴓1$G"9!B"{cw6aaM[{J=?ʧgAh<+MMއ'IT$$9V |z! ]L _k|dطucR0DcKW'Fh=SProҕ=At?Q|_ "&>)59z9C"DA%ػ L<q0v7tgӨFcLVABfXeG0uL"dټl!˷6ad&Ƞ2Tߠyť{+<\s?d41Uv멬|a_GO?b%Ěo}[(ݹ1D}>|d&4o3(Oocړh궉s}Ap8s~<%H"T%q#m(j!t%Ft&[˴kNÙ5,>/27 P"D؏B# 堲 w)u }8c8ix/9U  &f[]ۺ[~q#˛cT$,g+$b6; 6X(n'jZlf[޼cpdSiTC83ᵅP|$@IT'(KF*`1AlkU60za+$R/C(1,-_]'JUԥ8ckS3n?!B*>4|;?b}lz}F|R D ,2QZ6<*w G/<}m_V+G]v\ChgBZTTS[7m>]&U/B޽󯻊V W6`I"*\7#85v{ lp)(ƨC8;BvK(d}m.r`d .G=lxmv52(Uɴ좩-˶Wࡧv!(UCL>ͷ]1M{BzX<.kWm`kYdKhFS&UXy;7u9TYϝ߹Wnᮟ`"D~HE FОqD+1~B Q!0ZCcsü^/&HNjV>~b0_DbٞZ;DsY˯H.UED*;~n垳k`$1T9_ s4 $=N:bGVzYj`I\FƟ8߉' +QLFcP՗Oo_ͷQ2"1ʸapN>aG?oʞ[%DnTjY#J3"Dcc^zvRyxli\\e’\ؠ\ۖR(WM'cXD)aI$W,F wh6p? 4 W{ϊ/-%-0mPEݣ\_3D<ȲmdNkelv5^ ma\y"j(қ'@kuaXF(/oG,bί+#lo-0|Z=DF4jq gA5*XO0ocQ. g&Q ]H]w[:\χ8R*w5cd ָZ#wG*:?SEsู?C{׍R~_A Z)*!BsCK7=?b DD!B~|tCLCxOJ mmi*BGxivɸɧr≃FBNg?5Nl6NHP/RЫ ,+k֠Tk8)!B:NY9  ;N\K>I #Z\pR|Yw|TC2@6eذzJ9Ą^CGgIX}\xN&ŚKX5\7ntݏqNH \֮~7Y¶pRBxq\7':r"=ǣbe3f_"_7ֱv&L,._̬sGHD i-D")Sq5n1؅ZٗdYOŦ+B9{xk{a]7eY&6c>7~n:N!-!uVdȻ}}^ 4U+1;:&7E ۲m(X"aϊUߒF,[%,ߣۄ#Tc$EB{mŧ]#c:vH'8-[/} .$v4#?&m'>D1hOajU$H˓_mgӺ M8e5#N+shOi_fYBJdMe ׄ׆8JY_j>ki_o+ =؃7;TIŏIrnMl&0O1?x ?~%e'~^(4~Jq./|*FUgx?٭YT:C╳H._1ÆsvU'i̛u[w׸z`t6ß c\gΧ&&_ƈi]k ۼ<Ie~{\Wn3"'z=d8ᒋ.:OqV?l1PqASkAbto+_{[-WBV~Xkek&j@9W7iM;jBs:0nc™8߾e [ObZVeL1(7|;1bC|t`ޘ]oAD㎫^;꽁$1eRuzZ[Z,@c?DȺ n#t˼`X2:PϿx6?7}no&f~7$q;d!e.j&%獪⥻Ë1N[\ _W^I+ɹ߾m}$1ŇO~y5|8/A \/2ⷸoå|a;>2n6]ex^dx'kEk]*ü;9KH)C:SKS&oXY˹Ղ2 lt(IN8kd_[t)7޷vedjPֳ7C{bGu3@j6{]śV[ f})'qBڅ~ 0H6hk;c$z1RΨQD,ɠUd5}B 8@[7z7:ʪZweh>|?c>-!T))+:Sܹmc'̤:fa'rRxbU3tC8*%]\ȏ*=aߚ;oE= `"8_}.ϳ,9q:ƀT侅Mˆ9ܵ,DkS$Eެ |v\[AJ*cF,]]iE"{汫UѳNE C2zSj 18𶌐D!F7$y7Wj=oX=#fHi*\(KeIԑȲaQXeUW'hY4O>-o<1.9}o%Ne$19l%F!$i4=.KFM7G٭y=ءWEn8Gf珱t[ MʫC#SFG'ѮР=ፋWP_[Y,VH}-)JuayuJcY_%c+pAdKV>8"TGMҒ`jN_-aTNnU\p|T(?s)ym6 ̼.%Aհ?\'գ! :W*s\wy?nG5c\$ӺcG4!Š?v+OՃgR\)e_`5*LX2|-DcZ{ h m|sw㈢qUpއo`o,myϔ|_*(zGJ_nv'gsN%<Ic^zvRzE<%JcDI? Lx7E]t G#.ٶvx&MQXqI9h<%e41)Ʊt@FH$lm)+#Ӟ(N{!RF+̾D" $bG-,錋6H"NDgh"87C{VHVDqZR8d$NviO)b d:E OCx7!| 9"R,;JTjRYh,;.hv3d\D'D bر8;ht&EHmDIl;)mH+0v׈b^ ` |3Sσ,`ibKNsA6ɍYْ \׽{:MlNg|ͦqsi\nɐv2E7PH9n:pS>I7C"ٶT!B8ּuEHY9(4I)U*Feb͒v 8)E(dYC]0"DM+$PDbl!BwͺD": ;yRW,WEQ <+9ġDpRBx qxi Ɲ8'lԸ,3κG6${P#8)2|kV(7NJD"qFO 0-ē3SqpCӝMcG!ӑ֔.ajSt2omZ.7->|[GCcvL';lJ[:H[o@ˏ+p-x`tG{%rC6-^KV:[5EG RaZu.;?_B?M T`97tҦ 8`ܱe= h臁]SvY`2-O?%g@$h&i33?r4.7~e" )>3+R 1vkt"r+Uk9tG\B_ڟ .紘gM%r%Ӧ= ZRH:"Yvy' J\Mys%0+Y7<&M){*R0ܔ`J68i{BJ C||dL1?+Df٠ L[^~Jm6*!ϼ]5d2 B }&&ww:bA;KA /`MQEjk5uAFEAɟ^DN{$q7 UA#Zh_# )h8ȳH X`|:mƠ];E 9(sbY7E2)s_/xu1Fq^ǕDA)s p D*?aBx}0c8}ĀM^a #9烙 3(Ѡ\UdHaȏ i3@q?F/もY:W9aY$t\y IDAT%1%^܁[Hq^)Yssss@k *SgׇyϯcgU>ꂒ$R"ЪR֧܃_WF4s]:?%` #Q c{dA{FA@7QA U08讖("ܛ^SBk#\nΘ`(l(XCBSb`JK\J| z-Nd&A()Lƚ(' S"9g`h\JHqU:.P[a rL2kmqQ*FLIL^!7Olit)UŴkH.("|Co%/Ce :MўDt[GcWgJiT@3C^7@$RYҎ $lP%sޑxM׳d; ,X"q4׎)>+:xWT9sXtچ b1uq0 wsY'갃-: {JM+>qb$ ) C%ޚ,o@4/ 5TLﱋbBx<3)`CycY(:3S02؉7M5Etcrƈ0 1GV3hѮ6(m ``dDL+p;}NX`3JaxŝOyh?TFN ;+ rWb9#R!N +;;M DBBw*$sAv7A<#\J EGf`\0K\vsa?fѾ,lXsϟ#y6?8yC+y;=y>beq~KeW3姖L@L45pR.d>kr(7=zh ǵjQdnKBp ;[уe॔žTEIh@jC٦O刯Ԍ)}0r MDF/=yO4b_}8^صA޼~2 A$fxa̯qդ8i"@ u e.8Fx 8P`D0'؊E}E4E5 WX:$&nOaTA*R#W[ [9/rZv:dS#w1hp).W!/`N\щnR4s$3xXiXW1*Ta=ǐcb/Z/; 5"rT`L'sIo i%5 I2)cYF%@ % *_ؖq\-Ҡ]xl hCX\->mˣO(qQFb^hr=l!\rڷ|-`A Tb`W(cH޸jh}T~V3Ɛ07Ei!Xp9[ΌbHRNtnJg 5y04r&Ct zԦ侣5 "^dʊ`[PFv[X5J)"6hH,Wx$W&Pc#4='}~liPSv/x[kIc-;*k"8 p4w󎋋jh*2ض;^Jz| VTxF'GҟKA) ϯmaK/l% Um{E=b=:ǫ~{ZQPN~_; 5oҚI2ApY:izI|x 5FQ,,/ddSN JS7n=S UhGYXG}=去hFtH~4H9c-6<*ѸE^ޛǠ֥kk"]0 aسE=s4w`s{WvƟ>>,&3,yb.Ϯ܍]7s?~ 嘥cw|}KŽKxUԧ4F9(вm ]Į38sT#Mgxq~VqSq0gXX}.8w ~5=D 1e-Ѻ42<{45mzT .==>^j؝+>\qlذƞݰWWٯL84NTӼg^@5-v#N'o':CmJwe ޚmے2Z撤b?Uq)c©1ZDB"ҕ[H%gBtfGSo⑇籵]0t [+8Hhځ'E1O Ò'2wu '5m^̓Ͻ.U'P޴yvӶ^ɧaēs"9SW^!XO*׼̪(>pV=ϭڍ]7.UR.;l[0u7 /fK"T<̪gxa]3 HC6-ٍ1N>Ƙ sٷW^X#38ɺ.J)R%[qu; ~߷\=qKVBv:~i;O'ӝkǻ*ON9G<!*gh\2=9-}L&6qxũgcľ6;Wo!9bƹL`||H6=6v*LΛ{vOGk- 7qJޏ-iֆop=/3sPSϼJrxFv<4#kYSBr\iqɅ2GGX#d$z]D<vν}cpOY; A|S_̴&]?ƥ3#w Ѳn^A69*ˣ<&9ckھKڸGPtO㦨 ]A:W jƻ@_Lhe-)aϛm |"#{S練s Aa0N9ZWI=ĖggMUCM6Kė9exՌ~xl#ˇfLVF/8uܳ}=ZG73lBZn"1l<3tgy}S{7~.Mg(VM'?ۘ5{&jsm6iҟ+?C#kt2]r! ,*_2q$-N9ns?p&W9[?y0% bO<|ol;6g=C&!'4eRJvl]OނN8,w 㧜Ç:;mE6Ζt0g?I{r_ *t+ YٟژMx DGde<2^[sN/1b 2h)2 huPNgrޘDhf^ƀAio2y$G!qo8+W<9+'-y폫m#.qyA{l4f_]ivm]ELSAČ`U0WRVOyf[@Zx~>Vĥ3< /lN@e%aht *Ơ XG !\z?Ek44.#{' #&9ejn/X=3}` ^USpux|[q/M"^y Tԝ7mzvnbpR!k3eh#ƅlwjp5C1m&sϕzS- OCpr6f^Xy>xk,N8s"/,yUT KٴUQӿ3x3ǫa.RF,Q]-۠n5ٰ{*k2Z\'KriLkY2Zvo Kº7|e,qqA]2/v[O q'[$F5jJ'Fmd+>~v,1cQ;RK U2v5f10JȲ:zsj/Qotg1dD+>;]6:BkD9BMmխ.t#дxc0P؟Ӻ "_#薰X!qj\UZrO& %B:8` t߽Ig`7b{=_ gSYiqHuohDn^1QeS͊c7 iPSlyn^?R.t6_'Wi*E<%j[}V~$>6tIT`+.Ir:_RV9Bc}F^EV WhЮ\rОF+qP oH¼ez4tc1Qطq+"b\t@<2?:MSJ2i$.}GR-PG k77P{(ÐͻQ6n*#?{ggU9ĝ"Ρ%ETnDQ10GPTP;sx0 {aX{Z{-vV0RCt\00N+nڷEUT*5;7bqSzpٴq#bR-R/!I$Ul/pI ^oߖ~U &vT@PX<[ٴVo1{ho?:'* >E5rEjJ=,Z ;3"UQ|{944L |h%QS&(֯ى1D֮\YUǵv IKKAi||hEuSѭOծMdp=B4T76D:(2+Fsɠ&|cկ/܉;6$-YaVJC'K+/+h(+:p CPGDd;HE.BM!΅;6FԙΝu2hXt{ZF*ddͿ.!5CǦ⡢ZsIӤS4M-^CnbͲwUW%UxUEdWHN&H/& OEJCvkD41aFi+܉`""*)i*aTW٨*+X I{I1pR ά ,%P {1UfRCɫDLD驮HL G/*S씈⻹lEEVP"j؜lvu6Ȟr &,:!, >R>-Ga)QTt#1I>pH Ɠ?--DN da0X!$'Da4drjMQYj'*&>g9x!:؀sSP$&&(B %9TO`|+5c"+܈ B@ga 11u(H;(H x!4*?\R_HA%܊nǜD +#"„ݦ$&?rDkSYXD͍* 6‚t-R]8aڸ(B +Ս`aM !:3F 8̫W/$%QZPB݋*鈌#6D*)805c ":s<\vW#L\B,&󂭨Jd$&!(@ui1NB$pVQB0V_M!aEiN3To$I51KG2 OQU kX Q8ô$ ,La5hh\ N|0ȑW5JC`DS5dwN˳&Juuz<7[aNON*]VdpqȲ>PETQcڕl %wɨߩ RxBDY7BWcPd%&k j6|{;)Wzن$ZGINI4F{ hw= H36ߊT_j k?Kf~*] _o75]`K~t;.ıV\5?LOg+)=Z)\SΖ6#G)~ V _&&E|VHYMp\ =QU;n۟bnށ 85CCnki 5oig^&`>hB%233iӹo2iFhDıT̟ IDAT(5N>u2 5w˔ `++;% ~V@$Lfܚׇj68lntzÙ좤#1y`8mWMLkW$rP4xJm3 ƀa QU2?JU4$II PYYN#88rMnϷe?鍅%UeNGJr%eDGG!uқ6 /%1.b^B4MCо U08NJ:5hߡ=eѶ]{dJRR)`w (驪I:Af* (*-v-سo?ٹhfCaZ|] kx#Jl̼r_S*f^9چQ/a wR}FK3idWggPk}C?˜NIZ44jmlm{k3kk?Awg.~‹-CL$͓&-Qwx,Z~/XȷcCOuNF'":zWS}9;PnvIދ[5N@B#Sp1ѱ=<5^Au3є?IDƸHni<+INHLb0 ,l`6 cp80Y x5)j]IS12lt&^89m1kuT;֭hMtR:k NkFaEK'nKCOrPpB 7к˯ߩ F<4D}1 ,y ^[Ah2v0VwˇFP{Uh^#Yc$[sOޚ;Gǝ^b/?*ymp"dj eH4PY^7] xcpזw3{%﷑&4A/y~/2t"}= h-49o#PrfA}ve׳ϑc=ѽt.~Wh*}7} IOE>zb֧iZΗ\Cn2.탛%1~xG(ݽ/wVzI= ĵGV[D$|6iBn{aO5o=nk+r?}`חX՛5kw?|5MUQ48= gȲLIe9ee?rQCʄbG;#s˄9u0qw\3=Qk`9 ]r-#fۨ 4 Wxf}w::os~)b;*y4o1}e~[%;w{# ϜGeMEW7SD*7YJ7 'X|ڢz Pժ[͆Xs'3G ξ=h Hk*<,:(޹WޚKUh[ٴ\-T>{od}$fëHnL4_J>8/v ?f)+-G_ђ}ü?|n-%|%st>6mWIJB[&Qubhn_!9)9o%Mo3J:3شQS,AϠ{' >URO^k+qoT38, 15Uź]y,o2H੼xv~!X[gڌ\֪)3%xmċdwX#ne~fBd x{Pr4w x B*k;_<7[w׉1=Z? ޟ*#;&v ۋl*Ic6ʸ<>֮@fV6gC~~%eF'IzUcKt%wEFa1:Myx'ss,f@e+&cwl:7 \͗/E߇+y|Ag2ʘQR&_ّ_~Jcc8eP,{-\YOa_(BɩI>מfm㸾CrǙtYs>x)t(d09̕H;!'_ݷp x{bCl}&&SV9_2E*hfgj^82g:>3% aGDzHr] t6 DA@cvcFbPi7!s:q1 nP~Ÿ9I^Brj>{ Tw2+i։{_uߓ:G!IƐҍ_KsloBLF]ISoE}bjn(cr/;5˘[YY$M?;2  磷7KhI:.!Qaw3w$ SKis^'l̥h9o]r (x;HUlo5NC0$An4w[D&sv4Ӂd$*:hBV=W3|(BXX-w?v=.{Tm [;+Џ̝Htxd"'Cwx\4puJVӷYt},u'%ӹ2<444tl`[]t-RguqGKC!}:7o[Gٺ5 Bޗ3~5 c鼖zg4g]f5?PjW hI.blOtuojd KزPJa+DQw Geq% Hqj˄;S|%*!# W>-ǏYDpjoFO^ Od|`Ì|TBG!QV r@_'Hbcsg+x@|B>U&:1rDr&V(vqU۱2vENVi}Z5pUL2t~D K%V1gJQI~2^%Eb)Cxڌ54U$@SE":?*!4dxf$` 6c">U[GPHSn˃ uNYx#HxvrIDi^Av٨Pi}V"B=:zw!J,y4K<;"^g< *ثJ0 ^lv7L[=;yoo;{tu.F P%c1M8G%\2ưcV/\ʢ4fM<zvD9 S">*hm\Ep]G6*rA5uh u$OgxzO^[X"bHY }Pu?o$mPٽilv]ύ#:KH~^Sʍop߆-Taܜ8o$H@8f̐LZ76L뱳%Qβ7),/A؏ : Ʉ[ȶ"i>Vkע1: J0Cu|z;bMùooO`YPOcc|6ZC}WbW3W'jHv3w%SՋaW:}i"p2`kMUi.Nxc~WI1Kx+3bi}MGz-ጋF)A(1qf)tC Bab"ڥaT3(OQUS_07ΈN=Xmܽ#sDB^y-9AriB*b*?% m̆7'2qy+~=ē9j$o_ueE뛭{]'J.м9QP>j^kFPŦC"#+SǶ}E%LfӡrڏIUz8"DvyxQLY)ړЕm 1s~l4}+٧qL"G̭kẫAݾ7Nc`,A jj6:^|y=xh٥)yzu{W| gb#{QVׅ7| Gǧ/~64-%k}WǨƙ y \:d6~ .]ڶʊ RԑiN5?gEj,7֝9-€a(>_Hxl W#j7gWVj( ^r"/+BlyzD`iO\+˭o.T@SwbWk3{{ (rb 3춁L%<;P͍Ѵ pxg&TIT| [(>zf}Ph0yY$'&;08 рd˅itgcW^5ť3UO֔4>m1t4罟HJ'Eӽp4O{|).kWDԚLsf>0:੬$+Q_Krp3k9?#y8F^h{@|:s+P5Pd*2^h뼛ߌSh9 ~#c}k_PrOȟϢ*Yvn@E2EK>} 횴!j>y|x2>bk|/n~p"#O1~!ٛS[#:9ʛ4n.%jLgmI1J+ιL5xg/O'q,xJN;jqx;L|s?̲?3hU~~yNG7>Vf`"}NO7~Vy{09e;,\>=.a 53-қjztjJ=`4Xz#͛^(`޾R_1.mz3 ~юW{Ep5 =hCZRHJ{o$2%GNbښ]ECV(#_}0~ˠ:6˿d洩 @+ݙc+Y5ilǧq~+^zhiK4M[{2z EQn%1ivlʑ*dLB6kTɪlFA@>m(ۼt.vHh~$Jټj#F2#ʱ[Mlnf}YЦ?M{ iѾ-ll]BQmvZkYpk:݄%k Gh mRpnE5:x //n*G֭Dh֓(1=g' 8{6H`Y4 :g+wYhǫ*-:3 IDATCal݇$Ʒ@?+B@Мٰ2k<:ܾtNvBd>ĺrRZZ)ֻ5śVP؋qzˁ5k1GJv=ڤSؽkDܝKm1ؓOm}0Mw&=6==AvЁhDdZ:Nطi=GJP@PJwԊMVaa:@fۦ=fda:}ncEx k`Ta4#`4U=ѝކﱟ[Иs_~ۍO~kxwa&)C\4t`oPh$sPkd4W%pA1 2z$Y'' 3?u*~O7HuH[gCߎna6vZ5 Ah 5<'pIع;#GRACSU|nh*fЄX,AJ%rMuu_5v"48 <"d]O ψ4 ߩۨk| wSCPRB0)?)&SuCD}jprAo=Q1fFs`, "_iR/yiI$2aXw[\4x#g*x222(2,v QeKw>f9:x/m!Hf]>N:h@BQL ?8x:u -kѻm-d:kk(":Ita4pTWqf6l؄ϫCAS9ד5 ANl\YYGlٺӉhb tRPPXgJ0XLxe90<G` WXd9~ȰChպ5EGEU5,JJGU` @\Ah Ft<^-YʆM ҫ»~lr{ P@̢8tj ^7Jέ\.  I8.&3v OMհ ϭrӭ4Fm-ԹKmcէӉ@Z:sM?p`2k$A;)Ԧ @Ȳm4VzAz=3BKz'x^|>_|0u';] 3 @ju#6uD@0[j)G ǫi Lڟ@@2N[=(I>5&OQD&KU'"xʷ3eTT-0)TxB},~yor%"8wtZ'"5+秒 /?_v?AoC{8~~6 53'{gcꞠ_Z^ [&8^v-Nd td\\3cf]T.c{MQ( GAS*)aGmkNϥ*2! ֖K@;4K'+je|ic)IuW`7 >t0`g2]H!73(OU7293d`'3_x4Y#L}$:7k|!#X$ƬK.G{{o}nAFzPte{yXY#<4 cG]k&/~ 2u3ޥL2 ^r立xc@$[bx:WLxQb:vږk3S*-w y6xT]/GGKĶxhp+qCq1|EIӴ$,znǮ݇I6.\дſ`&C2D%% Y{!"Ǿ#y8&t!VdTA}?FqRm/@پa >ٙͫӟC:w㗗e TWo<!}vvth6S @4A 4:͓K$, Js9K^0aiz#/^ 7ajVzG01F#g/ rxh!&6 pUPF4m#fEjםQ& Msl2|кm#0M0CKhp0FAH=rx&CcߎҢ bR9+sAfH^RgNco8W&jD4Db-j8C*41_YbM6ť4m#`/ I=t ܑϲJٟkB8tAw%׍\˗s-&'|҉0 ]@$D=]b<Ԍ1k2iYEHf3'jom)j˫ l<;pNtA|Sfh"qn#8\h|Vɯ;}\x{2Ǐ=tssRQqգ6pɸa C@@tDYTgPbtR Rj'&PR11J:>rӱBM#nmfIEUD YjH 2Kh$QEUUB©VdB/J5@^#Mft>à AWV;-c 5cbw+BL%'VЩUc6i r~>yI6~̬xjޏpBMJs٧&?V75' *NA e{OKfnD*`/q4_N!\~,#qthp|>ӂ] ԓB/iMLd)`v=RhEL|: S/X#uDP?t(ui;rGTslky=h*ŅDDŽ`i^/!8WUvcL]ՌIZ|;66=ٸJ6g:i=$*fD͙vtODR4$–?Gi,MÄ1Z]7`N$٪]%&. lHJ5۲h-#7iJi8DmUꚄh N 9\ GyAbr*Q:&x4DJZvMD'kHm}JF8#G+˶D4/UZǞKxAYMuN7;(C3yqmC]2'Ȳ|LW|5WdJJ79 D:ⓛi9ˊz̾.)(Ӟ`E"4'62=ۢw%tF,  ]|'nqz Uj`1 z L-HLrb,IIjR" !D'D]0ztH E,WI-^"yqHJ @ii)QQQTNPPP l ǃ( ?]W̤iIDd$iS,F¢HKCokNX6԰\~(R$Tk:m(R{ViOF(ܨjO)9o`PjuhCzwuMߜng (w^Zj[ IR`8-l6t:AAA^ ZRO&U?J@ YV:_xz'Ꙁx:Atuk?5muvp8GI4LF]M0:ZcExwU)99%=9I!= %qY.Ŋ * *mEPqQtB*)wG"H0\1@_5 3 acPyaC8+&e7lءgWUBJӅèGRs :;hĥl) iRHnbA]QF|p#̸[؟fgl W\/ɣLjl(JK*T6c'#)E =Q$X-j/RHEPw7h\U85ffԒOQoe>`4R/ǧ8±*͕l+kj##% ϧ`EZb8Ә1vbN=M,z~ }IDٱoNd t[ԉDp/?^YƒLf+T+|΅̸鯄_ OpYy9bV/g-FbgA]MSDRP[U(ͼ3}Ydؼ {>.(_4œe".7:xxkבXKKLy,m 'zE9sÒ{ǰ<&nNx'&_ ut8 34Ό͖j:iM 0U}ϼN bφuw>Uرi:e0wj#*2[5x7P*ŌұprBiT@SU#$<_VFف_5A`]…1uEoMOBfR 'P+h{b& [xrAk`ԄڭΊFbRMy<k 9߲OĪҰEџfN"Z i4>euU O0p`,_A՗1c28NOErQ|V³<@||6֮}>T4.Y1+{1I49vԯwS>Ώa쇰 8L[+22:+oWT[3vQ+*~L5r?c3wMֆr{e`F3l,i&dZoqtEW_wsp@,F#S=B/ Tmzɟ}'e"X=l.b*Y}ٓGqQ1nz)ɽheE%5htRKN)%}Bំ_MSQL9~.:ţcO2)㲡Cy $"WK93tG:TW(L۹w{FFA*!l$-;D #q}hq5=<ظLZbeCD\O)" sf.qT IDAT\1E ԖO|԰GxwSZjt(|D@zbipm71ڭvLIQc?3w'޸A\q5`b[4XFw( ȤȶӇ`Ը(7˸˷e B+/kQo?7Fk !G׸|vçK {{;'Xf5ش 16ڄǣl23b`^nJWg%:6Xxi'.{gD%GAGkRf;_>)g1a`O)؏· =ﰮy s>OV~'9xgSɊL; NG 0}p .{S{o>$X0>Ӹ)Uskݍ(&\>)L^'FN1] 凴Bq7*zg܇9|*9zG" L|cUۼ'{Ϝw1ir.wC;1mڬ gBu-vKzn.n'^} bgyD!p.uࣤ0?\4 Uҟ1uLJ'VOsBCq+n6b-58z*J]$j{'V#B>TЄpq<:j,ٿh_yiV,<|՜>)b,Y 'x*+)(-ԋNBRAޯGJ2}ji.+ }A/ebVpxPJe,/ dT-auTװs"rwn&{0_Mtl.q,^K(w+Gb^XLLi_ .EG`9>&Tհkr)B)8sB~]d,z'9PMvIߥ/t"pZ^ɣ8ӽ>NMfC8Zٹ˨/Ft%aK u]wFUU2b;i벏h@!}c2.A-25(~w.fLy2g{ 0t`PjQ*444QPd%#7.*VPKN!e\ }umeרTBV|1$]=gϠ7X])wk ݈$lT/yeߖRg%WpΈ$YRc>"Ekьx\?sjhM*֘ߦLc 0GXy_󯭘qAxIHMt|XٷXja#Go%j(rn8po3{S"{sEYYbQ)Zii>{ no Dhuz&GYU]GLl 'X\SȊ+ RS em㨬 (HnBZW8'OJN>NK6Uĕr#F6WL\TrPVY,'bb]WrHȹ眍|VrЋ<./PYD _yz% >_rQ"FJ!!\FxH(<%(H`HN&Z[[عm LTl l޲5kWnqPWfWEVD"]"9HON!.V'+k x<U~@uu6o!($^6@wH$RK$'gџq݊jSh^KRrl6QU~N_D"D}x|jΡ~~Um?ѾBi?g뺯9q%\4qS]J.U;J~ztN+>>^ iKaaau`41DhZ9NN"4o-}RS)%D""D"H$RK$D"]"H$D"H`H$D vD"H$RK$D"9 fGIENDB`sqlkit-0.9.5/doc/html/_static/images/printing.png0000644000175000017500000010407011550640765021351 0ustar sandrosandroPNG  IHDR{rsRGBbKGD pHYs tIME [;f IDATxw\W9sWuιAe[΀ !o1f[%`f.0sg %d[ɶ[nuթ:TWux㨎 dg^:uRkk~gt:q$IB Kݯ)2NCQ$It:L]]olRX=6X/z<+`0X~=L B|>*++qo$ 2 Niihm=Eww7>(+WCtwwӟ||Sz!2>t:Mcc g.*]/eu҂hsnOOO>$wq l`0N蒞GT"K84vilBGGvn:Fez=z^r9rz\.`@דNQɤ.;Y^$ILLLߑfr9$I`0^y>o["{vAe BQ&''ٷo+VЮd2($IZ٦ؾ};EEE\{lF9rnE"޻FFPc0]jF'HvillG__@ @}}=twwH$ꪫlD"N:߯:+**hhh`0O_&֭[Gyy9%%% ZSQQ58VbPRRB6%( xߏ($ H&H&lTUVa4ٷw7oVZE&1TVTR^Q~WLo|'x'N`۹;ʡH9u:z@mm-6l JQRRB*۷FbHt)DQv;ׯ2#Xj<䓌`6ikk{t-h:J&SOӃ^GehY5^~7$ ݻIA8fI{9ك`:^ɤ97mK/D"`ddz={L$/Ze˖B~|n@={p-xം055`@eػw/Ӂi, шj#ͲuVv؁$I$ vАf"MWg.mX(++cڵ|{#<ƒ>8r (tvv/}f^Gf5„%  pp8LAAڌ`0#o￟j-z+ ˲?fٳg?g2$IԩSLNNbZ1 l:%I^n7d'Ozq:z-PpǏgn#I=#?NyH$1 XVlV+@ xxx<۷zoP(#@𞥼{w￟ =r _״NcǎEkk+G?>:EMM FICCovZ֭[G2nI3 UVV} ժ9/zzzرc^W}Ġgvvm۶iinnaL&pBnV)))kfFx<zvt:˖-P(Dyy9˖/j288Nk֬}122hc^DhkkM 'addf3罝}!"t:7GFFXt)3339rN***سg466N&PQQ^'ى(|>Z455r|LNN9Eב?of2~a vZǞd&Lb62@};h4vFHDII XIf3̀PRRBUUVp8^/D .M>癚bݺu-Q0=uz1Œw"| @SS`MMM_4"s"2΍vz"@ "! o.\(.D&)ZȦE>a*g ze'wͷ"_O?_W{Nݬm m |$#pj pa|f៯w_Ta*:w|ZN8<T;/8~W-T;{:3ykAAZv?cjV  Ixw`Mg}0֥vc(m~ }UѾo|=a\Y Ӄ|aWo-V:=PX{>_A|,9};?z; >1W&_<a_`WajoRT˷9@rbdIGN1Uzzz7)SdDQXWQ+c(ٴcy~qQTݞUe_nV.uoh@Q>_Qr3=(?Ѕ˹{\XtZQn=(ʏoULtQ~CVWр|wܙح(?c_P;Sf~KQv|Cl+UW=9M-S@V·R?@u P@>B"u/z3 k< ݰ_₍ #?D?L{uy:z=^)t~:[rWuf{]-T;PGՋg:K+NH94:]ON sxs~/AmSvmjc%Mjz_~-ת+1+Fy(Tdlq.;IRm՟~B5[]waUHޒ2C4pHGUow[ 6, /nVq2)Xaޜ5pϨNK!B1拪Ϩ&>/:Lڥ7SzyWe_p: 1Zb ;걼Xzjc Yϵ'H uVf%P>ɰM/f:ekLAujgGc^%-!QgT#^QG[שɓп^p_ugp^ORpl/:}~35x@C/~t7img|k=: ~kSB&{wRM ٔHt'=USj`/:STLyל_ΙQuTrQVl曼Q:R;[U;g ~Ϊ.EQJ|NR`R˴gߡjyz~{@63K5-S < #`m_}fevezYt{x\3֥((WzjRt̎tExڮ67roy 2 >$̻ri4/h~GT~|ר֨m߭nzlc#r]YTm^Qmy1@Y@ ojQ@gN~)z$Id2"(^ ,E!C<'˩H$ AHRL&QM$ z=EQ0L8N1u8΋f\yt:2*  )8EEE "@E D@ xmE9:'rۋ?&@DI&abb18%%%477Iy0Fł$IDQXM Io[o3WFB@ d(c߾}A2 NƇ?ajkk}tR6lO? {kR[7?ˎ;76D5kP[[拈$ڀ@_oÁ?!?7I6Ee-l6k,ˤi2 Qѿ˘f:;;qtuua6I_(0Yd2f^ynʇ>!Css3hlr9r1d0H$PI0z o`Putr94<`ݻۍ,\s5a2`׮];v%Kp]wK__XG_~Y䯻:VZ̀=Jgg'^իٻw/~z,SSS :{1z=VXj?@VS__bᗿ%Ge˖Q__'?I(;vࡇ NH^ݻ L&*AޏL9_$a0d2\s5=ƓO>륤8FsA"jW_}٬yW)fggq:z\.nd24>qt9%Kd{.}""8CCC<8V\!XnL&ٿ?LOOlx<V^ͼyhooiBc6addz=hߏh4NىjCu]G"`jj ɄҜg/;::ڵkX,A6m{>ψV@ x)))Ǐ q8t:8Gd>_|D"Ayy9033Nbbbe˖y{~TWW~lʕ+),,QPP@*ZƆK9t^I6nHKK 9X,Ɓx׸T1;;K,"^)tXHC'\Wt$Iɤȉ.^ss_(GlB@e!IcccxwT1Qrwlذ7-ߛd'tCCCTVVҡ-A@%ٵk[ne||EQ8uO?t'Np fɳ7)qݴXd ;wnGFFB!frrb/H(bddd2jMK 555,缤$LRXX,ˤR)Fb1\.iz{{v5a PPP-8;;%Q^^NUUFQ4! _0멯WbʕDQ طoF `޼yl6Ӽ꫌NI&v뙞رcfJ>;ԛ^Mtq\LLLpA$I"zjV+ZtAcc# Յ,x<:;;7o&kj@ $imm% ###,ZEQD"tuuv---,^yo hmmeʕ444M&p8LGGb1a;FSSD",TafggI&''Y|رcr9O$aŊFʪUX,LLLNEC3@pvZ[[Q^/;v젱Ӊ(8N/^bppt:jt:ݜG^VZj%HL&9y$lq, K.155FFF,YH6\.dn7;w0118˗/Ge4֒d!NcX(,,I0L!@ x#8NϟOss3 mۦ-p8ep:8q={zgvI=zG222sF<J,#`X`2fXV JEg IDATq8  'Oȑ#9rD"AAAzKb2ؽ{7r9jjjhjj"+0::n"f FnrGyl6k׮2==MWWD˗%222ݻqݔc9p&"h)IsV>;`ЎI4,lr֭[x<088ɓ'aʕ,]wNՙS1E$ѣ8qB"aZYh֭tG s^wwiNCk~yH>a,ԩS .=_PP@yy9tvvrrȲnk('N JQ[[˺up9rC D ΢FǾ}~r9Ѩv~.gNI,[ A2;d޼y8pX,n7. ĢEعs'Q-UB|GӑN&ˑJd2ZY֭[ǫʷ-/sw I>-[NIRZÇٿ?lP(ĺup8!Y 455aZ|  hhhӟvn`ppp8Laa!hll`0?A0 ,]X,hʩS0L\.TTT>ˉD"t:q:ڻydѢEaZZZ`Ŋl6X,̛7z(,,t:rU"mmm6y RY FQ|*oiZwxb".J%XJA8@&c k#dItjuqSN@pӊdZ"# $N8jtB!r)N"$d2St::ۅD"@aPFii霷:#B89ov_ WU1-p&l䜝tڌ`l6{ebb$IɤZճEQP,x{aEwN P[[.SE;6F2JX/AA@Ij#)^  6PQQq i~?;v$<;Kg֬]NDr^@0a=:tI9ps|7fz9ra '9@@[;8qEEEL&())%MOOS\\O i ^l233  f3󘙙!`x91hhh`hpŰX,rM7a6k.&'q:vz<ݻ!6+WRQ^@IvAuU#DQwtpM71=5#Gq=^V~qF=J:*O255ISSk֬:]T4^! ;˪Uعs'>,hݎN0==M__k׮pm6dYSq5o:ڶm555,\H$t:ƍlۋ xڑj)\KKKY}NJ˘7o'8wՊ򤧒ITD<θG_?eetN#DdHvlΝwI0"tRJKKq9ϤL$۶os 3lV+믾N?ICCx^w-c16oB_?7x#d}}'b)--e'OC'hB@w2V^,vbX֮[ @Iq @qI1rF*4/_Wqlkkkq nfz=p˅#bZikkcKLMMsh4РFayGf֭444`2t d4zdYnH}}=ޞzz{q\M&V+Ͽqz:-^%e*VZMQQ! ۷IyȞ^U#Ja4H$2RdEcq-[$>40'Ofa٘z4tpB'"3HDuu5W_}5hd2CV1MkZ}Q.]JGGzsUUUڶ磧p8Cez=z9f,lV-[p9d22mm߿m۶SZVJmM iN$yzi NGYi)gr.cNq89;:(xSJU3T.acZ爞N#Bd64Kss1|>VJJJ QYYyMMM|##Gh)VFMR޽BO!K,~r0}bjn0L1|Y$YFoގall1:DSSLF۰Ɍ͗l9eTPI<ZOx0YтLPXXHΛG.r8ˌ&"#;vI,X-H$X,9(( ^{-&C(//f$QWWG6LIGZ_Zf3]~(zt:Y EQd9|0%ȲL.cjjҲ#\Y!c>LFu8NV\I0d۶LNNr:q\TWWcZI 񼶝bPXqK:;1 R)$^/ d2),JfAg+W@βd=Ν; ,^٤FٳH$Bii) ?N2d|^  fq:TUUaZ<L&Ѩj?i~_dd…畹v:v@f>Y5ꪸZ~_x A3<4D6% 2dyq6ҩT$ ǜّ*t~r95jE$q n}B,N7Ǥ`4atX6f3eeed9NvJKD"xbͼTWWǒ%K(,,$pm1'Ցt:jjj(++C$Į'LbZ1TFӱb ~?& ˅hjDL&CKKiFʊ &@Nqzz|>p\w FB\ǃnG(a҂Nc޼V$tV=X-9~`ez ~`0᤮ÎhOstmnކjd2QVV,瘚tR[ShIU"mmm6(,DpP^^NCe-l4tI0L)ɐ_U[ӑL&d2t:,6O[y3Y$a޽r]wZj9Ȑh\.ǸO6L+(d9RzN5vJ(dY9(F!( T\.Njh (9@As9tZI"ɐf10$S),flWr~GNREO^%ɄW|SΊ@NTiSLqd2x^8X,K`0h\\z/!%IFAU%&$I:S_}8~ =+$iN ID^ @4USSZ455]4l ""wh`4R\l~@$P \o~T年ബ]L~ FHxƢ+"lV@ FtBR)+^D_x?71oD+{QS \TǏW8b".ny˖-C+1(tww~eeeazzzD@ \Q?NCCeeeR).]ʞ={hoogvv^n7@ zHDyy9uuu9ze}>X,)++Ce9v466b49qL@ -XVV` 044D(6mq8f )--UGFFPYYɓ'1͌!2Wޑɗo``H$dٲeF ^W[xbbB bhp8h4200@4v-388H"tRWW<;K(bvvކzV+lqFFF0 466RZzy9߅KjRPP@*" a6q\lܸ)~i֯_,E4d21==M<]I8uǎL&C0dffl6K" JŴLMMH$F¾}U(hL6i2 $ ֬YC,Q4D˗SVVF:frr1&&&HӸn9B"V>D8~8NZꪫڵE0<<XL[!9 j+b 4778|v|ttYϟGri*F0^)RlV@ xkx

>Nee%K.%28NM&Dp\,XRvލ([Iشi}}}477PPPyZ i3L&C<gƍ(³>믧m۶1::Jaa!8NbiK o**$I1<<ÇinnXZgq:444`6[y7PWWGKK NMww7X,R^t:Mii)NiBp R^^N ВjUVVRXXH,crrX,uccc(BMM l߯("'N@Q8qD"AKK k61迤+Wj۲,c٨ٌ(,\r$ID"=gGGhT{X,F*:ǤLV+nyaXp\nxxH$EQ D 5L&jjjh4x}ݧ)ɏ󾄉 fggx<̛7O[ >,˸nMX 6 ͆,˚Y*?x\u:&t3^QdYfvvVFeIl6X~ƐOr(++ y˻;Ǐsql6VeYd2aٴy1ozr3 5 ZNj#TZfD"A͂)D iaq\Z6z!F+Wlx<9ti:$ YghhHsk& ]zQPP@KK eeetwwke_/2,ϹFPɋ/8Gf<|>vw}޹\ns{ϋG.ԩSgf.tYYa{{2\"W \dI$i^/~X,Fcc#N>-)ozK&LLL0=}f]Պt\Ӊgzz5X ׋,˴pB8ORZ&qREuel6 ,pWKVՊd2aXt_b".  ());:,FrI҃h"E.y* N'T͛7( PhNz5,S\\c߾}X,- P(ċ/H  i㡸aì_~Ny3At:311j%HPWW, O>$ӹ<<}:t:ͮ]0LZعuuYUᠲa^|EM.6PDwxzgvv7j>L&C4cbq,FQ{  RPPE=bvMO$88frA8GTs|gYbDݎ `20[=oͦ]"Fv^8֮@pX,}JWK/DCCDD \HDee%G4Q&"+"v}\||u>4L FJV]IJ_NCe5+WEtRRTD6W,[/~K8?ۿKA{ȦK;?>V ^{W_Ү%w{v~au=? x!E"Ȧ`߯Ց\Ǎp'όžaphQM ?{}{Onʽjh 2XX~Nwq.~Uǂb) ܛz~P2z?/nNg}g^ \p}Q"n߻ZTݰ}? uɗìA={.xjϽLw  x% S:\ETJƢ 6> b=v.*6 ]om/|/ѪȜ.ZrA9솹wˎ_vG7б*AlhLEgAa/ u"C6wRvHNq]ϻp99Lr=xj(P|'30DZ]~/\L8}ih })ʃԫ##Q< Q$R}Uj-Qo~׈rD >f<>?|[\ IDAT~^;?}Ӗ;ޅ)vswBhH Jis+4w7ZZl? oD$lκQgA}詃Ok,bE}<|(߂N |.5wwPi-W+vU$ƩWGZ_+ݳOe}sD:˄Om_Gq{aŁ z/,M+>Sw1 [! B '}SmGuAhe Wɢry'}%q$/G+9?!!CYb ̿_T ~ T B-d1\uH\9V¥ QUV|>"scLL.s͔#>_Si}ysDK+:IW;yG?wm /׉6a&b} ## :|F=*'Et(5oԂ׳E$-Sx$#0ԏDmXb˙-h2Th>AȶBʏ`AD,>"5> y{߃CnLH$Tbawѧ~: kh!6ҝ$>`ۣvMp7nxA9ܣ],5!EsQp ,*>ɗſ>89)r:5V1~t7Z[e1׋'H}筟1S 2^q:gEe)1u+~9g :D+*+x$)xūh0qyY9C\8qG-S;O,GysD: ]wZEYI=u̺ν]Gsv)ZE߃8yYL HP:.C5ic-.ncW_X1bH.'a \(Nׇ멃WUtubLŀ{/N Ϟ%N9?*Z;ohӐ vqƝ+Kmq*1eUx&]A^ 7 ΟDJr)yF *addd ObRDrRFD)OLF >0u~s5L&%d 5و)^F@Q(PAE{=PjedsppRw|IMvR/ OVVwDp!Je=p8줨#M__ZvNƶ }9J/+%&ZN#Az !,6>[hՐ*#1/.Jl6#ooot2k֬"$ WnsYgE$J k׮!3[[n;I lڴ YguT Cls*fLM0߄A< B7ع^akbQ!1F Ȏ;X,ȃ yyy\ve'ܨP(D[[,^8EpZ̛7@@{q%|'FVV_~inL]}')^7.B!Zڹ{_+^Xî=ut[|w%/>k(J>3_%#^z"8H N[@wTc:_R$G/f[YO< +}rc |iÁE|G<2,<@*"=cS@xDٙ·ݶd9w$?6/Ê`P(\.?m'x<>bz~`g4Q tA^^]qV,Ŷ/֡dqycFd)DCCQ1d2&Ml6駟RXXHFFa$t:YnYYYP("B=p8ؼy3dffT}LZ 8كjEbcc0aF0qd2~?;v젧.h )R=p:=f'MUU477sjO,bc +xٷS4 /,=z7~vH$ |ytR}Y8pJmm-{կ~!3\ nݺ%K曹;IKKmmmٳTxx'Yh/RQQ /3<% s'H0d֭444p7}݇fcΜ9XVjjjΦ>;J?r\g͚5׋ZHݴ{NUU6m/ ڵk5k%%ezî?\T* 2 \JR >oTߐdHR*wroȠko~ʏ~|#ҜrssQ(J,YE]fϞ=n:jkk&11шjEӑT*j2i$|Urj6o0yl߾^zG̘1͛7Or뭷rJ6n܈&***0+ RWW~;)))yf^uzzzXd '!!:cv}l߾3uT'2e ]w׿x衇(++7d\s5v~ml6W^y%QQQ\={VkKYv-( OLL ˖-zjN'O= ,_3g׿5{Abbbp8۷o[Z-*o/9sz_Xr Ǎ7ވba޼y477SRRW^t;< ׳m66m555[?ŋr0ٳA"`fZuמVV.Q5VlٲDˑJX,8@__V^Ovv6oD>c;<͛7 ;CGGoxhmm 識sma}]n>s, *J*++Z߿|>ʕ+yBPPSSCoo/˖->vT*6Gy$&lقZ+ ĉ),,@b[UF/994{9z= , ;;L~?+Ww!\uUΝ;úדB`8d2博j6NյL}̶J#ԏNjdg磯NO~͛Gqq1?8<l6ҰX,<(JVX/H `׮]ﷱ 6ϔ)Sjn\.q jRXXHBXz- z+1c===( .By, tvvD*f23318o2k;?~HKGM]׍bp:X,Z-FBrrrhmmbAmm-͸\.x $ x<[JN [.^Z[[h4MCC r9L>'vZ3g?iiiqL&OkN\\%%%p8p:x^=\n喰8rp:$''c4qH$j5SXX^4p\zIIIATRUUŁbΝL&f3w&66˗s-n:T*Jш 0m4<uuu(Jh4$&&RRR„ $---lq)0f7cʔ)L>>\Nkk+F~?DGG3}tL&-?H|||O/r-tuu?"))Zj? 6'KOO\pEEE466nawrEEkKss3MMMMx:v(Bד`{'yu$3hwݻw裏b۹ 477R\B`۶m޽LZvj5j{rJJJHOOp`hI"3S"6yyyݻٸq# .faHJJBד͛5kpF [tttdx<455k.ڰnXv-l߾Al۶ ڵk9  jrss"33M6#O__]tzt:g眙̙3yWy1lٲ7bZ曙={6 Ne䥗^t}ll,mmm_p\.(fxikk [mmmlݺ\NQQ---s9t::::[VK^^hZ6mĂ 8"LJATih좸0Oee%n۷ ɄD"᭷ޢ5lY%%%cgݼ444z=G =>E[vfB@n000ɓGbATCTe &M Mf3\s5ꫬXP(ĹK~~~D`죥>~cΝۋd2gdd"YȠUV+9\b!!!Lbbb馛x饗xgP(\}\xᅸnV+qqq$%%!h&$$p饗bZY~=L&Jex p?'' ֮]KNN񤤤`6PZZj4dx<1b-ÁVwb0n8F9pW|bbbC.`R 3յl+bWjMIIsΡj7o^x@zYgw^lق^g֬YfR)QQQ$%%]tǓUz=F*2/7%B _7[SB3h4L0lhoo'77ɄT*eٲedffb2~@ZZ( -Z4jJ_?t:/Bd2:bz<f(n&3g^p|⮻bh4&MDvv6?Jń ;IJJBp 7DKK jK.<:::0 dee"(..ncʔ)466W?x{沈xrrrDדhg?S#-[FVVV[Kh4dffs s=0w\t:wuǓn':u*g} /dtkٰi/u{F &##S̙ŋIGR/{/vVɯhr]!Gjc-B>P+$0ur1㔻948\iHAd/_ܹs8q"III'}+fe ?*cG_שHXLqӖܽ Hrb,x1d8̰9Z>,V᭬$ͥRZ- 0.YB- 1 wml檫:e2OӭyT׶=d|" BT 9mõmߏt3xT߬b {{ IDATY飼 MWW߹ommfCmma}gv(kYr㟸~lf;9hn>N\mn3p|cT* y嗅oLC!dQp@\N\NTJT H (I i @BF#! GprNVd19hTo#*~ ؏~; \!MxP+FO os[A(4ʲõ̙?jhjBnULJҟ twޡ8&3ZЂ" /_;vO?;deeq.USޖOb(v\=6}VM{س1LlfS4wܲKN0JKKDz'tr׃?UDfoތWF_qW\{.-[[KnnR|G^p\ǫ>9Y<؝Ss/z:''$LG^N2yI&c<~Em}'5uTh@U(kl_:X$5xtR) Ŕ) s5%%{9hn&%K"_fDD\V<˾&J7|ɦ χkv:6ʠ`eeТ׫z ] ]l6 LTp֌7s'4q #ޣdd[/3gϛGe Ѳ|9tfH r$# DL,dbq&? lǖU4Y``)!hS*dg%R=šƊwX@cH?vh>m]'R2|ݹ—_RA,!KB0H `?>uq 9f,!?zGPb?>mmb*w$C8rxwP'55wHzhU#  aX0LDGGZuכ >$z}hh즫ۆpyp}r:zVMQOvV"驦Q3'dQn-n'&<&]ݻwsWݘ﹇+8sxAVsլ  Mܝwb߸#[ 6PUUË  --L>޺fƍ\s5%D) dlcPV%ppdǎ2::;gMz {ݾ ~?fSi;8f_K?65cK`au 455gn1fo ԄB 33H;wKի9p`?Ǐ'&&AVV6Eh::;;s\.wBaa!h9ٓ!B@}|V=;x'z##GO:? G&E__MMMZfQ?_bmm-`„ ]Alܸŋ#I5Nz| VK[[;hJBJINN^Ö-[8sH$ƢjH@P |>/#HӡT*#drcTFF*Itt4QQB^I0`0fCV؈sHeRjkk$55FC9L\&ۇDƘ*5RVvt: hF>X,h4Z<^~0*FN*`Gggg@~?x>JOKD!WݻB`64(pc ][ʔ)SXx1F4J~ ˅ZpڒASVE"<7.NnwY0L>NNj PĄunK>VT#J%-@Xjk֬AR$ ٳ)ܱcTcHgg'l6@BK.M$ᡇhxZ2o<ؼy3[,ϘARb"v.TZͅ. Tnr1UÖ-[9s |~?|,^z=ᠩ:v;L4F(,Gqq1PU^JX,֮]VrQVںZPɓPPPVjRYYIss `q )..Fs(^tv;ɓ0qK< $J%Rɓ=ط}! ZEVV6Iۻ}}\RRRƒ;v`&(AM6x/v COOj #tmwtԒR^^ɓZWpE Ym<0^f"J/{vIsSS8 SngSP]]}+Z4F#&LxsP.GhqQP`0RR>l2q hnnfWRILL $ eee|@rr2nK澅Yf>r dK̜9s0jlL>oY8sٳgPF[[FNYlM&* ZûCMM ŨTJK HzZ@}{rqqL6Tq%/brٳh4ƛԐ˹GT!Bqq QW088HRR"iiMJʙ;w.111щZ"9%`0IP]]֏?GyŒ(:Q" K9NILLo2!P1v;;\p!iHf 7;w$//̌p&N.l7쉗H$x=+fY\tBbbb ͦz|Y2e fJŇ~HNNr O~8chllV:m#jGKK L uVʐH$ىng׮]q=P(شi:ڻwo YhۺARSRjeVď; [%Gz m[?Ho83.7?o L<A->s32(.*BP088HGGV TJGGG8AA 0yTJߏd dRS`0x8YDE &&I&oj>\._K20DEt:Ym(pJRj' ʾ}{kɎB&R8NL&yyf\.jnZ4j5˖-G*{ J%$$$`29p`?VkueL&Qky<, 9DE M&ӐNSWWl}F*|pzlذO?gܰN(SSSD[ :TJFFFt\.G233JbBCPVl='?LFuu &c4mZ1>V\l"3#Ci2OxN_/*g5*++TWUh`0HJJ R HFXGt:HF0e6D{p A t PƱw^AjEFFF<  :ǃh4e8HQ6 ΦM%;+븉)7Jv;yHFX, Vk(**&&&NGuu5--ٳKA@@$Dr\/dHC!PRB!!, jRSSȤRLJZj*=n?ɺu^; ??_,ggPGOK3gs! qFR)uy¿].,_|!Fdu44鈊2GKss\.'\lA ( J4-i餦rW<̄D"V ^PPfpT* P\\D]]V-ψÁ㡶ł Rp`Zo#" (ILLDQ__OzzY_Chh@?zOj6INNr)ʵٌfcMi)1 ::: *J\1ʦ>&˗w\.ٳ1f.r z=MMM(*qGJJ ZbAoBFFikm%77zyH&փtzrsrc=$&$@__4Bfyڵ\gyӴ^_@J655%7Y0\Pok .㡷O6m"::I&Qc֯'+3 ''n8LYg < J|l6Ѹ=Ə DpH_oFc45~f34Z eeeTUW180BWΞ=ubQVT*!##֮-%6.K.8<eTʐJeő_PZZOȀ4 ٳg7VRmSziӦ300H}]YUԡh<$''"zֲh"ܬ,~+Ggg'ƍCoᠲ ECC=6.\!}ct-uj̙CJJ*^PeCJ,fϞMbb"j}RSS" b4IIMOmm-mmdg A&NN#66#LFaa!*YBד8n>LʴL&:(tvtPN0LN_<*^(((,C;w^,XA*h455ގNc"f̘VAPA[[$%%Nbb"&N^0 au223s9GNSsQ6ddffGGG'ih$n 7HԩH`TVVq7A\l,gC1V/@9h~&SNETj ..V$F" 㦭i@JIQ4X'XrѾ\ D-^L{b?鸾aR9t2HdrVGUUߓW$?@RLLJJy뭷+?V8<~o+yyGΈ`%?// >ZZmLBn;n*T}v=}{{6o"%9?6#kFwy|Lt{-+u9W1Bj֎!T"%[ $xW@6(đ\TTsJǷPXz"}HFn}"Ng"cdrlC~?OM!++C{ N7=U}Ƽpcٚrj[6ޘK9KK1|{Xt{z ahNԢEX>999bE|p>&H(7./"륄3IqU)L>.vĉ8٧)GӦ~0cIxGA?d|iR&P( ,BtW`LNE"Vwy2YĪ*vZ$H;>*I{vVׇ\*200@mm1 񢋸d^d~?իƒEll,F*)!rb!-_#_w!P2}!;lW~<%S Q y?}jM&V= (NE $… r9<0A^#KȊH$|S",_ {07.6-~@ !3@d2Juu_ى p "Ɉy0l6nӦ o$ljSҸ!Ds{-/4"8@yy9nj|EGGD&>0qw%Q0H]wQ?>nSD#"A t5ݿDn哚>f͢~6?9c 2_H52z âEA v] 9Th87dp8tI_U].* (K >Jt+g<+UAۘC?w.s⭭PƍTOTC=q"ٳQdd!kh܌ߏ0 f$7l@~H" *={g~$C( 燒w4yit kpbDz,Y^-‹]zɖlW$[ql$'^l.c$A0 T"- c"Bi\ с<ޗ<o P*z B!jkkQ [nT"5͛7io;G"IdR_LJvFý+W9td?,ϝs::gSAx!ZM$abb@ l᥎L&`џ?b099N#1<<̻ヒ pex<&I188`vrUF# KLwSq*  3'NpTjşWh4h02CPrJ)“)77۷{Q}ܸqvZ{:\.JKKIR|XV&''`ـK/J,x@]\bjhIDATO>B88uj3a6X,b1y׷~ngnnٌN/1nܸA6tbXH$yصkFz T*E<~^;fӦMR/C8&<\Vk3 3c0jTWW3;;륶J4---HQT=zD"Aee%XVlBnn<®]'sزeʯxH hnn>jqD?| y9L&D"L&aƍ@, vC Eޝ^۷o_pqt>͆bb䀳ԧiϞ=@4%`ZZ[6{eaaAT$# B__=N^8$JH9s#G0??hrى #S\\L}}=+'Ɖ'L&#аF>\xd2I:&,;ɓ,333@@AL&X[KTR+ ID"\xNAAܽu ٹs'tSWWG~~>h4T,f*555455o>Ξ=K(",swa&Y?t:y0LdY~}}%b6['Fuu5_}~֭[fd2rѡ$IܼyZ易D"W{^gjj VK<t:(CCCsrlh4O]ycXVv`mUWWc1LLLLD'+e\"e4˓>c>SѨx ccc;vy sa4 & Jhbyu+ѣGIRܺu ׋Rdffi)-- ZnA***%Jq%:::hhh@r!RVVFuu5555;v VB Nj1eʹDsqmyHz^$IvaT*7n`0H$d2QTTD]]:l6Kyy96l_xUit:ٽ{7%%%\~,[II &FGGQ(~|>i f9l6Jgy A\.mH|> P8jjjBRQPPpO{"c.;V7Xf k,oSO+vVpq,h)||e׳lr܉[%@AF۷"Q ߄B`fOT0A?R_}F)D%K8{YfN @qXu FW^uNbuF j1pKe_lLH!H34?!C?Bn7!h4BenvNYxEvuK;},TVUU1 z1mWcL/ T 48)Ub  ^AU򲴂So/ݲ-xB!Cll㕪.]Ej"1YLQ?Za׺҂4b1 F%`AeȐqyq1̈́Nw ?ngGAtޟw݀)x<cRp8 EBIa"BCLP'$t? Kkwj/}w>7GEHt+(8/NpxDLZY=Yqɚ@WfeȐ?$}7J8QYȟp[w]lXdfSJ !R00*n'B,0ƀ .t*_?|Ҧ$%m!} H0x.:G1x, - 'pdȐ'}q$%%aY%~~_Җc F?ӽSJB.+99!鼠=PyYI 9msEi>}6p=eP3,&aNj:U©6'g0^PuJ!D)ń2ܭ|$ 2Zlv!D+ R|<>’XMκYѶooobT@oEs̃ۈ2F@6LDlj@Ŧ{AԜGxE±LI۲dycq&kʐ!B^7^WEQ&aaxW(c" H9 %sx᫛QѼp 9RQQ_~SyLSf}#ŸztJEUj\\ёS3SLKouA&^dW,J)"<6.3-Vk5׺;%kZV*Vb# 2~ ҷZѣ,:өhZN8a)Et644l6)?#bMMMLLL}]~rw'}pyN[W^^ \ b(۫y_+oźLՕ3^]6X7X-}T ;1Rǟl6`eY鸤(Y([P$$$  ϛw#҇]Wk]{ک jw9Wzɟ6XP?zcMcl6גLXPũ/3ԙZ7ϲLK( y8Sna5kD$L2d;NR)u?)ʖ׵Zt:]mWȦcSgY!pq#QәϷw^ YiPRY))%MԤ/s SZqZ$E]ZJEӚfo j`DXAeȐ ҥvظyɤ4JR( K:!ct:+=yIm{cz9kvh6r.`4 zDP^$,rщ]7*̳g'U0wp;[j 2aSB8 nhVʐ!"SseY= x< p455$']w0z3nYt3c^k:NAo>폯XL J@@H$ $bh$K0cT*F ɤ} }0Qr\>$@Om8_ WN>jΝk0Ap8ҁ}[)6rn>.ln]/\ʀJZYUF"@kc'}Wb#X ` Q)ݠ.¾J!PAc8h-aA(SffO57>*C_G07=[vqy3ft[J x!_M?z< (VU( 7T(Ujuu6lw[Vy4ɝFk7l BT*T*m7ttUEa8KWUW5y.sp[Sf SkgVJK+8T oٴl ^pr >66V׷p8Qh{9^hڋ6!'I\9́@2d 2dA~S 2dҗ!C 2ː!C eȐ!CL2dȐ!C&}2dȐ! 2dȐI_ 2dȤ/C 2dҗ!C 2ː!C eȐ!C&}2dȐ! 2d#g:j+J!D)E("J!D"@ }@?0L !2IY>aAiƀ@)ȧ@  T( ~N!JAj( 4@T,2ɢT,~m P=>O'f!PYgPPÆ@HEu $MG#@!X"Q7<1Y|Kɉh> [wԷ%~CCnbADC",BCMPH{C!7uPC[dǻ/ šAb^B(BPec;[7%'" " XǼAQ鏄R"B toFNw(ٟ _#D :!}\d<TGf/+AB@( P?X|Bو=XO 4#͋! > +>C#sv F# S_1_郄 @4E"LJ4hAäFeMA?4Uw$P^%R;Ԃc2hO|bm[}MB7Nj$},`HD>hBQ nE%p&<|%TX/nG່I6=>I.Qh>0`\wPX@Vo$"ls@p@ kWT? 1hF./@g]ASP 0 Pxv4 ,jF`ǟ[=4gR*w@?#Di0@p]PH#"#\1B:6\[Cm>ri#t-AiW(#l(m2Z6)}GqH [΢v τ̷B-aFo Yk=G3B 9>wtQR-rM5DM3E,2 xBDt˴L˔HH'Lv"I_E2" d[xK3ld%4h(CTr=(5H(l @" #:49zFG#_ʚK롡NHT L H0!;(2?“CBIؤBaEKmm/blE=t.m;yNi+.#Kb- ""G(8+hiD~'$#;Ux-6rGT Ҽ[z\Hwcz:;s?^\dQȴ4mכ/)Wȥ5`ۏ _3_jiEzhFOʪD (V .h}~Q4aK[(Rd LH%q\'WPnRHLOysP},eęJ:n{\7m{䰈[~#"q>!B/@0?A-C 401R$/q iPFvadLzL##b1k+ED[(Q\2&poWZ[9e# TOyFRw_ XġM4JVX($ۢ}u'<=*402"!:E ̙fF$zjZD\'?!F]6Ѕ]oQa 9FG!Q8z6r#4;H?T,bSEvq,!"e'?p*X"(Tn;5mmck~v@i#!Dh39rLg#jK`9e6 ]`>bSQҶR8%M_PdՉ}wX$"kj~޸lr;Fa)=9 k{%;qR>90|ߏ6u7?rw96bƷ>ؗ;8wܸ U>W5yoT}dPor#fn:'p19=&L\`@lX;\ӎ"ߍnj~ ׂE1,8 "@ EzΪœݼ돿8jv9zEϿ_>c )Sbդt˪ 5?:6EEl{}y]=ӡv'<˖UgO9$ۀl^j{0(R1׼u䧏KbQMuP+( MMk8Z2hS;h_ &!5֗ԕn] &q݋UN7&[ vAE6ɤaؘYZ= PQx7*̑SoG{@?# ZAbQ 8|InfÃw LAf.3y:o9¯JM\2#6v)caLم{b ;m}r*ơ_jƱԣ{t.öSGv';Nn+Qݿ 1QEGAw PC]i{nڀ G(F]%{T71h?΢-LLaB< Vs &ke&U_H(vxv6둣Í>UNI=wzL CS525lsDpWҜdfD(YizЦ/{FO"ZQy-5/Z4Q^0Qf^wu5&9$bmT3J.pSі3l[vMU~"?p#C.u)gƙ hqォ7 NI Z(2, -&#B(Ài `hwxGTS8JC)PLDBDtrKmfavJ;+zܜ @ܡkuqd(jqN?TB !Pʘu,8Ѧ1HpEL:'>Y\SBS188XQ/Z!Xϡq/ cbcPc%z F4$U"}J~4( IDAT}x58;jDrx-/x;x,(Ippk FQJBPL**Ns9>bsM=Mj τ~X 4hhF dp>Sѫr `װBK]s8ΝEͧ[T k{]p:flѢe Jz 3sU8}ˬbZ(RM+lL쐐aЀS.ںz@91Wy(# 0g}L:;*TBz!-o &Zz5C ^]T Ð2,*uŔ`Jp?RS PdB'ip7Q._ڏ_cTaV@`ɸӞ]홢rڹoXSSuۭvRb**oJR%X`HӇՁܤ?U$5[]\1!}=ه'ze<|(Gt2ڰvaC{[Ҩ/7nHm c?sbULq-PIӒ5뷌S{k*qJ0 F~\zD{e ǘxF|65pJѤ 2R4ZsMLF>{+0 (FT:R+ R,mRڬGT}|TzK/Rǎlvۗʘze4 "sčS56_j: UGbiĞS _,XwytkDG1+)L<`*͝gL&m=zr(9a(E1gDn9zh* :?8tSI#v[ICS~8|e󰆤^z1T5Џ(yΦ]䢻53BdPSq 0ї=:t;wfU̮]2JQh t`1nҶV$6,k8M`0ߧh]TkA,Li_|l= u7l5u2{u{O4%xeVAE P v(EW\ә`N }vqΜqzh>L~roj~./o2~*@ \QOdzg7ˢF1q uWb89kH_aHN*lFYȎwUGa?zuQժ[_to2~!xW-Z>#Qx2.3cLγWb'Qx6"sk>{W6 2d\+B-;ֱcGd3ʐO%⒎ ZN4T''Ɵ8v8lFs CAVW644k&yͱ1ƨX8X2dՐJ>R+*8,[MB^n~Ni7bbbXmfkuC]emͬ9֗!C_(himuIueeC]IM:h=h<Ԕ'gW75P_RSylIyuXVQ![_ 2~M~˞aojdV)Tqq)Igu0lPjHb(K5.!+?{dȐ!WCRftjoHMN,o8 q h~>qofd7z\55Z6Qcb̉eȐ!%w#=pR*n!>8CNa4<&|yMUAneȐ!M1vdг CD^Rj5*N(.[<'G!]ԳplBdyW\x?‚/~t>{zF?}O6t2珹'ii M~ܻ>#i.׬*;?OqqDz_*ug>Hg^w𺾞ovfW|w CTL+ %M_;eM'ѩ|kJ B)8W>'Nؽ{wII b.-V׹R a]K)U^&E>׈gϼ|K:(MG\ ׎*Yc>K<>kv O[=eܹjY3\ﺉ/}[~5Wdٌ!.ﺭw+@A;E-pis0t2˸zq(矓#8 bՃⰫi?n}ؐ'|Wh~ oL$"?l Ws:|nL CWŒ兩: xf骑/ް@W۞{o7GZnpR:݋E dwqaǥP)^Ͻ߷Zݮ];u{<+ȳc?}q~|{5gg>=jNEos6 ~ rg 3J9w=U /P?STRX_ϔ֞)]~wN?nD7 ;V4'.^tgw HT|G( ?~] c'M'IL6ox~][RLJ[qNɛ?;=dquH(Ulmo@AZ )9Ғ%غܘOC :鿟]"`̣+HqKWRյ#0@ }`2* [SJ1!5u,Ϲ^KJ31&Fg4RҶ3@j7n]_tdu^../<rsNKI*nXeZ5U[lٺmjtc꿽o5_d-#,adWKsQϿy_6ՙ 2,iX%RW)?8m[UZE_Y^P;n !M7(|+gƳB_c4s?{q_)8x5rR9.[wh~.}YpI5BR%sItt:,0(T*^)q{VܬƴNEqxtNIݱ;&l?8޵(LwN$o=p_kɬ$DtW3X ێY:= FzO?vϳ/-:th#7^_К /ٛ7oj[3/PV ~E;+;$e@H>S E*lxXnyy&{ߟֶ+s&K{-]úCn.Va{޴eێoۓ.{6ʌ?juaG0(SkCH_ihI+.YJxD nrj%5f:>\SYSXXu.7rZnTo!Rw"@ G?mm n*՟ 4s 2f"ٱ4LyBR1;`1xܐ(;vܸqСC)I C.GHrH(Pz^SdL}jˡ9X+-'j=v`V@ [5MD&aNuFn#2:>|ˠLS6lo71q1"pE,k01gg_sեH[wr~s6Uw)Puy|WP]~W)x[s^Bv;BRjЛ.`hnnf&!.nU*Rx<|RRsMScyԎ8z' hmݦ#jćkGx pE`x5W(ޤ50$sQJ(˲9(n./0u֍1]b?#FgkN^0yml.%s-'*svP]gwR?m{h9hԻO߭ ْ>д)uV}O647~w_k^\ ;vTh׵SwuN Gis+~ַN9O~H=U 6ojG*FLyO/-r֭YuOv`;vw{1lK zVòvAw44д{WzV߮t7֟= Ru8;RR퐟W|bCE;9 }3[K.'(>"%?݆{x+"Mݶ`{fnb_yri~Eo҇'v3?mAo?9C*ZqO BWS(ե$U?==JR .>cև&aA~kEĒ\5݀+n:#O#RG$z>gQ=Q !Zu<7뮹̤o2L&Rt<3 k6NgRRb1G:ޣTXCZԂ. w)c3Sg(/y/5u=GxZH,:A'fu>؊+?FXQBoP(,<߷o5߬=fLQJ}~_ol4>=rHjd |vO^,<[LcGŇVy76\|ҳʚ_pkܑZYh'xYQ z(-PޱK9-fxK̷]אַ=EE"Xw,杊v=2_zɞ(Bs~\=./?ؾ!"šԙb m<11m;NL74?IV~Ʒf&c:z{k݌񊦒oFR,RܪET @0v/Cqиsn#Tzgy3z /,PAʌa7^@?`½S?=tS^mi{-cvd3H})*$X6-|Jm3ܖpau}=7b[l>~W2l.=zjL.o0N' R"txZq\m6VPq0ǹ\m{fZtsӁ97?HL^eҋ:4oUIFUW`LJ$J*K˫>}6IJg@zqI9 #/'|S /EB7/Nca),$wo9j`s9SPUEU,Ex B:cJ_z @ө=%hdsU"bleLI؄$dJBw3KYW'3dD(P;>ٱd7(D\L] i=Sb3>({uל|ә&O[kڹp# \CZv=D&pўV_xI/EQ2 ò( t)//4999--eYJqRPB0MMM 0krIE/=lA:Q|vK;ҟDQ8N}1bĒzˌۺnJ4 ~OܚTĜG:P3zcGJbGÜg61bO @1T@ JR"!cT7r]ArJLVF@0P+鎫qBhEI)ޜv0Bc]c$T il)v-DgI%s2b <hV Tj.Jo^[Ֆ5"THyʉn7h /C=LSx`jj*ƘaZzY b(zl6oC 흺f˙O{o(񢳋?^]RZQqCz+ڢD ˲Rhܸqyw{=1E1={0cI&E]SuRc6w] T25z( D_) %0L?ɊGZoygw ?Q$EHT=ݧ0 OE(.(p!34%`3@l >!b|?Rz'BH.ZF}?tO[㣽u ʵ_Js؎J"!WQH-5s4.75MfGr!>OpRJדpaB!yFEK=!yzOfG>;Ph=}#vbLԉn^g˗~|lBָT!1%-ONUOgzvz}cy!PUUfڲw7r9|qfiS \rZ1w­q>v1W\Wz@UszA) I)!hzZnhh0 cAxzN*=0m7띲`顃E (EشT`vs0Pu`?x{0;7#T,o?uh /8y (P#3P)]>)u//n2^g="Z $b>Qx*>%59AlϺ<6頼oސ;J #JoEg<p\ckt!EyR1" QF#!$7C+:vXǎ8slζ^o&(t| }˓>ɢ"2曚ӧh4voTpkyd\(Dc׏h%^;y<N'=CT*QA Hg{ N&8W2 g>8P\as}RRa[fQ,X駟~z|V(CF6Hn{^Q܂y!$=Z.CVA@ɣ?V*1g&k#gLֻs]nCϽ>+ {3ːqGmp͑t!qۑNp'> vcPJneo'~Nyy -uͼwI Wo>Sݺusq* .UƵF"j50RnG%Ɨ{ X:Cq:[(e.a8䱻b60vOG:J OɸGp͑~y@/˛g@e Vӥݢgnaqw_߿{F^W˸FUo-Uj:"CD{AaF Ov.iQPԝvTJG8b4jʚm)VM(*,"i2ˮ*mvaԜ9-Mhc ezA NI>!9ҿH񪵩^)0Sc:t.+JJJ񊴪*..n{t*Hn,W" 2>o.Fz..1%6^d(\t1évޝUSSUPWox;(%wqĀ$Cpw/-N "-)" ._~$ Wnowٙ<쎏_=žl!(sRBD_S*\Ğc1dbVã)` -:EYXXX/7Ky\ǃ NF!ZcppuzzS!8C1BLSVf-}I98C&Obb/K~xl$e$ q4=3-AwIJU\f1ESz1`P 3AVJSYfwtps0 ﰰWbD!!!EyfstqnYiyVNqTe; ю DKOƖ> $ F ,;KhO7W;v2LJI):/4n[//+=_*r{k&OΉojeSȰ៌GowB:XtZsHH$aW ^A"5jź\N![6kz=cR5$;dg{IK6w k!ZuɽSULw?ʹw "؞Q%a1.>B<3l OWs끢| L<\K|h޽ҥ{J%uRG{8k$uO@.921,4*{T+8Ч]VQns~8M6<6z㶸$(Lm4gRd~oLFu ˎ4ncTL'.=r㈽~_;3 `ؿ:ʪ!Mԅn06%FU!W[" z68uj01;IwHARϗ=瑟W$9:~C +z%mb)/$MF5-8$o=5ub_XN^{3en_#79s {qfKX쥧JJ*-A/([tzYNa|roDSG*9:eіkqAV奭~=q O+Th6:6 "ٷ͉ͯFqJP:Zhϊ; _ammp+!2%wM\̈Ȕ')7zED0Uyy'fۏgdS`ӸZeZ0zd Yz;Fv;eHc^brJ9To+;ϟ&c3K*4|:NbbK糳Y'fZ 2!ޏSϟ3'vmZ'&ƒ׆O .n]_eҙ]U?VXwDuK,+KdgϞoѢɃG) e1oLy+ >tvRth\ؑN aZsK+T }Z WȋY5/.+j|w1CW7K: v&9Gp3{wUkγ=ܐ.ṣAЈQC_ ?B VҾ3 4<.V!pʾ6cSͼ[/BFM}نv5/=t-}' 7?l⩶ktF’6d5YSk;[_, U fW9W"G4]ygӺy-ds 3~rnbUqR QHkOe 9s=w5g6y^fzˡ7o )+vzmk'tN Lw䶪mUw׏AA>~YYYY9Z=AH(i۶۵+)Id40SWX_L)0VFW5V0X(ƄQ,v!\~fqR. I9v\o*>Vw Sci%IG 잿ƶ^F uwt0w540BVt# oe[{Gu. _tQaIk&Jw:oѓZjx3ȁQr FpK@_~h6s8?֝/h7Uzm-n.>Cg?q8B\ꞽFk|zIQڤ[~=<ׯKQ~Ǝܼ[mtWO'r<8¯M2iz.v^5r|Tٕf/N bU|@U}ttkMWl``=<?[7x|UY9`@Y)y< +WD_R[$DkKTj_ͣ'3xN0aUZE{36_l< Owx EQ6¼Ќ3߭I^׮Doñr:>ymHW/[mE lO&Yesor6kӰAV#\xEBe;|Lo 0P]?g=C~M<&hިhiB0$$5mvu X,V˥o510)7r`.Q<^>Ǿ(Rs%ufeU^gV);Iȿѳ][կ{Ox1C_34غ C@{Ƹ[$Թx4[05!a;h WU u>ˢVcZ]Pլ^\\a.m9?/ qpW oٯlR0c/p,w[o@hֽnƻo4m[ڱQwpڰQh Ā00zQVSQemڶjڼ\RTn2@c2kL&$7&2.2Jw&$?*_ff}ͥ>bɗqXvxUtx=8(ɍfpEL靝g# aLf 0l4Z}QU-&lAqu2#PBxW}v5iwzm^q@>CQT!rwn޾o~ݡӗkԸvd۷[X$~g.a p pc\ˮR94'|UAq=~60ւs,|m-ؾw(,-L{xbHi҃rVo> s6|,ʺmno`?S˼IXrmܙNȭǪ]lBltG6Kx]\R\Cy= g=aΚ몷{g?)򳑃|a 8(2XfpW*3(P':ȅDA.D/N]i݌gvU=z1/&O|ġqנW&;mU(EO^l浳 uk#?do;H <Є)HxY_X=e![ͺ5oCx繟O}70%l֌Z Vu`G(O$}:01b<91= TrR_2&Mǝ|'7@fgx?w鬕3֠5:2$cp`\<~b0!j$ 6 Aƌ3(55^b-uQg@+(muG4lϛ+tIA`AaQȜ|y$Ŝ8~/ ՠ'˵7,"ѣ\`=Ne04i糥Zh E8]B6Uo8h> زbaaEF@ȵ7SH+pPPH=?_'W~"AvV._(pxj^a0E|ɠjV@ HAuP\]======gaaayDhl9/A+bi[G]I4̏~~/=[ϴjI"1EާAxph&2(#0m0//rVgb/ {#E׋䒑b]!ZVKIBNCz]bP*"_> L޵g/kʵf( o\+M~l5__ag'B! \.@PyʂR 6oĻ^0zSHCȌI(T*UGH !2[ @ibKꓣhBy4[lo0..ŷP+:yJi*s%^NloGON+ʒa]^5\M9:oЁ}G0q=<2fbI)iI(ZTUAQ'\R &6:ukG e`0}QiNCiŏ+?A|Opvv !{L8/?QE?x5=ނr(psʒNť A MI{8Ã#gddPU?4ڵkl鿚X<xx80r ٷ:?bhIWjC'0)CuEgsƤwr∡EW=nw\OOU&&=Л-Yz YeyE.&=x[x܅Z]nv+IdK՘LfV,I5͖Gc|7w/zwe]wpUMK>teϐYklϱI=R1rgr*/m>;w2dO)`9bڨw&~&s:Ӝ IDAT6ڹ[|Z*/m;WN\]5ٿKIqpޛsƌuluI :::nCCb{l$*NN48ud95&Te6 _$u;PUkU͘4[3S޴șǵP60 ߻wCٛgvn\RR3)))vvv.,#3ᯊ]a//r[9̣ʥO\Cgҗo0 3~rnbUqRPo~[5C﬚"=f҂qD֙KY&0Ah*?a1sf {o7]*;Z^e_j20  xΠ# AX& MGH<ݹdj;o"vP“DzjR/fպS\.}G:8x;.rRK׆VvkvN>&W4?l9-}Pϛ)},z rsk'Fb3]IN7 vj㸽= K;aЫ}*!йrr,i38Yɸ OۻpҐ.w盛F]~VVsR;w>asMW6]|||veOl+kϘ>+/8a߳lG; lu E)͟'7nvĒ2vѵYttttڛbb^yF?$?mCҌͦ_Uq茗\Ad=&::w@ݻitttAߞ,L֚׊]DGGG;{]mz=1g?]-{؝cޞʁԨ~F[} 8EDxy`d5jPWgw'YY;O{A!a[ qO foe~-S/+_v͞*K~SOJm_ ;bG}rRVƸ- _qUI/Go9>oѨYItr*~"qn\ՠ.;ύaë;f^:T1P<"5{m#n|AQ(JnL;,&tDVە(mNSyg*)y?њ?,U3{yBC3mN;oC=ٵ?2IP Y;?|[3yC;ko=cƔJ]&3nmhߟ{ŧl$s/s/.{Us!S̕pQ*MDtϴ7n߾}aA$@fl]9׽ۖv_t1ny]ۨ7}Qu&p,<PSSٻs<2X!@lde-o0{CYE W'^+7vױSOCG2%`F*L%h4R`tɹ+]?rT1 ߢop\q6u( ?/aah~16zTǥ,3\3FM'p ;v!YOoy_&|`c#c3k4ƃ[lzrZɩTގ˛4'smܙ # |G9R @BC옻ߙQ&ޕDͨ.C\ܼr%^@E_1ؕS9|^C+q " jRrtVHHSW³vyzl;0j=CtSQ9W āO(͌Uy'ۚ_ܯUsSHx~I5&?е]:lѬq6ѧ >HTF A@Fb @7I]浿/P M|њ1W Ċbl(iaЁ! 7L[-2-zүd6nnqu"yʜ]|0bק>?/ACʵܙiֳ, qԀȌ=ݔ{((oc!0 `C3P$-oxjº6XX1g0S4;39n.LՋ9̚܆_t3O(2w0֫+=zmXܼSlp2J a lj )jȉW:i%[F>ܽӆ`i:<*ġ]_rܢKܵum=8g<^nUj<#X @Dn~.)\no$/ Lt<`4x_DдAM:-]ONO EaJuj<.}t_p^[1SGu ՉMW3|3ÀE`8ەI$9&<(뇒0g1''oy=c yIvڅ;99|0FQkڊpyyޞWpl9 ql^X >*W~R^QֵKs;Ph_>XBƜu_Ҫ^$܀^d޽@.H%*TWfBW֜zSytyOKfV}}$~ ZSv|v4OҞ7IrUKT`$M$#~=(h%)vAA9)))<nVAD]3& (yǷD%nF ֍jO;D^Gt:.FuVb7 s3ݾู} WB]w*9UioZ;yV&X>K07&S1E}}=1ދ֌a/o}[o}G\/G,q=<kR&j0al/Λlq6~[ pq&_N:^+=vj!ۼC19Qeϫl|nzweNOP61oڑ>xHsan^8Y˿2\"-s-_[`z7U+. 5cfXXX Ҥ-sI[s tvBf/z O?{7OP/Li -0,wrGa2IHB)Jr>~N*m1{Zc_<}PD?2!r/A.NI"%Zƕp!(lL}&>}w~n$Z'OזY) (͔;y H;0dqI EaTN+H_JlMcaaayD?ɬ1Yk)BQe?[ H(%o/Whʺ|ReHJTja` ]6 hn5k^Y EeefSf&6l\>."0jLy`"I^Ǡ iB4m#\\ Zdq̠հ>.W,K"j"\(FypŪ+iFaLgaaayD_&u㘺T_̵8_6q./= $CkC0*pgK}} I P!C80Mc$$d@1ᨈ/qEV+[,,,,ZUn&ޭv;EQG!wqvD-(7}ZZMmHyyٳg#""JJ *PѶqbTj3rzev 0')SEQTn=_ E ^JOByowa:Mh4AL&Պ H=<==b1MU W @ !j%lyEW^www.ZҬn|uA us-}p+ڋlj)JW  U[-f04n-E$in@[0[AiL$ q$I#o @3z 77Gl6UU2b! VkEz8pp>v֮]{Դ(̏?ۥjƹ#zuܭ%i5tC@߹;FU|>W8>|dxxDnnntTC^A1@(:$0L*AQX(llJc/Vc.k8wLWY7gEz̤ˉ3~La^+߆.k&W|x\]yo-%r8`Uhík۶mo朘?|[6f֢u$77G^v-ϿSN酅?Q E Ð4U\Xd6R) MRVn7rˡN&z"?ضmЄk43={]L qj=î/=P81ww3:IM+LQ]<0Lv` pc\˖ܢ܆K ]^>zq xEt.WapQ;Jum[u6Ⳬ7YmxåS}cv#8r|322JJJOJ eqwq0h];:S?Nxɕ}_6l^;k/ݰ Y6CO&Ƹs~MHc/ 2ԟg\Ң-Jyjڙ]]C$j䌨w.#R:K>rv8ѽ:>IW^4ab``;w3z}&M$ "JeJz#LZmF_)Gs瘀$oڊ֓Ysxх[s3 stBX[sѲwvCM}Z߻[V/P YyYoN_&3`ǹH=hWjlKH7J-;ԬhvUvj[pij*}H]ޤU׭dq+;6<9m3w>a& r{qi{)0:~g"sGM:@ǝYƌ=fVۯF|.ș=!$ 0g( Gn/QƜ}bOi =F;VFf Ɣ lkWڌF_w;:|ӦǑjsu}_t tuu',@ )q\ҲԔ"G]]e2M׽JD[?9gޖMG;}쌽N*y@!`pp98ֱPnjC{ ;swo3\f{jxR]^o9R읙w3VTqau}uhcfˌ㗑}9~H?@ :bҏ+-_]+k2R8T:`eqUέ IRjMIIjD9; IDATxVURiUr9C(ϧixUtfPnj6SOݷw՞ ܥ s 8v[T[k: =Mn'S^ʫ(БrK=xDnVrkM͒)ŨwJ03- Cpk+,d-E-c&M%}hcۻ^C9~)^Em)}qj誀B6>߆l(ONZ94Ѻwh +>zbUr| Gb|.˅8o?  IpS j*է* {| (0$ !ȵ—G pA&{dXihq]|2x؆1ڴrCn|}9T$EϛFi Ӈ d;vũ0̙3FiٌaH$2Y:A!CMfTp1.$dv\@7^6A!/7jr͘m>~( CUZ7ϸWir򵛷-K6Nk v#\|ZfQZ`B!n2j4bW?,x;@c%,l3C $C(R*s;q!Jyҷ^i53vHu"Z9nNIDDQэRn?04XXXc3>:?h_;9,Se8CǽS`^DD^ NzՆ/õ ,5n ImR5r-osF *U9MS:)Ah@|BdlFFZ&tRy{1Hk}?=i:)U]>CoI=}rƽ8Tmp+O]YB_6M>p .<酻Poyleako,Vmaz];LFm\bM9"W ^S$GJnn)Pp9B.$bb]T9}w;ڍ߸qH~foo5uZdpppCR !Da20.G rХߟmsbym'bh7+WL`Ġj6ʆm,!ٓf?xPyyh.T*F\$q$M-6نC^oű>z} -A]*{2'E˶:y 24MuFVKӴ@ D$IA(h g1[KKK٢gaaayDٳi).8RP @TRTq8~NFd2 l>a @ `{,aTb@01am1UҮP2Øg?0T `0k[11@@2𶠊 PY-Pnp6b@?4O=yQGCYXѿxjN^\.WTŞ^vvA0$%`._$(ه9ϋYXXkѧ(ɩm2,=- AGZp8 @4#R#AP 0jť%-zLo&RT999R1$(`0?|088B؊KJJK˻wZR2M ;}}\A\^,xRx{{ €`VPTfU2 #V+[,,,,bT,ɡ,6^_\T A` )cV+QTZbTflѳg_^^r|d6EA (QAPjZ`4h~,,,,,触RE$Qi9(&8(I0 b,VYXXX;d4|{I )54m4b1c& phT*"V+gaaayDl 0K/~6׽ѯ*:Sm׮bayoD#M:ꢷ =phXK*3ׯc9bІ#H1)C+0@鹘)O>*|?q]7 ll˴P.[,,uzbp80`avvv84B2LBiRZyǶ3nXueZ(vʄv5;rA8]6;lV]\ՆG#_-LǾ; w6`龣y9zcGNfC%^W(>ޭZ"(0$$N&SGB  aPq->}Z0.Mi'J~0aC|,w}V LJ 6t搛cN3%xۍ|oXPh4wiРj)--e @QTfvhB"j5i 0 p84OrUc*uhٸcVfl* Ε>:,i3bȵӅ킂|#](9l)|$a2X AHIJoZ2PZ*+`q8 _rLm{w\Gǟݻݫp݂{c MXbM4ƚX![D"(HR뷻X@A|`fvvv>;[F3gh~zgG !X2ˋ颢"WO\]]$~~-ZwtX$T aA`0 jwپ&\0t9KŦKQ@afg"n"fr"ӟ,T-P*tqp`8<[;@|offb1I5󳰰..mڷsuwfl.0 MNo,'-9OJzQׇvy{Vz60k{Y6Ͱ0?*rn]/ݱ@^Բb+V᝸b\A & 'urX,l% !_ (V&&?DzdKe%>ghr8(fkڳVlpneC=ڣUr XՖƴQYH./+ݼqoʀio*SlBC6FMjE!L!'͢hq) U($ijj JdAv wt}OOϚV(acФs,7͐z?-n{W>1 D HtyG >۵;G\8? vz껲@јal)ng)1`(`S {%,\!+.++DQGue-}e"#\$þrvM #oF~scN78tCԆ ztQz})x< mH$9 Velax@ MNS><_ժj>_]^ԩúZ[Q)o0kw?k#Dx[נ̌sƙ?JNm(nVm0Ap6y-_; EQUZZRcbbnݺuKqw###\rbXإףc${-,,j߱ϯ.lL>-%gzKw~n{iaKH+ojKn4_aKl:],wZ.\YQ#%݌|;KhޤESөze]XMRTm1r@;KR Р6}.x.˺nҠNRT?>Bf$J/;cxI ;~tzmGl?sRi~Sw+;T*[cNP2OJ߿Ե.RiQҩ(u#VM6ŐREʵ٤^gq6 xbRr\YVʕ(Fɕ HQMl6.wnc=~h٫B̺Wq]T/܁^N=Fܸ|s~2̶G 4yjwfsnvoiKҊ|OYҳdǷs<h#1x޶N6IiVmYv=;)A7M+M(?{ةc{4嗏"7t;]5>Wzroq&w{j,ig,ݎ-kքL]cjI؝oFuزËi7m{~%O2Vp*ڮZ :?:ӝpqfTTK[*תNp j|~H 2L 3m(Ơ3sFSDޯ.'?Lz6&+EY7f%|ɦ}l*hH юyX/.h~v1\EZI|~b(:R>-8[XY/ ÷:b`'ittC`{ |=?k{[ 9bxl @qm. .ud{_sX# jc{h%?ڹWҵG kZ\?ܽ0 ʭyTUD_.2smm 4E,sss.Ө FA<.W$)U*R6sjgQ>%-2m-pSTTg#o5wǜF$~Ֆ˾; sF,kڐK[K-n.q2i۲+D7{.Nil}!D6|CeUVbvo \mb![ܹ0ZiJ4SHd0{yxǮ ו8/iTUY-|(V3oFUPXhiklakkeffFjZ&iVQRRRrm :2Qϴd?;gڒT̼.+  O`&"Ghd&~К5>[cO3X%v}fU%VNf\m/i.s=l~;1Y` .T}0b$ ٫Pk]90L^Z $:RG| v 11\r.|!M8 8lvJi[)WoXG! FzO8yDZ[9A蒾XVT}c$.fU0YIٿP(Lu…|pl).w_٧+@ 쨛3ݽˎL305L"Cfgg EYYbȌ$b1Rf\oBa.@v #x>.#/7oXu(U϶l쀥GѤ\!%ɐQ[B3݆~"yqnc|v^~xԑk ]m*Pm`?%MK ߼X`Py֊%a݌J)*/dKvwǺiMYU?ޞӀ˄";_t[AN7YZ\ǝ?q?@X60MuJԦuS}qs/]|`dI;H6VZN}~~;N|M.a3c 4eҒDD"GGG]XXXV)T AF$ŕ%?㨎 S{'/1X l?kO S8/i#aŀ5AJ1X씴̌ R.Nl}&_{/ZAP._VlI:Z`C=,͚4q6ˌF=Tr $a"teB$\.Yj®_ GǎrQb*]jǀ7Qt}J._Sb$!+,J# u:JTza\. >>>..TD$=L:iz^קٷ)}[}S0/''+#34446*$I$q| >B ÌF#3 (*>_(+PTVQ([^jehА+Y]RZz:F>֛*89eùhR'RtttjEޞ(KKKEZBY^/**"BTEDDI!uj\ T  -6>wWm~Fyu OnL4kS'ߴq'vޅ^^%$4nX0"K쌬drtzJAr]AOBC C7_&R5i<'" ll Z]Lb=ecfanP$+NLJVk5IPzbh/';'$S:2^Yo$Ø3ɩVB6.;}sIx6u/%Ҁ %m鞷lt vL\%tpTr|sD1K^@4$WklS e٥.+TN]8rxNv% g:Fe]7KL8Z4oĂ @+7[v >V6ۺ1l()̑ڰ1.E {ccdғgNڞ9wDjBQ7h:_`&ap4z8:8eee-XΙrVs,(pD dtr-M9]̞W}sk)_pݫE }kG vws#I4jm|{^J]s~ǖ*VHܲ螂+S(;)_[Oet'vXxFՂ ,؊Ux'.X.Wj^ώK^,׿u1wbO>k4yua-'orl̘39LSV-0ߺkӬ*CkUlYľ=RV7DB[j)c6Ka"x8y>))q(Oa<}Nbꄄн[ *~@5r3mQmX|p2aT貱o%7 lm)̝\rZz:*X3?PZۆ.qvOmFUd`acgKۮrmAnnjF ss 99htߵ{CskCqFȹ_2ά&+/C1-'f >(Xtf~$pMM5ע}s.#78_1y۔`4u`aAy`J^J>&@E8<ؓ+Ғ|Vcڋ3}#saO aF9F\泥8(hR/}s/Ќ[Y5IZcPy)+t'\ $f>Nw{r@ dYpsj/ٺ2)UW2Ff2}Zr^HB[}36#G HƭqbIYi,*~b9qT+wtvSf^kE@ -N?u^s؊J/_XZ] {wt|G#):B  3k{䈱3q[br^|X@3 C#=뱶[_ kGΆ?nDs=,"sk7uQ&Fӎ#}oo/7e3I `0  y`o1%2`11Oc07UDmyy❃b4DP.@@ $hР Cò3GuQTAllm: &\Q $PJm}-`[YX$^.Sgc @ygLҧڌX+nG IZХ$g|7oY!碨Ga[,/H8ΈG/60jȷ.66m:p$b+"p*w_c(<7o֬M!st6CXo}a-DbzÄۺܜ/H:w,uӬz;R=LztYs̳Z U߼&ܜc(S#Îlw_G DPxo} ä?+5 MKEsc> ;ϳC=r3Abzz[4mԖwBAƍ7l0cǷ}u dm$pkӾsڶ58rټG::w4y*QK&nK`DŽ;Wՠ(ćHɿQmآ_wcX.^Vv\Pt;Uw ~{z75:#xϟ1qZZ\}{7R`hu4VӓϿ{0yhh+)=@i*$_}2 d$pmY _D]΍eLAfA [/!%ƝG+9*maU2)yFܳ9e *tW$C|>{ǠzՐL'f豫;:ɊB̋'OD߳TIa>y^;B,,Rv\x:߈O9@xK0էpb5) ~ u}_WsR};(ԨU6v ~9. 6B:3^;Xy6tݲS0'.0f6c6ٙY\JxvuU_7q!ׯSO34;Z$*Xդq⋭Xw2 HRXm24.#י4νcSK H.0Ne4pt ˀ.sLrI:2RUЌO`\~qWh/:~ro'g&)oݵiai!5FXɯ4uʘMC6_]5k ETDR&޽ &"s×vC?ԵO eDK֞䊋0}(p2)݊pVS$`6YE-j>4d=B"xf$Jf}DRbX6^:fU#gDwMk [_XXIENDB`sqlkit-0.9.5/doc/html/_static/images/sqledit2.png0000644000175000017500000006341711533717256021260 0ustar sandrosandroPNG  IHDRX}-9sRGBbKGD pHYs  tIME$2 IDATxwŕ?3yW]("٘dM0ws:gclLY%v*nІ36HH }`3Uje1B>|h󕎣lCO9K9a1pn~ Aa߻AAD`  AA%  AA%  KAA%  KAA q<ŷv/߬L΋|8׺nkoOY]_FGО O|"a W?Re7cO>9ˈ s? =i3z |TL*nփ/夯ȕ#{*B{<Ëu}?m+5~\q :>}o΢i}O|&jϸ]އvuXpv 6WӭV9 -`h98 +=]DhvD1SΞ˔]XPSL1Q{&agrgP"LG(mD>>Y 6Pb(fLaG=/ MVz[Tfg.;Qqqmȯ#@43(}uT۹GYց[:/;$6W宿.emiOJ}Hlck'138h'=Rnpy ?on05S>Rw5O۱8@ ^O)w!n)A9>Il'|Ħ89"Zo(xK!G|dN+!4cɯwdO'0u,XAg1g/QZn|3?Eu/ץmAs8z$(w$7~xW?[ywӘ5/k;:/O.|DZ~ZSg2^X_:ek5K`ZE8{s/:{6~u6ݾǘ "}`>X!9u1y!Ù~ |*'`] ᇟ.fk]P`sm͟WS0Pv|'b{&pNcRKk_=F_1vғR<ǽ 7qeÈtJ^…Auw}1%8o}xF;Yt4S&^~wEe1S5yKƍ;C/՝>XaO"Ij>Һ%K?cSWUІ\~8}Do% lU0\8l; l"N&eHe)5CGrQ㩉XoE8ړ9!:Ljeݻ`(cy4ۗMsR r[{G^y:sj2b"_s<7s6nr}+OQԎeOK}ۖ9sf3QSsr»gyvidg-_,ouuѬ8{J9"Z0`jSjhFɱgͤO)u ;sOsy#N2=3ƗVP 4^~'gP(&b&+nDcL` <;*h:=/] tkȟ4uS$0vL;o'뉯}Ȑi0-~۴4#ÐB?1ͪ=@bv~ja[yJVfxiVU HJppƗX)F lj8>nucpz޾MGv&BmYkXi~[+9oXyU*9EA$**8TƘ}EQT~vQEAQE)8lMݾěaQ|4N*#gqxO+ 9t ,a_ݱxBԎuqEF0Ī_1]X'Cuw}O|WwǞɿ~tc0S;>:qՔB+")A5dǵ)az% gp~O5r{_RiƐ-s]!+ۆ&}W?[d_>l^^F{w_|Gmg@ & ;HPΫ8hUۺ&OK}Yhޒ Q IB؜fJ"6CE 2\u:>`ACj8ulaѪx Sw m@|!L.=ARc98gB)`P=մ56޵Rj8Cif]$RJ})q\JJIw6wBT2]MT3Wo}6z[Ӫw&o KؗU=lJsF-~~;~8]t [S86NJe͎NEGoesOH ?~O4Ͻϲ mog4e *'q,~mlh5{>!|]3<PD\:l~=aۗ[ʣ Wi Mx'ob!C) }މcLm>n0m+4~ O>v,f4'ƈaSSfrxE /ݖ;h6ɷ_3AسHPعy|B \ρF+?΍RZ5O'%2oKM:޵S0%pe3"ETm[k 1W]uaPa}*9=[V=GLaԎ?}> ,A9qe'ܾKga =PLߛ.MOO>H >U AXs͖OSW7 p1V(kIJ0B#_טԇB#RBb'}s߫"AD` Nb!־!PBH1V g2I11yD5s(yr/_.(i[>WAA " ,,.I!RoJ1"|a4^z  Kv1ŸNcX<:$d<Ѣ &[,OШh_  K;wC!xE ( -[63u477ʢEH$lꩪDiY)UlLMM-k֭d rʩ,^u zAXž R~QR4,B]!2~jJXh -[2 x.G/~Γqnd.*sH%C{7FD8=qo' g1_ClĀV7F]7q>ӻhutwi=[s>ܫF<eq v̦^]'J[{+;vtPVZ֚rvtiFF/A%$~T8L(8$u+ G<#eJzFKO ([x'HA69OT[vMoVNf|(CvMy`FM5͌R͠2_U>E3'Y*]q<2*FÄCŅ%I$ wVbt"0eV?z\FMu-mS-XK>}E8MpBb8%% K{,q6"&Sxјx{ʌ=q#+רӴ/Xj'lg';AkGUMmoRZZx)(ZrXި5kVǨe}sÆ eڶƲBXX8CkvZ[[4h |AXžXn,p1hnz,vюˎPA_H׏~v4n<>`kO)Vo 9nS Xݢ*J Xj 4j{H'SCd}7v Xj1lGc;g;c$\Ӧj*540PQVIkk+mF ôwS\\ʕG0s.2AD` {N`K&LȢp :_]O=wЮFg(=00?3\Ql*u֊]DQa!%E ĒB"YV?%Vyϟ>SOSPZRBEWW۶my={َjv\2x0s~N>&N=fBMM-55 6l7ou5]t =}kjeA% RK[* +14~F/*x@hō%t!3uʼ#ca[aͻ˱mB#*zyD\Y:L}{u= ٴi+V|8h p8qߌz'?Eyƙ': frk?Z{G<u]\ehPn'† y?3̹"; ,awcklj{Z%;eh%c!:xc#va,1aCdr[Ox,! :HphiCcD !{yF XAYyx~ ]|3׳y&kvtu3/<Ϙ1cy,_.>|?grCAA;q48ݱjeG)gй=R"4ghPw\ر8C^\JȀaqS1U##)@aYpSVXf BM@3{}P+NumPUXofcK(끇/r\K/(b0TVUR]]MII %%% 3/<ϯ~B!fH#4@$w5pJ-5Jv>p]׾JQQeQA nN]<+]HExY_Hh/TNJ#ޣgU=hb_yw! /Qr6-ݔbÆf9PlټƦFzzzIPZZWUӗb?=']%d)RP!pgȠ ӟwߩ%"l9hR /Q2/SNxh0Εkhyxm%걱C3=-1< yߩ@K`XXitee%Tb7=q:ټi3m;9S^6ZZSmF#}g'yy5ױfjTqpO{P8woTTTXЏccRJٴsoI~kr' ۉ@xp]75AZӭ+(K7(rBq5= j#\t` ƲaKQ(hi0!^zyQ7 +'A8dYgWꢲr%2w3/2Gy$E|+Byy,A%}\#lﱱ7BIYnJdr4(tC`@ c0dVr>1PR²9Kʮ>*'_}BJ1(jb xŶ]qBe(,S SR6/& IDAT\@IQEmz@q9(}Q;.cǎes1OLyy$ ,ANśK /Rr;Kuu5Mƒ?1G%{78'O楗^bΜ9}2Y K2hnjaPl7,,*j`uAyyy_ W2sknNM,(čX]Up}(3Ѓ@8LuueeTUUqԩB! {.p3fr)PX嚫ԿAX@yy9c&6PO8n˟>i@{{L}[DD5w^!>]"  D"vaX+q5wB(.Q\0|$L}  [EN}} q)p's}qWPrܳ('A u((Gw&#&g*BsX}}!P(,J:;3}sr)'SXX("KD` '*tQiEƀ)HƜzo +8_L,㦛o '!XV8LaϦq!{^ )e.BcVF>Wa# BXŌ3XfM7===|s7C3Ce1A%{*": 11hSbf_\mܸ rW~;>}jfh4JQaQKXtָh8[l /s=믿}{a<^{$!<Ka/yǴb vmp-pb0Q^^NQQ9f̘Μgi򗿤3g+#AؓWſ}뛜qi5JկpW x9XY \b;p] =y:gu] DTnU,cK2y2G}cƌA9K9rd8 mk޸gG"\TKIaT%8m c]E7Z qIĺSc8̚qŸv#yX"#"E` <&v4Ƞ1DƠ{Ο ӳ\ޫ ߨܟ^VBj`L~ۺ??WJOɪ__U3y۟6&$c6DE(U1 $bV~@+/`mڀҮ FAczPOƝ8m&K8ѴʪD"FPBIiBUrq<ɫȫTcW ]JgQ]R0tyddŤ0DTVi!ahPq@‡F˰=zEp HfsKa Rd6)6ZMzt,!f(v}ݼBLpݔ H17Bj2!x"k⪗#0΍cڦd<%ףNA(Y6%ף3.6%M O7dyR*& ~Ψ@ФœM%I$=p*Wea_*(҂/Lh6LzɌS ֆ&9N o W-K81d$$`tP "JitH-K@%tP@ҫLCӻJz~cQp*C? hB/WcG2~3(H2 r![2 N(,9"K:&J)+,#318T1T zZE] ++U{T\)U)r\2)"ͤmr ." ZF?}@ظ:qm1v+OnkV6:9@_uϜv\o脷^eϓX7}'؊Z?o۫K^^Vmĵ֧vmz6rgvd*:A` ,/7(/ercڬGߙFsXrp}vюv;y'OgʽHtO秂*?q'q?~vbvhݴ'`(3pUDh8.:#7+#KtZc  GѮ1D2(e(<}@۠_%kga/?hAX! :˃SO` ?cIݭ rR! Cǚ&zZRƄkmL(zw W!c(RŸvlck;Vܐm=8:g[[[y֏u(䃂 "C`e{V]99XƟk3_2Y`YAwn{?㨩ԗH|/˨UN(ީ2nL+#|X?m]?c~lJPaCzX:5r9/gzBgD[N)gYN} CN\!vsXy:]ىѦ2䕏b8PEƸ@cW  e0Z{/W]T0}y+_Ƣ\mU[tt:8;uBZ=*jb-nwy[.(Y߿],^6r!%Ts0`64ngV|o[Uo?79i&_Ơ$D(_!!BW=%䄵k^1L|`-(yxNyVY5m'sO)(c|79Ѵ<~y܀~pj[W1%tdΫS@s⋥kyɥ<F7 ڪX U12ͨ ~2"ᝇgpmlf"} y6Ɵ#ۦ'_;ђ= ~tkp^8Ǟve?GPa̘I3.F=0e=Jb,A$VS7s%1 " y>|&0r߭#1LJj&x) p'NÚ4C{+XPQ?ʡ3)Q7]Fk\$  Kؼa]^Ye0"6J8BaTQ3FMROσx܌RG5U^!u!n`+ezOۯۗ~f*39vm譯 в~5uڎJwSUzJ?v(ʺU53EL6OX6f'iL&I)R*u&e0osɷ*c`96zc2ƽ)6C8|gX>I}r3d&OeO;;oBCGIB%Zwe+Ё1LJyGwfr8(k睜ݗ%L@ʞVhN"ڸ),{.b߫NUJa}NL:-*IJiSe6`gnڪ,yr <ܶn{PȦI%>utu/hI[נSդE1i_0M1Y|c IぱTYǁg;(sXQIڣ~i$n AFB|, ,N* \f %`l|܎2Y$Rܨɼ<dFe+&wM x夼=<[yf28d{pϘr5ɜ$?>p+p5*PvRuSX zm uv "{BZ,/:K4Uz[۵],e0Je8kɔ,`oUᕓe SdXUVۃ:(6_ۛr%ۘg u}(-$I)Rq`V} MƂh& Ftksǃc K8.ԁ;eyVG+6X*˝>X/U8-._3SdzRaT/kB&):B1j hd z"cħ2KW&-U8⫤J erAI![kWaHlW@`_Owd@_)?|er#**iRL}cgHe ,Sc8( 6Q*jʺQvm^EnۃL!m3:۞6fL>O^e`O=粆@`%k{J@ځr4 r\8HU\Pa 6J{Y'ٽA3^Yᑌmy}iQ<;bMmJW։qWhI06&pw,#jqCOU.gƋhf-8=c~~02ePQM~`Ul9򄵒Sd|a21`>/3kݺo#w-e9po|Q&8hBՌfDeQOl\\mLJc!8c/߯¶,_!$%DZqTK t1xp æEqWf*bش *pXͼ߽ʂ5;9,ΙZ @/>So6jC 8d]:Uv0hc08uO?Ϟh~zz X4me;=2}}gA7M\? ;8Y(pw0k6=,/B7a᠊>t ##w{le7pUCzVqGalm#d XTWUPP[Ø Lw~YOe*gωsOQ~Žo@xE|l/ <|-8)(_y.xQ.{M>N'<3.;?UollZ :bLD` Br,/Lr2FW l⦊g䢚Bt{b> |P6M _SYEl_=SjV>ˏglKN<VhN=rw5N`tm86l~=&^v1SNtx|Xλ )4e1Wym;<h: 5Oplm4_{)W~}KV^+7O]2MqJMqp+~Uq IDATwͻ2RxCŐfc5{ӹrMܹ$uX q3ف'^w/g %j{dM`~eӶ1+]rgޜqQ ccUoޫ--VTec#C/ xKkɃF.kQw,f8㔑+W)zGq:*h8DNolg*^ ͬTW19_Vsd/+fb*Q=l"l'f0{tǏ.rZV`ŌeT֎䄳gȶ%,lZƖl}Nl+6Fq0khKh]Q ;$Z7(uL6rޱO {k#=E;,ĩ xVak(7yt`r+p_WWucڼ{ǀN]u]TaMnxmlI 6`Ȭ g<{2Zzl\S&qef-RPXDA{4$'` 9RxyZ /.aX:tCC.(Fh B7ms:4y^Uk7sn2]BdQS 1#ߗIGyB*r+%oЪ*9&Oh0#cl҃3quaZ!Y⤉*6tYAZKrk͆4é.P{>VcRu16EQͩ&z7dcQQ_J8e-cSgqB;}7?^&8f_?[[ڛ=Nrq<`jkzbݝuFK x1ns6k y-=.(%~1˗{ h1n8;W Ć [@]`ɲM8[ ;1H^q%׭cF)/ĕ˸Bk٧@Y5vQJsvmRd_$h~q~we22Zwoct?"49&_Ih?=p< Lr<]O[eD93ϵ{ױr"!B׫+5e1Znoa[Q(KmĴ KchmJ?siCrf;5R'.}y)cRW>~q5ly VGY߅ˇPiY9l;QAeZ^JEJ[gȭdbM(]2w1_3}CBP"HKEUSTѫmihVZziQ[Z;U^$Hl!!r~$bi$1GfΜ3s\͚ӻ#_>D{6nԭJl~~qjT|uہ2rn oi@jr叼gnl,xBf-g dipG<՝86̜ͫuCg}ntdeuٛRt؝v2ʍsYc8ma鮓x[XsƏjͫaŢUr-Y; NmZH_[֓W *:ٳbۏ|Q.t7c8X|JQ"sP6DEH9|KAfݬrHIIXN;(e}}ndԨlI.7<;C[X{ny)=sDbEL*F#Gs7O^M^T,FҮ$eqfgϺ$洭X(cb N\rڕ<9 gEcoKz.00+к)-^96eu; '&L\Q-ho쵅?,fS`T*D=}}AIg : oXqBOs") G}g#2AT}1]sp EByS-]׍ŏJfv+,8 p-DqNql)-YsZ̤Ɯ.X|KRE33gT2B{(U|MuAC?2?u0Nldܼ (D5 d8Ln9w{u!Q-Yg]ujP!vwVy+fL῿V"0 ?|y{?HZ~~EIMxpJ.ǖ+I~ 6D`/V' J52ҪK mww\9_gEJ} W9}2/kr h{,)K>^N\Of"MCN_B5$q#pN'Cm@ۋ(`IaԦOirq-Ez\,~N `p" v9[.n%0"%T@,YWSGm/*yތŅrqi<|Yvm]K:MQ)PJ",q˂Hᨏ0 @ UX5;v_W eDT,##]E(U y}Vq~b2SZ.EKpUP,):!44T(QZ_>5wHWfʔxХrDDDtɹ HI>*`-QOJNPۻ *`ȽC"qY:f(+NNԛ'X# EDK.9U{UOnFN[8; $m&"t<ɑ9jՊvs=iN42~odjRў!u;0Պ5b(;ovՊNJ̺*8u>-i`CciE½'urI[l؍iW("K=Xǜ.V|=·oyMa*0+ !-n7{ /[ȠgX1PޜuX4z1kv?Bvbg/ѸD+㊡/Of>N~?]-FD$ K/B-ݞW_[ǚͨI)RB }V-Nv000.l -ݎaǑ}sxxuRՊc1o_*NGDrJ "胴Ur,Q0˔Z1@"kԃ%y,#s"զqV/Bt{>Ҍl؁GlVodz7k4R~bÍyu} -i/ݻMգ~^:2z ihj fasdpzNS'N5}$sl oC7î$#" X9Ûp|"!{?d=zhBUr+߱uvGV]s^sjnLׇ곰7{f͆ |V0Q/3P&8Ss .Y oԗ,ɉ#;"Go 4-_9cۤ4 [l>iXU\!BctEջkX3r5-ߘn1g` +^lG>AM2E1M/ }bҝ_M%7D{rUNrRzT % |/% ؔ}ڴױz+~GཱXw(Շ#Z>wdޥ-V+IJ?XϜyP1ď #2NK8nZtل-Щ|Fn&iШ-K*χ9)?O_el3FEա/" X_UZ)O}ȼ֦\Qi+{I bsMϯ~s vuu?O':W̠u|?eMSgܷUJK!T13z Qbɵ}J yɞPz<ՐbM&Ց;al E7~.ͽ[;J v!q2)WgfϞ X0q;U~c>g4U,Vv~a3 Z_.ūPǏNc =v1}z\:X©9r<RҲ =8ŶOftz4Oicxb,>AtT/fmæ3q/ğJ ~G380X# 9s_r%t='}ٽ^Vs}:QN%kH2q)xm?=/qǴ.]Yu!_?#&&PB\ժiޮadm802p8v7ݎ/[/˛-CA%Ͱv!LdEfCz4,F/""; JaA8z^|\6"[g,-A]EDDDk{1{?7W}>ڼ]yvVUyED$iDDDDDKDDDDKDDDDKDDD}qFDDDDDDDDDDDDDKDD䮘t(`ɽ,m 6 r{8N1[}:͈ǮʈHn|"$D4NIfuON(Bs[:3V+VkCھ8g*J XrȢ7Nl6M A4[<gr|M_Yc|^}Kbm*(`I>缸i/ՊiF.;AfNMC8u>-i`CceghЊj%y8ѾQzd؁GlVCٙ}~MVpGTbQNA2_q$m25+P.O}+**]Y8]ae|r#M-/|1]63ىFfU l{;b0x⿰|i0ſDkT6zȯx,ć5h8n!ubaQ6kpaCMxs{c! Ƭ1=\13#;x]\irHf瞳k:\,^ĵ|كv U2j}9w<ق|ҝ&eBh'3?2ey&FOPJޚN n{u$勍.|g T_۾O/u>MԥyOYį)Sl|:L,}J y)3i/}WcPuoe!Zܳe$ .{3gMH!-aǍ<BPf^geפVǥ~{|#~©7>{P2=wⲈ(`I~ff .̈́|-,N SuAv¦;7f7ɇTK3\r&^zt%*U\!R)eĜLL/=0rfd^ QbRo3hl iӃi9fW}ϕ%JPԕ3?e~Z.," XYJ5Gd_L##ep4Zfmæ3q/ğJ ~G31?-X9i~D lY7[|,i9} )i m,1E9y0oOH'x2S+qr Z{a7h|r"yKr8Nψq-H=XCf0&K?U"4|k|G1qpw ū_u1Kj9|[1u9F2wGRqt|s~0Az ?ǗJ4}a{Yد0 Ce)\bbb U! q}VNy^aYN0 l v;NGiKq*lAOP DDDDDDD & /(`iNyX%"""%""KDDDDDDDDDDDDDDDDDKDn9OXVIQ=DDKOIٜ*\AҲ?Q2*(`I<|RyQӲqc_MC+Vo0sE8uGI24kvS$?1k>Ӓ:t<o-S 61u!" XR Ug, &9â,~Û%3,K^Xu߾sצ^۳3Y݋w)8Lz__,g8z/@e iN!)F1e;G MX˄ФO$v1o˹b-Z;?E=]yfy.<7,S L 33 \WDDF =%㔹<ϗy=6\ #aq'%i7[6!; M1LNXV{LԗQf>w+T+fкvGI fψՀeb}{˔B!OO o4Z-e3;!" XTc+Ǯ^ۈQX#-+c2%?ysҮ dc2p*##*-"ISRMyN*gՁNOF/4&ٿ}iL\ ߷?`ѻ;)pg]J+S2Xt%*âH.Rq qЧb ]~>:720at/f^ޖ~wy;)""La*/َ|Jnkp(䅘BCCUB\ժGg:0 a802p8v7ݎ/[/˛Ƭh}kIDAT-z$\ڷCgv0gtN W""%rWivh=g< jЃowZ(` ~u^U""R`["""(`(`(`ȭ׭>fcW5DDDK$< n8d$~-mՊ#t\D6 Xux,?ډHӥr|(J~CJlX9*O ~./_v/zwoҔWȺEd{*G%F3x{~8ϰ*pWE^ fǣis3dش=άbyՄe{![u߭s]'.`,/ fY"Va`Zb&Kz•+qR&J)2U0Qλ Z*h|G/ϳCB-yGE.Do'ĵ|х cX0A!ٟJ KW:êeGF*݊Q#a^y6)+>jm*X&&}"y[ΩpD3evwx:&Ns!3#\*"%J=ӋK{e6Y'd&%`zVrIb)KY3qUBh߫m ;8.LiZل㦕iA,-_ }TD3a6_|L&0p1_ 0;S,=ҏI 6烚pb3| ZP'n W釘s7ۓS"K$\)$o)?,*"]VAck3ٕG`_̀%1glv.{Qìܑ3g &Lz>vq&+kןnCk)" XRX"x#ưhw<'F1I)Of4KҨcUG]hA|ѷĕ*}X0թVf< ^{V:ybr.z!R}Gܡ|x5U?U"|1GGN&ݑT\)]+!>+7H#8ُ!3[| Fb%ݟm링ʸò-iLxi&L y+00Fx&=Бw'o`H3 'H!Chh Q6pZp:h }s:02p8v7ݎ/[/˛-o@%]"DGJ%TRNpH.S_!BQe"iPDDD$KDD/(`2 4D("""y'AIENDB`sqlkit-0.9.5/doc/html/_static/images/sqledit_setup.png0000644000175000017500000012437211533717256022414 0ustar sandrosandroPNG  IHDRX}-9sRGBbKGD pHYs  tIME/ IDATxwx?3}7Z$JfRDQZPEQDĎD, ({&)?6Y@Ww>ϳav9yww$0 ̹3;?zIAa|pRTQDpّ\n6[őeSIJU"a@pYe"q%)y̴#Ծ}$Z^-*Kpy:DЊΡcXWMV ];"%| {v -݃W딄Cfne:Ȧ?g@Vd] $_>\6T1q ?HNicmB` .#WD][1I?Xi$a] ЃXv=_IP$vzICT ĊaKKLG^ھ89CQ֗]IΞ)+v&RY%uy~ C "/ MP}`d!)KjYC `hwrH8@W +f[F^Ml#)f; wzރK[~nnQ'|@Qd P+`(APKfN+pYCH̊w0!.lಒw碪1݀٭ 프a؊d2f j-NTwGK{aD|N*eT- |w@,TтQC¨kt !I%+& V"+?JGP޻™Y4j֚ioa#1. Wf#(<'};!#5/ Gxr2@S6 ߙ#2Ƿek*D$7B1?@qB]"j =7,=WoY p":Q!kPp|3$Ng'7JnQxtLHdXvg>sMӐ"6K#xwbb"I&|LTNx. Y*[${Jн8=VtM-2_~ΣٴnR} X6Kan5 ϐhuHhef>Հݢ׵*Ǘ nT@TJfSn:NnVf"aPN}8+v;B1%V!i7}n;_.n8;KJ26.q{ҋpo S`Qy4fZFfbvDb1[8mJT_v'yG7@ iڃqi+xuan.OuzuGO+uE @Se>7"sΈ1磱pp.flkKR Z0t=0r?’~msnᙁ2ynНj:2Xk۟3ofMho1fi{5jɌUI:$bu-faFDOGi*ӾY&*&rm1ۣe?1o'| ZG59vƌ_0[Vd#~ZbL$w#&/`]au<6$I"`uoaG AR9lg 8j*9Vlٮ {5r)g uG-X?،EVT? ejb<(jSe~A_haUJ.⇭Y2~{d~G V92 |9rsIj rl}'&esvUJ:~8v:bX rpLl>TF(vgl+󷧩t]{Rr;6pǃ#i5t:=oc2s}78_' B^ \ -@D0B]V u5G/f'Y4^y qv"hS+G6]1cI$C%1~~ +Oޯ78rxmI̕[bvGo9&{ 9vsYjγXlw4phʚY$νؾn%{N|| >a]LLd yQ9g!!|Eۡ,f:M%g=3ҹ6E҂6#)npM P=:dl.v(kOØB@⭱t߷w$#n[u$ZAW7`LF9h+'|(IVLUsI>3$3Z5,I P*&s[ԂA|^;It~t]BLf vye+>|7jqDK W2 |>>1I)-(!2,#c>I< Ν ^#"Sx<:D[*f*NMNjjb`N  i IMox 6b_DssɂjI) D ncf -RlMӈq(Ԏ YvI hbn= ϗD%tQԫ^EoвU 1|ؒ1;괬,]!4Y)s#KH#l:^ĉnN Y#X7?1i ԭZ8/ryV81~0 l&-hXL{$\|MPө_N&.SXTt!+eg#oP{C")Sq'yL_@YTYF:.YHJhwP`_rśP3Xʁg%5A O@&FS+ o/8UnZ^ӑԐ'z= }:^Pe= 2Th~kY7 Ŧ\!1\1 *~L2a[ "%2@>eeYŒedYWDeͨa,$FTHs鏦بXgNlQ< (2Rqc1⟨$wd)8iҩd%kW\K7 TVrPnLl<8?]V,dm@.9q"<_Idlf Y t[J/Y՛")f&2&#?O!p|-_ۑV>?,c xfW`K-.;'$ҲTQ"l3YYghԤ%!-o!md&٫ϣ;M]0o6S=N!Oҵ j(a!v~(TMG3ݿJTjgV4᪗wܮ/P2fj6Lt\dB>8I &rv7uiUhج5߰plGښh;G"d_%fPGwHX@/8g_ fMD;ؽc+&L}_q$d d@ ,&}$$ &aT|`\xg.L@DU.KȥVX60̘-ЀvY +YC/Y^`,ISPNf3|6s=kYTl5z?f9|'e03{Ar3P5 I29,By%d!-\O(&>xEΝ:fχciz?_7; &CrϗDfw8qNơ'8#QPGtD^32 wgBugs[>l j%9΃EVLL~}4YgN9‰z_QD CddB|Y8>xcD&3[ӕ_,b7/bSzws͍c%?чiQӆ'0oxkL:ɦ֭'/_̫_jD$쓬ؕ>6tc;7O>gmL?1hHP~N$(&i[x/:m96no-))8{ܼf1â b _(0Z-e}ea(l٢8R>YP#\.f̜EdLL[[,ff̜UNttErrk\\ץ%Ivlb| -dEud2Y,v;&HDL,VLDFGǟw,JS K |SWN8I0sz6ۻGTT*V!***ܨls b 27SQ3=vkq 7RNI|R%0zxh#x<2yۼe+&(T]wCJF<5Ya'QASnVNL`kf.?q]יTjCM)XVnNzf$4 &&UU4R6n=)** ߆$&&rdYbժ5x-)єOQ7 7jN9zyTl3"^#}yv&5q 7QN?ŤдiS2N&..0/gFRAS 6#2vFrmc6ٽguItrrslL|: i˫9kV+nv~?,N'w- G^]֮['4hʋ/)jE$^xe8Q&2wm~- ̂?2mlٺN٣DcsGRIZU۶`OKU>Cyʉ'p]4h DŽRi~\ڴuڽIX~ڴAӖ|T ĉxt )1qHĔOQ4 3of3S#S17tlEIאq'/ط?ժ` aIjݷb3rhR7]IO(oX_$IXVꤦ0?q[ҴY1ǐ-v""\P:tʲ<3yƏ˖V5 GOﻙDFD0ٵm+Te(2K񎈈ݷ`ǖAeqgÚphZ̳gY~=?-Wӧ*]596ov9JVV6qpp^/Ͻ"t#yjHPwengϒXVϝ͆5+o1,qQQۿY3Ḝ2ѧO?=ԨQ]7Pd ,ǑGOo>SnBʕB$rrs8W IDAT`%8eS>)#C㸜N:/q1^;1/="^|Oc疍$''cZydؓ ~A==❉坷`GSx<w>ڵmO>믽-1,["* YVdRuZlAV9~^iӿ0 oUZpQǎ1xodd"#x_Cسc ״k˂?rh._ӎsp5q`N&Ǟx^ˎ-2>n\k̝CtT&'N/zܽSd Ŝofij- Y1LDEEzRQ/O9xo1C%26dž Gn۵6믣^;vO=iׯGNY \BTT$mbK/Ȱ'p7@,/=ya˼ݷw7ueP2G:t@!p4nD QÆA֦UsxHƒ\Orr^bU%&ի#I*Vb eG*ɘf\.qq jL%|e>S C(rQ2hP⸽]=vX\HAoH<n4oEQp:H$Idegq%֬[xEٺu;#"ك@iԭB!?KFuBʕ+{>BԪӀM"uWԵ lIiFx$$$#0ӏR7n!LlݺG9Eٺm;_zzvtrAZ4kJs,-/YSN*L!5$6Q'5ƍС $$$нg/ @;줦z]@"TUL4J״"|>?hբ~ӧKp9DFFb6X,QTXD%Z5kx1 `6ZXtUU)Yɓ<1|qq\{Mݷ0qfqnZk:B@L$.rk°owz|>n޽1]Xaa\**VdŪl6oHZ5ɓil66n|ZMש\"AQ3]1}*YgҙgZN|۬VN9adee]{sPUOۍ|l6o\鴣MӰX,O$~q>Yg9{$ӧ}.KÁiOֹSd=[/#,j׮Ūk,_ ٌAL/AK~J;Cl6lDJJ-0Y?n,23X1 ʱq 4 5kTgݪdet1LXKv-G=zn5r8tmۉ U% ǎh VJ\$Wm|'4k{Dwޟȹt=)Bc?$4Ml6iUf-[wA~na\Vaq [@W#a"+;3,]WH:ZʱcQdI J{]XHHgӖ-fG*h&rt?.//T ɧÏ :MILHj*~N'FJr2UTw1 HJ*koY^zyi#nsM7q]=# ұC{lìi~/"Lt#oNAyPgy'7~<1l6+5kTfi*UVUmYd!a\jլAٳ VZ'NEu} E ߟQϿb6ѬIc,d <&&/<̳DEGqo^tء 8rrsI(cnjǞV.t]Efr{7IOOG72>XjU|?゙AJ~5Ao |'|B  Iٯ%.?u^ >_^tk;ѭgoڵmsώ`yԨ^_UV+ڴZ- {Qbbb{=!]TPSx%>/ے,ΰGW_q1Mt؁! 3Quy;[nnwN~}u:js +7\_#ƍ2Ĵ eW|5kYg:K2M!(JxƔinAEQBoӚ,˨ӥy"jJ&),XJu)( gs)\24 UUQdxF,A%cWJ\+IR@(ηeZbQ²ɲT0uJiHiV|iݷ$%R\&TC8%0L}MKl /:(2*Oټa_({MfM Tp%>PJf1Y ٽJ]ӴR!%KaYjt5An|wKU2clJG*J4%~p_꜒ƸLtaCksKu%IB/З42etyJ#tőbt: vM.Y$ [:풆>(5-0 KjYf-E*o/~ZJ}aJ](//..o]Wm[w:@~>oΎ3@,@ QU͛vM.!DI7Ӆёs$IȒ'Gt]'v-/E":,W'Z$1?ޚD$oMnu#!8x'M[/klmeRrb`ٰl=/uQV\$1o*0ltʗO>}~&@ 8"P%&xjԮM pHVX%JfDn.5Ӱdv_0 ٧zx%`EגAV.o۶ ~0pED1"r(**b;/䞪 &6GD O9YpW_n%ڸXV &)s$#G[^& EQؿ}{^Ed21fvD@ @p ݷ/,~m߁\ǴϦP1E8v8,3s7\Ӯ-M~%K~&##<ޙ>c_m̌fYZ$ޛիUiҼY34oďgSWÞ|[ͤKcsUIp|?խ˥V7) Wl6qaNpۭ7{N{`XpڶСt:ޟ6sKVV6YYLxMF=;AgWʌP]{-ӧ~‹/)jS;obe=v|L%|M7zߜ@wr3t8~=Փ3DDs=1٧8ل,lذ1Ɠ@ \.B%iӹe bcc _n]_GfMo%Sn*ʗrJ4iܨL\OK37_c萇ODhJU頖N'}?H0dYTokbٳ><4w[5R 5o֔Z RN?À+V"+2WEQd 2pwsgcVLk-#G@ d䈧(rh׶ -7C4խːp8 Lf1B6oJ5qbtZ}OUyz `),,]@DˏD]J4j؀O>N͘ 6%3DRhDXL\,|4lFZ5iX>^L*|tq***J9y4fLwfCdl-dlפnehU+S~=ꤦ/۶\-̤xZ Mh]hU_˫OSNꤦzxva5\uU+UJZ*6 "hҸQҢEsTUe/ػw$" omRzuJBBWUUZh~GBB<ȲLp+WLtTM4زm;7uB/@5kAج6,\D뫮>P~ EGRRjGdVIpu,y);v]=HHHvZTV9sb2gn֯W}rc?_]z c.bgeYVzݧKEKJh^bbI]~;o-,6VY˃/S<&K@}<3lܸqCcݬ_EQQ`Y˸\N22NQF O¤Xa,YD6W$IX^08s&hXf-S?Ά+U&aoKmӻ:˯0sDFFRjU~>fJ=򉍉]6\l6q7ǼLp9]$&$0tC(LLb HMAt,\y?OJ"\E>F??`P?:@cH󌏪huپɦ@PC DwT)*"6TDDEED"ATAw(zIOy?lI>:?H8{=3]\9~?T3gρqt2111(BbLU*W{t$ɧc0C`LB!W^\ٲN"ECDFD`6х %5D6T fy6UJ{sB >bx,f3pind2QD<KyixYJIdwMH[u V^C B|rx^ʗ+w]9JBAZ*y^*'$1B 4^I$)$6|2 ΚuΦQ^7 Ş={1,[ˡERzI IDAT(Bx%QLi O5Ȟ?q9ڴn׶|)k&~8mQtµ5 !gUķfܻyf .hD ,ɿ R|  `وO(ǂKILFH>+bNt*9i݊X#cYl92e~ܵr,[M~Q͚4V6 Ks[Ѱ1nG;ЪU TUorEt]g-tx?nsαmtϗODG3yL2U\MZZ}}#RR F#M)Y6\Z"W%ǯ񹜼7v4ի'Qr54'~D…1_xo?t]FjTZBq%x?p+TƿqPф'J'$PAcnׁ'K!@ ӥCt}؈~=sBM<#r>$E(ٱI(CH(U EQp\agN ul|>?`NҒ`0a*Yx>䶢8rp?و'kbxdggCNZPχ HZ%'ljƒINN-nǗ}Bx;?(DƐ8h4٬'x/X,X,Ӌ9x:r:( (RR$5**32P@lш.trrxޠ. f8.n7aj( O< W^/? X mV+( NFNvbLl,LfrqO.ӊf ߇(hFJj*QQQ(Bzz:1dgex0L#">X)$zJطGSp1}N8IFBߠO?L19sMre޻ P,.NiH%!OJVLn_BA2e㉨^vJ/^w1uVnS)$X-Ο% uD"H~ӗh:BȰwr`@4p,DGdP-H$R`I$/f39Vrv>NѦ:gˇ:}:@Qx)=Uh,f3>fl.H$XR09f),x H&Ahdʤ -RlaYV"#"q 5M#:&:)33.&33 ~}(VȈHҍքOE9DFF7 T)Pp?h&z^ ^p`XףZ,gL&c#{qr*H$)$2~?r)S'OEuF#׮#V]J3}O2)ǓO?lDEEQlEpp~_&dhԔ~އ(ڽ` e4ٸiՒa'+;m۷ӯsx<vŋiݲ9fPTYѐ~ؕN1m4J@՚uX~=F!dc'ӨZ6e*Tbu SRkdUdah94d(ABDf!oE\2~g#N:i ^:JNع|%D"_BfV ʻcFr~F#B:cOnpa\^w/dIKKđs!,( 6m?Pz7999yG@B`r4NF#W\%33ڵj( C5)Y c{&Lb޷3Yj9?^`pa֕^ ?Į[iԠ={2hd\K5]]ri6oX3)_#r2/;to}7)APD"K/l2TaGҮC'CUUN%'sU<4rSLiVXUU9{ʕ+NNhχq{<%3+ EU޽?xw,3NmVx^JG{bٰXdffbdn3S}ƩSHȐL"HnMobaݪ?q_ͤK8|Պ`$99xΜ=K)JpߑrJ|D :~Դ`>;%JPb"޻QOESgf`4pj3gR8u4F1\ xooeTPKdlSUQDP S 9b0hD"H%t=f,qqHIMn:(BR*t{3={Ν:r>?Xf VcQv-tȻbCyͷٳ6UOz<3w|zzo0d~W_য়qם|>x}0}/ ZRUVYAm ISHf-]B*rxx1J(d"%54ʕ- \n ,@bbbѣ)\9ʕdU 5%%Դ+hbSUɿ*G"-X@Q!&&, %K 222,Ȅ+[&|_eÿ.\… %66l6KN>C&X,hk`(_l8DL Xq411Mq]]56&ؘt]G4+^TTenSlpH$X/r֛ԩS릦+Qhތ<~ p:ҧD"H%x<<ɰ(Y^q:4Ng5D/Gi#+~ yG[>BZ҃oKblڲhn*K-+ԭ] MjIXEK( ii4kҘqE@Ç~ ZlNlL ^u6p9*/GuBuv=N4i܈%KѦu+ .\H"g55ks˗Q_S\9> \GUQnCq8"1LZRظy3-FȊqYnu+Eiw=~N8ID ٽg iFXVN'߭\MFf6$>8o?gϝ#:*@%e޻eu%KsJNiƨʌgR">CSt+Wb8NZ6oFB0 ?pmۿ'66{۶A89:㫙t@_D"H 䟊f1l23j;37(_ JV^y{~9+"##xޔ(Q%YX#4UL6>ڵjgO3v|glڲՒxcp.[t>Qc&9 3I*X,3MGZ5ٲu^?ĊYǏ$-=Kst2./fjaEQ ֭ot1jXbccQ5۶soj׬=f,H>:>H9zCEf|łQ:ojo2y{;;dffITZgޟPUN ''re2dffre ⨖TIԬQ#GưD(@=mԭSyx2<|~޷53s70 łnaD",?Ӆbef,]xԮU|45`i N*$U ry#}ԪY 0|8avnCS5|5f2֨^E;vN ~RRR)^,Ϧi(D4< (BDE96:|%˖gXtFE'{GOCi_~CzFF-[0 ,uiidffhФcǽfQ oG QL6ƽ[7d21zxkZB4D0}EUbziذAع鎝?A(u;qG1͸\.Ǝ^.s;{&gϝBxGt֕ ٧q:y;v;﬏bf0 ,;zxqEy䑇h֢ HjJ*4R}D"䟍@j*NzB DDD5awU l99n)oŌ'Ӄj8~ 99.lV.14 ߏiBҥK;XV?@Y+VpQƏ{5:7ldi ~j֬yYh1om9u4*Vęp #2.nk=Xaf-YJtTvpY,f<"tbx>v܍vѡ}; ~/cv?,>K9sGeMו{TTRp6+$ ]:3lH4Mߋ˰!t]bp!ҙ,~Ւpgg3lRh֢ 2yytf /5mњ#ǎѮ=2s74kن.bpB~HL[?Ւse10`kwgZjNxGjI[DHSr%|~?4{.t}?(StM&H=`ϒ_YӴK S&ܥ!3@[Fx<t]fjr-XWۿAETLۇ'}UUA.Q%tuTUt*&EUL&'L&#^]1xp}fݞqjPYC*ل54rrX,f\.w8ЉeL ]UnOH d!{躎h ,:'O&EQ( o?-K=|e -|} BP?}^E{=X7EA"dB|DZB:].TUv'UE l:59ЅΝϗF^ ?ޗ y]v5okN+_nny(wn!W4)87*yb[= dϗ|ں8oY}ڱ{^"HHD7|$<O8`މz¹dffɉY"HH$'?s^-ŕD"ˑ%KYʋsz1)?G_ s+my<.]_K}k}r.HHLl6*'tيN9px괰?(UJW^ oξ Ӯ{=/׮B$,-ψGTUgϮnSr.BUU*T?_"H%`1 XVV+a6lX,EjbXA{UUn( Պj =V͊`na4?g)a 9+(hft[nm6la.pqDFbXB`ұX,z筧!l3p=MӰh-O{X|mYeX,fTU '2i԰>҃;t*>(K(?!`[?$j;;ヌ=*&܇19s,jt5>Z91WLpC"0G_bj`6cfmP.{K$R`I$*ilF~&[P"'̓Ͽ@jhشo`0x2*VN:?Xb j+%ѹeKQv=Մ^bd]ԅ{<`W^%V]Ԩop`9Pe2q4nފ x={M[v;QS6mނA7lBu̘9 uR6jգCݪ5X,~Gvä `Z$B>c&L焟»w6TDVYd"5-fԬ{3gFANNxjӤykFy Tħ}{^0.X džEK美TZg{CLl,Y>O?ۗrGFUUlFuUj'F)\$^Lj5inFGP|e!F]1r4I5T._/x"R"H%i& YM1d[b?vnѣYd)Gٳlݶo)\S~پ}3;uDQ=ƫ0j[lٰڵkb2VLh<@O}N-l mAjI8N>%;m6ѡ]EvZڿ^ IDATDEE[osXl9.\$--^z?ǖkiۦe^Etf &3?_d\Oe.>M5 Z _'1߰n+Ȣy߲=<й#yƼ 9|0W`9$,1@ܶ/>FX, C|ƔOgٸi3w5iNbŊ<KPRel\KG‹/sy6[͈aCk\t))fӦLbl Mh233=<ڳsE,f3 J>׭b PmvKgw1r h&[,3sU$&V`m""#|Tbcbx?֮Z[q,[S%XI;﨏 /$UBDD` tҶMk*T(ϊ+a<`wEsz)-KPt233iެ)4m8Z.9svȃݻ!`;X+l}x^+wVqSty rN8Σ`FSPЃy?ԥKiެ)Nzu\J.uqX1ԩ|~tHɒ%=߶UKiؠ.3gQy RQpYPiۚHRjzi\J\4oՊ>|̜-cFgmyviFaǟ?H"&ԮEzz:NؘZ4oF(;ս|>qu' t:iڤ1ٴje P11، FT#e^pVO(W)l=ǎ?c/V Pz ^/(G8X,lؔ4}K D"Drkrx}Ы,^'LrT( ~uĠ/3lHN'QQ"""Xn=v 7b0hץ+`μ( ۶o'HQ%Ud22ugQv"##4aC_= : ?bi,;KNSXd6q[Y~ED2,YS8|PD<1"{UllڼB8F#O=݇'׷O#aml߼hz=J:vuVpiFJq:ul3iTyoG$V(vcU*WҥKx<^vڍj`0wٿw=/<𚥾ud2hu'Bn6V+6lnj͚sӖ- N:EjZjV~.\Ȩ1c:yW/ _ \7(YN7ʩrgyDAڝ%+BBD+'',PT@vpjժA…xTx}0nf|5[={`6)[ Ç eA;}Zj'V_kypO>KY:6m{p\deej}33_uddda0(_׫#axCn@b'::wƌdL3Z4oJzzzѩ {2<֫'ͽxWOoGƎ#w!!g/иQ|>O~fzv{Њc9x0 6Mrr2cƎԩDEEѯo\Y *C g‡{^`mV㋣jx/\h\FF& ݓó}ѰI |~oF…p:L4w$#=]שP?'v-%%W(ޢhBQ>Fχf]~h4b4C6n>ӱK7=Hyc~@vN q{`<@|PA={qqx-f̝ON*%(^j 2f!! c40hzʮ*f EU47͎;_?h6p{bۃcHC!ޡ٬z8Nx _?T t:>vf9ʥ(PrC"H DZ,!Q/0f1ΨGE't4gg6AQҩ#QgLFFy5187/]8; w; _wn|TC||{Bm }mtm۾:%m9s$Vm[c1NNNN8|e^w]9Bme2^0'OFt޸BsRUUmr~7*׵*HK"-X(;UUm\-ϝ#2ADWk+$.իeؘWY]F!-X?i#BPHE}y`62q~7&'z=i$+K`@"K"TF{MLTnWP+ =ivFiE6`#'O*hgIIQPU6z{a`⫯5t]AU~ܥ#IIuAxBnjtbdO*"Z+1F^ㄢktdW ("-dg+:=:MCIa vL󮁇1b>g4 @7#O*V-c> Wd: &N(]Zl/i BO?+Lbi^ P,G=2rϋ yU/fT:|1\D ,}{7_%+ 6uqqPJ*4nSNut)xȶ^LT3HHIUuZ4)NNBz:TS]wԪby}>Eu褥D׺ *u3Wj_hgt:}ج||9Cl T ؿ_OU:(B'}DG ~ODD~kӑ'X\s5LfX\e‡^&kв.IK$7rc9ހ ,FDBӖ&~GB-([Ffʣ= KՓnT*('*gJ)s'Z] l*% JپŃϙ'Wi[K`)O:^`䛙^TiA]RB@҂ʕy w'~.DDZ$eoW9sVFuAP l6?Z$^b 5~ާͷe?f|edOiR+&HNVغM Oy'GD ˖k|n6:_ЈCH+ b^1p ~ҏ%T_8pPhxW`gGL@vU* vP0`A5L&h9x m+ddD" :tlb7Թ3l|E" :L&h\g<]糖w,X|F'+#(Q4lޢpBN0DhZ'= [  fԩ'0˖ ,\aРMkLWyZPPRe0]2AP#IgRcU`!0^}u*ݻYRT)~t<Dt+(mUvÝh_"KGp^FK_I$29%W.U$ҢD;%'@wf H$ ߇Q/^3f%tRڶm+-̕SGH;*1|b3Ѭ)[Z$߁`Iv(B۶m{EQXnk׮Eٳ|2 iuYvnEQdʕ?3f),EQxnF38i:Ilb/&N0 yi.W+D ,ɭ񐙙INNdt:ĕ'X'++L|@ݍ7zݓDut]tvCdee_|9[^'-!999BMff&N(TƼˆB5Bp;fggz|~?… )V)))FQmGsvvv8-[ vPvm9oo+}^Jh9DB.J~f7<ʖm%N/O>c9uÇ;`Ӈx8q"ۗWbوYf8NzEϞ=iذ!SL#66+Wzj<?5|<<rFAz0`gϞfQ`Aƌ@dddX8lڴ!C( 'N 22חЏ?K/D||<Νcʔ)$$$fƍGILLdۜ={l.]ĸqXb8-HIMMĉ 2MpBLBll,p8ѣ& DϞ=7oW^{|(_СCڵ+ 11~ѣG *Drr2#G^z[8p͆`L,%K:uwaVY~0̐!'% " (1.ĴaUt5GUoW]" q޹a8/O?]]uT7n̊+Xjv;wfժU,YqQTTĬY iR\\aa}vGAAM6%///zGh֬/bܢ"&M?ζmhӦ >N:[DS믹;2e z+A;v$''t*˪Z_5\pdddФIٳ }݌3&-RҥKiѢEsX)z֬Y'LAݺuiԨgʔ)ԫW-[w UW]رc|L>=]رcz7@'6|X`9d6Ϟ֔1пxODi giر$p: 5G}`0do*h4Lcƌoq{x~}@ IDATz) `ԨQ/M61u1 #s\@ q0Ms2Vǚ}Ygń K11[+W[w84j$4̸|} h" ]ue^1_y/9Y7k'uhf:KƉpUN8&TX5݀vS)ukغu+'xbz*r(((NKϵSJ*˨́{c믿1[E\5Uj")Jlm6i)}("_5Zt@Һuk^j9T *H΃!y=na@q!5Z˞A++72K TʣߛE s?7%"abwc?ӪCG,&s߳ɦ(@""Zq7 S3g3hו:3sԨqx!M Kr9{0eƂ 4`cؖXBkm%j%_ЁEeY^Ӄ:%;R Sx }O%I4*, e3=L/ܧc3\gY]'^JN Z9^9u9Vٶ8vd]~/H`׾$1;qj_?xSB/xPdFwax_lW "ZR(=4cK?q.!ofO gCռ+>Og|b-xp(EY,̄Ex.e#ǟy@hH5הύs'2#g`~DɆm9 K.ajF"ʽ]H9Tup\jwln5qT_k0٩5 m\u|UU1s9G*ۤ;68nMtu,Nؚl褼e oaΪ4{nzݪke+˳SkF)Ҷ__rRxRx-*3^gݏ_Wx$_G7;Pu;Ǘ>f]4{mR2yNE"OfbD8t<1An0+ƻ>gMfrsQlrX |.3FʸKxbl(JĊsJ[|$^+`gpc=~xb\4ּ{Le֊Lr O.z4恏hp"ƥ]rXas֗m0 uC|y|+>KG(_9mSŶH[";9el +/Yq3 -߂.)Ms \kgwaYs3=ObS6+#.Ʌ˱ܲw+/ӟ՚Ϙna~+I޸gK|aV>wǏ1) " PhU!a+DkJy<}(OD~쎩 uw8͠o]Ogz(PssZ~9x u妕LϿsxrѫe䇲)8!2^h9;lo/C)ŵo?xRk-Z;+UkS-i#.'jY \&}9[ҩ~j^1m;'u_ө~ Niӛ'xmĝ|m։SfKq-paPw4&Y]9/EN%(;{ $]Ԫ' Km־;cZX1Mqn֔qJjkхGLsb| [bqۢ8>=%;%Vx;Ƕ잶: -͉;CG ӹ۩t((bm+\xFt9A+Zim) K8M@;cJEaFuYT$b0N8CN ҲN#bvz\bvڲ-_S08mLePRFYu Y:2Kw\2wy}قԍ<yn\( U hyMXMs 0A?DܶP$=Ahl .79?G1ːTx+2Rn׀DoR!uCل1,OfR]ا0tM׼ ]sfɦkWk>^7)]]Vu|+(5%t.l: ecbzt,UU: fRZZ͉ :6Ofb<=Z7۸4kh*ٍ۩Nm!XrQJQ'5pFc䟃Ba^iQO21d/^(hVkpK|ݺa醥3Ct8eSxAAfs[cᆥWkn]ؾ3kݼ<|eخCdjBP]+=l \ !qSay|+L#/gzbw+:_ ZvK P exo+(צKVL{dx,X >?>{sǣW!DT]@t.lo?$bӱ^J)m[s'\ý󦂙|E5>ӃlQ'U<l];7՚e[u䉼s|c#VIzy)eK f|W+yO8xA.<~F;cʠ^(Iy5'Ńgp3WQ[]Bi,=d+8mv8~{KIRDRv csr3(IWxH$"4/c Z5c*hߔSo&cFb6>õ\q%pT8IϠ4^A/DE"l%'T-zx vJ0"j'G"qt H8v:o],NWkI81; B^?RlcUڲ-z v=O{|MNӴ֘I0UL$N!n[fa|s>L_0*UAГo<֔04֔0wbF9d<>|G;Dx{Su.c7=d3(G*g'2{Dnjs|a{6q':Nuю<_+QX,A WBmݗ{8(\< p|:s~UQ[*-eMc )Jml85W+QZܥcR~XaO-~!dݪ f\6&TN v5F >G̹a{x3M6W'*V.`)Qm̝Kя}w`|s6fg]6C'mV6(Lm3p` rٱs'W^} yzʫHlIRquY?E\ˢȑds G9`:ѧ x۶称،8|/={#y(P06|>o'`~,rhc\"D` p:ࢫm7 g%͚,߹eXYlT/@)&yF"1z\1r֭UҵϘC>?^}l?7Mc1v<CyvMN%Сu[, H- Wk,\H=ҋ)(,{n<:a,az0ދӇ gmd d2ʫ(9o$[oE`A?n .Cpb8zs=bVַ"X n@ff]͠NGu]LH}G @0[׮|̘ SlimYt>p?`x8-ZOM`W,0O} OcgGyR 7j*S&NѦL43xˉlTӦ+-tm8d^_o7JK6heTe˖sHPԬr xfd/] ?4t=Ӎ~Ҹuɶ7l`e7e[ |ulQ luQZ-%[8D#d!yt>sn{ zP7\ςѿ_?-\8̘1YLƍ|Y<\exr堎0! c=yr2CWevX4Lz2cKyɴhq~L20h6j* {&wqXVRh.NI f^ޮCA*֯|vt׋ǵG(\Jwl' W[h{{Gfe更lh4F]HEڦUb] Åjؒy}>xanXV}G(|Ws'ڭ,O0D "̄3 QXXgfMlݸ= W/gwJ?@5kסfSI ` y㮩M4^5oxlsi[ q [Gaʭ-lbm"+eYZJay۬xǨ_8v@?|x<A֭_ϒ%_R]6tnV#^.ӶqЖ hw<ؼ9j}k(afgi)Zk6j͐'ٹ|A˖-cA vM&PTp$q\.x|-.{>GIEE*0 J0]Ν;9n(hPÈFc\}C~?:zRZZaʠ'>IDAT"A<@FRԬp5W8m6틨P?[,]DbV5Td:P&M0 뱒SIг{woE\ƶm;(jք+/M~~>7PVVNx\s [ ?x<nqtyёekRx;uB;Gt,ZCg%>oڵ8J)1|Zur x5À~,0)Uv,SOܑa&g6h5W]ŗaƍkז!ƨ+~sZְ giرD `qNʖM/KP6h6^a>{-oFѶml>|ee(t=n]Z͛ǺQC9xZ>,eVLc$ >˲)+jHq XߕR|> qzX]F׫/1٢*`%,xaSJXJ)\u*-lN)ψRX4r_4ܻwlvxьj(HT9m9(`)CjWfzH̤H`ĀD3?߃BQQG#UFc<$t; +m}cp'?C .+cEޠ5>RmXٮZk Z3C&<4P0X].CD.GVGgqT c!O1lFN'`(CU9{=!ڶm0ndʲyN~n^`(H^N޻skS{ƅQЧ7` ']ZZM%W%W F~!MݎS]5\_Z&a "A8J{ ƶwOG(m֜ne꿲&=hJ j\+\8*KК֭M~u7bFT`hp.4GꇼDJ_̓d(%X ibVLIaʤMaM0 ׁ>۩^7KiҺu]pDZ=.9mԩSW K2vQq 6+>d\MT)Ѣ@uLP-i$Q.6I_m 7`gp(MBD` o,!k_lQӼC5}}Av L+  KAApA ”lA@i-oi. F)-lLcQxJ6mذ~=uɂ Q (6mMvb/` y}ȼަuݻ .Ldlڴ#>}(,φ y쑇%M J\y5mڵaFb sxIENDB`sqlkit-0.9.5/doc/html/_static/images/add_fields.png0000644000175000017500000005161611533717256021605 0ustar sandrosandroPNG  IHDR'sRGBbKGD pHYs  tIME  #B IDATxwևOٜ#,9,A2$*bFE1t*f+f *Iٜ =.y_STq@Afa  6At  A nAA ڵܪ*1xwIW@5י3z;[pxdQ_˪.ϣ{w]|;ǧߘ>N'jSE_&Bn?r>iIԊ]}F^C;l#CM3Z=~̾R]:XW^7ݼ Tuu+m!4Mɷo[ }}ih8ƴd^'ɏ5ձwQ@\nCCү IZt qvlZM%C&O=L  }-ꊭ\LzFzݔ?]WY X9Gɺ=;m^Nי ]ohlFYYrpY @ߔPc3>{7B/qM Ar*CdrJ/ӦUs7V5Q#33'6RdȆȖ>[h+Z3EˬGe嶁#xּes,>R+"hӦ_뎖~zAoa_6>g]KV,| *sɓn/YOtѧ8:tC/XsrՀPkTnQҼk؁suW=1lH @|lFGpyE~rBC^s[;pv׆uîܹY 8s =tOjWg5wŇjhxu ̙~g+$ joGbɫuѭ`j6vЍSwSLXfX{q'|j};ՠh)<{ձm"AvN6vv~攇GdIWW7o0\7[J'ܔ;6/l-[%3o^a;^[EzA)|9?,,0}3.;#}g>@k6C1d c E״8:%tM;=cڷ7T0{ΆS#C]K͡*٣%)!KoQg@LZ8gTfKҸ/N-]? ɞtx]ٲϗZ hmo;1RmA D#Wԧgrtifؠh/D6\BC|;|_oBc/{#?#***cϱ53Kz`Y:0{vo/U03bNsPRUyT}|Q~CE'wNjr~%s.;`A:q ]n!uh&,c@prhٵ9ݐPJS8$Sm\+}૥WбjjJ; 28-%!e]K #GoغQFǑʡUy޳d_-Z7nMJf`[Vva%5&</6kDƃ5w/Obŗ1vSfIz;U:嚌PΗ*HDIФ6!'UrQcn:AuG+Ta)(Zw|;% u^6R]ÎZ?V5VZ m{g͙'+ AF)"5k.U_*٠ i&r`%N5:.4 њb^v82pڔn߭9np+32xn#ȟ6m4!c$zA뱚=u.XS8QՌYlxTŭSˉٟn ʣɱ&Ԣ'?Vm;P0ٍ7g ,.,sXF.FeW-qod8Bw"gvF?CG$DCNEӄGBͩjwD)*X[K9_ _yu G܃{wO9vߡ=&.m7`sJ 9XNm҂^klUNzIDZ4=D;v#ّ{UfD'6\n ˚[w蛅 vT;ו:ulw6OaUamGw[]tUy_n(>gE\>[ڍf֪\7 ef¸᳗9Ljokͬf!nfۋ/u:fkېcp֐Qmۤ_XGeE<1tVy'E4*!wnkn[&ՑUVa#֬Đ."zN Ģj;ʾZjV[_oF}}=fyMsi-=S:ΞMtuSBx50Zòk:7V_|Ƿ8YbDu.kmvR0i[u6U?Z[ͫtꞰp;zA$ynFJE)|TTr 4jmQ$*Qz2ھw7i3N:pĕW~pI'bTg3Vmm֫ϔYH YBi3ʬsWw1jT(VA$aU*Fh4.D%oڮryj ȑbiQhɓo f׮-DZTA_WУ*GX#t#啕EMY=2"8)s{SVVq KvAb *auuNX܆jtǹ? ~t9N8ţj[m4mݱe_}ԫDbܶ~g}8 *U Y9>8V>rJoQ?,׫9@QjPWZV'qf:>TWcjlBY^$Q\4v? 9rnL)ɉ7>ӭNpn}rQ!:?VVZ.Za8I.mTߨ 6Xg)Xʊ+'r"2Z^{M)++ɧ+-"Ъ]\}B(0Z{RJó՛rTĚHmNQShA+*ڰ<$&&FDt$7Me6Di[$^mX*++M&jkiМ6JV0C "|U+q7+($C֙m; 1G ?G **VE궝 7kl%Y[©˽Cb -D*:=bE䊉6(Cw/44Rڻq 6oE(NNCl%~tь1X\\qjATmm?kubꌸzl2*'*p'wl{je7v $| !ZZ]͉S-nZ .S3OHl&Q\LVgm rDvyN)%67wo'v!vo5OI^;t:%IjVln-چP&NE>&{+3R> Q]Zj%͡a`mQuV!I:E2FU^lˌ7x7xm98WDvZjyEQr_ :,kQfށ['݇d29t-p6mp'Zt%KI@; _|-6&&%mȠGGNdD$(iQuNQ @b#M >pAnf<qqq$m?xDF@v1Bn'j-Zx?qK >؀r֞~0lpsgkM qqa-=1yYpR^xsh*H !12H~Qɀ#(QA./aB(!!!l6t:wHAOh$uzQ&G !<<\FDD0T*U]] nhn :I*cFF}M=N`&P8Y޿Y uE5bTWW[LmZTY;EI~ҔK;Wn/u5ܲʙW鬶 P!rEVU6(.K^ u\(cq'?wDjyZqR.gpZNem{TXoTʤSix|9 WۨMGiFRYVf0Fdy89EfUVVZ,9$biiiDDDEyy F36Ztof-[̊vp[UJTXL;B,UbyZXء:{ qJ%tuuw-'܋C.U*tF^4w>LN֟tUY4:+ W۰GDFȩ*O"?28J%_*lF_BZt_s6kUB"ͯHmwzhJқhmmz8^Zso>u⽜nQ\zsuy|o)OҧEΖ9*CD9BjM^2 :w pQcŒpu WPʦjOG76f7Yy&iѩ/ب?n~ߢ.hw#Ghfuv9k̯1Jf՝ɫyM cT*hb\ c/ǧH"e*49| ZWE*a#Q\1nضm[UUՆ bcc#""L&`tƛ<'$fum͏66l#1*Ԩ9~] 1:&]Q.*8忬.yL-DZ *d7'~JjU(:?Wq~3JIe-c\AV[[R0?ς!55 q-0{p#xgVtUhnvͬ1f}gfԕׄ1aLL#0BD !UTc,:`0xCB v1)¿YQNP撨(I{N JA+mDǜ:}h4L:l6 `Z8B^w8.KbҹSsb.z[!:45Ӕ{EV["hQP&4$B!c!?;[RNq!@$J&7N ywD"reÇzmOEEen`NV59[ r:)ܬ5l2 r:A𽶶I;n45ơj:}OeչzІBR_;_cxZjZFөw|!( ;'HO69xGG6,ħX̀a#_ɼQ=Ϛ4V_=}Km1 DFDHivjg%[!1&6as0?.0#b6fG(ɀy[|A&6l (͗~FuO7yA3xOESy !Qz@uc<ą ׼GARN[ 0#>1z &my 2w]XOEݚf.)+Lψ{A2ƀߤy7EY[o>Qf L$ ?E1J]!0ϔX(72F-Ѐo ?|6.yu c` 'GB<&[HEd^kh@gHzgL>tK)|RvglTa3>cAwݻg!!+DRN50&r {r;41Be 46$Q g}oF bGkXtS|#v6p)#*вoVcQXB1fA]edAH@t6hLiЫgO@K8gyibяgg 7$ D9AʼnD!K&)S yER1e@ޡ'~S!?i+R=6晻stTAuzo`xg.74=wQ%IÞg$x2qBDƘg^Am=3om$HsUDV;M5 IDATAK,y/<&1"O0< mmH[l~M@+|L̻җ!1rY$hP,t ~sK`pQorMBO\%pF?_)l/r; !S6eD|i152$M<܏%7y)yGԭ7okadE"S!f?+]9ܞS+wGcnxIs[Gvb̗ߜO|}N5ɽT/Q$dzsrϑ3OGIL F!bRɟzb!Bi88ۺM: Sxʉw1=N<zw;k,2mّ%3\ܓl">{|;Iι'E^C-ܸ7K+pQ.gr09)& A (S:ߌk L+'4i0' O~1 #]JQW r @SgN`D0 r*xߔ٬d, kI' 0KXwf³V o(Y@sy}"' F +7 :nҨX%6~_'QL%ӊ~AVd;Aw 0%8R ](}?KP,&XCT`lo 7*giu"|""G@Mf 'e?wP7 zjV<927&{Zš6u8 Zv|H k;rSd+@gXkf~bx'\H-Y=J OO|LW듵tR"_`f+ eAE SQ#^*X6h0eY+MbkS)NiRQ"qe{.U$"*:&rȀ7v蹫#yez8@ y4ngR{g"//>ˠr/Dy7BsG 9y7]N1<DI+_Ȏ[$6$1@״$ x-CD)DIb tԔ)? [ƚ7f{iFηbK x@ӈx4šaM=;i(Wj-mON;^x8D?WIjQ&8|K(%G[HYK%9' Cems̡ Ϝp>1T}dOێ)u;j yf_8!juY%vʺ}bzՆUKv0so%e׮ݱG#nGkJM,?pA"KRpxwIky!]ۑ='(H(ٺt՚6#һ߿c();[=$5f?M|KO WP}`ͪed^=vĠ4ߓ3?ˍ]W~ #S2HD{+W6vB<~~o~ٸopygi,k?Żqh{];nB'ݩog/.,е#z'_+ } xJv\TnDع c$i]9_k)e< @;|j/޲ĿfʵP^1XM߷!OLrbT%keRd\r/VWY=ufk7}R ،(dUNSJO=zmV{y\P>쬗k]u62[Ͼ[ `ILcs76:N?dɚ[6Pλzy 9U_5P'f9kj-ι<Z5D]@ݖTI>nU T;==huSʤ^*((;jƴy5?o;r׻u;w[Pq,w?YƢuZe(~t!y˩}CaTEXVHT^r\=}b3Ǭ޿wl<lg7&D&1` F'~?ooJK"C9%:˸}$/Uk޿E'Lۊs{&0QbD ALDQ8IQ]}`NjWJefwT"kPHZ.FI{4)JGk7.YG'O:呫T@ 0I\}?ڿ> DӨ6h#ŅJI st UBh)zfDzc^ioX POgd֝ kp2"uX/YV$]Z#jE^ntM?XWTwxSD(^po)@̀H?qѪё\Ny|$Sȕ/ȣ7Ή]~YVKH_aĜg7\nskjwb0VYm4b{&ukwx= t[{h AmG,8,`TjNnWu4r *Nm\!){䔿\T_ ,ެ sJʫH5VFF.}yκ̑tC"ͱa,Ts<6!B11[6*N-҆'iM7.wt7 Θ5ؖS޺kXJv+U7V5Q 9js1ƼQDz7'BkKJݓ@U:hH@uUܰ&݆2Д`5]~/"'YBLjj;zυ0+*|J$*OŦ1%fB[CsUO`TtV1'9XL*Bl'1;N O4([۰D*X]qzV_]Pa3G=F r* W.3Ā+l Ӄ+׭d09P21*O{ 5ZyR1QB }[yZijUqb4Ǩ+E.H-ukUF%ΐ:oZC{uO3k͹C*s3t&м[j U>{F&I-fv1#3-aAjjs ʲTf7MI#CX9q4ג UX u7uTކ'I%?.ENUR Sփ{g-ޙSSsR߭{7*IueeE5v4k#ayUu$GuM9Cy+bbUQ@HtZXLIϫ&Y_^&..R#ntxYujN޾K.Hc=C9@Bmi%)!L%1 TxsVwn/L&1&jU'dBY#G_yP&t}hI 6^\MH򾚴acn0l_fV'*sƣηN1o|pӒc@5OO?ҍ7g@b0`y(1uJnɋ͚?4}Ұfl5fX?Zrn+O:v2o{t@1rC@c6YjQ8'BÚ^`@MĀF U@O+o3'װ&۠6oúNS$AZƥ+o3'IO_c[ i&iq p劺ی %t\q}uAK6@AD#} IxYrŜ^(WA쓿m86 ? v _6 9رcڵ~> At  A nJD87/䊗t[gw_Ou)S^D.7 6e/t}ڀу3L8sK׾7ΫeۓyTuSF;SkY\yNlYG& c-W M|3/;;;{ WK=Ozﻷ7/ۯ陝sOro3_<>Ovvksrl?o=v7Nα{ Z} }=(υ2rQm&+=?OloΗjDe=;wЦ<.rӛSX慹(OZWlnWM1#%Cï^xF^(ͯsz ??]--^y(=wcvyߞmʰ[z6s4=X?kWۭUBbAɧ7:X;/NU+aLl_jR^ b֔x9\cۥ"K!Ev{|]=g̈rO9cUhBAw=,88:DPb'j@jm~zy̵ зQ{%Uqo5RThW;(Ъ]s^1ojES@œx |}U"ty^N&E[@Ҹx9\RF:#-lEΜ9}?-~gGrS ^~'GS'ND}`iK|PnqQ 339ϛ9gŞE֯;#FgD5wL$~]&]g'gA=Ǿm9ih'䏄?v,l ŧs*UaǏy9]jYIDAT]ֲ$ڔ>M|z=cE_youPקtΙ<7gFvzmtVqV]'>(c[ywr1#^2ntSh|;+c/Ͷ?{?3œ'-m3ăaxg?ig_6aoOͷq/EGl\1ABPGϛq  6At  A nAOSmTWWc!Wuuu(WA6At  A @%RFM/bW HhpY݆ǯ{=@kvz_57]]B Q/_Dߩ[#VsKjXVFeXq$7w {EmxG<5Dw3bcF*rWݔw(UnWxPDbg~xg3L}zΕ?MOWU ~Sݱ.?)8HL12/\NtԖڹrGO'&o#:w FV䢦Ehݟss+m:>ʥ 뭯߆|M</ S52& #K!?ɏ?!*sRzf~_ ݆P3fL{LhWb/{MH(Xjc#%T*ݳ,;4vd3w<޿zƨslj|crqH;W຿0:MJVnG)"W&Ii^u:?ږ a̙zǧJafMmocGඍsvj ԩJ :*. \,B ꑤo JrV)z!" mύ8tu߇h#gf実^wwUtxĎV'?o=S~Uj-M1ϸ^ճ'p\sM!W"rE[~kDUlZ2wN5~/bTpZ) Zf&1 C1[g8;FwL}wό\Ȍ(`Potbaڳ&㖷?Ky^Y\%; Yz(itu;|H<%[¼GxTN}z1ݢ2G>zAQ3{?6s蠿ٝĉ8'x;7 nAA @Am  6A6ñ+ٌr>hAA @Am   A" qWr9%l)xb znYo;,d˫u3m TH^m4Z}a jˌ]t p|qĝFA뉢Onwt1a` L:ܻskA6SiT'6A ٧ 2'RNUt AqW̞"{%?qOt HNvʝ ;!_.#r!VY|%hAYJ{86At  AA @Am  +M][]]\ա\qO0@Am  6At re t}qA.(CZf5A>G8Gw=4M+f5?6鵣A~dj.a}[1&BZ( _{7HǿyeƲg*vCn~聱Y&q"}FLyLhuCӚݳb͍ouW?ߚ6u0<eRoIHg?Fy|?f]?s&"Y. RHނR Z^`PB v#My[}o_ct?Q':jKN\9WocͰT탧^\e2[f>8 >Eʇk=x% \}3ͺuSŨAnڽ|-3F>GWDaJȔ̃%Uffݴ{sOUDmgݔ̃/3f_{wԙ aPP Dm(*#u@O GGѺahQKhO@pE :2NAD d> 9r>N.!}r}$艱Y\z7ÎՊh0Oyc]+"-dNĪy<쪋9cA#_TJH5wJĝ?qnʦ ۇ`e| ٧c|Fdq"jNI5 ߥ$~RKO/:s]xDVϝ!&iM Av}&[u<_&@6'v kʑ<7nM ߻8{L/ą]-dֻMw4}p!qY:vADv ]$""2lM,Ql_x:/$7&ԙa]ƿ\VmCľ#e单o^:xkG|'N@4j ۂ$}>dۑDD$i[^:pn􅢴{|N?aG Jt̕Ҫ"MΈ" >\Ć;w943\vf9xڏ=BCNmu[ʦSmʲrp 4WGͮkHɓX[~j3=MGXtC۳h:a1Eu&"BsR#<)pⴐNs'.kOskCե#YY+;Nhe'-Y؁3g; |^Gs[_w1纥&̧ʾy=ŸuƳ|~O鑯I LX>8pƨ }+L+m6-dxG+ $*)^f8V0'%g˫rqA}p_-r]k}ANq^N|zH4k8UwރcOw>_bcc-\o+ecdNm*<} h!3?:XdDj<1];vRϓS#g7Ŧq{~AJLD|O7x>i{+#Vzu_6Cay1=\'y;ɸKgX0;,=Unp'Fkmƹ[*u^qrLrJKc{OJ}yeBld;/fsxR%[v/_/ژD>_lqJ?_leL0a檏3w.m!Ց* "@A qW?J_2/M*whz:`Ds{]':}11%gG7&-dƒ//8> ,l1bwM̶EglfÇV/mI7``oNs޷K=<{zz؃o @lbmڢ@`r9{5hb"gY] :yqDoOh;ݭs7;XކGĴ URMmD:^}/dTszv -0@lo>|2M~)mFa=bۯ^ԖujG9 (6`Ԋ~\BD_PDBIJf‚8bDDı5e&"{aZ}( QZ{e&\@-HD$\@%JZѡӲD|lB2k,#6`3 0 +ai⽵ a˜zq\rr^7 6[lIHHhllDrb'O\fMSSkЧ"F#Hr9S @ N*r9VbY˫`̙b8:: ACC ,tDq@ ͝5khdC*9Z-լaQM0ڀG|>_(SNZXX,\P*b /EF͖IENDB`sqlkit-0.9.5/doc/html/_static/images/completion_group_by.png0000644000175000017500000005145311533717256023605 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|TU;=@ {T,X;EXւYUQDMKD33dI2!!%s){|{$* = Ac;ʟ{y-ߦࡩX}HzN j8((zZK\/nSߍ4@P$`Așc<埮Zc`LE)&4>iO;nMΎ (@dNPhWQU9?C@Јp_| =;j;pDŽ.MΎX: *};ho>~ Ly^5,ꆪtkۂ#뼚oYQQdIQdt&=צ4U^5,hzO\h";p؊Ivi;PX"x;(ί0krЙ}jd_AQu V}DAPUY{Y̲(%P/(GNdDfy5..qPsA-W;Î$<*d)٧I_tϡwDج=jڨؽ+r(ɻT|*w[=sSJzjggn?9;v}Z;k%5RÕ5U;qc"tt:$IN'9:u$BUQ1>x$6a` ~o6otG;B-$)kޣ(3$*` E.̾dlz~7p䓩!u"s)sV <UQ11#fg9;_W4~w@S5#,mm6'O~vk)$I5>4EF"WF7fnǣϽIe8G>yjl!M,{S D ?W~ _ʛnPr>?5{~:"GOpa Xh+ow^}07nlEpDʁ-+X|^y 7>Cs JZ$z,DiJIrV_YEvJg5]*%US;żk̒wq+X((4W@\ʄ:SEUY\}o`%- ]m5)E.xa.nɼ"n n3a2/;a$./n dX=G\ /DHc)>6.?妜 jcMNQ]씊";o[?N*ayh`Rpf_ўr>\7tgPr"gSTXAgʉTNt⨪BQaQWqp&;.m 6Ytn@F$ l*; ΢S+?$ȰHv%m^mUF!d_;pYv=**' mePp(dۼ&p\d߳ë '2m::fA&bCyR_˚ͳQ]Q̺5U}GvVz..]\`Urh.Jnʇe܏._݃ȷ .gO 32?'dFD|6J~ޏ 3P#:<ʑt36U sPJwI꟪&_ܿN+*gہjrݖ-mT5꒜m0*A/H##ڝa$Ap3U"Dk`ى 2$jlާ빖75; Uմi A$.}/j{JFp^ZiSS~/QQ GqKI 7%Yqjbmn9^4,l6{e 6i* \umnp3:_+˯.tٗ>BAAA6<ዯ3r:wW}n)LD|5>|ؕn1'o = dmnΙG^8l$.ح,e/~ӻ`}+ԩSy}t3=0udff~TW0`cs8'퉝/͈1\+̫Gu2߫{=-8q.`L7O/v~~e,|x'9|X'vn>?7o1; Q=XixEKy%*2Շfϯ桍?<"XzݮfO+ÄǟVUU~U?f?+l2_f~Vq6ml+<449?̲Ϧ͛r׼*,'v{Ȉp||,>f8Xa^=UOՇW_Fq4\8x9S@h=Q9AA|,sdYvgfeۼ=V**'v=(WmVgpO(pxtxK_QiRQU] TiZ$BC| u ?9yVyxX('O%_=EttteNuK]=,}m0)**bp̙j񤌂 zok7iN:,>r?桍د cYKdffsϿĥǹ/t<Ͽ2YYYdff*-WUxIIM%7// +0MMFNbSW~TW6U,o&]6~}3vz^{XRV^whߎEJqqi9'$^RģwL޽zqmwң@f=8ᝲڬ%,4K&N撉g}]3F\2IW^K}*-WU;i+ɘqr-x UzMSѫGwyGueݳ]q #^JpPL{^3]zj/{X3jzu€or3WFV[Fq4ޣh6?4V\XqlK9w{C!+fuj >S,п"D|UU]IR]pxBO'_KLUˈ 2xXa{gÕ_N`?9y;͖[ҵ3\}-]wV@~~>iiiL/?}{6h)9﷦iP4:e_2aX7M#ņ'8x*b3r ^\s,y&tArr:|6% 7xd;D@ufWBeɫdk'0fl6>Gh=sYCP'44~4_rbAQJ՞ӧ3JWQ?5aNȏ+h{3ƫ {7_}H/ 11͛7$''s1N<~^ENa2J6АX,gC-8.A1BcW:NH^pl3~x (tuj4ZXEQUEQPhkZZ:a*q5eBV-]p80~ܓn멉H#;yՇڲ_ק'[#IdY!6PTXN'#3QXP@JJ V_C(*EF 0(UЕHLf231؈>N3+ E0^G4O o _MBS!5#If74t:S طa5s!44?*n'?2 N$@*`GPP\WtЮcЩ6T-[ԏd@UZ1;v6hfI'K^F $ R:Az=)-8il6ڴmw۾73bg{Յ/_~c<ѣGA$d;J^^>Ԕ6mΕ'zEQp2PلCVPIaIB%ZL:`]Y {uuTuϧ( ~ILfa(Frjl'ʒEf_+ѽc4w \ǎZܙ39_;}h}{ NyJ6 '88EUWp#Ы67'`h &&WrrrhfpJEUҜ h sr~H$}L:X2m0_1F?܊,5Kt5ڴ%9 εᒑyWC\}ݛdY&h?@FFȲfgai^EU紕sL ƌ#h8F @ o~ L@:3Ա,tv;Nլ 际߻Q]t 5kN)MϜ!:t~Jvf W&ʪUIVVW}p \: E!&#i2á"/0m6gV.Zbd(JE+t/%4pG;BK))k.(8+h@˖vN#p.G.Đr^|3rE}@`HZÎ"(l91c[5UA42QTل,+AmH>9Bb1t%iHImJT:5]mI*GCBR.C;b011dɦu/o=5RkW_M?…<8&Ee-\qg}Y9e trqHZII4{jq&ΠsB4نN5fd Ja! EUxgpM Hk>p~O[d6kCҼysBZ7fUFAs[Ys 09J$!Vv愳I*]ʋ]?sl5FJG2ҳI/YY0v{)me˖>r ob4Ѷ][ Sb2PUU$5 iZd:RBV_ߑ5rRAQ0~4 ]D[Ƣ<~gxn:,+HH*9E8P4Ҍhh4p4!qvZwa"gFף$$IB4$MCޱv-[2|B@==z4k֬CǎF^~~Gֺ':[6ZISPQȥ"#Q&,%H r? RŬTteפ#) IDAT`c<]j ^uڇRJE.,<\Nʉci߃nݺӲeKZ.®lr(pQر#X,zP2228s&ǙÁ`p~ v**k 3A/hyin7i}'Z ih9vQ ;GO吧 |W*9K׌|GPs\0Gx-E`xfz=z$4$UER$ 0ybbbh<EU^Ml6,&m{س}!\ͩTWCKJ檖]ZK+JN)p bwb*:\8F\J+#=‚b@ll<~@X,^ߤ*QTTvtdj@Pp`IMO&b+RRR -b咚\^n.v??)7TзA`F26 ]ACZ`0 8Uop,l:0xqn2mAg0nW6qҴPPz=8P\:bۑ$YF޻n'88p;^e0 ]6]_C~%%b 4"Y-6 +[ `2l4ڵmCHHȅ3P^jSSqkݎ |C)..lbA4UP"223ܮVRn9Q).NmΩ%uzBCCHQ3ˋ)}K=.=A3 ً"=P3NBsh4ѭM0vN']k%?ɘm1~٣k IGS% \9s:@9z"\CQ2O.8ݎw'Ŧ 0+!gG~Ry(}]Tk ֥UVlG1nqH"W\}5oX@h^C4nSlS\lCT=UUf#;)k&77ՊR&8%%/u9MQ-$$[x'93 )?'U8v|㜎vt#؊Aj=4b13zH{Dv~(>f z |W?tF,oDo2 fʨqX{в\]Zz_5`$]=:w$ J`` 6NOe-t7J B tҙYfK`` ]tMqRӰo)ے۰u]_NjUe0C8SHHkW8 v}ݎ"+;Ml=jAu!!$'`:+EUJ2@+\ڎ+}+utQ5Ԕ軌G DKw#jm6Od2<&1}z>}XBF$Wo@7IzlyUyLF\be 8v=77%t\r ǯvړNl"##/ ݝlmgc6=TkX:lR6 d3ΧHCޔ~k맙͊d;"}11t+>hޠl6ѭS>3Ə쐉VLNN[N2l0|}}HZ4i_ Ŀ_|0+E\jhM;ZN e9HEE{9WK8޹īz8Y{ Etb*tv.II<4Y+>AQ?AAu꫌`Ԩ/vJDNܙCd"/?p EIzf3 Uk*l۴ b~Vמ|Hbg AJY/,VT7Z7cM&$5Obe5tlߞ\T޽iFY$GnNm[DRА|Vtl/ v9lHFвN!-09GyԁS뼆U ܣ~r?~oDJ8 n(' GƶsDGGcX4lZHLDU5^2v0g0ͮdl`4ؿHX?@y qt2; mDϒgo1hu&!5 tz_U{3`2a0پk/w;˥_pF}ߟ oOx*Ck=ulttgЃVI筵s.-[ઉFu8m9zR!]nj:G2G mZ_ײ%0[n./ ѥsJ'Μ&; AyTЙdEϋA`` ;v7`6HC m̙]-[靵za ߏg#Yz,S\lszt:眷҉:sH飡EA$f Rtp%v95GmO!)sLde90j+Yl6[BxXAAX,z$&%%={f]66ǧR]#%I4 I'cډ@m4MCQgF \[sNhb1j-v5dZuT.)3  |}}ڝA @ hM!rI#DN 4i &MG{yM@ 3m:UB,@ \h@ hM!rI#u5Q,7A`0ܶ 2n="h F#ǦdUDbBAI߁Chզ5Q>~aĶD]*>Ⱥ+0-4mQ:!rMV3|dzЮ^YDDDrqGiC%=%Șf @u#=%B(&Ij,DN 4i5m>AcD\= DL h<# Z‘U 9@Ф"WJkim;_(<˯ҧ`:t3tmwv~҄#ܚ۷[7b6yi"C\x?oQQXVyx-Ne)G,j`RS3n۵^ "ݒDFF4+AD4WH@?ǎ':_7\csx$,sa[1 Q#nW\Maaa}gՅ5$>ƛN||sW;.\$#モwvUi}״Jm@ hM!rI#DQUy(2z"D & @uOq|!rMFe;|E .|E|g 6tb IYlK~Gc<ގ5Y FS @uda(&JQan." F8!rMH_>uEr7mGS9-lXԴ%zh I>!r^мDO}p BR_[v--Z{HZ=5\P>?ü[hЄF5;TKKػw/&Lhۂu(=j蓻(..n3M`h59]e}ki>㍷vm;s#$y]Q4zvm;|JtGv*4ʜyhS|[q78}763ѢǴh13ffSl|gǯ-p$^?~ɓ'ba„ ٖ@dF_`pHNN4En,bX֭i۷vd9.]}5G܂b`[\_xi;eٽFNvν)mRfּ|v6S ؾ:zII<UՖ~8M\'MČ3HKK#553k֬z/(}DEEKr-r-R"+ͣ&4z[na>D‚UZ\/>?+~~F?џ,M,xvPcTEMM6mģ>Ν;)((K^VTPmy5mZ3{|<(]Mh"C\Zx2F##}uŕDGVˉ<ڴ(p_7N$S#SՅkW^y@^^:<߯hիl˜y,&=lQvYkI8,?mwr_v<N. 'e8rlW^ފl"="h]嫯,EEEX,, LVA;j@9%(LK28FgߓǕˈiK/-{Dˣ]`XB>ӼyYs=977?%~It5X¢E5kV#G2xpAM_:hnfdSQ 'gƴa3 by,3nd+9O.7s=9;AdddqRNpON \4A,!r^<>"'}B/kžzAo0Ң=}o{ҵ!@˸xNu^wuD4dzFB('vĆvEp ;]N4mZ7X<ڊ!rM]4 Mݻ5 ,VFTk >]hرPd6+Z"D u)˩>Q#AF@ hM!rI#DN 4i &9@Ф"wc2Txdsj M2t+w%Tpǟ];}:~(?]zp]GQĒ2A@\ ~;(k]nbƘqoۑ7p[stΝ\sݍt֋ص'YnGDDԛ6;w0a¥n}n/[NG}>0uW~֭s, AAhт"9RojX_Cƌ+_y;wϻnb"#+{eİQPP@מ}ϗܹ NΝٹ*ڮjΙ GUVtޝ'O{:tP_EӡK 3v$uƐ%0 ^wAtݏ} oۑE#hծZ>бkOyl-JZ>M)v4_.fȈѴԍK'_T۶mc-؊ [Z,INs˯ᅲNt*vÏxKE &F^[YY_}Ǧ[yϏ1+gm|Kvoʸ0{޵C]Jy/5v#!00gyUVy-AӠш %<,清/^?ϛCTT$VG/}3idge q}t)%**y-ﹳ%4$RJ 剹ee?ys~ُnq~ "##aSٷqMեU7oaa2JFFWm.|eddۼEls4ƌj8̲o 8rowg_y""*~6o^/Rff&-bcʕ'((b ˲_  ˚gߗ9p [q!hMF#raaJJr Ɩ k]MÄ#ܚݻuaM<5.y-SՄ$7&4Խ\'O%REBݺuRXXӧ~5J_\\bbp gz u.qhD./EFf&O?[n#IdY?AW}3c82[q3ϒJnnOFf&)t֋ jIDATg>%vwh~^W^r=ptZj̈́I`ب1ȷ>{1~d Mppf vmLJgƈ1zQ<4kF7$y7W>NE{ζ[(,Cy*Ӝ}>y$OQa>ǎf=ũ.{t1đ۸3)Ǿhocg= >U|b6_#<{ #ӹyUIM:g6@ BAF\5\hMU@9@pA(2zBER £<_=#DN \(̱Ⱥ+0-> r_O[E=,Y&~ŇquBF@xgf͉d%\{VZH\6%3AOYl)^V^#[Ƒ:OINylZݵ\[q$'/DN zJg*✽_*w#&_dߘ>DFE^Ն74!C{ Y&V $r“n/kÖ-[߯?$e9 188⢂s6El+ݻ Z=QزŹc [vC Eshg0w!}7{&W4zk5bVgMn@E")rÇ;'#; Ǝ'o~%A-sZ449IiOQмfyQRQKYfxɻ""fٷN `OW(21#֬>{?{?5g> tGqq1 ܽCʑ\>R'`ll̚5}rGqENtOzJXxDʤ}xѩ/ĄT+GqV+)++ctٛ^z3ϯ;u&"rcuȕdX } ./K+[t5O.]вeK,99,%R|gNNN 5O3``,j 4Zu[z-ٷo{˩qT&z쯧-pp`QyV+ ݸ}{3'Q(,^͹s=\+? &zv~n]1Te>n5\P_s1/ T*U{{;O 5?`ooW#pr??'`cmMk|תrѡWϺǥR~#9}P(Xz%kWDPNP;z |V6<3OKgYt9@aaVʭ rulmmr6lj-o`073sп߶>R--<7ӟFrc!s'ڧZ*`{<Б/ ܭNXƳ*$I>37'bffF~~>]Qg6vvJ\Y9^PT*cb hՙ-u%)j+/>nݵwppIuD]zsozW5E!Kɾ}[,\X"/..XH 5-H-[sZyRK̘U!WVߙ\rR%**.c`@#Y~̙ ̌I^, E!K>;;['MrI^== sSS'cfƌ$33er6 ~cSJǎϲrŲA􉁫K/Uܺ}舄sڷbn}9k~4k|McFPE 0eLJFZ ''40uO,P' 2e%iּRvx-f(Q$Z?aiRPuIKM޾^hcB89PV }8}(KJQ,)~=]\iajڀ7ԇD9~8޷/[6cۺ5PlOܺ{6M6n!~n-˾\Nzz:]tfsx8N5 ڮ h99AejGR13s=-d R˗,]ٳz&dLLL;oDb%3#Ç3G-tgCz:m nmCwGfV'&:tPsZPP@'繞 s秃ptt #3ݜIMMV뽙~VmZQW#̙3ÿRڮJ奙b$4SR\T_ɱ e:tpOѯZ`0ʚ[87nRW'ug@fVDo055ETJ.\׏ݻ%NmAB89A000m[#ݻw+i1LJRU !IlooORUs,.*̬,FMo^M ZѮ];\zsww5rblؘ$H{{Ofi\v R U4-df>o_>c'^2zVZN (!K>ΓQXrn'&Mƻ>@T- ž<}|qsG$&%K#kl())aW3ISػ M7Gp1I=u;UhB/lMaa ʚo ~ƥ ²{u̯xMw:bM Y'f](IOtZ{\Ȋeٺy#FFF|̓E֛Cֈ0ڵm˖_K_|'z;'%5_.!l:s%%}fzp ޞ ;vFSPPȦ Xr&aTG8N:͑Ǚ9A]XK6mXǝ=E7Liedo‚{\ri>>rE߸Qow4>ƾF3Rqr󰱱fTo#G4kV0n;qqV-K/bdd)ƼϹs1-bb5kVhaDNU++ =kIkl=yJ]ǢeK<'Tyf$jDov0Unj9'5_ [ĝ;9m#SekYWѪ=NԌn#+ٸ|WBΆ1 gְ!W L/ܽ73ش9>HaQ]z>M M6Mj:8p >YkK_tyIGz-D)\Ǻ`֯w/lg %^lm[3m,JJKqޭQgϞ%X4-j Q뵺*aC0l׍*8^t VWk9$AS.!lZ@(K)\5=g0!Miq߀`bOWW '@ EgCCGb F.]( EvurБi7Q,l͟2B0n~ 7 @ࠚե Z 4:bO= s:9IENDB`sqlkit-0.9.5/doc/html/_static/imgpreview.js0000644000175000017500000000270311530472251020247 0ustar sandrosandro/* * imgPreview jQuery plugin * Copyright (c) 2009 James Padolsey * j@qd9.co.uk | http://james.padolsey.com * Dual licensed under MIT and GPL. * Updated: 09/02/09 * @author James Padolsey * @version 0.22 */ (function(c){c.expr[':'].linkingToImage=function(a,g,e){return!!(c(a).attr(e[3])&&c(a).attr(e[3]).match(/\.(gif|jpe?g|png|bmp)$/i))};c.fn.imgPreview=function(j){var b=c.extend({imgCSS:{},distanceFromCursor:{top:10,left:10},preloadImages:true,onShow:function(){},onHide:function(){},onLoad:function(){},containerID:'imgPreviewContainer',containerLoadingClass:'loading',thumbPrefix:'',srcAttr:'href'},j),d=c('

').attr('id',b.containerID).append('').hide().css('position','absolute').appendTo('body'),f=c('img',d).css(b.imgCSS),h=this.filter(':linkingToImage('+b.srcAttr+')');function i(a){return a.replace(/(\/?)([^\/]+)$/,'$1'+b.thumbPrefix+'$2')}if(b.preloadImages){(function(a){var g=new Image(),e=arguments.callee;g.src=i(c(h[a]).attr(b.srcAttr));g.onload=function(){h[a+1]&&e(a+1)}})(0)}h.mousemove(function(a){d.css({top:a.pageY+b.distanceFromCursor.top+'px',left:a.pageX+b.distanceFromCursor.left+'px'})}).hover(function(){var a=this;d.addClass(b.containerLoadingClass).show();f.load(function(){d.removeClass(b.containerLoadingClass);f.show();b.onLoad.call(f[0],a)}).attr('src',i(c(a).attr(b.srcAttr)));b.onShow.call(d[0],a)},function(){d.hide();f.unbind('load').attr('src','').hide();b.onHide.call(d[0],this)});return this}})(jQuery);sqlkit-0.9.5/doc/html/_static/jMenu.jquery.js0000644000175000017500000001417511533706144020477 0ustar sandrosandro/************************************************************************ ************************************************************************* @Name : jMenu - jQuery Plugin @Revison : 1.6 @Date : 12/2010 @Author: Surrel Mickael (www.myjqueryplugins.com - www.msconcept.fr) - Croissance Net (www.croissance-net.com) @Support: FF, IE7, IE8, MAC Firefox, MAC Safari @License : Open Source - MIT License : http://www.opensource.org/licenses/mit-license.php ************************************************************************** *************************************************************************/ /** @ IsHovered Plugin @ Thanks to Chad Smith fr his isHovered Plugin @ source : http://mktgdept.com/jquery-ishovered **/ ;(function(b,c){b('*').hover(function(){b(this).data(c,1)},function(){b(this).data(c,0)}).data(c,0);b[c]=function(a){return b(a)[c]()};b.fn[c]=function(a){a=0;b(this).each(function(){a+=b(this).data(c)});return a>0}})(jQuery,'isHovered'); /** jMenu Plugin **/ (function($){ $.jMenu = { /**************/ /** OPTIONS **/ /**************/ defaults: { ulWidth : 'auto', absoluteTop : 30, absoluteLeft : 0, effects : { effectSpeedOpen : 350, effectSpeedClose : 350, effectTypeOpen : 'slide', effectTypeClose : 'slide', effectOpen : 'linear', effectClose : 'linear' }, TimeBeforeOpening : 200, TimeBeforeClosing : 200, animatedText : false, paddingLeft: 7 }, /*****************/ /** Init Method **/ /*****************/ init:function(options){ /* vars **/ opts = $.extend({}, $.jMenu.defaults, options); $("#jMenu a:not(.fNiv)").each(function(){ var $thisChild = $(this); /* Add css - arrow right */ if($.jMenu._IsParent($thisChild)) $thisChild.addClass('isParent'); /* Add the animation on hover **/ if(opts.animatedText) $.jMenu._animateText($thisChild); /* Actions on hover */ $thisChild.bind({ mouseover:function(){ $.jMenu._hide($thisChild); $.jMenu._showNextChild($thisChild); } }); }); /* Actions on parents links */ $('#jMenu li a.fNiv').bind({ mouseover:function(){ var $this = $(this); var $child = $this.next(); ULWidth = $.jMenu._returnUlWidth($this); $.jMenu._closeList($("#jMenu ul")); if($child.is(':hidden')) $.jMenu._showFirstChild($this); } }); /* Close all when mouse leaves */ $('#jMenu').bind({ mouseleave : function(){ setTimeout(function(){$.jMenu._closeAll();},opts.TimeBeforeClosing); } }); }, /**************************** ***************************** jMenu Methods Below ***************************** ****************************/ /** Show the First Child Lists **/ _showFirstChild:function(el){ if($.jMenu._IsParent(el)) { var SecondList = el.next(); if(SecondList.is(":hidden")) { var position = el.position(); SecondList .css({ top : position.top + opts.absoluteTop, left : position.left + opts.absoluteLeft, width : ULWidth }) .children().css({ width: ULWidth }); $.jMenu._show(SecondList); } } else return false; }, /** Show all others Child lists except the first list **/ _showNextChild:function(el){ if($.jMenu._IsParent(el)) { var ChildList = el.next(); if(ChildList.is(":hidden")) { var position = el.position(); ChildList .css({ top : position.top, left : position.left + ULWidth, width : ULWidth }) .children().css({ width:ULWidth }); $.jMenu._show(ChildList); } } else return false; }, /**************************************/ /** Short Methods - Generals actions **/ /**************************************/ _hide:function(el){ if($.jMenu._IsParent(el) && !el.next().is(':hidden')) $.jMenu._closeList(el.next()); else if(($.jMenu._IsParent(el) && el.next().is(':hidden')) || !$.jMenu._IsParent(el)) $.jMenu._closeList(el.parent().parent().find('ul')); else return false; }, _show:function(el) { switch(opts.effects.effectTypeOpen) { case 'slide': el.stop(true, true).delay(opts.TimeBeforeOpening).slideDown(opts.effects.effectSpeedOpen, opts.effects.effectOpen); break; case 'fade': el.stop(true, true).delay(opts.TimeBeforeOpening).fadeIn(opts.effects.effectSpeedOpen, opts.effects.effectOpen); break; default : el.stop(true, true).delay(opts.TimeBeforeOpening).show(opts.effects.effectSpeedOpen, opts.effects.effectOpen); } }, _closeList:function(el) { switch(opts.effects.effectTypeClose) { case 'slide': el.slideUp(opts.effects.effectSpeedClose, opts.effects.effectClose); break; case 'fade': el.fadeOut(opts.effects.effectSpeedClose, opts.effects.effectClose); break; default : el.hide(opts.effects.effectSpeedClose, opts.effects.effectClose); } }, _closeAll:function(){ if(!$('#jMenu').isHovered()) { $('#jMenu ul').each(function(){ $.jMenu._closeList($(this)); }); } }, _IsParent:function(el) { if(el.next().is('ul')) return true; else return false; }, _returnUlWidth:function(el) { switch(opts.ulWidth) { case "auto" : ULWidth = parseInt(el.parent().outerWidth()); break; default : ULWidth = parseInt(opts.ulWidth); } return ULWidth; }, _animateText:function(el) { var paddingInit = parseInt(el.css('padding-left')); el.hover(function(){ $(this) .stop(true,true) .animate({ paddingLeft: paddingInit + opts.paddingLeft }, 100); }, function(){ $(this) .stop(true,true) .animate({ paddingLeft:paddingInit }, 100); }); }, _isReadable:function(){ if($("a.fNiv").length > 0) return true; else return false; }, _error:function(){ alert('Please, check you have the \'.fNiv\' class on your first level links.'); } }; jQuery.fn.jMenu = function(options){ if($.jMenu._isReadable()) $.jMenu.init(options); else $.jMenu._error(); }; })(jQuery); sqlkit-0.9.5/doc/html/_static/sqlkit.css0000644000175000017500000003512111534024230017546 0ustar sandrosandro/** * Alternate Sphinx design * Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl. * modified by Sandro Dentella per ReteIsi */ html, body { height: 98% } #wrap { background: none repeat scroll 0 0 #FFFFFF; margin: 0px auto; max-width: 1024px; min-width: 800px; position: relative; text-align: left; min-height: 100%; margin-top: -10px } /* #wrap { */ /* min-height: 100% */ /* } */ #document { overflow: auto; padding-bottom: 50px } /* must be same height as the footer */ #footer { position: relative; margin-top: -50px; /* negative value of footer height */ height: 50px; clear: both } /*Opera Fix*/ body:before { content: ""; height: 100%; float: left; width: 0; margin-top: -32767px } body { font-family: "Lucida Grande", "Lucida Sans Unicode", "Geneva", "Verdana", sans-serif; font-size: 13px; letter-spacing: -0.01em; line-height: 130%; text-align: center; /*background-color: #AFC1C4; */ /* background-color: #e9d56c; */ background-color: #fff; color: #333333; padding: 0; /* border: 1px solid #aaa; */ /* margin: 0px 80px 0px 80px; */ /* min-width: 740px */ } .logo { margin: 10px 10px 20px 10px } section h1 { background-color: #FDFAF3; font-size: 180%; font-weight: bold } .section h2, h2 { font-size: 150%; font-weight: normal } .section h1, .section h2, h1, h2 { font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; border-color: #E2DCC8; border-style: solid; border-width: 0pt 0pt 1px } .section h1, .section h2, .section h3, h1, h2, h3 { color: #940000 ; font-weight: bold; } a { color: #ca7900; text-decoration: none } a:hover { color: #2491CF } pre { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 0.95em; letter-spacing: 0.015em; padding: 0.5em; border: 1px solid #ccc; background-color: #f8f8f8 } td.linenos pre { padding: 0; border: 0; background-color: transparent; color: #aaa } table.highlighttable { margin-left: 0.5em } table.highlighttable td { padding: 0 0.5em 0 0.5em } cite, code, tt { font-family: "Consolas", "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 1.05em; letter-spacing: 0.01em; font-style: normal } hr { border: 1px solid #abc; margin: 2em } tt { /* background-color: #f2f2f2; */ /* border-bottom: 1px solid #ddd; */ color: #222; font-weight: bold } tt.descname { background-color: transparent; font-weight: bold; font-size: 1.2em; color: #5454d8; border: 0 } tt.descclassname { background-color: transparent; border: 0 } tt.xref { background-color: transparent; font-weight: bold; border: 0 } a tt { background-color: transparent; font-weight: bold; border: 0; color: #ca7900 } a tt:hover { color: #2491CF } .field-list ul { margin: 0; padding-left: 1em } .field-list p { margin: 0 } dl { margin-bottom: 15px } dd p { margin-top: 0px } dd ul, dd table { margin-bottom: 10px } dd { margin-top: 3px; margin-bottom: 10px; margin-left: 30px } .refcount { color: #060 } dt:target, .highlight { background-color: #fbe54e } dl.glossary dt { font-weight: bold; font-size: 1.1em } pre { line-height: 120% } pre a { /* color: inherit; */ text-decoration: underline } .first { margin-top: 0 !important } div.document { background-color: white; text-align: left; background-image: url(contents.png); background-repeat: repeat-x } /* div.documentwrapper { width: 100%; } */ div.clearer { clear: both } div.related h3 { display: none } div.related ul { background-image: url(navigation.png); height: 2em; list-style: none; border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; margin: 0; padding-left: 10px } div.related ul li { margin: 0; padding: 0; height: 2em; float: left } div.related ul li.right { float: right; margin-right: 5px } div.related ul li a { margin: 0; padding: 0 5px 0 5px; line-height: 1.75em; color: #ca7900 } div.related ul li a:hover { color: #3CA8E7 } div.body { margin: 0; padding: 0.5em 20px 20px 3px } div.bodywrapper { margin: 0 240px 0 0; border-right: 1px solid #ccc } div.body a { /* text-decoration: underline */ } div.sphinxsidebar { margin: 0; padding: 0.5em 15px 15px 30px; width: 210px; float: right; text-align: left; /* margin-left: -100%; */ } div.sphinxsidebar h4, div.sphinxsidebar h3 { margin: 1em 0 0.5em 0; font-size: 0.9em; padding: 0.1em 0 0.1em 0.5em; color: #940000; border: 1px solid #940000; background-color: #f0ae4a } div.sphinxsidebar ul ul { padding-left: 1.5em; margin-top: 7px; list-style: none; padding: 0; line-height: 130% } div.sphinxsidebar ul ul { list-style: square; margin-left: 20px } p { margin: 0.8em 0 0.5em 0 } p.rubric { font-weight: bold } h1 { margin: 0; padding: 0.7em 0 0.3em 0; font-size: 1.5em; /* color: #11557C */ } h2 { margin: 1.3em 0 0.2em 0; font-size: 1.35em; padding: 0 } h3 { margin: 1em 0 -0.3em 0; font-size: 1.2em } h1 a, h2 a, h3 a, h4 a, h5 a, h6 a { color: #940000; text-decoration: none } h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor { display: none; margin: 0 0 0 0.3em; padding: 0 0.2em 0 0.2em; color: #aaa !important } h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { display: inline } h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover, h5 a.anchor:hover, h6 a.anchor:hover { color: #777; background-color: #eee } table { border-collapse: collapse; /* margin: 0 -0.5em 0 -0.5em */ } table td, table th { padding: 0.2em 0.5em 0.2em 0.5em } div.footer { background-color: #f0ae4a; color: #86989B; /* padding: 3px 8px 3px 0; */ /* clear: both; */ /* font-size: 0.8em; */ /* text-align: right; */ /* position:absolute; */ /* bottom: 0; */ /* width: 100% */ } div.footer a { color: #86989B; text-decoration: underline } div.pagination { margin-top: 2em; padding-top: 0.5em; border-top: 1px solid black; text-align: center } div.sphinxsidebar ul.toc { margin: 1em 0 1em 0; padding: 0 0 0 0.5em; list-style: none } div.sphinxsidebar ul.toc li { margin: 0.5em 0 0.5em 0; font-size: 0.9em; line-height: 130% } div.sphinxsidebar ul.toc li p { margin: 0; padding: 0 } div.sphinxsidebar ul.toc ul { margin: 0.2em 0 0.2em 0; padding: 0 0 0 1.8em } div.sphinxsidebar ul.toc ul li { padding: 0 } div.admonition, div.warning { font-size: 0.9em; margin: 1em 40px 10px 40px; border: 1px solid #86989B; background-color: #fffdea } div.admonition p, div.warning p { margin: 0.5em 1em 0.5em 1em; padding: 0 } div.admonition pre, div.warning pre { margin: 0.4em 1em 0.4em 1em } div.admonition p.admonition-title, div.warning p.admonition-title { margin: 0; padding: 0.1em 0 0.1em 0.5em; color: white; border-bottom: 1px solid #86989B; font-weight: bold; background-color: #fe6f20 } div.warning { border: 1px solid #940000 } div.warning p.admonition-title { background-color: #CF0000; border-bottom-color: #940000 } div.admonition ul, div.admonition ol, div.warning ul, div.warning ol { margin: 0.1em 0.5em 0.5em 3em; padding: 0 } div.versioninfo { margin: 1em 0 0 0; border: 1px solid #ccc; background-color: #DDEAF0; padding: 8px; line-height: 1.3em; font-size: 0.9em } a.headerlink { color: #c60f0f !important; font-size: 1em; margin-left: 6px; padding: 0 4px 0 4px; text-decoration: none !important; visibility: hidden } h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink { visibility: visible } a.headerlink:hover { background-color: #ccc; color: white !important } table.indextable td { text-align: left; vertical-align: top } table.indextable dl, table.indextable dd { margin-top: 0; margin-bottom: 0 } table.indextable tr.pcap { height: 10px } table.indextable tr.cap { margin-top: 10px; background-color: #f2f2f2 } img.toggler { margin-right: 3px; margin-top: 3px; cursor: pointer } form.pfform { margin: 10px 0 20px 0 } table.contentstable { width: 90% } table.contentstable p.biglink { line-height: 150% } a.biglink { font-size: 1.3em } span.linkdescr { font-style: italic; padding-top: 5px; font-size: 90% } ul.search { margin: 10px 0 0 20px; padding: 0 } ul.search li { padding: 5px 0 5px 20px; background-image: url(file.png); background-repeat: no-repeat; background-position: 0 7px } ul.search li a { font-weight: bold } ul.search li div.context { color: #888; margin: 2px 0 0 30px; text-align: left } ul.keywordmatches li.goodmatch a { font-weight: bold } div.sidebar { margin-left: 1em; margin-top: 1em; border: 1px solid orange; padding: 1em; background-color: #FFFFCC; width: 40%; float: right; margin-bottom: 20px } div.highlight-python { display: inline-block } /* Menu */ .horizontal_menu ul { list-style-type: none; margin: 0pt; padding: 0pt; z-index: 100 } /* All' li principale metto un altezza piu piccola e il fon a bold */ .horizontal_menu .mainli { line-height: 13px; font-weight: bold; -moz-border-radius-topleft: 4px; background: url(/static/img/color_tabs_left.gif) no-repeat scroll left top #fff; color: #FFF; float: left; letter-spacing: 1px; padding: 0 0 1px 3px; text-decoration: none } /* settaggi x tt le voci di menu */ .horizontal_menu ul li { display: inline; float: left; line-height: 21px; padding: 0px; position: relative; margin-right: 4px; font-weight: normal } .horizontal_menu ul li a { color: #ffffff; display: block; padding: 1px 8px; text-decoration: none } /* Colore dei sottomenu quando ci passo sopra */ .horizontal_menu .mainli ul li a:hover { background-color: #f0e84a } .horizontal_menu ul li a.active { /* background-color: #e6fc1e; */ background-color: #f0e84a } /* Settaggi per menu principale, quello sempre visibile */ .horizontal_menu .mainfoldericon { background: #f89c22 url(/static/img/color_tabs_left.gif) no-repeat left top; -moz-border-radius-topleft: 4px; float: left; color: #ffffff; padding: 0 0 1px 3px; text-decoration: none; letter-spacing: 1px } .horizontal_menu .mainfoldericon span { float: left; display: block; background: transparent url(/static/img/color_tabs_right.gif) no-repeat right top; padding: 4px 9px 2px 6px } /* Colore del menu principale quando ci passo sopra */ .horizontal_menu ul li a:hover { background-color: #f8c929 } /* Settaggi vari per corretta visualizzazione sottomenu */ .horizontal_menu ul li ul { display: block; left: 0pt; position: absolute; top: 1em; visibility: hidden; float: none } .horizontal_menu ul li ul li { display: list-item; float: none } .horizontal_menu ul li ul li ul { left: 159px; top: 0pt } .doc_copyright { color: #fff } .slogan { padding: 0px 30px 1px 1px } #description { margin: 15px 0px 0 0; position: absolute; right: 0; top: 45px; width: 250px; font-style: italic } #description a { color: #333 } .news { /* text-align: center; */ margin-bottom: 10px; background-color: #eee; /* border-width: 0ps 1px 0px 1px; */ border-color: #ddd; padding: 5px 5px 5px 5px; border-color: #333; border-top-width: 1px; border-bottom-width: 1px } .slide h2 { -moz-border-radius-topleft: 4px; -moz-border-radius-topright: 4px; background: none repeat scroll 0 0 #DAD7D3; margin: 0; padding: 8px 1px 8px 8px; color: #333; font-size: 24px; line-height: 20px; margin: 0 0 8px } h1, h2, h3, h4, h5, h6 { font-weight: normal } h4 { font-weight: normal; font-size: 110%; border-color: #333333; border-style: solid; border-width: 0pt 0pt 0px } .slide { min-height: 300px; /* font-family:"Trebuchet MS",Arial,Helvetica,sans-serif; */ font-weight: normal; color: #333 } .text { width: 400px; /* display: inline; */ /* float: left; */ margin-left: 8px; margin-right: 8px; position: relative; font-size: 15px } .img { padding: 8px 5px; right: 0; text-align: center; top: 0; width: 350px; margin-right: 0; display: inline; float: right; margin-left: 8px } h1 { font-size: 150%; font-size: 26px; padding: 8px 0 2px; margin-bottom: 25px } /* .slide { */ /* padding-left: 20px */ /* } */ /* .slide ul { */ /* list-style: disc outside none; */ /* list-style: disc; */ /* margin: 0 0 0 25px; */ /* } */ /* .slide ul li { */ /* display: list-item; */ /* } */ /* .slide { padding: 10px 30px; } */ .anythingSlider .slide .text ul { list-style: disc; margin: 0 0 0 25px } .anythingSlider .slide .text ul li { display: list-item } .promo { float: left; font-size: 13px; margin: 12px -20px 20px 0; overflow: hidden; width: 770px; color: #a91819; } .box { /* color: #a91819; */ /* color: #FFFFFF; */ float: left; /* height: 118px; */ margin: 0 19px 0 0; padding: 9px 0 0 10px; /* background-color: #ddd; */ -moz-border-radius: 8px; background: url("../_static/sfondo-box.png") no-repeat scroll 0 0 transparent; } .promo h1 { color: #fff; } .promo p { color: #333; } .promo a { color: #fff; } .box1 { width: 330px; } .box2 { width: 350px; } .box h1 sname { color: #a91819; } .box sname { color: #a91819; } .slider-text { color: #fff; } sqlkit-0.9.5/doc/html/_static/sphinx.png0000644000175000017500000012277211530470276017570 0ustar sandrosandroPNG  IHDRs+hsBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org< IDATxwxdw}}6Ψj`\0f0%@ <@^jx@L 7pz޾*U׌ϜqfѮՆ}]S)^}meY>>>>>>>>>>Dm"[[= _H.QZմ4kB<6>>BhtaeYmz>(z{_$>Q>>>>>>> ji[]l.>>>>>>>M~/+]'jVըVmhaDbI g_\.SU1 P8L8lŚ_gJi~gP+mXRP.)T*j:-mmmDǨgseBB|\bLM0-Md(hbm8n:y iFbD""'*94fa Frţ-(- ,,A(EQhp)AAA) dboY 0ܟf ;wl!Hxk\Zh4J B!h :>>>>>>>uBryo7d,YeʥJjF< H[0 FOq("uu[8:R7A7pF G4Zx|Υt K˾J K2e" -]\{vl;i/,f)x]wy!{[Q"Vߛ@Mw#vcc WW"XYl-w[ͯnwl2jp8BGGLDCiOLx 㨚T]QwdL!dƝ]Ql$4UQ+"YYD[%QRxT"J"%t]Qu9LQ\tEBܰPuLtG5 pK[iT*v;*80v ӓn,sj(4]A|$Ig8pڈD"ȲB':>KF's~,\譼}z^>>>>>>YH61͓v56Oivf*H3K+G ^qb&=xk\.'nmz>>>>>>IHb^T*Q,j躾 Z:!I2Ph$BWWMbfv`:1I{-BͮS,$H!/l ǐ"ОM{'E82iYVSjFRZRun$KȒ؛('IڈVN(KMLsI`jdJG@RMtWT&e!X&`W+#]RE;2`A dГn=uiO:;F#(娪388Hoo/h׊=_/=lm-f>WSx0[;_$Y<X_t0V333CXl^ʒC HH$,Eʉѓ~1EZ\>{ɗj-HxʚzN6ellqjO}Su|y&8>zGɗT&5մ7mP1d -VEut]Gqh v&Ük]:4Q4,lE;g "v `( @P.T$TUsܜƙ؍{p$Lgg==5]gt|C9p$SYjVmhL11 2Wʠb p0H4&Ce0T0;<a0\q٣x S*8~8[n%x}c6[n22+,gz_?}7^w>~ >7Gkk:inLTiܼ-ZL 40 4Y\,29=,f)kI]ivKqAr dBIDvģ(TD'\- ,ǭ]1mCd]M'1554|(]#hd2IX4J(&T:/wYW} WiW`&3LMNbYl'C{{;;1<3B'l UU[դ [vMĥIBѫL7j躁aYdD_ݳ'^}x I( $ɳv%[Dd0 weLY>>>>>>>+VmC2cSg߆'pR=E2XEdI$HbpP{&Dqߵ۷08Ћe&''ba! XM[uNww74mm.bl,"rBBT9Ʊ_w;2(;tқ"I4%200+z٬'*Jb\ I: }|cVk<|$3YeAMk-,5[iUaan!:ܻ}iYibGR(b}/Oxcԩ D@ !G\q{6ֶ'R[͓^iL<{fCk<Yshii +KBr6WXV!i2.yW"T `9}A<ʲ"K Hvp{x0ٹ} M8;7I(Mc,Bub3;C:,\&grLLg99Hj%b"%b"`L<Άqڮt=Q*&rV3$dӕ`.z2]4,@ @(>9H&tvudbg;sa#:>΁#fTU$(5DP*)M.x2rEȣu!y ÞӻWFVܷkKWG}\]C\+V4jN]թ[]j 5DQ33-`&E$/gi?Ή,ӋLHDCv%x[yUEg^h }c?th Ou7{+poNбiƧZ%œ-͍מϕmYU:ɭ9"_0ȟ>uĩ,|X$H:Ko3>?__aif%N=Now<<3 e-{sjeAW[m"K׽Z(WU/H_8>stu˪}'s3s{w~y徯~nֽ#59 x]WRZjUnZDǢ$(!E" %"HP"IB\{ {w mx``ɱ)ʜ8ɩ UUggLLOMRrbGf/ Ք)aX"$-D̆x]#vVA4(H#,E/w-&w2KGGyffff躎(B!"WS.8pH$B[[]]]tuu _d ӳLN115S3LLSNJ)2̥'$Y PP! ՞=ET"!D"+(rs/|EkZ$o|tG`@>a;2ž#S|K./^pņ6-Cg**[ý3>&XuޖBri͍Uoooy>SRϺz=Bogc'V싆BwEa~~ױ 䟾t+7^w>SU=׵#u ,A;7MY|w__xz~*Ϟ{9/V}zǪW֏hkw]^|lמǿz kY}W_2WC^+ysžhB=Ƨ"HӹILqt|R͢nJ Ռ%۹SsA`" Z UC2%L'ӦQYˮ"RD-BIHTsuSD7rz0tY0PDdL'ՃD{ؽ}[bb1V(U/MR.,0=5kH$J,%HLc;Iwwk0rr9"eٹ>Ӏtm1_x ~O˷VH=`@&H\B(wo /~W;.޺ ɍ Վu:6HʲEUׂ$oYOM/>oWz5 O7'VmoKo:r r5uI0LsTU#  ZND@ $BPP/6v u-C-9Ha011'ODUKe$ z#N7 r$GG&982bՐs- ݲnA`7LdBM"E[I˱lÐ,8`qoLDA@ bAB7NRg&WF5DTSD3$4K@7 SDHݐ&8R'X3p{>EDٻsۆE 144͑f)JȲ칕@KYpخel6iA$!cEwֶ=o[$=Au*Z UUUTN foqɤה|=6xQ_?ik^o38 *"]:O}Gy;]_M^tr}"jFg{2B6_i#$mU5CQNrpdB.{^U_ren>^Kּ[׭y Xli IDATar,v0MQhY%4F:N#$7H=r56# |KfK ![Ch+/SOu|Ͻ5U|E_jz>>>>cUneC!CuJ&I%$qRI ǥ^3554SSz-dDInr"F&8tOQtՐ!;\mJ薈]m`htIP2-$lmc.zȩ2!KF@-*j!x8Slc(! `'Ǣad;/0 fMs||g)֠fu[XꦀfH`Ky= f >cӞTSVY\\X,R.4s*eYF$NKW(=-Ae`0H" J5UifϾfB;mClO8&cM4 |>O>_\.Q-0 6Mt7Ӳ0t]&e"2DrL<f9O<|6j~$OgyӋ+nI\`]hK!G/yxiY;KH&RYiFӉ.g-9^w_Ѧl%v̤^?|g{y1۶w?tVd#nϚIWH2 zBHXX,R,(E "WVgpp?\'9x$NLStW8Ɏ([N ڮ于p J&!ٴţbm7ԘcY.@.H6%RTGc 8am֥6%101 sEt  e)FI&S>f1:1CG&)u?5]@dQDEIP6 t&'6 e馭-E{{;%iFV^y$I#QtC֍!< Br!IxG>s;7>cSޖZU.vu+ww}#EWn\6ۍ@8p57(Nˁ3 ~YWhLuYb!/01_O oܖ-kC[c-/͚5s 1:a&4[(6ֹ\4nݴ5 M?c'9ptɹ"5] xѝ&# : $ AP!$D1m}O[j)dY S,1 p8D  twSNHKF@2Q$؋7 J'dLArtwu288$IB!LӤVQ*طo˴uw3<`ze},VLHU"5jȂ,  LY (?"Y$Azכ&Fx|}i""mmmr9$ps$FP;`l*F<2 {_.qVVL}7^{^K!yxlngΏֹ.AM~ vshY]y˿Ƴr[φ6:Bw:^o_wseE>c&5;toNizZi^3ň:hp$B*)]gzvI)uGC@5@5{: KdH&AB+-ŠE{.8yͫ3J"&g;54cTj5ģ-".E2(vZ6*!mc~vn'nn~癝#ϣk:]=Nw{9PEQe˲( EY'?DKl ?dm$ Q{6Ҝp+HQVT*OTK%;]]\\kQjJjnhH]=Fh!`!uD!$؏E{ (? 0Lt4L")j>r=wF>'Jٸ|7֊}qpdٍ MV6\kOFVmS,i?CX]n z.֍V}7~ xxM,h;W=3hdO}n^Ww}gMQdOsHD?Aǧ51\ZFXT*S(ڎbBi*u:/9)`v3p˙ #( 񨘄1#2fس}m[76 9CYD"tuvFD"^:v8VUU0B6k! (I$$KbVBjhD@B`Ж 9nu+hD"b&QYV)  "|c!;xu\qQ8{ɤ;BJ9sj5Bh& 5IF:n;- PY:Ht*ElJ #EB$ jEnT*y1ouGp8L<\JBTX,/XcllT2 =߽woeP}GޣL̗PQ-[T.`lebao3rv:eXG40 ˲d'&?9@?Xs%YWolk;y)o{yGy?ϻSu aYPWW:LMt ɰuVROb{96z;;(TL*v[%8N!reڗ8Mqn (*?觯GR,F+;Gt_֏S9lA<]R(UVs^ϹmH|j]WGJUsy;qf|pZ~t!n[ZzRg㳜5; \`B\jbZN)+wdHH$P@$S϶-v|,BZT(ɬY˲R6]L;.X:Ih{GR<*잠mdY_~#7h . ¡5DZYW~x?w?t&E9eM#]?P`#XTUj;+(RDbaBѠDw{;1<~F' YXR*q8tH$9` Y^$IagRO8|8vl㏮1   q-:Z DK'hՈR "5{4cccuϜn WX' /2ϳH6}dΝttsr2~j9/>l^rIt^ `'RDADA!E$% Oyҵ011A\&z-ȥO)/;mp#Ctx˯ٴsYw]qh˦,O}#sHnv|yFB } x_-j|˅ l W?^U? wIޱg#mWv y[9cȶ B$OxعmΎN8C-.FIRR)ڈDQp:a"I`d2Igg'rۓ;hwe)Z&u!.ܑ`Ȩ(Hґ:Ϲ2[O~YW]Vyi_>7{7Mt(C֙]ɑTZ(JC|=˜]\Uy;7?x[N?sC^gbnnz|{Y17 \q[z|cU4"B( #XUB\4Ox,PMӘ'C"X5}&k&צIUUjZJBZò`Ϟ=ܹpخ-uC~,,VLjfzo N3#}p@$$:R.>;];*@q 1 G O oW[|\Dkag8F0L>Z/K-yZžKIY8|6ժ*f:|=ϗ+-Wx~o~&27_J-o~U|փk43}|||Άu6V;DDALGclAiXP,L,InYX9@${ؾu-룯CZ* ϓfN#HˊVyy`T*ά355q Rz?6\ d\!)[ubnc-,,,099IWW^X{=4{WGE{b:'86E.8DnQLj biϢl2!l/d]2==M6EE}b$I7\yޓ.ಽj~z8G>ţvsSyAZ^!Wӵ{v/b_Jʑlr02|񵖍w~EDB+C.?ɯ*" /CmVޱb7l;~'~(+)ȉ) e հ; H=mR(AYer c{XPbx(ö-l ywZ\.bEi@knX|y%RV@n[E262 ;vlp(.w5w%ejrx< b1jQ(nt0i+G9yXVH$ٵk' `c~FPRuvʁ膀ae:|]P$$DPOO|E\;ZQh4Joo/h<\nVZm?}7S>.Wr\syŭ0C#ʮiK7=-+s7(|žM]3q7c-?g6\7 )d|jnOQ פi9SoL& ",m+es&(`of-μl;\STrr9hԫ^^ݽ&L,x2N^PWW+D_۶]{y~v8@*4R)Ț,8nh{[KۧpXW68ڻwqs&D U)7/'o i 󦋖/QRhXY5:RrV<)7TǞ\]j)#c+sÛW&ɣiZL6*2mN<GA<'J̫{vލi#aj7DM<ʩ'ǩ'#%ظm//l˦>2$gK .`;(d,*LP B~C2f .ŜY3 y'L,r9@ G*=Ĝw`4M 4<^l6afk/hX}d6͗I E$?0Ě y~n6iC!eȘ S!kIL ,v>ݛh&@W!C/.ͥNCkjMtb`@ @}^eb%dKR2XU.'];4}"YLC:z954dAH-;j{5nyjcA>2RJq}cgnUKP*b*LHzI}>X 4 u͌ cczjBMHg[3mͼ v ;[m Tق[$UѭG g0wf3E#ypd2I:&0rUPQ\?PUUQ5 G9 Q.PnnLN:@X,N$/Dؼg'ARXiOdm>YΓ>BT\ E391yƒcc1ۉb5C lz9qݥ+*r5+9ɿ/V"GQnp>sNޭ\ڋO2 ?OGJ>HMBɷEm,x*{`r~q2j*b:veOeuSѼLt:M*ݍmDkk+Xf鱳yswk6ᙍ80%gC.o8 G`*9G@W%~M7M04s4ֆVx|ZebY[_ҕ8-kO EGUMUEU5TUAQTTME/RyS!ɲ=ر}IJXQҖBrIi' EHtu뒘:"“Xv֭["3glx!sۗ:xH`fCe 5iSIժbEpav _ŌeƒW~,.Vz, w<3,ɋ;.H9~e߾w)>x!hT"`MUъ|-_*XX*H1Wp1SlV1rJRx%O}t׊rD'&q)5,匓4aǎt{3gR,-#&LvԷHVB4EGf}%˱P:HUeu{ʮ3,>8yG%l=7Ao}%$3xlMWv\v"ښ&?PWQEUL轗mbfKGRJ&Yz5>k@[[m3hmK;Qmsټg˓J K*JwJIJC?ZDs^np+,ct m-O=92>ڀMC}활غu+RJL ^ *m=ⲋN[0v* 5SVXGPʱb_4Ԫ㑴[joWsէY1_kJ3mII(v|w)zҲl;L}j;< Nc*qԕS,wW4, ۶ QϞ默X&)vƍDN,9u|9-Y;M0!5,|8h8RtT,CA  (WEl w(O#^L #)E&8.qnTe,ue~SU p@-lU"}Bm6>1۶Yvccc44 kkkK_N%y-_3'.j?L?<]vىǵ~cSLerQI%|1LHa; hyګܺܟ%'GO$Ƈbzs7?I}?]8:(})8a  qYuTVô^r"YI{1d%Rt$}XD\# hkW旌M.}YQ$39O|O}5$0,w˧}wFw{㪢*xщDLLHT (!^5ϟrl,I*b`={v(MĬFf7r0:f^gCW`cFο˼*$/7*@Xo "O&'I_VS9O)K)EJn: o}YX_M$芉_X(&wlA!p845֑L&Yz 5;dq—۔]PMmZ674{`o{ \z1AJxf^uÓ"^=,]zHm/gD^ڸ)|1"֧!<}(GSۛ Wtro| 5ͯ\d.W[7/ ;投KQc*bLHi^P+m.!TXի{,:[hoA<┥9ella0ر LBF#TWK H`Sc2oܖA!]QH;88XLDbW BZ%39,;ٝ-t&ehF!!sNwӃlݶ8mmm%_'&L&woy(;Xu|fͨ.Bs]!o(EP]}fz*w!kJ,#*E%"v(1FGyASf'asDĒQ"WHI*;dSHPx~מ},#cY>{'-9}zJT]t.o鷟ß\ɋ;ʦ*(&ńѫ=O*Ax(u'PO4`FR# l^كaajhoP0Hkc 5Ɛ$L.z97-ۭMSݗ>|i>zl慭0B:UP"uҳP6ԓO1{ϛW;d>\\nozIIpPis߻Dۉ|3ye'x2ž\@_UTQEUTQ+Sbfa``>ijjdhel޼MO5,Zךk 3Y8.sfϢz$nꖣI&QŽ^u%&y)m'F ! 7<<'˖-t&ˍ=0bd,)I̗֟ g `Æ 44҂O0MEi0|5\ev0,aIaddT*E (G{ޫ*8(mL&0TN:X, -۸"c[7Z" $SlC$K-,TBQB&_,L K) 刢7/(GD,a066mm̙3Ӵwd6UPlb,u,9}+.Ķm֯_O<ښ,'yh`9^jѲrCƀ [hC]] FWdE**╉DS<%3LrgD?l&2Ilre  R((BAU|_4b!ڈ3k6˥MH$#d"dz(g.)pc.q~}ì۝b8HD}9j g,m3V d AZ[[Gu2Lzs3ͼ(C֔$ /3w\v eYDQh**rH$ͱLq֙صge$iZ)AA(njEQ),F"9d|/ozAt]'|%b;' z{{FQK.?SH*#1A?xݪ3شi3ءP!DY"隶e>F Cph1khoc8pS__OMM P_.**x1HWzM$8D"ڽ~fd 1mʴPTE D؎`9^ΔLgϖUha.hhT*E(*ǼZDQݍ_dby= $ [J7Ϥ6ą * ;v0r@8FӴI?"iXy:yqqșDWJn.^si| $Iz{{f^ZWWQEUTQE/-*Nxɒ7!Dzd G>}> Ms+J hqY 9شy3t6bY|A6pGlSD&#%-le l[*-MMdU~~!4V#*dJfddΙ?>K"UU! T.¹+i'B $#5(8.ll&Wض8+xT*UdѢEԐdo`)Cj[-p9m&y%2KRDazzc۸6 " р ,Y[PheY 222RDh-*%dqQƒcX_S&NS]8=]Iܹ۶8pՕ;܆ 䀤pSR!a*%T!cٲ!GF7;pGPp4ECKOa8úu@GG;܎MʞQ[}=e8K$O_PVia\ BH$BMM `Wޮ**&ai0 4T rhnnqxv=Lۑ Maf4yӕGuzzzXz5LT*EcS l,DM PWW 742dd"(\V:{;h1QI[.q$5~헟1gaY/]JcchOsxD0 r9xHM.(t6y5ZkHP(Dcc#Pp8L8& n**Qbڶ,l6K*"RW[(Kitjc!kœ~ HR߿JL9Ă| hPU B8UfϚDIӴWdIy9"9(8aennCHHF2* !]Rph }Jfua/]K(lS"+EJgfAm׋iN?RU$~M4D5U,>v>izj `֬YB.i j**b"4(Ml.3Xq;HtJ8,^t,$g襵z|n'2l4XH> i~ ֐RRLT*Ȝ9xfF~'H F 9%5A9-A~JZd]X4Fkk ?S@UU d2YzDRH@XW +dK-m 3g,^"\\**Ci#Ja`Y|n`Z6 ꄃ: u455aYTPc1ּ>mM ٺs/DHOGs <˗.!+H瓘L&T*1 q< ħDtImc[ $)6lXOMM 3f̠p8\ ЕR;M۶q UhP!s%u6/ĀUL_ $g #Dx +ю=**_I>`.ql7lG"|*AF0ࣹ\.G4-T]1M4}}{ 8fl*wcR:D#IٵkP`}K1qKRȾ>~?+V@UUFFeݎ~҂dN`:~ b I.>8V{ ǎ;hjj#tL&|6n'cXf>FJT SN^Z#tvtL</( =!yb}787݂ҲemUTQEUecd!ciSS|x,&:F J CCC={6 466 G</3u);388Hgg'۸z#&#AG:uF-cmvimmP;Ks}/R)LӤvxv629#$A$`n[ihR@+JBDl2 ¯6dzz'8tOlA/琫*5I4:rHP@UTE)"(  GD"a8͞]8$ * R@SSSBrXr$ Tdo/itR a{2$2%Qp$U45cYk׮CJBHDz;qyQEmFds")7(JS5%L"HBt9"?FD ?FInE&{1K`fpP2ٍR?(㠐Τez2CS `Xkȑ=iBc!Nw?9_hc{ %2 vQ32D7"ʱ}def n,s80Ǟ:c`rt"4mw!ceB[Gz>M34ʌ+!}|WS S(sط ^#5f=9Wrt"X{twupldb" Rd/hY:)V:7hA} 0+H͗T!ppOJx,@DK:=(8dl6K.+O("ňF-Ȧ8O:ftt~c9!-w<} !h*D%\s~ƒI֭]G$mRHoq3%G^t"gXض' c1M7j> k^mz'ɬ?@=.fҶN?8leو`;zؽHen34R`e q0jW/{Cn\Px s?zG8 7LQ7uV}ս#Fdn 팏_xJ U'o?icskm"mliB4,@i<.vb==m^DD|}e֙6'- @QQ jglc67vBOVlzg7H=Cs^qcT*?3[ nGg:c/ԒU7a9_JpGy;Ei;۽~$O\23:7T9-o^O[ˮgQ'_=˒a9~o ꒫üsjF)#MXOoc|sP瞏o:6"3ChN`V5E%?xCz|IK͏ҼeY.rهU빟a/X> Jӱ%d?:c^+C_!~rnn8b?Dy{^ ׂsױ :Qj:QD;(M2{^6ƝzFpl~eƊɫyp =HsE#2o}uLc=q=ξgv/:nNmdo8g+WxN~ߔbQGzQhEU FGK$-*$BiP:f̘̙33g+̙Cgg'--- _rlb^HOOO{졷-u8o{>sO~]:l :9&r ~%~zzAKKK!ѷh:1˹ryH<"icࠫt0<rh'w!mXOwco|%l=̇>c_~8LeBD[vdBy>vspv=~oW+@&r7^osW1g 3C#s0)8~D濎z; .{3su8۰VL˱7aœ\VS.$_|D9ԎCca?) 1x/N Y8SB&]+Ж]:BՁZ k\;9Qsz煈4<uy(us AڨsE~(*?S Ɲ^6kˮgo3n6ܕ9~EshK8ޘ29t?O{K1h'kGz'yOPTԅ1i)R:GF"m 㳰e)کw֧Q:NcE{cm8^SWmhˮE{>`pv>8y;'߿p|eYS]xIa rd7׹sc`w>TҲUS(;shǽ9~K k/q\zs"܀2 ^Ҹps*ZɱAwy rhNJӱ}n#eXOOq>w9*d>sij>IJ)C##躎a bUS(STPQP3{(.H(CCC Yb>Mc,?{J:$2WӯP7 0PeV1&jkk=_Js|CPhguEe(2<}DP cn}J7{_bMޅ~OHbXk~Ltr[?" w_I1q}Y[i>,. o-,s!3Cg$K%wu`0bB1~ obryr7̎ Jy;"Ҍֺr/XBM8QOٮOJ]=R3}|+Pb4oeb;ދ~em"":݋'JۉW؂ۄ/hK|_(\4-Ei]^?a= };=e֚_"{7nHxcWC[v-1N<ʹ7"~u%)p=/Oh~ kL)}pZ+nD=n!؇;/oCr4#doJ|<~z&Տ#A Dl"X q'&C((`^?|s?gw~²JNɱip+ :BDZ]t;guPro-iC"{ n#l̇ pj'@i<{p}d/0G<`e((!u3>D"A0hkm.`Z69"gZ  H$ g.LT@ @0$|/K bollbdd p146o7Ka=Cc&Ƨ8ă3as?O2B!d2jjܛ]{d-9 ˶pODmrd AQdBMSM2":7$P ^TI@ ?o'@);3h}d -POH?nee6QD9#W݄:SЄ $^c IG t]w'sUE#'lW/}9kR;0GD"+q9-' 3<`ƌ%J2ɐdopo8uΙmYHFH,'GFjd1I+WPeo.Y,A;#{0fOgs.+{od!br7LG1(Q$GYxA-rwً͛G|^e̻"ڂ~_/߲0Ж\]T u5(v|i:=Fhg~}g|;-!n'E xsXHJ@snFh?Sً{ӟKXD$q}/*b>+{ϟ~` ]tV&̓Þ"i==7 /KBA;`eKwbos}祑xUչ+n9?:ht'B]xia7T^9wzcQ5$"jf5zꇠP+ ~9agDŇ|/ܦv׊1s5q( p)dx~ƉK4vHf FY7166V0oIy ,&cD^CWW"8̘1˲x'߿?C9鬅"$)bA5v2aMl޲&:;]x<^y8D؇3` jd=cQ_|*sg#`4(>_S۸݈HS`/QѸfM99փy߿ߗ#)ts`:b"9XĞQ xeoF[v+Gq?;y|*0@(dyLwaI b)+6eb3{J-fN&J(] a:gts_mٵֆ*NNaѰu/m8a>ȱRua "#h'm; V;Ya|7"TJ.q7W>/E+~l0kmo-xJnMa IDATQ Bi;ߪ6I>0~ks22R!PdhAu:D2njB08c."S^ Ʒ+w?S@&o ~Vs 5H 10H3O $H$\x.WRb}}h `4Mc˶|7q]u Ip1ô.0ǕG{ M Oɤ5s&455"SQ3 go1HZyH)0`訍eC?ӿvՇ~ 7@#I|&m):)LŦ'oSɲb`O!SnfSQ7-;⹘$n9XMRQN7{VrC;ߏ:\7!.e2g%P}h'/2?Xfp'c,(;4 |?dbyQ>PvŁ1nupl߱r.?i12ep$E(i6jb1N;T"###8/j[jɲ%ٲf 1m̾ K¼I2Ir2/yɛ$ GL $ 1HjZUqm;m[|?]#R|0 i4 X ƐL&ڊls4ÆͯcGDS2*Ғ N#2zYtl_9.cOO/Q i4B!|>+ax'e֑=}(d,|"bQ\ ch_ya+f `歃2 (0K>[٥m~B +1l0߈侫pn&hט(/;hkI2DV.żb 4qO:P\=uGMkQX0JӅMg/B1KAĠ|$>]o,D昚o}M+`2 Pn)o]7cT%O\ [r^)܇X_q?@O_ZZy.s0@t̞qz̒*{焑ΑEVG`W}&Ewsarڎ>b8vۉ.OmDd,Y`0D"L&cv9-,#Df`R``(.y8xOX} @ɺhL[oo {8 Ǯp9QpC>l`M0gn%hN$r*qΥBlSy}CsEc?( pm\) adbк6G.pכb:5~4+GR0&"к^ֵˆh_1~܅SL:+[;F*\Q$@mcsAqgԪ;׃ dZ&h?; mS(I$AG0D4Ҍ]}FȊhbKeP^gLLqggkmx,qS;=Ϻԧs{)C%˲%mN&`YPͳ}m a00~̡D5{l,PD@U ]WAXeD:dd$ Ws<(VPVP6 HZ"HVf tC@KRRl6+y"۶:\)1wC@zr_/X*XP\J$p9)!{Ow6w]0Χ]Vl0&"W$G`MW^E0&~j;qBG K0GKYZ,y\2&P;h,|Hg-ef;)9rS)ۼ,Ra EnH^? b/p短$c6)#rl3/PW =z pk~Z-U7BȬz|ῌ#yԿ;e-{֝$Bumv/M:(T$q9Wu=a#փ/K>Yto^A~x=n+;_˗1)) 1<@x,7+6l܄L&ۍ:ȲD"R)Hc1{("d-tK1`ddA?1wtt`hll>4g{$Fci&eP0s1h | |k7iZRmߎ#>}:0c v>d%IUU0ddkC\< CKSmfBuzV5OE6C<=JcKH7N1vB}ݼ'Rm䈙D1!+$vHO\os v" n2MyǴ_gs.݊ȻqWo"IbrC/Du}\$)vZu"YMTgĖ1[<*F8TBѾI0skYεnRZS%zvYжH=XyW楹Q;^$VUc UxUA|*mkGŹ azHH5TamoxN}xM#>EPsqd_o5+aҏDߒK&R3.@^+;|7;tm#+G(Y"Ѭ Q__Od`{{l'bbRAQqt$眅E 0 $I$Ix<A,Rr@l&$ tzɲ AЀP(g`;K T Y<".>o9._ >èm>E"4 H$v$r8".\Pa\.x^K?X'%*a*5NML-fo[Klqf桚N͉:ݏ-. @gY#J(Ru 9\0Y >"Ny2kI 0y]/nM8\_]bNre MZOrCM!YفIY"i'`wrpuo]C#{Bb,5Esփ|EsCmWA?h"t^"^ka@v@oX_2!N=DZ}>Nŵ]Vh.v ;wI3oz(:VyhW 30&Fn%՘P{{!y$zځW zʢvtyd)ҮQ;0*އJxH䗿GĶN 3NpKhrk ޵dBT_/OЏvB =BEҜ`0,ruz=UV, )< 2Ċ|ĥEQy~"M`؁4/?(Ĥq G10Go6'<ڿaF888$R&&&,Аu}aaf,^sE(سw|w=ų_=Gw4$bIO! уk֞.A8[oxr%XUcPel"2 g._ 0044@ PO,O-r ` :0LHVjOQ. f+sY>˲W[L mrY|r5~ݏYYbW~˪!$JAyh2 .ǣ`ϸ[yK>h #$ TUK` hmgn|ƫC"]յP1sL455Y"?zv( E("96t2 p ~.0 Apߟ&g(ڶ[MdǷMj73[[cJ g sFrTY$ZLKSkHZI2-%Hj%UPh䉞]~o)jCy]|!?K%:ޛh'V#cQd([~ 8Ӫl8?N$Nd=b$ %H2i7fEMX\~ A* V}璕dcx.bV@b-se UE(gn ̼u`N|cU-g$TÜG5?eV G(PU=!|5~ȳMB* jD{!4D`$,:׿`Nz(V-])ۑܧ*s2g]5H_]f ?u ) z’`uv!| H6k|MX5~BGWpu˰G?-$k2T$ I.$'2cF,Zg#"I*~aX1AUIapV6t^tuBOo? Ri 22 Y`Y^ȣU.ǪgbZ#IIR޿DuuhoP(oA~qv׶EbcHq]ӂO[]148fK$x-I[\!I[_D ;6,aS40"{=K.PPya0Yy (]mǾ`@{9ڮF&Mh Wmٳ>X 2^v7]#=?B `ŕ̀p닐%AC1EJ3T@So##87;+;>wW0#9 {:|ů@\Q/E1Yгzՙw`Z$e8WDB gKe6N9$ HFTG^J%#9 mD;T қ4["5Y$WCy;ܳܪj\}h7ԝgꇬ6/\ᦧ!=y!LG7/}7|P&u̽K,̬0 ~+ɭ/lսeǦ%nۈCŬ1.ݏ9Df(vE:+/8>>GΝ{ȱ mq)1rPz?{On= Fd?O@N\m,j_ ΟY|=Kk?1vdz^DҌEuuuua#֬m-x?Ľ??l|TZIgd$&$Ē\{EDi CX46ԣc\2ӧ7YW^WMC"@4xC#>x)8))H$^S׉1E>3O_Ϝ{,ge1&&нX@P__o͌쏣&Hg>G#4{޶J` egLת(H&D"0F#cd$\:Hꖭx$Y`h qx]<0@xhuֵDc)EC#u+%cJAIC04 t)Buj[ zˀEU>z$Zow@NA=zXGXqNR=Џ8q}H$b2966=zcccގUa06Ë^XBӈUeiQ9E.ȳ8 M)"\ ٌpEՠEeU a1DXV,G{[Κq} rGnhY>.Kyݣ(zzzps9 ‚9wۅ={a̞=V<8pDUvaZX08bj DžBcCnj|:lٺ<AUBR48ɾh0 %mHIMסi]sڒEeh< c! ,f4a dQ͝'\Yq>$Sh͗Mܟd B84=+vNoI8phd2%)㱪߈ ˅h4M^B}(S,슟{U;IDATYlXwyمMz b,P5##+@#EQb=Fw CY zq8u<,蘃P}.&t]!v! X%< {ֶxIJ,t] gq`ɢPUBSSSH}8pO'( ,ZLˤY1"Ja֭ؾ]ĢE p|,_˗!X=Gq@bt[hØG2Kbfk3hQ`0Fã`Y>z-)"xjO,=[X,\BNx^444|28p'%L$͗KzGGC&3 ۍ"}zz- ! ];Hf%IB+`񢅸ꊵw}`*x28pc.͝H$H$,B9Nchh==*.uЀ>+HILL1>>88B]]nw6M麶 Dtm%"̈́MSqti{B$^qi;p>ts*= Ǟx HӘ$I)Ģꂦiphԫfh4Àe0, dF,IPU,By˅6026.{2id톆444@uݻT ---6m`8pܚ0eY`L&Y%I" 5+rnX0 >/غ 8%&ti4#˲q4 DH;~VI NLL`׮]`YhjjB0r\8p'D\n3uJMӬcEQR""'c',˂eY4օr=N?$&ľ}PWWӧ>c>8p|(*4M0 κc~&Y&!D.nN>ʼFy<{"[vk8p5>r'@00LQHIKJJL9>>e1sL466" X.}8pGn^!3]ϥaoI2̊E҉4K9;p|Zk]!I8R ð41A088ɓ쏔>2]Yqt|\pI 1rFt|k*IENDB`sqlkit-0.9.5/doc/html/_static/arrow_down.png0000644000175000017500000000031511533706144020423 0ustar sandrosandroPNG  IHDR ǴtEXtSoftwareAdobe ImageReadyqe<oIDATxl 0 C3Cd?AAQkk3`kr7DS4cO3aº-&G[ Aŧ[Ř]H.Qv ]`TGXIENDB`sqlkit-0.9.5/doc/html/_static/file.png0000644000175000017500000000061011317371701017154 0ustar sandrosandroPNG  IHDRabKGD pHYs  tIME  )TIDAT8˭J@Ir('[ "&xYZ X0!i|_@tD] #xjv YNaEi(əy@D&`6PZk$)5%"z.NA#Aba`Vs_3c,2mj [klvy|!Iմy;v "߮a?A7`c^nk?Bg}TЙD# "RD1yER*6MJ3K_Ut8F~IENDB`sqlkit-0.9.5/doc/html/_static/themes/0000755000175000017500000000000011531732665017027 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/themes/classic/0000755000175000017500000000000011531226335020440 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/themes/classic/galleria.classic.css0000644000175000017500000000775311526237604024373 0ustar sandrosandro/* * Galleria Classic Theme * Copyright (c) 2010, Aino * Licensed under the MIT license. */ .galleria-container { position: relative; overflow: hidden; background: #000; } .galleria-container img { -moz-user-select: none; -webkit-user-select: none; -o-user-select: none; } .galleria-stage { position: absolute; top: 10px; bottom: 60px; left: 10px; right: 10px; overflow:hidden; } .galleria-thumbnails-container { height: 50px; bottom: 0; position: absolute; left: 10px; right: 10px; z-index: 2; } .galleria-carousel .galleria-thumbnails-list { margin-left: 30px; margin-right: 30px; } .galleria-thumbnails .galleria-image { height: 40px; width: 60px; background: #000; margin: 0 5px 0 0; border: 1px solid #000;; float: left; cursor: pointer; } .galleria-counter { position: absolute; bottom: 10px; left: 10px; text-align: right; color: #fff; font: normal 11px/1 arial,sans-serif; z-index: 2; } .galleria-loader { background: #000; width: 20px; height: 20px; position: absolute; top: 10px; right: 10px; z-index: 2; display: none; background: url(classic-loader.gif) no-repeat 2px 2px; } .galleria-info { width: 50%; top: 15px; left: 15px; z-index: 2; position: absolute; } .galleria-info-text { background-color: #000; padding: 12px; display: none; /* IE7 */ zoom:1; } .galleria-info-title { font: bold 12px/1.1 arial,sans-serif; margin: 0; color: #fff; margin-bottom: 7px; } .galleria-info-description { font: italic 12px/1.4 georgia,serif; margin: 0; color: #bbb; } .galleria-info-close { width: 9px; height: 9px; position: absolute; top: 5px; right: 5px; background-position: -753px -11px; opacity: .5; filter: alpha(opacity=50); cursor: pointer; display: none; } .galleria-info-close:hover{ opacity:1; filter: alpha(opacity=100); } .galleria-info-link { background-position: -669px -5px; opacity: .7; filter: alpha(opacity=70); position: absolute; width: 20px; height: 20px; cursor: pointer; background-color: #000; } .galleria-info-link:hover { opacity: 1; filter: alpha(opacity=100); } .galleria-image-nav { position: absolute; top: 50%; margin-top: -62px; width: 100%; height: 62px; left: 0; } .galleria-image-nav-left, .galleria-image-nav-right { opacity: .3; filter: alpha(opacity=30); cursor: pointer; width: 62px; height: 124px; position: absolute; left: 10px; z-index: 2; background-position: 0 46px; } .galleria-image-nav-right { left: auto; right: 10px; background-position: -254px 46px; z-index: 2; } .galleria-image-nav-left:hover, .galleria-image-nav-right:hover { opacity: 1; filter: alpha(opacity=100); } .galleria-thumb-nav-left, .galleria-thumb-nav-right { cursor: pointer; display: none; background-position: -495px 5px; position: absolute; left: 0; top: 0; height: 40px; width: 23px; z-index: 3; opacity: .8; filter: alpha(opacity=80); } .galleria-thumb-nav-right { background-position: -578px 5px; border-right: none; right: 0; left: auto; } .galleria-thumbnails-container .disabled { opacity: .2; filter: alpha(opacity=20); cursor: default; } .galleria-thumb-nav-left:hover, .galleria-thumb-nav-right:hover { opacity: 1; filter: alpha(opacity=100); background-color: #111; } .galleria-thumbnails-container .disabled:hover { opacity: 0.2; filter: alpha(opacity=20); background-color: transparent; } .galleria-carousel .galleria-thumb-nav-left, .galleria-carousel .galleria-thumb-nav-right { display: block; } .galleria-thumb-nav-left, .galleria-thumb-nav-right, .galleria-info-link, .galleria-info-close, .galleria-image-nav-left, .galleria-image-nav-right { background-image: url(classic-map.png); background-repeat: no-repeat; } sqlkit-0.9.5/doc/html/_static/themes/classic/galleria.classic.min.js0000744000175000017500000000255311527440212024763 0ustar sandrosandro/* Galleria Classic Theme 2011-02-14 http://galleria.aino.se Copyright (c) 2011, Aino Licensed under the MIT license. */ (function(b){Galleria.addTheme({name:"classic",author:"Galleria",css:"galleria.classic.css",defaults:{transition:"slide",thumbCrop:"height",_toggleInfo:true},init:function(e){this.addElement("info-link","info-close");this.append({info:["info-link","info-close"]});var c=this.$("info-link,info-close,info-text"),d=Galleria.TOUCH,f=d?"touchstart":"click";this.$("loader,counter").show().css("opacity",0.4);if(!d){this.addIdleState(this.get("image-nav-left"),{left:-50});this.addIdleState(this.get("image-nav-right"), {right:-50});this.addIdleState(this.get("counter"),{opacity:0})}if(e._toggleInfo===true)c.bind(f,function(){c.toggle()});else{c.show();this.$("info-link, info-close").hide()}this.bind("thumbnail",function(a){if(!d){b(a.thumbTarget).css("opacity",0.6).parent().hover(function(){b(this).not(".active").children().stop().fadeTo(100,1)},function(){b(this).not(".active").children().stop().fadeTo(400,0.6)});a.index===e.show&&b(a.thumbTarget).css("opacity",1)}});this.bind("loadstart",function(a){a.cached|| this.$("loader").show().fadeTo(200,0.4);this.$("info").toggle(this.hasInfo());b(a.thumbTarget).css("opacity",1).parent().siblings().children().css("opacity",0.6)});this.bind("loadfinish",function(){this.$("loader").fadeOut(200)})}})})(jQuery);sqlkit-0.9.5/doc/html/_static/themes/classic/classic-demo.html0000644000175000017500000001500311531226335023670 0ustar sandrosandro Galleria Classic Theme sqlkit-0.9.5/doc/html/_static/themes/classic/classic-loader.gif0000644000175000017500000000347111524467346024034 0ustar sandrosandroGIF89atttRRR@@@bbbȖ!Created with ajaxload.info! ! NETSCAPE2.0,w  !DBAH¬aD@ ^AXP@"UQ# B\; 1 o:2$v@ $|,3 _# d53" s5 e!! ,v i@e9DAA/`ph$Ca%@ pHxFuSx# .݄YfL_" p 3BW ]|L \6{|z87[7!! ,x  e9DE"2r,qPj`8@8bH, *0- mFW9LPE3+ (B"  f{*BW_/ @_$~Kr7Ar7!! ,v 4e9!H"* Q/@-4ép4R+-pȧ`P(6᠝U/  *,)(+/]"lO/*Ak K]A~666!! ,l ie9"* -80H=N; TEqe UoK2_WZ݌V1jgWe@tuH//w`?f~#6#!! ,~ ,e9"* ; pR%#0` 'c(J@@/1i4`VBV u}"caNi/ ] ))-Lel  mi} me[+!! ,y Ie9"M6*¨"7E͖@G((L&pqj@Z %@wZ) pl( ԭqu*R&c `))( s_J>_\'Gm7$+!! ,w Ie9*, (*(B5[1 ZIah!GexzJ0e6@V|U4Dm%$͛p \Gx }@+| =+ 1- Ea5l)+!! ,y )䨞'AKڍ,E\(l&;5 5D03a0--ÃpH4V % i p[R"| #  6iZwcw*!! ,y )䨞,K*0 a;׋аY8b`4n ¨Bbbx,( Ƚ  % >  2*i* /:+$v*!! ,u )䨞l[$ Jq[q 3`Q[5:IX!0rAD8 CvHPfiiQAP@pC %D PQ46  iciNj0w )#!! ,y ). q ,G Jr(J8 C*B,&< h W~-`, ,>; 8RN<, <1T] c' qk$ @)#!;sqlkit-0.9.5/doc/html/_static/themes/classic/galleria.classic.js0000644000175000017500000000507211526237604024207 0ustar sandrosandro/** * @preserve Galleria Classic Theme 2011-02-14 * http://galleria.aino.se * * Copyright (c) 2011, Aino * Licensed under the MIT license. */ /*global jQuery, Galleria */ (function($) { Galleria.addTheme({ name: 'classic', author: 'Galleria', css: 'galleria.classic.css', defaults: { transition: 'slide', thumbCrop: 'height', // set this to false if you want to show the caption all the time: _toggleInfo: true }, init: function(options) { // add some elements this.addElement('info-link','info-close'); this.append({ 'info' : ['info-link','info-close'] }); // cache some stuff var info = this.$('info-link,info-close,info-text'), touch = Galleria.TOUCH, click = touch ? 'touchstart' : 'click'; // show loader & counter with opacity this.$('loader,counter').show().css('opacity', 0.4); // some stuff for non-touch browsers if (! touch ) { this.addIdleState( this.get('image-nav-left'), { left:-50 }); this.addIdleState( this.get('image-nav-right'), { right:-50 }); this.addIdleState( this.get('counter'), { opacity:0 }); } // toggle info if ( options._toggleInfo === true ) { info.bind( click, function() { info.toggle(); }); } else { info.show(); this.$('info-link, info-close').hide(); } // bind some stuff this.bind('thumbnail', function(e) { if (! touch ) { // fade thumbnails $(e.thumbTarget).css('opacity', 0.6).parent().hover(function() { $(this).not('.active').children().stop().fadeTo(100, 1); }, function() { $(this).not('.active').children().stop().fadeTo(400, 0.6); }); if ( e.index === options.show ) { $(e.thumbTarget).css('opacity',1); } } }); this.bind('loadstart', function(e) { if (!e.cached) { this.$('loader').show().fadeTo(200, 0.4); } this.$('info').toggle( this.hasInfo() ); $(e.thumbTarget).css('opacity',1).parent().siblings().children().css('opacity', 0.6); }); this.bind('loadfinish', function(e) { this.$('loader').fadeOut(200); }); } }); }(jQuery)); sqlkit-0.9.5/doc/html/_static/themes/classic/classic-map.png0000644000175000017500000000346011524467346023360 0ustar sandrosandroPNG  IHDRSrf0tEXtSoftwareAdobe ImageReadyqe<"iTXtXML:com.adobe.xmp IDATx͋MagŠdeCM )"؈ 6J))8f掙q9o-{UR1:bϺRENOxhNkJio,WCW#үsczL*6r;ߍYN;#_"< +o"""UA5ž[zcYs,"#)/R@C (А=4+r )J>@5y~cF@rS%P#?0;IENDB`sqlkit-0.9.5/doc/html/_static/jMenu.jquery.css0000644000175000017500000000347211533706144020651 0ustar sandrosandro/* .horizontal_menu { */ /* height: 20px; */ /* } */ #jMenu { /* display: table; */ margin: 0; font-weight: bold } /********************/ /** premier niveau **/ /********************/ #jMenu li { display: table-cell; /* background-color: #f89c22; */ /* margin: 0 */ padding-right: 7px } #jMenu li a { padding: 10px; background-color: #da812d; display: block; -moz-border-radius-topright: 6px; /* background-color: transparent; */ color: #fff; cursor: pointer; font-size: 13px } #jMenu li a:hover { background-color: #f8d322; color: #a91819 } /*******************/ /** second niveau **/ /*******************/ #jMenu li:hover, #jMenu li ul li:hover { color: #a91819; -moz-border-radius-topright: 6px; text-decoration: none } #jMenu li ul { display: none; position: absolute; padding: 0; margin: 0 } #jMenu li ul li { background-color: #f8d322; -moz-border-radius-topright: 6px; display: block; border-bottom: 1px solid #da812d; padding: 0 } #jMenu li ul li.arrow { background: #f5f053 url(../arrow_down.png) no-repeat center center; color: #red; height: 6px; padding: 0; border-bottom: none; padding-bottom: 10px } #jMenu li ul li a { font-size: 11px; text-transform: none; background-color: #f8d322; color: #a91819; padding: 7px; display: block; border-top: 1px solid transparent; border-bottom: 1px solid transparent } #jMenu li ul li a.isParent { background: #f8d322 url(../arrow_right.png) no-repeat right center; color: #a91819 } #jMenu li ul li a:hover { background-color: #f5f053; -moz-border-radius-topright: 6px; color: #a91819; border-top: 1px solid #322f32; border-bottom: 1px solid #322f32 } sqlkit-0.9.5/doc/html/_static/AnythingSlider/0000755000175000017500000000000011535607335020465 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/AnythingSlider/js/0000755000175000017500000000000011535607335021101 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/AnythingSlider/js/jquery.easing.1.2.js0000644000175000017500000001122511533717256024525 0ustar sandrosandro/* * jQuery EasIng v1.1.2 - http://gsgd.co.uk/sandbox/jquery.easIng.php * * Uses the built In easIng capabilities added In jQuery 1.1 * to offer multiple easIng options * * Copyright (c) 2007 George Smith * Licensed under the MIT License: * http://www.opensource.org/licenses/mit-license.php */ // t: current time, b: begInnIng value, c: change In value, d: duration jQuery.extend( jQuery.easing, { easeInQuad: function (x, t, b, c, d) { return c*(t/=d)*t + b; }, easeOutQuad: function (x, t, b, c, d) { return -c *(t/=d)*(t-2) + b; }, easeInOutQuad: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t + b; return -c/2 * ((--t)*(t-2) - 1) + b; }, easeInCubic: function (x, t, b, c, d) { return c*(t/=d)*t*t + b; }, easeOutCubic: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t + 1) + b; }, easeInOutCubic: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t + b; return c/2*((t-=2)*t*t + 2) + b; }, easeInQuart: function (x, t, b, c, d) { return c*(t/=d)*t*t*t + b; }, easeOutQuart: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b; }, easeInOutQuart: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t + b; return -c/2 * ((t-=2)*t*t*t - 2) + b; }, easeInQuint: function (x, t, b, c, d) { return c*(t/=d)*t*t*t*t + b; }, easeOutQuint: function (x, t, b, c, d) { return c*((t=t/d-1)*t*t*t*t + 1) + b; }, easeInOutQuint: function (x, t, b, c, d) { if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; return c/2*((t-=2)*t*t*t*t + 2) + b; }, easeInSine: function (x, t, b, c, d) { return -c * Math.cos(t/d * (Math.PI/2)) + c + b; }, easeOutSine: function (x, t, b, c, d) { return c * Math.sin(t/d * (Math.PI/2)) + b; }, easeInOutSine: function (x, t, b, c, d) { return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; }, easeInExpo: function (x, t, b, c, d) { return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; }, easeOutExpo: function (x, t, b, c, d) { return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; }, easeInOutExpo: function (x, t, b, c, d) { if (t==0) return b; if (t==d) return b+c; if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; }, easeInCirc: function (x, t, b, c, d) { return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; }, easeOutCirc: function (x, t, b, c, d) { return c * Math.sqrt(1 - (t=t/d-1)*t) + b; }, easeInOutCirc: function (x, t, b, c, d) { if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; }, easeInElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; }, easeOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; }, easeInOutElastic: function (x, t, b, c, d) { var s=1.70158;var p=0;var a=c; if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); if (a < Math.abs(c)) { a=c; var s=p/4; } else var s = p/(2*Math.PI) * Math.asin (c/a); if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; }, easeInBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*(t/=d)*t*((s+1)*t - s) + b; }, easeOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; }, easeInOutBack: function (x, t, b, c, d, s) { if (s == undefined) s = 1.70158; if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; }, easeInBounce: function (x, t, b, c, d) { return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b; }, easeOutBounce: function (x, t, b, c, d) { if ((t/=d) < (1/2.75)) { return c*(7.5625*t*t) + b; } else if (t < (2/2.75)) { return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; } else if (t < (2.5/2.75)) { return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; } else { return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; } }, easeInOutBounce: function (x, t, b, c, d) { if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b; return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b; } });sqlkit-0.9.5/doc/html/_static/AnythingSlider/css/0000755000175000017500000000000011714166421021250 5ustar sandrosandrosqlkit-0.9.5/doc/html/_static/AnythingSlider/css/theme-cs-portfolio.css0000644000175000017500000001137611534024230025501 0ustar sandrosandro/* AnythingSlider v1.41 cs-portfolio By Curtis Scott (http://www.curtisscott.com/portfolio.html) updated to work with the new themes */ /*** Note: the nav-cs-portfolio.png used for navigation and slideshow buttons uses a semi-transparent png, through which the background color is seen... so colors set will turn out darker than normal ***/ /****** SET COLORS HERE *******/ /* Default/Acitve State */ div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a { background-color: #fff; } div.anythingSlider-cs-portfolio .thumbNav a:hover, div.anythingSlider-cs-portfolio .thumbNav a.cur, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a:hover, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a.cur { background-color: #0d5c9f; } div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover { background-color: #080; } div.anythingSlider-cs-portfolio .start-stop:hover { background-color: #0f0; } div.anythingSlider-cs-portfolio .start-stop.playing { background-color: #a00; } div.anythingSlider-cs-portfolio .start-stop.playing:hover { background-color: #f00; } /* Overall */ div.anythingSlider-cs-portfolio { /*margin-bottom: 50px; add 50px to fit bar below the slider */ } div.anythingSlider-cs-portfolio .anythingWindow, div.anythingSlider-cs-portfolio.activeSlider .anythingWindow { border: 0; } /* Navigation Arrows */ div.anythingSlider-cs-portfolio .arrow { display: block; bottom: -43px; position: absolute; z-index: 100; } div.anythingSlider-cs-portfolio .arrow a { display: block; bottom: 50px; position: absolute; height: 35px; width: 35px; outline: 0; background: url(../images/arrows-cs-portfolio.jpg) no-repeat; text-indent: -9999px; } div.anythingSlider-cs-portfolio .forward { right: 70px; } div.anythingSlider-cs-portfolio .back { left: 50px; } div.anythingSlider-cs-portfolio .forward a { background-position: left top; } div.anythingSlider-cs-portfolio .back a { background-position: right top; } div.anythingSlider-cs-portfolio .forward a:hover, div.anythingSlider-cs-portfolio .forward a.hover { background-position: left bottom; } div.anythingSlider-cs-portfolio .back a:hover, div.anythingSlider-cs-portfolio .back a.hover { background-position: right bottom; } /* Navigation Links */ div.anythingSlider-cs-portfolio .anythingControls { background: url(../images/bg-cs-portfolio.jpg) repeat-x bottom center; height: 50px; margin: 0 auto 50px auto; text-align: center; z-index: 100; } div.anythingSlider-cs-portfolio .thumbNav { float: none; margin: 0; z-index: 100; } div.anythingSlider-cs-portfolio .thumbNav li { display: inline; } div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a { display: inline-block; width: 18px; height: 15px; margin: 20px 5px 0 0; padding: 0; text-indent: -9999px; outline: 0; border: 0; } div.anythingSlider-cs-portfolio .thumbNav a, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a, div.anythingSlider-cs-portfolio .thumbNav a:hover, div.anythingSlider-cs-portfolio .thumbNav a.cur, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a:hover, div.anythingSlider-cs-portfolio.activeSlider .thumbNav a.cur { background-image: url(../images/nav-cs-portfolio.png); background-position: center top; background-repeat: no-repeat; } /* slider autoplay right-to-left, reverse order of nav links to look better */ div.anythingSlider-cs-portfolio.rtl .thumbNav a { float: right; } /* reverse order of nav links */ div.anythingSlider-cs-portfolio.rtl .thumbNav { float: left; } /* move nav link group to left */ /* div.anythingSlider-cs-portfolio.rtl .start-stop { float: right; } */ /* move start/stop button - in case you want to switch sides */ /* Autoplay Start/Stop button */ div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover { margin: 18px 50px 0 0; padding: 0; display: inline-block; text-align: center; width: 18px; height: 18px; z-index: 100; text-indent: -9999px; border: 0; } div.anythingSlider-cs-portfolio .start-stop, div.anythingSlider-cs-portfolio .start-stop.hover, div.anythingSlider-cs-portfolio .start-stop:hover, div.anythingSlider-cs-portfolio .start-stop.playing, div.anythingSlider-cs-portfolio .start-stop.playing:hover { background-image: url(../images/nav-cs-portfolio.png); background-position: center bottom; background-repeat: no-repeat; } /* Extra - replace defaults */ div.anythingSlider-cs-portfolio { /* padding: 0 23px 50px 0; */ padding: 25px 30px 90px 30px; background-color: #444; } div.anythingSlider-cs-portfolio pre { padding: 0; background-color: #444; border: 0; } div.anythingSlider-cs-portfolio h2 { color: #eee; } div.anythingSlider-cs-portfolio tt { color: #eee; } sqlkit-0.9.5/doc/html/_static/AnythingSlider/css/anythingslider.css0000644000175000017500000001126511533717256025022 0ustar sandrosandro/* AnythingSlider v1.4.1+ Default (base) theme By Chris Coyier: http://css-tricks.com with major improvements by Doug Neiner: http://pixelgraphics.us/ based on work by Remy Sharp: http://jqueryfordesigners.com/ */ /******* SET DEFAULT DIMENSIONS HERE ********/ div.anythingSlider { width: 700px; height: 390px; margin: 0 auto; overflow: hidden; /* needed for Opera and Safari */ } /****** SET COLORS HERE *******/ /* Default State */ div.anythingSlider .thumbNav a.cur, div.anythingSlider .thumbNav a { background: #777; color: #000; } div.anythingSlider .anythingWindow { border-top: 3px solid #777; border-bottom: 3px solid #777; } div.anythingSlider .start-stop { background-color: #040; color: #fff; } div.anythingSlider .start-stop.playing { background-color: #800; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { color: #ddd; } /* Active State */ div.anythingSlider.activeSlider .anythingWindow { border-color: #7C9127; } div.anythingSlider.activeSlider .thumbNav a.cur, div.anythingSlider.activeSlider .thumbNav a { background-color: #7C9127; } div.anythingSlider .start-stop { background-color: #080; color: #fff; } div.anythingSlider .start-stop.playing { background-color: #d00; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { color: #fff; } /**** DO NOT CHANGE BELOW THIS LINE ****/ /* anythingSlider viewport window */ div.anythingSlider .anythingWindow { overflow: hidden; position: relative; width: 100%; height: 100%; } /* wrapper: 45px right & left padding for the arrows, 28px @ bottom for navigation */ div.anythingSlider { position: relative; padding: 0 45px 28px 45px; } /* anythingSlider base UL */ ul.anythingBase { background: transparent; list-style: none; position: absolute; top: 0; left: 0; margin: 0; padding: 0; } ul.anythingBase li.panel { background: transparent; display: block; overflow: hidden; float: left; padding: 0; margin: 0; } /* Navigation Arrows */ div.anythingSlider .arrow { top: 50%; position: absolute; display: block; } div.anythingSlider .arrow a { display: block; height: 120px; margin: -60px 0 0 0; width: 45px; text-align: center; outline: 0; background: url(../images/arrows-default.png) no-repeat; text-indent: -9999px; } div.anythingSlider .forward { right: 0; } div.anythingSlider .back { left: 0; } div.anythingSlider .forward a { background-position: 0 -40px; } div.anythingSlider .back a { background-position: -88px -40px; } div.anythingSlider .forward a:hover, div.anythingSlider .forward a.hover { background-position: 0 -240px; } div.anythingSlider .back a:hover, div.anythingSlider .back a.hover { background-position: -88px -240px; } div.anythingSlider .forward.disabled { display: none; } /* disabled arrows, hide or reduce opacity: opacity: .5; filter: alpha(opacity=50); */ div.anythingSlider .back.disabled { display: none; } /* Navigation Links */ div.anythingSlider .anythingControls { outline: 0; } div.anythingSlider .thumbNav { margin: 0; } div.anythingSlider .thumbNav li { display: inline; } div.anythingSlider .thumbNav a { font: 11px/18px Georgia, Serif; display: inline-block; text-decoration: none; padding: 2px 8px; height: 18px; margin: 0 5px 0 0; background-image: url(../images/cellshade.png); background-repeat: repeat-x; text-align: center; outline: 0; border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px; } div.anythingSlider .thumbNav a:hover { background-image: none; } /* slider autoplay right-to-left, reverse order of nav links to look better */ div.anythingSlider.rtl .thumbNav a { float: right; } /* reverse order of nav links */ div.anythingSlider.rtl .thumbNav { float: left; } /* move nav link group to left */ div.anythingSlider.rtl .anythingWindow { direction: ltr; unicode-bidi: bidi-override; } /* div.anythingSlider.rtl .start-stop { float: right; } */ /* move start/stop button - in case you want to switch sides */ /* Autoplay Start/Stop button */ div.anythingSlider .start-stop { background-image: url(../images/cellshade.png); background-repeat: repeat-x; background-position: center top; padding: 2px 5px; width: 40px; text-align: center; text-decoration: none; float: right; z-index: 100; outline: 0; border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -webkit-border-radius: 0 0 5px 5px; } div.anythingSlider .start-stop:hover, div.anythingSlider .start-stop.hover { background-image: none; } div.anythingSlider, div.anythingSlider .anythingWindow, div.anythingSlider .thumbNav a, div.anythingSlider .arrow a, div.anythingSlider .start-stop { transition-duration: 0; -o-transition-duration: 0; -moz-transition-duration: 0; -webkit-transition-duration: 0; }sqlkit-0.9.5/doc/html/_static/jquery.js0000644000175000017500000035367311147357541017437 0ustar sandrosandro/*! * jQuery JavaScript Library v1.3.2 * http://jquery.com/ * * Copyright (c) 2009 John Resig * Dual licensed under the MIT and GPL licenses. * http://docs.jquery.com/License * * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) * Revision: 6246 */ (function(){ var // Will speed up references to window, and allows munging its name. window = this, // Will speed up references to undefined, and allows munging its name. undefined, // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ in case of overwrite _$ = window.$, jQuery = window.jQuery = window.$ = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' return new jQuery.fn.init( selector, context ); }, // A simple way to check for HTML strings or ID strings // (both of which we optimize for) quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, // Is it a simple selector isSimple = /^.[^:#\[\.,]*$/; jQuery.fn = jQuery.prototype = { init: function( selector, context ) { // Make sure that a selection was provided selector = selector || document; // Handle $(DOMElement) if ( selector.nodeType ) { this[0] = selector; this.length = 1; this.context = selector; return this; } // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? var match = quickExpr.exec( selector ); // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { // HANDLE: $(html) -> $(array) if ( match[1] ) selector = jQuery.clean( [ match[1] ], context ); // HANDLE: $("#id") else { var elem = document.getElementById( match[3] ); // Handle the case where IE and Opera return items // by name instead of ID if ( elem && elem.id != match[3] ) return jQuery().find( selector ); // Otherwise, we inject the element directly into the jQuery object var ret = jQuery( elem || [] ); ret.context = document; ret.selector = selector; return ret; } // HANDLE: $(expr, [context]) // (which is just equivalent to: $(content).find(expr) } else return jQuery( context ).find( selector ); // HANDLE: $(function) // Shortcut for document ready } else if ( jQuery.isFunction( selector ) ) return jQuery( document ).ready( selector ); // Make sure that old selector state is passed along if ( selector.selector && selector.context ) { this.selector = selector.selector; this.context = selector.context; } return this.setArray(jQuery.isArray( selector ) ? selector : jQuery.makeArray(selector)); }, // Start with an empty selector selector: "", // The current version of jQuery being used jquery: "1.3.2", // The number of elements contained in the matched element set size: function() { return this.length; }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { return num === undefined ? // Return a 'clean' array Array.prototype.slice.call( this ) : // Return just the object this[ num ]; }, // Take an array of elements and push it onto the stack // (returning the new matched element set) pushStack: function( elems, name, selector ) { // Build a new jQuery matched element set var ret = jQuery( elems ); // Add the old object onto the stack (as a reference) ret.prevObject = this; ret.context = this.context; if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector; else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")"; // Return the newly-formed element set return ret; }, // Force the current matched set of elements to become // the specified array of elements (destroying the stack in the process) // You should use pushStack() in order to do this, but maintain the stack setArray: function( elems ) { // Resetting the length to 0, then using the native Array push // is a super-fast way to populate an object with array-like properties this.length = 0; Array.prototype.push.apply( this, elems ); return this; }, // Execute a callback for every element in the matched set. // (You can seed the arguments with an array of args, but this is // only used internally.) each: function( callback, args ) { return jQuery.each( this, callback, args ); }, // Determine the position of an element within // the matched set of elements index: function( elem ) { // Locate the position of the desired element return jQuery.inArray( // If it receives a jQuery object, the first element is used elem && elem.jquery ? elem[0] : elem , this ); }, attr: function( name, value, type ) { var options = name; // Look for the case where we're accessing a style value if ( typeof name === "string" ) if ( value === undefined ) return this[0] && jQuery[ type || "attr" ]( this[0], name ); else { options = {}; options[ name ] = value; } // Check to see if we're setting style values return this.each(function(i){ // Set all the styles for ( name in options ) jQuery.attr( type ? this.style : this, name, jQuery.prop( this, options[ name ], type, i, name ) ); }); }, css: function( key, value ) { // ignore negative width and height values if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) value = undefined; return this.attr( key, value, "curCSS" ); }, text: function( text ) { if ( typeof text !== "object" && text != null ) return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); var ret = ""; jQuery.each( text || this, function(){ jQuery.each( this.childNodes, function(){ if ( this.nodeType != 8 ) ret += this.nodeType != 1 ? this.nodeValue : jQuery.fn.text( [ this ] ); }); }); return ret; }, wrapAll: function( html ) { if ( this[0] ) { // The elements to wrap the target around var wrap = jQuery( html, this[0].ownerDocument ).clone(); if ( this[0].parentNode ) wrap.insertBefore( this[0] ); wrap.map(function(){ var elem = this; while ( elem.firstChild ) elem = elem.firstChild; return elem; }).append(this); } return this; }, wrapInner: function( html ) { return this.each(function(){ jQuery( this ).contents().wrapAll( html ); }); }, wrap: function( html ) { return this.each(function(){ jQuery( this ).wrapAll( html ); }); }, append: function() { return this.domManip(arguments, true, function(elem){ if (this.nodeType == 1) this.appendChild( elem ); }); }, prepend: function() { return this.domManip(arguments, true, function(elem){ if (this.nodeType == 1) this.insertBefore( elem, this.firstChild ); }); }, before: function() { return this.domManip(arguments, false, function(elem){ this.parentNode.insertBefore( elem, this ); }); }, after: function() { return this.domManip(arguments, false, function(elem){ this.parentNode.insertBefore( elem, this.nextSibling ); }); }, end: function() { return this.prevObject || jQuery( [] ); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. push: [].push, sort: [].sort, splice: [].splice, find: function( selector ) { if ( this.length === 1 ) { var ret = this.pushStack( [], "find", selector ); ret.length = 0; jQuery.find( selector, this[0], ret ); return ret; } else { return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ return jQuery.find( selector, elem ); })), "find", selector ); } }, clone: function( events ) { // Do the clone var ret = this.map(function(){ if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { // IE copies events bound via attachEvent when // using cloneNode. Calling detachEvent on the // clone will also remove the events from the orignal // In order to get around this, we use innerHTML. // Unfortunately, this means some modifications to // attributes in IE that are actually only stored // as properties will not be copied (such as the // the name attribute on an input). var html = this.outerHTML; if ( !html ) { var div = this.ownerDocument.createElement("div"); div.appendChild( this.cloneNode(true) ); html = div.innerHTML; } return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; } else return this.cloneNode(true); }); // Copy the events from the original to the clone if ( events === true ) { var orig = this.find("*").andSelf(), i = 0; ret.find("*").andSelf().each(function(){ if ( this.nodeName !== orig[i].nodeName ) return; var events = jQuery.data( orig[i], "events" ); for ( var type in events ) { for ( var handler in events[ type ] ) { jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); } } i++; }); } // Return the cloned set return ret; }, filter: function( selector ) { return this.pushStack( jQuery.isFunction( selector ) && jQuery.grep(this, function(elem, i){ return selector.call( elem, i ); }) || jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ return elem.nodeType === 1; }) ), "filter", selector ); }, closest: function( selector ) { var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, closer = 0; return this.map(function(){ var cur = this; while ( cur && cur.ownerDocument ) { if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { jQuery.data(cur, "closest", closer); return cur; } cur = cur.parentNode; closer++; } }); }, not: function( selector ) { if ( typeof selector === "string" ) // test special case where just one selector is passed in if ( isSimple.test( selector ) ) return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); else selector = jQuery.multiFilter( selector, this ); var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; return this.filter(function() { return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; }); }, add: function( selector ) { return this.pushStack( jQuery.unique( jQuery.merge( this.get(), typeof selector === "string" ? jQuery( selector ) : jQuery.makeArray( selector ) ))); }, is: function( selector ) { return !!selector && jQuery.multiFilter( selector, this ).length > 0; }, hasClass: function( selector ) { return !!selector && this.is( "." + selector ); }, val: function( value ) { if ( value === undefined ) { var elem = this[0]; if ( elem ) { if( jQuery.nodeName( elem, 'option' ) ) return (elem.attributes.value || {}).specified ? elem.value : elem.text; // We need to handle select boxes special if ( jQuery.nodeName( elem, "select" ) ) { var index = elem.selectedIndex, values = [], options = elem.options, one = elem.type == "select-one"; // Nothing was selected if ( index < 0 ) return null; // Loop through all the selected options for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { var option = options[ i ]; if ( option.selected ) { // Get the specifc value for the option value = jQuery(option).val(); // We don't need an array for one selects if ( one ) return value; // Multi-Selects return an array values.push( value ); } } return values; } // Everything else, we just grab the value return (elem.value || "").replace(/\r/g, ""); } return undefined; } if ( typeof value === "number" ) value += ''; return this.each(function(){ if ( this.nodeType != 1 ) return; if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) this.checked = (jQuery.inArray(this.value, value) >= 0 || jQuery.inArray(this.name, value) >= 0); else if ( jQuery.nodeName( this, "select" ) ) { var values = jQuery.makeArray(value); jQuery( "option", this ).each(function(){ this.selected = (jQuery.inArray( this.value, values ) >= 0 || jQuery.inArray( this.text, values ) >= 0); }); if ( !values.length ) this.selectedIndex = -1; } else this.value = value; }); }, html: function( value ) { return value === undefined ? (this[0] ? this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : null) : this.empty().append( value ); }, replaceWith: function( value ) { return this.after( value ).remove(); }, eq: function( i ) { return this.slice( i, +i + 1 ); }, slice: function() { return this.pushStack( Array.prototype.slice.apply( this, arguments ), "slice", Array.prototype.slice.call(arguments).join(",") ); }, map: function( callback ) { return this.pushStack( jQuery.map(this, function(elem, i){ return callback.call( elem, i, elem ); })); }, andSelf: function() { return this.add( this.prevObject ); }, domManip: function( args, table, callback ) { if ( this[0] ) { var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), first = fragment.firstChild; if ( first ) for ( var i = 0, l = this.length; i < l; i++ ) callback.call( root(this[i], first), this.length > 1 || i > 0 ? fragment.cloneNode(true) : fragment ); if ( scripts ) jQuery.each( scripts, evalScript ); } return this; function root( elem, cur ) { return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? (elem.getElementsByTagName("tbody")[0] || elem.appendChild(elem.ownerDocument.createElement("tbody"))) : elem; } } }; // Give the init function the jQuery prototype for later instantiation jQuery.fn.init.prototype = jQuery.fn; function evalScript( i, elem ) { if ( elem.src ) jQuery.ajax({ url: elem.src, async: false, dataType: "script" }); else jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); if ( elem.parentNode ) elem.parentNode.removeChild( elem ); } function now(){ return +new Date; } jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; // skip the boolean and the target i = 2; } // Handle case when target is a string or something (possible in deep copy) if ( typeof target !== "object" && !jQuery.isFunction(target) ) target = {}; // extend jQuery itself if only one argument is passed if ( length == i ) { target = this; --i; } for ( ; i < length; i++ ) // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) // Extend the base object for ( var name in options ) { var src = target[ name ], copy = options[ name ]; // Prevent never-ending loop if ( target === copy ) continue; // Recurse if we're merging object values if ( deep && copy && typeof copy === "object" && !copy.nodeType ) target[ name ] = jQuery.extend( deep, // Never move original objects, clone them src || ( copy.length != null ? [ ] : { } ) , copy ); // Don't bring in undefined values else if ( copy !== undefined ) target[ name ] = copy; } // Return the modified object return target; }; // exclude the following css properties to add px var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, // cache defaultView defaultView = document.defaultView || {}, toString = Object.prototype.toString; jQuery.extend({ noConflict: function( deep ) { window.$ = _$; if ( deep ) window.jQuery = _jQuery; return jQuery; }, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, isArray: function( obj ) { return toString.call(obj) === "[object Array]"; }, // check if an element is in a (or is an) XML document isXMLDoc: function( elem ) { return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); }, // Evalulates a script in a global context globalEval: function( data ) { if ( data && /\S/.test(data) ) { // Inspired by code by Andrea Giammarchi // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html var head = document.getElementsByTagName("head")[0] || document.documentElement, script = document.createElement("script"); script.type = "text/javascript"; if ( jQuery.support.scriptEval ) script.appendChild( document.createTextNode( data ) ); else script.text = data; // Use insertBefore instead of appendChild to circumvent an IE6 bug. // This arises when a base node is used (#2709). head.insertBefore( script, head.firstChild ); head.removeChild( script ); } }, nodeName: function( elem, name ) { return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); }, // args is for internal usage only each: function( object, callback, args ) { var name, i = 0, length = object.length; if ( args ) { if ( length === undefined ) { for ( name in object ) if ( callback.apply( object[ name ], args ) === false ) break; } else for ( ; i < length; ) if ( callback.apply( object[ i++ ], args ) === false ) break; // A special, fast, case for the most common use of each } else { if ( length === undefined ) { for ( name in object ) if ( callback.call( object[ name ], name, object[ name ] ) === false ) break; } else for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} } return object; }, prop: function( elem, value, type, i, name ) { // Handle executable functions if ( jQuery.isFunction( value ) ) value = value.call( elem, i ); // Handle passing in a number to a CSS property return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? value + "px" : value; }, className: { // internal only, use addClass("class") add: function( elem, classNames ) { jQuery.each((classNames || "").split(/\s+/), function(i, className){ if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) elem.className += (elem.className ? " " : "") + className; }); }, // internal only, use removeClass("class") remove: function( elem, classNames ) { if (elem.nodeType == 1) elem.className = classNames !== undefined ? jQuery.grep(elem.className.split(/\s+/), function(className){ return !jQuery.className.has( classNames, className ); }).join(" ") : ""; }, // internal only, use hasClass("class") has: function( elem, className ) { return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; } }, // A method for quickly swapping in/out CSS properties to get correct calculations swap: function( elem, options, callback ) { var old = {}; // Remember the old values, and insert the new ones for ( var name in options ) { old[ name ] = elem.style[ name ]; elem.style[ name ] = options[ name ]; } callback.call( elem ); // Revert the old values for ( var name in options ) elem.style[ name ] = old[ name ]; }, css: function( elem, name, force, extra ) { if ( name == "width" || name == "height" ) { var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; function getWH() { val = name == "width" ? elem.offsetWidth : elem.offsetHeight; if ( extra === "border" ) return; jQuery.each( which, function() { if ( !extra ) val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; if ( extra === "margin" ) val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; else val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; }); } if ( elem.offsetWidth !== 0 ) getWH(); else jQuery.swap( elem, props, getWH ); return Math.max(0, Math.round(val)); } return jQuery.curCSS( elem, name, force ); }, curCSS: function( elem, name, force ) { var ret, style = elem.style; // We need to handle opacity special in IE if ( name == "opacity" && !jQuery.support.opacity ) { ret = jQuery.attr( style, "opacity" ); return ret == "" ? "1" : ret; } // Make sure we're using the right name for getting the float value if ( name.match( /float/i ) ) name = styleFloat; if ( !force && style && style[ name ] ) ret = style[ name ]; else if ( defaultView.getComputedStyle ) { // Only "float" is needed here if ( name.match( /float/i ) ) name = "float"; name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); var computedStyle = defaultView.getComputedStyle( elem, null ); if ( computedStyle ) ret = computedStyle.getPropertyValue( name ); // We should always get a number back from opacity if ( name == "opacity" && ret == "" ) ret = "1"; } else if ( elem.currentStyle ) { var camelCase = name.replace(/\-(\w)/g, function(all, letter){ return letter.toUpperCase(); }); ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; // From the awesome hack by Dean Edwards // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 // If we're not dealing with a regular pixel number // but a number that has a weird ending, we need to convert it to pixels if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { // Remember the original values var left = style.left, rsLeft = elem.runtimeStyle.left; // Put in the new values to get a computed value out elem.runtimeStyle.left = elem.currentStyle.left; style.left = ret || 0; ret = style.pixelLeft + "px"; // Revert the changed values style.left = left; elem.runtimeStyle.left = rsLeft; } } return ret; }, clean: function( elems, context, fragment ) { context = context || document; // !context.createElement fails in IE with an error but returns typeof 'object' if ( typeof context.createElement === "undefined" ) context = context.ownerDocument || context[0] && context[0].ownerDocument || document; // If a single string is passed in and it's a single tag // just do a createElement and skip the rest if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); if ( match ) return [ context.createElement( match[1] ) ]; } var ret = [], scripts = [], div = context.createElement("div"); jQuery.each(elems, function(i, elem){ if ( typeof elem === "number" ) elem += ''; if ( !elem ) return; // Convert html string into DOM nodes if ( typeof elem === "string" ) { // Fix "XHTML"-style tags in all browsers elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? all : front + ">"; }); // Trim whitespace, otherwise indexOf won't work as expected var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); var wrap = // option or optgroup !tags.indexOf("", "" ] || !tags.indexOf("", "" ] || tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && [ 1, "", "
" ] || !tags.indexOf("", "" ] || // matched above (!tags.indexOf("", "" ] || !tags.indexOf("", "" ] || // IE can't serialize and
sqlkit-0.9.5/doc/html/layout/0000755000175000017500000000000011714210371015415 5ustar sandrosandrosqlkit-0.9.5/doc/html/layout/layout.html0000644000175000017500000011522311714210371017624 0ustar sandrosandro A GUI description language - purpose — sqlkit v0.9.5 documentation

A GUI description language - purpose

The class Layout is a class that understands a GUI description language to create GUI on the fly, with minimal effort by the programmers, that doesn’t need to know a lot of Gtk details.

You cannot do any possible thing you would do with gtk, but methods are provided to refine the layout and it isn’t difficult to add GtkObjects or features.

The purpose is to easy things with simple setup, not to cover any possible graphical layout. The main purpose is to be used in designing layout of record editor (Features) for db, but it can be used with any other program as well.

Layout is not an interactive tool: it uses gtk.Builder xml to create the GUI, ie: it produces xml for gtk.Builder.

Note

gtk.Builder

up to 0.9.2 sqlkit used glade. This dependancy has been dropped in favor of gtk.Builder that is directly available in standard GTK installation.

Care must be used to create names for the elements that are unique in the single layout, so as to be able to reach any single object, not only to refine configuration but also to interact with the GtkObj in the program. This reflects in the fact that the name is unique.

A normal GUI is plenty of labels and tooltip. At the moment of this writing no localization is provided but gettext support is in program.

Description of the language

a starting example:

layout = """
   el=first_name  el=last_name
   el=address     -
"""

A layout is a sequence of tokens and display modifiers.

token:

describes a GtkObject (Button, Entry...), may also be a compound. An example: el=first_name. They will normally start with a description key, followed by a ‘=’ and then followed by a string possibly separated by a ‘/’ and.or a:

key=string/variable:wid1.wid2

or in regexp notation and more precise:

(key=)string(.[0-9a-z])?(/variable)((:width.)(width)([-<>]))?
 key   txt    id          var        width1   width2  align

no element is needed apart the ‘string’, the key defaults to ‘el’ (Entry + Label), the variable defaults to the string, wid1 and wid2 (described below) use GtkWidget defaults.

The string part may end with .[a-z0-9] just to be able to refer in a unique way to single elements that are apparently equal (see element names)

an indication of the desidered alignment may be given using one of (‘-‘, ‘<’, ‘>’). this take effect oly if the element is position in a gtk.Alignment (eg. ‘ae’ element ie: entry with width). in that case:

>:right alignment (xalign = 1)
<:left alignment (xalign = 0)
-:xscale of the alignment is set to 1 i.e. the child of the alignment (the entry in the example will grow when more space is given to the widget)
spare token:

A token may further be:

@:it is equivalent to a newline (in fact I figure it like spinning ;-)
-:forces the previous element to span one more column to the left
^:forces the element above it to span one more row down
%.*:the element is a label for a notebook (see ‘notebook’ method)
display modifiers:
 

A modifier is an element that modifies the way elements are arranged, mainly it modifies the packing info or the container, but can also modify the display eg: label and entry on the same line or one above the other. A modifier is:

{}:

a pair of curly braces

[XTHVbNPSvhpOBmMFA](.id):
 

(possibly) followed (w/out spaces) by some opts as in {V r=man/sex r=woman}, this will create a nester layout with 2 radiobutton packed into a GtkVBox. For an explanation of the (.id) part see below (container naming)

Possible opts are XTHVbNPSvhpOBmMFA:

T:gtkTable
H:gtkHBox
V:gtkVBox
N:gtkNotebook
F:gtkFrame
M:gtkMenubar
t:gtkToolBar
h:gtkHPaned
v:gtkVPaned
S:gtkScrolledWindow
B:gtkMenuBar
M:gtkMenu
m:gtkMenuItem
O:gtkToolbar
W:Window
E:EventBox
X:Expander
p:ViewPort
A:Alignment

Elements will be packed in a table widget unless changed by a option when instantiating the class as in:

lay = Layout(lay, opts="V=")
Other values for opts are [>=-|] - to be confirmed
=:implies that label and entry will be displayed one over the other (this is the default)
-:implies that label and entry will be displayed on the same line.
|:will draw a line around the widgets, using a frame. That means a GtkFrame and a GtkAlignment are silently created. You can set properties of the silently created expander using key F.id
>:will encapsulate the result into an Expander that allows to collapse all its content via a click on a little arrow. You can set properties of the silently created expander using key X.id. A double ‘>>’ results in an expanded expander. The id is used as label for the expander. so that ‘>>.clients’, will be expanded in an oped expander with a label “clients”

a longer example

el=first_name                  el=last_name
el=address                     -
{V r=man/sex      r=woman/sex}  b=register

Comment

A ‘#’ sign starts a comment. Anything till the end of the line will be discarded, as usual...

el element

the most used element will probably turn out to be a Label + Entry widget that can be constructed as in el=first name. It will really be understood as:

{ l=first_name el=first_name }

or:

{ l=first_name
  el=first_name }

According to the opt modifier = or -

list of elements

A partial list of elements:

l:Label,
L:EventLabel, a label in a GtkEventBox
e:Entry,
E:Event,
b:Button,
r:RadioButton,
c:CheckButton,
TV:TreeView,
TX:TextView,
TVS:ScrolledTreeView,
TXS:ScrolledTextView,
cal:Calendar,
sb:Statusbar,
le:ComboLE,

more symbols are in the source...

custom widgets

Occasionally you will find yourself in nee for a custom identifier. You can create one and register it. Have a look at the code in layoutgenerator module.

Calling Layout

def __init__(self, lay, level=0, parent_container="W", opts="T",
             elements={}, container_id='',title="Window",dbg=0):
lay:

the string describing the desired layout

level:

nested level (used only by layout itself)

parent_container:
 

a flag among the container flags that says which is the container: determines the packing info for the result. Default is W (Window) as a toplevel window. In this case info for a toplevel window is built.

opts:

This string has the same meaning of the modifier string of a container that can be put directly after the ‘{‘ char.

Mainly this is a way to force the use of a container for the outer call of layout. Default is to put all widgets in a GtkTable but any container can be chosen from the container flags.

A special component of ‘opts’, is ‘s’ that asks to add a StatusBar. The method ‘sb’ is provided to talk with that Statusbar whose key name is sb=StBar.

addto:

follows the name of a container that will receive the resulting layout. Must be a container that accept ‘add’ method.

methods

NOTE: some methods can only be called after ‘show’ has being called. These methods use gtk directly, not glade.

xml(file=None):

will produce the xml needed for glade, may be write to file

show(action=function_name):
 

will directly call glade to display the GUI, returns a dict of Gtk objects whose keys are the element keys. After this method there is no point in changing elements[] properties. The action can be gtk.main.

elements:

a dict of all lwidgets. The keys are the element definitions. Any element can be configure up to the moment show is called.

conf(el,property):
 

will set a property for an element of the layout equivalent to:

elements['el_key'].properties['name'] = value
sig(list):

will set handlers for the signal for each widget. The list is a list of tuples of 3 elements:

  • el_key
  • handler
  • signal. If the last is missing, clicked is used
tip (el, text):

a tooltip for the element ‘el’. It tip is called before ‘show’, it will end up in xml+glade, otherwise it will call gtkTooltip directly

sb(text):

will push text on the StatusBar

menu(‘el_key’, (entry-name, handler, signal=activated), ‘i’), (), ():
 

Will add MenuItems (Stock ImageMenuItems if 4^ element is ‘i’)

notebook(‘el_key’, [‘label 1’, ‘label2’,...],position):
 

Adds the label to tabs and allow to set the position (top,left, right, bottom) for a GtkNotebook. There is another way to obtain this effect. You can add %label as first entry in the block that will be enclosed in the notebook. See below: notebook

frame(‘el_key’, ‘label’):
 

Adds a label to a frame, and writes it w/ bold face

prop(el_key,property_name, value):
 

Sets a property for element el_key

pack(el_key,property_name, value):
 

Sets a pack property for element el_key

element names

For each element of the layout we need to build a key for xml, so that we know how to refer to the gtk object in the program. Layout will build the name starting from what was written in the element description, if that results in being already used it builds a unique name adding a number, but that may result in difficulty to interact with it from inside the program.

If that’s an issue try being clear when creating the layout. Use ids, the string separated by a ‘.’ that you can add to element names and to container flags:

container:
{H.Z   {B.id0
element names:
e=name.id1/string

stock names

if the name of an element starts with ‘gtk-‘, use-stock = True is set for that element.

Notebook

A notebook need some labels to identify the tabs. These are other child of the GtkNotebook container interspersed with the content of the container. You can set them with the notebook method, or you can use a symbol in the describing string using % trailer:

{N.0  { %first_tab_RIGHT
      TXS=one }
    { %second_tab
      TXS=due
    }
}

will be equivalent to:

l.notepad('N.0', ['first tab','second tab'], 'right')

Please, note that you can enforce a position with a trailing _(LEFT|TOP|...) to the first label. Note also that any underscore will be substituted w/ a space.

Automatic name mechanism

A normal element definition is of the form:

  key=string/variable:width.height


* the :width.width part is discarded
* the rest is used as key, *but*
* if no 'key=' part was explicitly used as in 'first_name' two will be
  created: e=string, l=string

container naming

Containers need a name too. You are supposed to know which one is used: it will be GtkTable unless you asked for a different one. You need it in case you want to modify its properties.

Their name will be the container flag [THVbNPSvhpOBmMFA] possibly followed by a dot . and an identifier. The identifier will be a progressive counter, starting from 0 but may be imposed appending .id to the flags of the container, as in:

{V.my_id first_name last_name }

Functions

map_layouts(filename=None, buf=None)

This functions fills in a dictionary widgets.info whose structure is:

name : (label, tip)

when a widget is created w/ name ‘name’ the label will be set to ‘label’ and a tooltip will be set to ‘tip’. This can help a lot in many situations. Db field name will be mapped to user friendly labels, translation will be as easy as pointing to a different file.

The function can point to a filename or can read a string. You can write directly the info dictionary if you prefer...

Layout class implementation

Each layout live in a container (may be a toplevel Window) and creates a container to house its children. This can cause a little bit of confusion so I call parent_container the container in which a widget live and container the Gtk container (Table, HBox, VBox...) where I house layout children.

You can think at your layout as divided in blocks, each one named ‘cell’ in the code. A cell is or the definition of a gtkObj or a nested layout. You can see this list for debugging purposes with method _dbg_parse_layout.

You can think at these cells as displayed in a grid, each one defined by a row and a column. The constructor of Layout uses a dict named ‘cells’ with indices (row,col) to store the name of the corresponding LWidget or LContainer.

When the object is instantiated, Layout

  1. creates a container (LContainer object)
  2. splits the layout description into tokens that are or -a element descriptions - nested layouts (starts w/ ‘{‘) ...

For each token that describes a single element, creates the corresponding lwidget object, for each nested layout creates another instance of Layout.

LWidgets and LContainer

the difference between an LWidget and an LContainer is that an LContainer has children and must pack info for all the children when producing xml (apart from producing the xml for itself)

Window creation

Each call to Layout must create a container to house all its children, but the first (or outer) one needs normally to create also the TopLevel Window, that to us is nothing than another LContainer whose (only) child is just another container (the outer one for Layout).

Producing xml

Layout produces xml just asking to the toplevel (an LContainer object) to produce xml. LContainer xml, produces xml for itself and for its children, if some of them is a container the process iterates.


sqlkit-0.9.5/doc/html/layout/contents.html0000644000175000017500000003252711714210370020150 0ustar sandrosandro Layout GUI built on the fly - A GUI description language — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Layout GUI built on the fly - A GUI description language

This is the key of the mask widget and is really what makes writing a layout with SqlMask so easy.

If you know what glade or other gui builder are, think at this part as the “markup sistem” for html language.

Using this description language to define a layout we try to use as little writing as possible, we are asking sqlkit to guess all the properties that it can from the name.

What can be guessed? A lot!!!

Imagine you have a table with movies, and a field is title, anoter is director_id. The database knows that the first on is a varchar and the second is a foreign key. It can guess how you will want to represent it so that you will just need to you thei’re name in drawing the layout:

title  director_id

The biggest gain we have when setting Relationships between tables.

The code of the layout package and probably the definition of the language itself is deemed to be rewritten, as was the first step into python in 2005, but it proved to behave well in these years. After looking at te ReST markup syntax I am tempted to rewrite the language in a simpler way.


sqlkit-0.9.5/doc/html/misc/0000755000175000017500000000000011714210376015040 5ustar sandrosandrosqlkit-0.9.5/doc/html/misc/tutorials.html0000644000175000017500000003014311714210372017751 0ustar sandrosandro Mailing list — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/misc/backward_incompatibilities.html0000644000175000017500000003722611714210371023301 0ustar sandrosandro Backward Incompatibilities — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Backward Incompatibilities

From version 0.8.6 (first public release) I keep track of backward incompatibilities:

0.9.3

requirements:sqlalchemy now needs to be at least rel 0.5.4 as we’re using AttributeExtension to provide better MVC support

0.9.2

hooks:on__field_change now pass also the field. The demo snippet already had this arguments but was always None and was not documented
totals:added model, path, iter to sum method of totals. Impact only on customized Totals

0.9.1

hooks:on_completion is now triggered when you press Return, both for tables and masks. Previously was triggered on Return for Masks and on click for table.

0.9.0

Mask:

a fix in the layout machinery make setting width specs working... that means you may need to fine tune the width of your entries. You set the width of an entry as follows:

last_name:40
first_name:30-
sex:1>

the first sets 40 chars, the second sets 30 that would grow to use available space, the latter would use 2 chars and would right align it (compared to other entries)

mappers:

if a relationship has cascade=delete-orphan set, and you add an object by completion Sqlalchemy will complain that there’s a missing object. With SA 0.5 this is automatically done, with SA 0.6 this is no longer supported by sqlkit. You are supposed to explicitly add info on the Column as explained in sqlkit.fields.ForeignKeyField.add_related_object()

0.8.7

Table:
  • self.current points to self.get_current_obj(), no longer get_selected_obj()
  • button-press-event now has one more argument: treeview. This is necessary due to the fact that table now have views and t.treeview only points to the main view’s treeview.
Table & Mask:
  • now keyword table/class/mapper are deprecated in favor to setting it as first argument.
Hooks:

arguments in hooks of related widgets where not as documented: a hook on a sqlwidget acting on a related widget would receive the related widget as first argument rather that the main one. Look at the the following case:

lay = """
   user
   o2m=addresses
"""
class Hook(object):
   def on_change_value__addresses__domain(self, sqlwidget, field_name, value, fkvalue):
      pass

m = SqlMask(User,... hooks=Hook())
m_address = m.related.addresses

the hook instance will receive m as argument where previously received m_addresses


sqlkit-0.9.5/doc/html/misc/tutorial.html0000644000175000017500000010760111714210376017576 0ustar sandrosandro Sqledit Tutorial — sqlkit v0.9.5 documentation

Sqledit Tutorial

Intended audience

This is meant as a tutorial for the sqledit command that is part of sqlkit. It’s intended audience is anybody who is interested in editing data in a database (as opposed to editing the structure of the database).

No programming skill is required, but if you are supposed to install it yourself, you may need to understand at least a little bit of your operating system (but that may be as simple as a double click if you use bundles).

Installation

According to you operating system and setup you may find the very easy way for you. You may not event need to know which are the dependancies that are explained below for the curious ones.

Installing under Debian/Ubuntu

On Ubuntu lucid (10.04) and probably also others >= 9.10 you can prepare dependencies:

sudo add-apt-repository ppa:toobaz/sqlkit

On Debian:

echo deb http://ppa.launchpad.net/toobaz/sqlkit/ubuntu lucid main | sudo tee /etc/apt/sources.list.d/sqlkit.list
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 39012CF8

and install it as follows (this installs python drivers for PostgreSQL and Mysql as well):

sudo apt-get update
sudo apt-get install python-sqlkit python-psycopg2 python-mysqldb

I’ll try to keep this updated as the official package

Installing under Windows

The easiest way is to use Windows Package Manager that handles dependencies (that can turn usefull for other software too). The installation process becomes as easy as:

  • download and install Windows Package Manager
  • start it, select sqlkit and install (you may want to select/inistall also the database drivers for PostgreSql and MySQL)

If you want to go the “hard” way you can install all peaces separately but still use the Pygtk all-in-one installer (thanks to Dieter Verfaillie):

  • download and install Python

  • download and install Pygtk-all-in-one

  • download sqlkit-0.9.5.zip unzip and install by doing:

    python setup.py install

    Note that python may not be in the PATH so you may need to write it explicetly.

Installig under MacOS

You can install and run Sqlkit under MacOS also. You can use the all-in-one boundle (thanks to Anders F. Björklund) or install from MacPorts.

Dependencies

Sqlkit depends on the following packages:

Python:

sqledit it’s a Python script. It should work with python 2.4 and following. But if you used 2.4 be sure to add the driver for sqlite that was added to the main distribution only starting from 2.5.

PyGTK:

any Linux desktop distribution has it already installed, in case it does not have it, it should be trivial to do. In Debian-derived systems (e.g.: Ubuntu), you simply run:

apt-get install python-gtk2

In a Windows system it used to be a difficult task but now it’s very simple. Please follow the instructions on the pygtk site

sqlalchemy:

this is the core of the sql staff. It’s a layer that builds SQL statements and invokes the backend drivers. It takes care of inspecting the database and so on. You need at least release 0.5 but 0.5.4 would be much better.

python-dateutil:
 

needed for computation on dates, used in filters (e.g: date >= ‘m-1’, means date >= starting of last month), see Date filters.

babel:

needed for localization of numbers and dates

setuptools:

needed for the installation and to check version of sqlalchemy

drivers:

don’t forget to add the driver for the backend of your choice, the only driver included is for sqlite, that is the database of the demo and is included in the python distribution since python 2.5.

If you have setuptools installed in your system, you can install whatever you need simply with:

easy_install sqlkit

and probably you’d have better results using pip:

easy_install pip
pip install sqlkit

remember to install the backend driver, these are the examples for postgresql and mysql:

pip install psycopg2
pip install MySQL-python

sqledit/sqlkit

../_images/sqledit.png

Now you should have a working setup. The command we are going to familiarize with is sqledit that is based on a library named sqlkit. If you are a programmer and are interested in the sqlkit package you can find extensive documentation in the web site

Programming with sqlkit is a pretty simple experience that allows you to use many more features than available with sqledit, nevertheless you can do a lot of simple tasks by using sqledit alone.

New in version 9.1.

Sqledit has a flexible configuration system that allows you to add many code snippets w/o writing a true program, so that even if you decide to start with sqledit due to it’s simplicity you can add more configurations as far as you needs them. I personally started using that as my preferred way.

Sqledit can be used:

  • from command line, possibly adding arguments and options
  • from a menu entry interactively writing the URL of the database you want to edit.

If you start it with no arguments you are presented a dialog with an entry and 3 buttons:

  • you can write the url of a database of yours in the entry, e.g.:

    postgres://localhost/dbname     # sqlalchemy 5
    postgresql://localhost/dbname   # sqlalchemy 6+
    sqlite:///db.sqlite
    mysql://name:pass@host/dbname

    Note

    the URL for a sqlite database has 3 ‘/’if the database is in your current directory, 4 if you need to pass a file starting with ‘/’.

  • start the demo tour

Sqledit table listing

The demo tour is meant for developers, so that it shows source code as well, but it’s also suitable for our introduction and is a living database, so we will use that in this tutorial.

The demo presents you some examples on the left. Let’s start with... the last one! We start with the last one because it’s the window you will see when you start sqledit with an address of a real database (the demo one in this case).

The table listing

The table listing of the database is shown above: clicking on a table name pops a menu that lets you choose between:

  • table view: representation of the table in a spreadsheet fashion
  • mask view: a form with each field is displayed
  • table reflection: sqledit reads the definition for that table

Tables

Let’s choose a table view:

../_images/table.png

each field of the table is represented in a column, each type has different representations:

text:

a simple cell will render the text

numbers:

each number is adjusted to the right

dates:

dates are represented in you preferred locale that is argued from LANG variable or from locale module information

boolean:

a checkbox is used. It the NULL value is accepted, clicking the checkbox will loop between True, False and undefined

intervals:

intervals are really poorly rendered at the moment...

foreign keys:

foreign keys are represented via the value they point to in the remote table. At present only simple (not compound keys) are allowed. To help you detect that that’s a ForeignKey it’s drawn in blue. Just to be pedantic: you won’t see the real value (that may happen to be an id, normally not very interesting), you will rather see the value it points to...

As you can realize there is not real value where is points. An id points to a record of a table (e.g.: director id 1 may point to the record in director table where last_name is Fellini), but Fellini is not the value of the id: it’s rather a representation of the record that in many circumstances may be enough (and in many other is not).

So I introduced a rule: I represent it with the value of the first character field of the line. Clearly this rules is doomed to fail in some cases and you can correct it forcing a representation of the line we will call a format field. You can go in the main window of sqledit, select databases and ‘edit sqlkit field’ and you will be presented a mask to edit the value you prefer.

../_images/sqledit_config.png

filtering

you may have a lot of data and what sqlkit will help you at is to filter in a simple way. Each column has a clickable header that pops a menu entry. The first menu entry pops a filter widget:

../_images/filter-panel.png

in the image we have clicked on three column’s header: the filter on each column is composed of 4 parts: the label with the name, the operator for the filter, the checkbox to disable the filter and the entry for a value.

Some operators have pretty intuitive operators (‘>’ as bigger than or later that for dates) text have also regular expression (normally much more useful so that it’s the default) or like.

Note

you can select more filter for column, click on the label in the filter panel. You can for example say that you want all the films produced between 2000 and 2005, that means having 2 filter on the field year.

Pressing Enter on a field or the reload button will run the query and present the selected records in the TableView.

Dates are special in that you often have to filter with dates relative to the moment you do the query (today, this month,...) so that i added some shortcuts to accomplish this task (e.g.: ‘m’ means the beginning of the month). You can read more on this feature in Date filters.

totals

../_images/totals.png

One more feature of sqlkit that comes very handy is the ability to make totals in the fashion of a spreadsheet. This only works on numbers of course, and you can trigger this feature from the column menu. Since our test database does not have numbers other than for year of production, in the example I joked and computed the total on the column of the year of production. In real cases you will do sum with more interesting data...

Subtotals are a very useful feature of any total, so you can ask sqlkit to create subtotals when some value change (e.g: date, month, year, director...).

completions

When you enter data in a text entry or in a foreign key, you may find yourself typing something that is already in the database. In this cases you can have sqledit to search that text for you. Really that’s a must for Foreign Keys where you can only pick the data among those proposed.

Since the possible values may be a lot and we don’t want to wast time waiting to retrieve data that would only confuse us, we will require sqledit to show possible values pressing enter in the entry. In this case the text that we may have already entered will be used to filter the possible values and to be more precise:

Shift Enter:will trigger a search using the text at the beginning of the field
Control Enter:will trigger a search using a regexp. If you don’t know what a regexp is, consider that as a minimum it will do a search of the string in any position, but can do much more and really also depends on the database backend.
Control Shift:will disregard what you have already written and do a search on all possible values, thus emulating an enum field.

You can find complete information on how to configure Completion in the docs.

changing view

When in a table view, you may want to jump on a mask view or even keep the two open simultaneously. That can be simply done by clicking with right button in a row: the menu that appears lets you edit the row with a mask. If that’s a ForeignKey column you can even edit the value the foreign key points to.

Mask

../_images/mask.png

The other view we can use is the mask view. The records are presented by default in a form with the labels on the right and the forms on the left.

Note

This is just a default and the only one possible at the moment, but programmatically you can choose any fancy layout you want, but I won’t digress as I want to limit the information for non developers in his context.

completion

In this mask you can see that foreign keys use a combo with a completion element popdown. Same shortcut as for the table one are used to complete. A double click on the arrow let you use it as an enum field.

filters

Filters can be activated clicking on the label. the filter panel will be presented as usual.

The difference is that when the query is issued the result is presented in a tab of the filter panel and you browse the results clicking in the output tab or clicking the forward and backward arrows of the mask.

layout

If the table has many fields, you may get a layout that is not very usable. This is a limit of the interfaces at the moment, not of the sqlkit package that can handle any fancy layout as you can see looking at the examples of the demo.

The library also allows you to edit related tables (i.e.: director and movies) with no effort, in order to do this you need at least a minimum of programming, namely:

  • defining the model (as per SqlAlchemy)
  • defining the layout (this is very easy and demo has plenty of examples)

These 2 definitions can be written in the configuration for the a nick of sqledit, please read sqledit manual for details on nick configuration.

The Demo

The demo is a pretty simple way to be introduced to more advanced features that you would only have with a little of programming. I hope it will encourage you to do it and possibly to approach Python.

The very important thing to understand when reading the snippets of the demo is that each time you write the table as a string (e.g: table=’movies’) you will trigger an inspection of the database, but no assumption is made on the relationships between tables. When you pass a mapper or a class (e.g. class_=model.Movie) you are passing possibly more information.

The model in fact (you can go and see in demo/sql/model/movies.py) has lines as:

class Director(Base):
    __tablename__ = 'director'
    id             = Column(Integer, primary_key=True)
    last_name   = Column(String(60), nullable=False)
    first_name  = Column(String(60))
    nation      = Column(String(6))

    movies      = relation('movie', backref='director', cascade='all, delete-orphan',)

where the last line instructs sqlalchemy of the relation existent between the tables, and more: it adds an attribute on the class Director that holds all the movies produces by that director (and vice verse thanks to the argument backref).

Adding these information makes it possible to used the layout in a mask to produce a mask with director and all the movies, if you are interested in this part... let me know and I will add more info. For the moment I suggest you to go and read more about Relationships

Feedback

I hope you found this tutorial useful.

If you like this piece of software, have suggestion on how to improve it or improve the tutorial I’d be happy to know

cheers sandro *:-)


sqlkit-0.9.5/doc/html/misc/sqledit.html0000644000175000017500000005371011714210371017374 0ustar sandrosandro Sqledit - the standalone program to browse and edit data — sqlkit v0.9.5 documentation

Release 0.9.5 is out

I'm happy to announce that on February, 7 2012 I released version 0.9.5 of sqlkit that adds support for sqlalchemy 0.7+ as wel as some minor improvements (see changelog).

Sqledit - the standalone program to browse and edit data

Sqledit is an application that can be used by anybody without any programming skill. Basically is just needs a target database that can be fed throught a GUI or on the command line.

../_images/sqledit.png ../_images/sqledit_setup.png

Sqledit is a data editor/browser. At startup it will present a list of all tables that can be selected for editing (choosing SqlMask or SqlTable) or for introspection.

When run from command line it offers several options:

usage: %prog [options] [[url|nick] table]
   -u, --url = URL: an url to open (eg postgres://user:pass@host/dbname)
   -n, --nick = nick: a nick in ~/.sqledit/nicks
   -t, --table = table: open table 'table'
   -m, --mask: open a SqlMask (default is SqlTable)
   -b, --browser: open the table browser reguardless of nick configuration
   -T, --sqltable: open a SqlTable (default when -t is used)
   -d, --dev: open in 'dev' mode
   -D, --debug: print debug
   -g, --gtk-debug: use LogTheMethods
   -S, --sql=statement: execute statement (requires -t)
   -a, --all-tables: read all table on startup (very slow)
   -f, --field_list=fields: comma (or space) separated field list
   -o, --order_by=fields: comma separated field list
   -c, --configure: open SqlMasq on _sqlkit_table or create it
   -v, --version: prin version and exit
   -L, --load: load data when opening a table (if no table is directly
         opened, set LoadData)
   -l, --limit=LIMIT: limit to LIMIT rows
   -i, --ipython: if ipython is present it opne an ipython shell
   -C, --create-templates=mode: create in one of these modes: layout,
         hooks, programm, models, all
   , --create-tables: Create all tables in models define in metadata (models.metadata or Base.metadata)

If called without argument it will present a connection dialog:

If sqledit can find a demo in your systems, it will give you the chance to start it. If you want to type the URL of your db you can directly attempt a connection or disable it via a checkbox on the right of the entry.

The autoconnect mode is really nice but you may experiment hangs till a connection timeout if you write a wrong host name.

Since database urls are not nice to write, you can store data in a configuration file and call nick names instead.

.sqledit

You can write configurations in a file in your home called .sqledit/nicks and start sqledit on that configuration using a nickname:

[invitati]
URL = postgres://sandro:xxx@my_host/2giugno
table = partecipazioni_invitato
dev = True
field_list = nome, cognome, and all other fields
order_by = cognome, nome

[brasile]
copy = invitati
field_list = nome, cognome, email, ludo

Valid option are URL and any other option for sqledit. The special option copy force sqledit to read the other definition first and then overwrite. In this case ‘brasile’ shares all options with ‘2giugno’ but overwrites field_list.

Even if you don’t want to program in Python you may want to add configuration in a more rich way that allowed in .sqledit/nicks. That can add for example layout information or information on the relations between tables so that a Mask can present a ecord and data related to it.

Let’s say that that’s a gentle introduction to programming with sqlkit...

Nick extended configuration

Sqledit configuration may be extended beyond .sqledit/nicks file. You can add configurations for a nick in a subdirectory of .sqledit named as the nick itself. Hooks, layout, or a program can be placed directly hereq. As an example if you have a nick ‘film’ defined in .sqledit/nicks:

[film]
url = 'postgres://localhost/film

you can add customizations w/o creating a real program. Currently you can add:

layout.py:

the layout that SqlMask should use when creating the GUI. You can create an empty template for this with sqledit --create-template layout nick. That’s the place where you would register your layout.

This is autoloaded if no program.py is present

hooks.py:

the Hooks that will be used as default. You can create an empty template for this with sqledit --create-template hooks nick. That’s the place where you would register yout hooks.

This is autoloaded if no program.py is present

models.py:

this file is for definition of the classes. If you define classes here you can set Relationships between them so that a mask can display parent/child records as director/movies. Many examples in the demo.

This is autoloaded if no program.py is present

You can create an empty template for this with sqledit --create-template models nick

program.py:

a program that will be executed instead of normal sqledit. There is no particular advantage running this instead of creating a real standalone program but sometimes it just grows step by step (you start with a simple nick, than you add a layout, maybe a hook and you need more customization).

You will call the program as sqledit film and sqledit will:

  • cd to ~/.sqledit/film
  • execfile(‘program.py’, {‘opts’ : opts})
  • no other file will be autoloaded in this case

where opts is a class that holds the options you set in the nick definition (among which opts.url). You can create an empty template for this with sqledit --create-template hooks nick.

A second reason is that many times a program that uses sqlkit is a very short script and I found myself spending more time deciding a proper name and place, this already makes these decisions for you!

There is no need to start gtk.main() since this is no longer the main program, sqledit will run it before this moment

schema browser

Introspection of the database will give you the possibility to see all fields of a table showing all fields, with type, primary keys, foreign keys and indexes. If you configured a nick to jump directly on a table or any other configuration allowed by sqledit customization, you’ll need the -b (–browser) option to get to the schema browser.

Options

Calling sqledit from a command line under a Linux system with bash completion you can benefit from the completion that will look for completion in the .sqledit/nicks file and will suggest some common url (postgresql://, sqlite://...)

When primary keys are numeric you probably don’t want/need to see them, you can switch off the visualization with the primary key toggle button

The Load toggle button determines if you want to load data when opening a table.

Blank toggle button determines if you want to cast blank string fields to NULL values. When you decide to cast it you may be prompted several times if you want to save changes that you are not even aware of.

Configuring sqlkit

../_images/sqledit_config.png

Sqlkit looks for possible configuration options in some tables, that may or not be present: _sqlkit_table, and _sqlkit_field.

These tables can be edited directly from the database menu, or via <Ctrl-e> shortcut.

Completion will help yo configure the fields. Here is the meaning:

table’s field

name:

the table’s name

search field:

this is the string field that will be used when searching via foreign key. Suppose you are editing a table of movies, and you must fill in the director’s field. You write some letters and trigger a completion, that means you want sqlkit to use that text (e.g. “Fel”), select which directors are present that has that string in... well you surely want to search in the last name, but you need to tell sqlkit.

search_field is here for that.

format:

ok, you get back from completion a list of directors you still need to show them in a nice way (e.g. first_name, last_name). Here you are supposed to used the syntax “%(field_name)s”.

attributes’fields

name:the field_name
description:the label you want to be used. (Note that when using related tables you may indicate relation.field_name)
help_text:this is the tooltip that will be added to the entry
autostart:you can set an Autocompletion value for the completion

sqlkit-0.9.5/doc/html/misc/tour.html0000644000175000017500000006473611714210371016732 0ustar sandrosandro Sqlkit & Sqledit — sqlkit v0.9.5 documentation

Release 0.9.5 is out

I'm happy to announce that on February, 7 2012 I released version 0.9.5 of sqlkit that adds support for sqlalchemy 0.7+ as wel as some minor improvements (see changelog).

Sqlkit & Sqledit

Sqlkit is a tool to edit data of a database (as opposed to schemas) in the easiest possible way. By easy we mean both: easy to write for the user since completion helps you, and easy to write for the programmer as a lot of features are there to help you customize it. It’s based on PyGTK.

It provides:

  • a GUI named sqledit that can be used standalone and does not require programming knowledge
  • a Python package that mainly provides two megawidgets: SqlMask and SqlTable, very powerful classes to build your application.

A typical usage can be:

from sqlkit.widgets import SqlTable, SqlMask
from sqlkit import DbProxy

db = DbProxy(engine='sqlite:///movies.sqlite')
t = SqlMask('movie', dbproxy=db, single=True)

that will pop up an editor for the record. Filter panel will be available just clicking on the label of the fields.

Features

Main features of sqlkit:

  • Editor of databases in 2 modes: SqlTable & SqlMask. Mask can embed tables of related tables.
  • Based on sqlalchemy: can cope with many different database backends (PostgreSQL, MySQL, sqlite and firebird the ones sqlkit was tested on). Can be used to edit any selectable you can build with sqlalchemy.
  • Very powerful filtering capabilities:
    • each field can be used to filter visible records
    • date filtering possible also on relative basis (good for saved queries)
    • works on expressions too
    • works on fields in related tables embedded in mask
  • Completion both on normal fields and foreign key fields
  • Very easy way to draw a layout for mask views
  • Sqledit - the standalone program to browse and edit data: python script to edit db
  • Completely effortless editing of Relationships
  • Very easy way to set Defaults
  • Possibility to display Totals in numeric fields
  • Constraints: any possible sql constraint can be attached to a Mask or a Table. It can be expressed as a normal sqlalchemy query or with django-like syntax. Works on expressions too.
  • SqlMask and SqlTable are widgets with several Signals
  • Hooks for a very easy customization of behavior and for validation
  • More than 70 snippets of code to demonstrate each feature
table opened on movies

Table

You can see data in a tabular format using the SqlTable widget.

The code is as simple as:

t = SqlTable('movie', dbproxy=db, )

you can customize which columns to show, possible filters or constraints (see below), and a lot of others details

Mask

../_images/mask-demo.png

Records can be displayed one record at a time with the SqlMask widget. Tables can be embedded in mask to edit Relationships.

that where requested by the following instructions:

lay = """
   varchar10
   varchar200  - - -
   {N  { %time
         {>>.general

            date        interval
            datetime    time
            }
         {>.hidden_boolean
            bool    bool_null
         }
        }
      {  %numbers
         integer
         float
         numeric
      }
      {  %text
         text
         uni_text
       }
       } - - -


"""
t1 = SqlMask('all_types', dbproxy=db, layout=layout, )

Filters

../_images/filter-panel.png

Each label of both views can be clicked to get a filter panel through which we can choose an operator and filter records. Filter let you use a “human” representation of foreign keys, that means that if director_id points to a numeric id, sqlkit will let you write the (last) name of the director instead when filtering.

The filter panel will let you navigate in the output list, that can be completely customized.

Completion

Completion is triggered by F1 key, Ctrl-Enter or Shift-Enter. If the field is a foreign key it will pop up a list of foreign values otherwise it will show values currently used (just for varchar fields).

The search for completion is done using the (possibly) already written letters as filter: Control will match them at the beginning of the field, Shift (and F1) will match them in any part. The search is made using LIKE operators or regexp according to whatever is available in the db engine in use.

Layout

Very easy way to draw a layout. See A GUI description language - purpose widgets for a tour. The language used relates to glade as a markup language relates to html.

This GUI description language lets you draw a layout using field_name as place holders for the widget that you will use to edit it:

title
director_id

will be replaced by a label ‘title’ followed by an entry and a title ‘director_id’ followed by a widget suitable to edit a foreign key.

Le language gets more complicated to let you use main gtk widgets as frames, notebooks, scrollable widgets, tables....

Relationships

../_images/o2m.png

This is probably the most impressive feature.

You can build a layout in which related data are displayed in a totally natural way. The following layout requires the code:

lay = """
   first_name
   last_name
   nation
   o2m=movies:title,description,year,date_release
"""
SqlMask(Movie, layout=lay, dbproxy=db)

now you can edit director and films. The demo has plenty of working examples for there cases:

many2one:are just recognized automatically with simple introspection
many2many:is very simply added to SqlMask declaring in the layout and attribute with that name
one2many:same as many2many

Many more detail in Relationships

../_images/totals-embedded.png

Totals

It’s possible to display totals and subtotals in a table view. In this picture you can see how a table embedded into a mask can display totals.

More in Totals

Constraints

A constraint can be as simple as:

t = SqlTable('movie', dbproxy=db)
t.add_constraints(actors__country='ITA', genres__name='drama',
  year__gte=1950, year__lte=1970)

And browsing of movies will be constrained into dramas with at least one italian actor, produced between 1950 and 1970. The double underscore ‘__’ will allow spanning from one table to a related (provided a relation was established at sqlalchemy level) and from attribute name to operator to e used.

Sqledit

A full featured program (python script) that can browse a database. Many options (sqledit -h).

../_images/sqledit.png

Just try it out on your preferred database using the url in a form as one of the examples:

sqledit postgres://localhost/mydb
sqledit postgres://sandro:passwd@host:5444/mydb
sqledit mysql://localhost/mydb
sqledit sqlite:///movies.sqlite

the last is a very minimal db present in doc/db/movies.sqlite

Basic assumptions and limitations

  1. You use PrimaryKeys and ForeignKeys throughout the db. If you don’t use ForeignKeys sqlkit will just work w/o any special behavior. If you don’t use PrimaryKeys sqlkit will not even open the table.

  2. ForeignKeys are simple. Compound ForeignKeys are not yet supported, that means that you can’t use:

    class MyTable(Base):
       ...
       ForeignKeyConstraint('first_field, second_field], [referenced1, referenced2])

    This will be addressed in a future release

  3. You are using one single metadata. This is a limit but it’s my normal environment. There’s not really anything that cannot be changed easily about this, simply I didn’t have need for this, nor working cases. (While I was plenty of ideas on other features...)

  4. Spaces are not allowed in field names. This comes from the layout syntax definition.

ForeignKeys

Everywhere there’s a ForeignKey I try to represent it in the best possible way. More info in the completion chapter: foreign key description & search field

sqlkit supported backends

Sqlkit is built on sqlalchemy that allows editing db with many different engines. I use it with PostgreSQL, MySQL, sqlite and Firebird. Other engines are supported but may need a very simple addition that I’d be willing to do on demand.


sqlkit-0.9.5/doc/html/misc/missing.html0000644000175000017500000002461711714210371017404 0ustar sandrosandro What’s Missing and how to contribute — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

What’s Missing and how to contribute

Well, this is a pretty large chapter... there are tons of things that are missing from this package and that are really needed but I want to focus on the following:

  • saving queries
  • good field render for CellRenderer (notably TEXT fields and DateTime)
  • good system to warn people of all orphans when deleting
  • integrate a permission system

I’d be happy to accept any suggestion, patch or contributed feature on anything that can make sqlkit more powerful.

sandro


sqlkit-0.9.5/doc/html/misc/contents.html0000644000175000017500000002667211714210371017573 0ustar sandrosandro Download & more... — sqlkit v0.9.5 documentation
sqlkit-0.9.5/doc/html/misc/download.html0000644000175000017500000003605111714210376017542 0ustar sandrosandro Download, requirements & googlegroup — sqlkit v0.9.5 documentation

Questions?

Subscribe to out mailing list Google group:

Download, requirements & googlegroup

Requirements

Sqlkit depends on:

  • Python (>= 2.5, < 3)
  • Pygtk
  • sqlalchemy (>=0.5.4). Rel 0.9.5 is the first sqlkit release that works with sqlalchemy 0.7+.
  • python-dateutils
  • setuptools
  • the correct driver for your database of choice among the backend supported by sqlalchemy
  • babel (localization)

Changelog

this is the Changelog

Download

The code is available under an hg repository:

hg clone http://hg.argolinux.org/py/sqlkit

You can download sqlkit package 0.9.5 from here in tar or zip format.

Python package

Sqlkit is used in a production environment and great care is put in fixing any bug as soon as possible. The first stable version has been 0.8.6 in 2008.

I really appreciate any bug report particularly if based on a repeatable example, possibly starting from the demo.

Windows

Read the detailed instructions in the tutorial: Installing under Windows.

Debian/Ubuntu

Packages are available, read the instructions on how to add the repository.

Sqlkit on Pypi

Sqlkit is available via Pypi (Python Package Index), so -if you have already installed setuptools that provides the command easy_install- you can install it via easy_install or better pip:

easy_install pip
pip install sqlkit

Beware that that will fail if you don’t already have PyGTK installed.

You can also install directly with easy_install that often will fail understanding already installed packages. Should you have problems with pip you can revert to:

easy_install sqlkit

No one of these command will install the backend driver (psycopg2 for postgresql, MySQLdb for mysql,...) that you are supposed to install by yourself. Sqlite is included in Python stadard library.

Localization

We need the help from some translator to localize in different languages. It takes some 40 minutes to provide a complete set of translations for each language. Please visit the launchpad ‘s site or contact me directly.

Author

Sqlkit is developed by Alessandro Dentella


sqlkit-0.9.5/doc/html/_sources/0000755000175000017500000000000011714210372015723 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/hidden.txt0000644000175000017500000000041111534024230017707 0ustar sandrosandro.. toctree:: :maxdepth: 3 sqlkit/contents misc/tour .. toctree:: :maxdepth: 3 misc/sqledit .. toctree:: :maxdepth: 2 misc/contents layout/contents debug/contents .. sqlkit/advanced .. toctree:: :maxdepth: 1 misc/tutorials sqlkit-0.9.5/doc/html/_sources/sqlkit/0000755000175000017500000000000011714210376017236 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/sqlkit/constraints.txt0000644000175000017500000001217411533706100022345 0ustar sandrosandro.. _constraints: ============ Constraints ============ Constraints are a mean to limit browsing of data to a limited set. It's a different concept from filtering, as this enforces a limitation while filtering allows the user to change the values at will. .. _django-syntax: Django-like syntax ================== A constraint can be as simple as:: t = SqlTable('movie', dbproxy=db) t.add_constraints(actors__country='ITA', genres__name='drama', year__gte=1950, year__lte=1970) And browsing of movies will be constrained into dramas with at least one Italian actor, produced between 1950 and 1970. The double underscore '__' will allow spanning from one table to a related one (provided a relation was established at sqlalchemy level) and from attribute name to operator to be used. .. note:: This syntax is inspired to django_'s ORM syntax. The same syntax can be used to add filters to the sqlwidget, to the :ref:`completion ` and when ``.reload()`` method is used. .. _django: http://www.djangoproject.com query ======= limitation acts on ``self.query`` directly. Self query can be modified in any way accepted by ``sqlalchemy`` syntax operators --------- these operators are recognized:: OPERATORS = { 'notlike' : 'not like', 'like' : 'like', 'ilike' : 'ilike', 'notilike' : 'not ilike', 'iregexp' : '~*', 'regexp' : '~', 'notiregexp' : '!~*', 'notregexp' : '!~', 'lt' : '<', 'gt' : '>', 'lte' : '<=', 'gte' : '>=', 'eq' : '=', 'neq' : '!=', 'equal' : '=', 'in' : 'IN', 'notin' : 'NOT IN', 'null' : 'IS NULL', 'notnull' : 'IS NOT NULL', } and ``icontains`` that at the moment map to regexp operators if they exist or like with % on both sides. conjunctions ------------ In the example all conditions have been ``AND`` ed. It's possible to use ``OR`` operator adding ``or_=True`` argument. django2sqlalchemy ----------------- You can get a sqlalchemy ``ClauseList`` object starting from a django-like expression using django2sqlalchemy function:: from sqlkit.db.django_syntax import django2sqlalchemy clause_list, join_paths = django2sqlalchemy(mapper, *args) where ``args`` can be or_=boolean or any django-like query. Django2sqlalchemy returns a tuple: (clause_list, join_path): :clause_list: the list of column expression connected with ``AND`` or ``OR`` according to ``or_`` argument :join_path: the list of path needed to add to query via ``query.join()`` to have all the fields the query needs to apply the filters in clause_list A possibility is to add:: for path in join_path: query.join(path).reset_joinpoint() Filter simplified syntax ------------------------- .. automodule:: sqlkit.db.django_syntax Native sqlalchemy constraint ============================ The django syntax is in no way the only possibility. If that is not sufficient to express the constraints you need you can just use any filter on the query directly:: tk_tbl = ticket.Ticket.__table__ my_id = self.db.get_session().query(ticket.User).filter_by(username=setup.USERNAME).one().id t = sk.SqlTable(ticket.Ticket, field_list=field_list, order_by='priority', **self.meta) t.add_filter(status=1) t.query = t.query.filter(or_(tk_tbl.c['assigned_by_id'] == my_id , tk_tbl.c['assigned_to_id'] == my_id , )) this example shows how to force a constraint on ticket requiring that the ticket be assigned *by* or *to* USERNAME. Any further filter applied interactively will be applied *on top* of this constraint. This is equivalent to writing:: t.add_constraint(assigned_by_id=my_id, assigned_to_id=my_id, OR=True) At the moment of this writing it's not possible to write:: t.add_constraint(assigned_by_id__username=USERNAME) as the foreign key relation is not followed in this context. aliased constraints -------------------- Sometimes you need to have aliased constraints in order to ha sqlalchemy build an aliased join to related classes on which you may want to set constraints. This may be necessary if you want to use constraints along with order_by and possibly filters on foreign keys. example +++++++ Suppose you have an 'address' table with a ``user_id`` field that is a ForeignKey to a ``user.id`` column. Suppose you want to open a SqlTable on table addresses, ordered on ``user_id`` (well... surely you don't want to order by the ``id`` value, you probably have a ``first_name`` field that is much more appropriate for sorting). Now suppose you also have an ``active`` field on the user_table. You can achieve this (simple) in the following way:: t = SqlTable('address', order_by='user_id__first_name', ...) t.add_constraint(user_id__active=True, aliased=True) the order_by argument implicitly have built a join with the user table, the same stands for the constraint ``user_id__active``. Sqlalchemy would have complained that the table 'user' was already present, so you need to alias it. I'll probably make this the default in a future version. sqlkit-0.9.5/doc/html/_sources/sqlkit/widgets.txt0000644000175000017500000000014611547676300021455 0ustar sandrosandro======== Widgets ======== .. toctree:: :maxdepth: 3 sqlwidget mask table printing sqlkit-0.9.5/doc/html/_sources/sqlkit/browsing.txt0000644000175000017500000000020111421620210021605 0ustar sandrosandro============== Browsing data ============== .. toctree:: :maxdepth: 2 constraints filters totals localization sqlkit-0.9.5/doc/html/_sources/sqlkit/sqlwidget.txt0000644000175000017500000006205211673441611022011 0ustar sandrosandro============ Sqlwidget ============ This is the base class for :ref:`mask` and :ref:`table` that implements the common interface. mandatory argument ================== The first and mandatory argument is the ``mapper`` of the object that will be retrieved and displayed. Alternatively any other objects from which the sqlwidget infers them (e.g.: the tablename and the metadata where the table can be autoloaded): * a mapper * a mapped class * table name (requires you also pass metadata) In older releases different options: ``class_``, ``table``, or ``mapper`` where used. Now a DeprecationWarning is raised if you use those opts. main options ============ Any sqlwidget needs a ``session`` as well. Metadata is also used when auto-loading tables referenced by foreign keys to display a better representation of the referenced record. .. _dbproxy: dbproxy ------- Since a typical scenario is to have to provide a session different in each SqlWidget and a metadata, an object is provided -``dbproxy``- that can be initialized from the engine specification:: from sqlkit.widgets import SqlMask, SqlTable from sqlkit import dbproxy db = proxy.DbProxy(engine="sqlite:///model/movies.sqlite") SqlTable("movies", dbproxy=db) below you can see some alternatives that would work as well:: Session = sessionmaker(bind=self.metadata.bind, autocommit=False) sess = Session() meta = MetaData() meta.bind = "sqlite:///model/movies.sqlite" # SqlTable("movies", session=sess, metadata=meta) # passing a mapped class (Movie here is build with declarative layer): # the metadata is found from the mapper.local_table.metadata SqlTable(Movies, session=sess) Session are created with ``autoflush=False``, ``expire_on_commit=False`` but can be changed when building ``DbProxy``. Since version 0.8.6 default value for *autocommit* has been turned to *True* to prevent `idle in transaction`_ in postgresql. session & expire_on_commit ========================== The reason to have ``expire_on_commit=False`` is that if you don't set it, after every commit, you have to reload all objects and the interface turns very slow, especially when working with a remote database. ``expire_on_commit`` is a recent addition to sqlalchemy session (around sa rel. 5.03rc) so I try it and fallback to default that would turn to be slower. Previous 0.5 rel called it ``autoflush`` .. autoclass:: sqlkit.widgets.common.sqlwidget.SqlWidget(see_below) :members: __init__, add_constraint, add_filter, resize, set_records, set_format, is_mask, is_table, sb, get_value, set_value, get_current_obj, set_mode, add_temporary_item, reload, commit, add_validation_error, add_not_null_error Attributes =========== .. attribute:: filter_panel The :ref:`filter_panel` widget .. attribute:: related a container for all related sqlwidgets (i.e.: SqlWidgets that have a relation with this sqlwidget defined by sqlalchemy and that are displayed in the widget). This is used in all situation in which you need to fine-tune the configuration of a related table (completion, layout, ...) .. attribute:: completions a container of all completion objects. Needed to change the behavior of some completion (see :ref:`completion`) .. attribute:: gui_fields a container for all validation fields. A validation fields is an object that lives in ``sqlkit.fields`` and that knows how to represent a field and how to validate it. .. attribute:: gui_field_mapping A dict used to force a map between a ``sqlkit.Field`` and a gui field. Read more in :ref:`fields`. .. attribute:: query the sqlalchemy query object (``session.query(mapper)``) with all constraints applied. Can be manipulated as necessary as long as it stay a query object. .. attribute:: layout the layout definition for this SqlWidget. This is the used definition for SqlMask while for SqlTable is only used if the record is opened in SqlMask (right click on the record in SqlTable). You can also set it on a related table:: GUI="director @ m2m=movies" t = SqlMask(Director, layout=GUI, ...) t.related.movies.layout = "title @ m2m=actors" .. attribute:: lay_obj the sqlkit.layout.Layout object used to create the layout .. attribute:: current the object represented by a mask or a possible selected record in a table or None. In :class:`SqlTable ` in the transient in which you are saving a record in a table (when selection changes) ``self.current`` will point to the obj that is to be saved (while the selected object may already be another one). .. attribute:: field_list a list of the field names the GUI is handling. It comprises PropertyLoaders (i.e. properties of the class that act as a loader of other info - all relation are seen as PropertLoaders w/o column) .. attribute:: order_by an order_by string or clause element. Same as parameter passed to the class. It's a property, can be set in any moment. .. attribute:: session the sqlalchemy session used for querying .. attribute:: defaults an instance of ``sqlkit.db.defaults.Defauls`` instantiated with local=True as explained in :ref:`local_defaults`. Any default set with this instance will only be visible in this sqlwidget. .. attribute:: title the title of the Window. .. attribute:: mode the mode describing permissions of the widget. See :ref:`set_mode ` .. attribute:: noup noup can be a set of field_name or a comma separated string with possible +- sign to add/remove field_names to the set of field_names that will not be possible to update Note that to add a non editable field_name you must used '+field_name'. Using simply 'field_name' will reset the list to only that field_name .. attribute:: actiongroup_* see :ref:`uimanager` .. attribute:: ui_manager see :ref:`uimanager` .. attribute:: relationship_leader Th case this sqlwidget is representing a relation of a SqlMask, that SqlMask is referred to as a relationship_leader. .. _sqlalchemy: http://www.sqlalchemy.org .. _signals: Signals ======== :pre-display: A record is about to be displayed. .. function:: pre_display_cb(sqlwidget, obj): :param sqlwidget: the widget that emitted the signal :param obj: the object that is about to be displayed :record-selected: A record has been displayed in Mask or selected in Table. The callback will just receive the widget as argument. .. function:: records_selected_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-saved: A record has been saved. This signal is *not* emitted from within session extension. That means you are sure there will be just one signal for each button press on "save" button. This is issued from within the :meth:`SqlWidget.commit` method independently from the fact that a real modification occurred, so you are not guaranteed any modification took place. Was originally added to implement a destroy of the widget when the save operation was performed. The callback will just receive the widget as argument. .. function:: record_saved_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-new: A new record has been added. The callback will just receive the widget as argument. .. function:: record_new_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :record-deleted: A record has been deleted. The callback will receive the widget and the obj. .. function:: record_new_cb(sqlwidget, deleted_obj): :param sqlwidget: the widget that emitted the signal :param deleted_obj: the obje that was deleted Note that this signal is emitted only for records deleted explicitly, i.e. records that where the ``current record`` in a mask/table. If a record is deleted as a side effect (e.g.: becouse cascade="delete-orphan" is set) no signal is emitted for that obj. :records-displayed: Records have been displayed. This may be after :meth:`SqlWidget.reload` or :meth:`SqlWidget.set_records`. The callback will just receive the widget as argument. See also :ref:`context changed ` to see another signal that better tracks *any* change .. function:: records_displayed_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal :after-flush: flush has occurred, normally commit should not add any errors. This is implemented with a SessionExtension: if you used the default session obtained via get_session() you are assured that it will be correct. If you create a session by yourself, be sure to add ``sqlkit.db.proxy.SKSessionExtension`` to the session extensions or you won't have this signal. Read more detailed explanation in :ref:`hook on_after_flush ` .. function:: after_flush_callback(sqlwidget, object, session) :param sqlwidget: the sqlwidget that emitted the signal :param object: the object that was *current* when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in "dirty state", "new" or "deleted", but current was the one selected in a table or displayed in the main mask :param session: the session that was flushed. The moment in which the signal is emitted you can still dig into ``session.dirty``, ``session.new`` and ``session.deleted`` and find which attributes have been changed (you may want to use :func:`get_differences`) :after-commit: run from within ``after-commit`` SessionExtension. Callback signature is identical to ``after_flush_callback`` above. :delete-event: emitted when the sqlwidget is destroyed .. function:: delete_event_callback(sqlwidget) :param sqlwidget: the sqlwidget that emitted the signal .. _hooks: Hooks ===== Beside signals there is another way to add controls: hooks. A hook is a function that will be called in particular moments only if present. Hooks are the main way to customize the behavior of a sqlwidget. Some of the hooks (``on_validation.*``) are related to validation other are related to configuration (``on init``), others (``on_activate``) may be used to save typing. Hooks are searched for in the methods of the instance of a class declared in the optional ``hooks`` argument or in the global registered hooks (see below). Hooks can be *registered* using :func:`sqlkit.db.utils.register_hook` (and :func:`get_hook ` module) so that any sqlwidget built on that table will use those hooks (unless the table is part of a join or another selectable!!). The advantage is that browsing data (e.g. using right click on a table row) can lead to opening tables that are not configured: registering hooks is a way to enforce configuration (and possibly constraints) on any widget. As layout hooks can be registered with a nick (default is ``default``) so that you can register different hooks for different editing flavors. E.g/: you can have a table for *people* and you can decide to open it with *customer* or *provider* layout/hooks just registering both layout and hooks and using argument ``layout_nick``. Use it with care as it may lead in situation in which not all fields are present (due e.g. to a different layout or different field_list) The following hooks are defined: :on_change_value__field_name: this hook is used to trace *changes* in the widget. It's mainly meant to be used interactively but it is also triggered when :meth:`set_value ` is invoked with initial=False. It's **not** changed if the value is set on record change. Currently it behaves differently for Table widgets or Mask widgets. Mask widgets invoke it each meaningful changes i.e. each char for varchar/int field, each time a value is choosen for enum/foreign key fields, when a date is selected or validated for Date widgets. Table widgets invoke this hook only when the field is leaved or activated (Return is pressed). .. note:: Implementation of this hooks is currently limited to some fields: Varchar, Int, Float, Numeric, Enum, ForeignKey, Date, DateTime, Bool. Text, Images, Time and Interval are not implemented. Example 63c is a complete example that shows it. .. function:: on_change_value__field_name(sqlwidget, field_name, value, fkvalue, field) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that runs the hook :param field_name: the field_name :param value: the new value :param fkvalue: if field is a ForeignKey or enum, the displayed value :param field: the field .. _on_completion: :on_completion__field_name: called when a completion is chosen .. function:: on_completion__field_name(sqlwidget, field_name, obj) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that runs the hook :param field_name: the field_name :param obj: the matched object in the completion. This obj has attributes for each *field_name* named in :attr:`attrs ` attribute of the completion. You can add field_names to that attribute if you need them in this hook function:: sqlwidget.completions.director_id.attrs += ['nation'] allows you to add a completion hook:: def on_completion__director_id(self, sqlwidget, field_name, obj): print obj.nation sqlwidget.set_value('nation', obj.nation) you can also reach field attributes as dict values: obj['nation']. .. _on_save_as: :save_as: .. versionadded:: 0.9.3 this hook is invoked each time a record is saved as a duplicate of another one. After the new record is filled and before it's saved you can customize at your will. Callback function: .. function:: on_save_as(sqlwidget, old, new): :param sqlwidget: the mask/table that invoked the hook :param old: the old object that was copied :param new: the new object When this hook is present, no warning on how the copy is handled is raised as it's considered that the programmer has already coped with all the tricky issues. :on_validation: called when all the values have been collected in the object, before calling validation on all fields and related widgets. That's a good point to implement any procedure to add automatism's. Within this hook you can propagate errors to the validation machinery in two of ways: 1. raising ``sqlkit.exc.ValidationError``. Simple when just one error is found 2. filling self.validation_errors: a dict holding all errors. The *key* is the field_name or "record_validation", the value a list of error messages .. function:: on_validation(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. _on_field__validation: :on_field_validation__field_name: called to validate a single field as for the previous ``on_validation``. This is called from within the :meth:`field's validation method ` 1. raising ``sqlkit.exc.ValidationError``. Simple when just one error is found 2. filling self.validation_errors: a dict holding all errors. The key is the field_name or "record_validation", the value a list of error messages .. function:: on_validation(sqlwidget, field_name, field_value, field) :on_activate__field_name: when *Return* is pressed in Mask or Table. Good to complete fields via calculation on other fields (e.g.: total, vat...). The name derive from the GTK name 'activate' that is when you press 'Return' in an entry, even thought in a Table's treeview it's really connected to the cell's ``edited`` signal (limited to Varchar and Numeric columns). .. function:: on_activate__field_name(sqlwidget, field_name, field) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook :param field_name: the field_name :param field: the ``sqlkit.widgets.common.field`` :on_init: run as the last command of __init__. It's main purpose is to allow to configure a widget in a way that will be handed over to a possible SqlMask generated right-clicking from a table row (see :ref:`recordinmask`). .. function:: on_init(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. _on_pre_layout: :on_pre_layout: run before the layout is setup. It's main purpose is to allow to add fields in :attr:`gui_field_mapping` (that is only useful for SqlMask). Note that you can force a field for a table's attribute if you want setting info's *field* key pointing to that field's class. .. function:: on_init(sqlwidget) :param sqlwidget: the sqlwidget (SqlMask or SqlTable) that run the hook .. note:: **Hooks invoked within the session extensions** The hooks that are called from within the session extensions, can be called **several times** if there are more sqlwigets sharing the same session (that happens for example each time you open a mask to edit the row of a table i.e.: :ref:`recordinmask`) .. _on_after_flush: :on_after_flush: run as the signal with the same name from within ``after_flush`` session extension method. This hook is completely similar to ``after-flush`` signal, but is meant to be defined in a separate class so that it's easier to propagate validation hooks to a spawned child (i.e.: :ref:`recordinmask`, when opening a mask by right clicking on a table record). You'll see that ``on_after_flush`` is called eather. From the sqlalchemy documentation: "Note that the session's state is still in pre-flush, i.e. *new*, *dirty*, and *deleted* lists still show pre-flush state as well as the history settings on instance attributes". This is true for *after_commit* hook as well, it is not true for *after_flush_postexec*, that on the other had has already setup relation. I end up in some circumstances to split the callback in two phases: one that detects if an action is needed from within the after_flush/after_commit phase, the second (may be a mail, or any other action) from within the :ref:`hook on_record_saved `, so that I can use the relations. Beware that you may have one call to ``on_after_flush`` and more different calls to ``on_after_commit`` .. function:: on_after_flush_callback(sqlwidget, object, session) :param sqlwidget: the sqlwidget that emitted the signal :param object: the object that was *current* when session was flushed. Current means that it was the main object represented. Many other widgets may be present, possibly in "dirty state", "new" or "deleted", but current was the one selected in a table or displayed in the main mask :param session: the session that was flushed. The moment in which the signal is emitted you can still dig into ``session.dirty``, ``session.new`` and ``session.deleted`` and find which attributes have been changed (you may want to use `get_differences`), but you won't have correctly setup relation's object :on_after_flush_postexec: run as the signal with the same name from within ``after_flush_postexec`` session extension method. As in the precedent hook this is called exactly within the session extension method by the same name. When it's run the session will have no longer information on session.dirty/session.new/session.delete but will have all relations set-up. The callback has the same signature as for :ref:`hook on_after_flush ` :on_after_commit: run after commit after_commit session extension method The callback has the same signature as for :ref:`hook on_after_flush ` .. _on_record_saved: :on_record_saved: run from within the commit method of the widget, that assures that will be issued just once. It's just equivalent to the signal with the callback has the same signature: .. function:: record_saved_cb(sqlwidget): :param sqlwidget: the widget that emitted the signal Hooks for related widgets ------------------------- If you need to add hooks for related sqlwidgets (those that display related records as explained in :ref:`relationships`) you need to add the relationship_path in the method name, i.e. the value you have used in the table definition for the relation (and in the layout as in m2m=actors) .. _wav_example: example +++++++ This example shows how to use hooks to play a sound on completion:: class Hook(obj): def on_completion__genre(self, field_name, obj): if obj.genre == 'horror': play('horror.wav') MovieMask(class=models.Movie, hooks=Hook(), ...) Registered hooks and SessionEtensions ------------------------------------- When hooks are :ref:`registered ` their customizations are enforced each time the model for which they're registered is called. That adds some complications you should be sure to understand if using one of these hooks: * on_after_flush * on_after_commit * on_after_flush_postexec These hooks are called within a SessionExtension that calls hooks on any sqlwidget that may be using the same session. An m2m, m2o relation table share the same session as the Mask that holds them so that it's pretty normal to have several different tables within the same session. From the SessionExtension hooks are searched for in any of these so that you should write the hooks keeping in mind it can be called from another sqlwiget's commit. As an example, suppose you have a Mask with the following layout:: first_name last_name m2m=genres m2m=actors suppose *movies*, *genres* and *actors* have registered ``on_after_commit`` hooks. They will all be called on any commit. Adding a genre object will trigger *on_after_commit* on the Actor's table and vice verse. .. _diff: get_differences --------------- Module :ref:`sqlkit.db.utils ` provides a simple function that yields all modified attributes of an object, along with their old and new values .. function:: get_differences(obj) :param obj: the object to be inspected :param session: the session the object belongs to :rtype: field_name, old_value, new_value. Old value and new value are lists. .. function:: get_history(obj, field_name) :param obj: the object to be inspected :param field_name: the field_name :param session: the session the object belongs to :rtype: new_value, unchanged_value, old_value, . Old value and new value are lists. The order is different from ``get_differences`` as this is exactly what is returned from the SA function Saving varchar and text fields ============================== Text fields with empty values will be saved as NULL. To change this behavior you need to set blank=True on fields:: t.gui_fields[field_name].blank = True .. _`idle in transaction`: http://groups.google.it/group/sqlalchemy/browse_frm/thread/6abb815a3728d41c?hl=it&tvc=1&q=idle .. _uimanager: UiManager: menu and actions =========================== Menu entries are handled via standard gtk.UiManager interface. One interface is created for each toplevel Window and for each :ref:`view ` in a SqlTable widget. You can see some examples in the demo (70-72). Standard actions are divided into the following categories:: General (self.actiongroup_general) PendingDifferences Quit Go Modify Tools File Help About Table (self.actiongroup_table) HideColumns ShowColumns Records MaskViewFKey Export MaskView Zoom-fit Insert (self.actiongroup_insert) New Save-as (just for SqlMask) Delete (self.actiongroup_delete) Delete RecordDelete Update (self.actiongroup_update) Save Undo (just for SqlMask) Browse (self.actiongroup_browse) Filters Reload Select (self.actiongroup_select) Back Forward Print (self.actiongroup_print) Print Debug (self.actiongroup_debug) Gtk-tree While for each table's view you have:: Table HideColumns ShowColumns Records MaskViewFKey Export MaskView Zoom-fit Insert New Delete Delete RecordDelete Changing an entry ------------------ To change an entry you can: #. add an actiongroup in which you have defined an action with the same name #. insert this actiongroup *before* it's relevant one (position 0 is normally a good choice Example #72 in the demo shows how to do it Adding an entry --------------- the standard way is shown in demo snippet #70: #. create an xml definition and add it to ui_manager #. create an action and add it to an actiongroup You can also use a SqlTable method :ref:`add_temporary_item ` that will add a temporary entry, so that it can be contextually changed. This way is demonstrated in demo snippet #71 sqlkit-0.9.5/doc/html/_sources/sqlkit/validation.txt0000644000175000017500000000424711530470276022143 0ustar sandrosandro.. _validation: =========== Validation =========== Validation is very important and is mainly accomplished via :ref:`hooks`: ``on_validation`` and ``on_field_validation__field_name`` are expressly created for that with all the variants for related tables. There are many different snippets in the demo related to validation and hooks: they are part of the documentation. Validation Errors ------------------ The validation has the following steps: 1. Each field in ``gui_fields``, i.e. all fields that have a graphical representation are looked for the value and a hook named ``on_field_validation__field_name`` -if present- is run . .. note:: At present fields representing m2m or o2m relations (e.g. *movies* in a *director* class) are not searched for ``on_field_validation__field_name`` hook. You can use ``on_validation`` hook to set validation on relations Each method of the hooks class can raise a ValidationWarning directly that is caught by the main validation loop, and populates ``validation_error`` dict or can feed this dict via the sqlwidget's method :meth:`add_validation_error ` or :meth:`add_not_null_error ` This operation is repeated for each related process and the main ``validation_error`` dict is updated. 2. If the ``validation_error`` dict has collected some errors a *ValidationErrorDialog* is presented to the user:: class Hooks(object): def on_field_validation__year(self, mask, field_name, field_value, field): if field_value > 2020: raise ValidationError("Hey: how can you know the future!") t = SqlMask(model.Movie, layout=lay, label_map={'genres.name':'genres'}, dbproxy=db, hooks=Hooks()) that would raise a ValidationDialog as follows: .. image:: ../img/year.png Validation Warnings ------------------- In a similar way you can raise/add a Validation Warning that will warn with a message but will not abort the process. *ValidationWarningDialog* is presented after *ValidationErrorDialog* if both are needed. sqlkit-0.9.5/doc/html/_sources/sqlkit/printing.txt0000644000175000017500000000307711547676300021647 0ustar sandrosandro========== Printing ========== Sqlkit adds printing capability throught :ref:`oootemplate` module that in turn uses templates created with Openoffice.org_ and very simple syntax. In this module 'print' is used in a loose way. In all situations 'print' means producing a printable file, it can be an ``.odt`` file or a ``.pdf`` one. .. note:: In a network environment you'll probably use a **remote** server that means the file will not be generated locally. Openoffice 3.1 comes with python2.6 interpreter and uno module even under Windows. So that there's no problem using that interpreter or using a different interpreter but pointing to it's modules. If you need to have uno modules under windows you can follow instructions in this tutorial_ or on stackoverflow_ .. automodule:: sqlkit.misc.printing .. _psignals: Signals ======== :context-ready: the context has been prepared. You can connect to this signal to add element to the context. .. function:: context_ready_cb(printtool, context, template_name, sqlwidget): :param printer: the printing.PrintTool object that emitted the signal :param context: the context :param template_name: the template name that is rendered :param sqlwidget: the sqlwidget .. _oootemplate: http://oootemplate.argolinux.org .. _Openoffice.org: http://www.openoffice.org .. _stackoverflow: http://stackoverflow.com/questions/4270962/using-pyuno-with-my-existing-pythonn-installation .. _tutorial: http://user.services.openoffice.org/en/forum/viewtopic.php?f=45&t=36370&p=166783 sqlkit-0.9.5/doc/html/_sources/sqlkit/contents.txt0000644000175000017500000000043711547676300021647 0ustar sandrosandro======= SQLKit ======= .. sidebar:: Release This documentation is relative to the **last mercurial release**. Documentation for your release is in :file:`doc` directory .. toctree:: :maxdepth: 2 widgets browsing editing advanced/contents ../printing/contents sqlkit-0.9.5/doc/html/_sources/sqlkit/editing.txt0000644000175000017500000000020411421620210021401 0ustar sandrosandro============== Editing data ============== .. toctree:: :maxdepth: 2 completion validation relationship defaults sqlkit-0.9.5/doc/html/_sources/sqlkit/defaults.txt0000644000175000017500000000360211530470276021612 0ustar sandrosandro.. _defaults: ========== Defaults ========== Defaults are a handy way to avoid typing too much. Defaults are handled by sqlkit.db.defaults.Default class:: from sqlkit.db import defaults def_visita = defaults.Defaults('cliente_visita', metadata=db.metadata, local=False) def_visita.set_defaults( {'data' : def_visita.now, 'telefonata' : True}) def_visita.set_default( title='Write a title...', duration=90) def_visita.fk('director_id', 'first_name', 'Olmi') *Defaults* class requires a table name and a metaclass to know where to autoload/read table definition. Defaults can be local to a sqlwidget or global to the application. set_default =========== The main method is ``set_defaults`` that requires a dictionary as argument with *field_name* as keys and *field defaults* as value. Alternatively ``set_default`` accepts keyword args. fk === Default in foreign key are set literally via ``fk``. It will follow the reference and find the correct value now & today ============= attributes ``now`` and ``today`` will be substituted appropriately when creating the field SA defaults & server defaults ============================= At the moment Sqlalchemy defaults are handled only in case of a fixed value (not a callable) nor defaults defined in the server. Adding a new record with an unhandled default will result in an empty value. .. _local_defaults: Defaults local to the application ================================= Sqlwidgets have an attribute called ``defaults`` that is an instance of ``sqlwidget.db.defaults.Defaults`` already correctly instantiated that allows to set defaults local only to the instance of sqlwidget they belong to. You can use such a default in any of the following ways:: m = SqlMask('movie', ...) m.defaults.set_default(actor=False) m.defaults.set_defaults( {'title' : 'write your preferred...'} ) m.defaults.fk('director_id', 'first_name', 'Fellini') sqlkit-0.9.5/doc/html/_sources/sqlkit/localization.txt0000644000175000017500000001164111530470276022475 0ustar sandrosandro.. _localization: ============== Localization ============== localization support comprises: * localization of number (decimal separators and thousand grouping) * localization of date/datetime fields * library messages translation (we need help bu translators, there is a project in launchpad_) * translation of field_name (see below) Numbers and dates localization is provided by babel_ Numbers ======= Numbers are represented according to your locale settings as follows: :integer: grouping thousands (format: '#.###') :floats: grouping thousands, no limit on decimal (format: '#,###.#') :decimals: grouping thousands, number of digits according to type.scale (format: '#,###.00', for Numeric(8,2)) forcing format representation ------------------------------ A different representation can be forced using `Number Format Pattern Syntax`_ and setting the new values in the gui_field class via the set_format method that takes a dictionary as argument or the format argument to the widget:: fmt = { 'year' : '#', # don't show thousand grouping 'quantity' : ('#,###.#', 'en'), } t = SqlTable(.... format=fmt) # fmt as argument t.set_format(fmt) # fmt as method where the two possible forms are shown. If the value is a tuple, the second element is a locale to be used for the representation (as `quantity` in the example) Dates ===== dates are formatted according to your locale and the default format is 'short' for date/datetime and 'HH:mm' for time. forcing format representation ------------------------------ A different representation can be forced using `Date Format Pattern Syntax`_ and setting the new values in the gui_field class via the set_format as illustrated above for numbers. .. note:: for the time being this only works for ``SqlTable``, ``SqlMask`` uses a different widget that needs to be improved, so it's possible to set a different date format but not using babel. Temporarily you should set date_format attribute on the gui_field.widget.gtkwidget... Messages ========= Translation of messages is provided by the standard gettext module. If you'd like to contribute a translation for your language read the README.localization in the distribution folder. Translation of field_names of each column ------------------------------------------ Since many of the labels that appear in a mask and column headers of a table default to the name of the column, it's important to add the possibility to translate those strings as well, in order to give to the resulting mask a friendly look. The same column name may be translated differently in different tables so sqlkit adds a layer in the way of translation via a "label_map" that can be passed directly to the layout object or to the sqlwidget (label_map option). This ``label_map`` is a dictionary whose key is the ``field_name`` and the value is a tuple with a description and a help_text to show as tooltip on the label. It instead of a tuple a string is found, it's considered a label and the tooltip is set to None, Each of these are to be considered msgid that gettext will further try to translate:: { 'first_name' : ('First Name', 'Write the family name of the director')} Description and help_text can be stored in the table ``_sqlkit_field`` that can be easily edit with ``sqledit -c URL``. Each time a sqlwidget is instantiated it will try to see if any info for the tables it is editing was written in the database and add those to what can be passed to the widget as "label_map". label translation for related tables ++++++++++++++++++++++++++++++++++++ When related tables are used, the name of the columns may need different translation in each related table. Suppose you have a project mask with related table that point to users, one for 'staff' and another for 'manager', as in:: m2m=staff:username m2m=manager:username both would show up as 'username', that is pretty misleading. You can then add two keys to label_map:: 'staff.username': ('staff', None) 'manager.username': ('manager', None) that would translate as desired. That can be conveniently set in the _sqlkit_field table, in which case only the description column needs to be filled with 'staff'/'manager' Tooltips ---------- What we described is also the simpler way to add tooltips to labels and buttons in your layout. This helps delegating to the customer the personalization of the hints he wants to add to the GUI. In the table ``_sqlkit_field`` each row should represent a field of a table, but there's no harm adding to it more fields that can be the name of buttons or relationships (e.g.: author) just for the sake of translation and tooltip. .. _`Number Format Pattern Syntax`: http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html#numberpattern .. _`Date Format Pattern Syntax`: http://java.sun.com/docs/books/tutorial/i18n/format/dateFormat.html .. _babel: http://babel.edgewall.org .. _launchpad: https://launchpad.net/sqlkit sqlkit-0.9.5/doc/html/_sources/sqlkit/table.txt0000644000175000017500000002445211533706143021076 0ustar sandrosandro.. _table: =========== SqlTable =========== .. image:: ../img/table-demo.png :align: right :scale: 50% :class: preview :alt: table opened on movies SqlTable is a widget that presents data in a table mode. Columns that are foreign keys are presented in blue and a :ref:`description` of the foreign key is used. .. autoclass:: sqlkit.widgets.SqlTable :members: views, totals, edited_path, create_view, set_field_list, set_editable, set_opts, select_path, hide_fields, get_selected_path, get_obj_at_path, add_record, set_fk_layout, record_in_mask, fkey_record_in_mask, add_row_filter .. image:: ../img/menu.png :align: right Column headers ============== Each column is clickable. Clicking on the column pops up a menu that enables: - to add a filter on this field (see :ref:`filters`). This same action can be done by pressing ``f`` on the column w/o opening the column menu. Only persisted fields can be filtered on. - to sort on this field (database sorting). Note that sorting on a ForeignKey will trigger a join with the referenced table and an attempt to sort on the :ref:`description` (In general you don't want to sort on the ``id``). A "local sort", i.e. a sort done without hitting the database is performed when you sort a related table (e.g.: as explained in :ref:`relationships`) or when you press ``s`` on a column after clicking the table (to set the *focus* on the table) - to add a total on this column (if the column is numeric). This same action can be done by pressing ``t`` on the column w/o opening the column menu. - to toggle a brake on this column (see docs for :ref:`totals`). This same action can be done by pressing ``b`` on the column w/o opening the column menu. - to hide a column. This same action can be done by pressing ``h`` on the column w/o opening the column menu. Cell renderers ============== Boolean ------- Booleans are represented with CellRendererToggle, if the field is nullable, the value loops over 3 states. Text ---- I don't have a satisfactory CellRender for long text. Any hint is appreciated. Export ======= A very minimal export function allows to export visible data in a .csv (comma separated value) format. Follow File -> Export. That function can be reach also right-clicking on the table, thus allowing to export that particular :ref:`view `. Shortcut ======== in the treeeview ---------------- :Control-x: eliminates a line :Control-k: eliminates a line :Control-s: saves the record :Control-q: quits the table :Control-n: opens a new record :Control-z: zoom the treeview :Shift-Z: zoom the treeview in a related table (nested in a mask) :s: sorts the table locally (toggle between ascending and descending) :f: open a filter for this column :t: create a total if the column is numeric :v: toggle visibility of some rows, needs that you set a visibility function via :meth:`sqlkit.widgets.SqlTable.add_row_filter` :b: toggles a break for subtotals on this column. In addition to this operation it also sort on the column as it often happens you want a subtotal after having ordered. Adding a break from the column menu does not order. in the editable cell -------------------- :Control-Enter: pops a completion (regexp mode) :Shift-Enter: pops a completion (start mode) :Esc: aborts editing Signals ======= :button-press-event: this event is emitted when clicking in the treeview if there are no pending validation errors (in which case you are forced to solve them before). It is mainly used to handle the default menu. Look in the example to see how you can manipulate the menu. .. method:: button_press_cb(table, event, obj, field_name, menu, treeview) :param table: the SqlTable that emitted the signal :param event: the gtk.gdk.Event associated :param obj: the object currently selected or None :param field_name: the field_name of the selected column :param menu: the default menu that il popped up by this button-press-event if event.button == 3 or None :param treeview: the TreeView widget that was clicked. You may get the view the treeview belongs to with ``treeview.get_data('view')`` .. versionadded:: 8.7 .. _context_changed: :context-changed: Records have been displayed or selection was changed. This is used to track any change in the records both selected or displayed and was added to be used by RecordInMask below .. function:: records_displayed_cb(sqlwidget, current): :param sqlwidget: the widget that emitted the signal :param current: the obj that was current in the row or the first record in the liststore. .. _recordinmask: RecordInMask ============ Clicking on any row of the treeview gives the possibility to show the record in a Mask View. If a layout was passed to the ``SqlTable``, it will be passed to SqlMask. In this case all hooks, fields possibly configured and completions will be copied in the new Mask. Same for the hooks. If you need to configure this Mask in a particular fashion you can put all configuration in a hook named :ref:`on_init `. This code will be executed both for Table and Mask generation that allows to use the same Hook for both widgets. Signals and callbacks are arranged so that it will follow the selection of the table. The newly created mask will inherit the :ref:`mode ` of the table, i.e. if the table was read-only the mask will be opened in read-only mode. On the other hand the mask will be have browsing inhibited. If a layout was registered for the database table it will be used. You can programmatically open this SqlMask using :meth:`fkey_record_in_mask `:: t = SqlTable(...) m = t.record_in_mask() RecordInMask for ForeignKeys ============================ Similarly, if the column in which you click is a ForeignKey, the popped menu will show an entry to edit the referenced record in a mask. To be sure it will have the customization you want you can :ref:`register ` Layout, Hooks and Class. In the case you have several possible layout for the same fk, and you want to use a layout that is not the default one, you can use ``set_fk_layout`` You can programmatically open the foreign key mask using :meth:`record_in_mask `:: t = SqlTable(model.Movie, ...) m = t.fkey_record_in_mask('director_id') .. _views: Views ===== Views (added in rel 0.8.8) are a way to view the same data in two different TreeViews. This way you can split a very large table into different chunks (vertically), leaving some columns to a view and others to a different view. The same column may be repeated in different views. .. figure:: ../img/table-views.png :align: right An example of a table with views: *numbers* and *dates* are really fields of the same table in the database. .. _add_view_column: Adding a column --------------- You can add columns to a view if there's a corresponding ``field`` (sqlkit.fields.Field) in ``table.gui_field``. Examples #31 and #32 show how to do that that essentially boils down to: #. Create a Field class that has a method that uses as input the object and produces the output, you'll need a clean_value() method s well if you want to be able to create a total on that column (see example #32) #. Add an instance of that Field to the fields used in the gui: table.gui_fields #. Create a Column and add it to the View :: class ObjField(fields.VarcharField): """ A field that presents the obj """ def clean_value(self, value): return value and "%(year)s %(title)s" % DictLike(value) my_field = ObjField('new_column', {'editable' : False, 'type' : str, 'length' : 30}) t.gui_fields['new_column'] = my_field ## create a column col = columns.VarcharColumn(t, 'new_column', 'My New Column', field=my_field) ## add it to the view t.views['main'].add_column(col, 0) .. note:: Adding a column that is not mapped to any db-field leads to a column that cannot be filtered on. On the other hand you can sort (locally, read below) on that column and also get totals. Sorting columns =============== Sorting columns can be done in 2 different ways: #. using :attr:`sqlkit.widgets.common.SqlWidget.order_by` attribute of sqlwidget, that triggers an ``ORDER BY`` clause on the database #. using ``order_by`` clause of ``modelproxy``, that triggers a function locally. This option is faster since you don't need to reload data This is the only method you can use to order related table, apart from playing with relation's order_by attribute in the model Currently it can be done: :programmatically: by ``modelproxy.order_by`` with argument a string with field_names possibly prefixed by + or - to define the order:: t = SqlTable(...) t.modelproxy.order_by('status -description') it can also be used to order a related table:: lay = """ director o2m=movies """ m = SqlMask(..., layout=layout) m.related.movies.modelproxy.order_by('-year title') when sorting a field that represent a foreign key, the value of the lookup (i.e.: the value shown) is used rather than the value of the key :interactively: currently limited to just one column at a time pressing ``s`` when **focus in in the treeview** (i.e. you ave already clicked the treeview). Sorting will toggle ascending and descending. .. _row-duplicating: Duplicating a row ================= The row menu (right click) allows to duplicate a row, it requires some understanding of the related issues: 1. Only visible fields are copied 2. Primary keys are not copied 3. If a foreig key is copied and that corresponds to a relation that has ``cascade='delete-orphan'`` that relation requires an instance to be attached. The field is able to retrieve such instance and attach it only if you configure the model's class properly. You can read more info on the field's method :meth:`add_related_object ` As for :ref:`save-as` mask function, there's a hook named :ref:`on_save_as ` that can be used to configure proper action to be taken before saving the duplicated row sqlkit-0.9.5/doc/html/_sources/sqlkit/mask.txt0000644000175000017500000001330411533706144020735 0ustar sandrosandro.. _mask: ========= SqlMask ========= .. image:: ../img/layout-simple.png :align: right layout ======= The most powerful part of SqlMask is the ability to **define the layout via simple text description**. In the examples you'll see how easy it is to create fancy layout that have all nice gtk Widgets like expanders, notebooks, panes and it makes it incredibly easy to nest SqlTables into SqlMask to build complex layouts that can also represent :ref:`relationships`. If you know ReST language and glade, it relates to glade as ReSt to html. If no layout is provided a flat one will be generated on the fly. The key is that a textual layout is parsed to see if the token are recognized as fields of the table that the mask is editing, in which case introspection is done to understand which widget should be used to edit the data (according to rules above) and a label is added:: mylayout = """ image title date_release TXS=description director_id """ m = SqlMask(model.Movie, layout=mylayout, ...) will creare a mask with an image, an entry, a date a text and a fkey. ``title`` and ``director_id`` will be already instrumented with completion: ``director_id`` will try to complete choosing the values of directors from the ``director`` table, ``title`` will suggest completion based on title present in the table (and in this case may not be very useful). ``description`` would render as an entry as it's a *varchar*, we wanted to "cast" it to *text* using ``TXS=``. The description language you can use is pretty rich (and dynamic, so you can add your custom made widgets): you'd better have a look at the demo of sqlkit.layout that you find in sqlkit.demo.layout When parsing the textual layout, any token that is not recognized as a field is passed as is to ``sqlkit.layout.Layout`` gtk refinements --------------- Occasionally you may need to refine your layout, change packing, visibility, attribute an so on. You can reach the gtk widgets via the ``widgets`` attribute of the SqlWidget. In example 26 we use:: Tbl = t.widgets['T.a'] Tbl.get_parent().child_set_property(Tbl, 'y-options', gtk.EXPAND|gtk.FILL) that changes pack properties to a gtk.Table whose name is ``T.a``. If you have ipython you can start the demo with option -i to gain an interactive shell to play with an experiment with the widgets. By default each field has a label whose key in widgets dict is ``l=field_name`` while each entry is ``e=field_name``. Shortcuts ---------- :mouse scrolling: allows to browse the records that have been loaded by a reload operation :Control-s: saves the record :Control-q: quits the table :Control-n: opens a new record Signals ======== :pre-display: this signal is emitted when current object has already been set but field values have no been set. It can be used to configure custom widgets whose appearance may depend on other values. .. method:: pre_display_cb(mask, obj) :param mask: the SqlMask that emitted the signal :param obj: the object currently selected or None .. versionadded:: 0.8.8 .. image:: ../img/mask-demo.png :align: right :scale: 50% :class: preview .. _save-as: Save as ======= Masks have a function *save as new record*, that allows to duplicate a record (corresponding to table's :ref:`row duplicating `). It's important you understand exactly what it does so that: * it nullifyes the primary key, so that a new one will be generated. If the primary key is visible and editable in the mask, it's up to you to delete it or change it according to your needs * m2m relations: for each *visible* relation, i.e. each relation that is shown by the mask **and is of type many to many** it copies the records. As an example: actors would be copied in the second film. If you want to change the new record in the related table (e.g.: actors) do that **only after saving** otherwise you will also change the cast of the original film, that's different from other fields as table'mode is to save in the same moment you edit. * o2m relations: if o2m relations are present a warning is displayed that those fields will not be copied **unless you have an on_save_as hook** that would mean you are already handling this case. Blindly copying the records would *steal* the references from the other record. Just to be explicit: suppose to have a mask of a director with his films, the director is Fellini and the film *La strada*. Duplicating the record "Fellini" with "Fellini 2" should probably be a 1^st step in personalizing some fields. In no way you want to divert the ``director_id`` of the film "La strada" that should continue to point to the original record "Fellini". * visible fields are copied from one record to the other. Please note carefully this point: a new empty record is set as current record and each visible fields is furtherly saved in the new record. If a field is not visible that field will not get copied. You may have problems if required fields are not visible. To allow to fill with new values a :ref:`hook ` is invoked named :ref:`on_save_as `. .. autoclass:: sqlkit.widgets.SqlMask() :members: get_widget, clear_mask, set_frame_label Introspection of the table is used to determine which widgets will be used to edit the data. The following rules are applied: :varchar: gtk.Entry :numbers: gtk.Entry with right alignment :bool: gtk.CheckBox :text: gtk.Text :FKey: fk_edit (a custom ComboBoxEntry) :date/datetime: dateedit (entry + arrow for calendar + time) :default: gtk.Entry All fields will have a label that is sensitive to mouse clicks. A mouse click pops up a :ref:`filter ` widget. sqlkit-0.9.5/doc/html/_sources/sqlkit/totals.txt0000644000175000017500000000262211533706101021302 0ustar sandrosandro.. _totals: ======== Totals ======== Numeric columns of a Table have the option to show totals and subtotals. Subtotals are determined by brake fields/functions. This may be done interactively from the column menu or from code. The following example is very stupid but... you get the feeling!:: t = SqlTable('movie', dbproxy=db, order_by='director_id') t.totals.add_break('director_id') t.totals.add_total('year') .. image:: ../img/totals.png Personalization and colors ========================== Totals are generated by a class ``sqlkit.widgets.table.totals.Totals`` that can be inherited and modified to set different total behavior. The colors are defined in a class in the same module ``TotalObj`` whose method ``set_value_and_colors`` can be used to personalize colors and markup of the cell. Dates ===== Since date breaks are probably very common a function makes it easy to brake on dates. Totals... with no totals! ========================= If you only need subtotals and not totals you can prevent totals using option ``hide_total`` when declaring the column to sum. Signals ======= :computed: this signal is emitted when the total is computed. The signature of the callback function is: .. function:: computed_cb(total): :param sqlwidget: the total instance that emitted the signal API --- .. automodule:: sqlkit.widgets.table.totals sqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/0000755000175000017500000000000011714210373021000 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/dbutils.txt0000644000175000017500000000004211530470276023211 0ustar sandrosandro .. automodule:: sqlkit.db.utils sqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/fields.txt0000644000175000017500000000004011530470276023007 0ustar sandrosandro .. automodule:: sqlkit.fields sqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/contents.txt0000644000175000017500000000032011533677024023402 0ustar sandrosandro======================= Advanced configuration ======================= These classes are only needed for advanced configurations .. toctree:: :maxdepth: 2 fields views dbutils field_widgets sqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/views.txt0000644000175000017500000000020311530470276022677 0ustar sandrosandro.. automodule:: sqlkit.widgets.table.modelproxy .. _view_class: View ===== .. automodule:: sqlkit.widgets.table.columns sqlkit-0.9.5/doc/html/_sources/sqlkit/advanced/field_widgets.txt0000644000175000017500000000111611533677024024362 0ustar sandrosandro.. _field-widgets: ============= Field Widgets ============= Each field is represented using a proper gtk widget. Table and Mask need two different objects, a cell renderer for tables and a widget from module sqlkit.widgets.mask.miniwidgets for masks. Cell renderers and function to represent the data are defined in sqlkit.widgets.table.columns. Widgets defined in sqlkit.widgets.mask.miniwidgets are just interfaces (or proxies) to other gtk widgets. .. automodule:: sqlkit.widgets.mask.miniwidgets .. automodule:: sqlkit.layout.image_widget .. automodule:: sqlkit.layout.dateedit sqlkit-0.9.5/doc/html/_sources/sqlkit/completion.txt0000644000175000017500000002712711530470276022164 0ustar sandrosandro.. _completion: ============ Completion ============ .. image:: ../img/completion.png :align: right Completion is the way you can avoid writing too much if the system already has your data. It means you write some text that will be used as filter in a select statements of all possible values. Completion is active * in text fields * in foreign key fields * in many2many fields with :meth:`set_editable(True) ` This operation has the following main actors: 1. the **search field**: the field where what you type is searched for. In the image, 'f' is the typed text that will be searched for in the field *last_name* that is the *search_field*. The letter 'f' will be used to filter the output. In this example the first_name (Roberto, Federico, Françoise) is not part of the search, but is used to better represent the possible matches. 2. the **object that should be returned**: * the *string* to be used (as in enum mode, or in completion in values used in the same column). This is only available for char/text fields No validation is done by default on this field, the completed text can be further edited. * the *foreign key* (frequently an ``id``) along with a *representation of the referenced record* (e.g.: the fullname of a director). A check is made: there's no way to input a text that is not matched on the remote field (see :ref:`description` below). * a *complete record* as when we edit a relation and we use a filter on one field but we aim at setting a complete record. E.g.: we select an actor from a list and we may be filtering the name or the nation. See example 40d. 3. the **representation of the result**: when using completion on a ForeignKey or on an m2m relation, the representation of the possible values needs to be taken. In the example of the image 'Fellini' the the representation of the referenced record that was referenced by a numeric id. 4. the **operator** used when searching: normally ``regexp`` or ``like``. In the image here you see that 'Truffault' is returned that does not start with 'F' but *contains* 'f'. The match of the completion has these *modes*: :start: partially written text is used as a filter and completion must match from the start of the field. It uses LIKE operator :regexp: the match is done via regular expression if db supports it or via LIKE operator adding ``%`` on both sides :enum: any value matches. All possible values are shown. This mimics an enum field. When there are only few values this may result more natural than the others Since completion implies a search that may return *many* records, it's only triggered on demand. Normal binding are: :shift Return: triggers 'start' mode completion :control Return: triggers 'regexp' mode completion. This is also triggered pressing the down arrow of a foreign key :shift control Return: triggers 'enum' mode Pressing ``Alt`` along with the other keys will prevent any :ref:`filter_completion` .. _description: foreign key description & search field ====================================== When displaying data (and data shown by a completion are no exception), a foreign key is substituted by a more descriptive text. Let's see how to customize it. Make sure you understood the limitation on ForeignKey expressed in :ref:`basic_limitations` You can customize the way a record is represented by: * using :class:`sqlkit.db.utils.TableDescr` class * writing configuration info in a database table (normally ``_sqlkit_table``) sqlkit.db.utils ---------------- You would use module sqlkit.db.utils and create an object :ref:`TableDescr`:: from sqlkit.db.utils import TableDescr utils.TableDescr('movie', format='%(title)s - %(year)s', metadata=db.metadata) metadata is necessary so that TableDescr knows where to go and auto-load Table to introspect it if you don't provide the search_field (see below) .. _sqlkit_table: _sqlkit_table -------------- It's possible to write in the database the format string to be used in the table. A table called ``_sqlkit_table`` is searched for (firebird backend doesn't allow leading '_' in table names so '_' is stripped for it). :search_field: The value of this field is used for the search. If no such field was defined, the first char field of the tale is used, if it exists. :format: the value of this field is used to represent the record, e.g.: "%(title)s %(year)s" You can easily edit this table using :ref:`sqledit` format and __str__ ------------------- When SqlWidget creates a class on the fly it looks for the 'format' field to add a __str__ method to the class, so that this representation is used whenever suitable (e.g.: when a filter action is performed in a Mask) .. _autostart: Autocompletion ============== You can force completion to start after n chars has been entered setting the completion object ``autostart`` value:: t = SqlMask('movie', dbproxy=db) t.completions.director_id.autostart = 2 Completion will be recalculated every time the written string is shorter that the last text that triggered the completion. Take care not to use a little value for ``autostart`` on large tables. completion and Return in Tables -------------------------------- There is another situation in which a completion is started automatically. When editing a foreign key (or an m2m): if a ``Return`` is hit, a select is issued to check if it's a valid value and if not that value is used as base for completion. The difference from triggering a normal completion is that if a valid value is found, no further completion is done. .. warning:: Since completion uses the already written chars to filter possible solution, if you further delete such chars you are not seeing all the real possible solutions but only the already retrieved ones. You can request a new completion... Group_by ======== There is also en easy possibility to add grouping of completion via a foreign_key attribute. It's enough to set the group_by attribute of completion_group_by:: t.completions.director_id.group_by = 'nation' .. image:: ../img/completion_group_by.png .. _`filter_completion`: Filtering completion ===================== You can programmatically decide to filter what a completion returns in a very easy way using :ref:`django like syntax ` (the same used to set constraints):: t = SqlTable(Movie,...) t.completions.title.filter(title__icontains='love') this line will instruct the completion to only show titles that contain the world "love". Filters on a field that is a foreign key will be relative to the related table:: # nation_cod is a field_name of the table t.completions.director_id.filter(nation_cod='ITA') will build a constraint on the **director** table filtering only italian directors. If you set relation on your Director class as in:: class Director(Base): ... nation_cod = Column(ForeignKey(Nation.cod)) nation = relation(Nation) you can set filter on the completion based on this relation:: ## nation__code (note the double underscore!!!) will trigger ## a filter on the cod field_name of the relation nation t.completions.director_id.filter(nation__cod='ITA') dynamic filters ---------------- It's also possible to set a "dynamic filter" i.e. a filter depending on the value already of another field:: t.completions.last_name.filter(nation='$nation') In this case the value of $nation will be set using ``t.get_value('nation')`` In case you have a related table you can go back to the main table:: t.related.movie.completions.title.filter(director_id='$main.director_id') enum mode with foreign keys --------------------------- Enum mode is the way you can mimic a standard enumeration field: you see all fields independently from what you have in your entry. This is more natural in some circumstances if you only have few values. You can get this behavior all the times just hitting Control-Shift-Return or in ForeignKey fields double-clicking the down arrow. Since you probably want this depending on the values of the table you can programmatically choose to serve completion *only* via this way setting ``force_enum = True`` on the completion:: t.completions.director_id.force_enum = True enum mode w/o foreign key --------------------------- There is another way that mimics enum mode, i.e. setting directly the possible completion values via the method :meth:`set_values `:: t.completions.status.set_values(['open', 'closed', 'waiting for input']) or:: t.completions.status.set_values(a_function_that_returns_possible_values) the signature of this function must be: .. function:: my_values(value) :param value: the value that may have been written in the entry, used to filter values, as usual more customization ------------------- There are normally 2 possible completion according to the filter level. An example can better clarify: suppose you have an entry where you are supposed to enter a username. You set a filter on *active* users, now you need to fix an old record that really has a user that is no longer *active*. You need to loosen you filtering criterion momentarily. You can do that Pressing the ``Alt`` key along with normal ``Ctrl-Enter`` or ``Shift-Enter`` These two filtering criteria are stored in two ``session.query`` objects and are stored in the completion with attributes: :filtered_query: the query with filters. You set filters on this query with ``filter()`` method. If a ``filtered_query`` already_exists, filters are added, otherwise it's written from ``query`` :query: the default query, used when no filter is desired (``Alt`` is pressed). You set filters on this adding argument "main_query = True" to ``.filter(main_query=True)``. Of course each one can be customized with the normal sqlalchemy syntax also. Note that if you have both filters (with and without main_query option) order makes difference as ``filtered_query`` is built based on ``query`` when .filter is called for the first time. If you change ``query`` after calling ``.filter()`` you end up with unrelated filter condition (that's allowed as you may really want this). Remember that :ref:`validation` is a completely different mechanism than completion even if it's not possible to add a field that doesn't come from a completion. .. _customizing_description: customizing description ----------------------- The ``query`` attribute of the completion determines which fields will be present in the completions list, the ``format`` attribute decides how it will be represented. Default value is determined as described :ref:`above `, but you can customize it as you prefer, as far as you use fields present in the query. As an example:: t.completions.director_id.format = '%(first_name)s %(last_name)s -- %(nation)s' Behavior on completion with m2m/m2o relationship ================================================= When the completion is in a SqlTable that represents a not editable m2m relationship (as ``actors`` would be for ``movies``), the completion does not simply add the single field but substitutes the whole record. On such a relationship table's completion you can set filter that will act on all fields. Rationale: if you have a movie/actor relationship and set a constraint on actors so that only female should be selected, you probably want to retain that filter independently from the fact that you select the nation, the first or the last name. .. automodule:: sqlkit.widgets.common.completion sqlkit-0.9.5/doc/html/_sources/sqlkit/filters.txt0000644000175000017500000000733311530470276021460 0ustar sandrosandro.. _filters: ========= Filters ========= .. image:: ../img/filter-panel.png :align: left Filtering is a very powerful feature of sqlkit. Any single field in a SqlWidget may become a filter criteria and many filters on a field are accepted. A filter panel handles the filters that may also belong to related table: in that case a join will be built when selecting (read the note on selecting through joins in :ref:`relationships`) As you can see in the image a filter panel gives also the opportunity to limit the query. Any filter can be enabled or disabled by clicking on it's toggle. The result of a filter operation is shown differently in Table or Mask: Table shows the result directly, Mask shows the list of selected records in the Filter Panel's Output page: .. image:: ../img/filter-output.png :align: right Each record is shown with it's __str__ representation that can be set in the way described in :ref:`description`. In this Output Page it's possible to set a field and have records grouped by that field:: t.filter_panel.tree = 'field_name' Filtering Foreign Keys ====================== Filtering works also with foreign keys. In that case the filter acts on the filter that represents the record, what I call the :ref:`"search" ` field of the record. In this case the operator used for the search defaults to regexp/match that in turn uses different operators in each database backend: '~*' for postgresql, REGEXP for mysql and ILIKE for the others (well, ILIKE is not present in sqlite but it uses the sqlalchemy implementation of ilike) adding leading and trailing '%' symbols. Adding Filters programmatically =============================== Filters can be added programmatically via method ``add_filter`` that uses django_like syntax, of interactively. As an example:: t.add_filter(numeric_field__gte=5, boolean_field=True) t2.add_filter(date_field__lte='y', string_field__null=False) more examples can be found in the :ref:`constraints` sections as they share the same syntax. Note that a filter can be changed by the user while a constraint is invisible to him. filters and join ---------------- When filtering programmatically on a join you must use the field_name as known by the mapper, i.e. composition of table_name + field_name. Look demo on join too see how it works:: t = SqlTable(tables="movie director", dbproxy=db ) t.add_filter(director_nation='IT') # NOTE director_nation Here the field *nation* of table *director* is referenced as ``director_nation`` Expressions =========== Filter work just in the same way for real column as for expressions. Example 30 in the demo shows how to create a mapper that have a column with the number of film of a director, and you can verify that constraints and filter work on that table just as any normal column:: class Director2(object): pass ## create the mapper as suggested in the SqlAlchemy tutorial... m = mapper(Director2, model.Director.__table__, properties={ 'film_number': column_property( select( [func.count(model.Movie.__table__.c.id)], model.Director.__table__.c.id == model.Movie.__table__.c.director_id ).label('film_number') ) } ) field_list = "last_name, first_name, nation, film_number" t = SqlTable(m, field_list=field_list, dbproxy=db) t.add_filter(film_number=3) t.add_constraint(film_number__lt = 5) .. _date_filters: Date filters ============= Date filters deserve a special chapter. It's very common the need for a filter based on relative dates (i.e.: the beginning of the month, the year, the last month and so on), that's the only way to allow saving queries that will behave the same all the time. .. automodule:: sqlkit.misc.datetools .. automodule:: sqlkit.widgets.common.sqlfilter sqlkit-0.9.5/doc/html/_sources/sqlkit/relationship.txt0000644000175000017500000001422211533706144022503 0ustar sandrosandro.. _relationships: ================ Relationships ================ Sqlkit makes editing of data that have relation as one2many or many2many very simple. It's as easy as adding a field in the layout of the SqlMask:: lay = """first_name last_name o2m=movies - - - """ One2Many in a SqlMak ===================== This is a very powerful feature of ``sqlkit`` that is built on top of ``sqlalchemy``'s mechanism of properties and session and on the ability of ``layout`` to let you define a widget in a symbolic way. In the following example we will use a ``SqlMask`` to represent ``Directors`` and a ``SqlTable`` to represent films, connected to the first by a OneToMany relationship. Suppose you already define a couple of tables in sqlalchemy, we will do it using elixir_ that slightly simplifies the syntax, in the package the demo uses declarative layer instead:: class Director(Base): __tablename__ = 'director' id = Column(Integer, primary_key=True) last_name = Column(String(60), nullable=False) first_name = Column(String(60), nullable=False) nation = Column(String(6)) movies = relation('Movie', backref='director', cascade='all, delete-orphan',) class Movie(Base): __tablename__ = 'movie' id = Column(Integer, primary_key=True) title = Column(String(60), nullable=False) image = Column(String(250), info={'render' : 'image', 'base_dir' : './images', 'thumbnail_size' : (30,30)}) description = Column(String(512)) year = Column(Integer()) date_release = Column(Date()) director_id = Column(Integer, ForeignKey('director.id'), nullable=False, info={'attach_instance': 'director'}) To get a mask that: 1. lets you see director and films together 2. lets you edit them adding and deleting at will You just have to start the SqlMask like this:: lay = """first_name last_name nation o2m=movies - - - """ SqlMask(model.Director,layout=lay, ...) And you will get a mask that looks like this: .. image:: ../img/o2m.png delete behavior ---------------- We leave to sqlalchemy the responsibility to decide if a film must be deleted from the database or must just updated to set the director_id to NULL. To change this behavior you must act on the cascade property of the relation (or directly on the database definition). If yo want to delete a film in stead of just setting the property to NULL you can define the table in this way:: class Director(elixir.Entity): ... movies = elixir.OneToMany('Movie', inverse='director', cascade="all, delete-orphan" ### note this line ) ... .. _elixir: http://elixir.ematia.de Filtering ----------- It is now possible to filter on a field present in related tables, e.g.: filtering all films that have a certain genre. .. note:: Starting from 0.8.6-pre2 filters on a related table are *aliased* (see `sqlalchemy docs`_). This is relevant if you filter on related table via different path (e.g: filtering staff and manager of a table projects, where both staff and managers are a m2m relation to User). There are cases where this is really what you want but in some cases this is not. Here you really need to deeply understand what is happening underneath at the SQL level. As an example if you have a sqlmask of directors with a related table of films, if you filter on films and use a filter on title (containing 'la') and a filter on year (> 2005), the two filters will not be related to the same film: you'll get all directors that have a film with 'la' in the title and (possibly a different) film produce after 2005. This may or may not be what you want... Many2many relationship ========================= many2many relationship are as easy as one2many. Once again sqlalchemy does the heavy job. Definition is as simple as:: lay = """year title m2m=genres - - - """ tm = sqlkit.SqlMask(Movie.mapper, metadata=__metadata__, session=elixir.session,layout=lay) lay = """ name m2m=movies - """ tm2 = sqlkit.SqlMask(Genre.mapper, metadata=__metadata__, session=elixir.session,layout=lay) and will pop up a couple of windows as the following, showing the same data from the two different main tables: movies with their genres and genres with they're movies. .. image:: ../img/m2m.png adding & completion ------------------- .. note:: .. versionadded:: 0.8.4 When using :ref:`completion` in a m2m table, adding from completion behaves differently that adding from m2o in that it requires the field to be already present and does not allow to edit it This behavior can be changed setting it's 'm2m_editable' property to True (new in 0.8.4):: t.related.genres.set_editable(True) .. 2. it complains if it cannot get one single element with that value Many2One or ForeignKey ====================== Many2One is a simpler case. The table we start from **has** a field that holds a ForeignKey, we just need to follow it to know the value. This again happens with no effort at all. In this case it's also possible to use this field in a filter selection. Options ========== You can set the field_list directly from the layout as well as the number of rows:: m2m=actors:5:first_name,last_name will set a 5 rows table, and a field list of ``first_name``, ``last_name``. The real dimension of the table depends also on the expand attributes of the containers. you may need to set them to ``gtk.EXPAND|gtk.FILL`` by hand. There's an example that demonstrates it. Behind the scenes =================== The way sqlkit understands that ``movies`` is an entry point for a relationship is that it analyzes the ``mapper``, looks for a property with that name and realizes that it's a PropertyLoader. That means that such an entry point has been put there by a ``relation``. .. _`sqlalchemy docs`: http://www.sqlalchemy.org/docs/05/sqlexpression.html?highlight=alias#using-aliases sqlkit-0.9.5/doc/html/_sources/debug/0000755000175000017500000000000011714210370017007 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/debug/debug.txt0000644000175000017500000000470111421620210020631 0ustar sandrosandrodebug module ============== :: from sqlkit import debug as dbg purpose ========= .. image:: ../img/gtk_debug.png :align: right This module provides easy way to debug with print commands. This is not in general my preferred way of debugging (``pdb`` within ipython being my preferred solution). Nevertheless I found myself in need of these functions. Printing of messages can be easily switched on or off. Printing can diverted to a gtk widget. Methods of Classes can be tracked in the way reported in the image, where blue rows correspond to ``dbg.write`` commands and all the rest is tracking of methods calls. use ===== switching debug on & off ------------------------ The way to use this is 1. in you library:: from sqlkit import debug as dbg dbg.write('text message', 'text2') 2. in you application:: from sqlkit import debug as dbg dbg.debug(True) write() - caller() ------------------ from now on, each time you use dbg.write()/dbg.caller() you'll see the text you want to log anly if you enabled it with dbg.debug(True) If you want the log to happen in a gtk.TreeView window, you can specify:; import dbg dbg.debug(True, gtk=True) Logging methods --------------- Following recipe 198078 in the ASP Python CookBook (see in the code) this module provides also a metaclass to trace the use of methods of a particular class. It will be instantiated as:: class A(object): __metaclass__ = dbg.LogTheMethods for each method call a line will be logged (unless configured to be ignored) with function called caller class arguments caller function line of code return code calling class time elapsed IMPORTANT: since the logging occurs with metaclasses you need to import dbg and set debugging *before* importing the class you want to trace:: from sqlkit import debug as dbg dbg.debug(True, gtk=True) dbg.trace_class('SqlTable2') # optional dbg.trace_function(exclude='cell_default_cb|cell_bool_cb') # optional import sqlkit TraceIt ------- in case you need to understand who is changing the value of a variable, you can use TraceIt as:: commit_allowed = dbg.TraceIt(True, name='commit_allowed', mode='rw') and use it as a normal variable. You'll get lines in your output stating who changed the value of that variable, in the form:: __str__/function_name: old_value => new_value __get__/function_name: value sqlkit-0.9.5/doc/html/_sources/debug/contents.txt0000644000175000017500000000010211421620210021367 0ustar sandrosandro============= Debug helpers ============= .. toctree:: debug sqlkit-0.9.5/doc/html/_sources/printing/0000755000175000017500000000000011714210372017555 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/printing/contents.txt0000644000175000017500000000042111547676300022163 0ustar sandrosandro============ Printing ============ Printing in sqlkit uses the ``oootemplate`` module that is distributed along with sqlkit but could be used in a totally independent way and does not depend in any way on other parts of sqlkit. .. automodule:: sqlkit.misc.oootemplate sqlkit-0.9.5/doc/html/_sources/layout/0000755000175000017500000000000011714210371017237 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/layout/contents.txt0000644000175000017500000000242611530470276021651 0ustar sandrosandro========================================================= Layout GUI built on the fly - A GUI description language ========================================================= This is **the key** of the mask widget and is really what makes writing a layout with SqlMask so easy. If you know what **glade** or other gui builder are, think at this part as the "markup sistem" for html language. Using this description language to define a layout we try to use as little writing as possible, we are asking sqlkit to guess all the properties that it can from the name. What can be guessed? A lot!!! Imagine you have a table with movies, and a field is title, anoter is director_id. The database knows that the first on is a varchar and the second is a foreign key. It **can** guess how you will want to represent it so that you will just need to you thei're name in drawing the layout:: title director_id The biggest gain we have when setting :ref:`relationships` between tables. The code of the layout package and probably the definition of the language itself is deemed to be rewritten, as was the first step into python in 2005, but it proved to behave well in these years. After looking at te ReST markup syntax I am tempted to rewrite the language in a simpler way. .. toctree:: layout sqlkit-0.9.5/doc/html/_sources/layout/layout.txt0000644000175000017500000003515611534024230021324 0ustar sandrosandro.. _layout: ===================================== A GUI description language - purpose ===================================== .. sidebar:: Note this page needs to be reviewed. While it should be mostly correct when ported to rest format has not been reviewed. A complete tour of what can be done is available in the demo The class Layout is a class that understands a GUI description language to create GUI on the fly, with minimal effort by the programmers, that doesn't need to know a lot of Gtk details. You cannot do *any* possible thing you would do with gtk, but methods are provided to refine the layout and it isn't difficult to add GtkObjects or features. The purpose is to easy things with simple setup, not to cover any possible graphical layout. The main purpose is to be used in designing layout of record editor (:ref:`sqlkit`) for db, but it can be used with any other program as well. Layout is not an interactive tool: it uses gtk.Builder xml to create the GUI, ie: it produces xml for gtk.Builder. .. note:: gtk.Builder up to 0.9.2 sqlkit used glade. This dependancy has been dropped in favor of gtk.Builder that is directly available in standard GTK installation. Care must be used to create names for the elements that are unique in the single layout, so as to be able to reach any single object, not only to refine configuration but also to interact with the GtkObj in the program. This reflects in the fact that the name is unique. A normal GUI is plenty of labels and tooltip. At the moment of this writing no localization is provided but gettext support is in program. ============================ Description of the language ============================ a starting example:: layout = """ el=first_name el=last_name el=address - """ A layout is a sequence of tokens and display modifiers. :token: describes a GtkObject (Button, Entry...), may also be a compound. An example: el=first_name. They will normally start with a description key, followed by a '=' and then followed by a string possibly separated by a '/' and.or a:: key=string/variable:wid1.wid2 or in regexp notation and more precise:: (key=)string(.[0-9a-z])?(/variable)((:width.)(width)([-<>]))? key txt id var width1 width2 align no element is needed apart the 'string', the key defaults to 'el' (Entry + Label), the variable defaults to the string, wid1 and wid2 (described below) use GtkWidget defaults. The string part may end with .[a-z0-9] just to be able to refer in a unique way to single elements that are apparently equal (see :ref:`names`) an indication of the desidered alignment may be given using one of ('-', '<', '>'). this take effect oly if the element is position in a gtk.Alignment (eg. 'ae' element ie: entry with width). in that case: :>: right alignment (xalign = 1) :<: left alignment (xalign = 0) :-: xscale of the alignment is set to 1 i.e. the child of the alignment (the entry in the example will grow when more space is given to the widget) .. sidebar:: Automatisms with sqlkit Note that when used from within sqlkit layout definition any text field is rendered with entry within an alignment (``ae`` element) and an alignment of '-' is added to any text field longer than 20 chars. :spare token: A token may further be: :@: it is equivalent to a newline (in fact I figure it like spinning ;-) :-: forces the previous element to span one more column to the left :^: forces the element above it to span one more row down :%.*: the element is a label for a notebook (see 'notebook' method) :display modifiers: A modifier is an element that modifies the way elements are arranged, mainly it modifies the packing info or the container, but can also modify the display eg: label and entry on the same line or one above the other. A modifier is: :{}: a pair of curly braces :[XTHVbNPSvhpOBmMFA](.id): (possibly) followed (w/out spaces) by some opts as in {V r=man/sex r=woman}, this will create a nester layout with 2 radiobutton packed into a GtkVBox. For an explanation of the (.id) part see below (container naming) Possible opts are XTHVbNPSvhpOBmMFA: :T: gtkTable :H: gtkHBox :V: gtkVBox :N: gtkNotebook :F: gtkFrame :M: gtkMenubar :t: gtkToolBar :h: gtkHPaned :v: gtkVPaned :S: gtkScrolledWindow :B: gtkMenuBar :M: gtkMenu :m: gtkMenuItem :O: gtkToolbar :W: Window :E: EventBox :X: Expander :p: ViewPort :A: Alignment Elements will be packed in a table widget unless changed by a option when instantiating the class as in:: lay = Layout(lay, opts="V=") Other values for opts are ``[>=-|]`` - to be confirmed :=: implies that label and entry will be displayed one over the other (this is the default) :-: implies that label and entry will be displayed on the same line. :\|: will draw a line around the widgets, using a frame. That means a GtkFrame and a GtkAlignment are silently created. You can set properties of the silently created expander using key F.id :>: will encapsulate the result into an Expander that allows to collapse all its content via a click on a little arrow. You can set properties of the silently created expander using key X.id. A double '>>' results in an expanded expander. The id is used as label for the expander. so that '>>.clients', will be expanded in an oped expander with a label "clients" a longer example ---------------- :: el=first_name el=last_name el=address - {V r=man/sex r=woman/sex} b=register Comment -------- A '#' sign starts a comment. Anything till the end of the line will be discarded, as usual... el element ----------- the most used element will probably turn out to be a Label + Entry widget that can be constructed as in el=first name. It will really be understood as:: { l=first_name el=first_name } or:: { l=first_name el=first_name } According to the opt modifier = or - list of elements ================ A partial list of elements: :l: Label, :L: EventLabel, a label in a GtkEventBox :e: Entry, :E: Event, :b: Button, :r: RadioButton, :c: CheckButton, :TV: TreeView, :TX: TextView, :TVS: ScrolledTreeView, :TXS: ScrolledTextView, :cal: Calendar, :sb: Statusbar, :le: ComboLE, more symbols are in the source... custom widgets --------------- Occasionally you will find yourself in nee for a custom identifier. You can create one and register it. Have a look at the code in layoutgenerator module. Calling Layout ============== :: def __init__(self, lay, level=0, parent_container="W", opts="T", elements={}, container_id='',title="Window",dbg=0): :lay: the string describing the desired layout :level: nested level (used only by layout itself) :parent_container: a flag among the container flags that says which is the container: determines the packing info for the result. Default is W (Window) as a toplevel window. In this case info for a toplevel window is built. :opts: This string has the same meaning of the modifier string of a container that can be put directly after the '{' char. Mainly this is a way to force the use of a container for the outer call of layout. Default is to put all widgets in a GtkTable but any container can be chosen from the container flags. A special component of 'opts', is 's' that asks to add a StatusBar. The method 'sb' is provided to talk with that Statusbar whose key name is sb=StBar. :addto: follows the name of a container that will receive the resulting layout. Must be a container that accept 'add' method. methods -------- NOTE: some methods can only be called after 'show' has being called. These methods use gtk directly, not glade. :xml(file=None): will produce the xml needed for glade, may be write to file :show(action=function_name): will directly call glade to display the GUI, returns a dict of Gtk objects whose keys are the element *keys*. After this method there is no point in changing elements[] properties. The action can be gtk.main. :elements: a dict of all lwidgets. The keys are the element definitions. Any element can be configure up to the moment show is called. :conf(el,property): will set a property for an element of the layout equivalent to:: elements['el_key'].properties['name'] = value :sig(list): will set handlers for the signal for each widget. The list is a list of tuples of 3 elements: * el_key * handler * signal. If the last is missing, clicked is used :tip (el, text): a tooltip for the element 'el'. It tip is called before 'show', it will end up in xml+glade, otherwise it will call gtkTooltip directly :sb(text): will push text on the StatusBar :menu('el_key', (entry-name, handler, signal=activated), 'i'), (), (): Will add MenuItems (Stock ImageMenuItems if 4^ element is 'i') :notebook('el_key', ['label 1', 'label2',...],position): Adds the label to tabs and allow to set the position (top,left, right, bottom) for a GtkNotebook. There is another way to obtain this effect. You can add %label as first entry in the block that will be enclosed in the notebook. See below: notebook :frame('el_key', 'label'): Adds a label to a frame, and writes it w/ bold face :prop(el_key,property_name, value): Sets a property for element el_key :pack(el_key,property_name, value): Sets a pack property for element el_key .. _names: element names ============= For each element of the layout we need to build a key for xml, so that we know how to refer to the gtk object in the program. Layout will build the name starting from what was written in the element description, if that results in being already used it builds a unique name adding a number, but that may result in difficulty to interact with it from inside the program. If that's an issue try being clear when creating the layout. Use ids, the string separated by a '.' that you can add to element names and to container flags: :container: :: {H.Z {B.id0 :element names: :: e=name.id1/string stock names -------------- if the name of an element starts with 'gtk-', use-stock = True is set for that element. Notebook -------- A notebook need some labels to identify the tabs. These are other child of the GtkNotebook container interspersed with the content of the container. You can set them with the notebook method, or you can use a symbol in the describing string using % trailer:: {N.0 { %first_tab_RIGHT TXS=one } { %second_tab TXS=due } } will be equivalent to:: l.notepad('N.0', ['first tab','second tab'], 'right') Please, note that you can enforce a position with a trailing _(LEFT|TOP|...) to the first label. Note also that any underscore will be substituted w/ a space. Automatic name mechanism ---------------------------- A normal element definition is of the form:: key=string/variable:width.height * the :width.width part is discarded * the rest is used as key, *but* * if no 'key=' part was explicitly used as in 'first_name' two will be created: e=string, l=string container naming ------------------ Containers need a name too. You are supposed to know which one is used: it will be GtkTable unless you asked for a different one. You need it in case you want to modify its properties. Their name will be the container flag [THVbNPSvhpOBmMFA] possibly followed by a dot . and an identifier. The identifier will be a progressive counter, starting from 0 but may be imposed appending .id to the flags of the container, as in:: {V.my_id first_name last_name } Functions ========= ``map_layouts(filename=None, buf=None)`` This functions fills in a dictionary widgets.info whose structure is: name : (label, tip) when a widget is created w/ name 'name' the label will be set to 'label' and a tooltip will be set to 'tip'. This can help a lot in many situations. Db field name will be mapped to user friendly labels, translation will be as easy as pointing to a different file. The function can point to a filename or can read a string. You can write directly the info dictionary if you prefer... Layout class implementation =========================== Each layout live in a container (may be a toplevel Window) and creates a container to house its children. This can cause a little bit of confusion so I call parent_container the container in which a widget live and container the Gtk container (Table, HBox, VBox...) where I house layout children. You can think at your layout as divided in blocks, each one named 'cell' in the code. A cell is or the definition of a gtkObj or a nested layout. You can see this list for debugging purposes with method _dbg_parse_layout. You can think at these cells as displayed in a grid, each one defined by a row and a column. The constructor of Layout uses a dict named 'cells' with indices (row,col) to store the name of the corresponding LWidget or LContainer. When the object is instantiated, Layout 1. creates a container (LContainer object) 2. splits the layout description into tokens that are or -a element descriptions - nested layouts (starts w/ '{') ... For each token that describes a single element, creates the corresponding lwidget object, for each nested layout creates another instance of Layout. LWidgets and LContainer ------------------------- the difference between an LWidget and an LContainer is that an LContainer has children and must pack info for all the children when producing xml (apart from producing the xml for itself) Window creation ---------------- Each call to Layout must create a container to house all its children, but the first (or outer) one needs normally to create also the TopLevel Window, that to us is nothing than another LContainer whose (only) child is just another container (the outer one for Layout). Producing xml ------------- Layout produces xml just asking to the toplevel (an LContainer object) to produce xml. LContainer xml, produces xml for itself and for its children, if some of them is a container the process iterates. sqlkit-0.9.5/doc/html/_sources/misc/0000755000175000017500000000000011714210372016656 5ustar sandrosandrosqlkit-0.9.5/doc/html/_sources/misc/tutorials.txt0000644000175000017500000000027711536207726021466 0ustar sandrosandroMailing list ============ You can join our mailing list_ Tutorials ========= .. toctree:: :maxdepth: 2 tutorial sqledit tour .. _list: http://groups.google.com/group/sqlkit sqlkit-0.9.5/doc/html/_sources/misc/tour.txt0000644000175000017500000002110511714207753020420 0ustar sandrosandro======================= Sqlkit & Sqledit ======================= Sqlkit is a tool to edit **data** of a database (as opposed to schemas) in the easiest possible way. By **easy** we mean both: easy to write for the *user* since completion helps you, and easy to write for the *programmer* as a lot of features are there to help you customize it. It's based on PyGTK_. It provides: * a GUI named :ref:`sqledit ` that can be used standalone and does not require programming knowledge * a Python package that mainly provides two megawidgets: :ref:`mask` and :ref:`table`, very powerful classes to build your application. A typical usage can be:: from sqlkit.widgets import SqlTable, SqlMask from sqlkit import DbProxy db = DbProxy(engine='sqlite:///movies.sqlite') t = SqlMask('movie', dbproxy=db, single=True) that will pop up an editor for the record. Filter panel will be available just clicking on the label of the fields. .. _sqlkit: Features ======== **Main features of sqlkit**: - Editor of databases in 2 modes: :ref:`table` & :ref:`mask`. Mask can embed tables of :ref:`related tables `. - Based on sqlalchemy_: can cope with many different database backends (PostgreSQL, MySQL, sqlite and firebird the ones sqlkit was tested on). Can be used to edit any selectable you can build with sqlalchemy. - Very powerful :ref:`filtering ` capabilities: * each field can be used to filter visible records * date filtering possible also on relative basis (good for saved queries) * works on expressions too * works on fields in :ref:`related tables ` embedded in mask - :ref:`completion` both on normal fields and foreign key fields - Very easy way to draw a layout for mask views - :ref:`sqledit`: python script to edit db - Completely effortless editing of :ref:`relationships` - Very easy way to set :ref:`defaults` - Possibility to display :ref:`totals` in numeric fields - :ref:`constraints`: any possible sql constraint can be attached to a Mask or a Table. It can be expressed as a normal sqlalchemy query or with django-like syntax. Works on expressions too. - SqlMask and SqlTable are widgets with several :ref:`signals` - :ref:`hooks` for a very easy customization of behavior and for validation - More than 70 snippets of code to demonstrate each feature .. _sqlalchemy: http://www.sqlalchemy.org .. image:: ../img/table-demo.png :align: right :scale: 50% :class: preview :alt: table opened on movies Table ----- You can see data in a tabular format using the :ref:`table` widget. The code is as simple as:: t = SqlTable('movie', dbproxy=db, ) you can customize which columns to show, possible filters or constraints (see below), and a lot of others details Mask ---- .. image:: ../img/mask-demo.png :align: right :scale: 50% :class: preview Records can be displayed one record at a time with the SqlMask widget. Tables can be embedded in mask to edit :ref:`relationships`. that where requested by the following instructions:: lay = """ varchar10 varchar200 - - - {N { %time {>>.general date interval datetime time } {>.hidden_boolean bool bool_null } } { %numbers integer float numeric } { %text text uni_text } } - - - """ t1 = SqlMask('all_types', dbproxy=db, layout=layout, ) Filters ------- .. image:: ../img/filter-panel.png :align: right :scale: 50% :class: preview Each label of both views can be clicked to get a :ref:`filter panel ` through which we can choose an operator and filter records. Filter let you use a "human" representation of foreign keys, that means that if ``director_id`` points to a numeric id, sqlkit will let you write the (last) name of the director instead when filtering. The filter panel will let you navigate in the output list, that can be completely customized. Completion ---------- :ref:`completion` is triggered by F1 key, Ctrl-Enter or Shift-Enter. If the field is a foreign key it will pop up a list of foreign values otherwise it will show values currently used (just for varchar fields). The search for completion is done using the (possibly) already written letters as filter: Control will match them at the beginning of the field, Shift (and F1) will match them in any part. The search is made using ``LIKE`` operators or ``regexp`` according to whatever is available in the db engine in use. Layout ------ Very easy way to draw a layout. See :ref:`layout` widgets for a tour. The language used relates to glade as a markup language relates to html. This GUI description language lets you draw a layout using field_name as place holders for the widget that you will use to edit it:: title director_id will be replaced by a label 'title' followed by an entry and a title 'director_id' followed by a widget suitable to edit a foreign key. Le language gets more complicated to let you use main gtk widgets as frames, notebooks, scrollable widgets, tables.... Relationships ------------- .. image:: ../img/o2m.png :align: right :scale: 50% :class: preview This is probably the most impressive feature. You can build a layout in which related data are displayed in a totally natural way. The following layout requires the code:: lay = """ first_name last_name nation o2m=movies:title,description,year,date_release """ SqlMask(Movie, layout=lay, dbproxy=db) now you can edit director and films. The demo has plenty of working examples for there cases: :many2one: are just recognized automatically with simple introspection :many2many: is very simply added to SqlMask declaring in the layout and attribute with that name :one2many: same as many2many Many more detail in :ref:`relationships` .. image:: ../img/totals-embedded.png :align: right :scale: 40% :class: preview Totals ------ It's possible to display totals and subtotals in a table view. In this picture you can see how a table embedded into a mask can display totals. More in :ref:`totals` Constraints ----------- A :ref:`constraint ` can be as simple as:: t = SqlTable('movie', dbproxy=db) t.add_constraints(actors__country='ITA', genres__name='drama', year__gte=1950, year__lte=1970) And browsing of movies will be constrained into dramas with at least one italian actor, produced between 1950 and 1970. The double underscore '__' will allow spanning from one table to a related (provided a relation was established at sqlalchemy level) and from attribute name to operator to e used. Sqledit ------- A full featured program (python script) that can browse a database. Many options (``sqledit -h``). .. image:: ../img/sqledit.png :align: right :scale: 50% :class: preview Just try it out on your preferred database using the url in a form as one of the examples:: sqledit postgres://localhost/mydb sqledit postgres://sandro:passwd@host:5444/mydb sqledit mysql://localhost/mydb sqledit sqlite:///movies.sqlite the last is a very minimal db present in ``doc/db/movies.sqlite`` .. _basic_limitations: Basic assumptions and limitations ================================= 1. You use PrimaryKeys and ForeignKeys throughout the db. If you don't use ForeignKeys sqlkit will just work w/o any special behavior. If you don't use PrimaryKeys sqlkit will not even open the table. 2. ForeignKeys are *simple*. Compound ForeignKeys are not yet supported, that means that you can't use:: class MyTable(Base): ... ForeignKeyConstraint('first_field, second_field], [referenced1, referenced2]) This will be addressed in a future release 3. You are using one single metadata. This is a limit but it's my normal environment. There's not really anything that cannot be changed easily about this, simply I didn't have need for this, nor working cases. (While I was plenty of ideas on other features...) 4. Spaces are not allowed in field names. This comes from the layout syntax definition. .. _fkey: ForeignKeys =========== Everywhere there's a ForeignKey I try to represent it in the best possible way. More info in the completion chapter: :ref:`description` sqlkit supported backends ========================= Sqlkit is built on sqlalchemy that allows editing db with many `different engines`_. I use it with PostgreSQL, MySQL, sqlite and Firebird. Other engines are supported but may need a very simple addition that I'd be willing to do on demand. .. _`different engines`: http://www.sqlalchemy.org/trac/wiki/DatabaseNotes .. _PyGTK: http://www.pygtk.org sqlkit-0.9.5/doc/html/_sources/misc/backward_incompatibilities.txt0000644000175000017500000000523411533706143024776 0ustar sandrosandro.. _backward_incompatibilities: ================================= Backward Incompatibilities ================================= From version 0.8.6 (first public release) I keep track of backward incompatibilities: 0.9.3 ===== :requirements: sqlalchemy now needs to be at least rel 0.5.4 as we're using AttributeExtension to provide better MVC support 0.9.2 ===== :hooks: ``on__field_change`` now pass also the field. The demo snippet already had this arguments but was always None and was not documented :totals: added model, path, iter to ``sum`` method of totals. Impact only on customized :ref:`totals` 0.9.1 ===== :hooks: ``on_completion`` is now triggered when you press Return, both for tables and masks. Previously was triggered on Return for Masks and on click for table. 0.9.0 ===== :Mask: a fix in the layout machinery make setting width specs working... that means you may need to fine tune the width of your entries. You set the width of an entry as follows:: last_name:40 first_name:30- sex:1> the first sets 40 chars, the second sets 30 that would grow to use available space, the latter would use 2 chars and would right align it (compared to other entries) :mappers: if a relationship has ``cascade=delete-orphan`` set, and you add an object by completion Sqlalchemy will complain that there's a missing object. With SA 0.5 this is automatically done, with SA 0.6 this is no longer supported by sqlkit. You are supposed to explicitly add info on the Column as explained in :meth:`sqlkit.fields.ForeignKeyField.add_related_object` 0.8.7 ===== :Table: * ``self.current`` points to ``self.get_current_obj()``, no longer ``get_selected_obj()`` * button-press-event now has one more argument: treeview. This is necessary due to the fact that table now have views and t.treeview only points to the main view's treeview. :Table & Mask: * now keyword table/class/mapper are deprecated in favor to setting it as first argument. :Hooks: arguments in hooks of related widgets where not as documented: a hook on a sqlwidget acting on a related widget would receive the related widget as first argument rather that the main one. Look at the the following case:: lay = """ user o2m=addresses """ class Hook(object): def on_change_value__addresses__domain(self, sqlwidget, field_name, value, fkvalue): pass m = SqlMask(User,... hooks=Hook()) m_address = m.related.addresses the hook instance will receive ``m`` as argument where previously received ``m_addresses`` sqlkit-0.9.5/doc/html/_sources/misc/contents.txt0000644000175000017500000000021511530470276021261 0ustar sandrosandro================== Download & more... ================== .. toctree:: :maxdepth: 3 download missing backward_incompatibilities sqlkit-0.9.5/doc/html/_sources/misc/download.txt0000644000175000017500000000674211714207753021250 0ustar sandrosandro======================================= Download, requirements & googlegroup ======================================= Requirements ============ Sqlkit depends on: * Python (>= 2.5, < 3) * Pygtk * sqlalchemy (>=0.5.4). Rel 0.9.5 is the first sqlkit release that works with sqlalchemy 0.7+. * python-dateutils * setuptools * the correct driver for your database of choice among the backend `supported by sqlalchemy`_ * babel (localization) Changelog =========== this is the Changelog_ .. _Changelog: http://sqlkit.argolinux.org/download/Changelog Download ======== The code is available under an hg repository:: hg clone http://hg.argolinux.org/py/sqlkit You can download sqlkit package VER from here_ in tar or zip format. +--------------------------------------+-------------------------------------+ | Python package |* sqlkit-VER.tar.gz_ | | |* sqlkit-VER.zip_ | +--------------------------------------+-------------------------------------+ Sqlkit is used in a production environment and great care is put in fixing any bug as soon as possible. The first stable version has been 0.8.6 in 2008. I really appreciate any bug report particularly if based on a repeatable example, possibly starting from the demo. Windows ======= Read the detailed instructions in the tutorial: :ref:`windows-install`. Debian/Ubuntu ============= Packages are available, read the :ref:`instructions ` on how to add the repository. Sqlkit on Pypi ================= Sqlkit is available via Pypi (Python Package Index), so -if you have already installed setuptools that provides the command easy_install- you can install it via ``easy_install`` or better ``pip``:: easy_install pip pip install sqlkit Beware that that will fail if you don't already have PyGTK installed. You can also install directly with ``easy_install`` that often will fail understanding already installed packages. Should you have problems with ``pip`` you can revert to:: easy_install sqlkit No one of these command will install the backend driver (psycopg2 for postgresql, MySQLdb for mysql,...) that you are supposed to install by yourself. Sqlite is included in Python stadard library. Localization ============ We need the help from some translator to localize in different languages. It takes some 40 minutes to provide a complete set of translations for each language. Please visit the launchpad_ 's site or contact me directly. Author ====== Sqlkit is developed by `Alessandro Dentella`_ .. _list: http://groups.google.com/group/sqlkit .. _here: http://sqlkit.argolinux.org/download/ .. _Experimental: http://packages.debian.org/experimental/python-sqlalchemy .. _sqlkit-VER.tar.gz: http://sqlkit.argolinux.org/download/sqlkit-VER.tar.gz .. _sqlkit-VER.zip: http://sqlkit.argolinux.org/download/sqlkit-VER.zip .. _python-sqlkit_DEBVER_all.deb: http://sqlkit.argolinux.org/download/python-sqlkit_DEBVER_all.deb .. _sqledit-binary-LNXVER.tar.gz: http://sqlkit.argolinux.org/download/sqledit-binary-LNXVER.tar.gz .. _sqledit-setup-WINVER.exe: http://sqlkit.argolinux.org/download/sqledit-setup-WINVER.exe .. _sqlkit-doc_VER_all.deb: http://sqlkit.argolinux.org/download/sqlkiy-doc_VER_all.deb .. _`Alessandro Dentella`: mailto:sandro@e-den.it .. _launchpad: https://launchpad.net/sqlkit .. _`supported by sqlalchemy`: http://www.sqlalchemy.org/docs/dialects/index.html sqlkit-0.9.5/doc/html/_sources/misc/sqledit.txt0000644000175000017500000001152311714207753021077 0ustar sandrosandro.. _sqledit: ========================================================== Sqledit - the standalone program to browse and edit data ========================================================== Sqledit is an application that can be used by anybody without any programming skill. Basically is just needs a target database that can be fed throught a GUI or on the command line. .. image:: ../img/sqledit.png :align: right :scale: 40 :class: preview .. image:: ../img/sqledit_setup.png :align: left :scale: 40 :class: preview Sqledit is a data editor/browser. At startup it will present a list of all tables that can be selected for editing (choosing :ref:`mask` or :ref:`table`) or for introspection. When run from command line it offers several options: .. docusage:: ../../sqlkit/scripts/sqledit.py :verbatim: If called without argument it will present a connection dialog: If sqledit can find a demo in your systems, it will give you the chance to start it. If you want to type the URL of your db you can directly attempt a connection or disable it via a checkbox on the right of the entry. The autoconnect mode is really nice but you may experiment hangs till a connection timeout if you write a wrong host name. Since database urls are not nice to write, you can store data in a configuration file and call nick names instead. .sqledit ========== You can write configurations in a file in your home called ``.sqledit/nicks`` and start sqledit on that configuration using a nickname:: [invitati] URL = postgres://sandro:xxx@my_host/2giugno table = partecipazioni_invitato dev = True field_list = nome, cognome, and all other fields order_by = cognome, nome [brasile] copy = invitati field_list = nome, cognome, email, ludo Valid option are URL and any other option for sqledit. The special option ``copy`` force sqledit to read the other definition first and then overwrite. In this case 'brasile' shares all options with '2giugno' but overwrites field_list. Even if you don't want to program in Python you may want to add configuration in a more rich way that allowed in ``.sqledit/nicks``. That can add for example layout information or information on the relations between tables so that a Mask can present a ecord and data related to it. Let's say that that's a gentle introduction to programming with sqlkit... .. automodule:: sqlkit.misc.conf schema browser ============== Introspection of the database will give you the possibility to see all fields of a table showing all fields, with type, primary keys, foreign keys and indexes. If you configured a nick to jump directly on a table or any other configuration allowed by sqledit customization, you'll need the -b (--browser) option to get to the schema browser. Options ======= Calling `sqledit` from a command line under a Linux system with bash completion you can benefit from the completion that will look for completion in the .sqledit/nicks file and will suggest some common url (postgresql://, sqlite://...) When primary keys are numeric you probably don't want/need to see them, you can switch off the visualization with the ``primary key`` toggle button The ``Load`` toggle button determines if you want to load data when opening a table. ``Blank`` toggle button determines if you want to cast blank string fields to NULL values. When you decide to cast it you may be prompted several times if you want to save changes that you are not even aware of. Configuring sqlkit ================== .. image:: ../img/sqledit_config.png Sqlkit looks for possible configuration options in some tables, that may or not be present: _sqlkit_table, and _sqlkit_field. These tables can be edited directly from the database menu, or via ```` shortcut. Completion will help yo configure the fields. Here is the meaning: table's field ------------- :name: the table's name :search field: this is the **string** field that will be used when searching via foreign key. Suppose you are editing a table of movies, and you must fill in the director's field. You write some letters and trigger a completion, that means you want sqlkit to use that text (e.g. "Fel"), select which directors are present that has that string in... well you surely want to search in the last name, but you need to tell sqlkit. *search_field* is here for that. :format: ok, you get back from completion a list of directors you still need to show them in a nice way (e.g. first_name, last_name). Here you are supposed to used the syntax "%(field_name)s". attributes'fields ----------------- :name: the field_name :description: the label you want to be used. (Note that when using :ref:`related tables ` you may indicate relation.field_name) :help_text: this is the tooltip that will be added to the entry :autostart: you can set an :ref:`autostart` value for the completion sqlkit-0.9.5/doc/html/_sources/misc/tutorial.txt0000644000175000017500000003755011545055360021302 0ustar sandrosandro.. _tutorial: ================== Sqledit Tutorial ================== Intended audience =================== This is meant as a tutorial for the :command:`sqledit` command that is part of sqlkit. It's intended audience is anybody who is interested in editing data in a database (as opposed to editing the *structure* of the database). No programming skill is required, but if you are supposed to install it yourself, you may need to understand at least a little bit of your operating system (but that may be as simple as a double click if you use bundles). Installation ============ According to you operating system and setup you may find the very easy way for you. You may not event need to know which are the dependancies that are explained below for the curious ones. .. _ubuntu-install: Installing under Debian/Ubuntu ------------------------------- On Ubuntu lucid (10.04) and probably also others >= 9.10 you can prepare dependencies:: sudo add-apt-repository ppa:toobaz/sqlkit On Debian:: echo deb http://ppa.launchpad.net/toobaz/sqlkit/ubuntu lucid main | sudo tee /etc/apt/sources.list.d/sqlkit.list sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 39012CF8 and install it as follows (this installs python drivers for PostgreSQL and Mysql as well):: sudo apt-get update sudo apt-get install python-sqlkit python-psycopg2 python-mysqldb I'll try to keep this updated as the official package .. _windows-install: Installing under Windows ------------------------ The easiest way is to use `Windows Package Manager`_ that handles dependencies (that can turn usefull for other software too). The installation process becomes as easy as: * download and install `Windows Package Manager`_ * start it, select sqlkit and install (you may want to select/inistall also the database drivers for PostgreSql and MySQL) If you want to go the "hard" way you can install all peaces separately but still use the Pygtk all-in-one installer (thanks to Dieter Verfaillie): * download and install Python_ * download and install Pygtk-all-in-one_ * download sqlkit-VER.zip_ unzip and install by doing:: python setup.py install Note that python may not be in the PATH so you may need to write it explicetly. Installig under MacOS --------------------- You can install and run Sqlkit under MacOS also. You can use the all-in-one boundle_ (thanks to Anders F. Björklund) or install from MacPorts. Dependencies ------------ Sqlkit depends on the following packages: :Python: sqledit it's a Python script. It should work with python 2.4 and following. But if you used 2.4 be sure to add the driver for sqlite that was added to the main distribution only starting from 2.5. :PyGTK: any Linux desktop distribution has it already installed, in case it does not have it, it should be trivial to do. In Debian-derived systems (e.g.: Ubuntu), you simply run:: apt-get install python-gtk2 In a Windows system it used to be a difficult task but now it's very simple. Please follow the instructions on the `pygtk site`_ :sqlalchemy: this is the core of the sql staff. It's a layer that builds SQL statements and invokes the backend drivers. It takes care of inspecting the database and so on. You need at least release 0.5 but 0.5.4 would be much better. :python-dateutil: needed for computation on dates, used in filters (e.g: date >= 'm-1', means date >= starting of last month), see :ref:`date_filters`. :babel: needed for localization of numbers and dates :setuptools: needed for the installation and to check version of sqlalchemy :drivers: don't forget to add the driver for the backend of your choice, the only driver included is for ``sqlite``, that is the database of the demo and is included in the python distribution since python 2.5. If you have setuptools installed in your system, you can install whatever you need simply with:: easy_install sqlkit and probably you'd have better results using ``pip``:: easy_install pip pip install sqlkit remember to install the backend driver, these are the examples for postgresql and mysql:: pip install psycopg2 pip install MySQL-python sqledit/sqlkit ============== .. image:: ../img/sqledit.png :align: right Now you should have a working setup. The command we are going to familiarize with is ``sqledit`` that is based on a library named ``sqlkit``. If you are a programmer and are interested in the sqlkit package you can find extensive documentation in the `web site`_ Programming with sqlkit is a pretty simple experience that allows you to use many more features than available with ``sqledit``, nevertheless you can do a lot of simple tasks by using ``sqledit`` alone. .. versionadded:: 9.1 Sqledit has a flexible :ref:`configuration system ` that allows you to add many code snippets w/o writing a true program, so that even if you decide to start with sqledit due to it's simplicity you can add more configurations as far as you needs them. I personally started using that as my preferred way. Sqledit can be used: * from command line, possibly adding arguments and options * from a menu entry interactively writing the URL of the database you want to edit. If you start it with no arguments you are presented a dialog with an entry and 3 buttons: * you can write the url of a database of yours in the entry, e.g.:: postgres://localhost/dbname # sqlalchemy 5 postgresql://localhost/dbname # sqlalchemy 6+ sqlite:///db.sqlite mysql://name:pass@host/dbname .. note:: the URL for a sqlite database has 3 '/'if the database is in your current directory, 4 if you need to pass a file starting with '/'. * start the demo tour Sqledit table listing ===================== The demo tour is meant for developers, so that it shows source code as well, but it's also suitable for our introduction and is a living database, so we will use that in this tutorial. The demo presents you some examples on the left. Let's start with... the last one! We start with the last one because it's the window you will see when you start sqledit with an address of a real database (the demo one in this case). The table listing ----------------- The table listing of the database is shown above: clicking on a table name pops a menu that lets you choose between: * table view: representation of the table in a spreadsheet fashion * mask view: a form with each field is displayed * table reflection: sqledit reads the definition for that table Tables ======= Let's choose a table view: .. image:: ../img/table.png each field of the table is represented in a column, each type has different representations: :text: a simple cell will render the text :numbers: each number is adjusted to the right :dates: dates are represented in you preferred locale that is argued from LANG variable or from locale module information :boolean: a checkbox is used. It the NULL value is accepted, clicking the checkbox will loop between True, False and undefined :intervals: intervals are really poorly rendered at the moment... :foreign keys: foreign keys are represented via the value they point to in the remote table. At present only simple (not compound keys) are allowed. To help you detect that that's a ForeignKey it's drawn in blue. Just to be pedantic: you won't see the real value (that may happen to be an id, normally not very interesting), you will rather see the value it points to... As you can realize there is not real *value* where is points. An id points to a record of a table (e.g.: director id 1 may point to the record in director table where ``last_name`` is *Fellini*), but *Fellini* is not the value of the id: it's rather a representation of the record that in many circumstances may be enough (and in many other is not). So I introduced a rule: I represent it with the value of the first character field of the line. Clearly this rules is doomed to fail in some cases and you can correct it forcing a representation of the line we will call a format field. You can go in the main window of sqledit, select databases and 'edit sqlkit field' and you will be presented a mask to edit the value you prefer. .. image:: ../img/sqledit_config.png filtering ---------- you may have a lot of data and what sqlkit will help you at is to :ref:`filter ` in a simple way. Each column has a clickable header that pops a menu entry. The first menu entry pops a filter widget: .. image:: ../img/filter-panel.png in the image we have clicked on three column's header: the filter on each column is composed of 4 parts: the label with the name, the operator for the filter, the checkbox to disable the filter and the entry for a value. Some operators have pretty intuitive operators ('>' as bigger than or later that for dates) text have also regular expression (normally much more useful so that it's the default) or ``like``. .. note:: you can select more filter for column, click on the label in the filter panel. You can for example say that you want all the films produced between 2000 and 2005, that means having 2 filter on the field year. Pressing ``Enter`` on a field or the reload button will run the query and present the selected records in the TableView. Dates are special in that you often have to filter with dates relative to the moment you do the query (today, this month,...) so that i added some shortcuts to accomplish this task (e.g.: 'm' means the beginning of the month). You can read more on this feature in :ref:`date_filters`. totals ------ .. image:: ../img/totals.png One more feature of sqlkit that comes very handy is the ability to make totals in the fashion of a spreadsheet. This only works on numbers of course, and you can trigger this feature from the column menu. Since our test database does not have numbers other than for *year* of production, in the example I joked and computed the total on the column of the year of production. In real cases you will do sum with more interesting data... Subtotals are a very useful feature of any total, so you can ask sqlkit to create subtotals when some value change (e.g: date, month, year, director...). completions ----------- When you enter data in a text entry or in a foreign key, you may find yourself typing something that is already in the database. In this cases you can have sqledit to search that text for you. Really that's a must for Foreign Keys where you can only pick the data among those proposed. Since the possible values may be a lot and we don't want to wast time waiting to retrieve data that would only confuse us, we will require sqledit to show possible values pressing enter in the entry. In this case the text that we may have already entered will be used to filter the possible values and to be more precise: :Shift Enter: will trigger a search using the text at the beginning of the field :Control Enter: will trigger a search using a *regexp*. If you don't know what a regexp is, consider that as a minimum it will do a search of the string in any position, but can do much more and really also depends on the database backend. :Control Shift: will disregard what you have already written and do a search on all possible values, thus emulating an ``enum`` field. You can find complete information on how to configure :ref:`completion` in the docs. changing view ------------- When in a table view, you may want to jump on a *mask view* or even keep the two open simultaneously. That can be simply done by clicking with right button in a row: the menu that appears lets you edit the row with a mask. If that's a ForeignKey column you can even edit the value the foreign key points to. Mask ===== .. image:: ../img/mask.png :align: right The other view we can use is the *mask view*. The records are presented by default in a form with the labels on the right and the forms on the left. .. note:: This is just a default and the only one possible at the moment, but programmatically you can choose any fancy layout you want, but I won't digress as I want to limit the information for non developers in his context. completion ------------ In this mask you can see that foreign keys use a combo with a completion element popdown. Same shortcut as for the table one are used to complete. A double click on the arrow let you use it as an enum field. filters -------- Filters can be activated clicking on the label. the filter panel will be presented as usual. The difference is that when the query is issued the result is presented in a tab of the filter panel and you browse the results clicking in the output tab or clicking the forward and backward arrows of the mask. layout ------- If the table has many fields, you may get a layout that is not very usable. This is a limit of the interfaces at the moment, not of the sqlkit package that can handle any fancy layout as you can see looking at the examples of the demo. The library also allows you to edit related tables (i.e.: director and movies) with no effort, in order to do this you need at least a minimum of programming, namely: * defining the model (as per SqlAlchemy) * defining the layout (this is very easy and demo has plenty of examples) These 2 definitions can be written in the configuration for the a nick of sqledit, please read :ref:`sqledit manual ` for details on nick configuration. The Demo ======== The demo is a pretty simple way to be introduced to more advanced features that you would only have with a little of programming. I hope it will encourage you to do it and possibly to approach Python. The very important thing to understand when reading the snippets of the demo is that each time you write the table as a string (e.g: table='movies') you will trigger an inspection of the database, but no assumption is made on the relationships between tables. When you pass a mapper or a class (e.g. class_=model.Movie) you are passing possibly more information. The model in fact (you can go and see in :file:`demo/sql/model/movies.py`) has lines as:: class Director(Base): __tablename__ = 'director' id = Column(Integer, primary_key=True) last_name = Column(String(60), nullable=False) first_name = Column(String(60)) nation = Column(String(6)) movies = relation('movie', backref='director', cascade='all, delete-orphan',) where the last line instructs sqlalchemy of the relation existent between the tables, and more: it adds an attribute on the class ``Director`` that holds all the movies produces by that director (and vice verse thanks to the argument ``backref``). Adding these information makes it possible to used the layout in a mask to produce a mask with director and all the movies, if you are interested in this part... let me know and I will add more info. For the moment I suggest you to go and read more about :ref:`relationships` Feedback ======== I hope you found this tutorial useful. If you like this piece of software, have suggestion on how to improve it or improve the tutorial I'd be `happy to know`_ cheers sandro \*:-) .. _`download page`: http://sqlkit.argolinux.org/misc/download.html .. _`pygtk site`: http://www.pygtk.org .. _`sqlalchemy site`: http://www.sqlalchemy.org .. _page: http://sqlkit.argolinux.org/sqlkit/filters.html#module-sqlkit.misc.datetools .. _`web site`: http://sqlkit.argolinux.org .. _`happy to know`: mailto:sandro@e-den.it .. _`Windows Package Manager`: http://code.google.com/p/windows-package-manager .. _download: http://code.google.com/p/windows-package-manager/downloads/detail?name=Npackd-1.14.1.msi&can=2&q= .. _Python: http://www.python.org/download/ .. _pygtk-all-in-one: http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.22/ .. _sqlkit-VER.zip: http://sqlkit.argolinux.org/download/sqlkit-VER.zip .. _boundle: http://afb.users.sourceforge.net/zero-install/PyGTK.pkg sqlkit-0.9.5/doc/html/_sources/misc/missing.txt0000644000175000017500000000122411534024230021063 0ustar sandrosandro====================================== What's Missing and how to contribute ====================================== Well, this is a pretty large chapter... there are tons of things that are missing from this package and that are really needed but I want to focus on the following: - saving queries - good field render for CellRenderer (notably TEXT fields and DateTime) - good system to warn people of all orphans when deleting - integrate a permission system I'd be happy to accept any suggestion, patch or contributed feature on anything that can make sqlkit more powerful. sandro .. _PyGtkMVC: http://pygtkmvc.sourceforge.net/index.php sqlkit-0.9.5/doc/html/_images/0000755000175000017500000000000011714210376015511 5ustar sandrosandrosqlkit-0.9.5/doc/html/_images/totals.png0000644000175000017500000013371311421620210017520 0ustar sandrosandroPNG  IHDRPsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxwx׀ٞ+ &"JS:Ył  vD,VD]V,btBIۖd&&Y>vι33Ν;.2*^ '+vHUtwG-2̝q ~>F$i'ިR3oԲhVwlLS}$|Q KQ@  IgDҙw(~ׁѿeӑK{JXOVt%Ee G/Wa؉֫wwZQ]KIJ, !tTx&EX *X  [PtxW $6'v&TEƒ"ۼ*X /8$k 4%[' ۬Xe^`559F-;~]~E,ƪ|4F: ?@h "f; sn7NG޿+֥LUn}g;M|dBQl6/;G͆(ѝVo=INvYW][ѥUXrdF~>AO!ksN<2Led֩@P~-z5/)sI;|{w}6yNuU2zm2/j|+m Ż^Yp5ˑmVf}m}N[cYO<\3\5G;*W޳\1|8FFA4h4׾A#I3obm8xMJ| { "/BFA&>>qFQcvdWsR0O.#MD%Z >.oB._8}Cgn0tx9e[P6Iqxem[Y\26, ,OrCzunOĶ憓̍dow, LMc;7*DydWpS8xFwQ:A iXR~eu 4ؖάzeUk̃x 'O NC@\Ot>AlݸGcTHM3noϸ*]ɩp³&vqY׳-멜+D3kb]pvH,hTYF\T4#HgnS)&jSB+z/-@#I߲v?e%lbe% i#ZV{W#cJl?^XcZ}j`K I+q_Ol̥cKeloraK펻my4,3w"Of> aKKjk7v{x]NiCωpZ%۲mK[)eۥoW]'kұXuWLG )( "x/򙞰7lʊGr*lrEP9r2Wuvx\3c{*o2JK\O#JメS*9~M<"SZRZ#oW:GGDz#n1:₋ؿ~) _|HVi.hOr\^owZɐ[b\i51,>Dޖ!w= >TtQI13^eNsak_ *3埤b:Ei:͓vkR{>/7.1q9t$>&If O͡S%:QL-[nnMF#){v: mNЁmN䝢GۋH=v[7ѳZҒEVb"W3۰Z͎;9fjʩZg8we(د:kBBBTTWNCr ;X 200a%TYA*:i5>qpH\+'E\@tl3T5 hۍV VsdE ϢkѲ|$I&d)I_@H-1dlISZzvlipkJćS+X1FAk%>#+#U!@%ow4Лî (><͗X]R[۵q{3lQ ė_B4V4|gG5YcgΗ894,ߙˏV}Q֍-U圹~;{;ŤrGO޽+O4]ҷ,Űl[׾;/_Yc݄gQY߇IG[1ȵ]}=~>@UO 4iK=<9'es1Z1 3eΣk`t X3w0kz@-.^Ki[UQW=H ٻWsd&^?|BK/[^7uFAѐq kKPr:*c-,ū/Z+iuʗQ ӊNSD;2WWO۲R 主ryS>-Xgu!yʗ+?TTvg*)hژW`Ə$e)5͂ ąY3x'닿IKq/lO)y{]_z/J/̦`ҵ/~:ZHŒ\y|[3|ˋ>bZ 6L|˵lO.Ģ`+93~_YnH5Sb15t6,H0}54mmMѷe3]I*lG>|SyV,6?Dp-Bą۳hb!R2c M273әV.[4hbĤhC#g戹JǸ_c]Y:䴏sȹי{E:y$@Sg_]qC3i9)D;(CL#fE& m"th*tchG_YUެfcx'_Z)(  DiL3}ֆ ~3PfQfP/: JvcO~HNr;D(*EUk%#UOH[mOU4M]HʱQfUQUk%ZhV! 6:lmGS>>ҭ _ KEF*TV+:MaoWWrzq烜~tt`':TUzTĭd%ݽ%UFbi]y v#$T\G2c5ֹcp{0Ȉ 1RP? UD'jj7OtvU'qm#0շEۼ.+S\w d&w,؈QeALk?zu;_SR垽!wx}NpI|y8|{(dxƨs]jcMפMfβS(hiG0=&TH{Z'_S)L$FIǫB\#Q6QRFwl8RDvB׊^}SݕJwp^)1vg @(1Q0~Yi EN@ :z@ 8K#yeyOVU@ hT~+%=5ߦMw@ &E^ Ɖ&\:{@ hz P09B Ya|xvrnoQ l塙q:-x.i,DcS <=v ]9E)Pٶy/ӳK7 %@P 'twx"':(;=ڵmtzPUUF]>ǎsxbdTPٶ1`gݾl֬%##__uMggM[lЭFܥl[@ 8W4gx_ rz߫pA?___ࢼbAӝU32fҍLw%_|sg"~%=g_w|B{zwEPâ:uN\e/ /LCS\\,zLe S{|줃b执gߋ"=ګ/ ~T6 wul 'NKdۂvv'͚Šk b|[6KL]k֮cثԽj?{~Dګ/>WD:.jW9}Ȟ}YlZ _HVmz 6\؅¦[+`˜txug_mϜ:ƫ/rMrsb6٥=쿇o7AGEoM7Ljm׬cܕckW]hcOeՖ^>}f|'ܕU]ݥv>V=[lVł7Z "x3xΡ}NZm{?0Gχ?P{=4[L>[oFre+xⱙʓ=/ey쩼=lůJRrkmkmyy8UoF{܆r.@fV&99D3kL{|t>6c3^l_DAo4hQC{wPN9OL{TƏ7|_Ck!y3~\fdd;i,)&{nu9fS0 /88l"#qm޾sOL ^wciN6|<]hެYYx}z6װU/?sr\uUz'_Ï=NC78 D)Z D*!gt sJ?zF{n<+ `ǮD=Lmpq~\3wLF=x*]*vۋ^GUU֬[O>xMWwi+۔zaa^m_@ 8x}s믙Ȭϐr6C3}Loޝ[).Gy/Mvv6Ͽ"cFp_>f$/2999dgg3wދ5S~I?S6z]moڝoX;b())au˛m\u9SrQl6+"+WuuV|+΋+.ADfNթ)Z}{-SňLZjw湬Q%1lXF {9nfyF-7Mf.q%WҪe ƍ+vwn-@]=+x(#]ʷ]:{Aʉjْ^xu9+mك˯Hqq F iwy}," O]~#yYiHsYɿR{d5VEE:v́m5#&_vzjSMKHNIaa" FĭKs_T 4^jX_P'nulkzO*@ h0/.m@ 4D^ & @qO&=z@ :8+#T^32.}M@ 4>jX_ ASA8{@ 8 @^ &p@ 4qSWܳzZ?Pc\bBQ5ψCiӺu}a [><^2ɚ<ُA%X,V&.߿s_E ޣ^~Lht:zgͷ_1mTu,Z7ozp_&֭_vØe,u%GbxOnό:+.˶[Yo̙2?XwNu~E:^{>f񢛅&B=@AAr"Zzff~ؾdef)j8QZZAoɾ* llVtz=#G__~bsJ;cj~/Gܲߣ2AķkON]x7Hc]0vH.1mω%D6M&(@ 4$I(o߾#@Qd|LFv45ʠSUYVJϠc\>n<Z¨ý"&VSO0~8/(zM6m.];3k?` STTDFF /?? zU_縡JQq)úmI8QDVCg0` _ 9 r**ZW;J(dB lıDDDZAOFFV"88^}/?bKCef??7^OdD99|׳mvN>ѣGIIIa„ $$$,t܅Xf<1+zU/EQ}YLk-Z_@Mhbbð=,^pp@ЈiGUW^1(6xE)**$8$"+ Q}VviifsK.ݻKxUʴnݖǏ-Ge+h<`8/!7/^O^el獧mӑ^-#Q% E YYUQ%0OYM A#a^4* fwg+l eLPɄ*h4ZlV+t%!!:{zCʉdl6hZ۵UVPPP@dd$F(4.Dn8A GABQ_P YQI(m,"@ 4Rz6a|Y(2,#Vק!S(+-C׃ AA(f`0ѹ(czpi(jG_t>N>=iӺ-$aZ fk,˔hb޹!RU^EV%Tҳ8Z@zZFb'onGXX(z+zӰ~ỳUKdYj"6E!ILۻk{CH\;EW/$D2G&$$Ie @qq HOKcm;+ȲϿ BfE IDATB&n#ڔCߖFÞvGБhѫFW}tt(3yC<,_g fHHgFHGQ2@_ +ؐA<< ?>{fOVVl2MQRTN;noۓn E]4uwW@'on-@ 4Nի\pH7 +2l6hrGO Jn>8tݯĶ}1Jf TkRN4?B!C_ʐjAِe6|ŵ 3+N-Gk?F S hVKn^.vѣW֫=!S6Uq'?i*Zh_>hb:v|x-jf2_+zsAiެLqtԓWСC?N=8r+V e:>Fl X9q:[jKlL,Ȋ[O" @8z@ hBgD@/V ë*2槃,~PU4h; Gyʩ}~#kz=ZTST\DpH0))' `0 2 IcbH^XtfXN痒oEV5F!?SK#z^ǑixpAqAҠZϮo`4@  ^sYuwYAq:C~hZ݈U?T!bKڄ|pRpgI݉kb:eY&66#GBBBL&Z7#"//^Պ^C; #')Th: lޓNtA[VO} nnITŪO@g:㼽H[yϾŊV]iP@ FaRTG$H:ݮ@~ߨEZo]EAUUJKKf3???  *kyiiih뭓N%.DhN)!b7ŻqQx4C-$ L.,,#G}uӳWT"+2H4mNa'hb:@IRj+EN%+3 n^]dR+Zn`WAW򲲲jSu zڅwG >Ah4Z2v,X̾ =n8o a} 4 iDIN);@𚳯O^UUY$9޺ՌԺJV,Ƿ5+DY2ЂҼQ2 j7L-(ii go2I@nQ>FZ`|[H=DУ5PPa9sVLb_  N%g]}W~~Ȋ\> TIBRUpd%H EVDwDӼP2)( 5]mW- M̾@`wkZᄌ'CCC8}: 2 4f|0Vnh hZ>zLz4Z=NghusFuUiAAl5h@*T]m4hVy.(e-mGQӐwVr%+ vj- #KUUuȡYmDGPf.#??ߩLʉY:zbd4j1zp72׳J_$!IA؃{eG6?#!88As@p_/,YUnm$/YQj4U1[X,,Tw vgkLFm.RNTIj l朰j(Ҡu $qWdJVQ$t@aA;޽*zY9$' ?6mObcc djw$Id2ucv^KNa?|#C1J,"3~ax{ _֝L&W^~%gshJf_"[ZdTT:uMh2q4?B+hA@-WQUFzem|V%"<Nǭcl۵v_X@qqSɄDddC ! t5 lт t9")Gc14a4G+ ڴ"&&nj}@ 4ko$/+B׬e?aa/z45Rt`%ґhRw_ nҼ׿z*`(+3;kZ4; h4Jh@ӣڛV2"''b*0MJDx8AL&iUUeȟjK;zC@pMf\Aݐ$ NNVG g1.+AG8{@ 8 @^ &p@ 4q\?oC AiG\:2 @ h|a|@ 8 @^ &N5{[@ S9gg&!Qr_@-Z',2#Y^@ G~n6GmBChjdqtNtzA!U @/aowEpx[G}px]zao/@ 2&BT?(__GyEvJ$ !$ijWE$ܸp@ جmۈjiP= @p2m"7+;Z ^  UU9\r8߭^ OUM_˱dvK?‰#U_߳1OFF^<vmIۄ@ 4cǓl("""ؼa 5RmlҶSw]0ɷZZYY uo6Z'b\۽g/]:w^ xU{{n]+=#= N,u)|ξkgC5@ x@=\k#gls {`aO`EOS^n}՜}YY ŰQӱsw$'7כ %w lSܩS Ȳ_υӱK{!ySN;stܝnJVv#u|~) LZf5*`"2Xڷ` ""ۊkx5KN<;gc~ի z^p1 ={гgN˯cy9^ÜgU'qXrY,8$$tp%%'вUKNW,o az[ud]w0W3z;o ɾvzzCGv_}^2{Z:@ddY3kܱ"S'TmSzl^7|o,"?|.*_pI]3Ѫ^+$דkҟ0|ur~];#)_`V/wT͓8ܙϿ(Vo+jx0pug_qб#x1+] ?{vb4@(z٥B^z5v_pEDorx?tyRNq"51z"5)>>u.5t֕} UUYf-3}-zݮܼ<b._-pZ\^ߦu+1l6rfLF#+vUvuJJJJ+kPq-z wnT'.xbN<ʩWaexX,VY_]|p[Q6Oڣ1ug-S `ĘO&@pKN <8Q]6NL> ܳh©.O/4qW^?.Wߋx|S\|EN> .K>̚ @zmGcDпfF<.d,%7ع+P6;1\qswNm,|z?Y5JOMbv\@P7Eߓ̘ѣj/ -EUPAENUJŶ P; 1m;tyfh4UMq2XjEQ +eTEqlcΧ QSCq'n@ hԫg/2r@APU?s?  *T=&ݺuas=ݺuaկ?2xUn[,Vk} JtJH +俭Y6;G|yyV,K} @ 8[-,g/A^~K@ ߽:r&m=e~\cڎ;9ycUU$B"2x9R h܈y˱ǘtÍ<8vo~FA8:={@p" nk.ZnyGC,lٺmILׅuӵ{OuzShS}^ H})?={2p@Nf5qa]n߁ ٵkWzKyysBN<4-#8g/{Z Æ ec7RfǪ jIMۚo[⭷9Kן}3~pUi,7Գ-ap༦CȉDDEaAOb _fmrOPO?9O?/CYY:V%ٻ`9NAcb `tʾ, {KI-8U,q"-r?Y;`nfBBB{*tMtѫZ￶$'pӔi׾ڴIr',)ԖYy~ tփmi;{է/[l63cô@ |fS}?."Zj#ٻw6\sV޴2-omfhѪ \x_|u{F;׺7*PUܜ\PUOez!fS@QAGdy>#O>喛0O_|eټiu)*74SocO.w]~y+O<`ؽ{\ĝF{yw+8y_"-- ײ~N<_vʳaF~^#exQmPAmm箭i'e<{}0sC>%?|{C?Wb.=2Eev{Kq1bA!DrӔ̬1<DFEÓOrזȘ1WNVmO7@Nduڹ;z>>Uf8ºYauhuz,YJq\}IwYcIuF>3l6ϾgM6iG6>s̤E3-[:wז^[qUiߞfqiמ+iii 81ݭGj ֑dS˖-fy_mmW͞4om:^ˆ_?G}9'^e|r͚ʤ&SXXX-_NN.͚W-y9z+t=Ul涩F\\K^y:^ZZ-(+v#?~8Wа+]j`oкM;&Z .˗#'2"y/eƍ5gCLL <3>g}YO>qWK\\wQzꉏ~ N`d.l\'λSsc̷yFa6no;KGDD8+o{^w-Sc6Np~ԕHvld_M5dSrrۑP[էcs]Ԗ6{|ݻxg1^WΉ7Eo6z"jBCC?~<|S~ȵ^CHșȊ!jiSY<28t[ma߫O!?Ç}6֯_fU{8BdڷoJ'Cmvl޼l\VR/M?~^Xc}ݫcWϞ۷U"-=-[uOv^_TTl64kVo3>}:s};wҽ[W 禛n&11:,^={)E8wҟ~qxzO;-*eeeM&))'#jroq23f>BRR26pTWٳ"++YOf\cR[V޲.ej[w=CcZQU{G!י=siK-Z{vr8FEF̻;qsZR\lR=zP59dE\ƪqݓTomƪs߷FݵQՐFD{\&'[6jJݻ'<F-+-v엕ZtV|{Z IDAT5)5)uԉX?K--.t>HMOOSO>&''׫֭Ul٬8_ݾ}z%111!C^={;wlsYAe2mŅj^N:h]v+/;֖^5wj԰0s7_5~y/U5u+.W_?OҥB/nW!7;S;͚͛5S57;mIV޲.ej[_ڧwo5<<\o_u=oبO燐Y4o>5N ,G{cǎc8jv1,[g~RM+uOQa>Z/਷j+_lܸǟx;wQ\Ee9i5k/>5 Q{ķ@Fzt.=95O ̲o?f.Ӗ,Yʁj7$,3!!$&?"88YO<ΕWH13xU.S:mNtt4֮&66֣w>'^…gMVTTIIIm*rV_dd$)))nduuO3|0),,$<&/3 ̄w^nv_۾c:u8k.dF' t;wFVOj1x`(..]|; hZ3O宻q1l6{a  JAA!o6~e : Y^̘YddfC3T57?BVV6YY̘YpeeLFL&IIIy+ G{.WyäI̞4O̞47NqM7wqU_ 2 Tsޘo Jh\Y$W_{{" \p3d+p xs??'9gĉܾ}۷;t*XYZA5oo_ ASJAr/|܂D⹿hּ;tʺ} F~̸q?:cǎI`llziպFyacaa'u֣qFzW˖8kׁf͚X7q XPbbb]8tƿCмY3ԭGh٢}،.oԬIU7w>| j066Ύ+bffV*12*k"ŋŗKϗv(B}L2 ֭K;@5{!Dtf/)ܾ}[ H.K;$! c& !^ I%+oԬI5"8xji$BKȏ?fvB!Ddf/BqB2Nu2nG?ys2nOubccYƾxuHBV}cn-+OSK䦟}"7$ _k^FUMb?,oY zG|9:9O}%GfBV3=f|g?FΙ 2?9BFUDZ _W=.RDZM?1vt-ʓ@FF-3vt~ҩKR٥ ,}.?2!^J0K`,bggg +~ Q$ !^Z=bGQ#s~(㑺0++kMS{i*n4lܔWVedd] .[.bϨY.U=?~4l*nݾ#gϞU3f8<]'§舥%a!USfanΔɓevϜ1L>3gΨ6lDXh( ;;;j6mLHp0'QIB Z!YM9r:$%%QG'gyĊWeݻ:ФYsvܥ.KHH%߾iG'g]MJFk' 755%33S˓SG׾*W~rse}K^#DYRqﯓh¤j녑CyۿN͛``E8}"IrIz U#,jצ{=007a=^_Qegadh*;- 3==usa;v&(0ccc֭ˊRmƎ 9~=ww<۳88|ܵ#::U*.B?bbn/je yCxΙCI76IvpUct~6Hu8D̂/0=SC0`Cz׽*3,J miެW7,ř:kydddR,{w|TwSCj߯/Nff&/\`חPP*L ѹ|}Frr2Ʉj$ .IBիN9:ghEjd$$[?=&ʭ0R(}cc,jSgt(.[k#jژ m۠oj1*VVV,-- sw+0ecw9;uCq&<|:_,Z7~X<==x4jҌʕ*>Oƍyx1:tyA&iێf͚6O_SacZiK)WNKlk٬jQ^_FF1$!ijRnݎr6<=p;2Oyu;PwϤO34q wةseS<ώkؽoeהŽOs._c޻5]8G5dggoK](ϕ+/G*PD ھa%>[vFCGkB)*I;ve;-p|\7(WF:/)gcؑ<8|G+ptR Ij|+++}}}k+>}e)uSCB?v ="$$;vHHxH?zƕq>#"ߺɷ4f)\K:w`߽+ffS+mz`D`m>zUR"ׅs4kъ4ڷkOP@@i$J${!^u*{?T*l;GѩzxDT?Og`M떺12BI9?*20X"C+l5mC f(e@OW\$DnŅh.hcQ&N%;= yD;DKuk9u"7w;=q !t'3{!^qΞrN_ ==2ZVfQeee1}gԬ] كOTqaj,##0ިUot2utr?аqS\v={V]Θpv7YDGƨc}}x\/$ !^Z|>1'Q3a}g6NccçGC`x._<Ϧ 8qB]1w.^b׎q07uy#ΝN .='$9rA׬ܿ?f;vN$ !^jVgc`hz϶/{{{Zjpttܜ)'˶rn%$s#XrZZZs3gL33S̙3 7BΎiaZǥM7-kId5Ujz;>>|4=⛯1B"Ҳ= əJ]U.cƎȈ];kx?7{ᅿtA]ީC7bBuqc୷ӨI3*Ws5 5Mv4kTy ̜ ӺM[ׯOrݟx9lDa( /sKĄ9g0> ֭߿Nur:voٸ5eqӜ?auYll,~o,[q],=prrʷ?Q\χ}9zPi"t}J|:v˷֍([r3epTvv z졑®" --IJz1ӛo]d 669]{<<<|2w$= G.]w0gg@ w rnIIIAaWujNLT2q$ܹOtzU}>k: TZU]jU֮a4hj5غuy&5kQwvvs q033ӸTO{\z7xQNE[mk[$cE}6UQv\]]QzLaPV-_W|CBz7FmDEE]zAl,X0??oEqpp Gϫz[]߳?ϏkWHOKAx+SBPز~ZI&&:isΖwBLJ,ٿݮ=?q+!,nܸ)Ҹq#u޽z2~| $$&2n\.o?Ο?=*2̼tR裡իdffr|gm_=k iicbbBTT~EoW^r/6܏XRSSٲgO)u.o?Ξ=Kff&W^e萏=SBQb~ʔɬ]ڵ`Qފ|xU~T ž=իAoXɑuyΝ}ml<nlر4iڼ{xhּ;tʺ} kW%߳ղeNƖux{oū-+OraM4Y8Vҥ)rz݈.|} V6ն^^,\0R['x5oߑS&Ӻui=!. Bfv^x%!DJ?_!d/BqJ/cz_ƘD5{!+AoFR>9ReWZmN/գs_xīC═lgiqPKL0B۱.^[]<kq$zQ\r_J76 F>\k?ʥܧ{LٻolRa R*>#]G !DDuGTv5=/T瓽)ʕ.sϾә0q[~ fxYY̜5kC:thOĜXo枱}%nˋo={ kTX1G; g|FdOdff2v(הO>e˖ڵ 066z<ə`%ߢj*̞5kQQ|h117b ^ͬ8fQ2df/x5v,@?oAS!; gω ))cG{8pPE_pvӧؘ2y#ΝN o#G!0`vϞCu;i7b76ƢN-*~{u2=gNÆPNgYI 0d=|>Íżz5<> #b===sT*R^cFVmBkB!sn*goWh]JE^107ǢV}HuoƑJ9[[IBdf/Dк>E߶d@?93I*8 #s+`2 )qp ))ڰCK"NIҀx̜p["?g^2QBN7/~߮@D}ˣHul;Vt?wFFb&(9(C2sN4seL Poǯ^Gz\<֭ZN~2}ܪs"/x>$ Q{MT[{I;Xl*w΁C(:Ǵun,jޔ] C+K"n*􍌰BKo3ٰx^df/D(=G:xH̸zΜ>kX|oشi 3" OFFuK7__/XXڡ \d5uI[;[ZӹsW _[>xxzˁ'gFƦ,|U<061i 68;2wgYWնmۉ3 lP|bEp;vd)ʒu!>2g Kl\,Ͼ\ZuqIOOר&== ] ~=M7>R~۳6mqFMqjǡ}`-ucccS>Ozz:cƎËudP}y!޷[F܈A4h0S?%9)=cE~cǎq}ua Eҥ y~3,īJRٳ0䍚5{YYY&L"$x*&&&5֒ҰA8~iiiȑ#ٽcGdN\bc}=Ysš9x`75;Bk~0tΜ:ɩNpHX5\xM"9qD=j$s';;[]w 23{NIII;r;P}k٬jQ:gFƦ:6LII˛1{0]T~MuGGGY;w˗/Fxs>ҒY3g4a?,Ν;Ԯ]9ڽ߇^~l]qd޽{,KvE( ԑbeḛs'ݧ?strЁTZWѳ{Xـ qs +W=:GmJM9s|6oŦ#cSmlQ* nvMV[a[nQJvժU)f!^U_,Z۷RՍڵ6O=jժ+Wʕσ&2{,^dҒ)'\r5$%%g ԧT*quuQoxJ?֠A}6nf ,߈gŊRo81 R(lYn-w$sNN)>O~Μ9CjyV?;B9rsCCCRS}vOf117P(ZP(4\m>X6tp _'sF4./wQݺuYrΞ>irQ[ڷ\rl&Ǐ} N2K IiicbbBTT~ړ}qy222PTdfeˬt3z8D% L1 *ɝ #66Tliddu}qY233z*C|DONR(KCزg͛LoYO]F,YT 8)O?!$%%DpHݻ,--]vajp0JRɧSMZZ&&C`Є5._w'yV%~===F%K3z4FFFWݗRdjpHP})d__-[F@mly]5k.;ӫX2y'|ruرciҴy35^Y>Z*R{!^fOϲs:tXs&Mhּ%*t2~\/ʆڶ˓ 4^yՌ;M[вu\n_RRRjusfmxUAhѼy~5j[oӰqSll*.NNh4!;;;6kAf-ppĠ@mύ $4wOozM W|6>>| N>_qߘ>]Oaܵ>g33t`OHy3+goWh]JE^107ǢFqdRCk|Jy+ p0z<7~5H5iШ SC{n EPP( aMZoܴ vvvL e&m-74q{˷ HNNɉs-,ؼ%gv5LJGӆ >rk ssL/۶)`w+ GMhd/DкqxFB"@)\ .ϥ^Gq񨲳a5wi,6 -f%3fiiɔɓ}n]̚?GԏZ+J\]]ۮ.?ӵ[w=ptrË|~2KvhҬ9;v*vGd޼dee1g\Fajj͛ذa# cڵ?~͛OV>뾩1}{nݺZ%d/DS$SRa3SR4/39+֭H7ğ6qT7jJFr2F 6%<;B9rsCCCRSS۷o6:(R(m_VkÆ3h:q7t<**ߺuew9{$çomwhߎr1㳙?~ɷss3\]\زgLLMx[deeTqu,{{{:2E\l 7oD)`OoV/Yu\"/IB1r}uř߮Jp8&43/mX6j9(:Jh.Rɥd?zT+{b˖ILL$++7o2}gf=u5jd2RSS'hoMM@P`o8;~҄ hڬM"?7P=ѫ7 4(p\:t`ࡸ{z>/iP[UwZiRo.vBbb">>u>Oƍyx1:tй+_)J^fMT2^FF/"ǥId9k:-ԟ_ @9 '`ݲ9T9| QtjN7~Y5o׮%oro +-߲[7:blg/xJ; PQ6H52vdi (Er~F!($ !e${!d/BqB2XtB!xNdf/Bqr47%TTR$ !e${!kL Q8VZ 4>3˔X${!kgp222\pp^ER%]w슑);wյz[!JCd\\100֖.]:!rN~3ǧ?rjթcǍ'==]Ft򮎗wu&h7Ubco2kv6`!9uS'Ý|9j |<6Drĉb?zHΛOvv :d0׾S$Ν{n/kpYt~ cp,tH@DJ[]%w2~| ^03/OEGzo{ǹsiݪUۏ`A $!1*ɓ`_с|ɧT*ˍM|)*>1>oNNZ4hԄ!ܽ{HP(P(a&7nDXh ;;;q6{͛cbbeL8[Ą[ $''܈9Ŋlޒ3z{~A +b,*U)C ÇlgN.ұ9c:7|gΜ:yXӧ/ܱpi{̘}bhhXp |qcéS' ~8_ \(=UkΜ <9YZZ2e$~߳kW.f0"}?J%.mWWO?ڭ;^8:9Errruz{m4i֜;w;ѣF2o3w.#05-쪍,33S˱.}AuPb{1=*U```={ku4®"~#ԧ=;xz;i|m} :ѱC,,,cg38tpWaa8::HXX(߯\syn=/3j󢧧;B9rsCCCRSM}vON!@PhݗBhsz4Z 6A׉ܼͥ5άVn]V|O2=|ihCv+W־8B_%fM?#GjB8Ν;ٳ!$$xr> !m]B<{n4n\`s7ܹs:?Һu+*UT??{b˖ILL$++7o2}gf=u5jd2RSS'h[vFCG}>ͼO'**J}5습KfHk&z5s<-Z'5j\9s _ыҖ?⊷3 IDATڵ(t\Pbv퉌[ deeq LƍՓIHL$!1qxwb$,Ys&N̯~7{}䬁#..s9133YgPrrvEX0/XD1)S&v:j׮Ey+ZaCVQ]',,Tիc%GBCCK$(h111ԮSONr:tX#):͚Qn=ԭG-8pg!`1<paի[ڡ\ !^^wdIn]ڡr^g5{yb;vBL.!e${!d/BqB2NBQIB!ʸbzRq!+əؘ+q,CfB!D'^ڹw>SChԤ)ήUV& xq׳*ΫWucU4 r:{.īJs!>2g Kl\Ͼ\ZuqIOOר&== ] ~=M7>R~۳6mqFMqjǡ}`-ucccS>Ozz:cƎËudc(X6FG'gklЈJ] m |gp94ћۑƆŊfL|~ڳ7d鲴kߑ֭Yx|~۷ aN4jv֭Kf-S>)6tm~5[|7...oЈ Z ^zYx(["]uN:նuk>teWd֬|R077LtUo;>qp(HҴyK4Uw>>w5b0իKQ\ A֭mmh] 3x#K+GxNѿމ۱` 9/װh2Zl35?`E8y5V`8 ذl'Bbj'rWRܹyͲ~1$$AUbؐXn=cӦM+V._B iӹvSd~бCCxξiҦSe4|lbRs' Ç!..Ki;ᢦMYh>Ec/ \| Y|:jF+~H@` g:WaX3DT*6oJ@@ 0E8oPBTrHh(5kjCPdrBnǎ`g쿁gkkK||=..;;;ϜY~3sgQJ"KRx1֮^ܡEsĿX6.-]So*+q .Qr%ߩVF`޽z2q4bbbg>h#Ie?}>%KWv |rxȑ&N gAr_eO# 6,>?!(8uILHu|B!D3l|!Dֶ]&z{{;@f +/;!DBaݝŋ&McggwǟGt@H1k ܰem_; '''$ !:L4?8Dw SDqϑzGEEQ䋡%Kstԙ+^2*4~~-Gyּ۠!~?htGdɒe(J.^aCI}uh^5=lH)"}o5BAe9ÏY/_>5ˏ=zm۠qvvֻ/gggm JugCЯΟ#~o@VYf͚lv׮\b۶[jsܹsv-\0< Xk8>#Ɠd,>B\vcϞDT9sԩR ׬%110'L|~~DGGtQSfoo?UCL%**(NczIJJ+KKٯ!ÆsRRRPATůP(=j$VaXZ~}fHMM͛ 6ܨ ,ٛ22n+T"mѥkwa*O_K9hݺ-oD^VL4E\)Ɣ)Sά+.**2e߃ǎ={h~SJ.KIHH`ݚ՚:}P vD _knݺ4iւ:XoaCЦZ3'xBߠEw1Ϗ+ҥ[wԮ_m[OQ|Ef͚+J/~sssʔ)Mn]èW.z|Dr6lm[6x U=q07/:K+&ڵkOe|/Ç8rk֮L`@c}A;FI IHLd֬ܺyK_ߟo{߯`^sFݝSehӹ1]_tAkuiџ&m:Y~?Af0~@@:w  iysfϢTRL׮]}zmZZٰv:*T]jzĉlO`kgOzj\b%˔:fݫ|fcH֭\.]lw'޽Цuk(¼s8y73cnnn1c .y&Q#3_"T*6oJ@@ h"dIة F ߣB x8Ҡ~}iMh߉壏zоCG~۷#2".]0kGyud&'j摣G9z"CСC 3Uzt_~ZeO_np9DNڄ^$+Vd%O+O '$$PBEO11,Z_~ݻwqqqcL2GVV6<ɩk]6-~fXh 9\tn{/{(_|umlICR'1!Π;ǎP(2|9Kx39fԩ4mBXZPщh t`֬\pؘg <ϟ?ujDo?O$DZ=J.]ٲ_g#..N 3{ GPBSɒdqO6t(@TdQ* *VȒŋ8vǏ83̈I};vO~l~u\JeνX.PJ˯^Jf͵BM$'%%bmm5ZҢe+vJ%gԫW63#W,۷MffSm˖`I߷Z<Ӭr2'N'44P|}ӧwo˟Syyܕ"Dbduk2ˇNhٚ mgI|};իkB|B<[nhx6ǎ˻j!}63"o!885<~şE;RbklߧNfĨф@f%r'y"G;|gD^*$ + 3-Q?EaV +sC`Ϙ59gDx${!D?\FD̎q5؎/O~ 4/qʕ+:z!q{$ !rQ E&9UEr_Ά`ٟT*INQW܌7g6o!qUuw3쌋 3g맟NIkݢ)Z(666 )q̟;GkW4yF bmm}LÑG !qf"w[9Vi'Q&R07SP(Q<c\R*)*́B&>!R O/nccWEEEQd rɒ%ҪS;:xM^ٳ9{6W^%!ً2aCHE&gB2}ff SIHN%>ERzBS)߂q T/](Sbrvv&({Aś φ0_?.?G nߺ[gU"wd/ȱZtνH &f %l޳3TXSފAQpϔ:th4__b4_:v`$%%aem%AAxyhϿf{"wd/ȱ*,Hn7E(`c9=ٟ>MJ0{ ʹN@jQd漝s7...oЈ Z ^&ŋGҭ;uj*6tm~Yzŭe&$xW>MQ{-Re\M&6!*jȧƒ" ',"&XZga/>dgNd~u)Rb%ѷ@BBB5&!q*Zf4?/{L>SpzEGGSJu[Z}\l8xdᝒ^ e LS7)bg&z7!&ƏÆ n^H*Uէ$DVXrcF^.==} Y 9q$Μ_'8v87m6Ys3ҧVӃ  . `ׁ AB&|TI==o&ˣedNi&xR9lܴ8A[\ ;;;vb %.6*uIIɌ'ڵK'-A1'&&Qd#B yABb"Jad7kߩy9rr^nݺM=8.e0. -…<ښ)={ .6J: ų\71kA1'$j:&D.qԭ[[g7Y7 .8ySL0[LPW?fi'EKYd޼9pvvu;v?lݢ]>p3~g|ȅ._τk:j숋3M]LTL fzZ^} k۽.""ҥJjK*IDDA1=y/We谑K;vA cʗ+-(sqqqlO4eKfnBEsĽ U ^T" ,?= `=w>"7.-]So*+qE R׮]ei݅w]'IDAT!eψQcYp.%֭,XP;];wgkV{"ݺtֽC7eeWn)D^UZ_b iyyBmϏ{7s6[*| <ӧ'长:k>~P}!Ddzh,]u"4,5.N㽩V;u`:ж }[8:U"8i*uk ֖S/.}:Ufo^Vz9v;2}=no73cT ED3΋> @ݺuXd˗lWxf(:vÈa53WP(68ТyIFFSҥH8ĵkuvSRTLrnVSk_xxmHS'S43iѢ,,137P{QSӭ'O-Ӷ]&z{{;76j8*V'+}z'Y~ewHM)}B\s9=!r 9B!8IB!D'^!$ !y${!"3j6U~S!BU^>əBIB!8IB!D'^!$ !y${!9!ϐd/"Ml%{K+ -^v,^T!!кu[qu+J~Y_V3id qSLEV\nieڏ^MWFR,]7O}nki5+8,N L'0N٫T*֮]ǢE Xn=**;"OZlc?Cȃ`._{Y\xsQ6lrɉZ?"9}:m1~3Ǝc6O}HN^p֯Jj{b39ZQْߏcAS!8Unie+)]VϞ֗7E݋Q+K-OJJba8٥C ')))K$DNw/i;;; 0oN:̘ᇛnnn̘&EwP ݻȷ?䣏h:okkA͚Y9Ww1m.%E!peQW?N_򋳆vK$[56t(C>5׾V̙34I ̜9o}NhH(ׯ_ڵ+3}_tFСC[oPS .pu+=UUg(J@d]n3x^vFf޸jpPg<}?o ǯu?|{Vh8ƷQϨ8{,ݻwcIRT)ME Y߼Ymovq`ŴlՆyff.]̸uⰳ,(P'O .T*~:y;*rOzfxx1I{vD?4RXV57Nڮ&mgk׮#22 {G,lwp$22kisssZ tiUVti"""LȑGܥ+[6S|y숋{|<=w˴~-[ '^jL˷g^BC w:}z6w]Fjj*wet):(L]+eӦ+>!!#7ltƚ]X )1|>mƘ1y 11ɌHݽdÆK_pڸ &]f 涟}6kl{ʔTTZPR%()1ÏE SrU*W[Q7OB6>S'OѠA}MAҰAjFM7jD .Ա#}f[RBy/g6:g/V ++s?%K:hY{eO&'{S4R%'jVSë͎{eUf9f}K)7xW})b ^!D֪U&MHPx?~qbłhт|_~fT4|lBK"Og !y !93mIB!D;ZZBVV_,T}VWly%-B!D'^!$ !yQSLB!2LB!rISyk{~ԝS'e=B!r~l͚0mDړd/B0|b5kPFu/Yy2IB!DdmmW^F_%F%BɩYy^! Wוd/B!3T?/^β V-GIYY;@ϿRYN[ B/~ kB!D^'^!㌾f'`ccBa1ŋݥBNUjVHLLN&8vȴcEiBΘ) Lwn^TrƄ"BdPRݱ;ԆJ(q|88::LJw\|5OTz @j B!DvRUi毌03SPم<{:u(?ΡQ;TiؓB,֑`PǏLҖJ*ZнZ&5JW<3{TȀIoYa54.7RbUs42CuT*%/#:ƅxl:ZȀ|BH0~DHOX,,2)0Ø,߹rNa|!\@P< {BdtH^LŚuTL˿Sۗ:j2_ @VR*0jU+=Eհ9 3xRL-,-R>w_"=t - BM@F /gflM` z Ukbei5{!2"8,OO"C0j/Z,4'%%Xݶg,t~Wly,K{6 ) φyiX޸Bè3xf_R5Ӝ/f#q>A/3{/DcBąd_|n]=O5p(䒡#Ԩ䚽B+_R}XDqƨdD !ll3ֻgV d퐆BaT͚9a-<]æYx zNh o^+$B0\!${!"d/Bqi^xrV!BYkLo!D.dVE!BNr^!rL-ڵ74tD%#r[B!IzmOSvE!0^N_yFu153u keW !9dr5u(m>h:B|o`Ǟ_ұ};TDӧl۾KW0w ڍ\yrl,;B2[JJ 'Oиa}臅f4#"z&\25jժ2z0Ycz>8ISQ݋QqS߷7{~T*lΡCDڵ6d666;[[[ӿO/zT#Gq=غm${!ጚ~_,cʥDFFuvEܵ;2k>_[6~o^{Ѣ|&ik!l嗤QqΝX`.?n /˷_ҒM[^lmd<~5G۶ǏYz%KkZ\=sfLm]~e*VY \2eJ| !0?keF%Oigr :80=~B6+.jU$#.]VZwUeێ ۨ_5_oш`~89Ɔ޽>3?eЧpr*m~듡x^ߑcǵO%>qc>tVbͷ lf]RR֚e jO!D|Ĉџk{_P񼺿X.˼Ǎeضc'mi>,bRQ5IIIKXjF!D YfTwpp <"7g <"t/X}{Y666$%%ѳπ ;_>sƌKY0gfǝGǂ̟=BuGDj%7`W*/_,|ƣVp+WeC[&(Q8{wU߻/VඅB0x`b´>ʨa M>~L|ͷ4nX_Snk!Z<}K ,,-d՚biӪ2} 1ڴjkR^PД7}= OК`L<5jȆ5|A{e+J ZJ*e/0c=@ev=zģGزm;͚7^!{q}#z|qMy2gIIIk#o\Bؾf1>hӚ4nctԻ7Gb(Zԍn]:i{tF @.(ug :kk:o 5bńGPcF0/60rxoښV-)oݲ-K"|o_Ѹ'N$BA}Z4|ly6B!ҧkB!˙BýC>JB2t0Ӥ̈M!&`нac'O+-2B!90rNVhSSR3&!BHJ{K~?|Tѽkg޼Lbl L!o.:c7>zMfM&%VV!"79@d\荒(&!Bб__4 IENDB`sqlkit-0.9.5/doc/html/_images/filter-output.png0000644000175000017500000004237111421620210021034 0ustar sandrosandroPNG  IHDR"5sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|ulz Bh(FWbP" $P"g{w@ R HgwG%lM/紐13 gfgLxh~!?Ts1k5Xǫ1OpBdR OUO~!V=Z]d|]AWx}FBƥ88å gy_FP4ya*g+!h֭?R/Nwk4?O6<7'G=zm^ !D?ރ_Z\;%ᢡD㰁5/>Ji^6%Wa!hj:=#ۓwzG;r0?َNe餞d^)VTT:m6,ZO N6ݚk:Y[ -(("Ku,fe4.nv5f68h]%.O}a6mO]mZվzl"Quھ%U_tzZ-%uFբѕZj_OT烷vU;m>\!^i_Rvr\_i8Rթ0.\v +pO\*'w7{p{13O]K=#|x>~<=;䫎8 GILܮO%u8t+ͷӖ?Y)כ-kk;\4 _8k?v8*67MuA=EAѠ(4gٶ3uu=lx7j?LRc:'wLDOppy۝,~s%W'vd8Y=G q@]OU 誜{0NKz- Ҥ^UهNӡ73U Fc Z0AJy,28BeGSm߷1_bl?~ ߿'geD2PW3'%Z@6{WU8~l#%n%ĉnp:7^[ u|JsQ6  㓝B޶WѨzq߹v*N[āmҾ=͐fv$`c;㢃XxW~sh»8rUk+" LǷʨRP*M%<⨜b4͓sNz*Jd=HnN )rxzrSgpog;K!q `Yl޶lqEQTsqx(._eTؾf;7ߎq logá Vo8[m gynlg Rk偣줷bJyРTJ:E1&ʾ+t8x.b;H+ڏʹgcZ=]pm7mWU rN@@(o܅K\˄3 TD_]תefMOه[8 XeMv6[=PcS;?*ն cˮ8Όd#64~j*kR:0bh6;vrbx6&̪oH.TF3.EcC / 8T@A/W>_gQԾhϚJwX瓥JNPC;w6_aPTqTS~7Nk?*$CK>m5SW\bwX5O=hLQ l8V s⶞t!X^ޮ:9}%ҝ /Gp{/_Tu*vLj4o^KB;zėlCvʈXLGO|$Ul㍋(&[Cv3~Ϗus'oo^ 7vvT]eEc(?'P絖mx/miRyaiZIB!h4s5 {iKk@S~BPףzno6*MG?բ)(1%XW!Zgn3Wi24HB!7G6퓣B\,o\ʺv66vD3Q\aD6Μ y3B#gąB̈yMUW_Bc2p4S u!h@-GSUZZ>''ɉs{'Y6'%:=EE=bсPny] !!qNOI-qXچGPӓe/fkmʯjI8{o.nDtX=B48MDII)<ik?ղkVn=~}jSc_]q÷<3t:J~ 0:(cq884-Ds#SU\ͻ<&>^y!mQKwTѯO 'pp'E(׃^ (-FAIjq~Ϣy\J{ Ta vtݝIW=RI&"Gvv6>>۷'؜^*[QhKPS!M4£![_z{(/ ,2%%%,Z}/]NIɵO=t0w/,ZjHb`]7=֑it Z?S: W8w3."I$b;.tލ6!mƍ$88z]vb ڶmK۶mYb'WGC^MoVhŁ_XYomD^KQ#qr5giظ.\dF8}?l#YYYZ> ~:zP&E̞9~9O%%%:HvPBF<^o0bR ;y0eӦM <ذ}Q `߿?G9>jb] j^cx?v-]є-x|Q_ `yX3ǐgыE/ӈv\]$'7!XԦآөEANE WP[6-r HÔC1g~'þ<<== ^^^\zO?ۜU>+5/pnCѴ{99ڑc'];luY1CTVN?F}]O>*TUeǯxqqlPP()sr!KѩpwwGk NN899r&"c'#(R wR|̝;h gϞ >ӳgO냢(]U{g]~_:PzF>u;jGQ35>11x-rW䎱wx%;;Ÿ;o7{{yq\QǼ3(-բ׫t:ǵ3)I,DɾtC8;;jqE_OXpګ#R+W2|6o\-h<#,^4Xx1=BQ_jT԰?_ 5P樄5T㫪>7FǨ fFyGGs]2lX|}yn C"WaߨØ<[WԴ{8::Sh@》+O9k8~8'quEщ8fs5͛Grr2=z@QÒO?͍7HϞ=ٳ'7|3SN5^J-{Q-K^6g'h^.ep 3dSlų,qBzW*gV,_ŠK̖}j<5QL3Ӧ{h>ə]8z7_4 Nd]XUf3rhٹ-ϲvFqp@+ҳKa#EQx7y7k^/* ,&=#[1%kY|wwjyl@qu7T {uRO]qppׇ&3{9A!wF(e=*E՗4fHv|}}ZMAoժ^!=7FbbHwDFyJhh(MNr=WGTBid?Fw-} Mз{(MpVtϷkTd H;51'j4mrg;t@hh(^^hL|6'M(ii.z&L) ~̞ۥhu__"ڷãFu!lNގ).NONv 7gӏ?MΑ884-p:2{wqq!<< ??JKj&OZ}1Sad-7lh4쌯EEa-NNNθ쌃C .={C}#LQ4wY!0b2pܶc6M\ECx:Ж7vꜢ=[W; !D}182/6滢6L =,!D3g2p:4fiWmoMl'`*UEoo\jhbYx9n8V!hfo9*ٻg߁LT)x*UZ]V^{P*o0ZGM[KX ^ !DSd!pUm>n:`X ggՒM\YU(&u\ju)**&vtٛn={3":U+S/,aEt>oDF`n|jUgE!#k^#}h|TtV0l̝pp,ZJ~~Q}]Dps6;sVvlBZZoҦ)g ?++;~ݿ ++w]]:+HB4EGz6fXZ*??q~ىw{ߨgcjuwb `ْ1UO}fҗOKjWgռBĘq*:6^|z=7\\\1m*~iTOemʿ`m&}a;}8Y׮`2Q3ʪUF!Dd{j224q}-  %%"HNN&PёB\]]5jzuP:%IB4=GJ,msK`?FògTm luܝw23f鲗?NCzRXXą鼸hQޜ9{LW^%++תNEB4A9(֗*oWZ$)%([v^لD/|ݦV+gyKP` 7F&$97^}7mgh>̵T~j*wN3 |;Zym)2;U`ޒ*oW?}L~x W^5' <5P;+߭ձ[Z-OoDF駟OKCf3)**ǷYIXX{|xj[=\ڶ #799'7QkWg7Vn?B4jc]0{,Χ&D׮]oSxoRSpsscņe\|ӧNr~vmTZ~ٱl##&L`Fgefq ;۷mձ[ϖSY%r3ٶu3{^iB)K㸐vc9zORS[fs 23Xlc毟|bT_???_þu߾}ۻ]Jn6P6UMUe&^MC K\t0ZR\Xo `sKjPPMuo^a;%%I 6lmV=yaxҭ/Ѩ GX|-l鯹UV ;:j744T=vOci۶{c?vO]v6GjAyAU5""B={TդxΨ߅,R|Y w9WۻWՖYSս{~SK Ig=V դS 'bggԸF8zӐAjq  .nmSٴ4z]0a4N F孥[ϰV5C=vk_slڴ'[֯_oW?-dddcǎdddX,S;t@zzaұGEEѳG_ "<Ąno5mڴ!1aj0ULrra;))ɨtk[Bbba:M 7_Zgָ6BBAAATΟDppaڱ?;{k֮`ڵ̝3^Zۿ!jd(**WWW>cBM{gr9Z-L~x!&;YYdeetk[,2237/Vn)pqJKKQUڈ6#__N:eI2o^,ddf2wn 7b=ǞEL||~Cc5r$W\>Ó}k_k#DSѨb^yXG./0d`ÐC=v||cL0OOO:wohnzlNV5qqKKdWE[n6JحW7I=_ .ϯ]UU9s4xѕJ˗/#8${ҽ{OBۆlYzBtt?"FҥK:b3?ڨoGHפjR_m&֛o0z(z*A!5Kz4ڈc?%=#NGjj* 7\osߤ{7/L223;7dHx#?~RTUE6"ԩSf۟6)Oɹsj3)6뢾WW\]]ILLd Ka]Eŗ_һw<|[/?C˗L=޽'mCY,ΐ>~8&~,\?̐6g jKpcc2t܎?Sy&ږjۇu}DL|9j C qGBQZq!͓!v!.8BEBHBa B!"C!]$p!!v!.8BEBHhR^!jCB4ZO-~hd!.M2p$$$p/M #3l~g7VnS<1I|wV}JVŭbK?7?tO=ԧz[?;;+ k_OM{bCzQQLA`PA!L1"}L(<<x >|ĨU]ӱxK ןOի6Wt41{={SIMIk׮ηXf߾}ۻꈋ[W8}$g׮vа|_cX='%/ɶٻom/ ~+ԩdglrCҥq\Hc='))-3*ˎe0ag0סoɁٷw)Ip:;!Dhg;XRPP@d(Χ+)6ѡcgmLǎ8{,{2's}زu+/-7es֩s$11s0~<ڵvU[yōܹ3gΜaH8wزy#]tӌ} g /^HpA!_5v(6]III2G!Z3mÆ?g7|ʶXjаVGzz:۷7lGDD݇Ǐ񟯾̥{l_~o6m€խׯ:أCF파 CPر#F+;Zb{iiia +<<4ij&3ctR\T@Vf:i$''*ɤ3 @=!|*Vd4GGG 96:e?88ذl@PP]WզMMUL- !&8 quuՕDϘiu$b/ ++lbbU{eX \mKiS㔖*ZݵO{_wWQPP@ZZ3gͲT,23σoHoҽ̛KFf&̝Mn__N:eoڴ>}&ΝC䇧g!Dkau}DL|9j7`kOOO:wohnzl.w>>+,{lǍc}…kWU}|p-vvFC!:]gks˗/#8${ҽ{OBۆlYuϙ3A 1ϔGc vYxSsIƍȩ+ žG=9Pbbbͽt 5vkՁ#C={G^M!0EY?n'F'<ULÔMVedefq ;۷mqq˸| OڵۮצĻaYOM&5%];Z>s o>CqQA h㖞y%2 k׮gd0@RR=y$cpttDҽG/nDxxQ;ڴ-;eF:w ӧ٫^kuؙm[7ӱcGΞ=KLў5)6FR9BCCy!xx|`ZZcOQy٣|-&_Сa"Z_zz:۷7lGDDϪ~m -С[)BتINUiӆĄ镒B)ΞŚkXv-s̩BBBHLL4lW^` IIG6}&ΝC䇧?jH\|'}1ׇSNժdffɼyvw}l]`KRMQQ!.35uBQS8*̏a!s;>Ly1&N`b3?ژ3g5z޶KȮͷlnxzzҹK$}Fs dLYG./0d`[!jN2'Od:y"hxjbbbͽt 5vf8ADzEa% !D!sQYxv֬BԈ8BEBHBa B!"C!]$p!!v!.8BEBHBa8| !Dk#++N#M2տ_!F_~BaF 'N`M<7Yy]utꎧ}knУuxxz37*Sy}ݺe{yjB48,xKpuu)[ٺef-'LdQlGþ3fs/&c;~نZ3ؽW&SSBfgu^g[gsf?;%Kv}˗lsW_!D7U=^Qm|]-O+^f:-p̟k[(6&iZ+-yBh86|y:uz'MG_T||p-vFsar%k2hT}M۷ *gN駧r3)MKh)ٝNQ=:.ŮzLBQ^^Q#G1V,zѮz$p!D+_[Et>ӛ+z$p!D+_}7/`W֨Bۧg/q!D Eg^g5!hRSʟ~<8Gݗ$=muHINdG,sBB auF23.әrpp ( vk1p_T !htDѩKT)SUB!"C!]θE=:tz&!\w]Oͭz 8VCge,˹ٜ=FFcCv W[mGЩKz*-L3DvYm>c 6ա׫\C8:9Pݬb8w]FTU=z InIDATg5x\kmh4 ADǹSLj:yVRTU5Ӷo% !DyOI]X zjvzJUUOc^fM8K ԙDGޕZ QT&+I8O(JeFA dM8ud?W1EU ˄E~р^պIsđtmi){FVuQZR{zӘ́Uh3d]zvOUe%SR\HT8:9S,;ps!hX"SU1%$ѯ_?r.j]dTUKٙ;vkTԑ?i1jbM#WUuZ QTJkC?tFJL9w0S37UUKτE:Y^`*4# GUF PÓ㊢йg_\\=1=U%8DHM~d%j]563uv),˻]9iheMTzٺIsѱ[/ M^d齺Y ~]αqSfFrUh ɺ,p7}o=7r~u-,N=8NdW43EUrC!*^rN&&g:e_[pssア^hB4}ͮ 78 ?‚|\=ix+?td:&>Z5o1!hL[nBL 72! z)>TF!ˤB$p!!v!.8BEBHBQ͗_o`i8BT3m4i5zBREi2BOOTB*OOTB*OOTBMOTBMOTBMOTB[dJ!-SVJ oB! [,NUܽGq !hܽG1LUiKKOB!"m ;s'PQ:&N?ϕK<7w>PF Yx}"qqqm~ !h"4JpӐAjI!Dsjq+-ĻIENDB`sqlkit-0.9.5/doc/html/_images/mask.png0000644000175000017500000005164311421620210017146 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|{=W${"T( XP(vZWDٱ4AE#ҥ sew\rKr$s޼<쎄 Iu.3-.U ӕ߹jY1"i;@ %l |99cE)C.E)ɭ\tF$ OgNoqt`\9+ X@p]IW:;,CypT%@EAQdlBQ7ЩuS}>e:;3'3j((tkX gy԰@ оy# hWYAټҎl m3:U䤠6x  1UUmVkYQUq{JO[hAQd%.3: Y,\0m|4} uBQl6fCQRQ7U-[οDz},eYe>m|XvdFn~A)̫AkCNܪd!9\قɠ=|_ǽЫA={(loX<+rZZvTmGYW[޳w2vvNuy>nݚ#Nቱ~5ڱ WUnpO Ѡh$ FckH#͋݃"bN8组w_3'I1jyZ 'H]ũ%v:0lٜٳ̭?5HZ>0_(*88Z@eB;"p؊rٵm?_WTtdU)׍ƎlU^Uykcgӷto!rr"'I9o*lC"w^''gƒnlϿM܈YټshxmL&7&"{?+3;9p㿐wgA[N@Ұl"ޚwmڟ[cqQ}Ƴor^x)́Sy,G#ul Ut K0VN̺˲]oyݵsuhfЖg 8[=ܲ 2P*r,T]EIqF:+|Hg)*؄ȝO~ЩQ#쑧^"R_Hy+Qq|:郣Il;OF&eNścLODʠaxiWLNp fȸxsfҶ,&kx/KH)sEcKNV}uvDNY[S-qUܴ0SO\ ?p D[ev[93K(.r} rb&`M&K9QH)J.F6#)<o&!'TA)<::' iqPb)4.0{\? Sh9>+Wwv*Y1<Xʉ\XU-ՔoɕCrj 7x ZagT ւ, bZi @fzfBJʙm?0 }IPw_!:,ycnhr+̳ 9W%>Tʽe;*ax*>{Vm9GUǹogَt\vRˎԥV*r .IG֜+B|e m6(Nș zZlX'mcLVe¤fu;$1f~AJ8ꀽр˱M B3ux*گW[v>WqulMnZV7a TY*ڱڅTWj9m.o.PS:+je)i.SY\gWa}T1dj|G$1$$I%mO$ 'o"ćVϏl>GA70 [O߿&%z{%$gڼR ; 'Lޔb6mxS(~hJ/mӔԔjZ])ձ4nIBަOytޛ:cbS3J4 7(H\`}<)42c Ժc:3~X"^# @\NY rvULg@+F/;-bvFt>Z9"-;-2res_>MthɝGFu\zs8$\˶QbUQUk%u4QsŀDbKLJh4.lgh?e)4+(*h$4ihA(޹:#;W7>UC8_~t1*ځ -9*e1U_1by';aM#40V[~PB]\ى 6,Zlt 㥖H[ru-Mb@Poh$=GOҶ5-#'{o)+unD1Q3|@͉jCNY/jB"{vjSw‚ bX .$:nn.p)rjWiSQ9o#e_V:jUq WT!WrUPn{..}c]1ќ˭ Oϳ/僆в]'=OaM4u{?W{ѼOIlۑ}Z';1?E7]N4nUGkO|}}Cf3:Tڥ3?e7.cw?06/[XFλxaωGYoEE?[wFm\zONݘ#zVɇs yL0̓S^tދOb1;?,uN~x|euB흶tV^{_N7sG\rr2wMǮt/YYYu:L}N/ƏѮ߭vCSi0VChhÇ -֮h4oe&?_9m߉¦̙<4ɇMZZ:-[ʥ9u:|[͟+ˏ ظo9g'`,ʧ{9t~U-'5-7cml^kIBVY~M6d0O̞9/g:4O=Su=n _~-z3 |#7n]W5޲-r-tpp(Zb3M?pV~h4=>&*d{KBxx(3%K_R|U|/\Qxt#n/?ѫG7F~<4mXpN-FP7 &N]wUo:<1iCACxW˭ 0}Av"#]lXppcd2 l9Xe~w| _=9ԽR#MNM~8t{3ɹYҕ/_ATddik^o`3yf<@Xh()h()1,)>9dq{~Dx'SHo\)᱇ӷoo(,,K>9Lǟ|FnݘLk\wz‡H$cеWT `28sLtS>̝s2ݵ.-fՊ4hOI9 ΃߇`pScSӹxƃstzl6:ĴsӖ{~Lp'{vla҄;]6^z,xWjG+MVV/J\\=^|Դ4xWq%f3FAOfyyۀ}IMKc%dgзws[wn.LdFjj*&۷s hRR5}k؂y?4zy7>s~.ےei߮- 87lK֭YJvj:62uBRcxIΝctڋ?ΕWL܌>~¸r(>=0? 9FwZe}*-w$!QobАFG;^xf6/:qܱǼMBb҄q<1iwS\v={&lJv{rӘ;(..fk/;L7nCb~Yj5]zѳG!s~.|ϘotwM77WSӱqgMl.^S]"ש"hu|OK9Z^xi@ jW#Zr-9@ոǏ\h?4kݱRKoywF w|̽w\]u7+VСC%,uÇ@9cxabغV9//W=]Uq!/ggR7?B'j_'OTJQ_;{žt"ZG;&=IrJWѤ$N:v<&#]/קeY2jYVLKebKF\3e,d_6/'}5 lڸp7r73[)(( ==ISsֹuEUU uzdmtOt6:sÔY֭\TU%3#,hFs̼2l~8t#tn_jݒPQQDN~CQ&,RܩS4dSW/ Ȝ-DS8d9G<2]m6 """j zYz5tރ~_ŕ\ ze~e"#ɦǓظq=[ӧ9r'N뮣UV!2m۴%&&O>_y/EQmͿ||7 'гi &4Z 1aN0[̽ d!z͆t\ADv"-}\]vwUU*;UdEFmN"SP(.^T׻e]鼼.z+ma2hڤCV+RN6'GкN6# %%zP!(8EQє( F6da<=h7b2~h;ڬ/j!1Zh_{N4Ih$IXVd!2Eh4Z22wEC@sEUUҝD4nM@/**Yє<Ҳ  S ٳn35#,,u_ :SzmL|fD40II[t C PhT+m!i1$BumUO?P+CwW˺ȲNa.1Ӭy3 9 ֭]6?KN"2VlY`4`( I`JD؞&gP[&v-] Q[k|P$IBC"/@Ցʆ2zp"2N-6 ‘U diٵhS6YDV1.pZ$:*ʺ w8UUAUQQ7T3ͥZ|c(T \$KR)IZ #թ%WFttlUQb.qyrzٌf#۞.u i֜)UOp>WYO g8<▂#"fA=&?e+\sԇfpt҅Tl6İo>233 f-lڹ|t =~X,o;FtH(2ddqtvhg^X,>\&s3%\0STT'Q+*% ەQs[ h4 F-L4Ԓ,P-sFA{["?y%zVƃCB]{%כHIIq, '* !#ئ-d2dEFQUrр&CA&_*p  Cjld B @SЏy|DPh8 ]?Ղl!6lE׈<()1s( ~XeRhV%L vĝ'6WUZEVPTUQJǟU@EW$[ FB-Ɂɨ%HZ@]J?n%$ւ|]-B瑖\߾=je|\{WII ?#ƍc|h#nσS} :sFed cXWl5*ZhEii Gv(򡵨ǑJ.z7^?]u%55 Baa!ǎQ8ڴIJeILL$)).:tGXJt}/|:6%6&dE潙p^7A6lKLT@mriKT+gE Ԣl3]R[hEؕui%-c6ą|v&)gfQd|ĸ[yCDkN\\7Olԕ "+;&fi >&9փѶrj=Z:c"jA{ IDATҼEs  ĉd0 Ȳ(Hail2bsȪ|}}h4`jlD78ʐ; KIjR|r; XgEUhEqJ77@%y ja lTnZ3ͥGZr`s!<=!e"BaQǏؑ%v}4nɄx~ FEwia:3A?"QďEa؎mB>)8 QrѤ젮#=ɲLll,&8(VZqqL&Z}"uxD9k/jE!IR.dpr.VןMxAhYV~VbէODEN`4 ̙Y.}GVv~[rڅ&$tN.p >1TcK8T'ZUQ\pefPTX}5_ŘLsz4]l-<,+~XVtyA)(mH: I烶H4iZ gRj틢(JqqSl`0G`` !A-==JxXXjkN%.DHv1!b7LGեc(lٙ:|cLhtz4X,21t*9$I ׃}x̙-On婈ݗ6ThvjJ&#AC Ͱ)ohѼٮ*Ա%W^jӒsx%gXP JJJ0La4TYv;] XgQsSM!41Ptd)F#Zy wZ nX{_db22Dl P:[eO9%Yh5ZBq=z훅g{>Ah4Z2f]pGԻl8] a] 4 iDQv1m=6gAK]AW<4id4׬IO2-ŏb0gN-"W\Uĺ u8`hPU"cZX,X, V'{Peܳ}j s3)+ʱ$mA:~w"Qs hAiXEl&'Cdڻ&S7[rC.}F955 ?__$r&}Yk9aMh:|udYl#iZm?@`ԫ e1<(tygr@Ri*b4V-FCDmhnnVet,ΎmW:ʋTZrUtڒk?ABBPղ;hIي"Һ]Z۪ ??dE.UTy8>Z-?DM{WGv(RPj}LmVyr&S\bpv^U/I(,<NNl.jչo0h͠n|Vh hZ>zLz4Z=NghusSihzJB{I֏'0DF~{NydcpB*Rb”u t"6 I*gߥVgXao9u4*Oui7]6[>//2Ώb5jn*(9Ha(ZQ23abTk h@m5W-QUNh}|< j#::s NyN$_~#I4j?&n]2VKpp'#k=O1 5$$Ioboeگ+AuBwm"Rl[ jݒ+n!teBhbd4rh/nɕu l@*Zn*Ԧ}Q?fH~-97J&jyv`0jG1[غc7[w䟭ϣ)dd"22䲞! t4 4nԈi'd2h,Fa&LJhG4Yxbbb?o):|BPPZD+n/IX֬eaaѝ/K})BڿZ4);D4񷐔zfQRbviZ4}H٫dYF$F:^֓(pL.dg_i.}h4Ghh(a2.W .ʖ$ . TUEeFb:ހdtJWU=Ǟx$BP{HNC[e$B՘@ \uWȭ@ j F@ j\]=wDžC D֮_bRJ,*!r"'/jb ༠^_m신$?ʖ kH V%<2nФYy)'z/՜DKEQHMc4hح|B伔kVqYD:t߷$ $6&`VYōcƹG{) 9@pBFi RdY]TWjiY\"W$4:5BB1]{) DC/ yivc|47}?lH.𢌕)6F vlM ϑ[s:PFz#'e)6k_շk獢edzhn}$e歜Zl]ŗGe^~uM˶2! ]ک+kw(VCxX]}9+HSbpߴ7nr Ma-lK߉eV\Y˩eYasҨ ōd/(vJgKhvbs#"bϞ,Y[7h4+L[Wzuoy^K.y3Nfصy>)< ¬Gǒ~lcWY7W~ٸ}|fZĨz3ys_ju-[q_?f6=iѬ!:!rw ~YO<<+wJS@+~⢸o4hvn'̙G=/DFڋO:}| fK\Hޛ;m+x? b XdS7_BlL8~&NSY3TCxXP}뺏߷#>&#~̙9˵.GFl>_c`/W\ޅw^{ʋ;:TӖ̜ ;wĭ:z&""|GTT+Ixmv#<2"c'Ri ǐ"ƒ]:%×Ϡ*+=NҞoݲo^+k)ums +Z%p]#9TnǞ'l8x)SrJC"=#YVH9gѽr~}22ϐyO w18*SK+(ȟSם#<}.Gfٳ/wDyuČɨh2pD*O C\tF]{}BTS=&ҽkƌG2mW^9Xj:^60ArLGGTD0mAnwSO12nsG30GqS'_OA;c\֣-G?JXq5'ʫ}Gg}Hx azhSk!fFdַ/+r oWW2YզIK9DKN x7BW#DN x5BV(@qdYAպ^CFz~}!xSr9a)B伔`ƃ;,GKgw^J\>h$k׮'#}XwUpɣjn/GB@]hݾK}"xՂj9!B伖Bl\tzҞg_^'QlXKfF .yZ-QtՇ&'DK9|XIÉkF-wbDQdN%gͪ&y)׬ܭG}"x Ŋ q8)$^JFibԷi8n"Ȳ,D՝9@\"w1-,.M.j/*B ،XƳs,J< 55ɓ'` 44aÆl2p"$6mڠSncʩ-!zuC'OҫW/"##?),,СCLDaa#5k<|$-ZO|Gj^KХ;ϛKhފy?W4iV>>[>j׉G{.yJyʰX,Ңu{u-{wgfĉ>O>Z_$I7 ..~ 0qD bҤI8qI`0ЩS'vqNqшs"+;gٰqS{|Ğ={Y'n^hW^uO(|;dǎ]oˡÇYϬRSٹsKу{]XtkWNFf&s|8-M:.ꫯ%5-?U-ԩӼƛNi6m[73d<1sGX|9wqG^xnʶmHKKIxFԩSIOO'--DOjZc#== 6zj=zS;9s搟Ϛ5kشiSl޼m۶9^,:sLN:šC8x 'N`, OY]֯_ONN^{-&M:x{YOUUU5'tn-lro{oӦ v͛5sjR}W}wT*eDu] VY)~m۲Qm2eZWa-ZPmTNiwNNR|<<|z)<:NZNac+qjTTZjDDS5Q?TG2D裏j,8VS1ɓNaP4hV\?++)^ӝS|y_ex ߾Q{YOieffװcQ\Ct v՗̬,GŚu>.b0YDFVz}y6$33dee(.Ω^YYNiM&6υpҜT'OҲeKG7/::ک;a틿?$Gfff|;زe | vbرddʕtؑMx:ѤI~&M*+w =oiM剉q-\4"FrgӺ]äCڷc yj&=9)Wv򾤜 FRu֍_~ ~ZuHJJr=z?O_s"A\4"wGZmc]{ s}4yyk̯l}%5IUy0yE2bγ3jssweΜ9$%%a(((`ŊN&Mđ#Gl޽o_\\dd2TIT9p~=cXJ7ܵw-w^V+:妛nr:\4FDݸhDiH~W0쪫٣S=&ҽkƌG2mW^9?ʁ3t̋믽{4mҔa#*uUjk >>$$$S8&5Q^_=4"?p!:*ӧ}OѸqc6nȉ'ӧ>>>;rJG{>}0p@|}}[kc1`zdgƌtcgMjF>(_~9Efh֬_C@ j F@ j +e+~*nF駟>ܵ)~ +<{___9pGenǗw.vލ8]wG]h٦wkBV|w?sըkٻoǟMNhשO̜LVYt)o2w\>SW eclZG&Mxl7?~ ;nf+ybl_}}.iim9N7޼ upݎ:D^|E1]D D&~[zh4#3n4<=H|||txvK=qˆg'YxS+؇ ƌ3!''3fPPPvdeeQ\\̂ ߘ1cF}TEp!Dm 7u$4oEvqJn27)>++FqqFq t5. }rrrhРZ">>pjoߞ>@p1"D{`*coq^vo0SR'S XOJÆ +܎FAQ "F\ fLF#Gy0yE2bγ3jp4|4Yp!sanr-l69¸q/h}Bj^Mrtɣ?4"?p!:*ӧzF}R~ZMs.2zAxx8 .$11ѣG3f|||ILLw9\H}/멮Y3ʳL6W5#ȬvCpIK9DKN x7BW#DN x5BVeZx !r^JDt iNַ)Ǐ-n"wk~[̑e[Yq>}U=X}պqwŕWcIDATg2SkZ""X?9/E7к}ZR߮\tجlVK ^9/-Gt:zݸ"zeˆdf绫ނV%<2nФYS{Y*Sm{kuZbo-ZH!r^J Nl\c(2frFc|$~[k Ω.޼ZNKJld/Fcb4g>kQZ_Q\R\zB]w{c[degv,]36nr0^9mo`˦u4m҄^x 8r|ҡ}N]>={dOlݼȋZ;wbɢ8zp9QS0}ƣL{v;vթ.+TUu4Z !ޝGEq{VWC +\yQ'FQFdI!I(7DFE\fqL&9dq/jhAQAm\hl 6]>Vsko =[>bAVv}/Ol,8&ܾs55wqM NHM E70  (*.D,b[O|Qu&**}M{CC|cY v??\fW'㽔4^шK/c~br{ü((s8x aaBH$0<R2 >|H%RH%2|}`y,!K0Vn&x$.GtldR)fΘ#Yk7.JĬobfsI@]]unY`Sޘ 0˙>ko C"a6t*B:61 XBx<",,B hǯX$ |0 Ӫ?oDd;ەe 0 LU%iHy?æ}޽=z4Nm0H$zHe~AJ R;y;Φ5/CIPTm]Q^E✅00!\reyPȕP*p'⪶u x e,řa@Ǜi?oEIhj-_v ^_ACC! 8}5|)P̫( >sz(G, ;_rH3M/ɼ0 .[ۡF|fGHg(tM PTRkP\bvV\ZmIԝ(qTtlŕy0dĕyؽ7wlGA% .ٌ_Xwe{7te*G, R@hX8?CVVuWc@H~&JrAC1pPOBhhho@EMMK7$QZw<yx<O{4{$Q>>>(*sPr˲+U6rDi|fe%yT0(qԍB9#B~ <c"g6QVvYB Z px/#.+k2qJ>vaGIeg"*?v?!C!#ٙxit@o0y D;of,"@)z.ҟwҕBy:  AaX-(8 e`ore2D% l}MFS/2ܰ6˲NQ#EwY+(x:#劆ܜI9oL3!β3\[ҿ;cX1վ:ADB dVOr9Mr\/W͘3Cڴ7/['idS;3hbFa fGD)>M-6hh0al!}>EҢ,44:%쌯p -2y/@Wyn2<"zOkpO|Ċ}5FoM{koZii)Ǝ@PZZnI.c)TVëz9#̙rde&fESIqYꐕ XurLAIIӳ7K[6ez)̩(.;s)1w$^܆¼miX7lgs/DmH(@ꪭ6}dE?`'ƜEmuHMbisSUؿgOyӧM=V>mߑkT.#׬ y/?>(T#o߃u\풵Q+vwCW9Q93"~~L}szgv.z!z wԦ?m@Is^DV>m2>{&$yFl޴}** yVЎWmQZZ#e"DuvlNCXm<:]}1tuU[sr'O_@ʭ=_ں4_CFisN˕- `lfޥB$-Z;]x}c^}9}±,m2VaC:>)"^Fp@NRp$h4mTC[x;ԟB!G[xry_$k3ұnl{ 2wkPyڮVđBFީuʴ; ]޵rU7G/W&EJ TVCWGrqs@,Xkjf\ȻKw4bY}_`NI/Arq*U޲cos}A_/0wmgN{smUm F~λ78; ^;}m}}KI=xb?:?i/02R'E`_0sf>~}0jv ys=(1v$H(b<9 <9 h.'ciC&W/_2Sgsy5wm]wm7s*c'pMC%yj>5jUOOB۰ W$# ]kːvT*Uǔk8ga#xr!-x5%9B[0}8eYfOA&%|F%k;j)*3wTRr Y Bq[qjuo(%9ÉVdot=L&3  cMQ F,fs7c{vDF@"n\帉9]w*q eP;vu9B g⑝yJ\jth 9K#JrAC1pPOBFC#F~wpëj9! Z>Jr?! HJH@srN$G4JrNksNs;B!fC!vvU}m&$oDsrN$G4JrN$G4JrN$G4Jrml$Gzfj^DzM!^yJ*!sTB8yJ*!+Q\%pB{%*NhDr)Q\%x-GJTjc};b#.sD\9~y6~Bx)rd`dLz<8ͼ0 W/G#*UXh ef34?X [/'$Nx)X"5b"9~UN-.cvSIENDB`sqlkit-0.9.5/doc/html/_images/table.png0000644000175000017500000025320411673441611017316 0ustar sandrosandroPNG  IHDRtP6rsRGBbKGD pHYs  tIME%8X IDATxwt\չ}̨Km5WܩƐ`j /&&\FM  bm6WE5qF \%ϙ})Ovf>iڍ=TϙkK"kj[^oEu;߄CU߽ui@|>uܩ7|t_v#ͬĜM<"se! =q]j(yä7v%NGUeR޿dxr_5 <-r5BYwkt$ aFZ)fk$R{ŜaZl؛یL&_W{=":+ 0TP[^W+UE/MWVS0oFIøkt"g0Y-Y-]W  i?w=; ;7PH:T"A7ϛD2*D@.գg7=\#p[,ě\zS+]FS 5r<\ym(\}{,cKG/ȁ1OO8)G!I)9g/9kYȊϗ'Kjx$?)Yլ=tpvqٸ: "f%h*(M!AE1x4ECt 8G-*ז44yM'Ru'-8{W!84/%cӟ$H#gұj9l n]`O! v) :Z##Ԡx0۹E!WJ,u-E%k vh A]lc6 @iPX8:M-ƴ,kmN\Ɩ*H)O V1PuTr.zH0|D 0ĻjxV=~y#yĨ`(-K\ʒK85lMb@{",'L)ԌkBW\^$ukdxlMJƟ-D8V]{e8ÞR2jEPuHkip/s&KK *Ū8_u-7}fm{C12occ+vZ.)Gxۤ{tL4צ8?>D>Ni ԕ%~?c5|ClUcۘj80, tl0  Ύߙ#RV~ʥPݝ{xHDRZ&a8m i`&Ji;3Y#Y1"e0hL p]t,4.+KѴMzm!i!\t\±7/ dz ];ћk4-tUQ&eh( vnsEV bnp-D-m-ER"BY'/5b{طF*!qU_૿[B|J*!2II Y;U|mZ-=6Ah%zY5k汆}8 TW&Yߑ`fR-f- zLf˝1w"N9ԉD(ctQak#@ D1ݨj {q8`8Xl/z=^1؁D-z f?n`=X~&j8@Y5(: y@?=|?z)%v+Ӎ jؿ; X%T=`N~3jTrll4 B'D #Ѷ-\ƶ<+Po3OCbƕ "eĢ(XFGd(kP>T.xϿ`H[︊?~%uf|} c \kWő4um1Nԃ|ⅼS EUQ4ky٧{&UssC)%sI9 -U!v/jv/Us. R{~D~ Աx7C,2buPB1غwVSv`w'>i"46ZL]*İ\ֶ rڴ twAPvßdyE̜Ba9l&OA߽eLLN3L֒ljFۻw!sJxFlޱr ;i[r3!P:&"zj< o* =m0õL>bs$gOa"<_TE. r̻ٶxjˎ'G~g|mO[I$SԜ|9F<Nyc]?#Zzhä "uG)]P4ƿ[jOF:* ݂^7>oi6U' ?*c}w||/JH2MD! bіapoյ-\2 OxaXAٻC(;B2 :% ]4p]Ƕ]( EFcw帷_c-8u ӻ hP,p\MBtFc[X"-L2"}i @UT( (P229 XN>>Gtp9";F|$~GYO1xi<{ǜGe@1RǵW󡷶03h92QqW&Hl"{AY%5BRZT,sإcHIswhO[f2Ѡs5x?9.h<]l{A4e9_ 3//4#f]ĢG7"sXy+oyB 펬ƺfvu;#߻ozxim{h}}IL9Vo^ɩ{[XYz*}l̟0c]wO0aYlOfhW|s=vϨ_X4$(jI~.v!;­>IqbZƥ>V9}fb`s) 3'êr]1ӓuUSp,ǶL-Y.=a4.:Jey494[ F1O"do0Emh q( Wc1"qmǶpXXȣ6!!=?p'`݊'YyhIdY/6F:i"\U#'e:K xm O)Im"mb>>G5&9`G si(VM_㛋kn#A(²Elcf"E QGT%cYųGt`69f0gR,qYAuLphR@Q'j#XES8!ȴ^p \D8<5'2o$ܽh 4 D '(8Re>H4clپeqU \њeEEm::z.}{YKWQMT`ٶ 'c3'g˦ M嵓ǒ95ԍ_ fTr`XH@e[g8,|I !۶s~,ƅ3HE/x+]o7I:Mm8﫬9+TSOKC f:QQ2C 7WqNWB6_X(|QG;ҕ0hUFL,2 X,ELf+4l ih([;u"0euE,7<9X,ZX W4I犨p&0 BTTw&rd 빾y±,B+{4#&8 +@"//Ft]\ug>>G3.( m:ڶ-~& F|湧q r>s!&&!]h}),` uBEe5Y߾^:V1A:`zb>}H"d)W |bf w6L/ٹ}5-s ?i1cαXWRbҴ ==ZXS"r,[U?k CiuR  B,X8 NC*@$݀/mcP[?d,D /0u\;Rj'csر~5s&գWJf.d{R@:-eQxKp$s 'R.PM[ٹn9'S n^ڰ-/]T (sUە8븨B_:O'V=@ _شeȓ;dz׎zZ, X'B;va6l۹0p%Xm,tnԮb.^a9 =4N瞢/4XVEU@4ij$u%@/ E|JBz!#N)Q7*p9HE <{V?Nb!Ifh5W?=z>O_F[+toZJ(;.@+o#?E,eM'S~DAչS_mWa-E{vR1,>oHf"Mg0o3c$oX}  :wN|,[nEX=h$B B ]ڪ'0X"Jj\IC,G?o̚<)%HS8ƲzYc`34r%dn&LNTFUU ้(T gYW\M"qp;r?P0DgRUV/TeP]R2男aӄQp~&L"TJ-}a)(%%ƴ Di9ɊXu/8C_ֶ ~qU^E WP2*PQB4/BQP z +zv%KݰUlLȢNQQpٰm3p2:2o[ڼI yaĘɄJ ۦ ߹}$S>v>Φ5+iHhT Yl5O:ʒ)?d2\?Hy ]+Ηwb+Q_g;K[9as%Jz_yqS}4&_'nP$ ceA~&v<&D(kl _4C1=/= +q"K`{dn-gKm_ω-)B\06wv[/?,j ,[f-c zY6o|L&M:@?G7w`R@UC,z1sϳcmZKsN2 YjfIV˖ukYS}|yF6Eb%+A&(c?qz6r΍y6_G]!G##F, đ 5a>z:-&UFZ7oJ -Q6q66}M]E4ǘR"@m9/:M\} LS DJ۠лg"RLG_=,ߞ%oTF &!„& eޕMրx4ԉqf[GV-cvS.fXݞ'c+b((UQ,X$ap}+dA&1> K{P@Pf1ʶ,"D#!NTBc<˖.X8Qa׳w$a@ZQ`Kb0#mݏ_Gs*t=cYibC*X(jiQN$*^X}WhRc/>>"Q#2"TYo^{`xu ,e<UɒJ!ԮcTb8| \H)i 1pm@ \[Q(n*1BGܣj`~)B0p_bKJ(x)mT!KVʒnXtKֵb +e/ o)flwX@R[e3k,sxt0"FE"JђR]p P0% ΞS6mvـ8wo)[zd 2[gsNZyS 0 RЖ ^-3:"4̉4եOlE - hx*B PŰPkdxH\)QUSx\U%& =ĸ"T.e!U+} bm`Q.6je\yi I4 A83Ji \ BH8eZ-.;,T%OOb.Fo/Es[hU@X1hAv>|B@uM\ mMT2Ux c}h]  ˖%C_.MZzasGAWD#oy #xpMz3#% !]!,{XaoBTBQI1AXWF0,4f_NJew>~sL$@'V9kTt\o4VJG@1Zʣpv,׋ME,QU|v|9//ͩ`JJyTrA/QQ8R⸐J4mxT+]ԆP * RkFΓdq}8L'nxsl晇;Z>@Worc,@MؾXY5x/`t v3&k$ðd6z nQv%l|q+uIE(u MXZ9D'o&t4U,ubt<ECTMۯZ* ܨUVy]\)Ur(1MZ1Y%NE"-Wolgu~B tStV*DN8 7?^ bgeu jY1ǪCXREEªԦߞ:9=kgLQ06fGs;긆᨞T13 q|!X. JiYϡLQU&ϱ&U*1$ҵb$ʵ_V`rU%V ݷ_sԺT`fS MӼT-=(F~1=%>>[Kue)_kl`WzGUFJdY--aʡ]˒R Հ~̓ 螵K +T/^JIt]!uOlJT>zn_Ehw̠pP;Pfjz7onro׮kו iLk{Vrȕ|_|||||||||ޜD!@(}yRhU[ D! 5&usZ ql)=A:7S.cr-5/;oEJHбK;@Ͻ&\q"²lě4, B-#Px<4M_Y!sG褔A/_2y=oW4۶ߔ'bJnhqJq5aɒri c|K$uF#8ioXRJ"RJ /}||||79V%cI{WSL4-:'yC:Gz֭}~Ps(jŢ|sWh1wl "xw/5/,'x֪,Lj<3f߻X,J[{;hD2 YgeYX0ւj1%,[ ?FlKBҋQKd)`E(6rsVPUU [x]~;{cyGtzOd&>OKٲa#׼qW1F`L&5sʫL]/]I8&Li&ϸzֱr7RJTE;vb 1yRHڱ]{v B(dٴi D;w!ǟ B8};QU)[)//Ƕm6oBO_8q7d?i̳KuvףicNvK.}7{E.uuL<qX &J_d˲H&2~|QZĉ?mw0{L*RfZjj'b&F'?QfΚ}~˟8q{~aMd:|+W**q5χ:u2't(Zulsxu﹇l6w}5?ϰmScgTd vA4gimif7SQYI6عs/~/twmL0;wi:?SO;LKS#^pA5pV1EQd%ҋimiEسgNL*K.w>~ΞE,vo@̚9x<O<qwû=?ᅬϿFͩGЕ""/b*>/bY6S&O"JPJ PQzzzy'专NbU|_l:zKtdҤTEe6n̸zO(!TU?D?r^wf̞y ?%b`@G駟F876?p/ϭXE,翼&>b;6]]XEmm /.pAQ,P(pJq\@@e֭\p{Q*&m[47ru׳ia`p+>Wu|Qdrk xi:f̘/H0iSdX"c(JbZZܧ?IWw7l 6qM7q?Cyy0H&F #ϐPPU$\$DZ)+܌iDS)'?DZQAmʢq"_.<|KW!Mt6˫+!ds\t|?-,{. S_H$̢Osw^\WTUeoG'=@ĉƼysXzzG4HTUrXsC`a!\sqJF\ضK+ǥ}&Nmd*ٶ}R m+q]Rx͘wv]յk_bOi:~9r.tG:cň%EȤ|CضM@0q|COj 6SstcLjmn 2g סL&-Jo'L"hKGq%Dr3O'Lu64Eu<Ziin.k*UqY3]"ƜK_nIxP,~_^kK1+}rTZZ݄ua݆,^ N> UUfG:B'䔓SYYN6#0^5kJKyyp8ĢK沬\<}}$$lH,}\ď .} N8X"PI0:kb. E!:y2LX4JSc#];g)OƎvAvÕW|TUtB?est%AwwΡqjkxGx~.|sI345( M*BI** PZfϚIY"sESIh |]!9)E `ۤi "ҕDe؅=TVUJD*B<'bHidYe a.u xV\%W,"HH$E"$ɑeT]](B0$ Ng4T*Pz{zDJB*x+R)E?ѨgUU!"DBɧDQzzzF2(]]r`XdU^Wc (cOq߃$Kg%mxUriw[pby}F!p0{ܯ|9vGӁmx/mÁtu9^4" ϱ;燺a>>>>><@`AwpCZ o4~8ꬊ}ٗt~7+_8t+uGy=Ч:^8Cspg7~:L?_\~ߜϿDyG6b)eݐly9Ʀ-[!_2y3!ƬRR K\&M1cF:7 PUʺP$Ϙϛ;nԇC͆@h\-2Q\c)ƾv6}||||||||+FSc Zct]DzĹRJūw  b1rD,Q4Jd2YR)H ^}L]d2~6}׉vEL^zy[na\=tMTl6Gmm 6no9w?_t!>f^^]r1r7~N=y\wr% |Q:RJ߽EQ{u]^ze6oXuKq]tMX,A"0RBPNƍg_w7h u0 #fB=R(#KΎE>>>>>>Q>>>>>!$SIl$a86t`l6aX6j)X7P(&ud"x62{,TU=Ri88 'O!̳Xx[ FUUo̍ΛCeU _r)Vzc?CJN@/nuٲe+yB/+%tKil \AtB!OxE"AUԑdtpߛEdسg/SSSM0DTE5k0n8&6xӜ-}r/ ZH::x;L{{;?0^t;Ig(߸|CCYh?y؂,(dӑtݿ`@gzO /( Λ罗 $@ %8ڥHX/ JGާOWa  z@rlǦP,`ty晧O~O.}ӅB ۇeضM88LN22(/+#;t$eakunřN.>$ZB< gcsGŜ$KI+v@ AЍQyeyy(ǚ$>F%RKk+3O}iX9Y׺;K% yJXļ,~c3Ñ<|^ 7t=Kx0,-mq2K$Duajl)@+l֌GddY@q(x<.9%Fe4O2E3%lNnF$&Mi6$)YYbhhUQH$ <ՔKq?cSH N߁DcZUs*q$~NJՋ5 h4۴$\zmc&xӉ"x^z)# txFVUq</N.EfnYilI!!3SgB@  :Iߏ(X&0m(a rˍ,lTS7Px,JN ػC.qm++Pi$OԒ]Ԅ{`#VV>ZݫZ|~pMg`W;m#K2iֶAVV6Hxt,$a;P;ć3rS;Ƒe˅ #-QUE3R#(tDt,BQ@(~{yHnbb0p-I.OǟEQe:@ 4 BD"aә=s֘z2xBdge100@ffhIzP\TNrrr$ ƨXfcW8R̤EH(ɂ<&_*(7E ǶRle9)&e9YNM9@ ;m=LIJLn7aJJٺu;g`5L6ֶ6dY+WsE˯pm;8233PK ]H'jQY#9/U !$Idfft:x-gZVMS|>/CtO@U;wt~(P? -[g>Lqz|"($ Mimߋ rH㴴Oc(%{7t -GI@.l`Q 9!UU?b&Ã#'T(Iҿ~"IɇZ^uUy|꘾:{AGNvmzX$tLմ3*iJdM`G nt∺d!Qe$Y2}JƲúODKdYFc铒$a]mtAb IT5Np<ěCUGB$ ``(룍Pu]B!j>HĦ['?#33Nm348tXkq G4M5`088ȵY9cȶm~?/}`04:$0a`&C x$Ic&  K$I"H$u. ˶go+, nmд/A'2xvmêk%/( }i2?y]y5"Fh1z#Ih/~kgg=108a裖Ew`1#;;wrkY$=( aNx$I$|›7cW3'Og| n;^misOY%|}Ɋgז~ܓayE9|ξ^r5 /ɳOş|ζmY f9bql&-Ϝ9~9'F[Gsn0 uX9:@Ho.$aI"ɸ#RnԾݰҘ6n6eY $DBc|O280ĝƴ,2iin]y'hk@UU 1cO^~e)ӧO# s ]Ǯ|7{;2e2@̌ ʪ5NJJK9B} Zq8 Ǯ.Zba."p$>K$%'tvu\3-g /`og'`8_Q/Lm***x<3 iki(2#ۛ(**dph$.զ|˴ }}I[{;n0ؼe+?H2D5|( Pk?E7tI0䩧$"(;E^~>Ձ ].lÏ>[oq՟g[pX4̖mg?C,ܼ\3(IqqXg]̞mq޹0{#K3 +W;~Ovv%E47Ʃ'P  -ӝ<{BW;IzZk־Nee7臤/TTc[6yy_]y'bp k6Cqq1po]ԩS 7{defQTXDQQF4-oMb[5ina;;9S(,(x>.)eILzSLH #t$,R=k&u8N~{7ETVTPTR%_IJ+8s~D"䓟Iaa!W>{ n3ctN?mt:_ƤM|k_E4~tOCLvq`OK <Wh4]DccH<)a멩u됰q\46GvA1;).* #3*n po~pW_~Օp uDUe%Ȫ^]ʺuI4q߹:ҳ{AsrU .Z~I閟r9g˖/wEqy9lgޜٌf8K.o\x̞3;| Ln>f͠,G/gYtu[W~ݹStIO7݂ħ>} mL2ʊqsIp}tp?YǕ'dge_`k+?EC}-"/_Ac}*{~w2ʫ((G4r8>0|_ /a7 7ޱ_3NJ8{·u W/~4ذMsf⤹shg?*򕯰z~y9 xr'a||>M7UfΞE"%p]==|>̚.y䁿Q5~LbNr/E `bS= ֭_ϊUhjhnǓFppiSO$:e }h3{ZBCeM3xMB$)JOO/UUD7o瞔 #IbA*'ThEA7 vȒDo_cYaPSSMzvirf:s!$== ۶%ti{? $F/W`)'dm;vWHc/㫪ÄB!BÃhN_TWWc$QXOVV!e^&OAdxA‘0==EiY)`AKk+e<摓e%W>Sc1.C$ *+| W4&N@cSC| ֬^M(Y${zXv-mf ϧ.$ JJ`` y?4qO?łƛo[ot8p碻||K_ "2ӧO#H68h#3#u?sb&"A0 .` pHK$^xq j`80LqQ%%%%atqGFBƜZ6Vjexعmhtrw\]rP3PVV墹O^n*[m4MfN麟~.;W^}Mض},3yR 80 jYk455/"E_I*.b,ˢ %Ŝuvt]4NDuM-V:־Ӧ"#a&H |m+Vr֙gYMjcɉry`eW'|T$WȎܬtV9( PUYɣ?5km IJNV&Hɼd<ʓu-%,l Lۢz|/4*(/+奯"K2%384HiI1.kT;Ay:_tX͛PTTHiy9:mk~YYYĢ1Yg, '6$ ($188O=M~~>fLgoGF7#RUYiZbqL+6 /.RtFu]'-+_Z:VJȿғeQn,s$7qIX륨UVsÍ>6mق'??lҒ֭_ϭ Ass+ u__2/.y 43qNp!IpOj~? [(.*TOO`IyC?iDcO}<6o\DZZSH42j&_ n ضg466N[~z+W\v gy -A43 o?IZZ{4sWr:c1ƚ`.8aqSD IDAT_[ˣ=INN6۶o'"I2iZr֑$4]ORiw޵nHc LDtw,]޽{9de}09srr?_Uɮ]>BnN.ZBc]wzhmmKzF rꗾ_{r.pC5H4)bMlN9$2\tTʣʪXvR8 | {_[8|W`'ϛˋK^捷&24@NL{gYU ^f͘ήx'xuY.`YfR1n>+Wr³xL82vE,ז<眽u7Sֱ;)TJJJ ~:̞MiI1/X$7/eW_Ӧ}76{;;geIx梨YViY$hkoqdseW;Ͷ۹W^|`y3ibuIagY&q䉌?EBUR*Cecn&iii455eY466P^Vͧ0e&L0ƆzN_0lMJqQmv{1}:ӦN9@uXx<4M).*b`p,.jWZBQa!yy9v6u |>|^/dfeϣb>Jap(PYQAnnyR_WK/Ĝٳq\zETW#B! L~E9eH$0MsSNFL|>5}䓞Cʊt|p]='blvHOo^3grI(--!=-(t\n7ddESc#.̤塶Ǔ\i >m >.,QPTe_Imu5TN=ɓ&P[Cff&999  PYUɂ2iDJJ&QPOQQrkQQ!LbΜzQUeH)Bjk0 UU).."3#ysP[Sᠴܜ(.N0Mf͚Icc0kL-ɓ'qS.4rs^HYy)^ $ RS]HM)b&HLc՜~iSys;wgZ\n7fLgɸNjYp|rsb1iRraddd0? 4Śrcv8ܾZ:9yxMY̎~$ٶmc?BVTU5;p<dH0òLB0eqq{< X鸽^"* ^YU4x<+"AV'$۶znT%P("˸=\AK$Fs4zN$dm#sdx80,dE+_cSbk{Ƚ`y]:yM4W[Ƕ:\#߱>˲F{쾃meyTls\[z$Yses³6{~y`gNjihXfҽ|E2H ):XC? 'd'q 뻇wG/ uȌr^> =Gx5o-]I;2K; ͱ#;&-;umw=#9NBӘ:u ])j6yuMGDǶ L2MLHԙf*~v'IHRv/7N$FmZmdggݿZIRYa!4-,c7Ш9H$ڇbZ|PDƲ}{M)*,}¶eh6FM}|4]Z##Ô,;zAmi~_,|X1 #BяG ɸrCtaPZRB84e/?|ÇDnNpp}S$ss>\-F7 4]:E"m!v~xK\.1X(DWY&x?c$ 0"_B @ ǐ4egNFOh& Uo@ G) sBSYןyr @  : =C4>(/'DJU F8H&c@ ^8@ 9A'2`h(MHDd}`Z6͔*DI$-!2,*v#R6䢋/_<;N'W9bΪůo7=T;w200,!,KUPtdב;7_PsT <`;r |D]ed YNé/؂p<47FC{zQU/^̎;K0a$( A^zi s GvvvKF̚59sV}p\Gdߘ>}lؿq%X 'QXT g׾NOw99LHN*K_YoM^^gyEEE4^N>$3Xu"g}<1ctTU=h,ڽU+W288H~A>ϧ!^xz:e gt000͛il,KKtuw3SO  fZxEoh9===,_J:[y%D"fϞԩSp8˗H)uz{Mկ+ h]Yn66oڄ>@H#1w}HadY-;~|8g(|ا"˲Fu]ױSg>m*Z"&z@ #Gxgy71mז/疛B @N=t8֬^ͷBBXzE44 z)^"(O=4-^#aa#2p[̛oi*[s؁lڴ_W xydH$o<{;ob*wXYq+Kynbb1ya>T ˲Xt)?$Z"}^~v6n(2ᠭx",ȶm֬M7/Jz9YIMM_W][O~S_EKnn6oBiY9?eΜy<ӬXa̙̘9-7?<!Co,SA]]N_qC(?mFCC}+Vd2ؾ};M tuuǗE&L=޾^b̙3"  6  |kQZVv(,Ӵ8q"eazi" CCCSX躎ih޽<‘Y& Ʈ]bPXX֭[y <\tGl޲O>A¡0ť(JAA!]])UhokaϞ݌_Ezz !t {S ]7..Ƕr%-,?%LZ]Jnݸ,>p1]ZǏ~aή0}}Q$I.Ǝ}LN lh4>Oh [r%yyy\k ML__߼:͛$sY2Nh,MyGя~t:| KK6f@p$Ilټo)7|۔dht;eYh>:;YJgP.'i̯e "J 9x gi߿Nll ZÁ`Ӧ8]N&r|́HLL@e4Me޽o܋ H[֭[#"QAl?vݙ$5h@bb# TlUFQ%< ۲III#gmU"t 4r9v(GfL>T^ ? JvΝ[X`!_}˯i&}~.GFFF8a2#-E~zd҆ =k&;v`yodYblݚƺuk#0'gsΙ1OҺu+4H"33ߏYr%W`ȐԩSh7ڶa'cv6}aRSS\J9p&$Ea…do%sN=JbB" GJJc&LDږ-lMJu3pqFvZd˩Sء#n;ذavn'#}+nW1h i֬ H*UXn-{!5%4Y~Ep8zoo`{ v  7H h&fSM7!luLAM3,!/K$ìٳ9q[8p OMUu ;w:رϠkj֬En]3[n76UnB޹ ;u̜5h75EԭS;:2jxyJJcAppǰaL1ʫWψLvmtjլ$IԮUjժc %E?o~c'3edryfES˸{=z4kԭS]׹馛x:Mѯ__غu+իWeAٵ+M75~?bŊu]ɓI߶zE.)h׶֭c #`),(`KIP5qb&jӠzjݛ9s厢Zjt0|d>Ljj kԸh&qMz>t3,h\ye=Z΁T˥P/*"bU*Wr(b8o0ETI|*YG L4sg!70MI9mDQ: ]PPHN #GiPu% I~4=4IMB25LTՆa۶mѠAVN]$$vE\Q[6(99RqKs[6$ $aوnSZ5<;:pŅy 'hFǎ#@bb"j'32Ij*,c(kNʢvZԫ[7wP(PJ>[X!))Hi0Dv6 קf:̘1.`CѨQ#LC' 4lD5u 0ܽCQTQF->2_~رOrEIߖAQqX`08rnw11ݬOYv@PFh%k:~ɓ^x~5j;"l>Q$$$8=ߋSTXȌS:cz-:fmv;KͧV$Sp̞@ AW"$Y-BPT6 gNS2KJr۩6;E,KDG9"Fu|YIJI` iVpw3kԖ0(*#gzk@|}$ڕsD.\G2:dEAu$I*cDӯ  u+"rS`kPT0"9" Z53(>$"JZ0f& h(I0h{ş..xagfYk3LzA('̀=.$w(KYXXX.&uvs>ΏSʯgglεwވX粟?h?2rfrT v pYD^Ŕ ga : wb؇*ZmV㴰~cS IDAT8o ?UA{ahiUM<̿LЕ;5 S-Agaaaaaaa[%N9VEZo%,,,,,,,,.̲+*"Y/x~r"Ve 6g7̶gs~L~s_?8߳S}ZNGտPS*16QpY]N'" p-I'Oq/ԯ_*aHQ~vdi/FfͨWE?"\a4q "v _qa (`w4_!蘰3{ ʭ h@&'i%d `6gtx C' Bڣ>DPFs+%+DU1 aw9( ,`s1^Zd0 LJ,hA尓ڐd弅{B//alڴ)cdָlv[y\iDDsӍt.𛘐fjYXXXOȖmxك'NY3C;Gɧ:{ӛ LDebbӋݺҽ[WȾϓO˾xMUUq:˴i݊nݺлWv–-[>; C#cGQ$r {!9A+ + f'6" *& %鼽tJ;v-=}OLat'pqB?.WcqF't1 h7;wz3:&Y1 ;) raSU@_NCT\81A>YYc$99̘1U*c|1sN"Ifhw>h(Iڛ,_< ժUcǎ]>D̢NQf| _}51%'$~4@ YuL[;8$ItvGAn6o(y?Ӿ _BΝx٧yxx}E}+VĉlNf3ql6;cY\]׹Uʋ[ ٯ@:fe6+aQV-*ahN ۆ( l2Ng[E !b؆өpH8*w ,OQ0?~]H|x 4n$ۜiHzuTRXFyKҶu[7p3 p(+ѣۘ0sۏބ(\ue_nFTLڵkMe| /YJfM1 IԱ3Nw23w##v54nԈWc&5jTuVls еsgjתqD@E"o)].钙N5e_p/mvQW8PHTl<^_~x YsVaOp_g59UHd9dx<> ns 0E\E&cKHHH8k'҂:KyjVZ'fO:8$8`d_6oP\Cƍ ClSWƁشy :\8 ajef J84pYG&'v9O3N6LNT\ Y8y$>_ЬeK*THܳ[q߃8?qT,D~ٴSxCmǎ߸a `0{HP_@nilٜ{[wuDwGDA%-Ν;G~A$>>GErTܨ!Q&m1m+1rd9{=>=ӁeX0é^73w>^ 4DRm\3hz#FoF;nVrbbbFhNՉMt@ ʕvgM,,*d'pبV*=w( (\iHTr\ѷW4͛54M|>_B@xdeUd+E,lޜϿȃrJ*l4~$YELLt]4M%J S_͉jAΝ==4Puaj՚k$;v"5k( Ab|IOxXVY^aKZv:fDL'g+V"Az/&ڕǀ`Fdt&m/MqACK.!#$۶R)M2ʘRSnpIʬ[ Z޽'4V*,ȣ OqǏ$s'еvJ]x'mci<~9;Se -#<-LګDk&bKljhM Ŝ(,[^x#"rQ,uTV}( arB9(Jx=(€iа1xszCUѬ(Janv cxLCcOYv=׬ -mIp|^"uĄdY)W(ʕ+3'YݢrϴjQP Gۍ:t=oЀċǗd TRO1?\8Ay^N/3jOF^{c#}ΉDI@@/)ˁ@0,n~>r9vĔ2xX* ]z4MtC@V (v7W4H*( Bib۹ځ4nԈ~?>/,+I]JŦngab&`FN(jFaFmuaٲ;KGtB&,agaaasa0sַ|<~]7vt@7  |ǢAW_{"V^nDjْ?ϱ&qӪy^/׭=ݳ(TU9y \ӟW_{b[J+CxG(*.C2پc;11Wq$;Φ[ QX5kQ&NLlKOYHBbzlҶdǎ4j6mѸq2v=t1`K }zװ|!$)e2EYGzx|U U ֍aC##e?4MlvSwpMIoTqfYqQr U)"izoM,}MDE\ݏ $GŊ ʏݥ'#\Ԫj"˸QfCN(wdUmxyQ<83I(`qp[4w?#IU$IoXj5Yx)/`/HnܔLOOF>8F>J]wFڵ4'|9s瑒ژ{I69z<,'p%tzNCCҿ_*U#??HlߞA\L4W]y9'&M:)q}Aq^~/A رDݸ ;Y0M9 8Fb(D|xe<Gim!B^ v"c:al&躆aȲ,x<<^/(2(FV/@DbcczȲ|DY ?`x>4=X& &&Tp| Qrp88v {Mu W֯ I|cp9]Ve(.jFll,scw~4np\lD=AKHDE9|=~e=QQ+..Fu\.E0BHt=)Z$*TEDE4f ~ $`S%p1bYxxAEBQDDQ@CR 锱N;kz8gSpc)CRT~6oQ!ܙԮLRrJ9=#zíÆ3{w!ju푥[N.\'S%YBK~*2|6%M3vDbIEЍHCgaaa?;QQJ1Qʼu]GQ#۔t: G¿7Kul=l~a!bvH2EԮUzu¶$**㚦r^IMM!FKnFb[WrnV#)Iv&bA v\n\g'yLS\@ Bht8{mx9v35ҲS?8şϹC9*C]|j-뇀(-/'.vm n\\xyL 835rTlQ"zV8Qc.OQULIqC! YQyq# Fh?kR,f'. ^B^^vemiFڵm 4e"`% Lbu-O ( Z7 :01ty%i`}š0i)EU?nRijtE&?00JZ%1E9#m%,,,,,,,,xG"։4O*> `a=S Eu\w4MUEQOQ(Z 4 IPT(op3GU-oZMEwN1! (J_Ag {|4TU}Ȳ}ᗹi1c{:k׬f:"{Icd\qq16c{7R9:K -Qڇsmw.wi6u-?ҦϵTX壪7 DQm(%~Q'V(3 + …'J wlgH]wIqqj0i< 4`̛7S0QA_~ݻDw;xgʷy(J'dg .>*",n[m[缹dǨ6xK`^~O"Vd.(喆<?$s:ޔ;^xYTbsk?|Jo_6(Jo%QR? AsZ>,͎'cL vZJAv23wC#i۶-W\y/.bz8J#G4i"7mB9Ƨ9k?`"4MckN{1zkV^fw>k|󍘦S|F/ׯ?3䪫fwD.3qqPje&wNi۶-?@|;f:ӟa{<_VN3ĆFrrm۶(Fq<>mnkXD_~wM6m8|i4Qm&Ns8u5,|~?OCxz6L)ݺuK.|g}~s:Iٺu 7tmڴo(x^~-nTdY|w)S&Ӿ};ڴiٝlX}[n,nA]'I"2y衇ٝdn{n|;{( OѾ}{*VZqQyEQ`LzK/x5py t6@VEEa/ywiР*|9u*M6bPXJ׵@ddr&ܵ IRq(񔄄M3S' ;(DD䈱eytZz6Ն(JN^}ЂA /?-[6`S]ү_QUʅ~ a.2X(Hr;𝶭ih$ SBe`I|Dׂe &hyEZ0@v/$&&O"_O;Ͽ4r侅wvL7Qm@Vl垡 Ȋ3(zڋS%}D A˵CMBL |g_!"kg2)7$}%`w ѣdddd:vԳ߲}$l ` ;(r{|j2P8>]o%ݑ$'O2w\7j-yGxשRb:UE- xjj^}uƎL8;wsݢE|=-Z7R\\̙zRQR3 4;Ɗ+xb̓+ǣ:;v ɡZɶmؿMӻ7ڵ˺GڵPm*q/0tbϋ/Qr6kc՚> ϦM9ų) :KzF:99̛?1OGtڕ^zSO?Mn0` )))>rwVi0筷kS v''N';;Qg5THK gdHժX && 1klْ}ҫW/Xb%W\~9{eΝ<\w&'rJ`С=ΗunzA&MNcȶLjqϽm^=߀iL\N_|k}͈ IN!Cf2}v{l Z} ,_~W2qfkB #++$&V$UXΖ-iѳgz!dY!/={2Dݼ\}ux>>CFei$K_大;vlrSyWsAyŗ2x0_}(A6ofΜ9<쳼lܸYfL&b믦|rx5ƏcGKVVax׷//k.<󤤦2iDt ))呞U2g<;ݻw￧~?/_tn|SO1n ,Y 4~n_ZqP `KZ{dTT>_~ŋSO1et]#q }-[T4fzK xbϮLyO5kְtb&N yd&m:ͦkg&s~˜ٳx9rI?gyTX()dlNXr%'N$cv9 Y&z.P\\7ĤS8}Aywh۶-oM61id^}UO@% vyYz5,ddefرY;+JAA^_~C#dҥvEu?wq;w?`0fhVYÆ p(+ Mx饗)!ᕙʫtΛGBB;w:Ͱy9k6G⧟V0Yt)'O2x`5k(*, -m+)L:g>ͣ=Ɓ8i~2GO|Q;YZnҥ?pCyͷе ݻwsu~|9u|<~ҬY *TD wt4 qRrls`ԬQ52زeUT%660H_~na}~G|B"nw ܹsh߮=6m CtЁ< P,ǑG4ȬZo+UJE8ٶNFt4Mf(ǼAҶO,_v#%+khop}mJ})* Z9tU4M4M/'>Bɣ:F@m6!aG ~^3&>}p*V@llZϠi }E~duϵvԩS7ߌiҶncW_&>9sCaǎ#}.իW˦ r7w|.rڴnڹlOpz:+Wf;voûヌr2>N:ruƍq8tԉUĘ\riG v&7rGѭ{w5oN3`ŊXf W^y%d(?HQQA-H֭ѳn7^端ȑ}>6l؀(\{ zz,#F ++\~,]֭'11M~atp<wIrr RR%rssR իWGׂ{ߨ#˲ž={7o>,X_~޽{Xx1j+سgod /ЩcG֮]Ǜo 7RvmڵkWViѢ)1c ̺u [+T4-PdeQB4iBS_}=={0h wƲe˙5{ab |>>Ӧm$I *pÍ7ӱcg\.' 7/Yf[o"sbÆ ޽{HےFaQ!}]MՁy!]WYey|w*WP^n>FPEPE}0")楈aHLA~~16Ν;aϙ:j*&N̰or9r0x`j֨f#*޾};(Pv}`02 @U[RNmbbuM+sj LÏ˘?>M6%**xϧJ/pE) 2tvUW5$T(e]jSz7ܐʸWjI3MYqG), DQ L ?o\GNTT AaAa$}pcFL" /}A$Ͽ`54jLZp8(Ϋ#_²av#\\G;V@0ʑ *M%*+"ҴIrssT"}zSGr#*0h(FMNZx4$*2L4ǏQNר~Q&6dY쌸vխKQdYtII4kք?ڵjKÆ 9$VH$aEEEz-m&bN'f8YVӮ+2oz֭<,vyG=f4114lD^~>ժV݌ij,^yѴI*WLLL,^,L;'"##-ӠAF蚦aO}0 $QĦId#] II@HDDI_|΂ [iٲEȶիWԬQ\NE!>>ECUp9ø^@FUj~B^}u#t GK/qF<;KZV (< :,s v j:\sR]FITRGyѣFqUٳ5k\ϴR%'YYT -8))hQN'lrH.(b8N'W!Wvm2|{$Ya̘Q{^;= gIۺ,@ĕ]m-!Ko6'!>#n3TUv|4$yd8 ؼy{eú4{һg4!DF}%mPQzAaa!wk@ִPҧpg,wu,odEf-|f_eHY` וW^7ĸ^nJbB"c*?>A&a Ӆ8QqՉVhku=(*8 K@ $$~z"=<|L8LsIII#--=ݦkz|M[빂iiq 'pfwi5b@(6ko0- ϭn$;Çqcrđ 2>}{sg`xӵcƌbĈ*,&!!3a{ ryYx<;l9y@|lLϞ=Yj%,Z4L\x<èq_z[o{^>a<I'裏榛oEE/_pmT}O%99 M7@ޭ)IXɓ&b:GLύ UUƽ3at"h|Y6mnH$i?0 "0Jk3/W^ᦛp.>C!J6EnF <Ŵ2b돒S~<]1M ]0ڵ/Mo5E:u=}ZeSp]1 Q-C1tYqq-7̳Ϣ* %%YEZZ2:N~ @ݺIA{1|>?}2P>!ӽ~Uu{qmFE^/VFUUr]x(j{}>9 Ƞ;vlܻp8 eU|>zmO"! UW]+a¤$FڵkinP[[m\rfdjjyŗx?`P<ig*Ν;ص ]rג_J~~/W_}5.q|g\xx}~^xEyQ^ pt`wAbƌùt-$Iddd=ÆSTTLFz:yyTWU֭{,"11HB8i z*c;~83N?[62 uYgɍ7HO,222?RU$TE2n\=z>wo=4MG BM4l繶5#6m?Ұ,40pAeYuU)2.wcZcM]cRJh HD#:-MPTAbimv-,Gp\ y;\g_o}[DDEtmCH(6;Ya^x%owlO *AFEIV,r K;[2.bv(\xytdKZ%qNN"2[e(}g:#+*!+j\˭k+{־FӴ%^X2M CIHԇp|CRT7Z4q)4E]چa`v_uy1t-^o-ښ*>>/D&2b{̶bt,ӈ/h5}[վ$ֆM#(\s5~?ڕahfi_[c;L+gc:c&?g:o_w,=e(ڡε/e<\r){!\8ՎѡobUpuvT&zIJ4fk[1t{D=j K@bK',P\ ZTCQE,S@%H=_zn# ~̈u:a`~v|tы^};LXDQˆ9e̚r 0~Z\jN!f\iwf' t԰ξ:0q }ԃ&]z#j:[gf|".ܟ}hw}~hٱX:@u<{׭eE#(SO>MjJ2}uCv~rY8` ;s>bc[:Si3*uev׉@(ꦾ| {'碸8u~T|ۗsw֏lL=8иb+)n\ Z?hl;EEE{sb">DA[oR~2˯333{c\s52ȑQaFBu晴.6m2M9N'A$UUest#?" =%PU .δgp= r3}_jz qS`F]u?mxi?zbu?1zw)ońg\on+ mL -iT7h۱G3l؂N98888888|l J调Jva9BAg&@b|CloB~ 8>AgY.o޺ALr?cdYěookh H zȒ?AÚ5kyAPbgNG7- 6;vHeYv'쟽$Iq#"ƎKiYV۹ "^˲eߏ7rK~TU=$EQPlhyoʲ,no@sVI(/oVOt;@FiQMŗ^+PdGnutWE)f䙦A^$$nLnݾ!nzO,ZYպaC{{I~={p)'Ν+Hx4]g!vtnwa=%3=B0 a:@">JJzy_BqwfUl˯FSsS&Oq!Ke;pX\mmoKm[Qؾ *hft0'MA@0;x|gv# nCviq<}" 10h߯ ,=- k=*q IDAT劄CaC֭۸?ifDA@eƎRnFKK ,z&6oJyy--AjkkinnFDѣڌv%X(B4}nF(BQd'g!>3], wg/y׈F.{SУ.TTTPq 2k׮g477J~x?OPUU0tdgѽGH'>NY lJyW(-+1k[Zb唔l$7/?C=-[ԝ, nx eN /m{H";v4}{#ᰎ,޵(uaJKغh aXgx)DAT~Rzf4,*ʚؼy/Y~U-Qjlڴ&QEF 4l#A(ĦM{ {HVo[jj*r*&O#LeUs/O=|3es/P˄$W??IUeAdرc0M={09446ե g<*D$'dǎANN.]r9888Q].׃z܉|l矜p4(/݆ǭ0x 淿ͼ3_G:{yl|O>I]]qcs9blm7 Juani$+Ud.AB#3xڻ  % ŋVsS>DkjxUouuu̘q Gb_RQ AYx1P˧.غu;Hd E套_/K.4yp|VaiP(LKK ijKS}/ňHsK ;wArJ -MͶgs 46Y*fC};JK>|( 4CuGTddg4)..#+.]^%]77.Yxn)ހp73ib>l㨣 8vZw֮fRTjtM3x @Y1 F(V~aVf5L)&⥕H4b0bd6W^5/Yt-:k>&sKnnl?_:!C2C<;j;_<- D4 4?G1EQ]w#W:/SO9 &T_OiY6mw,Zr"n=2XsQa%Ib[-[k֬O\rۜ;8888|*bZA$I4 Z8 PYU[(ٓ.]21M͛PPO۝_ 6|I^^.II!r$BlhFb>yeN8wSGt4&װzu("%͸s=&P4}}3# wrɀDDޝ^LKƪU4$\?}rԑ]70,<)XEN- !uhFADdYFL]u `X&(~HDIKY",Bg6r 4]Wq1)|ZX~a|Xթa&{3OG駝L~|p!!ccCծrӨ۽ۃkMd˖TUUsί0 dڵz<=`yY"mcV2t`DQBUU|vI GHcvQG ϩ3d׮0@ d -ŢŻ8br>>*A?[hKzUL%+|]hH$ `n)M19t/45iϯvKhaM3#YeFuKwqʏ\ETm'ҌeZXi'X&ir{Bp=.0}B/<$7x^x~ʘ#xYo!J0p{Eشy3<\nE,IzFZ$bge+e:OƲ+0?'x<^U+W\NE9cƩ\M9mRRSHMKŲ,e5߭ͳs ID}^e'?XQRvYŗ 8AHKM%%9_xm[jj닢ʫ̝1ilݲ5!b_rc9"4$%EsSKJٸU+7]3Zg~\ m&eA0eK=if( JJe;gfL咸9~Zw*v5woG/GYV'#+.]r钕C\s钝Kfv.]+ !1 AW+~p]0}iӛc=~} N8 ǔ90nrr 6s9gȨsof@~qÈD"qSP˅( Rba9,&M\!#xɧӷ;Hk o}1]r f 6oN(m "2_B*O< C f=8LB(KHKM"?_veKzwgCr00 t'ӻ/_v.?dd$Ida|e }zХӴ4(cvK?b4N>pXQU1 xmG+lcOu1qBQI^{} ݺ%",rI`_͂?c^d,RDAx=.v]wN˲ DQ:eYn[Cv$ x*>'#'"S0-\.V(YAZF٧DRTnFX tCǴLY%׭"h˅(A}S n=C;( ih B7 E^}U>p.W* D"H(@RQiln&QU[JKU}.k>_~> ˴hl  /.Yƌ0J$ď%C:nEBQ쐲 nX44kX&$\iZ#&,ds4ڳQ'9AFM,@%֙k $$ :kp(`,j3ZE}nX$%(4t˲hjl/?˕h8epvïe+tʡO(J ϼY: H xIJݣf;$I$?~ oI0fnwZlC.v nLIRśMDQ$-9{qppppp0Mۍl2ARb"G~~ѨeYxn{ \ܽamihFٟ1i|>oܢ(/nE>GBri$xx'Lғ]{&3G'v$;!-Y0LEBڮ!IӴxn2 b( !veYAU]4XƁo9f7A9#Xlyy 6=z;r7uv-ǮǫS%رcbeh1I&|+sHc^ 7+w_ ֝5,ƛɵZuD%ygе(rCBm.a#2SEqh44b}wCrP:5,n ze#t;. qx]g3"DA%S09~eYx<<^ג`uppppppp _`KJqh&|I._iYe :8888888|c0 aH88888888|#`Wyg OǠsp8D$ EQ^%I!'8?!E˄c}PA~ڵ%Krs޽k\ߴJĮ]ٹb @(/$:|} Mw_"xk ٸ:.^Z~qގ}7Q]4-O QiBSSK hF0$ ;O8Iϧ㢤nA*^Vy<(){q]k|7dYfC&q$(d?D" H[ELӤ%$;i- uzi׾f~c DQBzlx @niH$Bsss9h4H$B(4ϫz||?;tާݏ( D":nRYš5U(jG[h47kBѨ3E?# B:>sٱ_;̦ GScz*yyyH ]own,SGeLCC1&eiQdYAw|(ʖ[5TUU#ŽƌC1Mu446ѣIMM0 DQYo裏&~Z4 ={;wp{0jH~UU[k.VX=\4s&ii3}vµ^ˤ sgyF>?]ӻÇrQSSҥعs'gРݞ]a"x/u,ZTر47GYd7Æz_?w+VUe@@8ũHmKShDG@xiY1)$$SDD 9'K/yfXv.Qcx|'tZ@T2o\noMMQc.bkVkD FΥLkū}u|;t `ԨB!{{^mDnZnXꕄ( q)@CC YY>^o}nҳgf8QF^SG755!?8&M梋friQ3NǞ[no~dd /aƍI'"&CacǦM9bc9|Tuu <W_} )HE.n6^x9>Cf~DILLٳ3}[tN8D돡Gxɓz!>6l(矣gw‘(㟄#! bI;|+}yyyl()!2|Hiryc.\Ȅ ݧϿӦGB_/ >_456bsΥ%~w cƌ#Fzyؾcyy| Ѵ(fbk1 .]pW\q|"+v3%o̜yv;K[ذܼ|^}Un >ٳ>fZНm[7tR>(\.]nߙ[v*~ {h"Ǝ;@ _ny 5G}@$ছon?$ p".h&6mf5JKWӭ["W\>ͼ[tNIhp n h4Чo&6';QH%Â̬YXKfĈN^-kO>p_'pōS %x׿ŭ#U< IW]ys/55%KIKM+.'// `:3gd|4o%6pgqSxtƯD?` ]xGBe7eHsS3?IReYxO>kd_=^mFŮ]p \~eڽw~uֳ@BUUc{y9ׯ/W7ĤDN9T>p.e(§'l_P]U[P5ޱz***سgqǝ_:JJJHKKښ=\{, ,O>Au.Z(\r 6̡ .8}w^Uayoꫯ&+;Y --A/YBKޭwWV^ŋ14ƍKK%wf(2iSO=Muu-5uK5뮻#G?ߎ c;Lv,z*555?xB/Jf͚Ÿ'%%yz8ē0($&&RY]E(|gÇK,,ˢ 7gEBBƎ0 _ѵ{c;Wss* {EcS#f )+/'-- ?-xEQ$wؾ}Ç22lDff*TpQGo?׏Cc|`Ϟzx`#g񛫆)D#Qjj__oU¡hUѬ^U޽QBl(.aU{;w;ӎ-dƌ>eϲz|j5w40qBWҼ躣o,3/O߾Ά ,ZCEOre <#lwgBfFݺwg(+iǒo@z_~y~C !#8 &izB (+/u߀ǥxbn7׬4 {ywvq' ˯p*d$+,xsW 231b uB4E, 05`ſ./_FyYW^>[5[P~Mt`lڴ Qڵ`_se?lXBAAW0 0<1Rbb"U쭫czz>4և @0eӦ:<;. ],VZTGQ5o,v+އ$эjp1E` ҅PH'+q$~szk:MILLkó=OFF4MGV;c%5W_MFFF|:;j]e躾 `u?, 0vbJg-1J1b ;˹p{<(,<~CK;eC2yd23?/daC`g3Mó:޽ݻ|Zѳ'>Mvvib&]==ݵlٲ5kVp=ADD,ˌW?^rBcu]'5-fdĈ44s˭ꠞV-8M3NVxҖCL07x9Y]b#" wi'ts>i~*A7x7|/Q#;ds\PЕ(P0BjZ* u@0،,K$$$hyxo^{ŋr,Y;uErrR| X8xXs|>/E,#=Ad9dY~uJQt`g>l(T|>oh4Lbb"o6 _`nrHoeW W0ȊdkY[5YDQ]>K2QdEТZ<&"풩lN:P8LCcC̕FQ49x<,X ^` 0M۪v\dffp">[J*+1M/֭A$Qеz7457n:Æ /@OQQ/SO?] )OK/˗sdӲ,>?]25ߏ]w'rE46ֳqc cFfȐA|)466!vq[4; a a.44.O?ƥ<&N$ 4Yzxnϟ(I$%%1va7aÆ Xh C(bΜwwBߓ1i:<,LNNN6[6o&;;//,L?4?s٥0fh,2h2|PDii)O?4t=&{ӦMc֬Y{ďL&̘qoz?UuѣGwSW_#99.]:tSAV^M4eu|嗴4?/@mM "kQ.B_$O:\ s塇 ##'/֯cҥ5 UUIgYwy7pFz_HKKǟ`t-Ʊ]4773` DV駟P5uVZ5\lڼÇswS\ṮNErss2ᤓNW_۷'NL|2{/qܴikc("CtM0*̝nIdg(.NisZt|iw޹ PcӋe(.NcĮ>F`,vh6Ȍ,{xN=AZFtKB"˲ DQ:'zǘIuM-~@NkNw:ݻ(lw"L0MvWWں:mہg|>4]Qm۩?/}g7m!7' J֭۩#)11)FL"5%YCOHond[ȌɎ(- uvۀGzZ-TRXXHss# $&p&:$QVD~e$TVVmvTUWa!@P(DYNz(4x{˘TCzZ*5u}z;܇(JlذB(v[L$(/+4ihldm}>.U%+T*)++0 zFRRR<Է}G9{rQ\TKlܸ25 h'0t]gGi 躎i}TrI(ʞJnFRR" { ]D2]fggZ]KK -`\A'epR^$:dP/ E~}q*JJ#)1zXj%k֬ klٺP(HnHMMFm(2z8d"Pa主 uddxeG J]AJ1 UINvѣ-?b(77&)B)74-e"hEV{4%Pqh$s?)S0vYt1}ONJ r˘5=[t{ A801`-6 F7軆u'^s?%+jL4W?@l{Ad>cW_!Cε_v&,>$DNe9&mv2VIUl-Z4>`ݶڄ۷>11`x _#ofI-8ybYVepqU}\׭$IHr|-7'q@ /\ƽ˖avZܹ')Ģ17n]Cڵq=,Xݻ.y":uȐ/BUU|)<(VY,Ks#dUcӦ}]wɚk$ C^=z?"%I"X]#nyf?jy-KK#e9z PK.gvꄪl޼9sk ,4U!-AQTb8K8j}\Eelۦ<㸤4{@𿁦i3]&M̍ï/bqQRRʌ2ص̌ |>( +Vۥhܸ}<qڵ oMxڵŗ^"Iܹ_jZ<ȸo VPXؔY=hٲ9kh'_xd%1k~ΪK$b!Ʉ@Q)9u!-EESe%`;.*,%Ui" @Q4x~r%dg{_#OkƐpĦ"b2iU9L!LuC ]k$_GS-d^0 {Ջ~(aXD#._ /̊+kk%33ŋЩOh~h$%Cs K@ -<">zE-x'ؿw,HIYԩ׈̌8([fMٵ˶wMbqIֵ+'%)*-E1 !l޼ 6/gGWѽڷN@n&06-E >ʣO|CC+d"ޛ47ǮbsKhɍۢ2<  lZ-hWX̦Ctꔇ(~[0~/yUb<;ltfͺol7V1.<^ߎ7/v2vb|hS'_ĶrF=tϽL_3~#I$Y./3:t vm ))"1z Ksg#*׬^gp4l؈oN$I̞=W_~+yT'1D(\n= ߠ>pP8DZZ*^\<"zan݆(D"Qo/VnјAjJ 0mEn5+/aFv̡2dE4ٷ?a86V/cbqmt>3ϷhD5fMs#a,v/S߄߯{w]aLzBƎ_֭e(J(C$b]xIii[x|>{ee12-EuV=VQ#atN4U%݀ko_qӍtUtO$jj-t'Ҵ,TU9JlWSyGl?e׮bRRSغuX^gdЋ 0`DcQJJO),lJ h VZI'q\,)ndq&MifV\g~+}۶YaP^bA<҃mY5[7j H 8NRI8H8vL$$$_@ff&M6AUS"<ˆ6cu K*1˝zK;l.#F>6gUUǥ6#vNo$IDc?l C i޺fLι(lI hƲm-'uٳ.}z7@SemzUxݍVS,&l޼CuǃmD",ǟ_d;x̝%> #f hj|Ee0$+,t@ .=аaC6_/%@x<|^X{wb!rjץYg V8+WӬYi)Ba0MHJxtqXx gYz1:DBg䢪*E;vu65+$++qWrz.>>]ˏe;X0Dc6tOΰ1%qWyL|Ʉ y <7j)ڍ) .ѨBN3ͦM~й.h˶bIeK2py7lҺUKf͞CڵHO>z>}>Yݺ",t@ ./W]ɼ/IdgPVVSѮm[ ^@QU6IZzmZ=g.%5mzs6^lܴH,d^uaFn@VZԯWW_CFؼy+]v7ao0 V-[x멠]ӡCm^{mO<~InnU2y-BrYtFm,N\hS<8hI 6m3"ѪU60lfõ]$Ţs\RcCu󩀃@ZNx9>,ɧQ ꧐Ӹq:]q.?nMh8W;*ۅ,{^%TUM KyyԔhEGy<GII)뒑NZـ8عX -5@Lޞ4y_ Yd'd(x7퐟GjJ*VґR 4%%۷t1PC0{A%j"N&9f䞻Gд4q]={+˭Mff&eŇK 5-`T+V\}ϗZ\X,F^^.)))ضA^^Q IDATະ@/neXf %?/ Ӵ)/7zU|>Y={¤{д=$ vQ^ ri:bee1DGdeyQ5GAQe _CUz4va6u/we. _{>}߽enבVWl#7MkUem4p]zXljժ$m;qT\EiA$IqǗB4%G|,¶-\LPXX$eB a&YYԮU Ƕl;)Ru]lƵԪC~}W.a$CqXhi7lYK~^.y}Me6l~kضFpWe['qx9ǣPX.q:Iʪ. ,˭Q.4laؤ烮+0 UUhP?Y]ף& y\at$I²dzFKй|+caY5 .q0kBtp:urs/vc@ 8j֓*  (B4MG&IRI">ѯJz,߸kSi60JO5[4A#M9ᘥJEiצڷ# G NJ0 a$I  @pK*½ @ 8\Zt jȲ,B @ $p\:y9WйD @p M'['&#@ -NE&'@  ?I9{46\Eӽ~̨}{Pt<Dzm6mBǒtT =J8YͲ&m#I֗iƀp`e:mѥkwpׯU5 Yնq\$@r`&ֱ[Am@Inm@QuX #mqD^{JiZҵ[hYQؘն Ǯmbk e(lڔYfq%C(+/GWӽyj<^~en,wCaaSyaNʄ ~posFqyQPЄ)S'33=z I> {jܦ(R˴ c[ɵV5]Imߝ*m'. G\.owu#i۲k69H$Eq~ӍtK.]; t]m6,[ M)))_z >s12-z>,ZlU\>>tƌ_@9ex<N?tˣL{w1Ç߀ B-_uXtѝm۶3b*uu/8\}U$z^$mTy1+ԫW!&%%+WpPK,iaSCػws%+++s=&eV‘z=+kzۿ0#쏠i23ߐ$[̬ۑp IQ̘91PCΣ}uG2ezn+3_d]Ԯ8J<^/V` C3+%t'˷KF\8B  ychtɷ,Y={Wa( &+i&x@ @0X,˶ |4^=;w*j"SgϞlڸ?>Ό3HIMhGC.؜lb|ч/o~Q# jk.\ ֭[GE0*Ρ۷-[`w2K{ڃ7V2q.o3 IOj:oPZg^}FZ֭eIYD1qH-[رs'Ǝ!==E,"(Zl?;&LIJL?~QUTT0.ɂ )*γ=Ǣo躇H$Bqq1hgy2,EU?mj&)مڲu3{vn.ć;?|}!r᠋p_z/eмy R #vEE*+㳙!Ieeelذ\tL>}$ ӈ֛5k6xl6nH,5@ 6,z}q x{Yh1\s 4[osg۷]8U +bl$̢EVL{8ӕѹs r((fZ>̻>}2rBJK# ۻ1ڴ)bhÖ-A a6o_޽! q@&2qw%!~܋y .8m۸iYI:`;7z>#G>KAA@6ѠA $9)lTU%1 vj{re'VhFYY֭u[̬'c˖-Фqc<wq>.r pmĹS<(-]JFF>7Хs} LTZn|=J۷K1M#9!I2X,{,+(BE( q?EE*c| U Welf"V\att&%X^0cy1Ng^*d[((_lGBEdee%ӭ{7)~?v}wlN|磪*YYY:TiDt/#55WNrUoEe{1 T=2FB۲lE5TU&bqܣ C==l7$TU"Xazi$-i.m$5.qOHp)Pa[wcw%,]4.۶p۶ͤ8öm[i֬s٥CsLjzWAz8id`BNoߞeK1{\ܕqZ8SGtL>&JKKQT^zۅ2 ~sT^~jOڱ`U6ӧϠG\{ ؖy8OYUaWEbϨ_~zآ+-e%l/NflՆ `oao:M (_-;)b+ٺMktUW]IvN}i8x*O_ByF~DZ m#"X^N8bw^r3S;v4rŕWSOr5гg/&N@^n.~:L0km۶;oӸq#s8N,^ |@ƍ(*AzbBIŲ,ƭm.S;С-ҥ{r€My̞yv det85eeuc=x< sSrp p VD5Fܹ.2p`3vWyS mg~](##ﯢӓ ^!7v2 UUȯSSN=tݏ'??p▫*o6kdgsz8p ~26yٷokM&M`%šMzz:&fĸ 6m*-[@T2˘ p]v9*  IAK+pM(npT.B dxYr%{.#3oҤKH%Mٓ! iiIa 7رc l/0:tLN[=U ,EEɩ5^G}l4]r}^}v/&;^y4/8{E411M'ُ>@ VT#y |O?-^$IȲ,I1uJ\%5#F_CyleiGT5]w.F ` p 6fuGZlm۶E\3>{ލƣ(2ry):v<g j'G`͚D^^I8`<)^dYJG[]fC˰˚S7qf0,KtG>5><7QǣJ-t$a67oMҥ`IbB4 Puzԝ`LwcZi1c* txd̶]]A`Y.`RRESdQ `x ]],F%Dzb.eb]WJuOAq Vyiܨ>0{}vY{:d0|LO-xQN>e%X(fڴ=fh -74*TEAet]笳)JȠMA-W+*<={,s޹kbQy=&7gўnf9+q<9!d9s]@clڰt3ϱvzZm1+zt23tx<>z ;'jה`>W uM$ ȯJcta.Gy(h99>֮+eM|y)!cw˽=oeC {dF Иhbܝ0͵̚Um]wu$2Ӓ$G& h'> 'w'j$=ڵkʫE%{k|J-^z)'D<<ޚ_L ʏNzw2eGS3v,PfǮ|\5|1g&C.L,:@ 8a"M 5kP=)B!/YB(&1; /_ΓOep]KZJ*NFhݺO' ӴLvqlpط/L?e0kΝA,IJ!CZr5*R 쨾p-xw|6 ؘ>]lQ $s2Lp!t"a!'Xo,Ȋ0\vΡnF|>*$.;ΤJf׮]L6eeeȲ̗_cŃA֎DUX*GdK,e۶ m۶qFC+hidC/LΝ0MC<@s>1<ģQS]SmIѲEsNsҶM `Y6i,RQQ'ߟ|Y8.Y^5"I UP IȊIh((@Z*rNz̘{šCѨŎAdYUiYBUe~H"#3>?/{U+XIѽ+Vr`)~%Lۇp({Oߦ|]&UM7hGzۀX̤y MSqgtQ8亮[o|X.,*u6F%CЯ@lh4F -H qf bPr _ķɲ7ŃplEFw { KSQVE|ͿpǶȚ?.(HDD4i辔jvhmpǃ@%R.@K e* DCnGF8O6> .Thҟ]Uet/s ̘B4ja)>!vP o@#$5-$M,ʉmr\N :?Oֶl\ץ"dk/ҧo_viݮ#[,+Fo>.(a1z,4m+Vr"pa`F~o}*V>McLxe<y0M _ 1WSo JyĵbDQ14*@p.n)IibYU k/!@]: IV@ Hg'/ :uPU't@ s5p-E>t.+U~@  u-af@ m@p#I*e'RkDM@[lx;ֶ6T5}4F%IG~ʹMaٲ=̟#o=aNQR5˥*im@ (J#(*S̩Zeɲ\m,(rwI\A̩RmQUIK2n teT5^n@Fv0gv>PH, xodڵ;cƽ޿1bRQ5fd |;ӈ"M,_׈E4 I_m8N|zIiI+Kض71klKZz:_>͛ئ+\Z\zPZnmٔks.ڶi˯ˌә8q"@ P]:wamۢN?<ʕy 9993YU&MZ kի!(,!+2aW9NÈHR|OYGt=.,5MFelqeb̙=m۶WKB$&L@8#<6nXGqq1_~1UV{|()aX[‘HwcG_~1˗b;v2m@ b,DYf-]y衇ٻo5 {xxt:v 7Do5kѡCG6o̴iyl^Ā8޴l M~U'HbU̟g_#77q*G(̞%=z4x*O?sAeI,K,_Vǭov" %dFYb, z/lJJ ~iuƢoxG4h0=zr`)nZh(iӆ:u0l߾ѻO?VX;L" r! L~u]@p8Gs#++@JcӬYskmKF7o;n6`t2;IOOFZZ>0/{0L֭[O>}0`0]\̵]K4##= 2h od &mڴqؖ%2@ 8p]MӨWn/ry[Vҕe˖ѯ_?,3FfV6))JJJŴjZc(ah̞{عcYmB+YvKӷO#4]ٔغ,_X|4 4M:0酔Mp\ %qܷKxq1˖O#5}!lABbʽbp], +ˇe z"_boNjZwO?/;vh+^_ ii\|EԭאC)*A:uԩ3qBE;vpٽ98xG8xSqP$^xիp 33(Ag`6$.u~ڮK,KNh^8TTTK/`B4h@ 8$cIUNɁ0ׇeW=PTeh㺸Hxy:.b6s]w݃$I:m!2OWt@"tKA=&pâHʨe5xpZjl't/oAA֭[GVm(-=8kkp23s*uGGU2rΩM^~>嘦9YD<`/{g%U}kի^jY`b'` 10a9' $3d!aN9s2a`&`ccc[d[eIekZKWUWwKl!uWzg{Xۖ{~] h˺{='C*e[t2 @68! pɍI]V`l֭tv5oC %JR)3 a|M\u\}U\wݵr˭#Tfq:;;ض6?_?0yLS3gwأo^̼siokO`Fs>.*||z|,YJB8n|SϿ e͚5 E.]BKK3W^q%/s >t zy5|_Oo\OoX,-5/Wt/^׿9L@)53%J@@~?q#Nkk_P~g.icǎa^ziT\{mxgyݿC)ك٫tE,G/__*J)Xx1{졻˗al,]ݻwxb }ᦛnbtt}ttv|CW>_tvvtF9O]Pd-lظ}EP{~QM9 ksa?`Μ|gVG'_W_g>LfE[]b1n=mo%s˄ipUs?^?|IH=;]rBYuv76qۉ',okx=W\MJ.p~+X K/h9_O\ JCo^p_޽4+.1DRJ ] 8Ӵp6BJ J4e3ض!~p!!| )!AR(,d"ɵR(+XM:4Ty~^*Es6iDaBqH!TjTUZ(*A*tٗTV"Z !@ |E".&P(Y WJ+! !JA6kR,*2R\>|&cbxLyX8uUb~pHunZX dKXlŔm $Oskun7VLI% n0SLLSJaY&MY@oBJEu\PbNhsR&O$9ĢıMZ[PR$.D b}.Dk^^V9" 2,晴XqܚA"aL ;~ӄq)fƏ xO)K .__^kv{ْt8OPh4גEg}5Ts«tOO:=R-'|;Nה: sa(0O]ضuFuUFh4WJb;%{L>cuZi4FѼBaH[ J)UPIT"eQA"Ʋ,l}Fh4qƵ8jW&e_EŗDCH);4 Fh4zR8BDHC :kuZA7n%kHCWh4F9[Q'À+ G Nk3]>-4Fh^ a(yb:?"_(`&ꢕ|DSsu Fh4+qlz{{d2{j,f 3iA7U5Fќʔ4DTCzyyrlo_yJPvlTѝ3 粏*Qǫ% @eYyi4^Ŕ =1}@p}0MA)K)mLu!0I-TQ:Z%z[nZ2o>?#x5ƹ D)T* tN0T* 188H.' 3|ӧl޼#GN"h43[|CrrFtkAVcpp|C Rr8#@]XT*P1>ccZ#E6n:J-|«lb3[26'wYlda_4ApeK }z{I9p {ytΜIϼ9dx 6eT.3֖f?h.OsS -q1/]  86t׷\rhkuw{~\y,[}3o DZ 7oa3]뮹Ez˱M~m/[ߌ0 |gvw+IgR o3gbܹ80cӦc^w7xr¶緱v-oΎNy ˶x7Ԕ%܌WO| D66}|䖷(y_gYz5k-۶HX LP*}ǤS)}K2&??ȥXB,fi1aWR |.9sy,B::+.FC{v[R85{ CE*ʕdhk>/a[\i{|r"-eb%ݽ itUL<`يhmo?1Rjd0\ba`>ÐjF cDY]`zG<#!(0MC2DJx^hi.iQ(1-R86eQy h"GTZk:s a[wD!aJ0M jaG-cPT*L#@<kmnVq^YOu*LDśeYT*UlBbzPֈ'H).X1n}b8F H)sRQ}k~Uˎ\Xt/P $jBCaFd+1CH RP#qQJZ-@)Ll۬'e(,Ӡꅄu-Lݳm9EI}<ϛHf7ia֓50LB4ܿF`\(&+ #zOYcy'?V)g7h?a4** Dh[ٸƆ0zQo1~9(R oq9Iı+Ưqh4DS^ϛj|?1%h=*G򙘢TR52W's4DbDjLΤ-4b5U5{'2Q ʄ8耓ytgrPPps ƕP0Fh4ׂm<,#0J%\m4bL"Cż'RS YQF\ %%i"OTQ̙!#D:h 5 IDATUќj,;NFќ|cb0 6ԭ eH6ӆiZ'DJJ 11{"E'0s#CsFdIDiH%d\xٔ:з= @Q49MJ(È\3iA\ue^th4gam3:xi;r]s!"#X8ejUfX?-$^-A }ēid4L˦\#¶/\ʐ񪕆7 b1j4͙O\İmJJ:bxdl3#̜1C$S۠1̝W.yGќiP- zUqA^=Sa'VM"JSyYΰlɉev@۬nfDRTԧL@I2 bFs~"C)BF*0TD*CӲ*ㆬcGjUzW&Hd;n8Rqx^= aWFʪlC & h43aR&L(47e?LGlTsQ͙ÁiՁi:,ӗRќ)BV,tyĆ{_x/0_99 :a;VOLzݵ.TQ Е59̫VxjD[kW'P;g3s -^{0$]Z4 d(1uza.$ɲHg)s矠S :JӳB|vb'X 9!TJ'u F9yUFp% XRT* }9D&&/PUAJ5B-4Y`D"MD ]=Dhnts B :=a8KZ*<3щOEY ch46a.6jO\9A+<7h4ta̋w4ËwyV\יvgBj}7ǬWJpx,!0 אmmòzcu㸮 JL&b˲bb˲b.xt6MV#/cmGcmؤO>_ubض5e?Ű-!lq\qkhf}1LU>6~b#}~LTFww7yxq/-/k9Fh4/iз?g9/N)\ˈc߰D:iRnN>qG3{6N>zE< x[f2|peb7t=s ׳cNꏚx{T*&FGNSOm<{ۻ+Vu($a* CC,^G?Afq}'>~;o[x(_ WFϼy onR߸&n&6=~nFbYvdḪ脄!3g!(! app۱ioo}[yy+h4\TX23m*I$ͬXGسga2t"!H`Y6L˲,OoC ^|.O:͒ Ӡ zزY~𣟰'ٷrQımlʾwx-T*I'}x*:;?+xRXָqrB0~@,_lfgٵ{7?ظ6> )C0d[hjr%.b1nzw ˴h,%N`2kQX,W_V0=sT $ciR(q;h45m[8xr-ulܸ7}i^C.\6l}L#Cttbժtwu!Aݜjk6/_ʪU+ioo˖,ga&6:.eN^TFhin&a&|BH,dPTIRd\ HAe?CJE6&`3 rBn,e[IZxIB6 C׍-`K|47e5 `tlJB6!7hccxO6&LFŀeL$suj^1RS)D"722Jss n= qS_'$2*,r` -Ci6ډO~= D߅)y\LEۉ)90q+FJiQq4- 4 8X51˜u.BP6RLyDKD,^f5860WFhoRHIjZK8RT3Hu~W[O:m- 6n7V_Xɓ̿XWiVFIH^4d]y(R՜"tM+Gr/i'6.Ά'}+bɥ564yZk ku Va6T5-lhz4TMrF|KTVc'$iH8ĪG4Y"R9bވb6f hbʜۚR%MܐDT3ԕ~pγbXa!s>!h@Y߫(]tDqUNT]!^b! FlS#ʄq(~O&`ޱS'JǴ!-DG A#Tu=ʵ8Go}AH9<5a\PANt+ މ%3nF/ds7q2>[LMFF T[oS#> I9s4Ok W*K8҂BXp jQJa6jQ8 MHz3PbW{EW,5TUX 0~FϞ=͛yy\9CZړwecMOF)X 4,>\F8k+ǞNPD̛fq8jšST*R~EӠG|{t+WنWsӧq2PW(w*8҄0 bީ_ݺ^{FtipزNa1u8.ADhwS⼵YN@ً 6bzұ妋M 48TEkX|Op4rwVgTo⸢ 1J ޻@ӉZص1= R˦YM.[IT!7A_p[Dgc8حE~;cgC+ǤwvA_Yǎ))0V nu4rmGZ+&%YvM9C4Wk9gWFdPU{Q^:Tq(XI疋ɒ8獻>‡t!@UU6?IÁ(brZ߫޿IǸ_)^+HͿXq؋vx,Gqؙ?+om+rLr3xs^7ko`Xsϑ$IU_,^>}z3Wxw9^o!|9]w?,+=]ٲe/Ι˛o~mǎ{oc64J ZD5M-[K~Ĩ1Wr/]̈Qc%iޖSR9)q_z>T%UWc?pwyzK$Sl pPIZI{K &=%0Hdߡ3Yh{ǧH:=>G~(["h{|4&/X5 ^PFtK#1G($s2ҷ@~TPZ38X{_EVns_mr9맆1gRT2H$ı)ds!!wyZt97\F+nj˯fe\}Xv͘Lj/(ȱUV':,]Qc:g]eS-RXMCQ(B7zΌ"ċoltkDg6~ $Fu#1n_׹;w&cH۽- !4@GxK@YGޚw;^7niEx=3 vn!r F`FjBk*+DNKyZ}-ʋ~}f-u(.)ͼ*@VBYMSF4 c8OGRRRwctD9+=97g}ퟚvN:e p[WxtzS:^SSղ+QcE\s_mPB7v>[Xȣ{׺g!ƐOKfb$ }̙>?wжXz>@t 19Qu/&zt0 ٯCV$~6_Żz{Hێ(59Vʌ爮sUӮ #^0{4O_ݞsU ;VQW$aDGq&)tbH.e$9%0ף49̠S ${bbv;vo3$0 F Dvm#лO %x>]v!c[ɷ:ȳ5_Ձnuou^YZU9;ϳUjH; .SPv7B3jMMnH>3>/{dYb,Zӧ^ο U $ot}r|K^{J^)=rˎuQ/6LNmd: (=/cH uv6sKKIjW9;!cp>,͗%Lsw4H߹qwh1 `2XPxd#Yig4|:Y&#Fx3疓yMewK*]HG(k\mE+ W,䑭+ܷDySNg r-?Y]O|[EI\v#[3{r+{[f0T%FE.{;R._.+?2vHƎ*}3ǹm7l` \a>6WWQ)?VUWuW(pX@p~F5@ UB 5J~oTh# drZ0.nj$F/@ ۆjl ѹn @`rZgqL~ 9w[ZRQ[*k@ V!N@ rY-Uf\<,)"]^Q/2m)J6!T +Z*r=mش/u.礕UԖ/ğXh={ѻEpXCXYڟ?VT2t@ %|Pνps/ '%V+zmҭM+-<Ns QNs 6*eؕ{v!A ϴ Bti۩m)/\(^}}}\Fԕ/ˇ=9j6o[Ȑcta^s=wcYstݏ̌lV'Mw|4SSv;BҳO+ĉ{C\xQ_:w;'N"==ԶO;2.ћ'={NzE)ٛr[.f{~[41-Yf=,_zIӛro?qCϗWuO}Xѹ8zF(νps/{œ))ga\wY)`OG_K_xn:q& |iugL{_DF3O=ΒKӗ,;'vU?.v9$8J[d$8cb݆>p=//YvN>6noW6oʙ1)bc 0s޳ׯ}*OF)_8¹νp"ʂ,W*jO0|U܍vqHO u}7Ms84kY&gzi4m^qVNemt\xQ?uFמ}?< pk[&MHKOsC<7k6:K@m󦜰А ?ı>M{nX8νps/{;RF^]%S2ZEFDpiZ4o'(kd[*lWe폎LlqN]~qOB~~=?<ٞNJOOGf_aۼ)qag^Gaaf,񦍂Vs>8nI/u)ǹ,ǒ6r8˹,K;lݶ_=(,,[xjsYνܬ|e{ySNs1U>*Nȱ//:2r3}?qx_O3ں#2{ˤ΋/̨\G 祗_%##tfyZ /%)9f}ՕVdb21 O(vuEXjvmu-{wnm+qJu4Թ/^ֹt羼z?z8;y:4M?<Ĺ/^ֹ4HK;׹Nz:V˖Qܗ|w!>(4gWRܕ;g~׽tSC|hc< Q 9!#ͣ=Jpِz:ژ'K-{͍ 68W套_[6a"=]m=wcsQ\wi3hߥkNւ6܇e>Ϛ1_Vg߁u}vKkxzlX ^8¹0$p3y{Й57Zg-*1F zwf:|(<(L+Wks/[Qr_Ѓ};gUUzw؂{Çγfj|:g=䜒a٘> w~fv_Ǐqp[eoy N9CLL4w1^zzWOmkA>0/`İ!<4F+lk7Tv K*leڌ111Lk͚JO(((ACh8ǎ " ^ ~ǕMM>u)@PԭڢX8@ 8ƶ\tpSV):jb Acy{xnKQ+Ư&NbNFhGFYV_f-0Lr&q6ÆU˖59kod sxr*W\:F[~oj'ZgN"ε?PeʚP,K\2p,nΥ6@P8޽g/>6ԔTz=ۯx{*w>.7_}Ĥ{kc/al7+~Z9h Z&8ns"ÇՉ#7/GOY΄ZBnnh4`40  :t:`+vl6bڱZmlvm^á`4 q,\ִsoa,{ z rrHMMb 礧}/]Ǐ@Zj ѝlտϫ\=F6ܩ .)I1aƎaCi߶RXX`rJ4TUEq8p8 %5pAaA!1Mhյ#?5֌呓_}Fyyyh*pA5+r,{ z&$IFU$u`BلVd˒dzf8YN㚫"44\Pe?rAgƍ/-L^^)))L|x 8^=>j hKk(JZr w`UװlBF Zk'i]|'H+2q5}8ɂn5#cw9s&>{t ᭷B\?ѵo>̙_ަG1Lu悶ZDb/c?.U3 IDAT5"s/{@pű?䨆p 2lFQ6nƋsyDǒA^^>ǎal{ gΜСC?~k;(t܅NR8|BBCCF`0HIIaՄѣw~eC_Q'f,I=-: 3NF >!֝'͂yj6e||<8o3Jӈx>Czg]v{pٴԙ :;;Djz#WyFs/{܋bGq,K!kR%DTq8S%,< lٲ5G&((Bf[#Fg4i@XX{`0мY uEUv}m`/a / /b˪|5v( 7xiZuG4IB@EBQA@!6.¼\u,Zaaaj 4k֔`f3Q_u(ƭGp8;Rd֩ jŨ}ࢗh3! dkM9Ս ^8¹Jd~ ,U U-8Ձpj%l{"8 ..NG6HLLbbb0M"K2G!#3Ï"T //ٌȲNtL :t$ر#}ucUUi~pNݺIA4$U#)#63_}mj`dhl6 LPPA ƍ0] +V PP/#Ț=F-z J K>Krk 99νps/Đv Hb[BJ=?!&}glX(qM: *^[Ll{ xfBZYYD!9I]z離/X6 %##fq: DVTPJĨ@h\ 4; dVUu,;?KR-? ъҝ F{]"8zmd$? V_Fs/{7L^P"5@RjQ#|%|CB#П5pm(a:/fI[UA423QTɈá@^X Wز559<^trOP:)*rdZZ+̡ud!C/OB|Y4;Ԫ0)zm]G/ k<| р8?6q5J%ȯFei}EbTЊsISl٨E`B=h+MЊ2!8Z 4$)@9.GB'L۹nX8¹}sJwn}]jAA\"iڤ 'O$??GѼE3:wβei߾=Ggn9~XQPκtlAGWCl@!9T.QEK='Cz^`¡Bng$'*̥טQ'5Kyg||CF@QS I2 8M:V+:EKOB-HF:smI8F -3.+K#.070 L5{_D8¹νpkP֤`@DlFvm#,))b]Ql\rsrYr΀ Rh#v@@`?&$}c -/Nv:K($0<d 3)~~=ۺ͏kVoF@)>OUrdn""͆d#98vDied`'::F_뉈]6}] &2 FĦA^5'jl??~ RStFM֗~6 8RaoM9HmhQO՟}ups/ Eů{c\TMK=E4%bY+.h4"3% iSiidT_|)ee#$C $|9=dlhBQӎCš2ҵM8l- ,Y1H0o1vJgpv+wdG^%ĈӡtneH2 4M&r5))nc-rSMxc0d!5WU ^8¹ν1.EV+AI99X,⟑MJJHppOXS2X%!I_IFw}i0Vۑ?Nvp=X@jn -Aڳf2" 0I糝$0w LPQLADfc1}2edO̳iq򦛸sGX,bZ}:> %VatLLlL644/u .ikmS6))6ކܔ#Fs/{ ^p~YVVV˸l6Cj7Se~3""3gLvY,(R<yiZ X&r\]QvDMڋpXTToB24Mcph]{@WtFd]sYg@uzdMa͕ 4&ĺt99|YNJ.|K 5dǍ]Ill,e~}.7#s/CJ &6͚j2zߨB60* ֚r9νps/{AV3Z!F&.:hi*0tԁ>_ .."kn?qK.R2͛$dr: 1̝S8QִsBNBپ53)^4{!jZl_fwG8kS> ee]IM_RHW,J$IF:J \\DXXOkccbmű ܙh$7/hZlIDD$,0LGc[ YJ,uN˃+J"5W5r,{ ^8-K\ZYn|S,؂83RT,ivUjc((*TOFF$I"22e+Xr11tlߞܜSݲLiSi9ٴmՊ>u#!!{_̀W. mlV$h'pl-{A58Ma$ L||<7M[Xs-} lcT {ly,ah;]H|| h!{@ hx_Rq,}pA㢜JH^X 4AM omHIȱ@ X ȌH%iPr424 641'v;Ct B- q\l;B24B¸d>`08q0mXKZj NNGTL, UCB7ǽ9""yF@>divDc8uX@n&5!D jfzwLҤY 1 Qܠ @\c VO?W#׬wO3]:(qicUp˄FbJ IBȲ; OcͪLFַc%4k\gA40oYTԤ3 e! &X ^9K^}M4iJLL,+/hT`}(J!=OW-W׎{lBzW-X_~Y QY*@w{^7:+A1^caV,AZ!@ 4!Aeێ_H5}WߏV̲_pơE!~A@ $nbs=疾~bb0tPuG쩓BYDu(}U\k}q˶op uʨW{^W'|aO:t'tk.|;we1y ?2•2(}u.ӟ]ϯG.heh[zDgy t*mfpD&7Of@|~IMw/eoa&MkЛĶɥccd%QϺE~;m/Cybu?iqL#VU(.iV#ܓ&M"00'No>>̧~J9uNO>7t@ 8yy o7_} :)XwMĝwg˟k:Zj nl'WwMd5|OٱOFΓJ|,6K/Z#m*2-5Nz$঻&_Oi*g^\;9iە?~^wG=͕#0y"%'2=_To)~>I9^\^5}y_)7pӭ,Ymg}%zt½q9^FvҥKy7 ""y'SO=EXXaaaL6{z^AeRA8|0cǎ%88̈#HIIj_EQxꩧ!00qƑJ/,,ob/)IHLLh4ҽ{wmV&be„ _y)?O˗ӥKF#|^׳~(ϳq=Jhh('N:bX;Zb /#%@ٜqsC@@モvy.Dpp0O}u6 Yl;n'??Y3g۵,ӧ<=k}vL&=&c'=#4D7]۝g.tR9EL{Wg-#5/O>kF_ @Ϣ/5AWx7غu7xNxM8ITT?$%%ѴiS Wڵk6mۜkz^ש Kep <:u UU<ɓ[MرcGuxH'8r9vOR^zh"RSSyݦr* >ƺ=zXa88;E^=]{pӭӳgwo='@=xѩ 2F?Au &z9W i }.j~^S=?_A?×Ѿu4GKޠiB ":*ϣ )(U$a4v«וnNu7xksJJ <Ǻ=55G}oY*+{`Aj%b﫸[=_:YtD֭wb"ƌ1`2yk8t3aԐlY(cǘ8}<>rb3c=}6?-O1΋]if\8coϹ @AC21kJNxM85!+zZ/O>DEE{pB_|k֬!!!֯__|uzM3{u XOd)g{zL2_N'd 4@nfjWY ytܙ}b0N'f͚w|WtEme K"eOA@A~* nQeˆKKKWrmHڴI4MG$˭]~; M4AΝW۷/ {{{̞=y׳3gUVj߿+V uEݺu˗:!!! {7Z[?pz@ҴiӐ8̘1kTj)00Gvv6Sxm㉪Ep,,, " B!`ATS0QE2e LRD?û&Sqû& tLU/T.&s186^¿r [Nf\;^bN{UncG]XUUMӅK");_t69_P0'ó7^,)q:`aaڧzB{wӡ}xWDk-,7mM[0(y Hk饮HL0Ľ0ع%Um_Qd߱ wW ~/YDDȜv`pLDU'%!.ETnDTbDDjP\1J˱Q54CǦR#J`_ ͳb@LDDe5DH ֭heJDCZj2GFptuTZr*"Sp%,f1{" E ,rU0>DTNOs/vT*ꅴGѽ*y'""1v#K ] r ?[&QUi\agg#"">qBZݣ(rh'JVٵ[{kghױ3joADD*33q^kx '+/8..yƮGkwpSkjkHNI_'z}'ϜiS&pqM?ĝw6˗!u!^#kKݺ~hնmDT&7Fm01y|7ny ޝ8[>܆{ApSn?[$~{ap34kSg!%5_""]c:?Y=8T+·1+l:=ƽMַE Xp^ kk$&$"5%>>5j2C~  5p\y/7n&W&q'Ȩ(\. ~;SREGOwf%\|+[OƄ擗mZ믾Dnn.~9rwE/!)) ֬WӧHl;/3?b$ȲO@Wz!d)wb6lڲ5j`тxIӳq|Wiqe?иQCL0PF sfNW`yUOhh3 8=Vxpqqwæ͸r5ÇEKpY~Q C 8|p?06έ^e7hWI{嘈><*a1c""""j[Yr8\vU} Z"Bd1&3DDZ,؎̊)Jcp,&d^ND@dǒX JEFFApu@ Gg :MP(`B#^'O(geea[c[A26^mc'[ѺM[\|E9>''cƎSvmӶ6o {Gԭ~[, Vlww/wK]u7ϯ]0vѺ))Cj SRRW ֱ,jem6oAP+ """h~I"+0u &Oш#,lNӮZ.\Ĺ"6>lmm1o%K"--wn… qi鵍6I8o߾jOJL7qrm鲾%=-,^)ɉ81s^YR9BdH-^>v*bcdRƅqM$'`n  vk]6l(\\e]ϝ;sENr 2-ڒRMɑ. ;PA#8pBh8u4?4nL25MiQR2>//aaO/olA\"J KsUx3xguwt.6V>}zAl[~ԯ_V-[#&> 0q AAA{Qk-=r=4r;:uܹ%m6?]ַe׮S3gNG>}>VX;u*kWy~zr޽^Ed]~-n^L&\.G!ﯶ~eYW+k[܏@͚5hUc?7Yū (BPy(ݵg-yQ;az:D2j5w<ܸq;>Q1歱8r13x lڸf-i<׶-rss{ˑVNӯ__lXؼe+֮]nݺba?:ge=-Z/`ǎao˗cݺٳ[dFAHH0Vz1L=2%yiG'Qg}W~p|Y̛?.]FfffJ$eg_r\}#Us@Ɠ4XXXr9k(6^U,WX}={Nذ~-zzj+M5m2ִ߳fymާO?:ǟ~d]5Mm}xR+[phX[R)9%|\]܊ +d;.Qw7v]= իWl^&Dc4.ᯍ#*r")1m漽yO3xnNZgvk6^^^R,ۮm~YVZA,6mڀ'dSm###Q4><==uI'b֭-[bi=vHCv_IˍG``_T Ϝ9_z.>SaE,}e=Jq{ !!Am\PPx!^RSCEvvllacc(wӎ7O@DDr91C FجHJJFRR2fV^xmf!6IHHLČavm+>rnܸ<Bn}N}ڰ!aƌ0$$&"!1ӧ!KO'&&aY>l޵K?G۶*t]{PZeі:7Ƈ$/22`I#!‹>T_cv g7t ڷoZ4oN/tRk_yַO5tRӡC{4o7+/^׶O4ӧ*|]{гZeӖ:7fユdPըcx?N̙aJLYI /3^cSq-o(x"sT}zrAggg 7PӦOܹJ? llDGGӧOŴi3J,} ӶZk!##Cr0g<|EÔɓ siCMakˇ#9V@1 3g!511k[Ddlll鮝H{h̘>M- tk>wGz{woc»jF۰Lm=Ιcii"C=R`kk?FrRc ʑ>U9" Wk?ƍCS.㉈z9 :&K`Zɫ@&NĤ)+э@fKT^DDD YQu ?B`eJ@xp\Y fQqRŕ/^ w@fz2w""DDWkбc߅A2UDXDFEaY >7`J| 5Nط߅6MZMoV8}oЪm8yQp(|9d IDATjTwtMX<λ 7޾8ysԦ߹k7+WCmW(Xdj7/ddd[HUNVV6d2쇈z&xА0wqQn~=wAy82Ғt{W"==7]¿g3Wtﶍ.̛qȏ8+OgOz%$$$bيUqKGqz?\/bbbj)/x>zbiW-Bkmŋp㈺w 666Xh)TMʒjK"KK0g ؼC^""R M쪼N:Xd!~xӬZ:oaӸc˴ǏOvƞOw4NmA\|<燏~`r^z%<<5W~r֭]_6 kSvv2e".䳽cú-]j(8K_P߾_.$%ڒ7q~<+%""@jw?:9t}'zЖm-mAuڥ-ZYl. pi$}`Z'''^BI`h=n ޾$=(cbߏk$ 1 o۪dkﶵlXnd2`+feG܌mnAJDT ~B^Zhڼ ,#̽[Umfr~HM}\l_->Vڒ `qpvqoب׺geea[cӯ[ĖZ|### y3zQtm-]6vZGdJP2Qud*RVi؄U…8Y܇-_Ps.0`vڥ6|]6l(\\ duuY,Y$ܾ}'kݗ,Ytܹ} .ǩSTF 61Q~}*Ν;sENS~ڄDDT*L~ٽ{6m___ԨQ+V,ǷI'㏷C.r9>d'Llec'6lX8ukWXvu xsg ˖-űcǺko!UѠ1QuTw0ÇiL*, 4@p8|[ <Fvx* k~U2љ3g1o|\tZ[5kHU40>ʸ[j%5Q]rsJزu+`֭>mZ- QQQʿU2?OOODGG+~hk#񈊼HJW>1ȬdDDg \$2nX?LjG] = >ڶ  msN}v5l $$$&bƌ07t`͚d$%%cf2Qvvllacc(wBմDDDDT28Ɨ;(`VL^'gW[&Ogb[MuP+Zd1\\Q^}h^6^,Y ԩ[ѶmXZZ]v;o̰pvqC}vzm7QNc""*fjsRqRb̒.[лO?ܾuAUFf'n^ aWAP( /^Q ػk;&Z2c0v4r(+ҏ<)Ib|3g!511kӛBDDDd,  @pH7/^B!"""22M8&NdA`1Uin10&" Ub""b-R(q^S"b`LDD&-DDDDd$ fc"""""DDDDD) p""""2[:nUzDDDDTAWiehиirDDDDT*1DDDDd[q8a;ᓝ"I DpY|>4knh|g\v]m;wFPg\E[tDT4$FDDT%a9O#1:x#GO?|Я/&NV} DO~VUadv """2؈-/?d 4Pin??_b„w6Svߴa-||jSLec"bTZpLDD"[MB"}vwqqDd1b""jk׮+=Ն;UZLYYʠ;%%Gc""I1ʪa MB[4+ >3;99Ν~~~IlݲEmJ~=شi|}}QF Xm966xIII#"""2A$P|oggq\./q"I3 ~-bXl9aZٓGED( ߋP*[\'$Joooן{ZLO*2/U8|k_At4c""""UCWP(@"ÅbVat2nX?LjG4mӦMa&<}>Ą˼G7Q!Wy%TDAtR̞ٳ^f8Yϼ8ն!CcȐ<ʈ [Ȋ] vكC+}Mi]cǎ%@gc?#""-.̘1;8aÆJ/SZlUDb' B +ޱeCk{ DDUP&]OCC<0VI2;+dv) &JY8DDU:WNBGʾzDDDDD1(E@DU#""Dd-Xb_6Kc`LDD&GR%8yR'2˖.F\\<~;r/uƢsV_F\LTӸAaMϞ]->DTnp&4?6k6c""ҪvWnwKEAꋈlllsy|񇰱X""*O"Nfpvuye _DT\٧(ADD+A+he8fQy)r>/ ,DDՖ D*'Fal^W1Q  TPd~dp ѭJϟ2 > ?wQhmAV DGı_0pDDTm<}HKӧ>ϱmfz͋]G.F.#"" /vfprTZr+ HKIDčh߹1i5青u6^w^Dv]A, DDUAݛl],@*34h'e^**c2*gMy4u10&"DDDXb '9='lm!~KWLDC*c""2Fe"J^餥&p2]Khԑ0obܼuc0K., v1ZJ*rDGEO`mmcv1'"*I`XXXYQ;W~ IqLRVdkLĝe8-  U( /,X@F$R)j}c?cȈ7ͦ+1烻ᕾ7$ܻq 2K qDDH!gɵ+E>a*1:FOꘉR9w k 1Y{"2,]I8{^H Dܾm:DAk(]F ""n]CPÐwHJQhѢ2SjB=݆t?dr,Z8]ܤǸ:brOD,`G7;8"3=դ7R" ܾrҲ=23u\DU;jѢy~xjf0sҩ}^vG62brO*Ty~Y7 IsT*)>Ʀ\ܸ<C|89 ztฒNvzqqqvUȜc=޿y^7TZI*#0;W+wHly~-Z 5)\) !x2N>بBfۘ09YhЬ5dU,8kɉZ*o -/-͛7(rځK6k kW"ܓ{b Y]QƬɉ~ B[WưJslie-!e/rR+X7L nt kWZ۽sO*dn )ekrqB/C" /3$ ƾISKY2&.-Z4Grڙn~+~7; RJWƏ_U* jE b ^X~u!;ΕKu~xde{_kl9}P5˽'Nڵjbbqzqݻ:k%LcDx}},33gΜ' BW]øSѦu+3 􅥥-$)b<@rR ӱh\r-Z5L2 dė>Ǡ4N~x o %*X~1grODQH+Bo1P(("99 D╫XJܷ))! 9QѨQB֭ѪU(voo텏w+HO@bb~pBK|iY.D)3uf;v?\\]aee;p]D޿ kkkr1Us4 IDAT @.W@*"((u~t3?(R`L</WO5W(Cnn2[!ytӶ\".\y=x#GsR.Add'|sAܣr.Yr'޽H?•q?:J+{]KDT*ƀ vvȓb )$ѭk,# WѺEK4 A$&'!)9.ΰƭX^mr \ԩ ܹ3h֬ml`EBT)Aqz%1g@*q\\ ?k&999{""EOƓo~lPV-ɓt [7J|r )$x1]aceHOO/ԓ!i&Ҟ'{Jn+*@H">DNvCBPn=ǯ_ ggӊ]*/6fr䞈WlËRݺu )<} ",4k 2Z5M׬i="7/nBnO2뱣puvFfSx w#GbIK$H[S,-@"+%A H`gcU QeFL㊮\ jwiM<° c@ h;pU_YC!-5m۴3Cիpv?\ XZ 66PllΏ3'":󉢈Z~~E$GU ,$^˿/@.uWۿr @AP>t} kjcGݮã$c&L Py^. 9cb}C`PՐM=0Ep2-WU./kחqQ4mW\PQ#_aieu>noa $%: E={"h5]  VA"}K@~~?d?!w$Pt\媴lX٠/.}xhQgjHu"br䞈Pb.I!˃p 3}iP[ IbucX dRX[[nP-= . xu\ *"IDTI3'jz~/|UPJ$SjĜS 'BA "rs S R8;C">.PR`XrRcr䞈<̘L￿2b}. ˶C!wAm!wd-$6~\i*akk:{q^fq Ri~.(S\dg@. 2;^*90fr䞈̸̇2^5RQ3O#%*@~`ܬ?Xg͟[d,YF KCMQ<ɿO`͊2Qa;>`7K!R D1˓+'C"y ѸI#څG,QeLT#2}*Tggg8;;@m \;\B @H˼Bk~Sg~ėArJ AQh< /Err2[ֲ`dsmY"LpVDj@{(WݨmϷo۷ܹ3:ẉ br䞈̣Ef!F-3[.OsLb!<"""""DTm~"""D|瘈.`110&""* ӥt K.YS݂bFDDTqI:"Kcb&DD櫴F 3'"2oeaeelL#B#o".qùGThzw`eCD䞈L%0V|Wl|LL,VZ?.^7Ý;wamcЦMFՅGQ5! qI-4\Ehh~#z&!J%#XaV-_cL:yM@[[[|S4] qFBJJ* ߏ#+;mZĻ-@.؇зwO@^^>ٵ>ءƌ~%./O.wg`gk>*K/[@m\^=pC<{m/,HMMe!58;wbз5;? {؇nj|uD`U_+žHNI "6o_~u9ӱcۖin+W! ܸy ӧL*6.''?+6 Bd$}qqRAkɽ羟9˖#"*PG~c2Pcǘop={N9ɓhwxQprtƼ&N}ӌ~\pmޮXE:7?7Gh!2qUؼ C,1g 99YcrіHLL—_(uyq5A&!zO{?ܘSQldqv6^p~"MII)1 -=]9>51<=4+-- ^^ʿ<=^?N4muiִI×{?E`Gy8[r(ЯO/pDFB]+D,;99!>!5'$Q"ٹ4 ZWQkgkk~}zoH#,+EEu(-W;7J$U>ԙ/>"v\'5 ڭ >xP(p?:7nVNdקHNNAfS|gr\k^#-=;?݃ڕNw>WNk2{FF&<|*}#>BǏžhР>4"#e٭BSkG8|pUB5פ0/Orl'͚=ݡa@>/5޿Xu:|psq -\~}zMV<l#o%;`YwGT zW{CL4h e^O""S=i$.]si p+FQPqZ qn%n(K+W]*V(HZ9$iNmn@f$˄A (C"+!(腉/RO!CFJٻ+U}ҹQ*! I* * !FML!dq=2%ȝ YwW޽ {vYloں,]f !dq=e8˿C>!$8]& ʙsXpBRI'* 8-E>/,QBHYY}*o7̽=!&,":м 7EDg2:qRN !X^[_]ry$! -YY}*7j7XTАED5 !d$,/}Zk,(vѣ~l|j@$Eh/%ظB+iX8 &HHi/hxD%Jm v25JOf; ǠԨqҟl+!dԉ1co* )\tZ 1:N(p'<["!8+^m]JqjPnn&? AY)E6cX҇6c=)+ƔbRK c3w2%WPv7nߑ־GqTRZ5@N\U묄RRfwB,N$O(x<]5A1tg]J{15)Ίq]b/BK! ,Dݽl^u^2OOUb9n o,r^.!3F'V(~D'ԫ_qᄸ/2 FtH1Ƒb #1Ӊ1,eH<|X$:Y@.}&R-1 R#psv!3Bb8=3Oe?3Ŕ^ qdG8!Ɔ: OX,kgPL6+Qqݷ+fua"2pGO!i@x+QRMV6Ss?SC~ (Į6 $g? IENDB`sqlkit-0.9.5/doc/html/_images/year.png0000644000175000017500000004456011421620210017153 0ustar sandrosandroPNG  IHDR+@MHsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|l6ɦB'@{kC!xmkA]l!!NfDzn%ޯfʙ3朙٣ƕgfh !w٠ԞwֳO*j,Մ\BfYTVoiZG0:\yf6KE5c.C5JBQB(=ͼ:Zz_XFMQ>j B!뵥\YDxl2w EPeRQU+B C!o2edޤn؞ͫ_~ugTz`U&sEcEMa!!TJTI:YU V9hyT+\TI7,DkK}u۟^SAᛦX-f5&-7bFTG@pԗUPU+fcە ]p_qɈdtuj+E@GGWs02ߐܛg=݆ZU5{y㺏zo.K:UUX,֜"z4o\OyqWg`UU:5V e&E5S. kGbJr^ CP_ 7-%q keK`x;^8tnh~$((Tmh*l&yj1;?j p2T%h@ӰZ-X%ECTsp2z:Ogۮ%L"x@LՕtAbL̽meC[dB]"@MEt#:}*I!{r}c{xk-oZ 㶶>7~w,`4z.Vg82~,|s//]+wM8* CG0FֿpIOc:QWc7,;)^e®rshF|!j+?~>P^۟FsgtZvv.ә%C x~;u!vN.ZQ=t9NIqZlZ$E/Aq.SϾ7Ƞa;MUGiH^qY.ATO#alm|{GCe)I!w3JGbD}@z欌fӋr>^=3Uu<55F_TCtVS)NsZFUTWUy ]*2'=paw'#s g;R`oDFES\DfhJ( YNߝNo`T%%Nb7>1Uoge 3GQ_^EQ@%-?0<>ezrr{6;=zJKI|BG$Ի=u+"WYvYUDgplBqEe*k6|Vclq<)扂5sn!:Cqi- 2Bm߫I cU 9Ͽb%en@Q,VJ0Ӟv@tB:˥7po("(2X̤v JnkJ^GjӀjMG3FQR\bCxo}_ Ͽ59nfצ3IMJ(uӸlc-ˮ3e1;S&z-\gAq< qn% GjV\L. hn^tDFyn~|oZcKv:{U[[킋Deĥ_|Bp7=_ & q݅g[0WFscp[O׾n 1]0پh5W<= 67ΩݹsoynY6l,pLց>6~-G,L䷬9|v:EA]rj0Y4b=9l oHԹuh*+7#Ԍɢ=>1tѻ4w e?>'*@B :r<ۻ\#ŕ <$~C):N4[pz扮陉=ۅ]מS=bYذW,{]9+>]rE-`o=wƦZE (@~Y)ME;E9o8G T4405= m*:^ۚF &XiBl[| SESlh4۹x==(ԭ'vZmc9o{SYcNHn zമ!D)VQU@`~3Bxd2u>nn8+۹C.S7-D%O7yGyJ+,*zq٠b?jB!z_4iO;WxtvL2YL,B4m! >Iݚ}Np2vQB.וW%?6C7gj !mčg^ ((o !meM;"BqчE'Bq[r{#B#(B (BBqRJIm[Z[KBJ]jh4RYYI ;hd2` ""Ȉp0 ]]>05JIM:5uO$ MӨha!Gseer\VKhHqMJ@vУ; ;OHHc$ɇGLMӨ`=l]c}@ot*-#9!Az>AGID@RL,H~M*֥߰{Oh#'%57~ѭgL&<8g fC= YoLz=gԸ|O<\{0}pQXT|O=iCGһ@nn*++=m4^y EZz 5>6uSRÏ5n"=gڌsؾccoݢi=w{+!cj6 2o;5}Xc2q}HK`Ѽ$\v3֏C%_ӱtQcc1L ޷]޹l2aEߟiw.~|~\fskhM6ػ{iO=#o;>絜>6l\>77nXkdޭk/۶m%n򺯫׬囥K8ZXo6c}rj>C6[S&3V=;j{*6o{+!cpыb闟?rȉ/xiم(*._~`W 6eaگ7մMӨ$;{/_ٲȐPtAZVTUUtni gUWUEC;tJMx1ѣ;aaaҍ:ƪ?NiCG%kWˮ`=3bxnZLDrr_6;Xqy7-L;VѵK1ˮdo4x׺)ilX(>g fώ-y_y[y2r=G-k>6j,ݗ)g6.9ؚ4M͛1 dг'ZP",PQPT6]UmiKe%J`  ̈́;E8)w4Ν:9~ܩ#EE^yE<,|"#L0moO<Ŷ;ˮ^]~~~N2Sǎ. \ oXك`0`Xߗ([ 9ӣG ܩ#4}XXX{*H {Y ]߾Xt㭧((v"ӎ8CQdZS[7 "d$OQqqqu}`.^?7^} MXosВ;y7f4TVV1k |$% tO!!mxdžԯk}UFBB<m!6}׽!b+_է9Gȑ|ˏ1>yt^XTY9b+0ޓE?PTl BGOgo* hrC(hVejf3ֲ2t$쎈 =AAA-3w?eq-|]  j29Z-CuB\<>M< #uf]9|/MX,Vf08LÌswݢ-BL{f4۷KT'ݥ]Īӹ :D7]Oǐĩu(NUm/UZJpV| hV2h>ie>V]P*~}l,' y/[n@'ppπf2w2 !m0-@4JJJ ?zU}՚S NKsBPP:v;N=8~TULb],4jEGD|tۮfw>|YVZMUU{ev"k᪭wkmJ-@UUJKKRZ}9 _Jv툎`0x 8szӜX,EDeeX"JKQU?(}99˹[X0o.al߾xM*94 UTz[Q;>bEtt4 $%%Dbb" $$$O\\cbbvMTTDEE9ᄅnV FB++zњGm⋈&8(xc5dզ>6׀)iG?^}3eY]~9&(syqOgϞL~{d9OZ[cwc7vp-IhhF _3P u;; H MCwZ,h=hKXYkxu,j)z8,keuq٩=O˜$990|}s~$"##x`a#IJJ$$$믽mO|ysi.W^Aee%2mm ;v(V^KRU0v@>3oO}v 1un-rlF t| KkkgEGCk4dUw3kc4v@ۘhM5Ю:x;#c,k|b/*]x7n_|vꬱ6vpƬߐ[[BEb:qF|}-:b@P0BBB\-ka,^jWAWIHg]cpbڒ|թ%x;tԉ}CHƢVGtliiHCzF璒RmuD((DEFRxڇhR+*Ъ1LTWWSUUEuua4jjj0L.\aXjAՊ:e aa.C7ٻ8jOSܤt%p9'L)!::J(kW>||Yx)F#i{su8iȠ4f VZ6;vhYl3}T{_uf w׵U?<<{AeXo V+Xb3V+ѣEE.]zϭEA u!!"#1őӥ ?tBi׷ HACG%kWˮ`[cݎwh7lX>z1]tq1qʙ5n"ݻpig9ѐe|S2RRذfc=Hz`m>n>z/~n)]א"Z|i`o)ҡ};B9оvdӶm ;LBbL&fut:@t(P*9oIIX!w/:wHTTAAAt: 6:+,,SD֟Y2kS ؁kk:z]D h0Jdz";g?'!z\QIlu555U` Xp0B 䇅-:H҉.]h߮QQ !r5igފhe8 Y?c6f56|a] !E rߒNRRi{1v(N1.G|t9c OL1<6qw&;d̨QERR'wYә ),*b]38e $௭5pcHnum` z}8DӾ]{jL5k0MMf]Al&8( zkT}!֮BњJ޽iG|jc# b<.+]B!N*2O`k!F'/?MYI![£b 5x]FlܴhMs! (:-t:EyB!N V`1|/Bj}JJ صq͉oBiڼce%mJ 5jBawf L?tY;*ɾB!Dsg`3h^Zo0C!hz' j!w1B!:zV+O< I~λt^="-=!Go~̨q٧?f'<ȵ7LwW\}EE.G?^}3eY]~9&(s ڟƛoֳi&Mz`33٘L&!pY.yضm;_/u 88e]b~~9XVi*N2Yv̻YO֬ݺ1?]?شn5gϘΕ\/fLz?xLlڴ|=N{ꙅOC^a~9!K=b+(-X,ڽ3ϚOPT\LaQs?ިm`&(8zhN%5L¢" xtc̘>B!h@r .*.{]w18sL Ҩm?1/x3K`РFO}y랻"!>qOgINJQB*/! _|{S^.B!ZӞ44Pm[hj9$BtvjVMUmU2BB2ZX֖)EUhr 46dl67{%NE}(?BSB2zB_ٜd$-D!^q_T=B&ϼٷ5ϾfBKeyioCnB!@B! D!@B! D!@<kڵ̵9kS׵bcԩK+DkNrz-yf7smzNu] \BCC\!Z[Cܹy999tܼy '|\g=o1~Dtؙ{KILJ&]{^zeǼ2:wJPpQ=k91!1q'KxwitԅFт#\u|\{YYYѽ^3Lڵ?G<̑ÇxwCb&ЫW/6mHޡdd +rCII `zl8L6nݺlQ&M>QFg7kVbm}O>/ w ]{cYYYF>#y-[orϽOEQ0q˾ϞO?\z Cq)3kO=z6LdggӧO{e,e9WdP uocҥ_1iDL5՘j꫼|j1SM55*vعSffu(//g{ҋ/mzs/˙0AQǯ.{W_{1>;oW^`m/RKZZ.e˖棏;K/Ĭ/K/n"22N:ҋ/8_}UoIdd$W\q9}LK͛oN׮] ^&OįEϿ9gϤ[n>s 2إ:v҅7F gٲi߾Ϻ71Xv'&}|jAvuNVVݺu믾dРLz}yڵۺ _xi7v,[laŊTTTW_3`@Ə.Hӏ8p 'H!P[f||\vڰa#%;?[22ħ}믿фٺIrm˖L8ǴM61p _5^{:$%蒍7vZn&BC0L>В/䚫3l{Ϩ(>N:mۗ-z3qD~w:{뵟׬aHζlJDD8?09gsۭ:Wݛ[Lz{zն'+NaWcWNs>ܽ{7Ǐs[o!{ukk,Y%UUUq; {cTU_/d2wɮ]OgԨ\p_^uocr[m[9vkֳ_V!Dm V^M7DPPcmK/ƍة3)zҋ/s]w8yw;ݛSNg` RSS''+Wy 5W3t}NM'&6oaÆ9ZYYYtE57o׬=:xkߑ~l߾_~dXc;uh߁okK_o%)=3> &8-zn!W_}-1q4VtG+yRz{2i$]O?k]|SO9s!>!/3`0xl;ɓ&/rm6uocpbxy{u򵎻ô_XAiaݮH]]V:RScbov1]O!^''' k!5{jEU?s+:ۦ,~Un:ep-wml2JJJ[$ E*}9-zիVvu'1gorss6l(gMZ!ZKhXiii| k!N2~b֞]Q!Jf!|BBH !BH !BH !BH !BH !BH !BH !BH !B"cw (&]B!e*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*Be*B4) D! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B! D!@B!зvBBT@CEM=W543 D!'}Tx੎4MjMWL2B!@B! D!@B!F!I&=/>yuBJ@@#Oz||2B!@B! D!@B!F!I.{&̩3=SWPr(-?;LKؕYr$BE{DȠ1MTV׻ D!'=]@!!#CǝFEEe4S݄B w`±c%LJ|cfB,4MEU5tJyyqBJUb֩(+u D!'㦰gzJ PUݢQRX1neT!I%*&=q0k';:BxD4{\XY:B!DKKlG\b&-SLB$B 億(ύ77bB!N-} ײR]mdqM5@,+)"{VHdl{>%z:Fn=VS!'Y[gOޮHQ^\H|^=IId*uo4j,!'MSf?t:Rewv2ny*£bTۯ@U%┦yȀ74q9һnZ┳;+L*ʋ\SkҜ۹(uFS||TtcZٽyE7h5uiZ׌4TڨB*4?[yt~4l2c*5Bh)I q4L `׀b{s7e**#33#z.ĩ.SҢݾ^7h;S{ZJz]Ns竩U┓iVk qTKL|FND)npjn~b`P} gMq9O]WHn<р.vR1MZ% (B 6y\}CGhށF=T{0^gtgЈ5TT5-]=zѱ{*Fcy2!qu{ڰB*4?[)QSSMoԼbXd e^\e*NAGd5s!N@,(+&,2)@ڇ][-mQ ~4T9(8!֦eGٻcf6QQ#!!FX\7S5eB!ZǷKW}[䋽G nd)92:E)IDATH!yk/8y^ۊB!ĩ}B!)LQ!@Q!$B@Q!]B!}Ojo}II5f<={iPBF~# ŧlKW.S!h#~[οRzJ0S^O^iѥ÷TVV]q!h#ΥS.J :u‘ùTTTyBw$Zz$N7ܫB)EQ!-m=BѦ~"F.oBmC8r0 Wf$(mI!eeE(;Fjj fO CBfD1h(:@lBB%m!k4l!h*-$5Fܘ/m̛+2mqf o1C6Q enC\Ν+Iݗ}+ٱc2:c.2N+ޤPBѦ4oU ˠNXSvY4,0i/'gL5+Yf%SLK.%'gӲ'_~=w~An&׆sBц4bU9͠P RlaY `9uk.b㙅xn3os]繅2zfgI !D[ w1JCw1DԀc^PmW4AzP -~YWg9d.ӗ-Gݷ"_f߆@BS= ~,VjT PzVT,53VL fFLMb9~+F]%ՙGII˴[n^Xd 6FQ!ڐj-b')h5(*(j`x ZP53b{aBSp< ]5:u"A]G ", j}m Bq a~ Ţmj P*k152jc8}ffߺ ŶRZb@bňP_5/*R!$bQ1xуAԃ!@Dlhр J!`@[YC[*Ȳ۝sdf33L7K"ُIOj+iϼڴe.[JdDV^LJˢ9}f""^bFŞn>J$ƀ17bstћJM?;l+U߈___| &8ȶ˝âE 'Z7ɲOf7K("! ,ؕ..D5rg0-nI(IHפx{U5Ʀަ̽o6oj͝3/vnIe>(ED$;yH `2zsV~opCga#!{cأ8kV ;mfMNj[N@("!!H3}:ȁ?By3BkD.3Tf G7 ΩZfg jm=y"+%d"F1hiCWE3,'F5(TLEy9i5s7-q"53+8* DﺓFLɶl ~l ,|>(3FKU$PRRԊ9)ED<`n'Z\Lea4"b6X&w-2>g P(6pNi0u]…9osP xHr `6EEEcx@۱I؎͙J.cg~u(-+U_YFFi锌T(ED<xitn?Zi u >""Q9 9|Βp.iܰ2BއQDCjfJ]ʸc;`p8 D %s)""QDDP D@(""?֛:DDDj9z,ikB:Οa =6,7gM"""c*Kn:|<Ӵ{.0q :X[:|ѺI6QHN]08yz[ܲZ*#Pr&IENDB`sqlkit-0.9.5/doc/html/_images/completion.png0000644000175000017500000003236611530470276020405 0ustar sandrosandroPNG  IHDRQn^sRGBbKGD pHYs  tIME  Wפ IDATx]u|G~g$!FB.w-Np-BZJ)ł|8B!y~̻=SCo<<<E' K<R!Dŗy6- ڞ(JyB$ X0##_D6",QXB0!5}LLĝgʕs01EA9YCwWU‘ v>rchk5_ !=<||~#p-2^b^FcAgbvI((s볇`v,o|CtOo$Յ}ͦcE³na;W|(zv)Llqێ^jfڷ\Dp.U׼DtO[`Ԟ͟\-tΒ/n͞MXgۺ;r{ufǞEhҳRQzV4cm>ՠ}g܌MUgF |V*=tk;Ǩ٧ dKt;f?o&av$#f[\\A`yNY.k\;h԰MWwr@~p5S>5|S\lnVS̀wLb'lmϧ?Pۆ5mIny K{jT1W6y2yMq7~\sԐYmٔWx;QÖj>vK{gIP^|vМi6 he7~rGUĉ__O;63֤,yե~j>j>ܬ3{jV6,N>4IqXyKOw/? FyQGLR?_0}]~39,CqGg|w7+߮S2{P+gY&ah:׸ZW[xNZ5+6onS|B{om&"E@] KEFBВϷR7+bJT'<}CYSi;;LRdr?$8.^ J)t|A`J߾]~Dڮbuj]g`_wu"S tŭ6僧N5sq ASw k`!H翕$WI4$;,IJFlN3@$8!pSnaeI}^jlR8w>QXyZ>Y<\ Z!~cC&} fk{ڞU;T,5K^unj. $v.IYqUڼbLG26 QSsnHlbbr}X1GaQlYw_]X6vگ1&wE= :}ϪǨB#k =kH/?x(0&Yǃkw)=۷^ž 70XcUأ[AARuo\q`3Օe;ם{"*ţ6?D|凞FG_M![.m^MŜ(Ijg>{}{VnDo ech'MPh`&10wU_~#J֎cV[3ni2ŪiWW.9>wl=o[sO31جȡmrͯJ2w4|_^sM2r0i7>y5 q4ٌ7_rԣ4ϿWޣ29!՛;i)Ԭy%k";$Do7uc=<mS]Nl S1ӱ& / wJLHr*@=ECtB0|xJ|\ շ}x1˺xx7E]E+' 9CI$DW,2,G9_pZ}SLT8!ٺήBR_@b+x!b8KM7)( y:i#yyKo8ED<_^$Q/$L9m,V1 BHvr+B@$65!536 Q( ' / x\ߡ:4I/ɡlJ&<7 !Z+*GK/h &F O:Ɔ"SY{'Ep7&Cp!@?~ 뷘_` }XC0``H"$,2>XX"d8KDabґD9y 󶤥i鴟EHَ"`( X EӉ!ݜ|Ĥ_j,^9K~CN0a^۞_&ҕO!!1na[7qcdoe:5m4)_jŠfXoΙC\Җ-m_ ɛ&ؖ;U!bSUEk`"+ђ}vfe k}UʑCXY!:۷H`Zb&S-gm"?+pK){?YMtD.td U lgWbn8Фx׳:vչĨ}9Mػ"q ·[e /o{%Whguԑ+oFY+Yag,s;Z6ya]-,gPEvDbrΜ9۸q#J1h@;K J߇'S%L2Wl:_iK+$2$c>ϋO>?ָW5O?QgZul8Ky>o/?|8|¾Ouߥ{ZOnFPܶ [~qwfl5jFJZjY[ $q{f͚*}ZZ)vl_s045(Ƃ9F/dD z|2B#^0vh^YrW7K|pql69$ZUr,.ٌhh.u7owku-\CEeyY#G۶mR)zvmbn/=zC\[TBU:k.6Qcnyh)06gHa7<էz)׭?/zPK*%߹}C33J"^ZZ|V4tBޮ~ңIR48Ұgw$Qy@qn%Q[Ze^/@Rzxu!"hUJYiL{ϦM?tWD&c>\ A16M8Ϲ0mIm歠w%a]}VzpO^$>xauDֵԔn쾁G zp'NƼ<~xګ8=mw ;w8b`iaU=ڸݥ¤s'I((ݦM 6ZҾH^G0LALDZ / !E~^igh@ޑQ7gfmY"ڼjӊ3l`T-V6~ւ¹Xe(MSڥNcu]7}=Ԧ[-޸m}c^B#|_jUJE~>stn 5 nmcB`Szxڴœ3,-&]?8/a իgc,W\9cƌ#Gٷ_u3R~O|W@udz0<>m!r{|#0b$^%rz?KPΤGիz2=]7+nj3f̘˗~ԨARAL$#A#<'$M!`,!+$`>ݜ// `ήK^"Ġf#|9[LN~e|< |YOS^Xr)d7e2$`bY]1 #w"? î@vqPY3oqa4Q4i"͠=QӍWxZXY 1s~ aYb:0!!IJaAea]Qp0s[1 RCȍMJg{C0Jij6y:Vτ7]E'ɰAœ8*Hܬ'b3WLEc0N.\ .ekU\~ 01 bb0+A>aXR̝e*c#zp4-%oX3k򭭊Ul$I%A$ &X1/*geE ! hZP$| ^aXXeYcy8;>\=!dl '/M.q|!s`,/c1wRNފb%"d5 f*EAKzmjJQKVB \RT*yBypK'-o)emT0Z ro 0TMv/z7=$yEa ljq6[}ձ[|l4dl);8KXlFAúkgRCxOI!K4~膤BtH㒊7S"/'vP/~>֜~U37}hCtu>5kl"Mݺtq| XZhBsogP, @v RQÎ7]3 3&O(O~դ;D. n'gǝ{/ l:n>2WUQ}n!H u$dkk&E,\t$L EaAg?$ڔ7?~r?0M9,+rs AMAh畦9 $&ƛ0i61Ŋ " `B1U(JI{u#Jiٯ@9jŁkY=YCKqp~پfӱt`Y+ZQGտꣿ|mJI@eYZhCN[ G^3&uˤ Zzy{,1"p}w"Ɂϟ$P>~ _vעm̕˓VK&Nڷ5#oXQ5䤩h ~]1앒B:^PKJ%%1JJR @H6Eǝ_2{[ْƳՈyl\fmy5fy\wcHfn޺if.>m# cC;X$8l S\Yi֭甉D. o@Xvpdb sb%Gl~?l#eŠ^4+'D8ʂg]]o9h|V|kWՀss*s(O)>w2^_@fvH髗s<3L3̿298++Y ]*:g`g;7=7Tł D}O#XtxlbŘMJ+V~ʧm|ܜWl6hF{oUue Up.]LjJxfÝgXw%>۬R"bժvsذYMg>L޻l9wc&\Ph~^V.hL"nĵ9sU/2^2dW,;j]9TȾ?:pu+hη\oy1qV0ڌCoTJj4ӆtߡN..%uCN< cj9,޴W-`-0<="R`!c!#! %q갳갳iM633W K +e,-,U*5Bcј\}~BhƣʣZq̻;JpaRQ;U\>20LpxLD&K` u,|Gu.I*{6^G;XkkXUr͡ ?ꤍ}'V̢WRr%}&NqQJeCJ6 ^ć օ׭&lÓD`XnʬgWJ5m{Q;qot'{l".<38{C?cnԇg\If&wd'xR*'s=l(wi1\!ߧfsvU)/O]b0Tw7{wُr(%'2]jIDAT ebitͥC=|!6O֞7n1BV1zCäBa9!dHY:*'r]ؙ#¶3zXř g$0sv`cn MLLN'u61b¢Bٲ !'s$sJtpwb]"ڕ/l_L{YVˋwGWO82g;!a2}3Ϯ=Z4B~-<ܥc,.ϞnHDxHŽ+b,:.!) PJNA}i7|F$eɥJ e쒧unz^ d'Rt78iSRc㒵.>)))9)55US$IJJNJOO[-3~F-]5ɷUwc`ec6jU~YYaVcuۮۺzTY?_F廓dscU ]9kKTPٸ)=x@|6a!6!\§kr;Vn{2l/?,b@d]jt_22J8~߹i+b]$_(1XA(IcA9lF`0cnKrs(d)eK:˓Kh4߶[mxsӨv5_GC_QdyS+~]4U]Mn.j-˓ˆuh4w*ZɁMh4u ]q.R@ :zU#;6޸׏ 4b49>mԚw<g9`*t`'^flݽ!}s2 7B.h1|/>dwW1]I6d=Wb[8_U۔7p32:uk5]3,_02R헸V'wjktܼ3ms{ 2śt҅`m*^PyF&ܖbtzԽָDݒ օ~I{4zo ,3;l Ϲ=oTNbtMMqz{ ] R/)F+U(FFn(Lҵ : {J_&*h_<\K.ϱR6yRy_0F1qj5oE}/m\kt#s*ºM,OV}k"u?ZEuMc{ y y y y y y y y y y y/Zv'\;e FSgܕT}@hJ>t]]DAj]g&h:,~qx]9*[֔eglF:U2[v첿ӺN9_c 0%Uٻvb6?s3KKDzu}l,pLdα ,-Qѣkh4՛ǣ4JttMc{jh4M]"^cZ7X_ˆ3RʕQ_s]KXshNڃϧ]נɬ['fopc|cFh9[gY^NK A;h4gJՊrDa|:MYGd26J%}utc>{F.<ld_G6_Ŏ|@@@@cC[OV߆yǗu5 ܜjZhl_I:zOohT[ֿKq??=so +fb^t+p>),uUN%pt6BF5Te/UB9yojT[/uI$P6N- Xp^Q]Rifu7iռ^H]mesrhϛ`Q>ג=kQioNk=:ͨWwZUﶜh|셋ϯn]տONH;{ed2K:۫{V=OQZ ebicZ 7IbCmX#LĐtWmVaۦ "4n(̍hHC?aѮ,<&sgDG>t.4O84c<~WБUgkF\ x&zC9_SSmߠQN-?*Ҳut;GZ$XPےՙu9zlv-[v!YK :=-Կsv?%:fUFշAT_SʲAU/ժY cuT:PIⅩ#.w[7ٗȨ5ŗ,adU\ ؞"_#( ЇԲiM~}W>T8 O#T (HQ⛍C,2 [sZt#tHчFt7\I:QrR)ʣV:^ꜮG?b/dIT=8՛2X,U%o;<'Ka{i8)pnD >-5ttl_Q:}- 9nٶ00z֝͘ 㗭OֶT!SJc3W}߁l}mߍtuGѷ#ť|ZaP۳`v,o|Crջ?Os6Fȃ{ȳ~Twˡ5.gC~a8bƒ( -aL̓)Kfo oÊG{~oisWvڰhbuSG,qb=I'ӵ}/k\,-cdj_gjZuq˃|w%u5̶t_oAoڿ Rs;/,y͑ 'T1 =i>%^:pݳQ^F8b;=h:mVbYqTYRjZ( 2r^ϛ 4)ePlueycDJ_"2-־ZiW{휽p_ fLY`V۞x^yU7_Wbk[j#ϻ νYCZU4G>׾㯧]Ǖn8K%^j]ykd˖s6cs Yvlez  ($Ic |2yyt\ 1WV0}]~39%2mФ O9ܺ03,)+W&Ih*. !g̬B0 -omŒ]=OxJ O986e{hއi7Cl *5~v{{,V+옹sWy 2<%A)B$u4g>?:HK9b<¹\:/髆6o-۴ԳZӯ_cϾV]4ůeݖ?L*f@pL87~E)ws^iϱB`YV$L O9I8oJLx{r|:Ty-Lil=P۷ɮi+o!Z! w. M4i]zYW+)ڕѮhnj%KJ,5crswu%ݺOosv`cn ilPXضu&~yg{|rPƯ.wyޥY,q O|apC40~F-]5ɷUw0ۮۺzTY? I.%* w(lܔ< E޲`v=p}ca=vwh/ĪT zy+x߾=+]7jyN?ӄ!);6{I,M5ؠL E=BcY!roJxJ'۴7}㟟}l9Bwy/;# ~Tl5?Q~_~5t| !a >0FS& SD1 }N؞rw~LC/PoZaBUPIENDB`sqlkit-0.9.5/doc/html/_images/filter-panel.png0000644000175000017500000003345611421620210020577 0ustar sandrosandroPNG  IHDRO`sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|&ݥ-t 2U/~)( Awin MW66Фmh>&|޹bPB}4!dyҲo(1Bq t/(9BfHƀa0 a{M P҃D1Dh`/RzǏW#qssԏya/iB%(|Kt111f̙3eθ׎ ppwЕID1ih Zoh$ߧԼv̚1 ҲABe*y*HJ.Ԭv2JP(AFx^0{`绕]jZ;eP"%(%=DWweh}'34P;eQ"EC|&HXmez6iz%+ڭ{)!(JPtl̛/[t;{w,w~\ݽz@?1U(ABe"zPWgcJ)ޱ =zzjuj JPx!%qUEfJRvL)3]^;eI!b@>+5E!z&^}]kN*|S{^}y(AB7u=#Mflm٨W~JP""=-LӞک}Ù!=(cuvJP"Q{(P"TIJJBtf\REKMkC Cn+Dw5{16!8JPJ]K!&Q"b=I޺jZ;b'rҬa$ b%5,>BHCC|Bl} Y1B!aè"'FUj|؛T*L&v;WClL&CJ-:~YgKPq<*MV?q; "ȑBj'G\vHU憏?9F7cۖ+^W !5YǠ,P=j|3NN:v^ژ!:kܿVʢAd=;a[.q} S`L Y+`Y"@) 񕑧J69I*3):I3!H{Zrw029: #0TP@(.ʃPqrv*...&~RB-!2 zM!JRݰ^Px Xg+cYm|Lz `$D6A IJ +IV hu1\3O aBz8sWR*cc!ur-wO@ 3o,E2M={C7YQzU*0, 0 m %(7Mj$ 9<\ƽ S𺈭77$FF s8njٳgϞ1zՊ* 0/jalJ0`@DN`"<_'!{#Φ8뷞w+, ];5CT@xrT& 8AA? BרPUlL1'&bڌzU H&5f0ʴӣ4-7EQʿJFM-xǯ)ja'0 '0h -/Vg_EN  <rAVZƔ)S._Q(A7͛3~j7grURr 뮘f`jT_C0-4?o3hBY=P '16>ڡN`J?s܂J%իWW\ eNe*_CҨ pz}Ah|~G zP8&\RȁyD~?*տaۮmNHWްq3p1t_4n;M?bjxO8;!CS !AN@Ǹr/x?:W.`wp?a ШQv j֭EرckbΜ9Sԩ9s`͚5.ҳe- yeP+Xͥ}QEi7䥟-Czz;qA~7S`-m^deee!h_x^{NcpX$lU* ?p`9t|F7_{-AZr 9zӇ5{r*Obb"{9/e˖111xb B*TY\pp5o[HS Qvދ>>ӧ!FtL2YW>md 6&O|`; 220u1瓏*Kezju a۩Qߧ/E:x`9')) _uJ%tݑWrJ/K]T7u2SwBj\` d-:+;h>[߬_}ww7Lp2:!QU.B, HyoZua.zr:|0͛7#""B7 JčVk83c n<8N.nBIWWW89ɠhn!ng"$WcHmҥxq 2ӧOq}L>oFE0ULm*q~׏͗2 Ld|*B3,t]?$LLt4^~:wG-//;nl!o "ұM歞â%c9EH-9` a!wSU{qm*  *ijQoǨ-ݻW9ʐɜдA-\[ 3^#80^\>}cZ @Bh3W=$ uйUmס.P` ppg z/B*IN b7NB!6Խ䛖hиC TR AH= oRUBMKa%kܬ!"rT$r:!8R['x'7pp4&|Bji#N 7+M7Jy 4joA#qxz琕ܘX _iohp!*At ǫZh|PK`,+wgp[Q=^@PUx-xSp^r vHbqq r0 0y!`0!ԭ'v|mB#omCWٻm@dݷvbF`J(U3՘jRUF`=(m3m;tE](..ҕ7o:󚷎4o\qW۾>=u!đTe|rez]2mظ |4puq'}֙9}*| /^LlniiU)wo3Ù镟9?-둕+VWβ BcP9Fz]:!uEXdeeg8qN{D;/OOL?{ԕWOyK@;w_b,;Q"82gU$'*]۶nF%r$0!u!Cu˫?:s_g)!$77o-;x@|4ܹsj\w&ԟJv T8ժU Z^^,ˢXҽ8OW烴txת^OO!wwa{#G =_EXyG@V-1`jߝԭ?F©K '79Xrw+oؠ>mE*2s0zu!9%U^=GW+RNիĀ!q)5˾7f4.7OH0fH?r7\J. ƌZ=`(z ھiOCǗcEhx CGE]z8͚>CF遑o Vz=(Oƴ{>Vx򉺟/~EV bXB 6*l~Ww/b`hmBIOcyP=(+jt ŧ;eJA1sI̶)=BAe8=($󧎉!vl`OLt9!đY6fe!>B! ZC|b!đLPEJKaWWWeK>_C Qw9pq$ !+la_~(!!Dͮպʔ !8G&KǠ!$JPBl%(B!6ɢxޒ͉?E!ڲkW.!=# v8 X) @ĵD J$wn#q^u7+MmO8o^Ƕ-!W8#,C2JVCX; @R)d2mZ;t`z  1+Ad#?{vnk^= d2$©G;1?eVš^Hۣ% TCpHBTtG\vHySq^ %(p@ ʒΝKBttsJY8~x!-Zv( ?܌ -JPbdyrg= kADpX82%( 2Ǟ)JUihXX J-r{?iuÈhР 8@?6_PAA~AAq [MBlDgYPv0[m V~\N gw}ȐHJn16$ebIC%"sкH{ی5Li=^}v4p1̂ PB@]<;f)fm6#s!ك3n<ڴn*:_~akpʕHH$$LBLt4Ļ4tBPC4x &=?v嫐A XT&i&4˽z+rJ)?BӘ6hSĝJ[xSWD=]{ϜϿlCuu^v]qXSz5m !??_*TstbHXC  6TO.vݎ8EFE"={4Y MN/g`SRv .Jwe$N~ްIN"KgL1KWϑCоݸ|[8v6['=0l(>r?Oݺb嗯X /a֟qQ(r_DZm g1^ǙD>* s5*  /ą_L%Y%A}2k쌷å˗ue;vƌS___|4m vڭٳB7A~~>fϚ7K7mނYӧ" xR%};b.h Ž7oaoK4jk`ͷJF¤ J]Y_T_Χ!3:8 JP1(/OOkBw96$$9z[yeL@n/0OM̯a_êÿŏ!adu-]8kT~ Ņ6mN@wR Š.fVZU4BNFJ=RRR]Zu/!V$ْ=ztC*ƕV0kѹk9;;݈h(n\Ad#JNvJ C!?KHMG{N=HEpHX7%-8$ iRT*X"!F=(p'& NΝ3k}㇄X D'+-G JD)-N(gJnnn%&O';A0"4Eɟ z} *$8cO $A@=(q|V T'$Lmₕ,=ӿ]߃7rblHĚD{A JDvuz6 //| - mkHz...h"? <ն ch][QߺcOu۷oAӹ]? a,ܼu!*8 cbv6|`3":JP2Mlq TrssqI|2ĵ}Ro^{_Zg鼼<|f-ƽ.UlRpʕHH$$LBLt4"E 4tBPC4x &=?v嫐A XZ[qh*v8,\)Z=?m؄v fxWo\|EW~7%#F3ѭhHdeg[1tuMٲ'Nb /77~y7#7n]2c{{!2*\mrؔB!a݄hYΝ;>`YGsDWߍW3uqM(p Jpv.p7 *85 bU/RRRQneezTw0BvpssE~~>ƴVb,9S0 ի>DV_X aW~hspuuE`@߸IW]GM0z::w8o7"#@4~_Kc>SC5xJPecP{o.qo]٠0eL$MFk1>J]\T \'RRSOYcl٠ׇa}HKKşy/C*&;vÇ>@RCR#)oL on1wTD/7Z`.""*icElNmDC|<=bUΘѺQ# o"#3ubܘ{٘3o!Ƽ3~~1|v+Ǩcwƍ?>Rڵ}.-}xb^zc1pk9M8;cꌏpN2 << SU߼zAYֿ__=v6nF~}ѿ__q@4p`ߐ.Z:iS[&BL&Ōibƴ*H$=rFQ^4:/s'wW>AՌt}X<kRmR>.N`*.c*,Y0X`zHư,KpjYuKk{NE?ulGF;#ǠJ9`U hc#x7˱,zb!hGcyL "$(ǽ /e&$DHMC JDnoiO/"(AHb;ʚNHhRCPm 59͛[;BP +eQJi3N+3{$ 0) gPH5$# hbH:v[6[^G p'؆bq8D^zȡDlJNC' ڶ:pvvvH8JP"j0(JkBLJpV(jPqHDJ;8B1%(]r ~i4ggJw@DTckCC%;o/zY`vhpul۲r3Z;$j; T*L& Qkw`C^C!&`R4l>cέxm6=T+ɐvSdEDJf=!$QIڃTUxsPbۂC JҦTճ8.b히8G4c-B@d Qۣ%NÕ\D ʒΝKBtnwJY8~xfrt}-Fv(cʾÛ}@S@Νm0𯍜;EozP"eV%?LTͭJpް04LDJ$9I JL<;oѠA!5QDe+C/(ׇ R[J⁃8vBB-&!zPUP/ n]Xh غm;\\\GtU8{-Rr!)kN=B%(KFUJN&UߺcOu۷oAӹ]? a,ܼu!*8 cbv6|`3":JP2M>xooOOZ8N0܃r*{( HB869]?8 ^kO㭾ݱt*gP"V8U/ ~ڰ :tA3r޸tJֽVhl4xk$u*ޟ2 Šu>vyW/^yUQ:kyƌE{>b4gDz==Miit=P_ , ]ξ#D*'I/aND|Θ:cV{kX {cΜ?ubμO?_9{o_Pޭ<S㳥qRWY-[?igszˆ3:jq'T@a.V\e^^]7ᅸP $ }2k<=o/S١{-1yE7o]XZ>6^a}F>שS|ÏHsSLĎ 0k?xU| {n3t_#%(BX%A&'P( sIh .^B Keee#$8Hڧ-/((m;p5t{)ճaB~A robq3]*PSo0 )vuw0BvpssE~~>ƴѕ %B)֗J(**B<72ɳ'Nb8pZ`~E899=lg6q?f `ܵ 8;;݈h(n\ ='O[n^=ӟĠ^ɓS2i+Ln10ݣ""x\x\DED p8 bC\qb̳UD 69!O;I̟;s-3[b"Z|҄@܋Wۺ^#A4i>Xm&z~zw`ʟvm߂7 eh2q2E~}y]: i*! 07Z:iS1Bj;+gwΝ }}/ ֛M7έ??h@r;mIq2Mp/Y0X`EF,ˢ(uֶsp9|ݒڞFQ`YVxv5gw  !"2r ֬^e=˭?3˲>@BM%(ciպJhGcy v= q"-=‹pA"#ĶT$d Cݶ8FvRE6j (k: #qJxGvIDLσ>(AʶAͭUx7BZz&*^jKKτwx#ۡ%VBVY[рu1}`ߗщ{uNp<px~"ۣHаzTCjJ2lF@d z#s r#*Xޡh5|"[ޞm_g:v[6[^gɿ8 ;ɷw6tWkdT愆ma8ZZ%jD^zȡDlJNC' ڶ:pvvvHFYbgAQQDTcATZ;bV*BWWWkBChSJ;8B1e B!v!D BM2ٳgȜB!:&%͚` !0m8!=t BME!&Q"b(ABI!$g+QXoX!) *XC!bK®{Q|lx!8{=~"atǠ4j5c"Ez%xZ֕Qg!8{xw'CGdT4HeNֈBygKN Tc D!O+nIENDB`sqlkit-0.9.5/doc/html/_images/layout-simple.png0000644000175000017500000013767111533706144021044 0ustar sandrosandroPNG  IHDRaq}sRGBbKGD pHYs  tIME "' IDATxw|\Ź>3m.Yd˽aijHH!J@.{B7$$$4J %c:66Vj۩3# ٖd}>kV9;gy뼃˯R`bKAGySaﱯzq Ɨ_pU| κ V6uFxPߕ&9q? hDޣtu9,*=4 tGL-?ZGT ~&""dVL"IRHaqyuY ;8gno֢q @\:M/|p 4BT!D ADԺr҂gЃF0B g[fZEhrٴE6)i?ϽA=X{?_=| bjQs/Y!r.ϓ\a'dy.U9E4LGkL$ @\ALǿɸZmLD9GDFv4(PVU+v Tݘ8u*v ˃E*`촰4"ʮe} .K$U1> #~gE)I ~j|ue4VU^V (M$tURpְ1<~}yeoьh_<}C,_ 91t陞e䛞9}?h\4󃛮;/Z=cVeAo{xS"gMl1X+,s=QQ=!) )キd51HQ^U+3E T@D2Dj:$)M8{s-7VP.tӕV%d9] pA猵tzsTF"&XA_>${t߹^=|cW|yg{Te^C˽\.^|5GztFfIl_\N\vHrEHMt sYregzh=7S^kыۿXU7jD{L~O*_}Vy\zgҞJ1[Z|g+<(m>L<+Ǟ8ܳ ]TU' ` \0R_7'>o?0ϝNCqT; (S8qQ~&"*%Eg$ᮥҒx:'4Huá$_"#?q# B(޾ 2])C8Rz6 [Z>I˚D2]r™r>y}[pk)DjRy<8' =?'2>$G8=^w~\9Smzzf֚$d{o[lD 6nB}B\6zx 8{Zo+{y)#\xQiGN6b>jo*D P(p'a[؞x9m\6Fa+*wzcEa؞k4Ñ@ (PH;.#B$n"3Tf,|_=n;A/Iv/o.gH !/Tm ,E%}CpPUDp]/Rik\uU(6޾Xa(`誢%SYD^[;I_Nm0Ι'A~tdځٹT3m b[[/?@x}2rs]\77/#H*=gzlw{f0lj͛/wG_wTqg魿tD͗n,@z(꘹Lֶto-8雗<7+`HPuab__;ѓfPhq햣UZZtuwvrYUUCpIIIQa2T<=8˦bXI Z\F{*Zy Am4Bͨ)@ȽE͉ib[;W2vޙ9$VPk(hls!H#j@y+e?8Wb-PLA?A{8߃GnYQq "L±Rl)TS)6qUu >O"9zS<2_Iye?<,y䑧Jy䩒GyM=\DRJao_c(` IG{謡G^[W`nIcU~?jk%Ǟ5onԱ[xSl ߽GTw-"8yXqHEs[e1+_yQ]|ʽ'^6{fQ9SCi䙢\Ea3uӊV( K 8qFlD\;:;H,ʲkreDfDط}"yl8am/8o8>J>J;iz.? 1Jlظ +yG<=%ʶkr3PzxW~sz cбF@W(SXۛ/x+[,΀nahF@5] +/>! U7}K>zB ] LQA#ufF/u AU|N ?ZqswڄԼjm.m ]劮T%UD#`p "b\ 0B픷݆jp-}iCNHo7o >2h~{~7vPyq?km]7xWo¨0BACWٿ>YW6#y:|/|o@/?`b2;vW]s[?W"ȁ^:??ś8|cO-Jrşu+8\ޓ]b}ë]o-i(>/PhpWd "37LCgnyvkvpɓkf^Pj_=+"73ǯUwM<϶{ߢ{h\v_/wAAٗ}To]>mvY[x-G-Sn~ƟoQtDt^Ze}/6>q¬/աW~7_|Ͻ8yatƟ-|0[/Iŭ9tW|qnm5 }E=^5MCI騱?L[0_ڣEx-B붍 j :ku1M-JvO>Ӵ-!j*'~瞛'M-aH^hKv_rjCP(S/Otsj_?`{*̬po̟Y`ޅ_]s+.K"c QI#^WԻ'XdcKF{ Go?/TC}k `Vw*4o?h;ūnoތذo纃>| Y߻_cқϼU~_6?/ (K,9nbQv[w_2+ h|P^} ']t]}HAuإ{}+yW):)LгK./B۲ݿ} g>93ΛQbjs9 ?:qVM>wÔ(v9$McN|MX(:ps_%mD*Wa!# @9:NlcgUr)׿HeK=i]k}uWG^=ˏ2T.'@ոyTŋO jlî̆ Y$K};Ys,( Gfq%31zB-6Lsh8`F{?~)te8[ Mq=x;ء2*f 䑘Rû5;˘OO9qCP )3@R""T c8 Ad[?~tOq~%v\F9a: fկ];έ=y{Ѣ9;xa=rٻЯ si?c;8rM*W&6u +~`d:]7SMYr&{0}b=< q"Ү ()*;ݳ{)6[5im^6~ƅzsR-Cɴ-9-+*5*g۷ˀbmɦM,{/o\֜XEIIHܸ-a;J>pRCDem7w{spoۢw7wݝ+uE+BcV_0EG ֘>ZJL=ف4ظON:e ^fɀ)ubs/~t/3"[Y?Wpq YHΔDP*fO[]#޷8XE^ Ю ߕ~W"W>wןɫy ؉g-Z?z/% Ȥ?0so|?:pWΙ=zlBUo8h $8~_ `vEy_<&iNo,^ng)c/-BD(4 <3VΑQEYѸ1F]]+~agc&}q)E?c8ctʦw &VqI8M<>l]ʿ?0ac#b[['3j"ѝ~!磥nORPMj]|:D* @ybCDCe#> gjvli}L:?2x '_yZP6| i"T@4wwv]]׭(++hm' QT&p8d X{ >ٚ|g{~FIj*N+iFN`dpyB^ytCv\أN,Z&W`ey!s=:nN5g_L _,ݒ|s@Q(P/-@$"p4~ ӦʶLתEg8hsh(^0L3О}vE߼ݓ6.KW5>}ML4Ms`'"vL$UuԼy{DY6< %7wmo]탃`c رMfHUUVՠehW}@zy#uK,/=pM7ñ iDL7pƘ_@u˦ˣ5i-wR IDATdO MgdgX25Mƚ)N?LAn2'1&oqF%q.YΩ9˱G-תu}``@J8>PFԸ)M fkr]y)7ac[LR"cu OXt#eh{7p,zRII@B\rT%[7>(mOd3k=i-o /SԁiJ$:dΔ/Ӕ'ah-r4fUɅ .q%5NJxP({hE{Ziljhx@θzg2Y]mۈX_8ƿiYs9ݢyD RcgBxD#ﻢp\r(HܴL@=k{U=HbEl2t!mWlr9`01zȌCHy22U\ _wˋ.[3gib1@O*t+JeeyynY޸-p Bx8STTdz*0=[/֋/I!C>x0drDDR5oo߿罫"A/Nሮ 8 \ZXYW4hze>F["y JɌf"ML͍2e.WlFdl'g:Yu-+/UFeZH4 ]usfV+((PU5F"Ƙu@*Is08p0x #]ׅ\NQMӒfoRew^a+~0m;,yk#E_9O\d92ɑ۸u`Ӷ939/:}ח92|eCݛa$rD8CWHM;sk۳;z a+K+oi=X<1:G{a$e@fG*LA-ܵl[əW%7ʻFz'Odr\$)*iKRn_477~ݨL c D0,(('ŗ L4M" B[\=HGyNXŌ Of LS2#d^6TWK"JmIAšiozNI.b[F{*ꅻ'Cۖf K8'C{*Bx&MRU՟S"2|=2jjj OL(ʠȲ\.s.^ bYYi]HYw|qCz^BD_/V.y\֠G~ǝC@.7{Yʑ y3 seK-$-ΈRvʋbޓnd}mnIǹUs$m[T њ^\ҪBiC@xzLppBo+م$D3'~kkr_E(\6_u3DZ0 \TbV.},>c['͉Q1R9(cDxhR9c8_v)sӶ]_VR>7πSqN=bG,?Uza(8FeIƟRcse ---d%KjkkKJJh$S-2M߮zh~\ce˖EѲH$bcq4BϚ9}/RExB.Ur"/6T@{b?ޙL';Zou3}'ܱ噿( [\3eU!0И\u]3#9YlT~RK,ZzZ'&e7תmM ']uT(p5sMou=W39%S2UU|!/UFBpg2t&dmK)x,VT8#3CWXX,ɶm$~05kCM͙Lz-4SW8D[1UUUyّi۞}#CtHGXQ8P9y "WU-NJ¿zp6nl:S"kR7db)3E/s.d)\`e}GݷS\l'5ι41INbEp%+o>?g rZZZlbYcdXa׽TU[̪URa#bsmDr%ɑW uM,C4 RZ5iup8R7B!G_i[eI97g P9xQHeޤ'm)4iwcf 1nQ׀M 5\a\i\h,^_%p )uQPGgf^48OLQulw7.mJ"T\34m '"rb)OqF `](g>6nܸqqygfKKӗ,YyE]J֭[d-[VSSc7> "B cҐOml\}3f̘>}(m۶mڴiOx/¹G;yE+WjnnUUU=P?""2"/) PXeI߆Tozb׵I3Q;xTjQ1Vb^YpXU\ݞCV0M:0d$0‘U/$"1adŧqH$BDwɓ/_`iӦ%ڢ@ 3ϬYljj3g΢EfΜƍjkkڵkO:餩Svvv6mobH(4I{ &pA"eHt@*(GU߸eSgO8_Ȧ~kO)L^v@Ghb~=OS.H (v0N16,4>> t1?DꫯJ)_{SO=uů(Ce>oXBK/ %ֻ<:FTO"Qy_VT?09ZjjJ\0v}p0ڠn(iޢضqU5x^x_)\KD>O |pA$CH ^{rx[l"yn?p~7w]zI.{Ʊ7 S%_S-nQjx$(My9$ cθ5/XZ}- U%v?|٫Y@b4DD@2 k Os܍9?";_h+[r8qpgA' <;+RX,ݙH$h"xg{wf}%-}vnm۷oBLϞ2er8r;w <Cc91f۶UOOO ={t̗Haf۶mۺuk: sΝ0a(J]uU@}Ȍ;GuԻ 4<J6Fuuual޼9˲萐֭[w 7lٲ3d*D&tgr&UWHG$ ] 鐚NKp88!. Jh 5"@>a<_ %Cb΍$"KJff]oЊy---}Q*PApC@,.uvl ұ}A(jRɅ! #1 ,TL A'rFp;}}> e0ZݬceW5,o40I\" ݛ3T&Hш.W/U>M5*˾/L頗Ax\Ej&fcVE|N>"ܸ+F&ZP9ӝ\ʜH0 2`+NS;|h6qVp@HI*Iùa~$DE멧[uoݡyZU*u2`MƄPQ2c^RᚗnRBdB3 &HA8SYlʒH1Wp,DGj~1UzxثCnYĐ\1ɈqbF]sX'QLD򤔊+47ܦ+o*Y"D`HOhutI>' IDAT` (F>ɜbӤ15!A㌃%FB`. ԡPJB DXӞUqv`5{Y­aQ1s W9Q  h&PRG/baM: *cjkkk>okkb :T0$-Ԇri٤ctd.6LCTJU%CNWUjS "&$F'x'J)0"RJJcǎ-[^F_uULMM)7MXxիKRZ,P(pkmRǾkvB~/g \2{i x0RUO@Gtr- =dt/uF#č'ҽ)npԵT1ӵl6,s`cc@$58Ղ"D.ߵkal6[7ڶ]8SsDAض====ϫ51ZZZvܩi"~:TN Ȃxr0@Kxqܙǵ8HBqNej|,FTA4lZ2dJ)x|8=܅_Btz…R\.FΝ;9DsD"q'TUx2OSXhag}_I) u=s]_ׯዖDQ]g3!8cq36 9%w*eam0>P+0%2NT 9"RAtuu͚5>… =ϫ#uzzzz{{׭[wުbΜ9w^*\|ŵXXyUbkOp9sԴ^n&hB KAie!*Vիsl<$vʻ6Dfp*mɊ!0@b5?I@:dYhX,kllu]]8PX(6o~CCCزBjXfΝ{mll|LܳgOR9>'N---sOM橧r]2C 1$@ }v5bd |謪|h)?tWɜDZB-rHf %HO[n1-E}022rlBeZȈes\6=ؽ$D`u{!0Pje_5d0>}o{+OeChTIg׳3Ds:DJo$+HO:Yj@HTGD? ęfɸa'kOQ\E":PR@J"W˩Bq,?0:<*DƮx|e(ceKǎ\qRJ"0X s MiFwe%@"4:* r@#%%< 0 hTk"PPs-} "6tn>ȃ/N6P(a sbP r%[WR3:?9otUASã?ٴtng>{sچznYFP1`(b@QWrG.l@+%+2P+Eiofis@[G~B6bojH&TLShOd2=ˍe t\R |k\ji^YjZxƺ;h+9#)~ݫ Rz"Ys "r:hUT@BDdʀeJ,"W\;<>ɍX5Tv&9i&|Ůx]C9<}{2L+e\NSBW18x*Aƙc$Ir1HX@o9KPC*B` AY_.ԥWO[\p]?3홒3-XL749w:ibP.eRT"!u]-D̛;{2S` c@u/*JR,JGjq~TW ;@JI1dX;Uf>҉ ܎>=r7\O: 3O.Yed~~]3BIRGsSo[s[ct$5obx:L3#TSB\[H4|1O$%AGaV4Ñ(s*D@H !q*-k_@;uzRK*Ue¹ Y5V<~zO keOo|\&*ݟl9s%5rtN:iMyαc'OYy XA/< F$bu4bӎūNݺic*uK;; :*k Iàl ό\^#Z4@:)Ha.:b<o~6;Z9WH٧SAgE]dc[ۛza}lzq^Uu)3ATUBs薲FSP9D[a+&סRWB@J0u@"%ƅ3uTSG @:2?"vX]8˗5P~15[!BSH@D6,WH5anfkݏ|g[ 0{wO䂳_"~gH䗛w>̀g)M}!*Aȴ8A5¡.nEcPBL49 ||2=|T9 H,L]kdH֡/Uo9 .p}KT`ȍ*jSPA %F {1]ىyZzhl]%Pa@4f٠5!P(yQF1D5PbΦwEf]ya(!XrӵMZ3$4kVe3RCM/_!F2|/^klK67:T)e9OL)_kXsǬS%:{5{x|.jODSt<>Aligܼ `& E kވSA5m?b߭_wןlF 4xnQM?RXg_|/-]Lɶ<]ȈaǣP4W!ԫh?U̥H/y|yGtmr ;Z߰nN` ծBUA58&=t+c&/ZQr&E/CD8x⅗rJgKG,d0 VK{%+,I W]q%4jjj9U- S{~~ӗ7hϰBvB#'ܤxzvοZg܏]aAZH6?p\yvaPi_W/Zy2̀Ϝ1t] -JbF BW"JhlVгVYi!_}$nҏݨI_j˅AVP4]vlb"PL1B*_b%/f5(1#lp<=wޜeYlřghsCr1](H tB|c*A, aJ @>s3 `"Lڢ7ӻ~?'ny7_BRr:-H} =PDJ!k>4}%jxhBM|=FzXӔQݒ _zxt\D e;ep4<"09|>SyslKnj:mhGSW!TCD :6֐ ""/Nm2NkQkN\Iv ) ZM͟2S|dyZӗ@ @F5Jv !(.dIQEXiH$Qvz:5=voC<Y3:t)2ug=t{lj|zir_ܳ[Cɀ]wVyd".zꊀRкNg=Ȥ7h7%׀/w_O 8֖]%| uQA 'eǩ2@OJ;v_У]:Se/ U4l|9O+[_Z5蝟 P]1Ox'OH1} #L$J#s.柤 Dӵ5Kyt ]ܺO2x/5⟼K@G d49<\̎ ۛ [\ $bpxALMC3Œ]){/LM+^改nLDĊlIrvwڹw]!'Tk!qMAȍ"DD T !*d b9җnߺmm^JR@H/U&D($ qb\qGBH~O&vm?lV7YE-N@RuJ M,.QL-S o6tݝWYxFCO$] in 28vv.Z/`]g/^w(y(AM`gz?ЃX%7mƒS?N#z:|>4YJ $L-鎙 ¡@wBSJJu᚝`X2.>WLcOj_=]ZU?jo Y_vjsD̞ IDATb`d@n_peL">ʧXkW,(JRF֞GGbyH){o]M>_r3Hm4״b%7*.:`c@mA ,fX\y$CA(k-]^ǛVϽ>qgyW阫{U]7 :ȗpÃh$<3:wZxL`:WQB^KVhYu]Jן}uWlݽWuw6_٦0nfN6ӡbN;/| W]y%ciΠhR26YvҶui<̀$J)ȡ6DQ*&ll:ꉳyVꌉIҋ HSuuzj .YǮ,;ml\ RyAR.EohLOO(/`2@]ө|νt MI)cj`sN #$@UG,J]{5_s > ?(& X re^3*ԃl"ePW,)% !"(D}N[sllC+N?41V)S.l^\)mq4Swu4+ܸy׾CZuK)zSw>48cj3*F?lݺpZ3On8\lF}ҹP-UO' ۲S۳nB7yw{lpjjѹ"CQC4Px/V!6tGg3DB%kN?әBRiE}o:rpzlNM{nµgz1и+UJyH$T hf[}`sw#=63ΤDfi:}1Ǝds /=ڛfу ŗ‚)_IIea[EPwf y]]QzM 'i=:!HpTj "ƍ|o~۬P(_};80:/c=}-ᆢ˜fT6obim6$C0Y 9+q׃څe+?ѿ܍9GEdE2Rq.{Fﭭ |UufV\hhp *Ɛ2يtFVxS"bAU$TCUi+A2%H@q Ɨ<[>dJKl;Gtjzs[TwϜƸ51tM1tUxSks[^o?qg!lNΝ& WkJsDD)q`ə_W<1I'ː1A =eA4b.ø@T01ԛ&=Q} %s@øzNqIRJ.:N19hfhj2KRqU[ɿ۽{'ט"D@g :TNU)b.!&C:Obn+V,< djdLd$$q֨k&CU$tI3>/D2#R, עtᅭry𜪩mͭJ)9S\FX7C{=loLf^{Zb-N]s )U=gYE*unP薞+=o5N*uL+Q.P"h,iuR#`(It]BOHp<DfFe3Rw\;RTx>\x^W2nh,TbҹŠ ._fcn,LLm:w͚B*5q =s/j)kd!g7& jj C{9B=-|poy聇TJLc@![-X#A'=DƤY2(Y* U @j1e1JpHIigFohjTʖ)=)0_) T6Cn& _8cpP0f-{n5o޲Ň^x ŷmz͟ ஻iN>v [{gyds]|W\S|VΨu^EӅB=x1S4"Q$sTPGȑHC`w428dJhr {@qvO'?൶Og yǩT*U]SJƸ',=lx\`Qyb<75lK_ve;OS;3$QN_w'~7h:#CBt|c7=TzKԡrz5 @i j:D,z͟d\ZJ P"EDJɻ3]J_uW^u>=S׾ڎֶÇmݿs]e'Yx'}E7 1 M|j:cሆ84nWg\GR.<أ-'2ӸyL3s7u5oF*O)u⣥042SxHHv"U7 C_EcA6=O_?'pӓ45͝H{`kk{Lxٲ 9~7]֬!N;QTbqj.b5ӊ5%_j@R7$P,ޔ|ϋBԺ #Ïr+P'uCy"!M|:{ƙg~[_҈# %A""%%$dtLDcIwSu7zAOH:!::`dlF=}s-+tr?p•sJlkm(Z[Nb9pt.7tޮ 0quvٶɖk@dz/XWQJHD!* IJ:;Dq9lh, v>Av*THCC&r*qo~󛉉B1IxK,]gDf|NMz 2CD*A)<~K##{vy)0DJY0 QcM͒QWR^cp+_rgW.9U# oyY>Eo}>/jG!i&qnnonۻo\1k}]K2/oωkWwtYK_:(]{w~M mٲe:]eeum\.\n«D $ `#2B I jd(@23%_Y+V[.3X+Wrɩ׾\!}Ǯt*[d=szNG=U>ˠhkSJ_ ܊SL@x4/՛ 3E#)dozϝOnHl.5ɩaZѮV*$()Zgu gnw]rR)?9s?3\{mvRJL O }RB 8u䶯(  P)$tG 4EB*Vj$X<<{|ޗnjm@! ;jWH#j%DP)yB.ݶ&4/.ϧko۲MCsέ[s}bT:rj |>R|V*'W+.^s6]t4R" b  $(;""o#LR5FPxdQ 3FGG'`?ψjiݮRn{g1C*RԱ-/S-K<̑5DD"PĀ!5o̙ky"R:w\BH^^竬n #̛7o8__|ycc#ƘR`?l6~zڅnYg"0ėT̯Cq# L A qDI 00,~D|ۧ5lܛCw}*.>BH6ډUvfkJȗ90rm@88/,Ӌa٤h ^: P`1 Ph۞.0ƨ>LoYi]O hkAtnOS B 1f91&V$!jLy>*Zu9}&304*SvݸqCsss@eT0YC>Sn.Yr1] U.(P + džjc"uN)H\WXXxunbXXݼSٳg7q*Z*q\d2Y,J%IdZFX,Vk۵Z^Z.nB  6R9aBTz}U:u>4$|dE'@ -qJ@"O$ @"JQ`nW9%Xbǁr;T\` NgH"U2C[q@Sg9J)`*@JD)eF$Yq"Z0(r+(0 B7LȋťQrAj$,>~IIIN3,,LCCC=Zx<q< .h4B27BCCzfBd>8Ψ(0zeYܲZEQ"lNIIZ111.a(fCAkNFY}TWWӧ6<eY1*111 h40FS[[0O$Y ?Id(vN!0$d *s3JReerL&$`0TUU&4770n!dX=st:]MMͼ^$IMMMav;qe^/,˚fr&ӃeYYUtAe?wUժT*%2228.h j:.///##C;QY( 0@=z8;GDDXVADQ}VkXXu8!!!rnTWWl] % **I|>vA-QKʅk$IǏx'TWW.A)MNNPTNf Y-,[ZZ<OaaaDD^/**Zr`M["##YeY6""[nv]eTWW悂 Sl߆&bQrž5 Xb, QBg@MM.0M8E ΁9`"5Yuɓ򢢢_III}} rd,8'k@,=˲kwerj49'./_0LFFFuUfkaNd6- Lɡ`PUoש,'B ?rs7ڵtJ$V@t:ٝhllu8'N,--Z999!!!ɄWRR֩S'BHaaauuubbbdddffg{^Nwo9""bݺuG?,,"՜OT6۔)Sdرc;v|vQPPvZcbbdt:F#ߢY{DRvQ7{+L*)>4:ÀY*Q[[k1\zJ%"BpC8˲!!!  q: '9Ut::.%%t| L& |>BV p0 ew8\OxX< Fc\\\SSSccZNHHN DB$.q*bStS理 Y9 C-$ӣRyl6Pj-vr`pȓJNلKp;յZ]ss< |>_qqq;]@RD.T}ڳS LN뽜auU~OgzNtJ5;t*"}HwYAҎjFI^Z4UW ¼â wp jlmNwΊV%中)oخl NrڪхDK;w NUZDZ:4c߁1qIRkpb*h:x a?Ót\v{m^j p+Jm>?( NQg-M Vfİ|Ƽf 9\@!ꪩt`İE(ʊ=lFKK7:95o,w B KJw?$!XEжm8rtSeRqFԃ_bwFf|7/Y΢O |IV(m^}g6³3c/6{4-)ް~֪7v$ IQd*Ot`*~=%Uw<7uSWF\2]fh`Y,?-w0#;q dTI߸*ygu[;T:~$"I0qTZr%*јMXKJ:1рvUrHaUZFhJD+ԼyB_ByW)*yB1(; $zvrRӉpSbPYy  {xaSF.j5?/ JP¼%r HaK@cm%p ’ eWctVpw~JEP_ \Dab/s8}#͇r.(PD9K+rwt>hϭ>R{VqAHWVVĖullwID6>/W{t nd4))].NWb_a}vka֖nαP_/?1;le9e5aqOrmXbٹzz7:Ô6J/iv 8 sr$A0R_l.[ΎMH)p8ӷ+OpљMaɩ _ڥN4CuU;~鷜J_̬(}cQ?m9{tg Tm9Pvp}1=6rh_X]B@o&66sШ~Q maBb>(D$5h{N8uHaԽ$kw7f\60)!_jtn1rpǔ~QlJ1'Lf}zw!;Y#JL_b}t$<Aӭ{z(=j?lgLQX똝"gAEcz7J ¸_E)Ea$ @e I"`(Œ3DIJ9]ҕKM_ba[3(J$"7-QIJ D 0bcI$ 0I"-a)caJ(%@ F$ʯHݵ~Λ/])"H3 D< J0H0""$3 IR,fhg 'N$bYڅJ$u[~e*bKI@ ' HE($~鎴[!wneOkN^"Ӛ1Co \=sЪ1%> \&QB6Y\+3$tꍤ[P))mï<(%H??@ƓP&ۯV\P+"Sled!4.a4`#6q"" xG_~- /*9xjc}-qKVA0,öEQ<(!U~mKx0q|NS'u=!w7JZtnm.q@q$$[2j;'.SA( GWHLjЫBQiQrmϭ.)~U|\OUUqs"*K~wlHH곞7enI!!@ 0SV#TRix2)>H's.R_ cR (n?%~~klb=F0,CG %"3UR1/7 X|^q!|dCǞ,TWS(Uk!jr-ic"֜PB%"(e [hkF BL{@G0& U|F27BSKmNX9ߛ60dv! QOSMK²z;=N=܌1Ro52⑏6$@y˷l@q-ϵSH"߿7ó9!Umng sgge^u+EHz4hРAt?;%%~B"###";D'F&%FuHD O$=ZRڜ-kv;wJe6l-i%69[]Sp}jU3-kVֹ3^qY/53mغ^Out]'Cث  qqՖ:Ul+'H G6 4_<"Pwd<:aH8:w]uaJ4yLIZE_<G?VSbӗدr7ۑr9N>F|ǜ?ĸ6<;?{^x;7qѶGV6%$26!*u :҆FX樨ƈHmrWQD@ Z"(~c>AkM7 ^'B:t1[nCnjj 6k^ƛ[I9=t1>+wRFULw ?>Wh,9r쐤-| ^_:tmG*/ =n9kQL׽r;Z,v[ Ak7V6UT~tS?> Ǿ\1e}olBDB^-^(j)H?}.eiFщx7PReeܘ6\QozsE(dh8Z%˒LY 9sS?tI҈Q(%nZW5M=cJ+p "f^їEEr&D,}*Tb0x0=8y>zX/B9GV҂:?;Cs^fļy,z6>'R90P%=,F\ *󚍅_̐."@@cYɲNL=F#i SpN[49 =u26 961]J5.T0#{=ULqpGd 1$qκCR\PrYIۯLkxJ$J)%Z(!c4j&<Ìv'N JPJsr3vɞT=}ݺ̨ED}:5r :vؽ3ʂ΂uy1""Ja+,|n~X_eTUCzG3(Tr ٙvC~戜J?msz(Wވ.iYܐNb}]7(6e`x~ݿ6mp$so15mxڤoY ]wmM}rC4 Ь|)غvgԒ,z6mQ3|ǫ|%u""QJ(Ţ(z=nj4V0+ xYB$y2Ƥs]0L)Bj.wC4W!=uW8$62ഐԌd,/*ؔ.iF5+/kr&%F-n詮%q!yk-=kJ ҂GPS|*KO1{0tb;c%nMdnLO3[R+b-v(!PyП߂a(>*gTP )]|b}rɭ'ĥv;ƬC] ḦH KJ s$w H6a zZRS1)cuC{mZS-e)pH-C MDJ|`Kߓ{&(Lleuݺ\:(@w8X, ! HayW_Ĺ() #@e ^'"RImv7z|~cpP%P/ΐ5t(.ϔSjDb's{XSnS)L0*g((Ћ E=gsI(84Z2UhRϣ. (T83n'F 0]H8j ;Db)RIPJ%Ibܡ)$aeFJ$3|;i(1D" AF|MtE*|%>T8e˖}ldIDAT' '7:ff 6<}MU'/]Eͽ1YȦfyOnϽl@"m*r,(݃@,_yԫo%H]t?&9o^o^\x[|ZR B]{u!\/񮗷Wf!ĠQZ\XkjduzYf2RB3܂d!Fky>j8_]ݓ\ca jbPPt>$L SBļ8gNh3V?mĀac&N5*[s 7ϿSnWTW 7`֗{ ngO5bݍ98+sM.kKuo<0lpk+.6}B9޹i;]b]_V,im [pG!?WVqذaÆdNuU,=dě_=ۃ3F<]U%! i\RL5x~)_9icA5l!&*WBH^m^}ع_xBZMAH,C qכk]"r'ܴ: !r'=S6=T4Xm;KmXuO!}yOsaIqջ6|0ׅHۿm׾[fW,^] > e޼cSC/, 1v􉔯-|0 qxC[mc`$5kXY>X!zq3vKprJDUv4Uc?..r=ۿ2Y(׽nO~wj=Rܱ}SQJ)_q%Lz7.mG$[owt:tkn}K1nj\}ٹ> m|}rwꊥߋTZ2bEܵgogNԐ>S ˮWU/ڱg^gS%7{vクXYnZy7ARƩrs`j'D`XAZn`~M_uV m5*F$-w5"@$w[ @W;|xr;acy=^ /@WEk(_7y(B;yMgb+&}RkLڸuixsk]=YXh@[2)Tu ь>|AzөwV؁Zaޟ-3Hr ª*;ݝ':l¨kozuW] #  aFJ}m[6x{CXb +QQ Ō4Y?ԩ<08+w!od% Z9InI_o\MMeғۺh"'yiw /lnyC +z" !K_i"D"b"|t7eq7m]WAaBnfTlBB1+"{[wy;zJurWDX7`漏hϭGn͜H7>)6a ӵw)1{0\* ߼- {?P1qRc.: f~ɻuݪȬ؄5hĘv4#Mj/mS.47T%t JŵIN)/y':Qw庄WtcNg5UlZ 64K|ɍ[󭯵O/ˢ[r>_)Cmp-kzy%Uh"ͣ,:Og+x͈W:ΐuɛĻ Fr#?Ki$ pIF; .Pj;mmMi*0B7{^|m¼ʈֿ;? (tMO=7ߐ l-R,x7_4~˷ܴR̘|c|yvh0w[[պ1?=_ygȏޟl4[xM'lQ}x;{~yŀ+op~j}TKU\2= ےǞ1qYu i^׽qW\_M߽?c`?;5>|8 eլO>'#|矿޼fP)oڴ[Ycb,_!XCC;{Dw>3P_U0%X@d5\^'DP,_ouKi oeq&E$D ڝ#+IjQF@(U|]B1**`͊*`Pq?tT9Z*4-fNS}U._搏H2t[wPwBw=BF1 ZHclB{}{}"t}ؔWPxI K׿4qg&#O7g)Zx֏_pQf=o"n|EqUWNszQ[ FH)]]k}mRfҩO˕'?N^DBh {_rwx)x%{>)Pʓ)l!֦(}~ 8* ./%~֟J!W?M&!yW' (PPE* (TQ@B (PPE* (TQ@ U(PPE* (TQ@ U(P@B (TQ@ U(P@B (PPE U(+^vX ~^S@)FJ$BA@RR9F H#'Z}>R{osRRx ǎVkC;t\TA;ޱJ" i#9J) DDiM(0Br5D\$1@ MRd0FxfIgݟƨ7,ryÄ1}fN08[T.iv_`RJi#j&giCEoZs<}_ֈ&ׇl*#Jrom|(g_o;[kӒi{v9;`BY@,͇NMjPv%j.l,٪}Ԅ~ƒufc3\(yoP[]e #Œ:cvSuhZ[5ZʩatHQ9Pџާ2Pa (9}R6uчקIJ=~x, Ꝣ!:K|eA5kL[Z˛9Lj0wX Jκ͕6AX+!}DL0[qecHPk%z1!SQTY]XmPA݅@oBT7槥0bĠe6~TOLP\E3ear>:#3o:bO .}}/?Ѵ_bCEO4KsihK5\w= .avrPiȲ_GRІybaanˏhfǗ}ӎ Ìޠ3F2'^K#.Bǖ|6=eef,y|ы+/W:bxnG㛯|hmi'quk:%OÛ>_~jb }O-xş=?C+v~vŸt{_~(,ZEt{)) !Iqu6t6)˴ svΰJߔSa5A j=Tbôyq;GEa8rtY؜o7Ve2t)q{ VéX,ŏ}YP:h4"i> (S(R/h{KU7kW_א!lH CGwSw`;^?+FwvrVWEEk|)A[zb9U[;\OrӇ?{ʜN7Fg+I%e'޶/GL GnBmB7,xyte@RӪ"Lݻ )IDHтn]D)={W΁C"UGlUK"$gw_u;K J(𴑭-P ]vr"ZyeF/$G'g¢z”BY_b{1D5Q)aHYMFDa.kE'K#;Xa]G{zTBq&F@ 0 J61 5:L = AlxҜO#ðWPf! c/n~FBBlNUk6#%٤FHc@cZlHݡx :WAȮZʲBS]NNy0ީّH$8c&P \H׌8-).>..)ݲD3;sX s>]w*B@(Q"am]ӪuIGJwnq}u)ڻeMAc$%ɰ,TF( sZkjQN)<#5O$vK'[vKҕKM_b[N4Tl* @4H[ ئϿ8ɂ_fqZIZ;`AbO3zuO"n0ztI1ٳUP\[OZ~lt2W$"O-BLV lrd4͌qh1|'lӷSqm'9%Cm>vlQ"GP R/8s uxRoH>AQc;^ ZRB"fxw-[~ޝ,/.~ ukY^Tˣ])/@ZGp.u ©|/7zZ֣rWVaj$q/;ۭ." (E((0c埖 *" .KFnj|#V]4UZݟح甶X4U,ÇVt 0*hCUY$ܹ?03OENh"jDD0+L9k(Rp bƨJis Y{)zͬp . "NФ+D)C#?2IENDB`sqlkit-0.9.5/doc/html/_images/sqledit.png0000644000175000017500000005662111421620210017661 0ustar sandrosandroPNG  IHDR hsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw\w]z{wcO~^kލ%EcbMbK"Ǖq܁7fHC6U5 w/JH1DFDfw Q%|X6hhWz>N^:IAb!Gba̦ϞgO絭 HK'CpT"H ^U*pv⢱w⋎4_XRQU(cDHV;S\{}6 T(pvc'Ji2{etJME{,GV*Y*5*@Q)L\ Zij2G 򎀾[[IݦBQR&zIh4jSeux@V7ϑmdAMz^Ȥ鞾+$R.ջP12kvbdI&/T\0RK {_}#vZRD\DVz}q;FIsa4WFՕCEW*fj@RGzZvIpmtpG)O#.@wa8kAر<l؏g7K7` p"bGmLR1o-FpsM'z*1;- ӳS?M>+p!VQzJ̥=\+'w2zV-}6aaCQ< 1XQN]LBN=0`GL2J_Fycgꖳ*3~#c@.QJԻH$,/h4ŹJG<͋l_m~ƣ^OZT*y|d-oV/}{ƣ~o"n1pA7-*+$wJф=7JƐ V*yԲOs@RT- t&}?lJO?ME!i2Y;-Gn=vfҬVYʳs>Ҍ3]³a"DH-9,q T*%6)S1\I+rtQc'"ڹ%b}[;jxiԁbj teE&XhT*J HRm Jx$oJ&%e3J'+ ?3^q~T_ѸLZ')!c+U!I2C/$vSdTovWq'Q2 !)I{!1-ů {kL9E3*8cEO2u(iIkYB4+SE?^~"~H)$. >b׹(Oo]|,"BӖMYpK^)}hTo~srQ)ШFI *e,NRb3ߤJӡI}-'NRI!M%VRj{[,ز~//.Q?XnCzs7DE#$HHw7[Ӑ2r ŋN|^Ƥu@s ʔ\yBC\9q`Ȑg8n˺Ex$D)6+ce!A6NP=՛h[Jj1`hJo/%ȝ}ecpܵWR 2IMqJ\Ncd2Gs輅nnBB"[2 vPp>b~\"͇u$$i?"-eY*1o_?|5'' tnnty>b~Zm[[ʈWBRyݦIx Q>Rr\ZXc4` +5#0oy/5NiT괷n6iHJWψi[5}Hk'ygr@xmID轉I7!% I)"ToљJ\<#!1̝?k"?G.3(n$OO.p@KGћwt[,ПRg2#Q3F~V FÓW7>Opo'(O³PBNsdV}'"[w͒qݓ ud'ǎgF} a׷Ku""g(S^J<lw$Imc4+F1rȶi:(l[mm7] οn9W!MW:pJt$֏b̯ۿ̹s4h"ՙ9kN?}ߖE~1y]'%Vͫ,XS'ӼYWg9b"2y͔,~\%ˍU<䲞>{g^вEs\b5'a,[F|ZbcN9?e=m؟kףRk\O޺|iωʐYavѴiuk+(<\]ԭ] {;FƉOcLꌫ3'?ZnEdT$1Ocdڴ_vbxyz`ggØ9_F։J%݃_ $*T;Y{k”cK[6{4<|'gOr|2-C&Ɵ)U%̏n}: Β|)+\dd$NNNڴ.h nܼM|y,%4yuv/QQF\b)_Ȫqpg4krƎauMǘ[2=iйW`=H֫9vRwĄl>+ 6vm[ӮMkS(S:gj7rv= Rζ=5Uëxwi B{JRH JH y󂈽=NNNjM\:s H>}֟aĩ&,c={N#{kh4Hd2d2irx=.89;ckggbB`iTZUk2i4 6=(8 |)_^}MOT(;~e+VV݆l27d/dYߔuc|4DNf]3zeCB>zuV b OUj7]{hө 57u^]oަUuێ\Tmx2kWn_#*RMN &1QABB<.NfXla}G'4 KԦ8{r쿜;}Ŋ1gmW=uڠ K~޽G4Zo>}כbrze3n];kna5:/\dWk9vx/7dt?/CGeؑ|P uێ8{gnۑUcݐ$,K(Gܺg+V`3a"VVTJ^x3s!Mv^m7WWM̧=z1qXC++7f$ 7l6iުM2̞>'Zζn\WvOkr+ HL;Bޕ~_fV/]Hrerˋv`lt{vgȭ-ɃE\\%vE^KQ?CNHFB"OÆ )\0.nHtt4EE ݾp".yƯpaL>3{NrzGk_eXx9kOgY '!!H;OVm Rtҷ3mZ7D"&Sne򥋩\2&N&Q@nełsݳ[7y4 LT"A" hh4(/\8olfZX8x͛7ѣ)[%veJpHE6}L8&booO\\Ԧ 1h/3#;y{z|ҙnӛ>i\@5U|<~%V.b8:8нG9[ӻgwʕ-̈́ISY0o6j̱0y{3p2 TT"A QTH HJBym YR)>C~j_ˢC6̚3E 0s\:oMOLLZ.J.'8$%KWԡ=s-d9h4f͙!2̞RXy al $;be-⧳?e=QZ.Gn%IX8ڜN>Rpg% 1ZMnhyK59r@4%&?-$WիH^ErG911( qww[[S%CR ^ƎMIxyz2fm2gBW'{QzUG~ABoԌ6:Qv-~d&td0\vKuҕk̝>IG&e}ʸQX&;1xx*U,'}Owsue;xo1d.H֫9vFz:nM[ wqA,! ͞=Mi IDAT)-Z7h04f׮]ԨQB޽{iڴ=W.!P3}"}>n5֯cLí&žm+Dt&Yu0AP+9;lV>^J%AahmäE,&mw߿׬&6%2 ^^>>E2ϐG`!0)*VR5_ʾ(( zŽ]i]D9o=  wݐ;np#GdYA*SN?)) RiBR G9tp?1K"7J$]vF%pDEFh?LL&ÓuPD 2G䣮/V"W,,,(Q,uξ'=q#O4o_QRyFnV ~ı#˭+W.`$~Eߩs~E {Bll^'qTYԮ Z|} K>ze2 *eʫ"X 2 Ȱ'x7fHD=0ѨyT*z=4w-E1mFϿ'ORju|KӷecnA (LϤ .f<zhk\217 ܲquneFF26=;@Y$$$b!e|?#jtdҲ] -^Ҷʥ+U*-XDj5)Q,'6.NGƯfzOgߐ/)ѻJ@Re޳7QQ:>~nUעdr;te0#GY(Mz *2gY }#z=\J5_r5KC.3w.]b ξ!_R۟}py.?CbŘ5{gr>=qH.[ ;bhFk6.\_#emy;fL ;;;&ǁudMkm9tb-c9viSqss͕Ӧ{lk-Iӧxi'r,ڟz;up4oO"99eh_.\̢%˸~zmx9k=ܜwfY56l :q<͚66w ?agFS˥ݟz;u͍?o#66r4Ǐq*5ȅ<h"<}^3Rl\Fñ'?i*O͚A>`7D_S;::PG'3y BP)Uܹse;l$&$b## eʴ|`111OyԾ^2wĨqܿRFj::;ֲЯ/>W^qu>WУw?""#` F Ƃy`1ÃǾ:zWFG69b^2e 6`YxA[FA>AҰ^ͱx0gD&Ϙk" sgLfxzzjl1k/E: 2GTf`gk _Q5k_{%2"L"092 w/6i4` XUv;rabF,2#YFM) VTrC =&c6.U~E :2 l3c,mu]=!@}^GG>ٱWvQ: f#,`WNwQ:27~M: ([*&L&QJbeԨ]20|h⌟L}̜?l@,7n&+UH &NFŪ5X&LCx7iޚwiܵ[~=4o ?ǎӪmJD&ٶ(b`ccCre>uwWj )e];Zir$X8{r쿜;}Ŋ1gγ=LdT+V֫~gN"˙hIN)GG̚1HJJ2:Wc.ܹe+ 8rh?OTÆ9{<aaL5G{19{F0jxF~1γtJN5ڥ3'Nn:SZ&woj׍v!/Z9r?rqcFҠqsiS&ziS&iތ;:w~WƎ]3szN*Ȅ:kQNmZø#3ex~mwo|ڣvZ~KO߰}>v̙xSr"#"yơxv;1pIC /2߸3qj YAmuQ#࣏?YԨ^-Syݹ`).WխYs,^85_nk\tE 'Zf׮c58;0mD7kSE1"#prrng5..]ahj4 MjMW-EVˑ`1x&OOF '..Uk_4yȮǏ'#փɓ%/ٳs{󻺺\^dg\\NpHor͙cb=wwWWO2aTT*}Gr%ӮV@j2֡]f͙OTt4Q̜=6j؀9h:g4j@+3lhݻORFڌ*MHH̚3ɨõiY=3vʖ)Ư%!!L.GG.!r$X,79RR5>ދիQHqvvҩ|>pjT[>T#вeӫGw"o葸Ѥyk4o'cFЦ7nԐڷK ۵%**ƍ-a\l颷/[P,Jz? ܊yl7kgy0Ï?^:ؿz'F#e .]BMyL}wZ?=ԓ#91@ ܽuF РNTI^WkAR`!`@RV^TT*4jv=y4F|.B @`z!*JLWPUh4c~ ͂  "K̛ren-~>X$%i7=s"I@!Z zũCfNN]sM`~s͠@ =^އfѩ@ 0 ,Q`!B @`"X(0Jnw='Q .-- cyri|foK os"B_W^qFzu FäI˛)SEU޹`6ca! O|7?q˗.r9vM6ڭ|YRju)Q4~IHqrvϯ(K-7epEzMk"_cOOR1eT| QɅn{KE;lf͚7̚5l1[ }cDGÜ9{V6cL"s;ѿfo}tؑիV0lP."1ިDZy,\ą 9{4!a)zue,qMW{3QRUq =ʿUچ'aOϏ i~3ww-[B 2Եnz,XĞݻQzifϝ;kztY- boovppK1r}v0q_ԨYeʱw^mZxx8퀀VXE$PRbemϯ(zeZA'6Mpx%r2¬EuBb2 MÇ8!vܕgxx_JlV">ʕ .^\r&(bV{ܼy$4 JR.;(""#=zA]6mfze qΝ} 4T*vݺ+ղOnݘ>}O<ɓ'L>=a*XtЁ]ʤISؼӐ3DV:72)ooo[dGIsqcP~=ZnC!'zDݲǀ_Tr4lЀ>}zڭ|  d16( Yh6c/$CAfղK>:57'< 9}> !!(DF!@ 0 ,Q`!B @`"XB (DFQ`E^L2BB ".N {sm&&h^>tރPS/y炅b 6Gܽs *OMVĬE~d(@>GDҍd2 .+~Fbb^YH`yN:TF|1K.ڭ|Y4P !ÇqH!(Ucǎ{ .\;wn̙DE5kiִ]ɗIޒ˗ 3occ3)Yҥ~/]c޼tee"AHi z?؛Y,$CNi8a%ٕh~WWV&*d EbAxxĄWDEhtdR#<<QB A,."B @`fwϢlܼp2ݺ@ 0-fٲHmԾd D(_!Mc'Ow[r66ԫS#!^*f4$}c}1dӖIgjըT&e0߮3k{k4p6oX7L~%ի5ܗZr˄ôrF )y#oHF ]{~<^OL4b@>֗F ˉ&S<=1yl֙T .|qAKWbnݹK||$G@Yv[4eM\Үx{c:RlL6|u_mގq#ҸAT |o^LMWڥ|Gg1v D:իxhk3uCkҠ.k7|ʵ|:ב yJS$>}6͕-_Ѷ,(_4ApYf[_9)ܻejr kҢaMܽijWrb%f<%rKBBzlfi=AA{S{wcL4 K%+d1,^6k?3,exya_oG0m/JUƼ ̂ٹݐưd:B'ݍ^vo ig>=W\vT*/ڴLJ |i?^ӲYc襕ӽ+cƿHaMoҰ&L'q(E;} LF^{y,4G?ꤵy!S_/Ri:=uW.:Rd$ӣ+}zt՛ުyZ5o7M k l j {OpH(-5[^F \`E7laWQk|cFsMv :t/>ޓ@ab߳ JXv-7}˚5_2ϑJu{9zDnmm Yξbbbe:7mVRJJ)e!]:Kٽ{oܠG9?:}?,J"&&FWh޲5uCuoظ.?LB-r9c#пr<̀>o\!Aܹ}Sf\*Un&_93vN_w{r\/ sDdC(CMOHH@nmʊ`Ǝ!ܹ{$4PT:s?kZ9aJncjK1s&K]YNz֭oKfΜY~*OOOv_~bt+Sbv786(M 2{*u{t?׷vSt1S=+A̢e #d <`Hqrvϯ(K-IOo%a+H 6eT| QɅn{Km$Ǝoa<<Vr |>h0n{2h y1c&QQܹsG ʟ={gN  qEΞ9MH#lll4yV~9ܸqsgpmo7x3x7qU1cAw<?mK˖-1(d"S ɿifVX/̝;ݿ֦oa6ɩKҼm?oglyAȝ{qtv 0(â(i<v; :ۡTXYg_O2ՙ""" GUǾVjժ{,<==yv;1xyy0;Hxyyh>qwwϒOA~ E /F4*P"C`.;(""#=zl8AJ%׮][7ѝ#Fc={Θ1o;9Ν;:tѣIDd$F.߮%JZ4j(KǍChպ \ѳ7wM2e2eʔz.S"Ei#GN:OCf͚e˖lxx3s挷.@`n$wCW5*a:c'O,*L)<+n$/Gصv]޼zUf3v*jENBVkד 7U'83s`9sygwFxϹ~?^Ɨ{r6lφ8־:y` zɶjjjp=iφ eeWhJr̤ͣ !Ѐdfeac $O!MRܸq bRS`nÂfh6m5Sτ7Uim%/MӐ E ))?so=c/}K eK,[[D$ ÂdaX, "aڊg}NSdh㛛3TD۷H qiqi}-LDD)Rdҥf/*ĉǑwDk"Bp87B]m5㑘0?>7??_b7*"[lA`` Ԕp)}-LDD)Rdht*"^i777ܸqCky_ af߲GY٭kKKK*"hޘ=,,N*=ju#0TD?L&QȐ;B; ӧO5 SIdonnnRETT9r9:"#]fp{$$,,~WI-7&O^bHThllBMm-W"~<%AvXzS3^)R)ri`?mm裏 3sݠ>oM#};vF<EbYr2%'+d[)ڊA8:C_2F )8j6t0^q&"2zmvO_v itDDF#"2 a |ǘ:}&F ̙3:߻1,hJ8q$ݣS_][CWzonBQawEUuv`PSS#y_#?7j=ѣᇐR{7'MRٙcTrX)]Ě,kdj~:_@~~M;nd4K >6[WWZnp^kiWW^2(aAF{5?~ uwǕWr;Rc0?JK}}}>2-"K20 C.NXW_[j477cuVf~pAg:3jjĂs%1Âfiiк+RSYbڌ8 ,KJ|=3ף???̌ 3B@pLW)bnQ4k43xxYlOC yY|&k\p!t(>ܱ _ӴS[Q痦p˂aX, "âD6R[Q|X0{X89b۶툈a4yyG!fMķ$ p$&-E[[.^lDQhllz1jh466¡-[;wݣ_dffgCER d~Q+* hiiOy~r[?xL؇01gC()By(,8AƍC#((NήarÔSQ^^ڳdN:Kp\ G΍hA0-WEY]t+5vuuu;vcǢq11-ϻv}3g (%]ņ]슠`\,||Gwww\r+._Cݕճ())\\\ ???˖'c;RSR$a!駟OwL}_JLL48? &&] Va QW_4,YY>gl47_'qGlDvXly V {swZ'Sdtos"Cb}&Ru}n6dz-[Q]UPo ,2*#GxzVb5Ulƌ Wh\p1yܣA0XDEE)18PMMV;z?xy^>P4?߱AL: Q1ؽ{@ϦGܧΝaAajVq Xr$QR|Z(SL>CUe~{J(++\ZRRoؠU* =/KDS%4Z ho_>4Ν;v <=/%JπZi~e qq3U"C 0yTDFE!ۦmېʀfy/y xz`ժWk`;}#++ Gl$L> W[j H[t7 ٘B̛E )pJKS 5HO_ ^yK? <&ƏG[5<#\t1,n@gYr2%'+ q !"YD$ ÂdaX, "aAD0,H° "YD$ ÂdHDšo#yTŠV7 $4_F gY/?fmb!E 1ԶbANήؼe+Ɔ-طo?~BFa!E ӶbAp1͈nxYtW ,2d  rrvEiO1b5=*tAdff?_A9Ĕcp*2R %$,Ud;Y}6TGN cKWbUഄ"CӶ.hYh-"CӶ.ht",,Ȑb>ƴmړ[ r0 '!K"C|ȞnMr 3Mrz3, "aAD0,H° "YD$ ÂdaX, "aADDXp"ӳɤ\G ƺY]XE/[JX7E +S\\.zxb޼xk7Fi]>ٮׯw,6/WlgccdTVP@kU] }}]7{= T+VI"C҂HTVk+Hԟ' &}(c267b;G~g=/o_89ju$dv܅ SS?œHJLDI\kk(^PA"O7l*mݰjmmpqq JJJ $0\PA"?{rr憸JwŪY\Xl߶ i xz`{`;u^% ٗ7 mEݰz,2DdCl'Y° "YD$ ÂdaX,EȆ#DQD@Q B״Qo^)aAd£Aa hBAcp30$n° "YD$ ÂdN"5a8|}^/6v "um^/$s7daX, "aAD'pjKzИAZFԻb3,섃Bc&q:^m6>ÂȎ8v"c/`0Dr`XٝA#jT88:FxFA{| A(1s\ )ܲ Q--mrbֹr"ZZ$1,lT}sq>u Q]'w\p7F AhxX GGDh |dXtHD|G H[ !"YD$c ua:A~G2,xxʥz5x66ΰIn{Me%Vt]$"$<'=üdv@`1[Ё |~}dIpw hye:τμK06,$mV)sf#{^ADSPMJ,L5Q`\ŋ⧂hzYhDsS~r;sf#W0h]')lي q3xȎ}}^E(IENDB`sqlkit-0.9.5/doc/html/_images/o2m.png0000644000175000017500000013434411533706144016727 0ustar sandrosandroPNG  IHDRsRGBbKGD pHYs  tIME4&> IDATxw|lEU%[ nM  !< ! [BKT0n{Ub۷Μ+ ˲djߜ9;sJmuʊJJ)xxxxx5.bQ+@%qSVS{MRZUPh¯^Mjm[[W^yxxxT!j*w&_Zm1& ǷTTD9 %%;C8  R@z zcC13A~k!>xH %B,lnlG.J >B- ks.TQ` eȊ BpIo|D)uS,Na!8'Ka˙$i!""mw{jpn ǠR$iޕxxBD,V_S뺜J!D6UcIP5*+%e陹mm 0% a{[gim-Y P9.pL ks.q8[[װm[ewYYW' [$@O>E"Ź́P2ܴ$vtiϞs O&FU:skNcϿR'_^_7dQv2OyGN۳򽿿zV3O8jF:M,ӯ~Q3c[ s!23:2GrlY[PCN:qE;zÜ'OfSs㢥;G0׿}wVww_>tDg_ڙ=# N$N:pLjP~iPUV6G1G .c~v:Gڶl}fnvE޹g*Gϫ?~?ԛ~7>+qO6ھ{$\7طuF0]U!i)Jz^nNy랆 Wܢl_WgXpSc's[ {s{Tںek󴡅RHSs'/>HtaeZflo {<[o4ջ4?Z?5եʏ.X2e͎u{#E46FmwnFi-?0ǔ^ytn2hto?sž5O sб9x;PI]/[rR461EQ[[[~'dggK=c/@B%Y$.w$D33o\ncd; 1M _:&SnNcYYi0Qf& Dzݽ\%+:ۖ߸yBֈ_]~T>6=o'L܈cO, |ƈt"jB]ז%Yb$ԟ3SG \Q0/RDЭzg6׫OJN|㋫21hf{V%xoϛwwy57Ce>ww??8wN)s7,vUp8"r-XXRwSʊӈ|}BԤpg_2pu?w͢D[ڑ7hc.͟_yE>4޷?n7vOps-r67Bg7 7]^ 8Oo7{tc7L8cKwmj~?4EěqpP%.2?^T]O-{1iĘD$iB|*+y3\}zj(HdYn ي`fV~K1&]s5,(Ae8qiw]yӹ>)4-ݑ),8Al۽3|%2Q+,Pi)<HTLD!xݼg}~6燄Tqgu9{Ѡiʳb1G"6v?a oI< M) H%q]n;GI3P;s1,GVOB͈zW7_ o}K7~5\wTSu͌l*t)gT8ôfHpKf^_u{OEwV4VGQWs+;W[еt/*8~צ{,m\BK?߹z΋<4W zHǟy1|o&FecJrv:* LsDΤD&9>[-O=<oeUeFL;ٙ';s*(@HE9L$Y˹p7**,r̟nsx]Ғg)_ZVK+6Z%J 2w+qVQbTtBiiAyIQBt⡰iygm#\_li©ꮻߖv͵S>za/pƈ+-/=Ry=׵M?yِ3~u/ts`3~sBvii;D\~G_^|Ve4y~_f2aHsĢu ƄcPG|8t9"B5h6Gtq Q.EJ*ے0';( T5;bc$Z҅d1ei+'ID2 &(3.Pjlغ)\qfIC6դ-'` *+1% 3P-QmAtI)}E,Rۯ \IpaI6RBX2XR5fQ qPJ\'.e&'@)$CIJ8imǤ4_l9gblͺI"xAC %LOٰ1Q@ ؾs^YU=͑pW Wgeegg:oȖYkS3sI "'2|V`d %vLڦb&;9CPB5J'f tp (̀SZf P$5 ?ET[c_"yAӯ$h :u趗Ah꒡QK >%2D)ntU$_@PJ$CUMW#rj\NP1R42Hh+Eia"BpE=,”!wR@P LWuP.@ V7a7a'"FcIqaSqKefS 0">}ʲBb_׹HӴ fB_+F-6-QmR;=crfnN"Vt~'yL%9&O1 JDGU%CU1&M ʒw?]ZY٘~1ZpPmDDbdmDdZϗ,lb= 9GkO` "Qd"$@Vf$I]J+!vp<'@VfH. SBm:!2:V2b$w|`S@R$Z'hA>%WUG #R( HT$l$h2I2 #"2 (\"yOJ% 45s/u^ PNQp*!e~ wA/Մ.|BHՑ"־ Kv{ O=|aF7_Dv$|WٹޗV;@ ~gMKHêp5[x~ћA1y6gT~ $tH ͸ O$e1B ZXetNݳkI}$QR>1"7"STO<7ldF wS sX%9'h4LW*~]v´]LV~8h\{DOM#0i%c$G i #R`ƉGJ R^%fs/'pN 7֨IRU4MWe:TՙPt]t]ԯdҤd%3#nHt2pN@Oauk(_cF4lE⺬~/ArmV:p_^SLp~M"͌E4G"qcI%LSC-oO\S-+-r-!\c˻o?_N-o`{`YхXs -qcm;ݔvމPR虴c?92'L4/$]|Gnm{XVl֛.ݺp/ivN=mƍEg b;#CD/?7/'/MK`=Esŧ늮|!\5I=d|G_AE!oܱO>|[dK/AD" +˕ŀ( 2 _4L m^ DA~R`IV LHr# !Do>Rلo*(iDE}}N8 D@.p5a8$!^c[kc2l"u$#/;1Y&7q19B? )w~M&\B%Fx&>YX6B(VWD!TAz/mxZw|,)[¥u[1^7ɐ??Z8v@LHڐ1cN]rm ?mE }r AG{LEB} 8e D]{fb FҽBuݵ93;視ƘWF(L;EsH%#BSh1@ff=X$CտX?ԊNή]sWE8O9[֔+m2B}7šKgy{ *JI@Ӓ/۬ IDAT׾0ҽfJ" >fOkxO߫Ee>Ҽ!!:+.K[-W-Z 8U͛꺶֖U,_*PllXi8JO7.+.>rFQYj_8LKwv֮Q+dEv[RY-b|1o2R=g7c.w%I-dů2W )?OǏ?3\wc]$@z.Bb.p\ W]G^GFS.;}u+O>|ڤ1@P|>4]W*use.>M7̗Kݙv_,)ivl8gغ~Q'xßw~:%@$y_yb^r09m软_p_$՟: -%蔥Waןg='_~5&SYtK,\)6_B%-2Թ G.y/[ˋL J?5W>#{m=}|[|l q%6BlaRJaooY<~;: v@ PY҈HڜrdI%BkY?e|jRDz\Lm@(SUQ"\ײ rB4]YWlLda TbZUTEb,IWeqPLڢ,Z>MrLHEaQp躎E]MjGV&K(%:p$LeDQmZ !J*+ EU]L$+Ip*I"(m90U_&EQd q)$..%Mmآ}[YIhXBi ɲ*3\AǶȊHc\RE eٮ$kg6k رw,#9tvu mW[Cl+ ˴)' j? y3-* }v>=U 7t;*&ؖ ];t}?Qkn縊m[kﭺi:);䦝:C{% cNesJſ+$9iT$s\W{o4=˶s\yυe<<<;ϖ|Iʒg<~0((k֮}?Z <;iӦ:cx!s=~0qGow-88NYYْ%6l$IN)8e""&kAJTXX~u9`'xA#u1RU]k{|:#2c_~?gܜ?l$@ xE<>7d`J PI){sssqq̙3wշ|Ԩzii4<'e頛mrM;]L>Bt?`}w!\ Î*yԶ:MrEڔ-!ml a%oc&;k^3,c,ְkO9n&7#z `y'] r!uaÆJ@rn'* SrF(# seY6u"f 0ۃPңwaҎ ~n n+84,#:4fee9S[[qʻ9B}󞛮ʚ>]^D{ziiMc)MҊO6G. hrĞ:tJ=G?nVo&5޸*^}&. &Lk%1g9C}SϽ'(sj@@$r`0p.8݊(/N"L`3G 1+ٖmzr)ASQޥd#b$ްa=":ǫk|=Jw"IeK%iiGB @uerN= PB1I}a-P6sxa4T5%.(%>+n[w]чC^N!ڶ׋K$L>_Mt;h2{=q՗o=/&5*S(ISW4 x[̎S>݃F0_/ &qv aF򧯽UVm-wh^q8M]!$)Җ.Cr =<~⎈s#-nm rILcg-ݼ݊[e[7DDDtDZN5.MoOr |x۶ʻWex.vPxrqp,JX]٫⮻upyy΅t0Mb֋ 17/kŐ+ナ|#{$ܷ4.+)fL9 c<{Ql9جH!fKs#]嚻7K+QrGyS_ؖG& _wY e'>/Z9$ 3n,$V4씜ܪ*p%p!ݸqc>cKkqIÌkK>mkΝJ [@ 88ۈx8fvh4w笾j0 B 3$#1{5`}ز\+?,=P@E$4%;jъPbR&7 L ' qiw 0-Nn^Tݛ$Iy0DD.1.E|:nmaQ.2$4|D" `")P]jE-aߎZ󸁗" Bz:t#]`Tɯ2_@Kı~")(嶺 Ќ,vUnXHYjwk:jsk(3*ؗU1#%LsW5Mf,X7Ph²y'"DwF $J}{=<~ž{4}~Dm[Ӵ|qAH(J"t)eP ^:!D@)PL`49N8ܺA=CWX,g%&.+hƔIy.o].QנggnUfo}k9oV[T()꾉sR&+V\6DaA,*1$3R_YwCTk:G-Q=c}l.$|io==A+7""x"=<~s*F}>i$!8d0 J뺶mG#D"Q@JUU]:[ǻ-#ܳѣ:UREc4?gnҼ#&nx,U~ߨk.G81/>6֬!iC#ӳCneA#SRkB.$J+_Sb9DnK,{"l3c$zc5ʕTm&YάH({sf}Sxc ƣA͕#-9{xL$ !EL(횱ds7iZ p>{EG0aպMCOMdҪ Zר҄jWvD16T 0  !NK̭e8,P%'v  Gg? Cw(q>K6$*߼+TD^[{ѱV˛wIQ}Ҟ^vvvqqq^^^( >1&,˶Tιm[l=s\+dG>l,**Ou]<3\!@aDihˍG3 ~Fe{-F!&禞 4ūVHrxu#WL{J beuywDL9w@}Up۝rQ.kII$IvaF!u-LILU <粻XahIh칣1 4TZiYib^z.y1lذLMd. =JpOE 귟.}i\ˆQd*TzfD+h4iG<}=UdƩJd 80 Gg$‘4In X͖/zk}5 \!l3|1i+}o9O8:;7}Ye0\tmeL۶64+YeqIQ:D?^q;iD(󷤆 0gϾ@K1y? e}(MӼ+Ii`v8^,w@3m. X.4Է6tdM5odUͼeɆeLY~܁O~AwIuMF fv%7YU{{g3 er7y1*!1&s+jyx#w<^c$q17-eRLA8d7G?swRnkDxHQ`Pp8Dq]ZZ `j ,Ī dY Ro;q(Pp.Q(%ztBDA"Rʀj& ;LH2I1B(^GcҚt7[3BPf!ZLL7/T1###t% 4N B,۩b~t/UΚH)=ڂT򇠛B`wfPQ] =<<~aj5~#!;bj!GoÛቻ'{xxxxxቻ'{xxxxxm_"oT2&ZݽM"ƣmW/V6`?{UMGo -Ov;_Bxwo{W-_iۖ5'?)5bcޫc:z[lc^ F!")0Ex_#wMVB-\ʅ㝤_@?:-ӃOz3 <~VBSbm޺nkv䫴QmB5'^>$Do DݔAį%GC2Nv){+)Ny׍qHW_zWO^7o#BiYag'4P}ɒD(i6ߴ|ڝ-|~ w_ȟr [ v^,8nh/ 6#9w[wdgsUKvc{˫?[Q憐?*/J7/rg7 Wsd[8nw3{yǯG+dUukÝ%%rP~ 2$^鎸/ʤ4Jejc"q%:/ wTĔAͥ5kwTA a@EWșyEBÅePS`=2pӜ9??RAָ';z#5RV~L{ꙫ@*ADoMFIQҗxG+ǶTuTdݜ;n~lv""=r<.+F̊%nGU[?7k\u5[JPD9yyEE9iuOh+˫69g%xӎ;_nV=s[46߱Eaų/\j-~0Oor ͳo|Nb[`Mor -7~NRϽ%QUyʄxdKCr[QaÊZՍs Ӄf:qln4oȔIHIr@gNm v̤>!bo_z湎spa>A>紁Y կ|74X#Ewm6$s= ˟<Ei&D oHŇ]q3U]r cFJ]X2N {-~4zލNs~rCӊ~]`Kf޼dϹ'Sgsd{Eu'9G$"Z\5gL9vܚgדw@$sPϺܒi':a;Z A3]kQ5'U ?{'E_UwO0sXr IfOQ1 b:x#IA2(H9-sމU~"} *~߷{U]eik%A'u57|~dcNf&LttOXF19+4̲D8V`h _͢\8 iA#b5p]. IDAT (hDZ0`X]ejnRJ~p}m"5e-:M(EũGPj6X*+ڜ ͯBB# zj]ûx׏!]w.{i!tOKc+H.HXաjO0,7A 5TuZSGMAY;vV $w]i]-6wRJ}ϝ={޿9Ge}üY1wœgmqdۏ֊-Ch,!|ZV['iر 7F06gR9K8Pmu>#^VݡWR0Z+WPdrc)]+4[( A63,2ZfM޶sq⃵qFtHNM "յ,} u5JkND&m•" :UϪJ"NĽ;7m(I@ r7ߵ'g;CT;9v{z8AQQH*LV ۫e.2#^R[7EqǺjxR+u]2I.i޶/>=4, ȹToڕ߹G]D6;EGs9;TV(2ƑPI qJD !3.P""4y׹SIsDL$ɔxJ% B2!*QJ L9cM.Dz* ,SԫGI @()hL!,! qT()MD%G"+2R+$K@dL:[1YxN-iP[p5!3%8 O{x`Es L4 π7B1%>VeW zsθ 3A߫\31W~m+ |}SHr-VK#$`"m v+(@)^Hu={ r #.n݁';rRJ[IcAm#lg|TLp|a UDKLIHn\dBδE"zO<$"Xp ˾o9+W]32{H74T5`L=ڥ0]a١3g,> h۽k#LS^)C]nQ9E~Cw3䂟gcڳuf!d (ܻO9suF;KP9κe9G^ lp*K!bZ~#'!@B$} @f6gݳ#;^ l+SV潱SD$tʹB;?ppC͞0KȠx[_O_摗oK.fl.Ry#S:|Q/{Q۞ֶmqAjmsuص6ίrcJu}jDfNĔ]%-#ڗi,_9&PK}_J-6'X56ѷn4}2B# {@.]8={RJ~ʢlpB3=tߤYn7_* >~r<+k2\M*77;z +( ´xզm٭zv`Llq!`Ih܂4{pș$wj"k3vH/\1g5:k~LyLIuiDhx+ҴYmxw>;9:~㍝FHupװV93u?}\pTN?lԱ?9@<юa%e}{0SyU:[4 k}m|Hqwo0o d rA;z]v ߽}R: uڸhVz{~`O`/q|?7Ơp3Z׼Sì7t%Ch [r'ܽ7>H6DtуDTkK0i ív7i>u%$e$F N2$c\ٝUxI5?/dֽ*I~d'4BLd~{s)2MbjguoaJ@.<1EG'3MZ#jK݀b39k]CU L4>Z\ӹې0$[ZK%kFCx f``J jQ+rW*io?"\|fm[n⏞>'"%'r]ּ[g|Kz4@XMq]hqV^>9hP$$JROH⣴Q4@ϝ ߡTQ ~tot,~J ˊM%Mf=RLEDI3 g/!Qa溃+I;G,QˊPgj<SKhdőⷥp(Tڥ['^SuwT{_-Iֱ W"ۍT_o; 2F' ?# ## `՜y@" Wmeӟv6oRgNcȖ?˟4"Dqַ8,0x;P/ J)h!Əyexc}%e@ϝF;S$yu/]:F_XLm5!Go}i=j7DqJRoHE&R`{@.0>'P`qWw(m]:CFzo1Ψ0ZdWr`緟qObBޣߥpDOeg{fe J K4M;-ɊsUu][֜jn"nMu=  )W$#d$.# I=prxi iL њ(IxMub۰&m( .GD5iF- YMuɊT'빓ֺ7D ,9>rNPSKmx|]\ $7f†4(BMµڄ4;Qx2Z{G)DNڌW=MuYT !*K )jl"@g `;@zټv"ޖna Ky_coH3qQi5)l5xZ2D^ 4'u6gM@)-;BxsA4b~)HULcwb$4,Tu*6}WR6 A@}P.z[zDxB?`yo߭ *M$Z6\iz=@HrQUo_!/{ Ө;\:J%_ӆć!mGOfӈG^!b}ف!' > ('eOzFC&6HW5$g@D>``V괫.GphHfP?@|/< "/򤪲eFdKaD42!`F4 z=WzQ <}  0d~#lu89A}8!EG S%#:.)@^}ݻW7rD}GͰ'$zEr?һA?(/%>ui-`bg3ٟlRDC(zCT~Hҳ}V z+-c?LzH=ԣt>#O7u=Z  (P o4.}vY^'?Eq&7 JCBM`@ ޡU |r{BD#li z&QY#P Bu$Hg)z+MM8㾡c@GD6,uG^no^Xqv~/Y7xoC^ϺpG G|MEIYiOr̷ߟ@/l`z[Aæ;vJO PGg:{H7j"7^(8l&>;uCy c~FW.77sZ!E{P??F(AЬ^_xMlh/r! ??M/5MTg[<6Ao$Vcz eYa.W]a$}&78C|Z9 Z\!c@+7Byy}>xz}7!Nӈ=#n# `ɟJtc*wsnي?zM1W>&*?xpQi<94|;&V3G>Hݜ3TECF7R􋄡@o +Ux &>N}x(0{} FO1V"#xC8zCˆ~j7 oxVOx&z$]mTHp}s厀3 %N๧k׶S h>Kbv\7^W[CGR(Qp΄_ {eAGis5D l)mG@;4Q LS҃o#ڢ}JpX)k-!b21l;gO9`?;X̾W M70fO7C{8{eɑ-F_60̹GBPt`1"))kku,R Kkߐ]լ]rg6vtg Qw2^؂GՄŲ6ԁl2&_C؄h@j/XJ ܯUV[rSVmpΊ-г IDATQH֯ۚgca-۸\s4`@ـ9uJUA;ٚ?Za *s6]MBes.x<\Rs&Gѳ}GC>!8kɢ"?Pp <E k wlbg\!=93l) Rbz=E.>gim`Ǹe?HB8?nۼżSv% >( }m;Yѥ ݬ\tƎ#Fv ^r]צ2$3bu-E&.>L|vQ=LEW=ҶeYZ 9ZxШMsgwFk؜*#|quHzU,~)v~ðV-+HIȌ1q,TNFB~4xZÈ "TQv{^=3a?Rå?V=lVQ[/H}^+wZE}|YXSBeiܿ-Ϧ "g式&_ݟZGMnEA=ZjV>l#X҇nӧsӮolͶo딢p&;WVk:NrBs>4m[~e7%tG׌_Emv).wٜUV}wI̥c/fp.}wCW}(HfLJ_;qTâC.CD:,~oY+:}Ǟ6'(ؾk"e{낢ݕylMkjbZDŦ5-k*K,c{ l \ qԱCkg zl@5\Tv'u:HfpEz+&%o3 Jb}VqG P":iD\Xd^DJZZW#vk0C.8wIjWI]fcå6ûپ1nm[b+wM9qfۏ/+VᎭYe}:;I"TK}c)ڎ2/(͇Hצ%}8ڏӉvo{!k-"d"s9Izoٽ{e67oP #.  =* (c(ܱ{֢Wvg. TtJ \`Ua*G{ `PncFƅxcra3r*, ʙCp^Qpk`yuAJ#B,&y(6%QѸq/ʦ‰@8"z>@ &^ݣU[RSx$TèZ[΁Z"bΜ48z^!(BODa q:!)$-䖭,>ںS6ieޖw14ޢn.a;K( " @(ZuYZ*FA['@ ڃ*2J- 4"2Dq,P#8' HZZC^ͨV] @ʻsEp0DMu)%Ԝ"Σy T 3j-3 l [h}K& fU9E*"/!hPd4s% J$>L,RHMl6K$a"+DǶ%ipeP3k[2-޺ōcǵPHߞ}YK'd1.B<-ڎlIhCv1rBw*ŎQ&pU",QsX4W4@| 厜"D. j¼giU’*3M&A-7eTqd9lߣ5 f^irٕCk%xϢ훮BP 8g c3 c !gqSRךm5ƺQ}¿7!2;(J._/,\Nڣ(  @Xl୛T"Pn%S KHזXkc- 'ӹ4.X );^`YiHZuЂ8g &#߽/?|ںwlLڤJ3ԐduقRR2~EUc3(g P\P!(w Z-[v[ܙm7 qׯ0$e}Kw]2&Fpެ2@C""55QR5qbhoظn1͔'f&DD|sĴf2:Z-[\ߵPrM㇧}<)-Hj\R!]9gk*?׿xhZf+J۹F_}4,ҪC(\dvuD3Vפ1{[RQcF{B|(ؔ0seCR?vnaR딤]ܾhܪ$˖ֽ$/C&8$|ܰ -2Uu>qFAv9׭\-U%;Jr sU5ܹ5":&H5ŕ LQV೮(BCjk|+eRXpVVZ]Ua)p[in951G dRjpRQ;n𚒒j''RhxD\dfr Q@]W[RG &LIXxbEv %.>jj\搤H(’*'LAI QjILDE~it,|eXQƐ(U[PA-ZUۢb" 6J)26&: 4WQ#&&BhBC "Әl%U.j d0Cqq5&,VSr{m*'[(((w0f•RQUX\T,Vf3'%E='!ZĘ0 QqV%aiy(+Sjq[AݚL  !jͫI AEeU6UHrdl\l +KkUЄ0#(r{B'̞_Pas3Pq &kJJ(c,4A  Uea\WZRXD ,I.a0HuKMI&6B5,9RJVa|…J8o2I%t"BdXȱ"q (qg/$TVdvcleYܘ;  ye#0(=TP<~&%_pkx\9ggp L{As{E?7)Gt7sd!Yb['@$q(V's'g=م GCa@&@C p0t0XBWw* G3Ä[g.-wk/}㜹.8#\4"wݾkmۡ]Jw% RSQq<$ L6$I& N4Us:V* !+HTS pIe361\rrĘr٨2a  `췚9'IR/e988oK\.f:9m.1 7؅1&rJrRYUEttȊкezQA>wqUSQ y"r`PGye:vX^Q١#sjSR <@ Hr~ZJd*,*)(-/)%i))UA$!X@ RS]ǝv0;wj4 qpDHttl4>թ䥽O ' _a;cC{ǹC~'[S 6L7$O&g2$Ir8aё&p0G8ΧI!'{khop(U5A:>O풃9AnGjuf>')k6fcZp7P GUvCߨ"⌅ #꽣X!һ^wL݁ůj*| 3U#_h5Cvxs1SBjÔq7}meJT1]]"xsLcy'4W"5g՗ߎD$0V1? TX\k^z8ә^Kn7o¢A飦@F99r'fuS}62ާ Vcѻ,*)}~a6|6+X=g;.jͿҴ佳ߛn6{.Ϥ`J2Y*˔Ǧ#7זAojV͛\u&m:8s:!~ʛY SBE΍5);FKummFL}Zi?X7 ΜM?%qѫw\ MaK|*ܙe-=fכֿ^UcxVbo)Ý1Tٻ,*k|[8'U{7=rkDJo^ 9]ֶ7>X83EQwYy&E=v6jGp$zDaeYTXYueyEYTs\C%HӴȨ(t:O)q%2{?KuofʄcL vԹ^sﭗ7%ֽ^?p PW%tyAl%}BUk~'{dZuΌYK+D%}uク.}.Êֶm/j ߯5'NCz(ó~XI+;Gnbt?64s@G;Y/lúl]q#;DozmG+}sx)WF..;<=tƒO «_Uŏxzڵ1\kW+ ɔh902cԴ&[%q-=\bpjCZM?߅e$jO'61N]:ܻI ZΊ[}ẅ́K{CX6 88g>1VmŌҫ3g%/F3>gIJbqL0"1ZI1`S>\=aeT&EUg2ioFH͑?]h{YL|}N KU&}|<ٻtO[< w=+a?|.+签-\u296EO1,+VrNA&1(u~MyWN}"|}guG nV~i7s_RS3/>>)ӻYQ^]w*Z'~sd0LosʋxuH6`ȋy},SnJMY_zai6ѫa珥Aڙ_>u%MbE,ٰ?V>y=>8]vM~UG$D)\[9}c1w#9]ڽpa7@R3'nYyݴWO^y9;}vFm|@|@\Oz99%>IR̦ MeN~ܬ으’ҲҲB%K(J-w"ˊ,=e1j?|Czg'\γ^v5[w9zGm/[6}^>l}#LJ?Evk44i/U/uM{N<ɍɓ{oM_5|#}tG`n}pgzVκ J}Mop7"gγo\ݰ)_٨zkEO{γO9wrӞWN]R P>#BBSڥ'XafI㣜A}0Jm/4,7?oWF)p}c&$E}^Lqnҡwhdm1u^1hxcVޮ~kDe˳A6M(/"l IɑGS`??yƠO0mٷ/{z>f=ErZEum4/,zvJl{]ͱE՝:yZ0枸e[-PBzlıֺ#AGްedh=fC塺_?ۚtS{?7J}1g&HۏnW/WMO7nr=Cdp.}k!2Elsr#zo@]Ztxׄ]lv4g naQ3UNw䈜&$wP@8)yzw IDAT.CYtYsD_; 6V~2A~vpw^ :\1ĸkuȔxmӵdr!#*::*6:jU Gt]:hP5svjr|M.졏vcAsgn}0ӀݛkRG*k"^sd5B^׽zſuP?W9oM~['%w8m5wWΧ7q2ξw.m$ԫeY&+nkzIw]ٮݍg'6鿣[dPcDnlԶp'\3H%SxjrCRՔ JAmu3>ya@{Uj,g?%M pZ ;4 T)wv 4~mB憾SW~WLha[tйyq}a^\ gtBԇfς~[5X?)qajbɯk^ØqҀp}S+@B$Ej6qTʱt*,c5TtO, BhdWOWL}uS,.<8iĉ7>>ip(ޝ³Duws[:EJF퇧ŞvQ2@&Y (eN`W%@d 99%I Q:nLJ2sVT3ʳSR^%w G+H]k./*w`ǟr\{B $H ti|a/bA?ElRTP Ez jm)$8?4|gʣ*,mְ +C=eե]oÊmֵ<3_8KHM]T<_ S 1/b~J~?\5n1@ XPW? 7#0]͝%'յ5BerVl`'O,/kKr~(U{ٴyBb9P=h'=ʞGqQ>vEw[Inٰepr+=1r7/L;NzV y-|'McFra_9^ e?֚rs ?]L0fZn2@-!#y-sY|64#uCP?9&-{|5bDngr}e`\h:P@6e[7e,\@@Ffj,1>\UvR֔`֝pa Ƒ.SI3jpiI Awj:!^RUz.Ql H{ć-<42WX`/]qoC_|8ܓqJv7?5O6=_Mzx柩?o\ |j>GDHkgY> \3c~?)űb}[09R79D?9YdAC;?`ruHX,kjEXĚ]YvQ@C5]Ɔ,+c|薡$SBC%#kL2Ԭz4 FO.{OEyg-N|qw6Nt_(/LO=>` *e/7L(rX9^hOk<cOh,p"Ć?Ge9%, '5Vu=5?( u дsʛs K%vɲty }W4%Ej2(abxhee 201s9>P7jpLt'^/M*z>qM?ە(?oq1`w;WYπWŮիaV tw_^rs.閻47urX~[qx׈E^ȟ 0%X~Ò(p<% j~Xx޽%7mPWٜf{$Zm9,sƜr'>3+޸\9浑ڜ>Ia OǃXԮ#mwM/Ov $o7<3w +XTc3ѻyv~4S,^q/YxWl[RS[:3(i@/O$3g}h\]T)_=Q/߾dgA_ԁ]GFNגNq] 1ē ط'-2|<p۷l9^Ue_Qw=@siqWaw0D_E}tͱzWҼ}'gyIˎWuΟԍчxO-9 W}M7b=/ԲTi/|xׇ&(/nO U@HԜ-HznEq#M%oSVJߩGiJW}xW_~,Ԯ4]~LiEVyҀ~=)Rayv1ݍ>Q%^=Hl9V'ϯqyu,/z#Dݽ>q5[L~Q^4wt:фDGpߞc՜waf%t6:x"_;js vVcU*؀n`Uf̨㒻z`8| N2SL-8^I D`/ٷX (HP_k˩!zǜPF^lM\ wfHy(62?6ogc tFFFlll5*/-Z,VYQFZ_P`Z63Ͱ yر7Rݿ E 5VxbM)̃rH%¤8YU!,M1cj屽Nuc~{%;ؑqp^SKJNp]Իp};ək/-{VQ͗2wEnsnFH\.qTQdxP{<$K\&{]Wx [UsUQϧ hA*8pТ{kg`tNV]ppF!VuQf[u)Qc{{& R=iepĢɼCSzk#jHe2aoP]z&im٧hXpxmhD/UvCx|OZA|dʝw#Ow#MSf)5U]Ʈה9v- Qcn{+{')k*{0/9̀QV}J3V3?|xx}f$cRv3sс'̒<[(Ϭ%FuZa -ʤR/CevQwh\@H3VّL᫫:Lnr]zPFHRes]!/#@v׬;.D J[ҋ.9Ͻ 5Zq%X6m śdZA 6 q}-fsmmMXHhTT/?nYK0.]ff~jڒvEE[o}e@me~ݞo.oZZkM˪׬l[(Ƃء}nt)oyn>Vm˓[XK{iR/󧝎]5ehh-~qoӤmJoq}F5V_kJ7t$yHƲOȲkҺöPZ^Ӽ\-Ӣ_[Ŧj~5:]Χm!OmT#4;Ij0 j͒x} ha.̿mh_x2Ļ%%M*F$ʣ&)7jJ[yk-jđmaCڨH-Tζ˶׋_mSJ ֞kuiMiOpwە=s2P{.pQJcïA{w[I.Σ-5hS;2&ZcZ6m痔T^XȻݒ$R<<<%ںu3g>R"f~I7O+F{zWc{z/eN+ȐAfuF˯Wﭠ)CSNS.٭ִÇTk=ǿjaa VT}PJBm12q;.QETK7g)) 5}H+96驹1[2_jJf+gC:rLr52Z*E7oM>M!R(P@Q0 $J%6*%[L OO=Yv.@6-w-h$DE ڃUؾ{ǟvRBTG>xSU_B r JOô27pp{>9ڛq[G@7=4!*R͟z'~w]J'=" G1էΝ Lo5hܲ;~sr?oά?fU5-CWMvv|eSQw==3/_/x׏ ˾e,=o xzk.%@*;˒UяьxE_;k>r~rU\Pҭ?婷=pKJڜ]]_#(#`]_5CNlw4D3@o^r7ykNp;Lh~ٹ߹pgtBӟ{eޏ'd8pTnb'v+siɳ׍:~ҼElIWTF*ܾ=G~ݍ?}7_^Myw]'bth&zl9U?Uwg>?o`wY+o61n|8ckf,tت0GjBnnQ\\LJT Cc-;Jr<~M@HaJ2:埦;2!q;lIS x پRiL&F*+ΛHvH9lݽQz oUl}~ٻ!>9D`Oeɽdsr "6Z. $nǎHp㐭2{=wϨ(O4zH(֞!gs#RiyCҽRa&C~Zдlo9ٟI1F/Z_\g|~D)QSlscRIEQ0vmN 9Lµ7C>4uwě )69RLށu yZĴL<;pԢ,+饂jj,pE|Klr`MnMO# @֣yOyj4 10'̝[Ę:`Lˤ.78rnYaۑ%yu?V$4xX/t.*/k?|cu[&"k%^,U$ Abcī%,\w/f ]Y?#ǺeqN8:@ڎqe,M~e#o ;y>;iլ7Ȼ3gB4w$a=8in-RL6S7tPɩ?l ^b,91s2o3 F`N)B}OUv[=ҳGϮ*Yۂմ$ѳe<" x]3`pW?KCyUWB-GXπqK.I)4n {p [JW"h3.\/c*9%zǎ}~CfvM-:"<@p84}&7 * 4Ee+@6B-Ǿk3tG=զME>}:&&Fsiihil63 V/Ⱦ(oȓkq;E᪜csۙ*"q<#{սD!a{/ Еp#lu-)!5.{)bއ_v^'y7"z0*_!,f}Ƅzcגi{ k#{͸grԛ&}ǀwG^/cDb3\.âG_YUTmJi/ܳOYgᰇ_xm).RPyy̬"x*WZtd9 Q_byER*2 ݔCHbU~faMB H?EC~0x{W}b@ -9unIg nn#u%Yf >AF2/#_xTI*/tRh5+,=s(-Fe͵bҡ&!-e9rpo̪uj<#\B }Fkˌ-?e4ax뭏J)a1~VX]w$/ #I!H|׌^[8wے7q~KPG^ȳ\Q6?QK\V2I<'uӴD)_/BtwC$=|{JBIyӏ)h-zci^=n`x7&'y{@=vac酷M5Xy}"Ib͜&Ob]~p{Z%E5+}bfMK'$ 7E=O^OLё[|Ι/aCjB{nힽ~:7Ԥ<=d5Kԃ >wOLd*!!f5ߟVsk<̲S#{lY/%ř33½|'%+_6qGbN~k\Iiy?~UZ1%Ej%$HGWa@P6w ;t-C [ 2n+O~~Κy4k ҫ!KA#k PC \N+Nz>գ%u^:~(k]Nٖ{=_l̙.Gfh-]qkyO^?ﭭޫ}k|֟/\}tN 9 # hg 9. |9Ds]'1B߲|#?%/]5u%#OL!$=(?Ob|韬ϼMOgc|oP㪣NՈ ^]~ojq#ybl7DgVx(δzeS)(?;ndq\+SId }ELKBܩ<[0aU%.-DJ!N2jt/u:;{8P[}\=j>/HI9ΊvJI8wg+Ȧץ(Y2{jqG m?mc !QíG5>=r@ QP'E/2XM; vu䑱* Pyjr;#*S C\1gi5U|Xـnjr>|q!& V1 t_wG8z˸1]TWxgQb_" B XrqOUwqT@pt*q dҪO~!>&(ȩjBތl 68m9TAfR^xBy)zQOHg@e_;әӞi2lgfcR7-)CըigiŔ"ta1}dU0:NG%YnhbV#t7ƆN4ua,:?m]'ά}W |?^xF''FsA.}Ms˔/Nz}yٌ}x/n&Oznp~L\L0҅A^jܜv dɳ WXEkgMj牌nG(O58sґ ڢ 6&vJf-GvV^{ 6BI9pMI'F2 {Bp8 ~x^;>o`cGԼ!wh uGN=wRRWGeمy95țw:u'Vka?%=4լ{*k*7\y̫ӭ3+شj_ڨgqO^yƦ+ j._3kΊʣV&"pd\91;8qNw%9~[׬ܟK5 5V6P 1~ސsͳac-HeޞϿ2(KMqW/)/M]|֋۵LW!09^XzϷUfce6}5ϠPRׅzt~ =g~2ǏOf VИՄ샏̛ƩK#)i S'àęs_ܠO1V f_#/Vys*PFLvϬi-U%~MBxv)ޥ s;L*ޠa/xhPvd) ΟT& E(:0{+zɂ{_<'Otϒ+ڸZqq/ϽQWW0Fsu -R>W/jdm&;յcrl6`7p+Cۦ.sX5U'EČF,9~㝕'D#@o2OOm$nno4c9j3zdZwӸp9v[FܰaL3",KE( N5""c℉eeՒUiIw+*e1{qش/W2[&ѻ EeB:zDQq ק7qivA5u|ESuX(UZn"%E Mq3sSi(Vyt@nAMs:#f5WYjJYVYER^(*??wֱGf Y,(9WΩM2"KJ o˷IENDB`sqlkit-0.9.5/doc/html/_images/table-demo.png0000644000175000017500000013360511530470276020243 0ustar sandrosandroPNG  IHDRxsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|N$л*TD뵀wvXPQ,XPĂ(# @ =23$f@!SNۙə >\2kB!:4s7Mh85f6` d9zB!h榴 2Bl&^ruuY  "B¿B!G1PvLp 2vգf5gAr 6B!jzۍG$+NI9qύz~N1tt]S#B!Do~΢_.-;xO|BcW~m*e. {1\\Ź-ZB!ı@tR;PEMIhՓ"a0t W>tӢ !DX[ q 3 h|7W`Z96UR(K QmFrxLaw !rlI;xA&y-{ЫŷshL{QڐzFSE>gm6cc-Bt]j :ZeV_9smkP֬r6]>CNMPXHk;FejvQ !4S9ג۸5X/Ì){N'v,g cyn^7eR«pAT#9l] ~[Iژ?g9{C$3Eq u;2j٠S+ y7{e=~mh9gVs>?YmV9YUSSeر((*(&ϘH_N\&pxdYLj$t BؽnVlg0<mc]O%pT,daeȪLFݸ'نEP3ꕝ,YMHZ C8N?ֱqX4~eD'\w(0@*3 3t@.־A? j,W0Bqo:wD:9+v2CA/ZOOq:ZA/XE//&dUĥ fSz5nItԝ|6VNEa7qo.>e-]u [ʚؖ6$giA)'R|޹Xu!*7k\?m̹\wЏhkx߬oݦsDq;oeQy3~z1V.DwnՊK+٤괊PqXbH+3*RAD!>ybc*K=Xn/fI nMo[V/ܱ6{_Ƣhg:՜̙dz)#(s:dT\uTw^_Fk̾>69})^p-?Ō'&{D>}8QFBG+؞;-ԣ3: = 1c:;PC6lscbk: nB[ l'fSإ%:=ULj}+\VOh)ry5U]ҧ׵k :Z**^>zMrN}gO7ͪ mߠr47iju@*QCө xZp'K")]|WYSr;>ɤMT4ocuϑ^ś=ǟr~ߩ0{&ylC =G#}_9C)npg{<7fzw &V A_+,wG!{ 'ҹ;3okwѝɃ]n9FtVP^F""v.Zc](/+ŰBfbqtb܍L9/*B a|w^|1Ǵ'gN]oIy=\Ղݬ`BfToTxt4gy.*}6WUVBy}kܕ׫(kR9v'AcEPR~I/gͦ?wΘ2 Y57 o{5ǀϣRu;)1gf rZV1SW^z=loAqT.Sm̄XǜUw>#,\Ngt,R'rj8e_owӶZNMmKu7|=}2}flX[ڃmp`+!U6;K(/YB;NJJn`YWNS5y֝V:up:Gj͑ΚPjz !(B|qyl3)PB y+YYpHjc"_PGQ6DC(X:yG:ӗϥ؇::L(G8T^Z$=tWx[M(+f80/ KAPZ 1AIZ4aXUs494UDiDf)>{^ ]:L+8NԗRΊYߑi1Ԯ`kT!@ex! c9,|a& +&F!OO_ՠ&9?3}/qƉ]iKrŘA!)'5G;}ԙ?O!dFIݹ3tf573[g ԟ.g]J7\Żiuw<諴UJ|ɥ`T}"ʺ=p"6݅d[$LZttDsHX\j\Dwƹ\[{Ҡsn<ލ1`܏QcP{ֵ˷a~Y.Can;p>Lsgc&us‚ J\vTz?dOqt=A."FO 8r?1EbQi :3n+}MEj_jА0{OtA6;=TCr3=ۨDǘ3ɱ[(ހ߭ [ bS_ے< V7Ose76}Wr$浜jjPt[_B$_ s\"m$D[,:ѭCio׈s;N)}L!L'àRNX|r>X,f9Tu R̯ 8fӢeW ʗ»}Qcop^'ynoIo<ÁN0ӻ7V?_q&̊"(()1aVP&n:n5>SmX | XBQ}:WhsZo /㰿C9ImIԡn9Pgk_GߒƇa35AB4&F|3Ӫ2p77+m [)tz14O&-;ְ2¢7A)Ua|g5ξkVf[_('~-߾wT{FM$uu%vDL0h{?kYt2!gօϵ28X+# cp-gc#yrsψS2Z?΍ZGG1<[9h^ Waw[3AvZ|,/ҟ;kwӎcQ^@-Z/Y>N{;w(XXO{t;JQjL+؉wl P\91]NSҽA3#AB!hqeNg^(/诮B!8F>B!8#edVO4zI!Bj~}ڛ}>L^!BKdS!B4 :%B!MlG:%B!-\ !BqLsz_\,Ƅ6?6h!xm?B!G׍&_`4~,jFTm|~q1[7bo &$I$I%|F⟹ 7]ki,d4|B5(Tcslr֙c*U2Zm}Ɏ^u?F΢ButqѦ#[>\sh/lڸ&b3YYr#dR`n6O簹oZOI$I$!?ob ?siXck? )$gHk~, }U2kgcr{7WOвFVlL!߹l-k3#ľ :m U\=$UP .~FM!1e,__Kne~#1ݬ>8 ?M嵧kx3ew=54U3!ل##Og=Ho+⟹pT{ J!WUשO:̅WIBWⲽl{?g@bzORK1 V+wpPg2&70c Ws)/]&O=K$I$I- [Z8y u3]ƦrM'\2 Wo=bNE2 àh,fN 42V=yd_gxE.r%wڧo๜xs 60CR`[i3$t 3Wl`۟ضyVSΆg[חT;([oKg뺵l^&-~!Gkz?ؒ/dzOK.ZtdI87nE6i=6߬ENV{a6'T]yb*~X}n[xcw~Mkݩż>} ,­ʦ?ټߏj6X')<267O{>b7 0k-̅^̰69į- P@q$sɥq8G2Ŵv=ʡ:ůA\t %/u(g k1m>sX?iB3w)#9E$2l@H^Y.ёH9']1[~$`nũ %/Humgλx(Ͻ:WgrE}ПN6*9)􉱠ַ\J/dX (u \u i{hZ׬v6dsځàs.[8R'ywW.;9*N>.m7u[/ZI$I$f:\o@@ffQ)Jnơe=ǓBlA PLx%#^Y_xl=VāHFZ׳u%|=,c͵ZޞZ,qGsyK.Pڬ~rZXs h,'@u4^{s{'M_wok x\:,S8#L>Qg! S++sq57zA`bkob+X*'ٖU՚T.7z1?/ByـӨ\0Lks8['1Lpxk@=Ug+L|`.! FI:hg[qaV\Yi: W AeflPF gK֡rwrË,<+1!VԜys7G7SLfk 7_H߫ mOKPDu]דaGP@kc_0*FwZ+ʦX5;-6X`zޜ~3 Wn[LTOwb%'mY}sX=۾ĝB!fHo9mι\zm򔑝KRVCaUuZ*KD&,eXS3g->:ґ~br+pgG{|8k~pl_fUrMLCg҆y}I!霚>o{&r0Zn3P^~/~e56+o`: P摖QQk:e28f;]KMY>xUַi Yuod:{N;kMt8;᭟jo|3жmO$I$IG&[_mv&ʓH#8O2A-Цfmsc=7mu~[B b-s}ؗ\:j)'$j[Rnz{,厉#=& >nvї[:G0`O>JD.˧}EKϤkԆ_ ϵ'qǹ3gŜ <t6\tF0LNR+*܉ V^qoOLR;`}7Lff%hU[j,c.Go*i`gm_$I$Ij2rPcedVOtV^Z!Blkos#U!BhB!B4׏v-B!1u ;B!DˑN!BqHB!8|w8B!Lj=6g9G2B!S5 :-,B!DSu!Bě IDAT!ZB!∓S!Bqt !B#NN!BqI)B!8Hcs3ӺUdy]f1tNHhJu}R~_AumM*%-^yִGjrz|ڕX.B!84)ܜolf3K哹sbQɜ7%Kyɧ]&-- /`qM*<3z}1sکXv5 [W]NBIKِ^Y׽ç/_*B!ZLNʞ{]ZܹU X,dg#ը a躎1[,x~L<:ߺjz~\+/zڷ>G4"Hڍ=zW䒫o`)9uX'v9*(/+'4-S9ŋ$B!Dhr (*7xnݒضm 0 tUQSjn &@trf=S'OaW_2a)ןE/GaɄRXT ::/[A=9̳9s)))!+++n}A2 r_br*l\8e(f9놏%B!D5+40ЍA7Aulv;Yi<;SQQNxx8L&,V YYY,Z1'hjz'x'mE^~%%ޝβeKXz-ر={0uTF4zEmy-Z/]Yx|p:tJ`h0BB&݀m8릏s%B!D4+0P?JYuz[=FII1lX*uyyNg}Iii}!881'Т)! ; !88kN8~Gvm`˖-~yX,:A9i|r^v61CQ Qt 0ThAyI2z/_XO!B4YNEAQ}{=hoйmwq:كU|M-#'; ())fc:jvGRRDrr2gn<L&vSNQTTD\\6VZ**kѺ62o^zM!22P*(hAfV9Cq]t !ɚtZ,v<ߧ5̓khk͞a؃)Ny:-0 <"]7P+bFCϒ\l`'`Ov`t4[NI ؏ ]PMXhF4RTDNn[RVZJff&ABC[&07 ">v{f(:)a"972TRg+QX,B!7Y/He퓦i=nr5 ۣ͊kEžo9~J߼+/a чM2aYx4(ɡw*NPaQ19OѳP#1(֣.ߣN9x>C~ £b09Uq<4̓夰;_"TT8IWA߄`ܚj}Et.gi݆|ݗB!ZV#GizlJkc!5!k&vG¦Q,I11zGK #{7J('c 8EfX aࠁdf]۶۷RvM٣GIOOg@".r+6<:ٛQȴ3ЦuBtWEhhB!hQGΣ{ÒFn^G#88CHڢ0 a9F0 ԸDL=b9f+kb`2 U+n={ ji(J Pa= ['B7JPP Ŋbf{z&n@xPT l{ 6uB!ĿG9Z>&eѩk:Z=lZ PB!88 E; Ű:p=]Ѷ.BhYeZrW46mڰ}v"#INNfvL&wFCAA7ٌ(>.-X7XBX {[M_Sio0젨x ܖLS!̠Ө~Ք eS9Y\T&$$ۍ(tP1u?UA6Sim 5.c嵦;NV+^9 *I)33:&G*+'(2 T;KjذaQU6hvTK9 <^!Ƕf5w)#a^4]"""jo6X bf'ԃiCYՁ*m=gzd']iSkzUjTjPdv2,,(\L(?b5?וGUU,A&5 kg?xdmG:ڶ!$$͆ɤ6>F4""S]¯۷PN QVTEAQTdΊK$""Yז !ߩAoObhTy3T0pNEEE3 -oy܈~+;Fc0ez F^<a/+v}чMkփV+YO kGry ?0{ع3CMքa7{EnӺukΛ4kYo%(. {K+2(-!,vsqI}hݺ5vE$B&#;3?R9u@5)G.͍ۥa`Уwr30FcF]6 %{0Q_yPXd9 ^ſ|Jrrs[1 2Q7UeP>)z]GzOW_x<**rL&TͪqI彏~R͊lb1袮TTTPPPHvNyyytzA섄ElL v 8BhkonZ) @4%Nn>bb۪Gm唺B!*-O8z} \z`:cs4SE:P괭!Ѽnl+rIy2<|7LBbN-3d$Se&+g]| 3dZ t&}O'eLd_5FR⼾x3AcycyJ_R:#1FV>c %mw}*wOx+&L~)K_!91 9q<^=ջ=%}P]6g؅kzZ4 ~>N!<:R9sٓ) =9k_fh`XJzSꥬzJʘ뿯e/qgZVORV_ygR6/!;]>ql(^7|E7Y&}ɿs}?S^߀ LJU{ hL~ {}4u7z2#h3A4Mˁ'6ob3kDL~GE1YGϡ\$&Fޖ>J-cL |ɴ}F"%Dƚġ}y㑳f(a3SxvnaȌ׸xL/U5=|sl[@ig^'gSg77?vjHg/fPȥO)+R35m-@W&r3Xp5 2v7'vC!n $qmA~1Ƶ=~iĕUkϦAЀv}(Kf]̠8ke._Z>A= 3v^I8Gzo\g,$b1Ph`{<iu:>b1 P~);P{=vy}[.Y0/@eN]; 8{zdzJb&H\³PeNY1%@Hf,2[(VP=kի^uրhHk5E2*-H )<[s=eڇ^im''i;}e秃Pk&FD!gr<(bt졲mq݈)_wh}S bì>SjΪJ6@K}=iA]=4&hOcZŭ_}C1 è]Z7L~D]рj8w&35?'YInrun"2~CNlw qU@vW{gyo"rG}>ҺjJſSbwMwR"2?|E*~dz:Uz= e|Ft#hOZ"-qe]uDСu2ǿ WtR.d{2C`Oq1aS D>(-Y.&F<_:N^P̀ǻ/1ƑǶCe;hMDBT+rL~ӍP+jLB#[ǣ!оހ>8l x|1 P~ōX6`{;vy>ZIk _á ۘnJw3sT4cOT$C𡝲!+'6+wQwYבILb6ٱY~tF)uf? ΙKyoqY;bWqރOs}oBr7/;ei P|rqj:(Qٗ$fo2\[lElxFޛeLXؖQGw*݇cugfo6%&$un=*B XR\@z>:>4Vc l{}4lɥ&]tz?:'Ȍ{| ,[5n"R̭97ny|Lثxzȫ'h?PF.zn&vӝ=R^@;wf}}+zK!-:7N޻W22?cn눁V,߶Dpڳnl1>=s]q߼qrSmN0 s&7ƅ4vu;nUmm #GF^yΦO0ܛH1) }n-c}5 E6-8.Q}Ivsï; %g֩׳C׸5v> Kk@B;_"?w7mB) :F6a{ \C5&jO.oG2rPced6x%{PeuAius#3+FϴEw| m+M;}ڛBO C t}W6QJI)D ׌tV=@ўW~ IDATk[79Nk^4k C׫_{?{Tp[hi@){!["c){O-D" " d) TVetG'mڤ%Rܛ{OޜsMɢI)<7 ГԚ~ڭBUֽHX~!G<.BlSҩ!a01kV`!z f <<=yX'xtѦ( 'KB!^6&?s:7_W'&%EB!D1oyJ:ILzwB!Ϯ<%'bv!B'm^O<%;B!OXlTKn$B!$B!"I)B!$B!"I)B!]K:#w֭="F[Ͳ !xk&AV! %hTPP(M$bl2* (m죋0UhP(R镽'V@Ho] G  {apF8.8mY{vͦO8(U(ec>`An:^sZgԗx%.HБΙ /Ï6gݵ{<dž$YT.ij!It,B|i͚+ݹ.e4JT%kϕ812dt-jo^äMb©F;G~CvS{ͽ\}x2Igm)7c [gi]skυ鿍1M)>Yz*}كWq%t}4G'δx>7tM9Nhv '>}q+k>5h[XQ}~9Ȼ=|(ߨMsWdei7EL gA[YG1٠!YNTs0Qll].o uܪՅj7FfՊto&yI+AdND{ҫ+*}b$ 3;W(lm;rCf:XO wWQ2ʼn=_d$-ը[0z0{6r.6|G!| TV>pijXʶ.hS3%+Q4ؐj&/nHKjs->,Hԩ/QTdX$^_&MM97ҾX%ZLbĒw(okY~[gZ,jy{/w 'tQ'\e pZXPYRcKP]i(]S46 {l Y/xN@\qjJtd\uV)#U+sE'p;<7QDq5=e=-vv<^o" 90vwmͨ> R>v@Bq/{H4[.ve}ЍE#lf0΂8[_%ч8l/0ga.UL9Lcԥ{2͵L}}:Kp(~vT͎ҿRqUf+nPE4:%ДNm5x'[ֳ?y-D?1eʹҟC^Vj7e&8mM]qr[Gv݋I^օ>´Gq KLwKl 1u>瓮4Fs؀Qgj8 6nJe;s837vAکNfڛc}ynx "[ZFB!Ϡhˤ9>:/FElL&~<^2h0W3jl cßB!HΓNoU: "2'B痪/- B!NN!B$B!NN!B$B!Y\+7p)Y0Jxyz=)!./ !BgDcFc/g-B!D1Q0*~0{AiL !B,=.IB!"yz/)L !B,dz]!B;^B!N+<B!xD= *< H^$Ou<'^bVN4+2`Z3x>EݤG3/&&bqE xŋbW.h N~~؁ ZW괞\c m[+2-іf')O??zuL_qE\Q1>(H=Ѫxypv>K=eB)μy7 sNb[yn2L׃uE⻒OndQ9|[ ",^#g,=3yJA#TSYOۜÈJ}dEU}d^eN/eZќiV ƕ{2-z/WsGqD~<|/)g pQ:yBY %JFUeVm[Nu(DU‹'RAL>)i;km˪q,ۚɟ,(7g'kQ| {]ɳے\9) Z-Ljg_xcxwza[>RSac./Gx%vbQ_ro0{:L s5dۄS` ]USHӑ Eԅ5AZ@`AG% [?O7t]n\AwX\۟cnpϒ1.G_cF/4rKB2pUz** -M˪괧U[P& 뮰|@cwWԚr{sܷ<<?UZl,s=\ey! cUβ'tz@7JSԒEux0Zz:PCU&,xr :_?Z0eQJbskxR25t`\#eIkȭhr[ʘWqIK |:L&dfc;ܺ Cld@!,b$%+STd'"g^3  ;Ba7}縑A%y);9WJ5OӑaDEm)h8&!Ҙxŝe02.6{>* Y6$g zu6mSak焻1uT*T5dZ!|QYN uYG[{ʻd;:tzcrl4~-x<{Po&Oч`whm^r,^LBMJ Kw9!FZF4)HFQ_N^T*Y fS8]/SCz Ȳfeq"?PUsZ?D0H]OMr][?8R:RUE1~٘[wQJܚt=}э/űWw"x}is^TK!,sfLlV.ytPZӎBLufvTM3%kxӅ|ӿvRJ>.UJZ ݗ]q(S1?'F"ɍhIV5ϱ>Zt-MI1>+Z$n휿?fkOx7kjx+1!eg2z ސo=F! l\Qg[GH-[HG:B!,ƮӎmY: "_"BQH^sxt !B|'IB!wt !B|'IB!wt !B|픖<B!(2ާ3HB!wt !B|'IB!wt !B|'IB!wtfnY@YSAn[ayne?2?q,lII$Ω xV$IzV!DcŤ@oj]U(jJ}[.cBip d]677m(LLm|F"=WVUC\HW ֪B[!0/"wVB(Fgh-=fQg1ϻJ u9IgؗW_HMx >5ԟ+"xys5MIXחG=\[KcygM,)u}yl1ß~ƝShu'qH•#1t+-s[umpby?} Qip_?XB_j+TqQbk_ z%0smtRBgI J EM>/ J%(OGaEے=9q7Jf1Sn9~ pUPhkeaWsnBa_q>e֞p'vVBYZo.D!u7rm7b7*BJ~>Wmt rm7d܄TѨP(xߚ q-Yښ2oiّMAG;]}+GQ:~xlIEg[읝i53&Y e~["ѶLePC[Gf~EQ(3$&2G6(UԺOMIft.G^N o-$xQǖ1mQPWN:PVo!(ETՋgRF3jO>Ǫw'j:nYz*}كWqEՅ[i6b281b8a>Î[a_^ٯMיЃo'N)1=xk.^F:#϶bPCjzpM6 sMZo8}t涍_+aDNOxÿV_&̭ ZxUҿ%ruRn:oԞ=3ۣG Rڔ>wK:P֞vcQD`πxt\Ořux$1#n kg$1}+WpѭT9F,3ȻK/6se .nXNNʘX{.HmهolJqNW^AMr̵2b"ng@$ :6ޚ:@L-vgVrS9AfaD\67q1(up:'c3u6e>n*>jٙK7:C9r:\MtIa܎-Amf@Q-ebcgl;fTje7jP*)%OcFMćg2k@eL~ٹR^}&; 3r 7[<DpvU[dQ\_ƪƕ/j3#NΟci-ͫ[sI'MǕr@&<嶽9~&ڕBTH;_8\OW+g=3/9E[:Wft)N٣:Wzx"n{`LBT{G uQtGY5v:,=%s! .ǍMN|1a'BtBN|ܒXumŊ+8C|ecVj:\[I,WIH8CQ٬o6됫6F/VNN}&o26{iuvz2,€_k1dZ#ήچĩxkVZo1ćgOӒ:ʉ+T.oͭ[sd4]0̩/4ǒ+E$"{kxOb]L{Q5a.;O/KlNO𴳠ܒW>N>WXz tdgn3q"v_%a1wì\sZ &}î(s$MKiQiJtwL#'E>[Qu)(,,Ȇ>#;+JqcD?1eʹҟC^VmttAك`Ǽh/#/|Z3LmY'TKZ S6kRnۘ N_[m.lp/y,c_Fyu&\Hej7e&8mM]qr[Gv݋I^ĜgY__`A>s$w^M~aQ QMԞZ|=vh1ghmқZ;6jǭ~ҩ[k%%;:=Mk>r z̾i>´Gq K$*U(/]7QW*NJ9l 2o<=n{32qgnTN_Si99xf߱Y٣:j .^gql6ʹ>nx "[ZUE۪+u9K]^fkSE ϋаG[BgqF@4eR]0O5^H{Lx.!7d[`H;yׯfԤx` !UbvSnɌןÄ3(M̹e/z5ńS<tWy҉X'AvBDZ"w֭'/ϓ<&Zj/:[FϊHծS2SB<,|+D$ip nmKM3Mo+Pu!B$B!NׅB!na4F ?`4&mHۈјԇyt !BT`HI* iɥhh0fzސxZgyJ:y !B!s:B!DS!B;I:B!D B!?o%B!i[;Ex=>,z]^]B!x* O)ɥEݤv96B}* czÿU2,-ZU+$ł3Do̯'CZɕBw *&G-KͲ-U۔gt^y;>׍]gZo(YZ'ȐQwת[&(9lER;7v8ږUX5?kFn'e}1/~m{_ -4NLYZ ί͕\ n#=Oڦ0.\e{2|KVsw'Af=ˡn=Ɣ_ucFvP nNe\JNx7> "mv'>_+ŢCO=c3WsM76o ʅtcBAY%8|5cz;%R2\ͤΔ8hܱhOxl=ye`u93gz]gz\7:uYm)n8xѧm4_LOrCr;mbzGr//u[䭸 ,XMZGeyTuf"KG/ap>p8ACku:,̵/(SF-W{=L"9~K^J.Ҝz+xkZXm9/wѲtt-fGcFfO=?p}ǔѥ4bדjoľgSXD>hSFT` 4a- P_G ZGcfDvϦ4LKR}ur ~G7t=;Teyoi[^Ba vT~XӃ{ rΤ|;/OGGЍDߠAӛeϔYh)϶|Ͳ3> kIV<(ݨq6U fMG ~ ) / h;5'ӨeB!(b8< SpdD$_ 4.?3Ͽ>jlCo1x31䓭K 68+'ג B!&5vvmI)x;Ԥ]}(7T&_ӆu0GS*أp*GQQn}hӏF_.l=+1|Eỏ׹g&F猺p y(ԫ:7,$-%"KO-=;)H>w2mjydt}9-*ɶ 1rmKp(a$t0F><@v}y..4j՘?\,ciNsWkRB[.3s?mœ1U'~%~qz=rmK4a̾4(;~n~TԽ ZxU'8a)7_BWO غe\1.#n'tq˰]8i3@͡?F9t"@t\3 9RF_R,]Ƃ3KÉ{qpv #Z~o1Ǚ:|ɽsixy+g2gى>Yz*}كWq%Lf?S̕/gqTnK5SCrtͱZKŭx op6jҾg8!_hХcyJteϹIŔhfޘŞcNX2j-d=㘒r}JĈ%P2!r~ZҕMV9\hcd4&.~)eMiS^ʵ$?{ i*C<ċ赣/{jQZArJM*fF;O6MLH%|Cjߎ9֧Nz9C1?n?q1R:u{̕/a$z f藃d$eD{{"b'Fo~Y^C_8֙%S#o[^>eUנ.Q_g([=Su_fӦ%ʣ8tP[*dڢ{N@\qjJ֕^ amv@Bq/{H6-D>єOq꽭|x0k%= 'K:8n:P=N3߬Sߖlf_o_11`O Yȡړx$RTeG9;s Fu(GN[2l6N{ aʁlƍQ7j2X%g>*xd&<ӵ겭ע5[C"K5+7_Si}'-ZdETh ץ`Ѕb_Ă ,rR#?sϵj\)tBoRh$1epgS~عR.s/FeHFu$}"#؏+Joh6 2}^aR Mww\ۥt܎EcT1n#Oóm5eWk3^+yݻb-uzYwgJvKڢe,?s](78É M Qj?DqZ1l{fzG4խ{9ޓ&nwJNs0nM(ooAym_ɺvP*U(Ήҝp՟ijeK9*!9#rm-:ِIC]{+%j8 I=Ys(*qԵyC+ƮX]1YITwmLB{D30k*fs.dSrov趪 oOl.y%:Fmj'ͫD @U RX6bX9gR1MϪQ"al~?3,_SqV^N( ^qks6: aDN }-LkUp8og϶/3~NNJ B߾%L}d$/ӷS4_LXʼn,$y  ˮ `ksۆ =5F1z.͚0dHΓ˲<xS7<,(OsŅbSP}++nÎrRەGa2%b:sA[?GԀ-CYڵZw_̦c_k^\"MM?Bؽ}kX"yQB|M!: ;*ĊfG_8* h|7bLr+Ho;GXS?sjwdcM3ʚ3jL%^n 5 6͚;A`RkLސJ! ?6vy}Oi_ x¢9Է.hk$֎b"^6&mڴNAn{|v1jlCo1xBBX!k0x]N! Uvt}2I)D !D>E[־ȇ?O.Djg nBDN! !+^B!NN!B$B!NN!Bt!QRB!M&!BdS!B;I:B!DS!B;I:B!DS!B;'Q+>QV,B!Q2)Dau勠B:IgF7 D_ךPFZSz1!'iXm/MC+ۂGݤf9jL {Yc@J uEN뉻HL)Wԏ<-gRљ%x&犺TwqU? e"GjfYZ2gӜ35M3GniU#wme8rd#{ý?@A/׋,99ozd}3ߥ焷7 ޽OQNJgꌭ~cH/5+_Ui榰LrLB`[PMYc%B+~t"3y)6*Tc)If6wBwçDve}eƌב.Nfs #HƄ)N*胯IlysB%"\723o#6J}ps Y;^e |^^(9jZgٯ,'vJleg`u#Wcbu/ŧav>>@#Q}YHF#`B4t+cLp 93oUC3ƞ`Z SssЙt< U^O8Ű0bng~_<2gnBE?I%MJ))Yg洵cŦ7cOv`9ŏW(//gspx+  :3N|׷` {'56i=z>gwoR> !/yhxgij|ώs-:F ۽'_(q\Yw+wHV-ͱH }J̩K61YSI ڒFgBx%#8-tFxNk{mlDOUp:Χ?ǂBEFR{\JcHpnڣ* B!}z:cSчf}-<ln}w qٯMkOJ! Fc^om#NҭjXPB+|>v唨=#QW!(RVTD'fGphhs}*ltxzk5'puh6SYL+l~BvfUUWA2mkqgyA~ADwi1rՖʿ!_y! `〧. !)וXǸpxcL9%`Sȅ2%8p_~'3YT0]jF\,sn.K ^`J|[(GPX02ϙO RhЪBwrUs.lLkJYWA3-?rNg>\qSH[{ tӬ8Zxc>we{5[ɉdBq+Ih.5mH0"3Y'xl(=i׽4'@`" 2k/xvo;sJBZ WGm[;a5xyp\֫z1셧bd ~1?qY.>4-ze0e\N5š н^X4^ZeO1m++ƹR76{|3a_܍&3ӱnyJ A͝gae:VJ;ѐf.צΝ)0kܝwUxWnvn|9%%D—xQcX))k wUO\XùY%]]z5be:~+1;H۹ӎޔO~}m28se\]NPwLW*<]R6jh6Ԕ͞FfͰQ6~ 3)8\n)BgXvyR -SD!٥~<=ESߊ=u-B!DB!(rt !B"'AB!(rt !B"WDJG!I:EMF:B!DS!B9 :B!DS!B9 :B!D|n-c/ig}1YnB{:G:%y2M!xjY&|܂6K=bmZ{\%x젹ZJE{4#03;IEe&(tR:<5ZT/ ⻄(OHB!C$&%>]XƁ(bO[&椓ƵUx%V] tt V_''I1;hO2Z*cگgՇ4v@Rz+ۘ9Jx6Ay:gTw֢Ҷ`G@fv`!q1,+{7CE)Mޫ8o̳,|/4*v;-qТR;S=@| ~_,J*۹Ob)<;ğ+|I{iڮo4)2?%rg6#cfb@\ &:Oݸcętۺ32.7bF7b?laoVN\hw&C7ړƟ737+]e[*G~Ā*l>;f^X} N|@ugF4)Qƒ5=_}FBs'lFzM-+UnBH.bQA]-.BSo=*zcWA8mbdJ8kf(|ąĬ4Lߟƞl=7!G/Diz?KbkXa^-B]™.4sI cO_Ԑf*Ntrf%~A CÓ vnL<=UlW!BդslwE$7gWD43 IĤj(ҖD. 6^>U(5(߻&#Y2x|&;5!# Д(i宾IQDM-U>$-YQہ9S4!60mܨ*j$˪w!Zx `#Zp33z?KZ"WYQ\)ٞDn&cӓH{v׫}Be!~5>BQtX5 HPՈڌt2$ < IQ$ꓧL_zV "19˩oCܗf̹MI~(#8\] 2%×p4$,ؖݨ3߈Q8DRqX=ui[~X0`{.G@m|{$ZmW'X: NMmy|ϳhQӈ8ҡS9}&=JaِOrY6L| 0}j[7u6`q\N&%-ӗغ 9/$Gaҡ~ӹ),~!$hIb[0zZ2si#ɹ3c.p@<]$X}2;} $g}/#n?{[q~H-8>]n2 9YwVxl#^Tz*f5{^⍲8Toߧ:h3y'+}O3IiyD6Eճ0mU{EGʶ+Il";Tj6#N/jGG?1~zUqA_[PM_ZAO-qwxο 숱ά e|ٴVV{4ޑyծӫ|e`}~S;#S<.k.^߳l5;zXXoj͝G̻s<s]-:W9ɰ.ؕHEe'jjKGI_zVpdioGbΧ·QC㡣lj1 > v"敖0|u!B<6b>^4{͚5F<ڹ!c&x?#B!'AB!(r$QxmQB!dS!B9 :B!DS!B9 :B!D+BDKC!B+{-K4'=kxw 烃9)u.lt"9+oRs:N "f<̇rIΧFNe&LUpCw¹F'>N33şƷ:']ĉhCmfFɼQ3WwS8Mϰ,'vJleg;iYf'oѹLH=3ۭE~)>-3po^pLCOslۋKmg5m4)+Q}jSc՘k7eOKپKQyҤo_uɏqR~3g^%*_e^9LTl{7Wq䯙dNİMXMngn ;yFO9 0ixN[rF*7WXyjqz˱'H=Ӛq6[ݘ_ßîߏsD`4b$)@%vRx8ӗv9a2;CR0Gjom1_:u5xx٣Һb~ԱĨ9[6{[ՠ#\5k&,_`ÇO;㡔: +GaFṊkO֫1~ Mub̌jwN_Gdث%p(wz,QBmu:ֺ)9BrY2$8B®kDK:D[ϻl#$gܙu[uZ|΀9]SEtZS!{BFim#df'UGq2(? 'rl8k{SB+Fn @N5hɷC'x5Y|q,(XK'\n_njR8UTp@o Xi(TS;SIƩ&MIS?p.{b# 3r.S_Ht\8BBld_63PZe=l9rq|/KzX Vaט`׽SN24#611I΢ &R3u/==EqmNIBqU$F+h 5ܻx#"wN lxo_*?9ښ¡ w%DBd8ϱr_`$xe#?㌩@1r3ù<AR!Mu){KW"$@|-IDATkt4AΔ\5NKqk;*83>w0 4wϘEcLa(עUc6dѢҔAU7U!@*v盞hhzVW \ҕ=1m#6cR}K9,TexD8NRhy0 !iXm C[ע^֥&]N}\J4M~\;7un43ciԏi{+Lh|+3#PqHw*mp%NP΁s|}5֜"9i/msb[mJ0bk^'qdL D]DIwbo\3MEu{{!KFZSIωv֛~Éײ+Yo݋ox/Q !qOs*vM-+n܄2!'lF:@!:eNۗmHOvRfԩ] ڵjp _8ahyoYt1#*5֔j3./+9NUg>4pdV0я5?hͼ:դ ~mϠiCј ۵?-&CRmA[FLCr̰''k/jH3oZ'WN9`Hxq`$]ugy'if$9tB/PAibC1Jj?%ѤƐl0#ݡ HJN"%!FS׾42qHi4V}˥h4BU Z%Bm[Tj-jw1˖nhH9CiLp(枝kbQE!wg'ef>:%LM{sL`K <~&fݒwoF|"x 52su IQ$X:0m$w,d|4K#֬)əi~19NN"~ﷳ(9T:ᭋlqvxFp>$;-%ᶞ-PQヷ8܍;~[b i[GPJTP2#ӌxbDJ[ukŻm)>M=m? "FB)_q?xs.;hx-~!_:H xkH7AF,^3^)Ơ3 8Zi|T|ep#dzdȶ&=JaِOrY6L8@We% I")(-%e7j芣]B䢰lV~ X6d-3qV\$,Z@p=;㶮|+eCN*ɑalt(E]տWys-YD$!?fXr^QEe7vH̱(۳^J3cgDߙl;BBZ QW2Ox;u仼Xh\}w\p+| GT?Rֈˋa! ļjU}loA5Y[ⱙ^8xu`Sl'DA)py)@Ҕoh- y5: Q91pK=^sp?Ԧs6Q`zGUN*їM+TeVTn X.xbCDeD=w_PSJ%г3%+H%|ۧe7q;]kfnw% C7nݬ=NtN_}B!)!6׮^񣄇Թ5.1|*=8pWs]A!B+S>$oB!ENN!BQ }QȰ`:vVBa~z_aB!к}"FKΘH|wtJAѽۘ_x>B!ƵT|}g|4f͚(0f훑y+jҞ^*a*vE_ҩ _jh4`x )!Bh0(U&?h޵VVx@˶9x`Y ףs(ܭFW6B!O4c1`eǭ]l^ڵj2o/64JϿȵ g ~yM?B<"C-X!,[ތysfV;RQB"BAjQktyczz]BH֭CxC=BK2blDŽja*贲Rdܚ?B!0!N; m~f[C#|S\5VLm|>gtB/ Z6|џaH)Bu+G^ۊ3St,*X 'c.s=U.sИϾU Ⱓ]U ql^5?5%a9r=OxR aHTS1ٟH IJW^XCv M/Lv&d҇[( [U$\15#߬`Di0߮}QY^}?6hk2hBkjiځ]C 9K8uzEOšT$!B<:yzb,X n$uUn?n$Y|ِq˿b/1X00mdzo~>>ò'Zi&oTg5lj:5,&̥omL\KSƯ'd~ ɐEٺy l+?+dEVi||&1In{Ԣ H r5Jk)] ˄B![Sd |Ub+$04)&f7ZRs?1uޮ^iփ_>&ZF6tѢԕp:t)79ֽޠjIUV}Za,zFJ攟zFvvբw;SUkf.Ϝ}^ۘ6V60N=JP١LO$!BTQ$C]`nlH̽/@F<-=Mt,KIEcm])79}vFDR{޺s:B:T#pxllQ>[A!Ϙ舐< t&BCAh7P@P(sDZ:teV#?jg쬁 Ǥ\:/{RVUzDqwinF3pY\zTQ:V +?7M7og?.}YJ˙>W}DJ-BB%HJ[p e!iyvGtD=s+u:UmZETE*y]Ju,܍!^4kvoT*Qٱfu^[1tR4Ztjٵz#WMVl[HKO!_l^sTպnSI̎; N-hYʋf صj;g“IІM\H2oL1uN6.̙STD )~lY*BgV)uLߥ̛i(V$U6uyZe|Zmj1Kf@<:}0sh<9ܕ#ywtizc4M| q _}x#*j4Rh|~C uZ4UwY嫩83w0Mf]7&=d. Fd%lpo>Ġr>jVB!9G54mx$B!yM^ A߁6B!`RB!x5sB!yws&AB!ȗ9SN&B!̙Rc'lhj(B!I9׎=v };=p- y>mX!g'Mߒ+|ZojN9َt襝I˵&]Sg%N6,8;MC_U}oʜn޶u a+i~Nss K*SNMSqWZe9,`UGa4T4 -3[+v�n6ejW !\4Mtbqaq:4 $騮ϗ+S;`J*kl]b#jl@U]˵2Ts4DWYQV"c3R9!Dby*K]޺)Y2c,ڞ'MS]=7v&QQRwirSu;O6T[l;nk!yS_H>&v\JzJj(3z4`@Q  y,4U.u!rT,cQP(<7۾V9=&}"زE^ۗfA1H^.Y;~m-<:lUo2't 8? =="n~Q-pf{2/]MU 1F]cn0 IBҵRt0[5NWUU6dwEOxN*(EQNyCQU'$QBIwc g+hס+p#,ct=&t $l]Γ%}s ̏Umq4;G͆W:cO_@;`xWMef>{j>^#^=? ;چ7 n$2h؍9KDۨ$uKZq&{EfCDVѳwq,FA#= tmߠ:ۜA/O0+h\2Np̛H:@d{uXB'uZʗIY){dr^+?&f9)X4*9FšD{]w/=G/ٿ^1-5߭2<9(W\+( NU'dkt][jqlZ6칩X¢d 2˭}@bNl'DiR\Ra|*>Qu;%>^1Y-W'Tߦk)ײ$5abJIEި*N!wI&5 P|u6>٘Ϭ I5-Gwagbۼ|l%y?~cK+=W`ݬ*˔*4@`B?BFfu@"W&u#ߦWzQQidС3ʌH?xWTUe$DnQa\0lclG~1h٨i&.ՙ^>̌4 6WBZ71k^^<󊳡1td!Mob(뉢X_7Pꯤ|Q!XLFYE*m{~$ 42W[OF~hTu@ۛnnЦro]#IB! m*|+.* ?L$B`,^qB!hPLaj۱+vy9zJ= ܇1|عqSCyJB!ΈZDwVa;ؿ{Gy_jUG6Y[5t]g܅I8xwԩk({m{c~},[T֭+^s%C v+{~KU_u*SϭBN=Qw{Cixe+>l{oI@@obnl2﫦r$ mv'1tp%QeNf5l6(, w|cX},'Zr'MS^ 9^X;V{0=W6dM$Jkު:z=ڵu+aXxvK^USG$33-[n&s5rG /))G'y_z 232s~X7Tz=~{;̸xU\{Uu+V1I5Wu1⎙uZ>CmX;Vc}B\t^~M=&lI^fd:wpgjz ~#i$3~w/۪>Aauλ^xİ%z ddfҢy3͛ᾟAf1ǫoqϿ ǎb aZt}aaddf42ҽ1o׹޵],ۯo6A^b7eoclj+r'o_KMLto~f{mynjmqu?n4y 222x0~s$##yc\S[5rOBBN]Q5էVwG"33y> M6d*bUW=}uµ7DZjo6q;~S0j$ƍgϺV>$F'((i^+5ĶjI}r]1|GidfeL]]|˶øm=ML$U+}82{S.qcFq[| !hpW^Kvz{ pz]L? ƜPuڛ=;70BAkvJaϜ-_@ 7qQƍ%߹HS!y¥S4, ߹GS!?,;oN\l3_^~sm%BqVUOZZ MfS%Bq֨u(4Mv:o4MjqϱAQq }GRi*4|k:>]])th߁ '_c>meS#qɄ[.];s./~㪫$??Tfqz*~P]N~A?/U'1b W_Nzq+O{":idff߮3fX^p;m&di!N::^CJ$4 ՊUO0rC^}Wz#_HK\BO,$1 I&F~fRSSYlaaawFV/<ԳD6iJfV&9rkWyN8ABBGK.cǎ7@UUtBtt <(˗|Ӹ4M咝9BiG6!Y1 h:DD{r/X-ul2p:C@h}>M)H"%IuEuUSQU~MSCu(*j%7/MHH8aέHOӯk&~yOP5ȦMh٪%avmcoۯQPl?|CyqqmHMKjLahт#. ..={߶YٙfZhEXhQUϿ]?%UƌNф7 d6c@ #MhԢ#oX+@( )6~&hߟiNgBYdUCIk?E}>'4]f` SՇjt:U qqh&#G=2c7olO/M@|,V+a0q:4CB@@;v믾` |EEՇGp:DEEa4oێX %22BӦM1(9DfVOc45tэFPt'h$qͼ|o{FNZJ (g0^Y~`턷 kIiL)!ouz58eN4MEUUTi4t]_jt:Ziʝ@9wj}x :}U(.*d6ah$fv9?Xqqf;ԡ]Il6( (ƒ%[Bfà;rdADI>+7|]_U{tQUupql6ƷCjބΞF=gͫ 3#MUphE1`MZG I\*FhzM$dlTNN,a❮_Mwm`A9l~ %BWרjWV+WflizN'_@%Ocs"DOeBC*;m`Nߏ+yP"QAOK@N(Ɛ6A$R.ʓϽȬ__L޽IMMt_P@tt4w&==`Nκ)d0pM3ngD5뀊BZF!wnex{ neN[kZI{$Me+볖'o+ >7Ulf-:`pVzA2gnw(`T 8&T~I59xʅ5 E2$%%ЪUvԣ#c.G.Đ|gdfdj*N AIZV_sT B(I0}5φF^0/}Ƙ 9vTUu\="-ZmO*G\ U`CI(bDǐEyUna9˺ubi몪:Ct/CLjc`g?c2uJ#(F8 >d6D}HNNyf$%%QPPGh٪;d%oߞCѻg=;&,:H<ô)m!$$USykIr4MuSKzJ2 Io( 3ѳ`L&SC~ #a$B;g_UKMO"detxmфiz}<-ZѪU+z]fdf9Up8_VzzN *wci4cЎr/}>gIlh4DӦ:/'QG&i'oz[NCDN9T@HH0 `6a68p(1wm'E(tc,Kc !#=4]Y#i{Iq(P@K܈^,&Pb`c,!]|{#“ϒSݸI_P\WIT&((eڴiNmt֝VZaZ5߯U!`>@%(#R1N nyxe(a- 0C6:ӦJÁ mDǎ9rVѵPi&l2(JP9iF F~Al+ݽOwӬg?š>,qˣI(j%4YhZ_W.:X^Qfr fgga6dtJc]sJ˔i] װd`:yj3N l.w ~3hߖpBzPWֲGmzcR6aJqq1P,V+j# USA07[gH Dd!e?Pb/BwX~ \#ycQUHH_T+X|rQHkr͵AzzFpkΗG{k!a F29I`>Baf[[|fTD*'; E\ɢ}0Uh7a?SKplY. D?l`yW4m62]ֳZkLC`` jw$&'@ppp(ˆAYc Y[&Ld8NnC1w?JP` !"WOjPUD*7;  CHhKIb0`QJ.ԔTN΁Z+ B.u2IPz{r} CA`TO`vN];<@TM- :z08knKhS0q wCݵ-y.cQB)mkNnGu >vUU_]PPވ'Rيq8u~hȾMj~0a011(&фhBWl0N4_Hd(*cy|b!22T s$BӥN6kt JRtvH^rbbY?~{}hv <}c1ԆvdвrP"NAwEb0q+`jIu&#:u|ؑp:DEESl+&''ǣģ 2ҳؗpb j`4mh4aHX{(~{Nŀbpm*>0vHB47*ltw|Hr|M@ !iTW!3=l`,jFkG8{* ]46\(шZt!M|R `a21M>4bsHKO'33l%߭hX $<<&jJ!LJaϜ]$J_uUU]C6q)e6aZܽj5'BԤ4qVPɄd" L#BHCB!jA(!BZ$J!$B!IB!jOصt!BqVj۩G+M:Y!BpB!D-H%BQ D !BԂ|Kp8p:g: !a20ޝ[ϱzd6I~41o{ӡsw6 ~C.E=~"6ooeq:.*> C1n * _W?BdE4j6iMb^ykd/T@=R6;~֓j=wL>d4 ݯ$0rOA!hqIXl߼1GÏ0]7-ְq*ڴn͓(joIr 3Xl~;l`:|{?~ ׯbsu#vGvE͹oO= aݟog;@aR!;@bR*?G7foQK\Bq6R__z-g:".#[7#,,b;+=ACGijw-ҨQxǮ=++[a#wiڵxZz:&NaڕuW{kBT*),ѡݳWoӼ¹MF{$Pm^/ϧmfOHbܔ9Lωӵ!g;̭L9/ jtoٺ߻vSTT(K+[gJJ*#nj|n.~]<@AaqS2ku}iYǵ&5-IB#z,z> LPPtٷNu6iҘo|IӦ>jW$<~!88"ZOvjΡ#'*퉪LdF>L5IJ !䜨l6 ~ IIs>9_*44}<[g=ťS$V!!$g=œs^\qݳuJBQqp*Kof^f-Z1tDV\MZzTU=! ! hIdCFLYV^$3ԭ7>ӡ!Îa\IΜ3Bq0LfέXD=2$95+IOKAӘfg: !мU,i'>_LUUB!|h4bIB!jDw<׬]GN7mզNo(R/ !?*:U_|^xCw!$vyKB!'IT5l6&!B=Zb%O]n 6/Z>*> C1n Ǐ&&rMҹ{o:tεӦ>ߑ>Ѻ]'O{Χ{~Hqڿ۽USeddfro[֬]W7'Nd֬YB뮻ǧNc=F^^+V`:W\ƲeXz5YYYL23fx o+d,^l.R:͛͛ٲe )))s}B!N2kNOZ8CZV(ߑꮯkϾ߽¹Mޯ)4/>Ve:̈<`쇉vSħXwP˖-3g'OyVWOeE!##pw[8bQt"""++[gll, ۻ(%%ݻWHBo=>g&w̙[9)I>svDX&3th~ϓA2rxPY=lʥ_EnHǮ=lN AY jtj ZIRϗ֬Y BQwRRj…,]=zЦM-ZTJ(ǶXwTZ}evӛ^Onc=5}yOgܭW\}-{;oIm7" ILLL\WMu; aጛpS㮻"00 .z袋 =C>}|^TMϠA1b\yL2 !5T6\!wVM,B!8r+ RB!ĹB(IBO2׀F4MD!BHUFϗ$kMcg: !✐t0M$Q !Y"FUkBON>f^9Q X2|-iɨ !h4$2FҬE+IՀ~t֛NzPBsa{u$Q XQaNgΙC!8gL&fws,fd㚕pBQ FƑM;`۶$Q ر#,})F'E+ ?)BMS9x,bz=/Jl?:zwCB!f͚ٔ_-ಫ,qЀ%iL3BqNh*^/IT !>b4Ni~$QB!p&Q7l!Lr֛7o'Ov?hڴ)]tg:ZAA/2w{Ou7_|^eω8t?BQs6ڽg/C >aTP=c?3QQQDEE/x裏ٳ'c7|i۶{ߎ;6l~i2UB֮]; e}?B_:g(;3{wy;ý?119s'TxL-ZĔ)S<a2e~8rzN>BKdU}nCеg_˃?n(1`0Zg_0Q܍'i[GNݘ0i {ss41o{ӡsw6**s]$$$0j(ƌW/ҴiӘ={6]tPfӦM 0}BQ^z-Z`0~},YB.]#66w}Һ( o6ѳgOm>~A&MDpp0VqƑQޢuX,tªUi׮Ν;w˨<@dd$\~yտ6l_5礼y>~>—$tԡݷ2))? /Qn7ܷ˽o՚5|'l߼ɓ.fl >vlcG0]7-ְq*ڴn͓2._?upVZ&99hҡ;][yׯg˖-hԩSycŊ[Һl2V^MVVSLaƌc'Nd֬YB뮻DFFzԱeZ5 ]{V׵g_>tY :MWWW]L ~bڴikM۶m0`k֬a\('|¨Q'77cHIQ7ٲEرcĸlْ9s0yd7ocj( PXXHhhhyC K IDATu~(Bzz:ぁ366_~]ߗBIN>pJaaa\V}='=|T|!DM3;̭L9쉪LFF-[po٢9MJ&KV}N}֭\zUt֋tړ̬,]Gu&L@JJ )))=S#ϬYxyeIJJ:v&P .dҥу6mڰhѢ:]G6@@@fLPPߐ*}s.-_پu;v:<5l\IM<uf!?& $7_YL*֯^}عuc>V[?`PΗKܹ'W67\sZ:wWk.{*w:_VQQVʡC|fy73c p:ܹ/ 0qDꮹ2|Njz B_$Q=&3ۻ(PE b]X$K4A b5{L,%ybIbL4֘GEAil(n sϛ׾8vvgfEC~_1ѓt~Kcђh6| aa!2c}E`Xl6m*]vZxnݿӧO֭[Z322Æ #OoVD[nEtt4ѳgOtܹyΘ1]tADDob 11gΜY9W6QUlAD+;&<\[K.O?deȐ!ر#f̘acM3F{Q͡rQG& D1Dd*DA)F2AD=QdP|c6-l""՘喨VZH.EKQV-ge< +# ""iwDz_p;!b1CDD oܭqNkԤ)ENv&biVZFѨIS;< ʌծcajfYҒb-(3ICnqNDiA2'I(|XoQ=I@H+9{o6""""ҧ"No`;8yZJ$v|,jױK=w<"""g8֡pT@eW\=<:g8DEppF8N1b\E]W/_R>b؎:WLucee TeV.bEDDD/ DŒŸ~sq3" (ĞDE7^£yu-Vg3"dB؟Hڜ+_E^=uY)HuC'e DRb'""]agP% V+^"I$?PMWxLyS{~h0?͛2E-[cgѧ@l/t)Caa!o}D@`u)SoȪ0EŘ:}&ۄ ]2bWF;#u;L4'OK]zD`=Ҵ,Z a:!8غ]ۿ ]{t]M[Dx.li3fXf>Tqrh\ Gp̜=2]#Am1`P\%RVWHEUEmس~=?(+ϒ{ͩj"L6Cz~شKyy $%57*oDty}b,ɹM9YQ~ꔱbkGc2h (--[=ܿ_n<[A]JJ*%nڕKX`<}'Lɧw;b<|n/6}\,[J'O~x̻rL#:fY<gΞI|\vKOKWGLؿ"64mϐx~ 2y_r; ſp>rrsvݧKJW8rZdfecpAddê5d9w~\:~}`jZͣT2\ٱ͞؋rWOx,%Sv֬ãGp>uL~9m9yglѸ| .͛cђej:):'+O2V7zhcLV];uN>V/ i/3 A8^߱W0*7R  H~UOvػg'$%#o8::S?q0x2.Z [_}K-P MܼvU-U앫ڽ')ܦc߯ѬiSuK|K6bpSx̻ۧ7B;ܧYYW~\@~쐴\Ixc8{*ݱ_Y3zc[8 _<WWeW0ۄ#1j#JUѤ]檎᯼awPR+T_.ݱo\&wѳjTտ}SKu41ymyyeTvG=N٧tYi3~a?|.[u7errsv9s23QTT$ݶE _|^<z|Y ̺|ew]+@ u-NnM7\2YY%uVVrXu?BD*UTE)Or5i꟬D"=/(OfPZZv5RG*NV؞:d0o3gLT_s/7WF =w6nHUտ.)X|%]ӧO(mCչM9Y2:WkC[lU\DA 忟=֗^|FAT9QIeOȱ|DP``l˗}7`ݧ2_hۦ4kenVO"yx#5-M@jZLم忈۶? 8qΘ @Jj+QXt+%5 nn}i%GU:o>ż ѧw/Iu#=#C_}pf}4=u &$\^vmήqSչM9Y2Ku{ ŠsRRR@z;AeWU-? p|p\'lU9_ܹ`QTTQ^~9+&FO3<bђ!W$‚EKeGf̚))(--̈́L4E)u6JJJ!$b4m!p23#,\,*ae2x4ٹ.nIR`RD0b I{HeUymO>)ӱnJ|&L^VՇjUm7xKXt9󑗟EK 梨vEjZfΞ'[܆BdeecIQU6edy*A&cС} ń1'BޯO!Zb銕Xj4mE[hP!ax="ziw/+t)#GL$OQG*S֮kseǞ=o>=]CDbμ iTݔN]{_A F:u_Us|b,Zچo!,,D&}8rڄc eQU6edy*& DnV b lGc+뺱 D[Ơ żEDDDL/A٣?#[""""}"_3ڱD >%"""QP3 oq@DDDlHo⳩*).4vtW H ` P" ~d>DTJY[pHJɀWC@j^}QOװV^{'ZFLL|PW3Idj:#GDvvQqN7 G"`-XrlE"""zj ''pqq믿n<~"@~n&39)NKvF 0tpsD]gW iÇ.$ Nh۶m6jwhck-[@ ptrFxqUbck͛e 꺠]HN>AH󌋋#1{4j.nxs([AeݏwSVL秪 )EuTCd9 ^^^Opi"  >@zZ RSNys Oߏ EOɓ8ywdgCdd$ƍS,8QdgkCp!9|9ٙ>|8ƍnl \7.?ԻY+cڼi EE=lڸY&}E~=/G͛HOK28\RSRep.?'*g~ľ#:TVg>?Uul>O)RCd =^x(pUN~nN2Gg*lla=y~- h뇘hDF<6ȼ7zcyxǓV.=eMZϽʺyk_@Vv6ڇ#56$t o%mBf>[󫲿vjڬ9~;v-Z$&&Ot[;hРr('UYu_:6tT֧:,GMcyll, CI_nX^֯XA"C"y%!H$e%zfL6Gcf%cƹu9znk=w护wѣ}x u888T/o]<322кMt(Ifw~-ɅK=٥rrry6Qrhvv>>>U&* *3D?bќ:cCAMTu9?;wPdy&EE!96 7' H۷GFz֮]JroYҡ⢧j OB|HL!no!FR󼼼,;)) Z}Gs>?Uul>>Cd*vڅ7|?:t`rX vvvHNN#GF||'H6440QDDDDp8H #""2c$iyDDDDZp^ڷGDDDDUScO!zTwX[[ڈLAU;dZv12K^!M_9e~{Qry9ik n.z:c3CPv4|6kVl9[@p˲/"?㏋PR*AؾUx9I0s(V}u9l5Rslq$g#ȷ>ZZy n8m?]'#缌֪3Nv%>]%b|W|2JQQaq)&. {\#agS яwH ~J.{3,{t\K&2]q]yU>8mcHVxoȉ1.8>́3OĬEf'/Cx; p+y7AxOP*H>{%?؛(xZ 9{孫gzJ 4evEN~v-~Y,p/|Y24rE~NG;DD$~nqZ֪~\3 Й7/|k3O&hGOmZQx Ucj KH%M֓ONG6b^ToԆg]I|*?W/w'$;NnN*YUG;DD#-UPXRX -@9Ϋȏv!vJJ$Jbik];$$hvOɺԂ]ZHNCԢ*˧jh/PZ*F\b&ha/qj>tZ7F25tEX`#9|I+~Az1(m}Zc=F1Wǫ}(Sc#.\p1j+IDATVk -ZZ?~ @s4o_|Ϋ6{K2رe6GvFQb_\}2gbV{Ы.Gvƞ#(-uO7j<Sw|1!wǼ{-{/c`Ԁ`3AkB0JhL\qCn9w+̏ꥺ+3t?b!""9v(<}s3ucEDDDHAW,k H$(+D 0a1'k_,XO+):?׀%!""B?A?ETt^%8ÖMVuvk׮VpYZjsH;}xVdz<""""C<""""C3},^(및-vh= W\ewf v<""""#k^ Ȭn ]Y^cEDDD ApusBXagEDDD˺V-lލ| c̣bEDDDv: =ʇYrb9YA@"`mmU%M"|9iW"\ܴW@"aEDdw} [ "'JQno^O@krDA Z­düj=&2W$Z a:b#Rf W/Au:am"D$bI+Aɕ("sȸw֬Jb}7mgvA(2kE:yLdA)hNC_l[=J9Q\Pp8,QRJBCCYDy9Q^| H5Q~mp/(N&y V6$ s=&2WD Y3*LKĨe{%rtޱ\'W<2s)t\QEi:m'3= T%jq<0.njw>"$EDdx%SwD"FFZN~} sکpYǧEpU;*{UZI {>-r쬴d?Yݽ!k_z( nDiADiADiA'*^LVY'7ڽ CDDDd.UY'7!s H H H p Hc*L{M""""f*L(""" *q8HM8GDDDCx#"""RB󈈈P4<""""% q8u8GDDDT:Cx* lDDDD&K!QA!%?ͤ]aXV#Xq(pٜ4{]+從79?$ItAҗeJ\FDI#XFKjg;?W`G8/ϱەy8[Bes Ndžt~Ujy%<2I5ߝo&GM=u e\{<-|g5=N~v Q1!Wz~ʳOO.Df|[1u/s: ZL *?'w}Wʠ8v0ˆ:}|$ \5nc៵ynj[d׎-X9WncϗS+_T'm˷ss[ɝ={rʨ$IT^"3W i39OҴ1lP4HÖ 8_:ʼ)ijέuD7[HoiD7ÈO6qF=l +Q6f̓m] +8׆ѩic϶jBΑs_ooLtt'C߰+r-3i?>%8[iS!V`cѲ??wJ'M~?^ =BVnX)lc Ļ<-]1 {> 5En܂iqfΏP'sN}/wfe^i0彌/6_7 u_$I[*I+!]lйuA&mä_7kw7xCGpQ͘r4&c-cy/dx{+Y'˻AJf|dW|Cdؽ>oqr^߆7n`ꏸ9{-r@\˓/"`TVožMy:&L\Ir~ ?xiNc]+tӥ{9bZ5/Y{\h~n>ΉcY~=׭Qgfaזg `̚.K$IRK 2dn6MӠ ^r!os72䅡[~G_ؗ{qy0lx(˧.'əS1b0 tl6}~0-Cx4g=6U^3~0-C,Ca)<鼲.32xp:T͏>2}P@OuA}ˉ˻ ?7-qs :<3qtgBK:ՎXS22|'_-S- }{q_o˼yp_jЮ oLCHF6U-JAee/`Z9:X9Jcӕ'х8Ҝ{b@O؍ȷ|YWI$k0i-|ZhV1F^&dͭ_Z^cw>{dv7#ϭdp*7!ۙCF csE#P;9j*Ə ॷgž8 !-s!>7zi: y_C@CzzP>ߧMbC󿾋G? +_CrɌCoeFŅZ:ޛۛKJeH3}"9[ge dݣI) ByfJeBabS?*ףӴoW ;m;t8Zc- ?"pgr׃?Uli]3,.yzQB)MrnMCCv2U.nV²`V _񛖾/_i:ux:}ƱoP؎}sA!h0X.wq2/Ys8 ;?2-6f]GL#wmёN|3[bSplaߓݩu<ݐ7o}y8Noc_sA ZۻկX*g~1gk9UXlxp$ݤؑ}]RwkX0}=Uڐx)&t.IɗWiMuzATr n-ؖ*>Ȍ[;]$S{9>hć<3;q}+ޗeмhd^q};Шc,H+Ք'HhѴ5NbSԱ]skf aUЫP^5硡HM؁Mg>4ZFZ{ohgUq6d# a:6oMo-Vٮ\˔^j~i7Gi53a?ffQ.o/I$I/4MRjp wHJA2hRA\̈A<2  k1#j PŌHAAʵȌ  Z̜</^A7/ P~L AAzq  "fAAD  AAČ  AAcY4j?ٳwEP9,8^=S+2RθpQ֮[uK]km`koY6٧s'qN)q:] ?~: $.fONlh4b2Ι==%_йKgm~z@θpI1^r8<~[{Jv3`!@~7e3K/7ߗYCUYl?cbEBY3YIx{r "Νu{d"Ş @Hp9Bl]4TUEqq]M&zϯ~ᦾ2CeWfgGTE/:4ߐ)S?tr"|oM^=][X\7 p#_3o"hN^Nz8UUZξ#SUȓ{aNJ(h2j~Wj4 PdխG[/?ӧwOjz&<n_32A)S?aM4lԀ]wTUekv1]TI~XT­5f3ûD\D?H16HOO'44TsSB2r7sseNД4 K޽(ϻ(deejjAxR]AFfxrعq?bKgBr 77# ~fʔ),_<'WDYd{2V___WADD}Zjح+۷:IKd2QZ +E6բԨGnMUJMF4(Mo ,)љ#2C5^qeq͠לT7ԾDhʴ1EUUPEqy)9َiXm^R)]vcZ]Y!r0}Ռ̦}u_AUr0L?fYYn OģEuB *Չhݲ"kp\v+TVEQF7r:Mbb"6/|}+Mie0Ԩ Eӡš$:)zٽv/ "(&Y.tLuG:@S $t:8?蟜u_xjOԤtzot @T!k8" Ac,'`[+NS8DEGaQTƶx;TP1rH=(-f\nUq鱞Hy3vړg3Z2v#jW9TڟߖAm8v(X Nx.'9$''`$)12-\dw h:P42K55,,XILev211 L!!S3{/A `Ph?P^)h47.P:=*业tX^G==!BOy h 83 鲈8Ad^Д 1q#ϑ'BtיL6oބwv LbA&ľ *UQ<]g>mcJCBхFǣ%t{LD9 ];ؓO1hZhArr"nl*W={III[pq{,9YzƎYdݖ#WUʍ mgO #E'd2v\z%M4:TYsh &'h.tȭjYwuIhy=ʏtoN"5= r!fO]vD;q5jDt:IN>v9ګM4$""{QTM#Ub1v+B2C=R5Mne=}?+݉m7LG _!w9/>ǿR0A.'ۍq;jձɨ./A܉\.LF#:BT F6ڕD֞|t6rm^4kM@Uc(\ )LB*X,TLvSJjBџH^IL +EGlT2.f':@I!';onϏj"'+3zY.#38.||q\3@8]хCg[-^GeJtH;QaiYPi6Ϗ@ȍX)e911ࠠ|S1Z@@@oce~ѮE4z~U*[Mt*T6&VύF#*UNt0?|Je ;5Vb&ϩ]Gx|m`62ؘ cԉRJ2•Ff*g =xy[O^^?,V+:MCQTt):(к"0GKOĽIq+ׅ44:g.;eBԔcyoBnn.)S,/7Zol 6΍>GSRNc WŘ+d28*[s GckQ|8({OӽCX*A-33T94es4ѹJF L' x ⑃4nZ,d25oC_3-̆^ˈ)WbJ"3ϵ+pf90o a4 pp:99pk*SXZ^:t:4pǔ~mq9ED=16kˏG}CG@Z1^*ygR=7.j-y0yFF:(aKLLf׷ˆBuYc=gX1veN6gӛ+܊ 󊃜4J]ƍ7j/S<_Ь[s)LRQ^QQD;.NrR2΍l([B̋b"3SG etthq=9.FF1_>(?4EwK<}a`0롯eR}Ff Ѣ*<3NŭׯHԦ8_R I<\nwfFӽUsjߌdeZɄ`Bg4z> F#΀#Sz4Ҩv6%fAR 4,ժ!bj@E̳cdʖ)b H BB@y> "2:ShWmi԰AWy79r6uh(IG͕<\h1=6-M`4`i\Ӿݻv L#";~n {z|||Z, S u>\]Ɍ^C?Cӣ{z"2y>|ohI@gTZA~=T]Hɂڌ*sBƣ 4MKKI< 8 ~6:1Mpf{^ܚx!2(/})\%OK';ͣUhUVp\$6lGM8SdS9,6-ҮuKBCBPsJFF&Gc- շ-JXlFۈi Et_FԨ^??%? IDAT+:nT>NtT{U"99q1ԊYLt.*tnv'Ԟonb0v&<x"2Lf@Hp0F?b$vv6n%=3-ZجVBCCھ-۶ ___|}1+VACDzGXS+8AoJ ȘHpU&+WGL1Ŋ[q-+ߟz FmeNȔ5q_>}MD6|C'Փ}ݾ%6UQ6o4 UUq9Ύ1 9c &=e{<X&L&c8p&//t))#]so*UDHp0XV2BLAi(:sr9=3'djUT' A]Nhh4b!PAAČ  AA3  AA(G4=K  _1UxIA2t3  bFAAČ   3.rv |b28~{ٷ \pQV-6 ^/ n wK& A# G "f(ҵ$ AA1S^k1exc?zE A3¿/6 "f*0(bΤ8 r~Y|EBAČ  "f|7nYa_v.?{<֏1D6W_gsqI t[UOX6f1DFe٩bebD|z0t&R O{1JQv)gX?:5!2}I/+9^OLBn.|1T -tz.?c=#Gi[HOl_bMv[Hh3'/m[K7/v.cm3t-֯5oḙ(foi<=wSnu[ٳ ǁإ]Kq߆WneO-C{9ea枦|qw ,rY L8ysv}Dws/0Ʒ`ڦEdٗ77anRr95Gw5c9x/dXe+y)w.ľi:=YE2'߬8.us`9Xz'q >lاc/?Y+V. t7RNA(#b&w?f؋CiWhK8ٛ g*oL`+֐f }~ւKEF8UEq#igb֟‹a*ԚjZ5o5#& bbxUInސnŶd ;sx]n7!܊wx#~V"y敛hjxˍ竟 ~V_;IĺȹH($?sA ]d MN<_/2&Lottl*:Y{{#ׯ sVMU 1Vwk-{ IСf :@ä+AKΩJt 1]d CpVi sl96Mۗ:ф8fiE6d*OdN CLEl2]sH4A9[r1R?\u1$8{gWX!G;?0Q3pc2?y/:"jBFqkbҢORLeѳP,ᑄ[v^M<\]Jؗaux9ãۮTl>{~ ބp:FɷD eDJIOH>iυݙOJAlXB)caF0]xL@;e%y~b;C]8uMϳ YvaO=;s&whO|fKgO=v$&J`mM~ԬŤ'X?\5TθLb5s룍r{gdݚ= cb ;$ό>i ]// P"^7W]vbƫ3lw2o|+<:g2c,8plI=xJo]L0ՇqrYG^g}8ؔʁ/]VŔkPlfN-))[qҮk?#|^Ȏ%b=&d{:aY=zj1[$Iփ`#ĔB],=ÕGε1v [ma2W::]x|u3c#j .=VoolzrO禳J|' GbZ?)OR=Z7>sܮ):?ɟ5SׄT>%ä;Q~s:=4vwjȰA|ѯ?!Tܲ<oZ>3`H4 wA~ v]3G;7^0Ӟi59}57OP:^Mlg _ya+԰?A _dna`;;`\vw7`/ԗ>\]~+ MVŎc+1 4z+ & x67uhPj{w B[NMӴij )ıC_=˹,_1y~{@xX%ς" oddowܤLmYRZ #;{U!$UA.FbU~3x8:m1㚟&))z"s#&衯i`* Lw3 t3 BF$\`@UUq  bF(Wƞ, "frJndÆ9HFA(ȘX ''(8DA1#&3FY! vK\ OnN6nw8L2q rv˛gEdCORt Chծ#CAkr le:gǡ1#GYR:vKDqU e͊߱XTVC"BIܿLNmڭ<#bF(ukVй4oFqR*a,Y8;PjbMWTx;NqG.@XDq5j \G"1#\H?`G \5햮%3  -L: BE&[ϗg=fQe{9ۉ" _Yc)Kvؙ-&*'o+ Ϋg&c:UǦӡ٨AfF;ʙ>հtr-d@t '} z=}c Ο/]a[Anƒ/ ky!=Nhzǧ̋w^ڳ;'scl;// 7-x!$bF(=Z&!/жrؑ6𬒻-<߱?s(3mbwlG@~N4 Muuj55ZŨ[d@a}}ǥ,/MCVr>A#07~)9$}׻Laoy~~+fr\ιk,hBL_x{iCdt {>̴YEZ\wMeۿ5imDdt >뎳s Иbz>W Γ>iݜnc`KznMo{"zqh^':7nr%na VzbvkmCXхkp4zoEo9ί}yZc/+Ny.EtF3pS4Jȑn)0ّ+3AHRyTqQK83eBW/i5ti֭?#03][hG_{ kO1g\$BsL=j!Xu>i2=W΀wh-n}x {9Zpt&󄧩W'i2t.ϖ=#Gi[,:Q H>Ĵ[{m.ϥҥD[!WqN0=4{/uNVZ^t!=qd&{s6_6jG~!g~G=;:?VG1nzo߸Ytso-̾ع}ϘWד^pr|;g7; }l~gě[.͎c6r+{}m)r'>ʷOLėx^5Vet8q՝49y7>Fc_:/uo~=}Ա1O؜^*nb_q${ɋ¶~]Uh) mi;+Gtls#da|/:G](KӕWZrq^fQI9¢)_9Q^6k[ytW3nXB=W|߂it~O._K=s!Oi9[Rk~͹F9|ٕ=ω?z X\Ō>ONLoPZ}/usQcxaL;W-pm[ɳϼ^dމ#P_7?1ʛ HUffcs*o8V^xdCZ964Zk2jPZZ`Ą!XWʿfcpTRMԑwWU.X?yJmzOP2HHc?k_34t z&mq1ֈSxc*Zуz7דQ܏Ⱦď {rOz]V ZRۤOzx 2h|3"gWI iWՄ˛Q?Ɯ|ĨIin^hBak9&};w.3ӛ9Ee{6|oAj^Xkp;XE+7*|ȉ%VL@p x۩]PJMh>¢wrʙl4e欚]dїc1c!\3Y0.J=%0B-L]fT:ф8邊!KlKYpPc !ΩJ 9ZJzOXi5r7Ds}_.:z0-ݐdH48NS9^7ikp37ώh )X)~FOקxnΏ4˘Apr,ۋd8t2IF n' 9d\ ft#FU劦tq5ӧ,ic;]̗ }Ysw&Lct\{ѸY>Q ŌWgae+ߐVyplw'}˖̜-9]SJP#gtZvpRepeij֎VTo؞G^6~5[M<ǒ'+խśek:G(o  Iq[Yu܅#4^^Tɭ,?r0]ǚ\ &BL=.ݣζ +Y!/9OΡ]Vؼ4δ,gvե],=ÕGε1v JS~[maꄟ٘ 7y7KE@hƮwi{d|AFLapS_Z&/O|ӿAʘ9&^FvAxD_tii)g)]I_]gqt'Λ< T\Y5qȸ AJ͌amxl䶿AqdҼ2Fؽho F FM݃i`w Bi1 n_AAL$2#\`@Uk1 A.l48BČp9W&Iq5#ˠjA.J5cHL#.t3 YBY;Aՠ(n&q Eǰ馸Q^#f r~6oX=9E.`0Nv#9u ; ~["bF( ɌdG]N.8B&b4cդ$'\E zaaB,84lѴ1 NU5E߼KVv媞H=?"kGPʃ N8ƪeӹG"UǠ~PTSǏrowvݗ,_]v N`SVC[kqS媞H=/b!ju BGn՟jkb=NAZts zB,UU0LhUe't:tPU[sJ'",2Zz=reRelU^ aO<%Q(^o@S*hj*ekbkfUu:3mU*TE*sY2e72S!*f6edFF+CrpqmMS\ŰN#>ߥ218¼^Oʍ^ sdzEA20lY4>q̿t2q{,z/μ)d\gv߭Z_(a]A*P+j*OGXPQ #Q'\i'Ð_e[xW_I(JKV  }@)cU%þ.yд w^VujQvGo(y{4Y5[ؓoI١UkԢj<:ci˸(6dl來ZXe,FcYOˁݗ͕E\@cf~~[wiA< 0+ X#ؑs߲-Ý߷c/r{4mݑv3sw&VB+6 IDAT{=í]:Ҵìtst%Diں3=FNfe+Bc2pBj,N}dwmɿ1,;/m[I\=ϥݥ>zwi뎴 n:opO߮4mݑ}Y\7>̭f؛\9 {9eb.@6vo&M8wTTG ˲*Ɗ "{${7vc7k4KL4j[L4{E JG: ,~\`=Gߛr;ǻ-3$\cG(>w{sЗT;]i L}l{\6tVkZψ-r_G:]GOs46lH]Qj2ro6{33ʐ z[uLMÆT]g3S9T}xe&UƚlmͷG4hjRJLfou=jS=/&U&+1}Q; k> g_O3ҟCk1oq[IcyK9z.ɌX < ^tGZ~̠u1Iyy|p/˖>|.څ2%~B9.WrLrNq7IK1nwڄy%||5ͪ)]XIcy\[=u!1\@-[}8vȾ4SzO)q SL*ydj O*20uMf{ b4< y ^0C09uMr[ZnpSsfwTu5eKrxo'^:5|B0ZQcER&e< , ШըuXc.}k}Lhؿ7%| !=i;G2ڜWJn~nƦGtLG*]J0sN+Il63!?/_#+Zi5VVɪS^ @3HҾ$scx2)zJJ>%9ފ+% EK@+7~}a QYFH^Ű~P|tԟ9oW=Ueۖyް>g~u5l!u,gsI \.s͟?RsDeړ$c"Mn&27zJfЬ-Y9ڦ(N'QWQM1o V 2w1wqM7R4; .đN"<Մ2tkGMfrB˚x)yOn+=,;Y'zLTO~&{glM ^ݒkE04MϔM[/T$5_S%1ڴs9Tť5Uj3S0u?$v|U 34LzalWתXvQTr0ekʔ)׏9Wh2rˊS{yR ϫwPH &+(4^!MO&8_VDc֝P_}l%>6|Uc7DY3*Շ@ x?Wf$3R}B.]NH4v^ P"A"6tsX]yATNCஐ IH:യ c&UCh6'JӴc ʙfVyБ? Xnj5%,]{eE-4WM>R5A4Z!PU<\Ak_^mM4%U<& 5~ͨL鼍̩rV,e\$1v}t^rri%<ĀT2}]gF?s{\N&R?~EmJ0we6T坦S7:8>L KAV:ݧN r=P䬓W\! ?F ;Ú 1~ M~>3{H]%.sf 2?̪)~"\>{ |PK^(B'Uf~[} f0Cf$-WsElEaMV@ xhefeBs qV673"(T*RRR)_gJ122N&D0#M 穿/qѤ/斔*낭P@ @𶉎mJuJ|M4 q1QAjdP@ @6pR*XP SSSܣzFB!C)xD0x51B+3T=+!10 5U#ƬD"; @ {>q"VB @ x(o3ŞO] >L*uNzG2-KAcS %dRZ)ħٺ~1>aA,e8}$beF x$?Y Nan& 3% :^nLپ߷FB )ʣؼg[xl\UB@΂M-ǕqvoM׉JJ˞ݨY h?}?> oet“~jsL.'Sk\ad-w]Γ]^bOҧfF[󑬹}ҨήTj5Ѥϲ`OꎳkU]ʉlezB:8j݉#Cd${rG*\3 5?$}jtb4쎳k]ZMޗ]s`Uvٵlztj_{E wq"66 X 7Ma+7Ա2GH ܉0j Nm{9rZп.)E)bpv#L3@%dO= t_%@CX6g3m9|naWPG* _&M<͜skXv$<5}U2>N}(f_ \Êl^|M=_lmuX5-0& $qgHx盿.}e/cM2f3❄xԀo罓fnoB|޽3Ð; RX;5+ʏjɗmcӅYOTfٯISIQqisw8Id^; &=2FQU^OɓiSUz!6C~%]C6 r.cfQ?(KA92, 10F!U fiMjR<*#3 ,6ѵ*qו,_'UiGqA@ x+3FV'4YG(eAq |ò%!w"Yju,y˷qz | R h4R@yeSb<`' QU__OF%jH{@X+192G7ˊ6v@h4QzgXt/C#W)LPb`V 4OAO; E2#@ @ wx}ü{4zfCb uFIh00E@.QXX/q#QXX Eo O0R k"޷pPI(D |33w-+V&|n_%5U451`fnEU "޺Q |TP&EL*Y(B `F_LL̈́B(J7f^Ą8A@ W`@ "@ ܼus+&@ ԋf@ iٚZ3M M^?b.L}mE@ 4" 'S{'b* E0/+F;ή8ފ(gW@x/)(\A!3j<ܗ8֥;ήUwvQ{줥J' *6S\I\آ &w8({ P:ΦE͞z&l,:o~e&&_6 ߬*sumF:Ӷ**7=܊I;OWw]3hXgjUrǹvd\SGҦaM*QZylq>cu5]i3Hq8b"P!mo|xJ5k'{uʛqr&ݓ;Rʟ1h%^$?%iTgZ4ot“~jnw&xۚ'Z8+ނ>щ=Ң;n85ܪiӚj|35+\'A5uShS'VN'mrvI1WY+q+dzsK% Ji+.DHJ+U5:`ȷ̘T-ɱ;^즋)ftͬCܸ}S}kzO05  'e|GC.1_ؤqqwLJ1c MWi2}Y//{q}fPXܽܠ]\u?5 wg4ybϮ}zfm|S 95z޿q?;F2ǻ>u+{k1.͜c37֯S^J%dO= t_eF8ˏ{'-^^1$\s^Lx롛9w6<_9Ixn_mQ,% )悽l@Yk޲,2 ;Hp{J ~=@*Ѻ<)fbIv3JcV'*=+UVrqe4U%,ၝ.:oeVDG}l)"vRPC-LY J?w0 +Yä|o% b"U$%0pT)=.a4,@fJH/[^13S8@M,VtzF9 O/ӿ`ZIPI=uz5*Oh@Pɫw۹%B37a5hXcCfi0Abn (1{83B_c*t F&2+MI$c!kҘƙ ~L h?o5(i"0=SZ<tR!҇  pl;gl gz谧^y_G<7;D}V sp^v5#y=mJ;j*3֎sJ#¢gyexC. ZQ*A鳃c ,u<)1*mhjoM~; 2V ؛fn$CIFOL^}0W9 {9Kvs;$q!.q{̨"!fOs(DSlL 3S,v]UPjj2A+Kē+O(Q r4iTAxSy uP'P@;kIWA+o' \V\mGeZ {({^ZW75ĺz_DN.մjzFV'4֖IjI 9ΗSy.z- aYu$!wJ 7>W̯շF*qOyeSbd IDATi ^I_V{tSУ>6ENrW5g\O");q|&nib뜭&I$ M_̅8 gloIT T*~ $Grp.볁Mc9tY>@]8 wq5,kX#q;RQG=O (;ymCF%fݾX=M )L1tV^cW.6p">7\K:Sr=Z4qm5بG ߽M^?.;KI4;ŷ@ mD0#H#^#@P0oƞ+N0# &&@Y/fj5jVT5 &@R/fQTzJnn<6z)x0R,'JUr2beF AQ f.]hO A f:\hO @ @ `F @3@ D0#@ @7чi JnͭSx#%|DyI"`F ?԰͈q{PB@Z^1ܶ[]V h0g`dTXʖb,[AfZZŨ_q^ܝa*mbhӤg6eK4\Ԓ7-1c:sIO] rbٞ!޲ &zVWY|kLv(͊a-HKg[,9GBzBVc'[P֢Ԯ7x׬292gw<:oM~2+JP|$gxt)L&XQZpT&aun8!_hd7-^6t^a$oO]T3{>u=ת1*INRK,8n˹AUwҳp)>C|?*϶BaHn?^h$/<{LZ\&. \cRxz^M:i/w" je5>@En "&Dyѿ$' K%~uWg+)&VV|2Y?a !޶\XLK P8Ҹ---L΋38ʴaufqgy㒡5̖Sh{˗7a%[̓p`j f/֡p2clu~ǝ\zҟ3hR2ߦcWr12ԡՈ|PfϦ4}iiWIS:PB̑dˉD±2|Ge'dg9Xa"5BaLFZXd ՚b3l_#n4jvôʹc,Ogtm世ZK9vö{'c T󯪻z/oL0ՈJL),1,GS]E ;ilcy)>9,z&G2U5K*~BPɻ fY}ڐ]+S/ -\™;"?Ah' :C+0 6gl ʳb{^Z|!nCEO#!l?pO[9۽"ITE{m~}Nvx.;v(Ow_x%^]w_+鼈`Z58V*'6|ӚݨbԄ@Й Gx^%ŞcȼT=z.8;pz/tuq.DHp/W'BGl `dŨ=Q+2dŶj5.d' .k}K29H(޾ |Ã7 'llaS&J&Dš3Q}eDf[U%165шxP}}o1SC&H9yN~w㿌Q8kv1rb]-˕}Lnႝ!R9~ײm5EÒ:h8<ϥ.63v8:)]#(hwm,eԚ/hVU%z-Ggp;$L= ͜Зsa)TSHLJ׃SƯ "_Y:daUVa8(,(m-;?9M1-V1+&R^`Ʋaq4IX Wl&hP(#}yE?'fԦa3"\Re0|Np>xB{7gf e8$D2dJd 1r3jh[a˻D 8Ւ:*}Ө:wC|Xɜ~巐06n DFmh{$\WG8OlFLg(Bc)TuVG3μ$sIǙZP&?BH%?Vp43ԄYF:e0OJ|`m-p0Բ 4ۥD`{1Y69Եc*E4UYTx1e@y\ "&]GWMG0=iƎ`\'i|cO?[>՘M8cx]e{N$)IOũp(n6TrԒ -AjCYm(vSYWezv^5yQAg2ޡą!%댋 n{N$%)jV 6k>Mɧ]_Y"5ézW~?Û!m3ӱr,?㠬_ nfR0jYC|mEF +7e)Ztn7K~(1sܸ2*,s?A7wd ͋H#RJ)" < "HrGtyǏkaip/ՠ-;$D%7S\OߓZS4ଊfZk]3MʄpVTVkdKْU^֡@%ɉ wmvaюɘŷZ%9$(Ll]h=v,'MR#]?cRu0Ɣ5I[I C &8Ǜɫ#Y6S.Sm򳝑-eUYv_Yw_ .$؃ LJ8*< |W%Hbh\/Pp%y $C$RX6#M0c ׵1^A&)*~vyİGzSCQD? .] r}h+h~}&A_1)M={7bTbkZRDG;b0uJ %!EE\M}퉷sh.-D1L08sIÿ`UID>#xC%%Kyz.rc~#&4ټ$&#h ]t959?ix^pRab |A[v6-_fhU9o9]"R{9 l)ee{n:3Ĉ4UTҬ>Vk|U,={-hӥ \bmS vdzTsn2GQ&">B-`~8? Hq꼞}2i;*À 6XjNX\G33SYw  H[GkU sS Z6?m|)ge\G p/|i%m0zS(A462/)SgJ[Z`S{y}tmDv ,'fEn3 w>y_W]Ao"JLڽq@a_'=X{"tmCdp`6tά ʮ_FQ=ˬgIe#k!`^a^g%ͬp7? h4`3Ya(@Aȭh}u2}.[Y3vug?2Jo>%ݙGnQl:@¬7B{2#:&T`gĞcάH|9NDLni"7ƒ6ֿ벝r*{U}f@P+3@ D0#@ @ @ ()^U@ ?beF f@ D0#@ @ "@ @ (U)ʍHzRc[<㇎.=,8J ?F{Kcs)'"4p*?GX"V}.c?ۉ:R|w#TbԑlJO+;jt<ڿcPjOwXG$*Ѫ݃Aֽr[ar.SpD0P԰h͟hg*Ttl3W=&0EV8iaBgLN/()Vގ%YJQ,;OBzBYLv†B>"3S [r2(;&nXZآqN}pK෩(ki²"ͧ& }]}٩өU;rh"S2+;d=uZ"U4:f N #BweM3ną8=dZr-n&e3CghM.6Y#7Gs`yu "HُJb_Xב-Y1G{{5ɏi:sxLlSg߯ῌq\2a~ĤhL1\P P ϭO2{n< $?,/d;3B}x=%k/NbǨ`&I_soؑFKKjo_Y;1@>LNgR4YmZ\4Do w 9}*輲`]ua|Yb o/GSV>Kn#GY{=9MOϯ|˳7<=KآXI%:V#[H` qK׮FŶPe>=UsV1sv_EaՁ#ϴVb&Y29Ůr>; -5V\V$>4b}Jz3;`¢$忨TPqt\KznEU? MOXcU姌OXF? gb9!CG;z#2g1'6p?v~'4YU$& 6LmϚZMxj2흔swdO>B~q/  iӉǏ~z#(qwv̓%' Vvl@Yx—o aKλ:}&b,)ggpcq&ikOxpz({,]A1$h(zo>{Fl?'t1\NAt}!!̛#6rtS79ҿ &z`dUc"r;윧_]eZ/~̒Q[Wuglp)Ϻril*w[:,_Ы 6T4M 4/"3¥!\iD?g B+)eruRks,M,#z>cVNŌq|XW0DBiw=~ozE' Y?#[:EYofVؚH[nj-y<(Y%0+h8ϳfyק|vw1YylM ;nD9wOzF.e<%⮰n,?7c(8p1ksׯ6JL[<ڎ2݃ sWIis<.LIRY*/13~_||,MK!H(~n?w@bZ;YЙSf!|sUM%"g&.NrL3fpp':\iEa}ZȵC`oN)ki̳ :PiH__Rfo00X^>#BZVaa#F@fm<9o ͦ|@> 9bOnD -|$ў)#RJ24k00$)e!338ʓyrS"c15<c78wo$Qyt)oҵUIx<{ʬ~` IDAT0ߴ%4ۂga$ ϢWp6Y\'"D)F GfgWѯ68Bd2 VJ*3_R R5OS%sf*if"Cs[f9Nٯ}&ﶾ\ e-y7 8ed4AD!-S{I8O#GZm0vĭqa4Mё0"_IFl? 54'dax#, 'ʢ,=/_2WLVr SmI=N:^DOy֕Mz sͅ?qxFT^= '::Pr洕3,Jz&D(D^Hβ16wco9ʪyݝqӵj`}Œ\\/&25̿:Z+GՏ9xo#36p9^H™ fMt8b﷠Y)=E"1N1 :j'2,NI ^ Y@)-J-)^pVJ G96o'ikNLШL(DgMl([ okh >`6+ N lyCڰ!ܬ$V҂H{H5)gF$܍NՂqo?Fau,aeP6=]Ț2f ұzYW>6ͫ,#/-a R4>0C g|}ܡ K&1ol8+m6I[:)({E8*K%C@@FCpPA BK+{t]yHPhQx p}?ќ>w:\>')7@`;Vp1-sZK : (麇6r:!<ңj1 'б<'7wGKYqƱ8$Q5fmTƎHByW~W&f,rpԨЫ0IJz!eޣ|kT^#2X|%SN5: bBY=nF8Q0z3|zNIk3X1n&Y>v {QW}skdaY -zTI,f1{6\ 'C Ȁߘv-VȞ?kߜ/!;neW_Q#3 [IrsȼƖj;; ec +fs'穬x)1;v4Bm/MW%u`]{\z?\EEAU[le/OjxPr^_Iϗ} |s>h'%Yi:mǀfQߏ\Øj^9bM MCNdExzU/*<'7mP1 R !Ŏ(f^5ecUڰl,MMB*}&(۝UN(UN(Ux}^o|ԭ>n*'@{{rJ{],f*5q}+fSSqQ"T 2Y{̋#3u(21:vgցad3pqeixSqe9Gb>@6'm _EI"2 W[v kGЗu CN? cH mGӑȺqpLŖKH2ݢuB 6w,+fqXwGG23=8g˱+N>͚D2~ev5GLN:3Sk?'dR\G7^ ;8!OCvȯ;}V #u'|4̔l}W_ѺCyȷ:DBzIRO!ݟZvv%tLt84l˽-fRxzZ ˜fRZ'ӗ6**NZoB#+5mjEqˌ~wG;t.ty9'iYdȢ&QgfT+, |.@;f4)² 89\4U܎7WlAMK ]avljh1(Zڼݔh.˱<[h(}Ԩ}ZޢC6Z'+?83t0Zhfhvm,/M 7`Cnq`PH8X^3{4+ <]{Q{7gȂl7Wߙonb5pwAHx mEhRC8 rID*mѭۧiѳLd0<ɩ^218},#~!03̘ƒٍuRfԓt{G|v%U(TC^^-$8Lfp.X¡,2\#QYN/zU9G'1E7);h\&yƗ9_P{1/m:7 k$ڠe=tRrmܾsU1r/nwqGkmpD~i./W?bs9hUkߒ׉rW~y»-#wޛn~Nk ߊOQӇuufn[,e& Uc꧍ItˊeڍFc0t3M /Sg'qpLDUO>Ky|T-%;Z4fD BFpzWHRpq-+f*;yI& d"%Ij`ߖ(2 {FMKiuJ(ѯϾu=?&mdty5M-wŭ`VkYQSo>LAHvʟ,~josǎ|ldbΌjmFԉet%cZ:MѬ5lfŘNŘQOWxg>fY8ˬ?K:~Byʀ1v]G у;Y6s1ǰd1;֥&fʈ2gјN0bL81+ܗz@S;l21bɈwswC̩hCqv)szb;-y]X>v5GNJ19˅9pi1Fd)7>HTƼoBɆ_?|%EgmHIf`F '1rG3'F{G*i^Q"7Z_>\T⮝[=CP,V1$_Sh dmov!5{i 8п.xVڛ;L WMO}?:[K2cf?F[}@ֻMaXæmL˕]W&li77qCXrֶ9m}qֺS*=5o|D::ޗ{SSNBۂu"ku5}o;~ֻYcM$jun+f} +w6ݼ^S1Z꼰:0^MM:{E[#8Vw?vŒ_7ϝwpdj,㽳cpi4ʺ?LpV!ăMѤ* S'MX,vn{\!BM]c.T} |3%rPa2B!h23#B)fB!B!bF!}oYB!}23#B)fB!B!bF!R!BH1c#/!2By[/ȶ"b-,(B!b&uK=VP㙟X@W/҃wYoKfò]Jhۑz74<3v'wj0?1}]\unh|202ϧ_Zunh\kЬl.b@˦ad#%5=}oAYR!(It'v_#+ o^^h DDGܦ9c^|A8Cg0r ' @毽BJ)Q?3os9gӟ)AYQOwP'9(37^IDG߯:g--G[{%??_FFX~`f /0>Ε[3dLhYe~Pji;p 5cCdz0Brߊ4:AW%8dΜ}IIώ~fv^{\w) j|6~N>+ۇEu6"I֝ϑ!LOɶ3H<,m#,S`r='5>6T u)?Iz Ϝ֝ܡ;7x3Ąʤ82mYV̬Y7 C%%-bΠ0fcyy_lhڰ|ͫ>^L7I";T.0JWN|M3:70'{Sg&mWOtaLT)$^I%]CAGwsyy\H}Zõ&cmBHrDe:Q۾*7 #c1يus,.<@ìMq)bo-[5M %ֆwɘ+Ƽh_JE- _] (kNmoUgVLq~9Pŵ=L5f>7칡=cqW~"_ѧu-̓:7KcmP7eQ4TbNmp%Ӄ խ-g^i"P|t7bRqP+5=ѭMcnTb^׉ɤg<x*fx=Vk=y?;,MUrP¢63ҤpAE_%k‘~>1aŏo)D=xK8Ʈq8,k$iq}QY1V ]"OO_LdD~E!nRr3SRկOē~0/Q)A_GZ52,r>#>1e;q)1S5C@Bm227{(*fq8[|:{X&J{}eWqˍ'Ļ%qVԈ =3*3Ug'-,L9v8jTvbbY= /D=!81*+B'<{Ι0s@F c=JHc: >N1>cOѾumbY1VƋOXˑƄY5a-Y^PZ V[Eh ca]CF^/{-kDFթg<|1{Υ`12ޙ`%SN5: bBY=nFXևҞ{Pc]@HD2ƼNu%գKtl>B՟}};y!?Ȋ?y*+ ^S' [ ft->XFݬyy -ᇈ$ 9d^ cU՝ NG3֛x 7U!rx-ǫ~~V:E Z|IP}A{vjkϴL|-Z\V'kOX hxI˺}*KV%gwaYX{64gl_mmtˌ֢o{nlpO` 3gTmt{>l-o_ eIK?o:1aIDAT8`VV cҳqu''\{5_fLcZ.!o>ȲnAdE^ V?Pt&>p |g4R<'7mW­jgU/U4_DVn7^z"HHd. r)K5BwaofV|FF7Ӷb+};2*B1] <)m?eX̤F ^뵆97ͨ,ZO/mUT(4߄FW/jBՎї+v8\~sO!d%>DɐEMh@ͨVXHtK7]UEwzhSevpr'5&ˉh0$mowV"P2tqifW[RV^L[ԘJj-mnJP|4X^ŭԁZ4>j>xo`!GĄyhh@3COk/fƻ˹{Վђo$-⒡ il^eֳ'%>~g_Sa1${G|vE)P(9+Iojh7J44-9qp"@c$"(#‰|9l⇩[JvhNřc| KF#c]jlmbz+yY#Ƅ#,}w,CFX2b9]ݫvEe^~ӏ='&|4~ov(f{DOh}pQ5"[v>nM?AEJZדtXN~ O4ړmxm0څykOק3C_X^RiZio6s3'+/@_[6>l-ˌrouW]X6m[ph3w|6reWw:綾˜W!j~βy;.s26R`gX,~C>{⮥}ewV~N!܉@!R!BKZ]؛E !B!B)fB!B!ģoc$(B!B!B)fB!B!3B!R؈KtFH x: y,z1 R )|(f/QMc5u9XtU2(=}VB{+;v &όItX}3aDƲ3?4K!.~6B[&"8<7_&,fJPVI'̍j/kCj^w~I~W-ţ.,B LR4vuZYKtKxn\+)s:}<ѭp$R0:TI$=[.3YY7뗢ѯ;8e XF 9?DuFͳ,9myY1&Y>v~nht%fhQtCK8ZZ< йyq2!q?3?BGfE{? Zg3Dm ኍT3NKcDzUc)/Lf7gfa/Ǖ{Пf1{Ig4ûr:q(i}XT7a#.oL azbMDgiAdf sFwݓp^MȺʔދؤ\=ІgrpkzhNA$+12)L;x38|wIIx=3(X^E1]'v|A}+6\#+r?g`Dx[Vq{.ӽ [8HZaǰ$Bh[CSFk|Z'+b&-~3x‘7j<`QCVVtR4T2f~?W;.&@Wo*h6!0CP0x^Z8eFTs*X1[+ y_JJSi |bS/~bXO}?Ffkh@餡rTi_Pעפétp57o|sh##hcͯWRIoWBB^)x]aA;_De:wT>nx+c &T'V0d?|It>ؔByiu>3s^vZ,Lt73+\[bf1W*/yYѾ([5'|nܼAɿ.a5 K,s!}pD4jȇւ&ߐIF~ᾴjWcU&g;ggμm2pS:{*M*$*F%k% Gy܎ D=Dz\.N#bhsCTk DX54θ8X3[o!egTs;01ŭo(y(S唯 {:&H`*fq8EJd=HgTZS)<ΰZW ޞ?X0QWaeC+QEA٬Ƒ< WY18 ޝ<=s΄9'2bQB",`g]ͷ,v٬wzg57oy. 8jt0ńz >חS}̝3}(fD!~D=cx3m^"GrZj5StO3gk/T_{~]Ei0EVLbҳkqu[IP0=Dc-' ,)nh2.[BssG݅^g^c5BӜ}q,3f6H݀A},iV_#GLaLz:nkO 1oƴy9 ݼj9_ !g5co}BpPdz B/`ÛUZ;bIM/`ye !_'hR!R!BH1#B!ŌB!B!bF!B!B2W=!BdfF!R!BH1#B!ŌB!B!bF!B!B29HBo!5q@x텫#X  ~W( Fظ^bIݾ#sUUܫk+c9;8;u'E d=m"l23#B &9'PsU056r˾ǔm k<}3t5{[.v\ TRW!zO惨|55rwoh*cy64NJwTZ{3B!MáukEjTPDc=?Ă)5FVy6͚H4٩. Gufgg*cy~r+%\ ۉ3B!-0ۍ$P4ocK)ecɿM; WdN~>ZSrUQȿK`ꇽ ݨ"!&/̐U\ }e;'u=f"[cqFq#' +'Z8u Ğ;//'k~65"33B!Ͱw'pi9=7u^Fh< ~|F=2sO`m}6C[̡G1电d[9dG pBffB[N枍D{\4h:F(v`rsBXp9߯vqMH} Uz *<8 B1$k@Ꝗ_\@CeSbF!Y d|2˿P-~oSr?kH?Gڙhk吹SSp<J0Ұd8UY )L1\NC_L }V8qy>`Lpն;bIM/`y;H$B3J[XpQ#iW$#ENj`b|rH{ɈѶO&T;/W2g 1JW%L3B!x B!bF!B!B)fB!ŌB!3B!R!BH1#B)fB!B!i%<IENDB`sqlkit-0.9.5/doc/html/_images/sqledit_setup.png0000644000175000017500000006606711421620210021106 0ustar sandrosandroPNG  IHDRW csBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw\Ue].\9PQq{eڰܫ̝iӕ+mʽgVfifW)"*?ʅ;^xy{< ,:LoiB^ 7S:V?L2^ʡDJ'⹤תIx VoMG0g''hCt%BPx(Qxx#/wo^bæoE`@>6ɱ>#/y*k[ uB,1$~c DNa !xA/-":VT{%f+{\J7U:<(nTwcQ=ԌBd:Jyxag[tj4h5>\;zU|$:&S3Ϸpe~W$kW#:x6MԴS#@S6EÈ?'~suq*^VFzi:SQCq/ANEm2ShcZ7w4O=^ؖ%k㕷 wO4NԆ.^wfq~Gݷҳ,RثGfPxxB+8^♞O6wdq|^j@ @qG6 įVWr,2޿kW/Qdnv{rkmz;qrW%{ݽ 'J!tx=x" V+t(%e$VM1ϹK/#m+Fᶟwj yw0gbW:I?Ru_3kf %yt*NXSWNZ4S++tZ7!U tb4,9طkZC}U;/M/[(WuS!7!c.ujQ-Zps 77nn Ptxto^wF_Us3w\*AB^(awMyl=x3w0׭6`GQ/KrH:PGĨS_Kµc^=RniܛgW^y I'ZNaZ/Wl's{KNh\xS~_%(bBfCӔ͖\kP(܈5֛m1[mK|+`Œx&Z'ۖ}LS/+yOA҅.?QbR=I wO>9oEQ>Л=+_>|UƸ}wƯ+x}faߛgҍ +-cm]Nggqj,( 뭦</kR_.O*M|KyQ( ;Z:osbf?P-b BQ}e=Pz2ˑ(ŻF=px*ť3֮+W@n5C1;_  ڒ7^h/7mM !f7{tiSr>C|6q 5^=zL]Ke@w 6mF RǓ>n!~o]PY-ѡ,M:5%mmV@i 5΅cq>[8jP6r+}OEdlˏ7OxOHC >\.8f>\Ճ,}|O.<򦄟' =~9ܨU<5 V|5Gri<ҵ#XǂY߱x4(Pv$o<27m2nT#Af8c/| r;l /ryʹzYj[7*N]l7yeѤ9KtcU^q=t.Mo&vUSs *1o/;y8T~*|8TeZ":A~06_T.؇jR,ᠬVTP>>ǃI! okjxL TjAC(Nf?)߰-jMxټ`hWk};h˷SX.^niVѐ_@'k|>,_.>~,ըkuiǗ㒓xhaM'EQHn)Sxy$T(Qf9܆q/o r<2J c < OV<7yݨT>hlK$?OO/~5C4ZOX\lW,?N׮E s>?߿I@NtDܕYr䆻 Ojxg;:CW]c{wT:9 }8!k}#/u{Me]O1n=xqj(+Tjq j$RIH02 i['W:٬͒T5 (^vsKM(<fߊ^qgwT3-rcjXjXC͏Esj)=(䀩֖Qݿ2_A:O|\3i\7-T0^w t''VZYCnJsSjM72|ʹ)P=mိSrŁx{ާ\w((Zա~2_A pfn-d~@AH0qLF}1E_q^r#>hk?M:Kޒ5*T>8{$Y^?o ý[C o j-D'Ҳt^FB9@a_%%JNNZwxG[V鮠t }kТޏc81HnK_<~of7^bHRb}`fDoCJڵȫ5`0eF3yQIbO;'~coQzaD*-K:d$o4\VU ҷK+_Hxټ$:#G>GpAj(X()KjU/Km["T{IQ׊$%%C)z$5\jӠ1^9(ܨwڂ>6`y%O_/+tob0Z'yw#X0i:^LRvcv|M4xX|yT@19fz m"(䟛mZp9bh9^A^}wOj4 PpL˥}dZam/ry"gNCaݲ/&?RmJ1͓7+|%6mq]Cq}qkŇH'e%C zEJ`+ۑ:<wOO4qӳN^v/dѹkOcغG-M ?O=Ҷ#miiۇTk֙?nOrlr"ѧ^>nuhr}֟J]hnv/ķV71T/r uWJ'RAP^/} %50)ox454GqOæ3\M&QMA|^"0'5yq㞖MgLJKn;Ut OϙD4˸L}%b^U/K>$4(9)$(MJ+z;+$N"켐`*!Id=(E|JyQ20e;gn~8pQ4y:+1I5zr秭S^=z=KcΌ,U\\ںroBǾK-SxM֩ՄȲ 0p䒡QoJx?|Q%8'Uk֭qqf;[B[/?'d[!?hf|ŋi@gBPңD& !FQBGxW1q7&eByǭB!3!AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HB2B!!.#AH!HʢJqu,N-DS59@ !WY"u֓KM[d)oueܕ g\Z̲C@e|8=^mOByh4j[ÇdB!,ªT&\9~c=!!)ǨџVа|ѸiK.\h_3}p"t>|Wl>lomllϲ},Fʬj̚; a3umb DEdoҝnjoDžYvmBtti^~ЫG7ˡ)U$&|ee N9@x̧mk3%6.]75ݷ?`w{?̜ͩSYW؃Rd󶖆9#푞z:}:̆kٵ}3wbbw?6d'K9~tti~=O DG/ǙaoY[ٺ7I>nlϲ}*E;nL#FVeԪ׈Qxq._ʛowg 4ͨN,[%-KRR5=tO|yMCjpI,ukoe)^M[6[9uH67e*Y;11m2[lhތ@eN]գۙʱm_x{{g>yV~c=ΘƏ0oB1׻tfАtОMj;[ߑai{uM־H٫CF=jnE^ax ) :F-رobcc .Z4?hbcҝ;1-bqcǘ8i*NѣG( et֍h'V̨CjnݦY6fc9HO=Y'eE!&&[ߏ{|rQN&MF6 ooo sujٗX:L?.9z8P!c5־HZꐑ>nlGW2SzdiϟIIIL? k#-{wPݕ{vpiN;^Ow]o^ϔS:W+pU$驧#' ;~J̝G'jxxx2et;fDΜ9 .Zun Go4nNe- &G`7MYӮӳPj۶q%&6/Ǝ'ݓre=w>IIIܺu1~n3N/w䋱㈎;~i^rr2J%^J%##xgOZithߎq&GLl,_huٌ!O.]b67_џߵkh4Ν?#o) {^{l_cqq#;#xj^NMw Ϭ9xo&|Evhǧ_aN-Kثkj grmllϪ},FF?qӖ==qX޴аtz ֩m3!Rd)Z@f*T4o҄0 [ݩV-+Æ7o5 uԮUC>xU'lKڭ'+Uei޼-aykӑ[ZUZ~c߲yS DU8y*_OSxq:lzf6 66vm v IDATmKOPF Ge55[i96Ug>YUz0Aaw@"ڵ}.OE|0ANc_m⩓ $DRl.w|[Z.D $D";S-!.#AH!HvekPv: 0T\Kxs4j# 2jԮU!wu2JB^ B5hS%'3⃏L͜=fv>>>Ӌ._zOs:Ky82ؼӠ1%˔7M[|% 4lJhӞGkoTs[m Te= KSӦÃٽc ֯6}ڃ9̞3!;9Zg1h<ĉXW7޻U˗pA:FG~l߾eуmm֩ed:!D27AÜ@KME\?4k}ּgzE v:/K#س q>|}f@uB1;.|oٹ{m":([n xnczWWJ!p~M#V5-1IoH9u*}dƀc eVNZ828Wdd 62Yf3}N>Y=ԃq ع搕3m[e:E޸asw eiܰs&!!аNkT`/\ \9gϝPiW( fƴ)U׫C\ҝϚի L(a8޶ͬ>'.|7AÜѮ 'N".>xMds7^Qc8~**ǎQyV fj oϳ.5W Ε@L"3d do0.[9cİ!̙ вu{Upfv׿_:ґFJT1ZW߷u_ҙ~P111-8kBỹ R" oފd5 g.B^ Գ{7zvb! '"k $e$ !p BKovE*Gzd -؝_"x!PvRٶ}Z9MH=d dMܷt:V0Kۑ&DVerL稛7Mn߾`o0 f4b`P(xU*TJW:{^y߈pxл -4kE!TV,Ҫe RsnFG۬G[TReR߽kq RR܎؇,/KCGL=not0 v2mGA B)wtK*..ލGU6N˰|o.AA xʅ3\:w*Cek&V5i+csa:|oOܩp_"v6bi~舧vX_";rAhW'>.'MgM_R3ooŴ̀8sCBS^'r|||ez0OnҔiܽ{ϡAR2sҔiܿvE^}.7myp-MK͑ݫ;_ȝ޽O*Sv=lyRt`_w%#Vۥ,&Dv^`,<.wH=CiuZ_H :w5-zL2ǎP(Q#M.4N YEЯool݆ݲ қ%KSN=TF=.[jf,Mđ[x{{h27 ח3͟>m2ou'0g񴶣m4 /V-;FOCG<A{0!MfW|*M[Tl3,]#X& m {9)j5##rx%&›42;Z& m^8Whߎџ|sQ*TPY?|WXei0"{2A(\kݲkB9rB $e$ !p.==uBGsDӼh-%hMaM B=w_sSf؈8p_P\~7M.YiRQ8gm5gdi v(WJ 6B8{rtP2k apNZB7!{6;4(5K a8'@kB˲̠vpNZBs2]&pt9hM!,2#Gք²l`xB!'B!!.#AHdg=ޑ$AcrPBgOcB<{BL B*Q?%4a5x̧T*C`u4dӰtI-;wѢM{ʔDMYj˕(L͵xgΚ'''3⃏ f̜=z3gϥFGINQkׯӧT\r*ӽW_bbc*y…~7 .ҸiK }힬R1ÏM5gպ;R?d~ZISzzXCG`=ߑ/[4`69|ipBcΟwA&_ -m6uiӿ1[ĉXW.?nۼe+ogLYs7!]:3hp:vhO&jrbS}._J~3eoܔERd жܷj{+#?zpLCBLKr ewƚEg&#[e6(h411.T{…m9&嘣ǎTC #.>ޡr[WNm;1eVƐԭSH_Ě'hlW?gu6Z1 QNKg`*#7Lӑr dq=2t{z$(L;q|h+2wzvȏFs(g~~~܈2#oܰNʺE޸ߓ;h(G}H ȝ;7 p̙EYrIF|lڼbȑTg|둑6W?{/# 5WF %OBB&Ծmk7Xbbcbx"ڵq*re=w>IIIܺu1~n6\x Z^Gզ0~$㉋g܄Iv7aqq1vWthoRRH>5a;юOKMˤ;ogOLl,_hsy{2zu> ]F :©[H9d 4r0iܴ%$(0u*̈́ՠko.9lޔQrU&N&# !gΜmЄS5aWJ6o_| :4o҄0 [ݩV-l][5QѮ!kۆX5|# !o+JݺY%˨۰i6NǏ3gSqSJRQ8?̜NiYK([`4y۫7zd$FdyPJ*'~Ѿk]͑G_ҔV:6f~ȶ4G^g4nڒ2+Qa~5^oNeѩSkZ~Ν?Ow.6m`%KE$&&uwKLl,5d >ǏS9qKM/\q"bK{t(\+XfLIIشagNc䈡X/NHߡCGX5/^⣏?Iw_֛k9{M]{0mZdʴ̚=סشe /aT ȗ&driqlzNհ0Ν?Oۈ)[ Drg|̂Kj[GVRؕ g-Y>tёc~k4{|ODUAډSfV$OYq3??j2+pp.t*[B9I:vBeٰ0|aǺ,[ujSJu4j5W-L(mނkׯs&Ә5׮_Q2HʁRnNV݆Do'GYfLiZ^*W9׬춴љ~~ }MD*W3FT:S BЯ/K`ߛIJJw=wđy# ៓2lvPK;#ڸ ??U 9^ơGvTΝ: ҝ^Ѻqqqf;:|S7ʖ)ͪ?dt=2Q?.:*Yc8-#2ZR0(0|[2^r Νp"{AazT(† Yl9s ę(BڵR>,ҳLj2wISkfMh,tgm2AR1yGJݻұ#s/DR1gxi[pLj2qf7~:S&M`媟\&7ߦ^: 4v^|||岔,gwDfCKe4e? !!;wѽWLCd L#8v#G %׻tfҔi=vBAհ0Fiߣh4Z,ZLz .Zw㯍]DE4bi#e>m2ou'0lгh4.^ <^ǯe(y_oN׻rz$~!~˩zk+{ߏ׻vq+gwǠSg866w?ױgݖ-ٽJ7K.Fzxy)V5={fZ"{2&dVZ$'طk;pi?nDEu\rov1> znν{L @M7idv4M<{[lo,fϝO||#^4>{*#i;!qB!/ BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FB $e$ !p BB!\FBIw Pff9Y/z"+oBBt$2J"$>K@!AH!d(};eB($yWS[)$g 5 IDATɝ'j/K^|8YG2^lvN{ ܳ1NA%KtYA˞:GP)̋ g +Tbj^*4L*=YRU/~IM n߁;lZyzS%ܹi_֪MxIA^8vl|m?!RXŋ!iiFGB>Τ܊J߁ð~bzϷo?tϾn$&)R\muKAbi~t׵fM5xMSe 9rFd r*%vl)j?!lY@͔i|5ŝ<>0qV-us}^"=ߵ'uO:hޞdn'sr270PtfZ4נGeǐ Л;rlO[YTų*&apO4! Ah'*U:wҙ%% `h״|8ʃ()xy)THU,]Oep\U3 z5OۖW_љ}'F2hٲj?!lϓs zj8YgʛWυs2jQ'¼ȟ_=6 -?p;zRÑZʔS'U{RH`Z+W3eN-^P>=iVpGҐ/*^kIzwwlO[ڶvO /=5IqΤ5h&ͼ8t? 2FngIj2`dk( aKzgJ?HWz^;4,g"o*?N*,IY;N!HB2B!!.#AH!HB2vVh4:!OO;)Aӓq_V"rh*GhXCWG@>wCiiZ w7'Ȟ:QlM!lUWg7Y-ףpϢy$[vAY*kV?"8n F}g|~GNDՀNGTl¢>ofʻ|Ƅ;wr1HNNf֬Y3gș3g… k#G2g_Ν;ٿ?pkc*ba_{5233رc޽D<СCF.\CHNNˋӧۍvM=,.eo:Ǐő`j{ǘ}fZ]tRŋJq@”sΙϜ9cP.]dvԩS夤$%((ȡ۱cұcGEc7Zj)O;(Jvv`q@zY_Vp\J-luBY?Sy_Ǽ7Q˔})ҹNu%#([+_W[Gپgr~9uxҭsRWBu1;))ɴLݺuMu%99ٴvZ6oLVW7n9VRR}BCC͖/]DƍMjm&GŁ_|٬{ҭ[7|}}QTj5.???fvm`kV[is"3(=!UЪ=ΜCü'ř7;1dK``i}lذ+WtRHqy !!!tFQӡP"do!!!ZO< tF2fr6W36gD a˸VTuW9K>(Qb" e.BSNʕ+\r)S0|pSСCMSRR4iѦaÆq t:=^:O6+::iӦJjj$77cr9z=ǎs8ur;==ӧOөS'ٛO?ĉIHH ##)Srssӓ8gn{s.x"Dy)Š'ќp_:y E 9EP׮]i֬3w\Sۂ  ~ԯ_Pϟoj0`aX/Ү];w8͛7___iڴ)QQQvQ(nx? 0 _(FoGhW,'}@-n~ <7~a)S0k,6m-nٲ^xreP,*6V`4nOк0y^NwMaÆѿ܋_cOeGHJN{GRhLE&M$EPP|mb Qq1hẃ{"ل+#!-a8/0Z!SBh$!2:@ ]"ܨ GfI~]!n7V_x#?:ڸ:EHN.}iХG!n aTzvl(!EN!p)BN]Br]tWo#Igy] EHn`r~ա QSЅ^}*w;!*w;5$gu^bmslz"\KމQٜkWj0/vg^b_x0O.ʠ zj#9V{2{/ ݟ1-sZgּq00|2:=Ίc[su8j7*F>>M15/u'zns1(FFЦMnٜw5fݭrbi=Ϸ}Ƿ/gdkt~w֡t\OB2eomF|:޷M|>6Rc{vf-s,`i5|[YWYz;_>hUH}^6Æ{ElOYdzf+'b-터i%гNk}>9oL$gN:˿NopD ]p[9zO{и쏍".3O̪!ܺ:j>;,V ŧ_s6#9)s,8UCfwtx} v`t>8->'Rۉoy2s,OxtJ<<;FPެ``hr,mBX\.ܭm[?`ðGx >;q RgVN*Di9ePfqa5<} Q{Ȭϔǩ] O;Ms&ߟ S o7Oƶ{]GM?ƤC𩆯: {;OvTwsL0-^֍9&ȷ/w4/qa =}Jо&{yCW∬r ڄ4]㆏cD?nk?t65g9,v87yU'=:UMˡ~emC뎡jO͹S-4N]͹N3ϧIqE #-V{}4_ ';sfVJ˽NO[w5e{y+M O.J~ș%b.~m`kV[is"DY8 m# nr>5uZUHJ3{]^^UO),hjBbV8e}~s9+1%'fcf5y$=+n^UHN7h/oޮښ?{o?av$WOf؝kY(65g͙9{#8 1CQ+癵}OV,=yYekG*x*.]O`4Ȝ_0?R<+ٙd~c^\L)ۆ~[Oƍ,qe13xuKg$7FTgӚl26cy:}=? )4 \-nZ7.g8Y\lˉEr+ZUXx>>=@z5ּsmkv>F6Û=VWsQZ #[6j}7Ȗڢ`wI[߳U<|<ڬNXeI:Z&9Gbt[_.ko}›1sg\~S6Ӻ/fq`HI˛:C:#_.mm T#iw9Ok ͹jK9JٹgIE8o[>d*|w&zC<`q2sCrB<ύ"_#{︞KZ5>}­pNӾ-Wq΢rYvWoS`%ܞߖث˕Ae\e\q(nHpB8!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!HB2RB!!.#EH!hm5C!=UVl![7B!K !p)BB!\FB"$e !p v$ zùhBzw6ϡ^o^>?7iݽhPZCe.I6c?2pPkF+LizΝaUxxzYf[Wc J`4j9XQ$~ٱj5he.ߖ.lڶgD~&r-VKFM=m[&;;fk>Q+T Phj"j]8CRR/wvD.lJ@(VvIj?]^A?4vwOArHII!00j_ٿ)B&ހZEj>p})sf.NpE]C~{H]IKT<!k󞗗kUq}N!t]pNU<deqEEA1hD1p{twz)B>AgfdSEZCgEgGgcudfdtR + cR F0`@1LˊnɿH/ +&{!aGJyϣT *5(ՅmYJ3]Vyw1k)Bݤ ǚPXxT7.¤y貲8?z UK ѼݸNc0`xw we! ҅ C> ')ҪxzK/kgZ TFJ֨\<벮Cpz=^wTnEGj&uNæ; 7eeE彐!aؿ1]4na#9mK{9N497zBs֥"^֋NyTdddZV P^j=u*#GѠqS4nʈ8ytWtӴnׁe~|G 'mzۭA3fx1ڀSaB"x\M#hl9"UN H?{61cNS}?lrz3 (\ |, c}tԑ /Lr𶥽u1"7/}r1b霸_z#;{B^~žj5( Zrlq ${9~zE#+¿8ta#F0k1wdc׭o+Mm@K BR>@~WIl.ٺb2t(w®p?7uoXaۿ"$*[ Ϗ}3gϢ"_ZuW#s7o}'ˋM2YD5 mSQ1rWtu4Gf iӦҌWhܴ_Ʋ]tz=9ZQMeq<wUa"T;ceēחOggywɿJl_wf0S6gE3ŋ.Yͧ@EL k2[zDq$kÃ֭[pQ>G&ͨװ #FJj9Ӣu;kފe}}ġ17IukX#I6}ݷpay73fXE/â`ݦ7?# i]vfu0 ੧a9~3^1fINJf׎m ;y/EdxUGo[ͼ5g`̢V3CeD={4h@ `]6m^?[Ν|o7smzR-Uٴʬ-[ o-~]{KL׭7fM̞aO(u^ H{Szz~~5lmϦ==<>%:u߬ڵ߱+LٳfT4)B®\Ǿɣ(ŋ ̘9Ç_:^fZVSik\Km/Sz5S[Rr2eGRl\K%ULA~^pS֩ŤyehWJ>*#T F FcTt(¿ PT:Qn-_Ht@SR̶ _PNj:e7Baqsx~5j0n$1=;WB7ͩΟo_F9ѽ;Ι 0j4f};EudOyV8yLuׇZp5kr1!:ۜ})-9'$S_ }۶l~5Ͼ /?+z}yKcH[^~>'NbX[&Ohj> ^}m.&78},\q\h>{cWҸƼoX>ys/(Zv1<^) O3fՍ_}\]"ϘljШA)X/L;,W}CVvY9\OJg|Y])<+ʭWBz=yϜI2"m:&L0]۷S kn Utݓ&ekȸv?ńnm|<== R"3g1>AMRRv=o..}YyAK?K>>ײpV:İѬ~4h҂`߭vX߸С][53ezg-z݋S&P&=zG~1m$s;AaTxd_&6GSDfr]BA㉺1E.v[jTT_:uO6:uNnټc֩c1۶e{O;eγ?JALO ?/VLO_sCďg*Dnli*@Z@e:|gܽЪms-Ѿ}L}\0o.y<9r4m۴1>ԫ[GCbj+}Yy7[(e}d&y%5+:{ø',31 }>Ց볤E+ztΜ=>F_8ݿ-"$lC>عm 1 鮢j =M~f:o^.?6EyoѺ{ܩag]>ݿ-"$j>jGPJ/OO|||z 6 Aj tBe.I)N&J/00QQ~NtB;!!.#EH!HB2RB /q!ԻW=گ޾UB!j%&Nήo^seLB!*]~ٲ`˶'SG;Bۮ&_ZU&M}-vMz_xsusq !M6TX[(!!Nk5arIENDB`sqlkit-0.9.5/doc/html/_images/completion_group_by.png0000644000175000017500000005145311421620210022271 0ustar sandrosandroPNG  IHDR9ȔsBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|TU;=@ {T,X;EXւYUQDMKD33dI2!!%s){|{$* = Ac;ʟ{y-ߦࡩX}HzN j8((zZK\/nSߍ4@P$`Așc<埮Zc`LE)&4>iO;nMΎ (@dNPhWQU9?C@Јp_| =;j;pDŽ.MΎX: *};ho>~ Ly^5,ꆪtkۂ#뼚oYQQdIQdt&=צ4U^5,hzO\h";p؊Ivi;PX"x;(ί0krЙ}jd_AQu V}DAPUY{Y̲(%P/(GNdDfy5..qPsA-W;Î$<*d)٧I_tϡwDج=jڨؽ+r(ɻT|*w[=sSJzjggn?9;v}Z;k%5RÕ5U;qc"tt:$IN'9:u$BUQ1>x$6a` ~o6otG;B-$)kޣ(3$*` E.̾dlz~7p䓩!u"s)sV <UQ11#fg9;_W4~w@S5#,mm6'O~vk)$I5>4EF"WF7fnǣϽIe8G>yjl!M,{S D ?W~ _ʛnPr>?5{~:"GOpa Xh+ow^}07nlEpDʁ-+X|^y 7>Cs JZ$z,DiJIrV_YEvJg5]*%US;żk̒wq+X((4W@\ʄ:SEUY\}o`%- ]m5)E.xa.nɼ"n n3a2/;a$./n dX=G\ /DHc)>6.?妜 jcMNQ]씊";o[?N*ayh`Rpf_ўr>\7tgPr"gSTXAgʉTNt⨪BQaQWqp&;.m 6Ytn@F$ l*; ΢S+?$ȰHv%m^mUF!d_;pYv=**' mePp(dۼ&p\d߳ë '2m::fA&bCyR_˚ͳQ]Q̺5U}GvVz..]\`Urh.Jnʇe܏._݃ȷ .gO 32?'dFD|6J~ޏ 3P#:<ʑt36U sPJwI꟪&_ܿN+*gہjrݖ-mT5꒜m0*A/H##ڝa$Ap3U"Dk`ى 2$jlާ빖75; Uմi A$.}/j{JFp^ZiSS~/QQ GqKI 7%Yqjbmn9^4,l6{e 6i* \umnp3:_+˯.tٗ>BAAA6<ዯ3r:wW}n)LD|5>|ؕn1'o = dmnΙG^8l$.ح,e/~ӻ`}+ԩSy}t3=0udff~TW0`cs8'퉝/͈1\+̫Gu2߫{=-8q.`L7O/v~~e,|x'9|X'vn>?7o1; Q=XixEKy%*2Շfϯ桍?<"XzݮfO+ÄǟVUU~U?f?+l2_f~Vq6ml+<449?̲Ϧ͛r׼*,'v{Ȉp||,>f8Xa^=UOՇW_Fq4\8x9S@h=Q9AA|,sdYvgfeۼ=V**'v=(WmVgpO(pxtxK_QiRQU] TiZ$BC| u ?9yVyxX('O%_=EttteNuK]=,}m0)**bp̙j񤌂 zok7iN:,>r?桍د cYKdffsϿĥǹ/t<Ͽ2YYYdff*-WUxIIM%7// +0MMFNbSW~TW6U,o&]6~}3vz^{XRV^whߎEJqqi9'$^RģwL޽zqmwң@f=8ᝲڬ%,4K&N撉g}]3F\2IW^K}*-WU;i+ɘqr-x UzMSѫGwyGueݳ]q #^JpPL{^3]zj/{X3jzu€or3WFV[Fq4ޣh6?4V\XqlK9w{C!+fuj >S,п"D|UU]IR]pxBO'_KLUˈ 2xXa{gÕ_N`?9y;͖[ҵ3\}-]wV@~~>iiiL/?}{6h)9﷦iP4:e_2aX7M#ņ'8x*b3r ^\s,y&tArr:|6% 7xd;D@ufWBeɫdk'0fl6>Gh=sYCP'44~4_rbAQJ՞ӧ3JWQ?5aNȏ+h{3ƫ {7_}H/ 11͛7$''s1N<~^ENa2J6АX,gC-8.A1BcW:NH^pl3~x (tuj4ZXEQUEQPhkZZ:a*q5eBV-]p80~ܓn멉H#;yՇڲ_ק'[#IdY!6PTXN'#3QXP@JJ V_C(*EF 0(UЕHLf231؈>N3+ E0^G4O o _MBS!5#If74t:S طa5s!44?*n'?2 N$@*`GPP\WtЮcЩ6T-[ԏd@UZ1;v6hfI'K^F $ R:Az=)-8il6ڴmw۾73bg{Յ/_~c<ѣGA$d;J^^>Ԕ6mΕ'zEQp2PلCVPIaIB%ZL:`]Y {uuTuϧ( ~ILfa(Frjl'ʒEf_+ѽc4w \ǎZܙ39_;}h}{ NyJ6 '88EUWp#Ы67'`h &&WrrrhfpJEUҜ h sr~H$}L:X2m0_1F?܊,5Kt5ڴ%9 εᒑyWC\}ݛdY&h?@FFȲfgai^EU紕sL ƌ#h8F @ o~ L@:3Ա,tv;Nլ 际߻Q]t 5kN)MϜ!:t~Jvf W&ʪUIVVW}p \: E!&#i2á"/0m6gV.Zbd(JE+t/%4pG;BK))k.(8+h@˖vN#p.G.Đr^|3rE}@`HZÎ"(l91c[5UA42QTل,+AmH>9Bb1t%iHImJT:5]mI*GCBR.C;b011dɦu/o=5RkW_M?…<8&Ee-\qg}Y9e trqHZII4{jq&ΠsB4نN5fd Ja! EUxgpM Hk>p~O[d6kCҼysBZ7fUFAs[Ys 09J$!Vv愳I*]ʋ]?sl5FJG2ҳI/YY0v{)me˖>r ob4Ѷ][ Sb2PUU$5 iZd:RBV_ߑ5rRAQ0~4 ]D[Ƣ<~gxn:,+HH*9E8P4Ҍhh4p4!qvZwa"gFף$$IB4$MCޱv-[2|B@==z4k֬CǎF^~~Gֺ':[6ZISPQȥ"#Q&,%H r? RŬTteפ#) IDAT`c<]j ^uڇRJE.,<\Nʉci߃nݺӲeKZ.®lr(pQر#X,zP2228s&ǙÁ`p~ v**k 3A/hyin7i}'Z ih9vQ ;GO吧 |W*9K׌|GPs\0Gx-E`xfz=z$4$UER$ 0ybbbh<EU^Ml6,&m{س}!\ͩTWCKJ檖]ZK+JN)p bwb*:\8F\J+#=‚b@ll<~@X,^ߤ*QTTvtdj@Pp`IMO&b+RRR -b咚\^n.v??)7TзA`F26 ]ACZ`0 8Uop,l:0xqn2mAg0nW6qҴPPz=8P\:bۑ$YF޻n'88p;^e0 ]6]_C~%%b 4"Y-6 +[ `2l4ڵmCHHȅ3P^jSSqkݎ |C)..lbA4UP"223ܮVRn9Q).NmΩ%uzBCCHQ3ˋ)}K=.=A3 ً"=P3NBsh4ѭM0vN']k%?ɘm1~٣k IGS% \9s:@9z"\CQ2O.8ݎw'Ŧ 0+!gG~Ry(}]Tk ֥UVlG1nqH"W\}5oX@h^C4nSlS\lCT=UUf#;)k&77ՊR&8%%/u9MQ-$$[x'93 )?'U8v|㜎vt#؊Aj=4b13zH{Dv~(>f z |W?tF,oDo2 fʨqX{в\]Zz_5`$]=:w$ J`` 6NOe-t7J B tҙYfK`` ]tMqRӰo)ے۰u]_NjUe0C8SHHkW8 v}ݎ"+;Ml=jAu!!$'`:+EUJ2@+\ڎ+}+utQ5Ԕ軌G DKw#jm6Od2<&1}z>}XBF$Wo@7IzlyUyLF\be 8v=77%t\r ǯvړNl"##/ ݝlmgc6=TkX:lR6 d3ΧHCޔ~k맙͊d;"}11t+>hޠl6ѭS>3Ə쐉VLNN[N2l0|}}HZ4i_ Ŀ_|0+E\jhM;ZN e9HEE{9WK8޹īz8Y{ Etb*tv.II<4Y+>AQ?AAu꫌`Ԩ/vJDNܙCd"/?p EIzf3 Uk*l۴ b~Vמ|Hbg AJY/,VT7Z7cM&$5Obe5tlߞ\T޽iFY$GnNm[DRА|Vtl/ v9lHFвN!-09GyԁS뼆U ܣ~r?~oDJ8 n(' GƶsDGGcX4lZHLDU5^2v0g0ͮdl`4ؿHX?@y qt2; mDϒgo1hu&!5 tz_U{3`2a0پk/w;˥_pF}ߟ oOx*Ck=ulttgЃVI筵s.-[ઉFu8m9zR!]nj:G2G mZ_ײ%0[n./ ѥsJ'Μ&; AyTЙdEϋA`` ;v7`6HC m̙]-[靵za ߏg#Yz,S\lszt:眷҉:sH飡EA$f Rtp%v95GmO!)sLde90j+Yl6[BxXAAX,z$&%%={f]66ǧR]#%I4 I'cډ@m4MCQgF \[sNhb1j-v5dZuT.)3  |}}ڝA @ hM!rI#DN 4i &MG{yM@ 3m:UB,@ \h@ hM!rI#u5Q,7A`0ܶ 2n="h F#ǦdUDbBAI߁Chզ5Q>~aĶD]*>Ⱥ+0-4mQ:!rMV3|dzЮ^YDDDrqGiC%=%Șf @u#=%B(&Ij,DN 4i5m>AcD\= DL h<# Z‘U 9@Ф"WJkim;_(<˯ҧ`:t3tmwv~҄#ܚ۷[7b6yi"C\x?oQQXVyx-Ne)G,j`RS3n۵^ "ݒDFF4+AD4WH@?ǎ':_7\csx$,sa[1 Q#nW\Maaa}gՅ5$>ƛN||sW;.\$#モwvUi}״Jm@ hM!rI#DQUy(2z"D & @uOq|!rMFe;|E .|E|g 6tb IYlK~Gc<ގ5Y FS @uda(&JQan." F8!rMH_>uEr7mGS9-lXԴ%zh I>!r^мDO}p BR_[v--Z{HZ=5\P>?ü[hЄF5;TKKػw/&Lhۂu(=j蓻(..n3M`h59]e}ki>㍷vm;s#$y]Q4zvm;|JtGv*4ʜyhS|[q78}763ѢǴh13ffSl|gǯ-p$^?~ɓ'ba„ ٖ@dF_`pHNN4En,bX֭i۷vd9.]}5G܂b`[\_xi;eٽFNvν)mRfּ|v6S ؾ:zII<UՖ~8M\'MČ3HKK#553k֬z/(}DEEKr-r-R"+ͣ&4z[na>D‚UZ\/>?+~~F?џ,M,xvPcTEMM6mģ>Ν;)((K^VTPmy5mZ3{|<(]Mh"C\Zx2F##}uŕDGVˉ<ڴ(p_7N$S#SՅkW^y@^^:<߯hիl˜y,&=lQvYkI8,?mwr_v<N. 'e8rlW^ފl"="h]嫯,EEEX,, LVA;j@9%(LK28FgߓǕˈiK/-{Dˣ]`XB>ӼyYs=977?%~It5X¢E5kV#G2xpAM_:hnfdSQ 'gƴa3 by,3nd+9O.7s=9;AdddqRNpON \4A,!r^<>"'}B/kžzAo0Ң=}o{ҵ!@˸xNu^wuD4dzFB('vĆvEp ;]N4mZ7X<ڊ!rM]4 Mݻ5 ,VFTk >]hرPd6+Z"D u)˩>Q#AF@ hM!rI#DN 4i &9@Ф"wc2Txdsj M2t+w%Tpǟ];}:~(?]zp]GQĒ2A@\ ~;(k]nbƘqoۑ7p[stΝ\sݍt֋ص'YnGDDԛ6;w0a¥n}n/[NG}>0uW~֭s, AAhт"9RojX_Cƌ+_y;wϻnb"#+{eİQPP@מ}ϗܹ NΝٹ*ڮjΙ GUVtޝ'O{:tP_EӡK 3v$uƐ%0 ^wAtݏ} oۑE#hծZ>бkOyl-JZ>M)v4_.fȈѴԍK'_T۶mc-؊ [Z,INs˯ᅲNt*vÏxKE &F^[YY_}Ǧ[yϏ1+gm|Kvoʸ0{޵C]Jy/5v#!00gyUVy-AӠш %<,清/^?ϛCTT$VG/}3idge q}t)%**y-ﹳ%4$RJ 剹ee?ys~ُnq~ "##aSٷqMեU7oaa2JFFWm.|eddۼEls4ƌj8̲o 8rowg_y""*~6o^/Rff&-bcʕ'((b ˲_  ˚gߗ9p [q!hMF#raaJJr Ɩ k]MÄ#ܚݻuaM<5.y-SՄ$7&4Խ\'O%REBݺuRXXӧ~5J_\\bbp gz u.qhD./EFf&O?[n#IdY?AW}3c82[q3ϒJnnOFf&)t֋ jIDATg>%vwh~^W^r=ptZj̈́I`ب1ȷ>{1~d Mppf vmLJgƈ1zQ<4kF7$y7W>NE{ζ[(,Cy*Ӝ}>y$OQa>ǎf=ũ.{t1đ۸3)Ǿhocg= >U|b6_#<{ #ӹyUIM:g6@ BAF\5\hMU@9@pA(2zBER £<_=#DN \(̱Ⱥ+0-> r_O[E=,Y&~ŇquBF@xgf͉d%\{VZH\6%3AOYl)^V^#[Ƒ:OINylZݵ\[q$'/DN zJg*✽_*w#&_dߘ>DFE^Ն74!C{ Y&V $r“n/kÖ-[߯?$e9 188⢂s6El+ݻ Z=QزŹc [vC Eshg0w!}7{&W4zk5bVgMn@E")rÇ;'#; Ǝ'o~%A-sZ449IiOQмfyQRQKYfxɻ""fٷN `OW(21#֬>{?{?5g> tGqq1 ܽCʑ\>R'`ll̚5}rGqENtOzJXxDʤ}xѩ/ĄT+GqV+)++ctٛ^z3ϯ;u&"rcuȕdX } ./K+[t5O.]вeK,99,%R|gNNN 5O3``,j 4Zu[z-ٷo{˩qT&z쯧-pp`QyV+ ݸ}{3'Q(,^͹s=\+? &zv~n]1Te>n5\P_s1/ T*U{{;O 5?`ooW#pr??'`cmMk|תrѡWϺǥR~#9}P(Xz%kWDPNP;z |V6<3OKgYt9@aaVʭ rulmmr6lj-o`073sп߶>R--<7ӟFrc!s'ڧZ*`{<Б/ ܭNXƳ*$I>37'bffF~~>]Qg6vvJ\Y9^PT*cb hՙ-u%)j+/>nݵwppIuD]zsozW5E!Kɾ}[,\X"/..XH 5-H-[sZyRK̘U!WVߙ\rR%**.c`@#Y~̙ ̌I^, E!K>;;['MrI^== sSS'cfƌ$33er6 ~cSJǎϲrŲA􉁫K/Uܺ}舄sڷbn}9k~4k|McFPE 0eLJFZ ''40uO,P' 2e%iּRvx-f(Q$Z?aiRPuIKM޾^hcB89PV }8}(KJQ,)~=]\iajڀ7ԇD9~8޷/[6cۺ5PlOܺ{6M6n!~n-˾\Nzz:]tfsx8N5 ڮ h99AejGR13s=-d R˗,]ٳz&dLLL;oDb%3#Ç3G-tgCz:m nmCwGfV'&:tPsZPP@'繞 s秃ptt #3ݜIMMV뽙~VmZQW#̙3ÿRڮJ奙b$4SR\T_ɱ e:tpOѯZ`0ʚ[87nRW'ug@fVDo055ETJ.\׏ݻ%NmAB89A000m[#ݻw+i1LJRU !IlooORUs,.*̬,FMo^M ZѮ];\zsww5rblؘ$H{{Ofi\v R U4-df>o_>c'^2zVZN (!K>ΓQXrn'&Mƻ>@T- ž<}|qsG$&%K#kl())aW3ISػ M7Gp1I=u;UhB/lMaa ʚo ~ƥ ²{u̯xMw:bM Y'f](IOtZ{\Ȋeٺy#FFF|̓E֛Cֈ0ڵm˖_K_|'z;'%5_.!l:s%%}fzp ޞ ;vFSPPȦ Xr&aTG8N:͑Ǚ9A]XK6mXǝ=E7Liedo‚{\ri>>rE߸Qow4>ƾF3Rqr󰱱fTo#G4kV0n;qqV-K/bdd)ƼϹs1-bb5kVhaDNU++ =kIkl=yJ]ǢeK<'Tyf$jDov0Unj9'5_ [ĝ;9m#SekYWѪ=NԌn#+ٸ|WBΆ1 gְ!W L/ܽ73ش9>HaQ]z>M M6Mj:8p >YkK_tyIGz-D)\Ǻ`֯w/lg %^lm[3m,JJKqޭQgϞ%X4-j Q뵺*aC0l׍*8^t VWk9$AS.!lZ@(K)\5=g0!Miq߀`bOWW '@ EgCCGb F.]( EvurБi7Q,l͟2B0n~ 7 @ࠚե Z 4:bO= s:9IENDB`sqlkit-0.9.5/doc/html/_images/table-views.png0000644000175000017500000006770711530470276020465 0ustar sandrosandroPNG  IHDR8sBIT|dtEXtCREATORgnome-panel-screenshot7w IDATxw|I#j(!^D"6b]QP`E ҤB=!$%GL"fv{vfvvV 쪹?Ke%7kSx;𵘐t:@ MqW`fk~&l)^nU{qZQ PYb@ .\$ IoFg $31o)uyr5 [|0]Eݗ8 +;h_]UTU*@ '>sïKjT}uwc|ҶdVnݮa@flq:j5;>bټ5E_^z#.ʏ+?cJ|hPG' 1Ȧ\t^6(TÃJ5 ̵&wzsc;5uޞ z<7SJ"iJt@$Kg|JlI4iw~eb%pL݂OB_A X^ŭdh\ZRjO0W',~/[^Qٹ)+l^Eu]?@q񄄄$KJ!u ʠea̓L=ˎ%ɀ0, 7CEWeo#țy.nA DɰChȆ2.[Y}i4Dp'iU1dj+QЉ1wlIfK#xcqr.ÿޤ$aY}~h{<=Ū]9<%Я2MC * ez3YJA k]MNH aZ9yXQ]>\ɇ }H(TrA* thGWFņ{}YO=ygюd>X_.E<8Um)l* Z nI-w2e^8aC1ޫɘLXEt_#A׍VcoG;&>>n|}uMz6eI2-F!bod;Sׂwd.CstK:  G~aLUÁD-y#C$ՖnL=W-}2-[HuJz vM) P ZXP쀱MB+o98g\tclC5ʘ G6 /hɇ._#Ľ s؇d0h З| 9QLVq 8zQ}h1RA;Bu.s:!gϹ^(@֤)Zqyap2Zg?¡>J^˒K%XL@>!qoäٗj' D#{`[R NDԬq1f\'h8ؗz}qSo?4vE0?Ó6N)8QGX݃6bo>XM:V5L;Yv@'0ď2Aztnx7oQ X;<{F0i?NZw<0 ld&@\_қukra󽙳8$wߡw'J '9i%83 ܸ9?ch`!H9b%!S4_sw>{"W{9h~<#@/g+/x J%ιuAOHb=w;ۛ[xq~H_%{ZE}M <^zܾdY}U ~fu$IC+dj Ȩ@*Lf9!.IQS'7 "ʮsduDV2FB %x%,*@*Y_=MjDžIpS*S\J낞|iaol((;vX TL-)0" <@OF,f/N=B Ķ3[ 1~FbE˩ P%-0۹w)mIVl`wuzw\Ux/ >]K{|iE>{ƝڻCߧv{//뿭g2o AY#S~'Y}6ky*U)z<f4|%՝b=%s 罩֣(sΏc~B?X]){l]yٶ/0s5ܻ|=ۖU)|{q q /_ϞT>z;M^]i9Y:{-n>Mꖖl$geWe=c_$frG{Z@݅qSgoo# F(.p~@Ae*,v{8>^{"XM;"`?Wp3f֍۞˪. J 7?=a[inrӞr~*֥z=w;X0)W̘.woNޯCテg >}:ަ0œX^o{E64a܌;b7?~w]ٖ߫-ܕ|Y\_;3cʞ|PRԓ\2]ƣ&[u7ˎQ9xv;% f(g oMH2 T~Ì[˷2LN30@y?*oҰC^o&dfm%{lם-eQ5tA @??}'kգ.A:7vٮ]ُrqd0b_:O8P:5} +YhD֯ԦR~Pj u-Ulg,{epS^//E 4id{~: ʫTvߓIIza.QL/? - Y28b#8,ʠHcLR\z#0)v7Lmmz)O4mM9u!_{a7ch|ĮY5kJmOM  MN+'H%h%Nte'V-+)_BǼʎJW`{uem᳥A<Lli(w$۾f}o2DWҎ}SNc_-A=1guB&vg>)fo!]ءդ9gNjl 1oؚjŖ/_*zPsiVli;9fs/jk[s/Xw:{!~{:4(2&_,ĭ|ڇ%[%_ebR$[Mr]2ħPł>'q/4}1XV~ݦ =̗sbwo^QWyu VP[1+U .[dz*;;+;}$JwO?۟HF1t6yUKGпgH_bLh74@snܗKaCѴme[yh϶ZyC;f#O kۋnc^ccQ cKki]4^oӍɷ̯cԥ7=~Gr/_\ӏv]u[߂M?g@^t;GӾ2{+cZ8=oLòOa + s~/oYٱ/Hu4Aدzm@ڬ_d)|Q ׌M`?Y9Œ?btܑ^ 7e7&rssINNW_goUM+`=z'rIMF#N17&AZAKCC* Z@RUU1(Rp*NN9Z@`` aaat: Fɬ\ e˹d5jCI^x >| Nnnǎ%~ZnFbb"?~1cMpESNDF6g^`՟ k.UUYz?G ZO:UF!8}\NP!ṗT4gEs&)ur͕jEQmv 8E@H?"A<}a I{W=q5p )N'~e +!4Af Yd Gw4%cH#E_Mo0G=HJ:Cƍ9y$yyy=zf͛ұCW-vڑ@$?U#;iV=& 9N$fqխhوUaƳK I5ׄtTEeE0A46: GR\7-A Ng0LնI Ε Z)}hXޣ**_7<H~--/ (W"5d,;~ϊШQ#>LP`9vfεchXdffd8z$Iv 49|"UccӞ3*7 Ž+UiܵAM 8 4$srAK  ZZYɅÁ>;T#еa{$|\&$]g\'lhFAAA|hחRAh%$BCB 6:6O/A:L~fT}Ab7L`A=>fd$]!RznzoV*`UzZU(:;a-ݎT(C ?{>%8g٬8jh4ҤQz1wmK @#Abl@z׻NӣI:l9YI=D@ Vв]CX2AAHqL ?=۪25пo?epmhYI(;CB )@M<(@MM9 =µS4 ^d$C{>kF $""JVVV:OgРAXHIP|7ɄN'Wp]# dԐRֲ 7؈,IHqJ2JaY_F APP` Aurgry ۆBbdMp 6ݎnjT3 ZX.A[h8w.DK?hMbQQF#$W`_d9 Én׎lvY{hd!5=cE(zQH0N$If"##^-`̾z >*]#?-Cl)@P8w~~%*NB\xNڀvŁîѡst5@O#1@JF(tQqТGڃFNGXh(z 7ٺs'[n'+'Ruf3>f3a\ԯbO?z}{7,Gf3@LڴRf 1; '%BeiՂHg Am G[v=I^WZ6lDjZZB޽ xW&k{]HA|q.v T=hq?g77`c̾|w25dɡ {a*]LdDZcO0aA :++c89ۘ6y@liڛZT>~V䔩SWXY/rÈ@Ɠ:7^s3|{ %S@d㬇5`.esp(_9v=5γ;>!s?+r$W #b/g#G|mu#5?ύ_˽m$ ޿Z@l\r4V$;\6][rUgE;w2hwי4ߚяN"b/אwho=o煉TE)T?؁' +~|OkZ:y?mC~eÊ̸2/6 ذb>JaI(X^i1k}R?iSTC]y?Xc&ᱷ6,_p_ŹKؼ]7 ŊKX$:YUE.;=GÙb$F|~a[SK X7~ɓ7H5eW˻KVuݻ xіs[ *AKs?l#%hڲ5M[Eq&lŵ.&_-d6zʦ( P>.W]C/(,GCQ)SUfcЄe܌y-SqH z :r1Yā<% еt34咻n$|W%OE@MQPTIvqNZ16)wkRʵ٣/` 26]GSca[-xף1: ̑ FZE\_Uږs[ *1KJR 53s~bb )>џ{㵧j^^9C.Jh`vL֣S8^U:Eyh>>ie\\ jFdT28|7dfI[.8 +C;&t#>=F_<^Nyoz=ĂKԜ^e^®\_*i-Qeʞkd_B Oe=>|ā\c|Gs# 'Kk.[bôynM1g%f: '꭬-U[ *#HЇ3 ~Q-bTvX0[~ s'zZRyf\ >/(.vmv/>VX]O l箛EBˡw$D7ya4;l SdWF1R\m-o=v81EtecOT_ʖ{+7zVc}G#.]>`oy.~^ j|-2rfJ]xw[I[8w2h׮'35J2Oւhw37]mJio;_׶)rQP-K<5W\-j{ @ J]9٧\՚O^^v'{Z IC+a:UF3Fj.~~[FqmӾ?͖ fYL"ڷHTA\'N)3i|&zHT\N;B{W-8eT.U5`2h9%;Myz},?U/ Ԕ Jt\\7+S}^f-6?ᶧ>?Fet4[O'~xg7ٳUzny~<+.UV..KzOGڍ)j:Kz\HoUJO,svJ\lבvCΫѡ#QW&j^Ws |P9ڱOqPBF2T/#i?ID[|”YqhaÉ {(O(3 ?.~Iv7nNvjׅ>`^Lrn6Wߺa:eَ[׹#Qq'(QFrlwnc?sקY/pD1ʇ5$X))AO͠H|"0i'^_ ~6dofbD+Cb3sG`ٖʲgo`wGK\=9.$O /JN1. -ynQz싗%H v,xm(}V"ı&-ymh}nO|ǦYh[HVqq4_7ф/`g>਺-;}T@ٖ\̞I k\8N">1>(gtph"-kyi0a&kv_y9z-<Ԣ d;%|{"/Ûnbڏ ;ȏMعW$qS}e[mgZbGj}ΎD"2e'^_ |Cd施9XW?6_YGg^/''N,u+O z4'_d>}GK\D4ʧ(Q#aHN'ŮѨ0>fNF<68~c<@yK3q2}O}i,tXLH 5/gS IDATkI3[˷~tԜ;8}ΫP&~[_/i,z>Y8\2N>8pB,mXbC w?,sgw2hUs>x|u-Y秊sW ]1nzZlQ9# PY?wQTkͦ@ B -jl *UHA^EEXQDki ("]HM6G )wC{'Ofwv93;ߞ3g|n&,l/mwwc#{'͔ #P+.wrO\VNYKb}/Dz(|h%_ͅĜ iIF0Փg})6lOZHϚ\U ٽ H={DIJ'/CS kY:!\U % ث̚Q5~uD:=_>dLICfS~+F33-^ը[Mm0j8O 5n[QGHH.bĴCxg%# Ҳ  ixͿ&sY>Lz o4U㰨@GD0 Fc/Y09r4B#S1v1 4dVrϳb(~}fcײΝ]W+o /k LF' :as8v&r9s9Np͍ ')Ĉv(Y:qvf.i/iy'!( 'r{tOΜ:__x6%nDF]uN%8жHzx#ܵt;(3eDZӬ s5 u6̿(`,^kr4:)_aʛ#hי_X-څd?ϵ*xoH J䖅_ xqO|&Ń1[{|^.kuΆuGN4 :'OjҙKˍd@MZ mγ@ˬWlFG̬`@l_~C^Bro/w[bs$=C:Gof̺k_\pPv/X8-ڸ=` o9s58Ӓe嬕캐JNk'wyCo̟?:,xQDzrfm>F+U87*Xuǘ? _ݚۓK,} [SqWD s]>͸slבS{GjxQF:7_ʉ` @8Kȗ z}W[5KyBxw0V`ʞZ<6NhR6l^3-GGL~'g!*/E]'%1/ #]+P#GNSL~^ԲpTqd\i3UKũ̦"ώP5$;{F`Đh̟6NNBhШ Oo(6h0Mo8|.B^ԴMLq SAKףPXe01q AqZ Z:[iU˖$SL SsA+7zZB"nru:tRBq8UBfzrt*h=\!n&3-妧)gB ZB!܆-!nCB!AK!ې%RлV_V|>NAj>}ْZuEdHBR<αvD6{ԥ^Vtf݃-!܁k\Y`?vK OT0x-l⒠Ïb c ^f^ 8~Qz//KnwؕGkQ~r.{iF !bO6imRFYv^gCƌcх05J:tcƩ0,Z̿ K3atUT9EҊt7wa&lÌ̻̖\`Wh9ڲI3|xUj-7w#VcT,|-moztbADQmX r:hɉir؏ U'r hcH~:m>7ʵ}xjG̭ϖ43a^ƿI{=cf2`>M\2mȿ&G,>@F6ʼgqZg%=Ǔ^Ĝy@^{H>Gpta.O-2kfwD2Zb\R$*+x~珪Z+y:>#:Px#y.&diЋ1߉43Y?/3a4ayhO4KykMkk{?J0zKdk]CvT?\am1?%6hLڭ,zaO2l&i GҹOۑ2rsRr! M\ f onRQ7+WISc&[BSNS#%BD-UX$5bI3yKykM ,F/ռ+Mo|^FK. M1ENLf0 S l^ yٷL#5-*l.Ve(;m2-/{{AS'fĦ&qN 3%M&r".譜ǹNu ,Zn#+>~O>BCF z\ɫEiͰyeעϻo#k2^WrRrr0u'젉ⅧX1UԴ=0WBh1|FJ`qՠ |̭ϖ4+^ #?ufyAwLR|{Y˻}?1O 7{hSO&Oyi~|*?E]'%1/  +eRлRƝ)'.P|% 2z ?شP4u.gĘZ/Ne6yvW״ dJRO助+UO״JQS|!Z\{M+u;O=\jY6c-$śTZ枏 G{BJĵ5z&ӌOoK49 'MRn{:U{깶k!C:7U^'3P\r !Z\[O@WLVk=욖l,{s !Z\[2/'=,0&JVԚ{h{^D%b mǀʾTDݵqmMv^_t'Ƶ%/NP"Ԛ{h{sqНcԍ4<`8V%OJZ̞AK~ugSqrV`?v+xQ|:"~Nz2w}_@W\MQ/j}wyfWx%L'bP=(.c+1EӎB;s"Ճ; A5kDͺ ^-!.'̓B!܆4 !p<(mTsOBq+A!nCBJ<(B"̓B!܆4 !pTfTg8Tmq Ǔ\w[ē|k 91Ų×k/ENB&f=|u2 Gǜ(ޗ0|q*S<:7>r5y{%O8Jo ?G`7_N"3\se_=%}ʐqyO?-X9Nv2rB^ȩRϢ?1v6˓{lYc_#n۸RjqYuPemw[F{x/pG<޳c:YX/>G05 4Vߘ~~y]4mHC爌Džl쿞=-H bʼt?('}b:Մ`O49Ǟzk'Csٹb",3[)iK޷ػ-Olg}_D'b(h?vp2!?~ߋ_s|wc @ĹssK,ޝTN^ 1 U kEr_Tb3C*1fo$Ohg|RAC)7K52=߫: V=0lN 4stc/#+Wq">ACJme4 XgsS,Ӗ2-oMw-Olg}_DҎ]P۾o2쾡<~hA4ۆyGLK񗺹bzUU^ը_q7~W DNɹzkTwৌBFKMli9f?P$ 3GFD^!55Xb}%Ǟ1WCWK=L6/ew 6s4˴ľ5îox.4\{Ыѳf& Qn:{0r/g"V{][o$O`ٴFO]yON)/.m~ޛChkgZ~T&q2V5X>q ?f}gs 'scL}n~,z-O־/pqw%" !7?N4?]^̸)AD>;:Sf}Bu|[=I2ϘU+5CH'6MooRx~K/\A_:m 6Ci8t浅}896BC%v7~kdǞ<gya{g_9+ܦXZyZ:9}_Deѵq$ټs:{!ԝV˹,Wc1 bzCt}c&j[8uٴ/_1b!CB!AK!ې%mHB6$h !pB =譲 B!i,RB6$h !pB ZB!܆-!nVz˖ Z^ι{-V.\\)~A@[R4ח-6 Xꉸ@F6ʼgq@2?c =I$ZIk8\}L%o%>-kdVtDǜ=z'+%3tE'TR<[ΥfuOm?FBǤAiݬ>Lv7_cw2:Ԙ/0d߉$130mfsyGݜgxK3{>;_`{#X%RN݅ Tqdzu3.Z m4 &q7y] &oϓGlabS8~['bFEKTj4 oi7 [3Zꅯ+>l-ɿKn|Pؑ_`h9fUpDNݘs8|é^,FHυ_6E\߁Xܿ1~5h;<ߟG7ΘԴ=0WBh1|־[~81-!~w/MYcj7gZ @%B@_ZD`3 cWw4{'yj@=Ch}3>b=v1޻8Z`Sg/˩1$B̴% 2z ?شP4u.gĘZ/Ne6yvX!%mHB6$h !pB ZB!܆-!nCnB/h4Ee4O Pg; ZB!\iCAP2'р`, pYx <(mHB6$h !pB !i[Gmk׮]%it;lyP!ې%mHB6$h !pC!˝?y؋e o@Vm^ԴB\mY;{uЩ{4+AK!) jKפC׻Ցz%h !(4oיu0b$##Źu(oB!D^޴h HX%f40xx`0lq!5-!N&#]d^'++ۮe$h !pZ;p$'&`0Xn4$'&pt]HBVqHbϝTC57oM``233mNǩ\/!UGp0kk<(mHB6pvVP(lGD;B'nM5 htzG7TJMNtq7A!&盲wf^~cQϞ:AFMɒB.^8Ge߶q# FҮ'r#xy+ lZTкp$M#p FCʌFޙ, !0h4Bl%BT0 ZB!܆-!n5#Gov>BڵmS=AG;=3B!%)Ie3L}P!hrMK!ۨZA+3,_=BQJ ZIBT8A+3 eЬgb'qGN-Hvdڙt茔+i3pP2 7p0QBTkZPx}ǒ!&dHTlڑ@Xf㙆'xw."=}obP=})db8>hk,~XOw_$?¸wQp2Fbê.:<޲/ύAeλOf 媨Q= Lb̎awY2ZEZ^Y9{oumQ0dbڙtVZNߨʞOVS$zl,nB(̦Bm5c_i]ZKw{N b#a5-Y]~͹;܏&i>덅'L-}O0mZX>nh-'+OrݛQ8Djl8~>/FlX:|~`cd܄lN&{y'b6ngv ?_iø%jL:ZK?gE+'+1VlzN.~zrlB _}KPl숡nyw8ֹ/\?<BU]\1o;ۆ큧:Ώ AbJ/ν?mjԥmz9a>s?+ aV,U8nUfZK݋G]M5J||06Mlɿ- c>hflgzRD%V!˨QγGKǟ+lF5r2WLUFs Ŕh >XvLxKlGY? Z?QoQmG-bgwkI}]`KٔN܍^طKYt;4 !n8:)|0x|9ʮL(`,^CPCNLr]{=4U~29gݝx ©G/L}W[OhHϚC6l5|҉MCA0K%َ'ʬO:y^6esAm ꚴ/N|m[FEJb\OK٠/}U\".j=ӈIQT}߭_:tɧزz;Y{@efc<J5])}gMf=ؼ[In^dҏ8f:wEeG9Cr-krUgo ٘>te۪o9z-vZ[oiiOfŤ瑝|=_nrh;jF!UI5̓E;+m[DՈR|(Y竦հ)< 441W@mv<9"VN񷌠C>ݩD42^aDY3ޏ Lid|.> 9^5hNꝋlJ_E'1r,dBXb٘d}sO?X`o$::)}-,!D9+^<ѵq?$B&AS̓/ '!\Bk'5-!7Mm.MBƖ&A̓(u!U-Mw@P>9B!c'M}E̓y2B%7#<v[Gp*mfzdL!(.)Rxqv[Pb`w4̟=/o%*GG!n줩E JB=vq#B!~wL?J2IENDB`sqlkit-0.9.5/doc/html/_images/gtk_debug.png0000644000175000017500000070244511421620210020151 0ustar sandrosandroPNG  IHDR^H&bKGD X pHYsHHFk> vpAg^\IDATxuG_u9.2;w@I@$$HK!Hp . c0zlݽ73f]UgSBJ)Q-o,3"ƇL}MC- ra0 `H$A l1 cEwŒ N +(Ѓr`|@ 98=tسvӮZ;Nk}~cĊ#ãi"Np{=I o+o8/B^zȕYQ0 w )LGH'o,?b>;EMbNyO{#Hz/=\$)5,2H 6( ^']@b #Pi1c;5U0Q(µw~rW:TPHBWGU/4(pxWAY%>!D? HyQp]O 3. @\5WC= _1/"u%UʵZqڻ@:&Vq K'`\"$63D@ÅB ëLjH; Y%ZBĝ_[އpX&{1DHHinpc^/QOp3? #2RNvi{D"/|9:3vm;I]V o| @4̔۹2/,sH:x=?7D~;e&8ld[ 9_LNl 3(M{L@:dd !HX&!SmV4,WU?P.R <_)E^"S|Lub~^;~U~T´ 8)DirMVDJ?¢lt#9S'x/cP\6`TN~2#@=Bx{d#y*h`<@mg`Z:g"U dO7!g_|> *$0sJSq }ci5T|hӵH(6*u)xprvwv]nS^,+A[ZzWe̱D|"ڈ@0NP`4-p됪 N~GSYy/ojjM. t|Ȼkig#cw[pkZәwCr<w H\f΋ARç#,![14T޼e#Ώu5FE+|@uP J$4Pj(I*h =\ 4< j Sr4coP3e;JΜ#fʌJ*g56=jxz+;?::HvA l]s-io7P#?ukWozS.(=j;շ{e|5L,ȹ% "T,c}bޭⱺ [Z{g`8 Z}`k<Tx;GA%5lS+ ȡ exW, 嵂^J@^ZV尖.52N|vW.`fnjrEsdWke0^D$!Q vTtӓqvN/NTLlX0ߍ wb$x^/ X~ޢ3d5)}-5pW}Z7O~ S(\ѪpzkAHӐ_7DBɆSw7|'=ܒwcDCYL8[AK{oH|Fv 1o{_ ^a -.q߳o ysF^L`AY*|IѺ,_9DeDz5{/PqPpV_75 8r%I*y2c5r8Q`y*2D$dng'@N wiNqȎytntRȂ\\΃X`18dk;v ?-w#X-K ͔ IA%g >w.28*<ҟs jlGK~p(Xl[v'V`b>x=S O dLUAYhJ09N$RT] zO ui?S@5>`1>$(yUxHFI`f)~)8wܚ szl\uCȚ\[-^r*@V-rwBU݃` !;+gbh8Dټn d~Ep6k'z?!(XƿkWvnaq;xtGr HL8;#G`m I.6]zO~E۽9^X"(fyQ_ <#O\^?|'A6#z|m3hn.,([gtL<.pȻ268=3s_Y/$ARȦ Ho}_BF#&O0Pϒ!MN wr\B~C 1D&VS=@ƫ#WYej$ݎ LR7 A=Uւiid3>L/_ckX&al G {dʙۋǁGpjdz.gbo܄߆O(kn@MnGÜg@̥[o#yl= \`?yE{HtЊDÃ.fK}㾞gć< ndu ~9Pg\Z`R/sƦP,cm? 7Y{zISbȵPFP9 \K Rg- Q<pt&VKx>4 爓O傺B]]xKNQObrz9| ȧ `ocM6nK}PyY bu:X)vPsc"}֢]d!ХT 4U֙KD-`P Ѡv*-sPv):P3D.+/KY՗AizAP>g'nmD޶`ZPE@' 2e !jP5b3YzDWVg[:/iq?n5WRs $V&ަ)0bV;< Im!ϵH4"&?ыa m OdZ,ǦBp8J6=N(i,^ պ%2;\SM'^>]9EHU!sk^!d-8.u.S"R*{7=BA:$_+Φ2j-^ѴyН f1mwꥮ7!?_a³]p5}Ogvބ@WWx4>m"Ae lcE/ BڴAn)!ތݠ+LYn @(ECPv΂%%N.2 EAvr _?Xv5H> >[!ws`nT6hmZOt\{v!֘/@Wy&ٳ_TDaL서~g 7y!:[G\i[\ I`wt %>C59 62ȥ;`J`ÆƋ9,@ D"G0`B1ް ^>&G%VJ12A~ 2@#_.pgh J͔A." @Vxb /˶я PxR?QA&^Q]!p$ Jm&m-rA<Ӊ*p>@ŁoDww$ oО;@2Y/˓I u@=ș[p^?Koyc DyyGga"|i$#jג%y%M~¾u^Sw&|3wI$])*|6TrE]w=7/{7̓\\Ay[^w$!_ę/&JwK#+,own|.uH-c3ͶJpgd6h Bi~Oݨk0 )B7Jeu6l۵D=D5Ql#;$CjQ9I0r,|2~h#0~+MʊP9t L}|n+{>D%Q`~Zy]>9V{͟9tqD } -nd%p3 lfxN"6 `0?~iLQ 'bdQy9qy82@.f?A+0PWϬ1 /TݨC$P $Hκ[[|_K޻<Lʩ\JzvA+WBqQ $Ol`0 r%c΁yOx;~N;i6Z?;,)`\ 3tg5h; /rnj1`0 ÿ2w ߁"> >J^^R(Fjٱ%j DH)?-Ao2Je@.8 ;ʎ|ǔRD'dTocYG?8o< D{x|?>Rp ".$j=@yr,8~ARD"B ~_^@{>5e# qy\L~[e0 GAG)84 EQ%!̜!@mٜ7 ;%ʃM g\\]=?Ǿ\#1 cq =Qi\}t|N-3OZ%umy/@WUP,YϓւhLI;d〝쑟1WRӯ5$2@;*G)RBtKm HQ9=-`T^Ѓ;BÇcF&˙6 `iYJIQszBRۧR?{W=ەi@u5%~Q udEHҴ/fZK$:]1)tMNj.* Q2.z*~y41@kF╇A|ʲ} r_H'ߧ$).plc9:/߸^j1(FZ9\Z |/+ué'Zw V~Szwmw"`!]ħdk^ٟ7MZ{@1@44 `0 |qx(7\J [-W P!TO2]~O_@Vun`c\6.:eyГ`;u%-pgӜjA928 8*-@|f {Nrt/ iɱAxͅ6i˓->Б1~/frZV=#|c;Js6Iv)c`iK {q[{ mmHzyW5sIP.UW5y_0`0 ϶4|Fwe.h]^Z*^Yhɱ'ǧd,W.S.gGk0Nn&Ė*(@馜!rF9Y9aop]k*mgCEjYQngiYWny>,R&AIS?t<3pVAj%luG) Kl̲|_Vn@eja#|ir ''Ydz6^>RΤ,J!ZDptթOr<5=t)<|pAx 'B:чvg0 ;O{AN]p|apy# 0=48DЏ3pP"DQCgOON8>#旋*$6Uu6dCHk:m>V{ =rϸ)g@Βo_xqavُfۀ̼n} Εnڢgq;!pW][>obfHm0  ,@W)/ -"uMC򓴰/0y W)zSYCv%W~g)LaJ^EDuiu!SwysFq_%y8g C֥ ]墲Lڃh GL91v٥X`{ W q$>=:d;ޖ Z>k/ܟ:~ZulJoG|=n΅ &65s}m.U4icds#dLb ?e\ `0~__qw{Mk}[S͍AtKk ,AY)`}wC #E3}ςRE)AhDȀ'ګ@'/`jh7~ kB_y|_2\Lܸ|\њPMo"&.vWcrkz@>6`sstt |}(x5pv H96$#df'Έic_ Tڔ!wIoiTNyα9 6 dNEH\#-k=ۛ`pH0`0 T)$|!{qP1xLpw:)ȫ%i]Z05N|79j߁_W " J d]-H J&۠h3^܂MsF`Rd݅WoPϝncMJ>vj*̎g\} E> rnTmOQE1^7Kk]e_?&TK ־R7[!rz~ҝAdrQ' PVӚZ󫁜c,Z\Ɛz2|%80zpE `0 Oi'qx-O? &$nA64\@9ha(q_܃I+灧{Ϛ%K: ۟]H}lQ7g ~"Z->>e.Xfyuy^hD,hW X; }vexO"p™\K 4~cJ-P fm9ݪ|փ=kA:T}^ i8/DB%Bp3ʮ@.*T>zюJtc(O 놧9`+>ӢW\@ϴo>%w]Z: >=@hA֝t$HH"DUTS@.j_W`0 e,~ ΅{-X>]usgB cCI 5h %R\3]́Y?7jLou @$uD-Y\YN.42[=@QVMвbhzS;i @{@4u3s\zfZ؏@ ]U@^ 8jK/ g@5 qLo.Y>@.!U8qU~ZH=r:WR 8O{yOׂRZ~a/ 9.@/y"C؋ki+CS!Rh'`0 [~}oWYS6*AAů7d?U5;Yprx`BpAmZf9`&I d>~BO.>#],2ȾPiDEḳ܁n )' ӖX&xG*X[X tI kQ2 Z% f. %wYM1_piԷFG:WK[@Ck5` pp/YFFu7P||r6*7}K-(?w8Di: sA*-8Zޏ7Cv9S,z+:rwh Ƙ> JN >R(AOdWB0ki*z,N@?8Cn0 !yjVdYGvrm@‹TH>77 1,%i+Fl {rRpfO+! I&HlfT` XayhINY'/ɣ g(QSTNV+f"`O96G~%S>VD>a:@:Y':OI줐HȤ\&"+ `0 9Q]4A}݅UV] x2E3lf0 ȵr-`}D} _70L2#?q>/oP2Sf, \r;: 4%yTA)!#<24~q^"P@(SՇ?"hʫ Sh0 ?ޔZ:u"0AyHlk0 `0oP^#8_iW`V޲ u^~lmG-K/_c|$m0 `ȗ`M4 ЌiY2H"7P&`r614?&»Pjs&QR>`g&'y a·Gt&ω_o!L^ooDS_p\pYl@vdO `0^IU*Aރ߀[z T_eEQDƃ(mqQ^l#O(?+Xg) nu]?p- ;z6$^=D7}+@^` NoY8z_}D/ ) DM | c9Pg8-:]VJk⪻U{u[ }'Hpαqf/wLȍqJ:/Kń`zaO_(6l E qā,&Kr4R]>w \,'_X ÿ?_ ߽e x| <h_}GDZG.aYU\C]f.U^>pH! *ݿqx7X-6 IbCb }Պ_ujKڀ}hQ;R7Q+usr@geJdL? |⊈OAd[Dm+aeK=J.^H*r^rcթFU`*FE&6e)CiE0/o8Ky #pTRIes#(2J=%eMY6_m(t]yLp8q<&gL}Y$c0 8 lv7^h1Jb+Cs{iH1.C]vL`|X.Ÿ}r<b$E^ /f @^e}> ^9Gx3Ym':p0P}/Iע!IvXvZ4(d= D>e*?   s I^5p9WsrCӌvR=r!r˵A̠̮ЎkA^[mk3S L|^'w{՝Ōv_q] H4^s<=W}ɩ32dnL[o!s\,;DHEdiY&Cܴ F!F;÷}h9=X-A4D#ݜ 2fHЦj `af[G ɹsۃ^J$kVQku5rzݚ 8' ߖ_"#ddl6$:Nd+FW1mF| l1v.xg5e9X3j58(8BW8{|*UӳV6փ r*;::49xmÏ.Pa#En/jT^\ʢ`-E/ )RS[ku4Ojt!Ubъ_p ÿ?h$*XA'HPznPJIx<<_8@,-=F zMsr_'>y+. nf$4xkVueU_Z4!mH=^sqZԥ&RJJQ+b!{nօV00+Աfu2 N9urYӠKMYgӒi:\mGPGX67;_uI75|rp<$#;8 G!T ]N=iiͺUJ4,1Bpe># >D εM}J#>/>>} ߷[2oٻۿi"upȿњG]j@QJoPW)r :D}-h4_2*P tEÒ~>?/~κX(e+էrػC 47;=eZC>w{S:dȾ .ο4{P1^!4h8t,5v qKN]:0\Rݐ@)*>zT C7}|/ցƓj:%\ F-ppcg}^{w3posmK(t0*mp}õdκN;uB mxLT#9{K"G1P?([ilhSVA:Kw^=2C#yPgxɵCGU>A]u w +NC? LL5L8tsz @iU*%2sZN˄oܡO޷zUeR۳ vN'ܢRWԀ_ IS'g\vɐ-1V;Lv]Vw1Aޠѻ}^u/B(*JoM]n9\[ty܊~#tjp'f7;un“YGyk~\-x&V=.]JXz3s#|͹5hO-:N.Ap~Pa@˭(1ctU 6N՝sg|eLܵ4ʢ f0 ^k1L%{'ל[rKv˛ 6~DM7 [7v983Mtx 2d.Ⱥ TWs$OτUn:ܺOWH*_,zH8̀=!=" I[L)E{MiDCЭy7?tlD308]'\n:ou^ ޥ3tn\Iq9j+BNBh!-@|&ƋסN;lZLhAdɈЈ@\Q rI@uFD|q?|D|8|1K/W ᅟ9D }X&Bx 3 >,VJ@лoVX5!Nxm=<@u+>˳ 2L;M+L@z  .-[t#P(sQ 'ʥr9` 2z 7y1NG])4[du=`߭ue^8X$Y`͚mg1(7܄X-*A= e/05]TۀnяM_u'pypōR̼Nr2J uO_{ z&(IJ2 %raF. o>l=lSAb yսc0 FސigsAz xKyc)1r.-}KF ɚzOq}զc$@|.fy`Y`)Y^l9r! ǕJ/^ԝls;L|d(X7ҟ5Akv@!U`0Xϧ@'uYPE 3% {|T$1P ,|4b;1+nx|I|\s RN+%Ⱦ>pzxr.c?oL[̅)dc#Xr KȽt\ ȇ|U׺"/q (t6pm`,kX5ae+.K=$8ePF\џwW&4A* hH_ic?Sf0b|;øL2 C P.* _u%^PnQD J"L_@ 9׾J((1}6|'rYrZgD7-u] 'Ir"%Bo70пKo ):#pMqRI!s=dWٕnށ%B)!WԫUA6 ecP&@tu }(WKyq벿IwԝAyOyO-"p`Fsun+k/rh.1r,x2>4'd~vv?n2ʼuj:-pg'gs;>Fkėb6 Vη;;o8s806\>e065bl6L_h2BQ.O?ϡ]WYڳFT?%?念!h e b )ZdyD?eb*)e^C:v}6\vʋ-%PB,"J<.`LH9y@~*C Yhy D sg1aM˜&ID8~A!{s2"ߟ v.uEmPB/JW }\zD{ b2BE)Pol7.ws@IR2T(:"@K }u:,7A@[`1\H ULW0v^"$UzgyGu߭ PӳF>PRK9[~lW[O\;zaKCo w'c+ÉCO~{U8̕?ºu_Oo5md?z[#@ƳRao} %:D`̓itw r$eM̂ĔHhrq. r3{>Ŀ;`'4j|DCBu ,(sІQ4k +i]8. D~ )-`bgs)qx,8|h,fCB“A`~hvS>N%\KSrHƼQ0 %b,mm2Wηp׿?~5(԰=* iNgVM2eI}^cH7:p6xtp(lvYo"Q7*=- >4*S+ֻO_u/(V@Ɂ8O g&A奕V?t5|<{V`Ѷ ڻB(U%*{z{{ĹpْZBeU|ǕG{i?r(i*>8IX3>@ŋ-v 0L>` c@GQP++?T PW*ԯ- =} ^޺k@(szkͮU|xy+쮵'a7Цjk{"G6v5عT5Sl]#qݻ|o,-({{P}FCFɯN:5^<[)hF >K"nR(QJ$ϊ_ǫu"ܢ 48)Szl'<LDSo\~|ᠬ)"\\X+ɤXSሇ_| |.V{޷?8 ?׻,H,U:8lPosI 27XF8}~Ppe,rpj˫afqc. U=o nأaC\GiQX h ʅ\t?~qVt , Vp u Qr?~)#*6/XN%o,dN+klb2+@}; !v]b;=2g>x CځBk1bV;aV!)yV([{_RoGݝ2u((Q:=H>ub}tُ!uvath?dwU_2JyO 5C@"PIU_S7%dd˪g.syyԍRM+v;>oAk6Lx9V.j=ZBB.Son`1,-X۟}/- ,+@ @UQ7$O 5՘vVW0 SUqsOͭ2e+{ހz!^Bp[oO6XcV3V ]"Wd.YI,A%7 1ÕWN]-KW,-\t%jG<+ypvعoΙ7 a¶By!0h3Wk  fl1O.x.N5_/[}s'!zd. n`R^ Rk>L&O1 Ch.fL:K5lm]8 Cю>~uz|.^ʥ $~|}phvqq{s1=s/++w*M' (q"`ƫSvKs>=GgewKԑSCAOr\:ayy*ULLp;ɽ]H+Z$XkϮK# .΄:joo {{W9a9!Po̦hױͶ:ǖ z[}Թ_qX YZɰ֭@ٽe/B^}=#&ABᄴa,D&(+J9PW)ŠlQ;g@{iilV6* aw½^t^ƩȦ"ׂ"RLL7L{mg) aa ȍ|'Y``뇖֕`jlX__O-m;|r¾?X/$lsǨ%ݚf6(0Ok6 vc8rlf(T֩rFhcu=Mπ[8W6 D0 q ܡΏuW) U+X-AhăP"$xo nV";=^֩pjך=1}X2 nSE{ӎkg@($)k/aT}lgǕ ";83$C"U;nH?3 Z,dnuj_m NC9xSd{lm1@Q\,R9Nğwfx#) i= |97M Y2CpѰNK!pO܏y4Uksu $E͵dteh/ 5e.AVH3CNpj+pB2d bJeVyPd*֚zOPԾ<K]7=?s [!'17+7 z& J^7y?r!W}4\X8֑G=LW|!ǟ E+ڥ V % "BDPy#;뮺'G/|gf|>5XpIqrK{žK{ ]_0]_nܻ Isv&턧ן? k.q. `Nhղe.w!/"mpAC¥/\/8 J3 hiZ QYQfđG̅QCAв|`X7[ om7잷RPIYK+󭬩҆/6րߕ[O1~XVi٧*R̆'99Y;@昛1A~.ZJ4S?]ِ<'y]e~El=cR?ƙCtn;r|fs)Z;-]ZK^V zn"H56Z}ղf˕Cتo*_߃cs#={7w tb`an ?_hWg{J%wM d4Yظ,O3W `0\MNqcW׽{XKDz-GC Ix`AXR_;f :$pH1[!tw#W=V&3S[xPWWzd7Xa=~f#-A 4`/gݬ/( B *gyYIn3_:c(\%c)nPq48ǽ5h;yFo~3'ezPla.r >U9̧@.X|FozՃ`0JH=Z/!{CA?߶TYT:D ?>B skW@p'850fۚ-h:nݮ}89Nu% a3JCܷ~L{^Ogsmv.;? _& SEW?:T**O78܄Ν;\78p ]vY9rWsU+.EXBmAIJ( Q]&FVfn5ݲAN~'+se?o#?FoT/ S*{ǁW>bS.}9Vj߮eC!Bބ':kJ/;p 姖Pdmڜu,མP^]VaaԲ2.~tᐗ[|OշX=;Jr(p/W>X@qWh 5 0vلç ߑ-ԡ+=tuޠ5+|S?Ȯd/'J_ N*r/!Қ~Вx M 6gk>@ rC>?W K>߁#qPxB2࢜Nȕ$߁12`%^? s ,], G̘A<A{6N!>SU  &.rJmDWyQ^_V EfijRhz&`l,?-@m͑jLLLl߲Ie6ЧSAU}T tt 5,[?. W{]{@A~%/@}j@CvPUVVY@yY>{⢨@wh}Vݮ~j[rnjՂUmReȊr } ܶmu{*R 04և"ٮ!07(&h7SloX|,΃)C;tNoӎq@a[A~&ʭ'b\L{=e_0К Oj\Rݏdg,.믷h`0 ` `0  `0rPTH%B) IrH!, VrE;ю[TL , 6b=&hv$2LyS JRxM9d J G"8`0~%;fy o jiii(KES_Q=Ĵ4*mDkq8e 1-0SeS%S)H%4Yq L;M[LkA'1 lC{ɧjZS%SySi0M fb: @5E P߫ ChPB`%pΠPEi+P+@%EIP;PGoC~'cn΂CB"9%GL`"lxrb&_ h(AWLP{~QQ1$qYAK  +EE5J5 D Ծb(U?1 D}\ M3 ~ t\pZ^- XCM02ob(6MtS)5 u): j>wy`kmbX$u:L+MM A-P הg6:QD-1:"D`\?7ˋw; q\9ӽ@=<7CXzeie p::ϺbƔ˧-ncM8q&>({MH>&0`Ŧ@jv9ˍ'\W%ȪU;l\Ѧ6kK- InlZVQJ+zhޡ\Xyieʹ-qO3`6_0νsnǰׅ{-uG1'w]68DX޵L̇m'n ?Y/6~/5T VAr!Mqyia0U5U2GAQj"" νqNɹ8Gq$2`l:g:I? i}M;P2VQjb(L :N#ja$f&%5r'RY)B{)fÃ{pPE`:k:i:MM@SnchPW x1N|`ۢ?_J vhd{}pG^.dhilP_)wQr@3]{W{Ԅ۱ $4ڲA/T׼4h"|Uީ7l1$:y-s tHLPaxHǏH_W""D9YNTb?+'nٺuzBV\VjV4~xҙcj UUX7 ?T d2ڃ2QRށl0p4$$tM#G{g3 =T]SūvP*)J ʫ8!Bgpb}'IeNﯼoM 1}#,7/o s,;v?]O}gz>/WnvI']c? :tkh8~lq_8<|< ۿ'??pEH 49̀e/G| "XȈ#G֢!}6N;qMhrU&@_z nlxk:ܾx3Q"@7wݾ hq7SmO@ـ2KD5ށ.?t)}m=r_V.=s&.3*PAmy8sJزx+6v]$U/TS 7x:k!}ꐖ&BtCCC(^Z^8*iS<('HP4a(zxcxv,~]c4tl ,#)w] '[!+?=xye+to__ 1b6CuaӕM~|jñO&>z;v6رhgpT˹$$%ׇSO8 ':r!t6LdJ0G;8x!ʆXi}սסP*SٙgI!P蓀AA䦋Ii 3f1YLy3hH3 BϊU)Ν΀4Ϸ=Okʵ宾q5-;!ĀSZu3>QFeCÌ ۊsPj74~vCQNPsE>5!vCOb(UqVLPjia:BךhM@f֙c>օ3w ${]&*"^Cz @{`9ss%d&Y{Y3{)LK ^xxcI #TRu׵&Vn=ئ|g%ukl ջVK,s, PQp4VJqҾi+מK^dq /\3 rCDxDerT9Y, lm6,7@ЭollUixtf/c%TTn՗!&’FR=G\ ƦWPOv}zM\iRjLU*:޸>733+B(aOs; N_qn]w-jVgBQۗ (Vt(UKwջ}kB砫oט.C֍O@^pp:w9 ;q 1bcFBOb+׀mb{M"Mµ3.n@UY ARYƦW4B"~u{&OZjCAaAXA%( 渍rEVE9Epi3VtOsqܿ K2Rޔ ZR5,I?< ! yV Y*,4V@ jU3}kZjZp|jZ[P9r~BQ⁸mG065*QU&UW" H+HI ;Zܱk4/o|`v.p)r+Cq+|0nMg6i ;q;n3P.,.Z]_k7hUU@-ku/]c3^0.^1E*&Bi#koAV@ yꦺBAp.LJNoG8q8l-8p Kږ i1t{nmK3f_)ٔjʁ[yyХkFh[M6_8ccG|0HUYQy ^EyIbX^<|dѣDp~zOJ]@Yi=Y| )I?\!a^_s\?bq66nkEօ=L!ΐ}Q*,C3-,4McibXIgfffEjZVg!(8= X""Gă [훷=A{[CIAEP22`_At]E:-p[v-}]v33vhUp|yv4)Cd=,yXzuٚ`ow_ G#7%}D:eyo/țq%`t61&C^ylRҷAD&@׻vt80̡%ؾjm/>=S6@5j͡P|uσ皊le ][ ai#^@uà/ 1Bl"?אָG5YYYej5_uk`k1ܕso&=̙?gGy4.i:\-)*^=1T~Foo@(]PSg¡&M?w* +*,G^n:msLH=V TJR)`SG'gvk 0EQNT8.=x.::oOw77[H6d]d,͸Y^i^'@kg6 amgm 1leʖ:ppއBE#էJ,+~;@N&ȱ@".640h ~>u,M`|]E0(gNBLhA`uOk{N@Mj״~o@U %%e'Xuj`CՐ;߂(sZ ~ֽסaZZZ C{6$mR$|Z,Y6< 6i v!5mSb(nW<0o^kO!v`[jԦ59Q@}`UeѪ)Xc[ 5plpsx mza6zƅjM/ϡo!DVBuSzu&@7jv nh?ϐ}#;.{{X_`خݛ0dŐzGCRNף&j 2 쾅i+*Q(#!ħb8'2``1bgƫՄ8: gT+vJ6P>TߦT״$ɂmxw C bP`ުU  z%8~v*wKޗ\ _Xv NNENQߵu/YM([2V:E/>`jcl`~U*`ot j8t~ w߹}[ߪzu pMꭡeE:pLȻ:-pT#Nj0yƵÕtp 8",soxSֿtpA@\wh [z/n!'@h">oSs p ECaH~ \kvaPy9uumPK֍ `hZFAL ū 41 ER@ωTA<t_uP :y@}fl5oA=PwU1Ok?`ff4-Y?•nzۜ]^V?]} 09Dƨ1j,\WTp\\|/O?Pv ԧj  ^m>^W,Yw_H1& RHT=w y5P +m>➈RI%LER %uY]-Z]3`lj{߲~Pv^9ꬺ-QA FQb$h7kePwTMUbX*Pd_D[LLAZZ%x@Y)d /诂jکM^^]k\:T:[upgkT %H)hp$(6pp΂gCo̺ FqK؎dZ^"PY*S值.ʋUߩrT*Zˁ kzCDxjzIM,d< Fz3kObmj'B/rArU bX \"x@QCZTZ~I3hoTTfTւaj^6^6^p ot)7]r/67 \VDU' "# w\jWԝ`_͵ŀ& FPvzGNkդi1}]As2L 'U'u+X "uSKR/r33?pn Si=c?A~+@t[hV !++k:`\qx}H49iD~y"}(W*?]%h+ _^L+PdenJJEtA k wo<\x 4?#=[!kl奛KBΛ9r6@VqYB;._F7PRZbU RL`fF{2V$aa<ZV _&T02e\ CEc bX,{w0 6GA+D8 h <@SZ 2r8m `9 .quʫP`Ex Jȃ ȭ`lkl) 7 2OZ=ccki r\'.qxqA1JuT rDx#gv133K'|⎈( 0kqZ4h-2k> ֲo-A"`3z@vdКkMOxPkCŤG5蓢E?Ս~Fwk?k~"񗟻?DB<魛7mn>m _"Y$$A"H>^U7x gn1Ζ~2d-;vqu`wnmptJ=k:DÅw/J> NNVסuV9.P,\XvZEig1Z)x60$m\4 )!/ay$4n`qr.JBbAҎy=uQqG8oaJVxZ { gݯX7Zw8%CGd{֡񷍿.VmM%Qn`>. :%Vm{֫A?_P7ٿ/L6 畜sV7ol;k@mE H7^ -CZVgD n'xqv&}{vwjPՐ!ۘm^N\pi% n[ݶ "z~Y~'AZltwʎ~5z% |=o@B(R l“'AbU h"i3/y_BB 8y~9sP=BZ?nuY?_C8qdNu;vq!kM_N8$ާwxaZo11Ƥc?qѥ"JnNÝBk3vDY(xV\-zh{ !iWJ3-l,,!4/jÓ&75yҋ} ao?{\xշdB,YjZ9vtCZAye..O_~R69­[GZ-wi-_kq,Μ9 ‹sέC+dC3n S75~0/]-B`,hzo0tКC~d[?g=jZà-g uaK:'NKpa ᖺU?cŻ]^r U,Wn7Jk F2d(*AZ$ 쵿vZGN'>\qF>;ܹc-AYY%%/Y'J\ F/W[pӲ \^ppmRMu1|7\3]"GLI]#mKk""ۊ%.3~5K&88{:%îݣwW;Cb7-B[8p@W Ǩўpeךh)NJW:܌Y;5r;瞋<柭wv%$tIxyrǮPٶQe81dp}C@NϺjJ3֤E|w=>~1>Dc޾ f1@k? ~)+8i p'Pޢ{Es(UrGw۞+{?-swٞ[7߈p9D *l->\~OW o^ފ#ǣA2$JD5$]+cX砵s4&v#vtizԎS- ~p; `}A!F4_55A`ji7ŋip饧C v͝=n4W loEc o 4m`܀`or$?{D]|h`rw~T?+EA7ZҚC ]'TF5v>7_:$.H: .w-LMEsQ珪Υ;U zs Dk9Ul| |^Ʈ#8rl =WT4e>|_ B\jFպRzkՆпd Q|:ޮ=7 RG9))Ջ;yٯ@r|rI(/=Y%!Ips0l륌xKjiQ)\Yw5T֪l_>@Q}i?jffoJWehַk=r/BŐC|ƶ͂ǺY!`rZrM]=4((, .7~-~?pJ+sYJF>_.ܪ99 $&&⯊&UF}Kg[I$8(t]ӷ@'՗n Zɖ) /@ f@zN`իb0}c޴j<z\u_ۦ11`fs?~#i;@5B_v> kP-%sGBua׆ S]=;w7N(Xh#Ǧw2{vmE01nL!Akk#VjrQ8 .!5Codjf6*Q!J hIsApskwrٳgΎ{{{/lp:c([2=ST}s|U0-6-5ӳ{]9:t,vWZ,1_))}jEU^OR65`t;ayׯkM0cy3tKAT6yeRJCy@ }v4:P?O p%;=a,au _JP`n0̤1-UsgPHe@4lSQL1Į Nڈ2@b>x2y .v ɺ| z^AQG ~&40&8h s(j齊{@ M-ެz vvIƍ`Y2*]j֞/[Ђ ei⦸ ↈAUOE¦Et /:ssCuk+]9{ xTc@=P *ll~m||[> 77.]uM]SϺ`=D~:Y@KYz!x :Ղe: qm&_.^Eފ T əPܤAq(TՂJ݂VZ\ז\z)lp[gA`,g[j9x@<@s"aU#%gxǦBO[%B*j y}`/q4hw>_O;v];־k5(FQæNm=)Pkspd15c]dZbr4j^>W}6M6q dl/yԆlD]=S5;i;@JmtcBvݡ$44\v[\w`[ 9}>|؝bCN DYy ^Pn=.$5l^mlaOgjffJ*i`ݺM o9Cn\K"`5jU٫jio9F;C{Cua禝}-{WU5;oo.s ~}|wAZjJ(ľxw^߮ 4 F7=R}U_l> ޫrnAAߡ'8qƫti Ե)ߖe@ Z y_fe%AkIpiGhV! >j\0Ə {ސ<&xZ# *LW@x /m|41 Y3|KAH'-<)4oˠMkݺe dNiV K]]HϺ^_BA7.]κԀO+>C!0:ݱelr.v ut*,*CC KtjZ@]:PT04:Hkt v,N Z}T<\q:QWvyЩZG>ZP&ǚ/llm67*=z{y{z'%oU/Bf<~8\BZ^<)xxv~ a+}^``\J/_ziZisGuWC؀SA69MQ=sg bEppgAvZfBTV+); mz"ӓ4ikee)`ia&1y[6 6L^qp bH[6TL2)y@OooUt9t&O:I3Y@ 1dfYA}q zD_ч@;@tݠ[X z^5 >n v]\)C"8) lTy FZV&x{ܷ}B= t&Ыt-=b'A{C{] %*$DK`–ۼ x,ǫ_Rx(#z'/} +"薺nr/x^0YJS* !y@qNgҴڴ#onh`ZV@0ZVK,͟{"(%3eȚOV]zoЗ+UOEuUA;Hi"H!`:c:o Lf2o6A{QM?Qw(K۽ |jz]Xa?RBi/“tvLZOoaqM]Q Ab LP*TK+'mz'Pm rY|͢M~O*& 4{FZ~{rӪߕXJB-rPN;=lv-k+܄ BV]vv GՋUo+XXud̐)E+Pqz<(F|#Sܤ@jYz7AJiIw=@AK 4!*U ߉%b < gAUϫZ{=\v`ǰ )%5U5%5ͮZ_>' fJP WjP`uou x"D{^CتePUwGCpײh2uDqS0)vTUϺ {ajZAA۪m6ѲxMT) -> 7/_?u7(XRgY,YV38jo O,F$߈ C }N蚙}@s5GI@[PBO tiS'k+@=Pv@$ gfffffffO"U 8O)*FgTXFo/- *UYbC7UT@v8`v+0sڞlk]N}8w3|͊XG|۰T1STܑS*CLȶlP1pBzAI&ou˾+HaIwл鍘 ʼnEǫmCŢp'1r`2}RqbӋAU &D;V䊎PSjϺSl Q7^G/KC̵ BIb *>HGA;w_(ˈPTUYnW.o@܉vv@Ib /Oջ-}~~aVav0f5ܾr`({ղO/T]/H8052ti)i.oac 눛5A?πxzI? u[کL86ؕc5Azk7.rRzG|6%2<<< ~­#¾ SW%@̩̘V&_5uP}ٞhؿeAX ~%ygό>{R=2~ zWv` .߻GP^UU w XqL8ϐ>:}Rr8 ʞ9 DRS2SLn# "9"qAs촿SKDqr[N}vXv6*3ȴͬ^p|Q#ȳ<ɓ m oy;guΞkݯ\`u zXPzzi,99巃+WUw S-9^41 )p?.=x r y  8tk~< -&*6T++͒饟e,˿,UeRU~= ƽBO >u`X0ӡ[Dw@˜1gG[ͷ6oؼwoOƔ$.| 'ˠ䫢af*=G+p;72'HwvI?j%@3O=z1Gؼi+`0^3[R n\nNVC^;bs۝. cJ{b|W?q>DE?}rWν8L(xp.!')ִ5ZK}1UۂXN_5m pBfB;~BSN^Ӝ Ȇ׌.a ԟSoT0I fvAp/:x~9 zo^ScaC1S.`!KptrO2=z mn8 wtL|TQVڿ=N퐶m9se5#_?u쾷b(9VRZb G ?6L{dk%}lKP~ɤ pya&Gfex#Q~{MOkG k#u_ w߉ ~x-ğOMoۻf)Imؔbʄ}5 o9ʸ{wpg!28E:vJj˦TRAE=hZd~uzN`Ū/BX>N0LT1P!O*ߓ3j@iZIrX /6l=_~]P ;7HxZG_A9+_q]ɠ:(k!khfNJ)#s[EgH0~yKXdQM ud<{jQ=ݎWQT'Tݶ} xԱZꌰm !+|n; Jv2B̠}jdr%'%Ec}#Y>sy 0}hBiOKizֶ_tJ0UGLWVZ@o޷Pл!CaW{;׊!ZⰤnUuKaHhkQjQ>ԏ?^c=z\zmɵ"[#?p DDߖ,+7>~>"=_AyMށeGog[RG!P! yZT{~sgj!bXoS5p1ig 04ذh7Wc{/6F Mj@rV`jh,:ZVtR>`0\H7A"uY`58j SaQ:VMQS՛ " +ꦺޓ< @2Q :5U POg/ɔ~Rk *+@5V-Us(1 ʢbr`W=Td\ x5=T7ä郡ukoZ6n-]_ C zL]'ώA(_ҞI|Qr&ޅ+\b^/cd,7vrKjuR.AkA/e¹翺P26f2L vB\|g]*V^! /*pXzq0΂Ie`x0O @23 т>~?b9N]JO. j od+b,/X y@EZ\Y8UvA睏&>L5w@iA+g]} 4hdl`ۇ9@ήlevi8E0>}<8G΅`ƾ>j^{@<(-s+paŻC4jr'}=DZ_9εeKy@hޱhv{ӵ3=j;Ooۀߋm} %sG^+[]%n'>//[Vpƭ[uʠ 9QPrE|4(X0Zna|\}} ѽMr`Z<%kI`T/M^0+}`fJ]Nr`/8C CHDwބa݆= ;899)j)lmv==|<\!=(9h=d*l**B M;6I}i} UgU?^'~ |^UBpz^a95M6iՠѲf9<)ׁ -loG#%ucTojpR:'* -FN֠lDX7Z>ϯ+L= wO R1]kXf mMƫDBhz']O*068-6 ߴjNMm(|mVkXBì3m~lp2v1;ޢ Ǔӛ{ܡcoffvXl)>MetK.v U'޽B1KA22"[`F]u/6orY}@AU yief6d[_!>숊$hv8rr *Q%׬c/ߺ!nxEZסWqcf4+@Wa"7ʪʭ*H >AAAa¯~ >˫]&dt̘*d|q6+~dcAW*Q%DUB-޳{L9a6.`=g.h颦uAT!䥐ѵ ff5C_BFI3{7:UMvsX QȆWW ܪ01=`d}w[;hҮxT\@|PƄr$=V-m+ؔf yH~&'AU{w{IjD9%9|s|?K#},ju>p77/y9*AT: <-P{^m=7 7YlbK\*6m$AסEY?T POVx&۸vRb[e-Pq*R]}( DP O}>ŀ$Ir@FI lr)nTS^'Rr H"(qhZAjfޑE1J[M:t^6mZƁ"_@9rؠ/j%w5,ҩ5Nj kA up1DȤ(%9"M(: UUcYiDHAdL{*BTQ'Fd| ^U 5 %02AǩxEyuqE\, 3dLuNW008l5}`r1yZ%X> xvL*JКiMF`jhjbj<}i6l mi2FFk rDH" LMMwyA{_{GEŠMA֓ue(hZj0 x5 ])+14/Cs&L& W@^d8z+H1Vtt*@j ]tV[ڦ/՗Aΐ几].itttTmUGmT["T@9B %@sl4 Zك)TK➸Le9.~0_Ώ/nvLUMY͏n \B` XzyI^gA,߈ogzhz KKgЛV }tݤ,/>VbX)~vS jCwa߈ %gJ0ӰC}01ƫ (mWPnv(=Q$7\VDU'Y/~!Vz^]B튺SlK׽>#h%ʵr~?+}/|%^,#0ʰ_TKhU۫vWӂ`1|ApuY~.A뇠xG њkiR~`5)zSZ|n^~Jo(=P07 xSͨC5c aVGܓ^gj333333uI]Vl6n8a :<OW*t`?~h`Q\vj Vs2'QCʞ<5fffffffyUW Ϻ1 *Pɝ *@diffffffffo(h%ZӤz^5EMA8t?ňA6dcd|dI&-A"H,3q\S ir-g-{n/>D.܅pQMT"@V+AXu n=G[kff!ZV-?S~L;MMA`[oRJAd,c}cmc0 y(ZX k,AƗ/mV[ My"Wd`vEZX 0 Y`8j8` b'O%K1A 00D_Һpj?Pb%D$IY (%CA Gr8B ) _WXX d,@ Ŕ(˳`|8Zh,/*V#DQ_BԷS7 r%!zPZѠ-5oDemo%ډv-GV<7oMAQ d̐!?m^ O˓ؿ?~@U[dYɏ%;蓢ϋ@ Eރb&PڧtpP5곪OEl d@vV;̥Gdm"P.y@l.<*dm D^yrWN:@|_TZ2wk P!e j2@Lk 7P2-'[PhkRҍM 9RQOҼY$s*qOr5 MنK3.։(µ*5 8Q1b>O~IKvY\P`hfm]m#}FiAX`h;-z0: yhn#bǓ+M&zhݵv70SPYP3bY$at1ڂ>+WFN{ Mw*RbR= <=Cs';b+aϦ={n| zn;CmNp+VIWm}dXd [s((`uNaOX"o,,'[|i83qP^q&MvN؁~F?Sn,q;wC{.oD4s9n{n [/:1V.I:@rvɓa}?p> tSqL=o=w0\5ʕ?h+ڈgJ|ϋ(qK#C6zځ%z *D(}-+ EO\89;o7.40⣧h0y <Y n3r'g^Ԭ5K)A|(>@qEkX9;W_.x`1FZcC36':>Įv1)`xdxhYO {. ] <|!eF_6bW{j-E[_ewCڑul- $KjdAꮯ;|V2 ?#& nӀ#<,z<޸ +cCa@ŋ{bRƒ@ PQe p0(3A}02YMPOt\,q)Avrqݎ6W@!P>+KP9p s V>u~ԴDU^iI@E큉 -,B@o XĽʸx h|LPM[4 u)LD gM.< lg&33&JE(:= MupG pnt(#TȈlmԲ5pt2nvuVu|)Xf?h~V v~˼o"`cxA?T W-NBfIfef truݧv}A 7qTVϺ|9W΁4ﴺi`!;|N>}q\$6PTiWwDéj_=]ZpcoDQ(f;. r~ٞnZ7K_,; UL IesoX1|Nl=O/˦)`;b63g[SpFBNG;C?A|Aذ C ;$- {V݆C=>4gU\lriƥ03MA[S۪6Vmb?ίʷ17խ`5jU7h|F͡(X\n>|,߶m7W_P=%[# o2:4<J{8~"-{wopA7K[W]{v7!qmC2y,!FżBtYڡvCSz%C ^j;i~ywbWƂRz0́΁>ZVm;/P%ΈS ޵D݌ V=9*ȨҨQE#:px  v9T)0.2 ũ .beffޤ&I1I?<ʾ(_\ֆ+.vǧOaa߆-nNp#0=v'햻U .1+!?1,_>tb)<4p*$LJXpvA~VC ˶??yNSTpFX7zMr侞1b W,.@}j :=N\LkՏ-oUe4q[%D': Z۪_[PA:]!Gy!r@}F 55IMk*@MQow>#!ñC{{]ܿrp^}dO^~A"4x~~0CfVG?{Nns.J\t"PX Կ=Nv8f}|\bse4oL?~r{;o왹ga\4\~i%ZD yAʹ8xMu!VvHv\?OlZh5]~װyKG/C ֽ>qpSO{k3]^d͜O7~ ק]: =qp xytNT0xc+!M-GXX0oqߌ>cc6؂ڣwkErˠ*Wd{d%vhY^'#YA vm 'u䵚@W\Ls[ &oJ/fdeW|ZuJ&(4>Q0^'S[Y>jp!ayG'G[@(+^d &%{A,z/s=x4ȏ:F߁&2D-n| i g@-;ZuaC?P5x禂ܿ`7Л=7ٿ_U^5;v3U! Nv,Cg{ >(\x%$> V1rՈ7ૂk7úȺ볠jcU&׍(^q1\xe,H5K;e?͊8'Ή@gΈ#p3ї>/0ь@5 H c(E%*\6veصy5|ܺG ;csn: b@ } z+.tv ďMX K6/&'X60Q 8Y}.@{rTpxxSa&יƫ K>џ~2Fy?N]@A0ix^*߬ {_7j`JK4]e`r1|0O Du.X߶> i_M{*ׄr={ Ze&hcsZ?~t,0 Nтij[t<{ /. 75؂'%:h- qжϭ; #434:; Mm (((}QFdQu JʫдiÐnC =}XZ<7nx R^|qޫfZ2gxĤ^Oo7fn&|40ÀBc.d37;ColP=n<.1NsҮpl~g>uz 3(ډoqyWH`{TR}1(C:X31x)ֻ[.L h-eΩK&.A$F쉨遞bI/&JeLL{0#j8}]]AMw)t bԘ nv b2X֐FPkZ qk|6b~{^؛ l:u I%JV9sΟM5TJ޺ }мWh 繟}Yw֟Cn>ռ3 #5#?vQc *j'SN>n$'u><q/8?0ېx= 5BzAD7o4GQPp;m&VuPW5-ukn &}NnS=Эu8y¡5.‘UG';}6\|PpKa8{i_X0z5y,* in1P#w mG@|&A@5PcΎ;ZAVYS^5aŲV*dp~Eg[ȟYZG8į'-@u<ƤE8z_;s@plhMgf} 1zu=H@w{,*cuTa(9N ! -!>rvJ03}ixycgg½{@a-:| lr/qZH`8b\o Uq_\h4ņV-zPO8}gZCEʮáaNk ߅j۪  ,CP'?~~ |_'uhh>}p"ꖺ-ZZ5ܳ >nAo-j  z}A~ԄLJz@gw1jeSwxPֵ)[HX5`e5b$ qӷ;@54hUՠ2Y>GPFL@+ӪO@yAg ]PɄ >- - TQ[^U@)dw}d`􍽋{_DXrw|pos4Ȳj`EP |&X>>lۗrO؂n|^~zD7S!~/l_i0}w*ԪUt}~JFk-Jʲq~!}[ 5nfM }ג^Yo&usy :o5:uyM:W]s- *lw= N󝶂*R8wߕo[^Sބwz~^ݯ[{`MMmػ𪊵ߙvz!'K.(tT)*D"ҤX轗zRI'{8zys812,ff^k}kkn YRl`(pLNi l.AZJwގ~  +tJ*eQ­EE6 ꮺԥ.uUJT U*/1C/@3kGd&*bUR+V-+,j 3C|\>uy L"#GԄ~.}g5;b܎{s^.=#3!X5K΋!MK;VUV ︾6M;WTj[MtHuPˮ{{+ƒ;ѻ{mԓW=ZuwMr^u[55+.x6y5[`aiE;D qL;P{}&t0Q{_WK_]}R*OnV[Aߊ>ֽV7 sVz+}bw:7輳SOVe`;D@C:BleaJ <<@{8=",ظTwյ=OO]Q{fi(ɷ4K@@;׎>(|@ۡm6&0ͥ`t2W\*bO a'5薚Ay E-EOxܺ"CCna9oAzsUmle z2|eM6{bX(9gNYO֖a6l$e02L#cH>c1XITv* .CvN;j Hҽ 8p@!(?;% 0$gpڼj㪥F:+ VrO} \]A}Ϋ3@J ,-|}l8nrn u [nymR!^h[p#оЯ9`0W qKv:)(PI&' nn|J*UT+>P>YTJ@~R?=V7" /#r=\?<'W3\ F#(S\8ܾrP|(oYv[BrP!vZfd(+M@#ntJ*UTڧ`dd8#j?ǨWJ_pjdx"p5kq׵2}~LeIzkD7MtN~MX ka g2c&- ˖2Ln&g$@ȡr &O h%/%h7+Zzȍm;>Ro%Btע\"A4Ec(R~!v ZWE=QOԃ7: |y\ ijʢ= rxtp./ ƷPsPtg()-1PY[7 3*X`_8bA4HwN<\#bWd_z#yT{dt+@;%>^Ѷ|]\@x; Z)wmJ WG5RF1qVL@rGu1PK&j;I%4_xA+;hd3y[r̟p>1[}Abr|[b':\3A˓_iA+ ^E/qz9Y<-Pd.f6LvA4y˶b(<-S~ (?hU4=EM hZb:ș"Dh%E}>b:;SywqcUzpYܪ (Ab׼/ӭ! 7Ŏg+{޳ҿ|qV@+J_ p}w[@G:InE@ %tjJ,O‚]kzL~;]S[6cq=p-_n7 F;d䋠j}X)@`x~mC$2}1Mɺ$彞 ֖,b&#|OORjʨI9yߦc'}XYYjΎs|Y_6:h}0\LmVI} -)y:o3nav^YE q&!3S g0&)?DmȌNNil>_I#^m;P-,dXq^ܗPmj5urR2Tœ6P\ܦ!y9 UYbgdS<%}ʹ~˝{Cwپ)߃Ϸ3.֐8:o`Z>$8~K'zdG}K_h"zA󚊗!qF Pn\.}A%:vuPͬv߆ȑio&ԅUKk*A`Gm.7j4kT,ۙ^Z]TlBD" ,x6CBpkc`ZCAPܠ~ x4<|addʈgy~y^bh'Si+X~b92gΌ[?ybkףn(̯Znu&OcM |{wy͗?]c$5;@'aUGiMn:"=dmJ׎^мAWA~}}*rxl{5W*]N͂65g]_uoz5:jvps}b}T-ÃBu0G f…\3׀QFA{Y/%CcP{R@9w>ݵ qq96)>`3]⑷0 U>xcpa[J H.ʫ!dqF*"!=uDy6ktp6J+yU${潟92} J ͦ`.!̜51w*Cڶm&HqOoo, f^вߚzk軷2-¶V4aҖ? |xk$8Zε ͏ oB8q™r 6:f_OvJ 6:cA\־0beauO5 nW,| :(X6,Fjr20wV`&M9 LB#lU =t'۟<;zZ oT7W?yօ !fWSxh?Ñw.a.@Ć#R_{B["e lXm wz9[zUj`qt zXhAQT-( ̘RKaCyOކ,a=v;m9@X[om/[@ٱ١`{v`ɢE77_վ0ҜRO;eoAƄ{3`d#$<fyFuyT{=.=wzXܵ0R`8N+@$DT#xq㯡߁~AuS|נê/txQ1qaD~ p㕹WJ!j#GZˎ`w(?A;k r9> c kQǠ5Լ "_76 %IyO5I}`kn\3`+Wwrnb,{"T5>g48;Ymr6BOݏW-jj}NUi%/U ,A\>yV% "?-Y' _ͩ ƦrV9se-Lln}2>prq s :A򢚹 n^ZƏQ2Jmm*C=aq+y(Z“:9Cp3/#Ǖ[=,=۲* gl-Vxϛ6BF9Vt2嶄Y!;!LG=کM>5,Jk@KUz;ͫ;/vQ=99%_L„dlDB`n>>wtxun䚝@,oBsm)͢^[ ~I}(:1ԏ<4 ߌwk$,4k^%`'lpJ.qBEvy XS;Фw SW&>PՆ=*KIFZm>aMEh ds5@;`5x 9W/CQwxvl?nNr[VwgNfi[? ~~ ]:R|֐1>L}= ;(-[| ;BvG Y[%KKZC+9 ꢺMH .f^}H'iKꊺ T6^k&Fm h Z% .-A|j85x;MYq\%be`,v<-# &QrWV3[:~jdE7Ь1Kߩ(~T?CK'`*dWˮ]U6_jtt?Bj_< _%?w;ݛv-`fPofl>Z^a . . 4Epnyӹ`/2ʀ*^_ u&χ].Cw<T8SW%m?@ vS] ]Vq}2$I:\fw.L4M x]3X UCA@{M }U:32tx'h׵Ϩ@>eͲ8d}b$'B'@ldv@6mD}ZTԇ9fEpKVe,<͹m2t6 Jg"ZP}21Sݽ]|v5j {S18Ko !vw H-J*2nS\ݛn^'pBs{W?KG/L>z\n]ud,w2NYO!WldɽhQz}pg6j6q; 3 </X׊FM|YmIcZi`LW5}+1._/fP/í(~Buthq/npm0"2V#uuK}W+4;RvǩFf.T*B^<-s7TZh5P %T[ j>x ;Rt>dMzc!;b pChjZ;[u4Q!pv`hvFm}]xxXߟ1k/tCAڴհSM+(Nεsyބ'>$/\_!n{[I!ʿ] -_h Pyefcvl w>udn8uPʲj:. `Ucx%gҧ'Q4jWL(Tͯz> 8yjpǹ oW`7?>dq{mYU}]OΧBUV}#RfNo_KT ( 8g/p[o@t oyl~28_qjv[(Z05s6-lVw6@5q6-}ٗ O?VrRн`>l0_Fsj ]eh8sl`e1yMޑ7`f~P\x5asS(\06m` kڡCY5r z:*O'`]ZBGM>_[|NZ{8I'nl m.fl/[3=ZW^Uw?_tKO;W#(0u5u1u۷mB#ƫT{{9!T1/.z >*VoѸ#r98nNb?*!QìA WAu>\*- RiˢPZ<|;^e` ~E& :g9|.m/85FBasg(UaȰ- zZךج4[-X*@#KH{ F:+K/amVoABH&۪}{0 ^xy6֦.pkdaQEl9!dsGhi￷y`ȱ*R ]{*ہ4 pb|YU2"z+Ni ?[@938ΐf=J+Ag#Po)z}5_Z**2?,rZ:'-M)McHhW^R8"#3mByu Kл \ݨ ޳?,-ޱlUQm6,]n﹏+G+ |tcIPwv]:PRBHNLHt~a-؍j=ٽK;mNhpZ mnj xn WϹpW016\԰T}K㦏UU9LN=@:΂jrS=S]S]`1s*CeL`7wl1.ܻ`q(hnm_m P&qxPgw>?H4DJ+Bp+w+r]v}x 賧w!x~(SwppMw]]G1c{Xd@c^<ssOgjWi8ù 8kD)j]U^Xqkx㝣>}6Sw>9\e|p_}/r_68::4dBG \"[G eRWzzջ~xyԯ߯\T{Z2xelY ?w\ nqLܠW^WL:u.:\42r]tn>vfOӳ ly|flپyRbH,;6"]uyO! ҋ٥Gm@=e$a:I}DUUb1="MJ5?D Uh렊|[y Ҁ.^50 [b߁hvS@}CG O B<NWV#^-n;i\`GhXۢTi%8v0F!15]`w*&WA5P+8NPj #@ cZJxXw6IDATTu .߉`D4I_x8tv.5{iBUdƀY$ 㺨,5gã/e1bTgk]UX,ր4QR)\ڋOцa PH#PkiAv-E_ w"ր!J40wUj+`DT%\P(e2ĨYC֒AΪ3Y(eFQr*7l%[f`cn`4'gc/t&@7u07O4OY&+d :bxKyI^ьe,kA-QKǀT?|?=lj>RY$ &RN8NfWi2+h0x#:\ h,hvJВs-s=s#"KdvKk1.W R XCoFӀPi*Q%ojb=}wMy 1H 3fll&Q2j";`$IF8##x՘ja+-{ꮺ8*B.ZOW 饤IkC7AMe 0|APQzAi&)&W/,,X_o{";7BWgR(mxRBW&Pެݰu6T~'1R4!orP^I 0" $p A.HPU/lg#-jBqEy(^֦x/8ZXU3R4:ϯ|pB!gVS81r>v,qSSyJ*U &,,scZajZ}[ߴl)ͳ kC/Y/N/:u )11ŵ +7\ Ňe'qwICڪn#p%Ѫp`@*OAE/PUx#Umt՗KQ' \ іzEsU' *W?aLJ*U3P_zxeYeݺꞺZ]V܋ APjG vU~`E5#iD=vJ8*H-UC@;HRJ*<ݘ³*\24+W%ueiTRJ*URVۅ|9OkrQӚTPA9nKlE&C~qk~;Owa.AU {UTD _ $s9c@8 g[#o'qzlO>>[i.iktm2{a+lA<mЯsj@ ;a"[d {]证Fl5VFe abz Н*N~6SMO qB緀 AOП [)H" n z2Tʋ^nTd@I۩mQG-m{"J@FpFiD= r-G" _#*ZŨ'k_}?`3A]S-.EL.Crn == W'Un-tCx$ɖ w&J9d$AޒdWK8<-,x~,%z@//ߔ3A#7U.BE;AȘ ܄p'$@6e}[w+M"ND+oP"ZDk-"E$'A\Z]X cd l)@Œ/  @Nȗ$ZOOq{|hKA ryB 2nBSRϤO_J*=Q2Vd$UOYZ$O{@;юI_pm61075 rE;L`+O@B| *!ů5j5!QGk wUgAT} FZV@w{V{Y"Qq[gεC烰6p^PC bbkCNam>ytT?5N<߼ p=A}>Q .yNgz=*B@ѵ' >?J&^Aَme{j7oݘN <:)" ezBAD< y T{tV!W3@zH7og v~Z.aq΄H85Զ3ް[ǀy}bUE;qqD}>Ex(:"E<Э4]3$6o`֡ñ? 3ag|&mZzῄ?x\"r ndܼOn5+b*)v & h}^ZgxљpgȹS2HVX| 7W<v.6>SOMwmA'Ʌ@rsBw xZ+hj H)(~1/ⵄ pLwMq ^.̀k 6碻E%j!M"{a(?\q+i[2ROR^L" 딷(I#'A;e8Ns7^xUGH ,tϜI7dBH2.!  !Swuۙw- e//a!܄#Ww7<7\ <-ҡdgɡTȮ=6{%dffLMŐ6 u_"_t3C⧉7;Aڲe|-AކE#~^B}Sthkϴw:. 9]YqV¦ .fåKCc HDxDx,q(R&~$:wB1.M-_yk!p5k<#O {6DϏ2>x\x8xX w/!탴i'aϓ=sVWY~̺ {䞉{CԒ}ao= i{Ӣߝww@l-<}Aޔe'c # 5kipz@EXÊ8ȿ_^.ܚw{WȈQ]@:d\&?KIa‰5|a)p8ܡ!H9-]l x{7j6s aS© (QVTcq(CB`ѦMujƪ6dfiYF_A d"EmוWO/o^RM: u4[Ac`ҋ Ke7xwH*_W6^v'N!nQGJ|`(ŦH[5ZsX/,,?b!n\y=!vFu` 37Aȼw"])7 |Bx㟠n:xB̑1!oWފPxUw`:o/X@s:\|WνW 1j.'kB|pxz֐9i7 T+HLK{Wimfn̟-.k1Gչ׶\U8} 2gp T:̘K'2^FC]BK9vژ`-_rdɞ=l4 ? ?j0 H;9,yK7(zeQ10fvOBK),n?ug(>"`N-8|;(_b7kp[>3D'` Mx֯:>,uo^8|[hKmǁ6W- i{] ||mc6N#5n'a'=Fυ/7K[nEuw*|lܒB_>́xv.|z(q/Pz M6~xu(ޫ :8>Śpb1Mz?,ޑ>}Ԧ9/^w-?/ = WM#@}'pvȹy@3U <>s\X72 OX[f:jӊ_iR UB06ߛCwh|4!_#aܭ {3dfgd`#sx-^[32viݵ&1_s rgɝ=@GEK M917yKr dDKR6{ރ@-jQ оVjAk5P"_1x d% sJ>y4M7I l/>X84PqbOE4 !Eep<~LߛNh!根jN=DEbª嫍/ _̬9k-O<`b\2{65s.t3pBg3/зAUCՋ`6N[@5ך(YVf;+ƢhZ;bMij_v=\1#Ь]jM\h6iROT& ,[[A^d]T {ʷoS*>O_4X@ŧn&Kp3ѥ[^uz]N.54oԿLhu6+K[L_V@۪!I!!Q-'sǂUup ptqsy@AVA:(T([+. 3s8G ^5/)a))g~`$u#ӥ2iNs5PuE]6m.YI O=I,, pKmԛjzlBX/ q7#~W|(/)7vF]V55D)"e߈|Z]u\T'@c6sdF_@nqȚj:.3A9d:D81i;WFA-W!ygEg_vʽuUƨ6Ѷ c\g[;IXp0@H^r=e&މ~'}c>Tl1C|fm53}$@ 1ֽVK*ePz 7.|?,xa86P^UAـ/FBY)H#o ءqU~b!pDrt N?️s[ni"8ι/@`@ooe4SH^Wo/v?M#9 1_Dg vCm6s ڃkaצ;@c^w-dcENj_g$ b($/z_E$BEdElE2|4=OyA9[жicR7ۥKS0GFU;V^ è3N':yhZQg? 3;\8Wrd^}Zo@Л@:NEbАAvD-Q3D!ŦT-0xO' `oZ glZ)TO?6..5/{E:pXPp\r}ϵ&\/+J.]AAj}`j-"nO~x36LL֦qsGmp-YOk~BC`?޾#ȖlYOK&鸓=08ljwKk[ Vm[W/> ކ6}%pTrs %Kn0Amdbu7 Gb x-5J}%JΖ$CPZ`@6[gX_&D^U%R@H Ųrwuy^:jelX1VH#a`-Z֞: Xco  * KvB{ڶQнW+?[m#[SG>ΨsT0uK^ʠ.3:fi^m?U3pp_|&_> hkؿM-'M@^;^ݼ8J- YdFu]V/|&E=ӇBK|a1#A@ffpz88&hVִ;e= QQQ!`9re+:5۸mu+~eZVoI z tiGe NDIhs cɵ_5i>U|ё[pWɢ;ҵ[F64WW? q泛s2,2[9/}F@0!`+X6[Yò$T^ 3?㬟 IPkbᠤҔ53{Ӏ=rħG}(Ţ岺IgK1_]8P1u xPZԄ׈W#k9˰6,Z%5ۃdloJ:*BTb0ʌR 5:+YJHaeX6 ?@ QƧgj0댯AⲸ(BdL5|\Piu*Xr|Ii`ko~mK<ˆko%A(v`\6xh )& 6<&X+֊56Mmsu.v,2T aA4E98tyyy)(夜Y3ElA;1dQ`EF)wB9B 3Id0:ZmbsES)t; dcP6WW6l hkS@G"ku?{߾"= '_ϲ/}@~.%45UM c䳬'֏YԃKxr>*8)黿b07hT~NvQr\lc7xb ʓ'`2A6Me0:F=dw #)00& PUq]\W@66p[5NW iRߤ)pGAKS0s/(ٮfY\qc7(4ۃrO ᠾP"~^u{Umɹ))7+x7l].o^ A0c,`6Qr{`Qbʴv\m@e <dOKC,@ɤ%_Z_^ǃzԣJ*UJ# d`Ȓ)`5F` -5[ߴh)ZB+vAچiwTh]kJES+F=(S\8ܾrP|(oYvVfiOQ*I%/8w| JUk-m=]U[/ꖺ VYpPyRJ*Sw"Zb#WX j>_T!*6z y?5@]S7_~2y(_T߼n c/,WjnLJ*U_ +@-VGϻ1tUx, *UTRJUwefx(2xZ$RJx_'2 \RRJ*U7쎩,2t ?ʝ@&<Ͻ1餓4ˑ U _T7,^Q(qǸݬ 1SQRIMq-{n\J p`膅э l}ޭRFI(wduY]O ,@>ЯY:jmV lqQ\gAPWDm6pU=Hz=^֧Y(eOo4bY+׊A{nh+ڊ@9TkRXa ZקSI ~? 2y@{AJ_'L2 zu ɭr3eYX*ȏ@OX{_{D=QO׬Ħ>YӳemM|l*pTMUKF3 dLyVX3+\peІa'qE9JӪ/e~ ?2˝\C\v҆kI3׀X(eŇ@6iL~FP.E. H" i" (egXFp KSw[q ?>XY W8(-byPUDH,(UwJY!ķjNo7CsӪh[MA zS^D ˭{}E_ч_5{VaDñ,g(0PBo+a4;%@I%L: ~YMg !BC]ކ= w⼋?_ U#'EuNQwwDZ}ASU?r J:7?"E@xI*W\;{`4@=Dw ** G9+r \zͫGJH'qyA;B!뛬MYAJ)3e¥ބ$$Jޝ|.X2>hOOOt/?mbNf Z֢qQ\=H{vX; -1Ẃ}O$>h'{\q- l-zt,Τ=H i fWm%Q_'Ɖq ։ub-hZm'=\0gO^NZKh'+kˮԻ툶_ t'AvV;r&MiFSd]Y *)ץtܴfb嫀+"O7-]-[ {gj91i1~@\oF5g){-b~ܱ̉yŦ'2n+PtLI``/n.t16sdR T4QwgU_V]\)WRK@ V$@P &gg},߶ZMm6 ={A|)0?QJcM` P'8LO^J~C Cŗ 67q-H*g|5&ANá!on]v; 慘cCv9@ eҵw %'%'h6ZO5Y#r>uuq~|cwCn 2[f~}ʶhz0F}tTw:ku4U/] 쾒sfYa !kB>d:vsPЪ p.iN`VMfEpyЕ-W|a 7&.zP{VX|$>ЀO:5N5P5€yA#-8$n軰}q;dn!>,>(K谱Ü53?/[[Xׂ>(8 ~cP}ؓ۞`P pmUk`䤑F=WbN6ez؍}MQمfg :g[;A'aiA- < ev^9.rYTXL_r~O20E,ס~Zt4)$y8nW!miw[`w} ̛եPSA}>d1N\v0H f/0/o":uuA~8Ǡ};TpaA96x20jUoPG U˖߂~pLYoNw݉bKk}9Լޟ> ʹڅjUt myĩ#}*:q+ :^p3+%4dkz1/퀷m7 Ɲ,:!5OtάU؏3~q o웽?=kc*ϷZco*Ҳe@>*Ul1bcÚkju9ΝP:$`S#ӠyÞ{[mی{)ܛ jI}M5 dp'7Bdnڒ O(_C ys7=%èFq.c1cbGu+j>lYyya t hn9yyy@Umxq:cC!a#ޒWfzڵ%Dό14kV*@l%_Ċޢld#ILJ3z>ueqpq !IVZyC^ћ:4ph@fn̓oz~sx<\r].Qr 9DZDF.neKoô6Smm >cG%+bcߊ}|s4l(T\-1_t~fG~;OvN;iur<zXQn5pt8Up[aª]N;uhk퐶@լ,;^ߙs2ut23_S}i#d;g>G2zgC-YS֔^',8{zhh"ͮFB6[K2R*dʌl=z5 ҜaNk篽s;F-pGCR>ɮdܓO]޲Gֆڿ` @V.iT%N~xC& C ܴ{6qk[/_@ii1P[QmAt7K\7߫UIȆoސNBll-]Vm{#ըnqݵ 6N]"A. P5ѳy~ obXފ=DVDMjP$D `a-u恸y{WA@<QBx@і6PWT90gs&Ku^ 0~ #:o?:r. Gi/ 4ȀW_ycҗsZZ5ޫѬFۙpw{O5O!Hޙ'p ¥ >t=,9yiCk078 zvCS`9O*gq8fy\@8%pG]h+h\E506u:mSBܺ<N;qv (CPPmÐ12cfZ)Miti3k }1c<@}h 6( S,Vo)M/4n~,D0rLPN@N)dť@;ޥ{)p湪ee8Lc ?M{Z/4i#v;@ypy3,0~ݸ^l}ӾpVhj Rٗ ux꺴;`lS! teo(8h|}' L7.Y6`R6=0>Q i@vc@Mq?EˠwKgtHŁ *P[ 6AupwTT_;/Rs-- ={@?TxW.4cL5 h;Ǯ?2 26ee_{jS7P;uxd}@rSUTB2)`9%~1\Mv&,X "]tP }GMH[v0m7r;=n05w1*|4B\ &&,I8 [nQ[µF:_jVU(Uvwt Mj6~h x/76 ^={xn"C'iniO{0^1^US{@/xmx_g5r|4v!srˋ~k_m /Ù~~}AXӯ9qFLh;WwRWO OO;}5z*6^\Ϻnw]\_pw,Կ-.Dߞ'VW/NS[͆o#b =i7fK!4#pvۧl6̆l ԛv{>m@j+W]} @5"MK Z'kcc7l'ǞJy,wZn]=[v/rYI7)lԲb+v]ͽ}kr/n \q nP޲sy %h^')?_T.iZ%Oz )'bAZo@^TYM>U $ lo_JpL* U^!yEV6XRj kf>f W@Y4DqE^E 66i:iZ jlc9rK@}bf]}"dܩF% yM`fj4;P':euLӨ}0r(55B ,Uy-}*U{% p_6 H&ZMWeesh93F5:s^?B6?g:vx:d ܇F>{6 #Ba n!*dn`doaۍH5Q{j}}{VZMiu  {MV=pj?z}Tu@ͤ\.]| W]u5 W"ub-;Lk[}R_ v|&hv[PU' uLV-c**c0A BP+G<" :wZکDeΖB;V*USY47SϮVȬnH.HN={!x7-nչaa5†B7nkzouo peU+ݡUl-oߟ|c STy-d%?jީy&Ϙ7=v=Cǩ SͷAMjvN!/Y,eiZ :ݖk5wGGwU]kc@ X bE *(m)ԡF)R(E { bePyγ>{]/ʕk{9Zs~QT(7h 6XTWu.ƔFGD}UVuz M 7ϫ0K7VKZh ^s&C9~*TO.88usnvxL} ٺ 뺆Oc. y7_|_o35uq0jҼ#eom^:>X\v9U@=Q՝}[KUT0$imA^z_(zFŲ 8o "m& Lt0abŻ/XttBGQJ `XA={T&'CEt G=M̤ت*(&hcA6YPEJeL_6걊r]JV@E5++gP"[d "Od; +IO 궺BXJI2QƂLۀ b?t E SeLqB@bR<1o 2\;~RkZOˠ檹=^{O ?Fѡb׿>@}>P d̐ɠ1hkp^eK+llqAg@~- AV!z5>zC.B/@elj.wOi8'Ή3 C`PO&m}7pk\yz-e/Iq\>'t. Z"^zo/37Anf^Yu^Qw]A\yor* j =B%0v_5QQMzJ jګVߠ DBж۞9W//Wr_[ zwP[fjڃh+[.+*(2+yTd !OAnz7v NםA^EY"TJW bcr."H})-S:pdTͨ ;xҠS`h t(WWtM;V2)X *U겺~a' oMnѳ406d7lY_ D{D%sUsm 1@5z6@#?']}\MޫS T~[YYYYYY+5'J- p|i3e5EG7-z^`aoq 7m 0RR>IƱOý[!ncsy uIË"euT Pػn3/W5֗蠭85^M{{ G| ꫂ^ttGqu !ເIcT&l5W}cOm=O|rv?fZǀꚺ8}u N&jF5:+?+* RA{]:sش T&X@E p'&!#XB,E@|4B45ᵲz0Im!*;bČPU%jjj:TZ$@'[GJIeZ+< o Jya "r\,r%ƗU)_ve;.Y:R1u,F<mkw3 mvy2wٿ׫] uw kinsZH5kt1VQĒd/@__tpWxuo?] % @RUl`#mm?S m - @SϖME$XR*iXq_C-C /hZuSxSb--R{Bˁ˄ ޢ >mo ZVwnv؁"~INk=_/B0604 2@:K'V {)G`ci Z+D- ׵¾%.CRtRiRs_% ŧ5*\g$X\2Ap G(AIY5eݟlY.eCݹw@oQx>pq_%Du~hh"Z'Z E/1Q8Jɿ+*BH1 Dx$/1@G8,=jɟ3at1X  8 OsY7A`k>yX(u`9hg9h/q f *= LXJx-(۲u ˽r7?J7AH !{Aς Dy-!!CAƾAɪ2>Xyq< =L}x\,߃]ۢm^k h.-v׎hA^W|X ~͆uk;Q^OԛQ@?o?^?_|.>'@eX#\܆Ad Z!ն-ܞq>/|g#h Aۮm66YY_֓u hS3xC ꉺ|w 0kc&)6DeM93@'!_]c5ld5,Lښ!Я/Q/ RSڕ^hY(vsfZK V8wѾe1TSn_v%otZ1*f4 &֦;fw?!Օr+47-VFnF;GmAtIr5E_VVVL|/@kwY=H2}cz2+~xx|>%P)*I%@~fmt x e@R~C7WCkym6_g]Rહ]!!!sC{<>ϥVDŽ_$F><ʉ`w֞{fǴjI0:tԼQy`w]c0v32΄+*:_,/~Kp>|nd!l{vm ]?ҩNX%n7ٝX`'ɉ#?v%偅oSbBwn?tiHC ~J9HB7쟻?l>1،KAW:5^۰lE`KmeX e'K H\>aD(XQXo/^ {m ͖6ٗxx1N㿍6o,}jc!`,5+A|RŃW.^UAC} j~^ՉNj5/_qva,Zٱ9W9F՚f6jBJڔu4*o^0,5|oX99s[{89;Bȵ Rv$NɃ߁Q5|7W1M?xu\?K.78/&;> j)X5{L>o~ْd &D  @!Yz>~L=؉(Q Rt'`&3nh1ELS@. Bg{v=Dݍ5 :UfQsWOOK!GgCNM;6ݟޠLb/ 2rԕ@*<@lIppKq 摈# >Np<r r %?h >g ptR,8^횞D@[_X8}o Q@7nxdܩ&h`10#%ȷ=ZwII/SΦhF<=Lݼ}t w4 QΘ{Om>h2,6,++=MLuh ,=2`F=utñ(HqM9eĮ{!~Z'$8}41ޓ''%6g_>| OOO9^r =0B*b PU; b_[qɂ6WV\;<>ꞫTTmeWPTj 8(6VxPZ1ILx+W5lXBC.Uvj[ o'uzO_|1 %/ L<2q;Xs}΍X ⿉wzK֣GgC[-c:@py!YӤ\fj#XSNMzKi:9.^8/|m}iM܂=':i%FPhF$> >:/.6N幕sBԹ{Y!Zhxh;Xm_5n`x0|1p9 :{B~ *5 qm3+,~t+λиIh͏>C)a┋M.EwXx&^ze)O <r?߻aNAogyoڠHH*;+T;Td[9$ ?AUej pQV~lvv;zSVUa >htmM{?9,Jd>t;e@I q]cp5A. wf\Y tm ^YPSj.zU ؕDB9j0mCVPfO': p Ę)Y:>AAVg2q]̓@\8&D̟(yDa٨NRsms=sSh٧Tm꺩:1t=un3굱5!HaAf-~2-RNݝrj5'] ",~g!pkʭ?>yq87O!akD iAn[=h1`Zk`6=lٴcvB@*.Rn1[42K Zo#.=C=csݚ55rivںak+8hї|Oيkpź_ ΀%Òa΂6cF4!H)fAKCA"uinp`bh> 3I={$Mb$D$ʏiq?/c-[ yuPƺƦ.`;f8M}d{d` 229Qv]]8d۔e5`vpq2l h\tK࿟&Ƃ(׶h 4/LDg}SLYiq>БtY$E6u짐?WJb;`FUsM68AGtK*JlԷڎe7wCCj%åW.B{pjr%ds9' }k뮅]z$LND{'0W&~"Nq콒uMl5"(LR/)߈`|4Ռo=J_S*gUjּ[& 9c#D8z]s7 z^GY<@]sս%" ( Աsl M|UϨ~ T&M&{l"9.5nw<jF!n}4lMf4%TEA劺qjNwk`m@}ܣn,zEȼml>j"![ ġb1Qâ^ f\sW2_j *w[֞T1)@r3/갲K8uP 7)KA0 DS/ܧx|##gB)- Jyt+>4%O\wN0hu⛓gjP;v!4~@"ʾ}z'ի(?Y~쾱mjD !nӡJ*-3b"1|fxD.89\:4u GVϯ?Q @C.>ϻ_cr"U@I%o^) c TsSL=F34ӗtDQ/zVNҽ{>ޡm,ȼgN>}thظs?0 sh&\wK7|@PϪ׋^CՆj׫v Z$?n.F躸Mp3帅e?Θ@qÒ~SmpqVs)bĖľw 76\`0Եec=^}P덭?j?jYC 5G}Gtkx׷Nm60:ءS2X'Z ~2^IA셧c+A.= ?N nx0=H][@#՗U]7u=͌ݡ E'[eS'%ۍzy%P?6_FM4Գ )oH.]Ӷ#=whȵQmlޣB=͈鷖BlGIXw%Q/cT:^ikom:LEǦRhA0El}k_G(r`*tqwꉺo3^ {P%D#eP~Kk0%r\ بyj>`ÀQ5{N ;:hMTe D$@'kLzP.6 )b֩] 6Uc\~KP}@,6Nz@$*Ai|8'``P (W%@K,BY?J)q Й)45MMU7 *A%T\CM%VV WCOzkT,@% lqJGAS 3d|/}AHOAޔe:%b*O嫂?;rJ&@f9Tvxуo(@4 @D"]leTfe}>F)yBJVm@z0S4Ї EQ D.sPUeuY]'ʠl-8j"BD ˳4_zS lcA;Pkj-3ьdU'\@ĉX HbP GS?b).tP/aj.r\+W'eg@*?Up?oe9 <#$PT}x jzOj^/sE&s}^]:^wQ} q]\ed$_ >uu[25upsO!ij/`@S_mlz%.(iB._s̀H\uX0]3ǁg`1k9RJmpllزa_kR4ߴ8'Q@EK`x&BEտ8!PDq(XZ[^Z\KA5SU߈xY hS)*%t7m'پ? \X>N_@DP; O %9K_#Iu T@@a鍦{3?qPـY^tVVVVVVVJꤺ L0@EԧVvl، ΁5\^UV&RsqYǀ_\#^thVVVVVVV/ .:./:NjGJWυo_5bFr8, oyu 9X Nb@QE)}A ' C1cS: @4 ECpn/:*"H4A|p-_0k?DtD0dSn0}lYa~Dk/(%cAq+cӲŐtkk Ib) H)ĀpvdsU䂰À <L\(e3vw4oF˞2 p@ dW[z 2]z#yi%` L07!7+E[ R @Dw(H__G-6$mEu`PtYY(=o0G+Η8/"Ǫk?D|).'@5^VdϫpK\I'k/v]] D ܅pd1YLm6K{cqL DGQtm6HZ3DDziyJmėKrgy _C`ЮkW '"&l"E$vS]Sz/ɺ%k2I&D?鷸- ,XBR/U}Q;222 R秮J j3Gi<&1D'ք_Dڋe"}a3A琐p{w_(įċޠJX)R@ Ib8%W )|(,r7E{2`‚g8\yCɩ  D%)ڋ  'Dv-1)ڒ񠅉Eh+ gN/K?e犌 q̧ 91Ub&>+f6e\D%6\1S."E/'Rk01ˊVbÓkY$w@At4S2N9dsEVf'<~C(?i`$7pSЎѠ}n,$/ߛ [V1 'Ekʇpsy^HrȾ* y /Z۪~L-{?CҮE@DDl2K:/@ƗNAqѾt(5OhTyYʉ t94Z Q&W DQDF\&Ɓ r8" 9( yAGU ob9 ?(Yz#q ky!bk0mg9"ȅ31hZd$h-ZVůmӶh?CdAdC^\^n;ȟ:! T; Éov^턭W ZDvDm(YNї b+qa[r ̠^oCN)Sx[e>e5z@d~d݇!}yU*1ALl1'bbcZCDOS,_“O=c:>\ h*b)RVv-]ZY^"D#lѹ 3$V*. C]+~vդ99o~<[ʳ P 5_. OHgAeˎ ,ERdw]vm\E+fqF෻Cve>ksA6 e=DSW@bYLZ ,~2X6M@D 'DGE43Ѓ4s%-0X~!ODSpv K _xjMxY!b_$M-7bXvC 쨔㐻ias߈=(,(w+ 9#RAA2A?F) cr@ݼiC|y!4y$-?qRzOi=$%-ToBƉe@料$De:w] nKg@Q-a˭г'!yiT̠fiW(*R OrBqc SJ(?g1',Pcy.`f9k>9% ³ǀms;Dh·5z!u蓪$B8pssev]eq$v[ x 4~Xn& Жw,罕 %[!gt邂@Xms#t "Ϧ? @19{S#蟪MzHȻ>deLqSOށ3AOg}!ӯ887.H(nm * κ oS;q KҫiG-Y,@ -̕Yiᐼs*0 _UÕU%}?*;]dcy)]L{ 2)vLu(8Xַ:XF?`,PK)8ZQ FۄiELS^MK| >CI@V[5 )[!FZp,&ql(Sv!0&B[ zV1PvBY$d̉e+Ň#]67?q6Jr@^J3DS!c`ƌ oo[ZU^SC9;Cv s,=Əq@i-nwO˟;qΖ|N?isė$8' -P |nא!g? $YX.%pmsp=K'/O^G9O/߅IqIagÝ_ԀSkOY/ =r΄w=~ lrCf(,.,/kxm;}9`i%uRx%.7w΅\v3@^&j Ǔo%gC\BK|k0777,Nq.gN;>J.ăX&BV`VǬѹGɏzBΤs~iuH"< ~2>(Xr$!qHX"@Ƀ%&( /-dU'~;?0m;={MJ淀 Ab_/_rrs޽v.O<֦}4C@-[h5a8gr*A{G#}[_UIdSC#NpE`n8es Z֭VEy 3e?ý)m3xnWi+(ij =&֍j⢏lY񽴁o<|_WJ~p s%$lϻ6jxj~pcVK q>{3Y}pZPpe[`)6E]a{Ԛ.m=-".@÷$ּqAblPc,CkZC[$5o[ֳ(x2n"hbd|V >n5_?_s1 TtVy.C}Xv-p5,nSGAR=jhYN<`rfMx╭"kuׂ@woBy/wRǹnMלg%Huf @|aD yiPzfP@%j X(kM>)(}ݴ<^$y<ٕA_vUp{ʳ: rs{n{ mHPRf/435, ZU"P߼;?#{ʮ7"n^YXlǧH3SXOm}`ii 5'8-c[nQ؛(p^@I~双Y+ͪZ+?:uh8pQGFC 23e>HsBx9uҗz_'_'Ч7^N XBB)pɹ7zLXo퍵Bv~?5qyC?)6[2 #!#&>;2L({|-jxnTrtkW)<:pФ(IP/F1c(h6Z \\U ,b! ٽ`P[nWnnrg*8]n^?? 6\`t>KiŦ_RJwy=04`m6A%ɓ1PXhta3cOa{}<l~jߤ_~#v`wݿ^6e z?Ox,"AK$8]p`*ttջ8tnC 累I<G'Uޞlف"Y( o~߇3KBm8d7tiӼ$nϦ=w 0cǸw ɩRS:Z` _,P:3촙mS8r{?em>khϫ`АޅS_q ؿkh}_Jϖ8E`;quX]R",֑X-l@kdG3?/?!HWQU SCNφm2zdvHPCZWyF[6쯂Fx20;2) s3ۗπ\"dpd{a،Na\0Hmڹ˹v{,g =Fӎ6Tgj翟W -Zp#@/'͈:d\]#t_e,=z,xqiq nUhGs/`,(HV.KbxUu.AVN[4.dJ?jAٽxV~CaKsZU@Z\e̋ @ȢQ@a%KFn[5UR f/ { f-WN0r_=vA/<* bt\/4PbZi\frH[zw:-15d)%omqCw-l3ضB2QSjtd iN1xtn TR,'@\eX"q`+g{ QUUI<.~g%z5Qdjl2$g ?A*_1 xdH*c@GBGM߃o]5hOsu/tm.?S 9g;vH005M8Hpj3Ph]q 8byK[2R|y]^!g]"a uCK@*NJQtӨpֱԵ0Zz=5P/rQP=Yȧ`o2 MޭE J£ƸV~AW]sg/h}ḱ'g;&ՃMٗ 4z eVà޽OWB[x}- U>qsʖVvli 2ȫ=\޵Z00UENƾдql\j`a/ٿz^8M}#e2COz2e]c )os pkppHYW_k%s#zZmWc=Od <6vݡbyD%%hjH 5{U;w]!죔yJ`sQ6!vg p[N&8k>& W=|i@UuBz1&f-JCxyyʾ. ū^_ʂ @o'p.Krˍ?Bwk 9%φSrWje`&4<];y8;]vHl&B}Nx5$>J&c ՊkSbh"zD γ:Pm 8t ;nu p-ˇZzROIuȑ<O&Y sGfyfs87hjYˮ ^$f[II5ZBwvku=z/Eֿ?u@RGRH#^_@ƒ̓;[n _Ġaן!_{O!m_rsgoRq'[E̐j X]'4wovi0Tʩtҏpd. &9!,ZX=Hq9Wx%uE]J* =nt{9T TR+ꪺ\皸 UJc,a!CB{ڃHgp3+ļde/p߭n2oa!`FLFܮ{ͭY`N6oKgp{ƭA֖84īWu8tppIgKk[놋Up/9=ps?ލ |j(_yIuS٭7UW+p$ceRkn5~ :RdqǪ`m0xG߫qbX}iФUMj>rNw;+W,aUvV2["Kr| jX7;| &~:>g w<@';͠Zjͻ7bN[)6G[!\[K__KIP@-[u~7!⦹6j\_i*PoNkK U~Y'&zC TmnگzV7ȼP% :/ۓx4uN65CDo@wUy 3FbȀ73jCB5}Iyv_B7IBAϲMCF/CUHvsNoԴmv*9“C4jcz6Pt֫ea'O;B5l ˒PDZRM%&ɈBn[+Bq14; _="}jׯ%N|]A|븃}= dx/shsݿ U:j?{Cm Oܱdxص*\VkÉg/h<'xX>! 8ؾpjV5or!xx}VcnP`heCাp+YȮ*7wzZN`Ls2,Օ>~]]^;bzA+W?^;]/bH]UMuQy[3``c+4^q sOCf3o?w?|ݳ3Z/k: 2fKU[7 :U [?<-jk84x u(/V@wtO!fй ?sЭCylkMsKwpITH_ȃ2@3Nw8J AD]n oVS`vhs}ں=Vkz505L} NFNۡuQ~RkU"aj8-toqIptDZ ǾC% :7\ho&߁;..O '=wNQ%hlys歯ӓN:5k.t|cqFͶ*(?hu'?;9jap>>Z, PB %}GO˃>p|!'Bȉ W^NBSM5q&mj)| ݢ[ZR,_AM5=SKЦW{w2դsPGFnaaa-"@u?4Ft OM2k5kEhլV|=i`{RyPXuvPà鎦M!yiʂ[pnkf״j}[Gy? *ϏAN v.F0(`A?џŴ`.)LIP{ M% b[:BWq@]CcPO}uEWEDD pGQl#P{ ` "bWFuQ) э @uܨRITg'pfh@ej'PnS՝rY|3x T.3UC[g#㳐{hqΊVM|LmYbĀh@j%VaAdzfi*U#^ly |#x.j*&-J=hK@Wj JcAmWinQUv)  /BEQ0P=XǛ yO%@Q" F qW-~}RAf(~P P E VSEszXtPZxCA sx@<|%>a fyU|j2{" t^L)F`i]prchh}XIM q D _qCmhA%3]WJ#hCuH O9ցLk1c&*_PUzSL18 8%N9ur\ Cy*/`{b%U2 b6w+\_߁^..sA*h1JUWU@{"l2*2Uspxуonv^@,ŷT1' ߐTP 5rԯj AUTOS$&1ԧ3!uH(y@5RA 1j 8qW ՗ *I%>izZV;⦸!XOTUU *@]Tīb4#?j:*O>`@ oQWQYef⣋6@Y32Wθ v{4559df k`b'>UDeg8*à*DwUtV EH@/@PctM e%C{_{t:5OoT0PknKP߭eְ&V K_B7ƱeAWN%ڪURT \Ւb)Īyii8}[Pv #zOBj([\8{p귬T$Xa|[ z[cCq@8xզrduK_ "?2o%D $x6^2yGk]`_5l.AID{YOS7UCO-@kuJ*yW}jꬶX]+++3\SOe`㮍v;D̟Z_/UT4! @odig1L K#P-> d7hOg*/5?V&5Ec**VK?RԠ5 .r`HkKii> ?X蟁i1z u %/MXd74},S--=hKy_7.sTtt&v-FrTtO`b r8o^Z{ODWeq dsLRH( D (uUEKf*C !tғ@s:ǫIEkrrքQ)׳TV=@ĄD5~Igfut1]ߪ. E_dVSzHwi> Έ3W >[V- mw?6;ObhZ_ɯB.EP1b1T C@6ӟϔ,]/"XLi2=*sϫ"șkM&ZC3L6'"+61 <@\yi ]tcD(M^&\)We |2_l}92s(\3yǴkbbtńl*Fy*) HPp 2J\'wȚ6D (KPN2eض΁@Qe[(@IbpNQ-b+3%vJr|({@^o3W,bhZ"/8)&&bY(s+3Ym3ؼс@|]Nt1ML0Q `e(Oc@ cœ)1FANSD TɆ esd| @Ň1\6A C]y G#1D !kӴ)xl9)ȓyy-@~ ߗ| e(ȪO>VRJ)aFQyyy!SePrjC7 q?DA 9qu>*!*2ee8+ փ1b̉9 iӮ O̍M} tBLb0>3K@3pDFJo=$.gϪ%L%g @Q(Rkj2B33LdYO6'㏦M ߓs={11D~;g(1ؖx\V_2Ò%R7!oZާ +* .jLH:3{PQuGןqbȳ<} "@wLUy 7kH#tSe{ '= _ +C2cgf$}4o?:iD+#2_+|ALX<,y8r[}r\6-l_o<9> ; ݆_@eqI\dN:Б4$քW {)SN1} ly2cEbNSV}&4K ДQx=siE'chV2t6J{ fqg=Tb8`@)Q"{X(܄:ߕѱ FLYT' /z HAT5E@t(U 5c8`P5TAހNOz0? ;A.[P5`+򊑳v &Ib"WBFV9 ߎώo AִvABQ ܸx/Bl$lL |qqT+ z^Oo]x )xM}m^P_P -'|9rA,{n 02(*+l;H9p wpΡB061M]l[4ivvŹT|i-[OB9Wdɖ‘G\‘GGj}&.VC.' 1!Q%vi#5|OblEcw]vv̀qDYlA#pH1TN ʷ\w0Poxapo4]HG¡Z>߁o8yq82.官]!7,a .~ypiƥmжmǟ>U :iii<[г|H=zӍs E%jZ#u$8iqXOX΃977ͱKƹ$kKPra߭|&ϫ0`J܂222̋_ګ?-a(&(lufH0aqR^,$X[:=a~bDĚBJ1?@q○ZAҬYkNEt/* ebx D[炰 PBUep*P"*8[Gz=&S~وTh#<2<dPPzppF@<&OHzP?b'gMΧr@4dt hcje~+}:{+(kd<Rx;-®aw?Aߠ*CQ5lK :9f~{֧Zzr嬆Ug"ȍxK~Dh/ `ģ ##A_/ї%tx1;0I`ݐuGAv!߃`efo8t=lyԖ6&eSJ aiԽ{ >_X1?7Y!lf0AeOg)ynzOgsE`XBu|˿. !CƄ M6޴.c  ""G@zݙv3ۙ`˹1KpURķ޿a6Do yM7;v޹=[m'wKp7:,=kv6N9Z4h_8OF._Zp) yzCVU=_~>}?oaF.ĉF<=z& o%X%KW~m-Klxy`b^g9"#ƒG\??>TSC=*ܲ^hõBpFgW^ ?gI7qO7~I["LoǬ~ts_C߅2g<[LoOWis0>6| !zU* DZHLH+ F$;[SnG _K8dbN><`sue (.AQ.XN/zwl6.\j6GzUTz냸7ąXaAy}d:L^ |ﴸ3Ay*/A@oK-xC9諨 DŽwuö(|w hUS^@g^ɳ}2ԿYkθU6@r;|O3׼ڃPz-EŒOg<^ q>;UUjw}7tAh2IGb3 1)1+%HV+s|xlokaf9MfiC =d%Fm58}4rJ+;@+5y6o ԙR{p| xȱ9\):j$8k8s+_>P XJ G 7A± _3j \Lu.S\:-FФe7Bʲ9 p]7AuVi>` )}癠UҊ4;(mZ־%NNc,``:&0~΍ǭq@mElDZ-irĂekMf4V]z+^ 6@:I`xtvp)S!ԯbaaЖAm4i&? :Y:^[jO 6l:4_|7~qc.\gpe+s@=HulBv`v;evvxcSlٸt "#E&M w[021-.̿`9s1Fx/\oLb-hAHhX&q>g 6w456ˁ>˽g`7-]ubOq?0E<~@z&vz[ WwW PjCEG;zjT-[|5bhZѵvJ?,Dw*T (7vzuSSUv*wA䘇'Yճ: r}FjZPz!ijg$8O?\ `NqNNKcB=df3 <8rrpʃAVBƧb+ФfgH^);w\#hr[Q dY φ˾M/tգgԯ_.]z=X5uC&yCf3aa`2k`o"A̚p[ՙW]w; ol|u.6w^|Xb}{ eK[߇'==D1T*SF0xz܃یoaUʔ q*gU?~:D,0OW6P_Z5~[ Oގ_ CU[is2?8,eL h0~Vs,;,$Lٟe-V6pOSN~Zo~89D8\e.w{7~-9 -?8|:TX*67(`-ȎѫlT40TyxB>tzueNt஺i? 媖s.w:z5,* 0?kx78s2u˷pő)GMa&Cŀ3!Z@7K (U [lzse@kaB{4$\L0&tٷZ׹n_o&O +<-?|pehM" bRf,K9 '2/e=15Vj]w tSɔLIr!  hA f0z> G[Q}AB T1U\)"[y+_IDžO@J)W=8y A W#hPJҀ.t3N~W799+gϘ <5QMR@V ƆpQcs rt ?ѯ6-޵>` ߧ ʷ-+UR["@IU 5P&\A$NbAy+ ҁ5LrUna~ el' y2/8!<\9m[h DU MUeFnNj%/Z,UTZg`kg})[,$& V'[5(4.+Hx_]^W@[jXd\x+9l jzԼ@"1jW :μ"_d~+y@0xO |$ʇ9?6?u8t~=(KˀJFKD9g*(y,~3̧D"_P,e jX=6њ[$6J_l+NI,+x Tm?۠l13з iTT'^vN*G*3xOлx' Wks%TVRJJA ւ@ 樹j.x ,^|YeNR_{O%_`fn~C z1}^,ZB, }m@%rbR(t() uW Q{`uDqRZS@᭟9"x bR  jZVJUi*pWX\X=ؓK7&"`-;^[{Jk%AMPπʫE푀qYd r[n !q<QuTx\RF[Mۻ֩pGƃ2)g}:FjT[La+dtɘ!awc܋/d|y }V.s 奜gUJK}@Fnizag4 ~g]'mpڗ,spp_$y& Vpy^ܖ[].i@~d.Usa% VkO1hZq:)ovB;q7qඊ 4-~~)=qcRA¥h_hAԆRX#h% i{];r%ܕh.k  [OvYrC˵'Xk/~?DEnN=}+dg ZVgD ߀Gzݑ*RE*~M_i CKbpbCPT_$L$ :Y+k f|kGkKU YQY2?<0s2hAk`^k^kyuf N,FȾ?n͆7S|0:Iğ{ȷ6w- ZwZ)8p%7X]-u:{`b1v6CTdԳFT'C҇=>{Z녟T?glu LIS04546ԃۓn mj@cofʃS{Ne2/-Nk ZV^+ 2[fw@ y*T<偷d ENyri΋+lEigpt߹;}bZt;^sGLQЧۧٿA3##zXrA<]< W=),T 'mO:@#- $ln \ud@@>3Az/JhSF ࢹegH49ﻧmU^+@uS=T/o_JI<sjdܔ^/VXS!`iXnZ-wC^kmRz?eW1 ;kwC/C;CJ*_ 4~y^g{&4u.W\;NfLڹրԕh whݡTru@I= %Za݄u7K'k `MkAfNPbRQ[BIwvm]sm|>w}Bߡ}G G> O./+TLzF?*wե2Au(,vkhOݞD]㰶nuJ63DS^hZ+;6voҲxvI% 0+Xk}7$T]ūaQs@nֺubpc l9g;π!Cm{/)I)_@7:i x{=zo(0)SKޱPd}uEIߢo{٣祅hE"`ɃsL"0"}G4ʩa/xbj`mFnN;^^+2VއqM}w*n:;h)D^.^vjv+ܼ6.k*[+kmׁD3H"$h`5xdO6 .L.{; Y .ց*UrD~EPp+egC ַª5 *:qFtqgx[eÒ4g!y^FTqixX7ceC^[ w}{gبo4mZVuzt*;Cﴙ y#,L0lptsN{l9sSRO Liw͍[@Sfyyl#`>:ؿ%2=Ph.UWBGSGSBUqނ]wvuΪDmJ7ި:6rljjKZ,mu:\}k q#ۛo€cGJiq8\xž Oʗ)sNڛj{C*΅'Ϝzv-yYfKs TΗ=ajA-___o ]^˽x~8)r꾦GYPm{o]vqqr f %?8]܏5N@^s3gn<jA&۩Z3JV+z=^j1ՀI(?CGVn{ 7b@6?L~l"P+WWPۼB<{Ȼi+N2k@9Á:&ty ~n^}YP<{iuԙm@IΫ{'*o e^Sz"1b;wo;PlUA 2 zbc[ W']=z2(Uf +=ˡnI'f}.>uiDuL7L.ƣe8H﫳NwbvŜ b{?XZuVeڟ|]H9\\#nϽ*x<n_Ӽppp y*gB > l qPru K=6$ž}ކ3p2dSKm}k=-["@Ni'cGu`ٹeU:z=I-R;jE :>OO kk +xni}~rY{~^eqٙ{oA^^5 !Y~>='\^y9k=dzQ+ij4e( N`2\Lw A@k{]Yw}w4հZ`^|;ch {WՏ t%uXDbloz;l_0U15"o4 H; lGcv>huZ($=K֒kB\G< ;40Ao|a:~|m5_xc h-[oQ,-,;`NZBJϗl«Ukp }oLTexmW++ P`CvǠTp tXrࣧφfv08 ɿ(~ ju>hrmT`*ျ9\pͺ3}LM}z[<`TT.9%hW} _ҽ} ؼ$ HsLі逫}>ucSKpqwE{ :33nݛkNl>)^X6-fn::{g? 9>u|āsSlY>OprH.]Ttt&'mA>2>Z-HLhP:8 P:=pmjm)( k M6n:B BoOEu|+&@rx6psj7u xn3H)`G?:>iּpw ruʯW)Yy?TZV+9]P|@x04wDГ io e UCPsg:Pysɕ+gpOOP5 Z'_­n] wޙv{iX3f7՝WAE> l?|78y# \N;_u_XK^=ܜ|`M[ X4x,{WoRJJy|}|Ӄ{{̈́nkR <x\ d 4N}>X)S~ip I'M  W鐰9\BUx6ٺgTʣYzҔWppO7;լf5_^1o53f=EOs Wj]!B !pnA7v.4Z9g/\.` VC0V}ÓoJp$=;#H)%%ڸ8;%^L|1ؿg>|?}ma6Rwϗ^%S;2"&j[%ӽyAJplOQKZ6 k箅T= C.@.j@PQ4*G+ D[NY`r5Gl(01ѲEPV (i/qpү7$H4m4-MM;4P`Pڥ"՞<ʯ>ZkPF`$0XŪj8=@R+K _z[ bckV@ZFhl*Uπ]o\=#Ȟ;EEVp4 Pp)O_G()xn]J$8S+PHR[c2ii ^ƽL$Dhj ]ragZoA w"Ӌ[)T v*d](SlP2!yHɐDl0ɢx,kFIpvub4_zn?d+(e찫/w@u~N~I_IfLW_y5ʙ3}<Յ='7޼55#kLj_@.;gK>-I( ysHǂTU_G*ݻD4Zk9x.mT0ื޼\. G7&Cee$-5&hZ= L^#ˎ=Zol 7[n1Wzk @uC'#K}nR ;6ZR[[6=-՗x~6* w-wM^,|;sHu=/dV8(]Juf6kl]`ÛA[~rQvgaRV@MVBѻE-B^ ) a k .sd+oC+b!lPѠ1v DhiR*#Jt,aeBC/B &k=oq)aC+fBf-L-5G⏎en ǠSyzo-߶5&Ph_MV\jYuxy ΩWAŨk˞ֿ 8TB$tgG{?0;`4(Mu X xܗԢhLcVCד@jZ;qRY* TVOFwί,)?<NjkU3 Đ%5g,@"O pf>T0Џ"@g#gAm`|.yL'O}(%0(UpXg|TSi*M4)j7| le [G<1Prv]Oէz lc <,G@': n:Ψ\eKdگ@8ᄃ8F4)1+]bYmV@ֲ4PU{VT+՚6 G%5Ec,cA-VbPw=vm6W}P*W~Q7C4Ы5ІiC e|Z$H@ªi z |*- 0GANI9jZց^ҽusI$%䄜``` v 2T@B m''.x0Zl|:SE[`g-aM|cޕEt۞9#- (j:hMgqiihCOuT}&IOH$%(h[[?9,z١9888888SGJWkoOtp\ `)?_oњ?瞷n( kAM:uHQ._$~筽 ZH$\&SrZL4[~~SWKsDHe|71Y%U%]3yqpp|#UT.dkvO2_~Y( dDH^$8 ' O2g|$"R#>6ܮz 8q9#Z-$Y2KDzp\rrSrs']dXnXnXb_h '+G m|<2ܓy\K`n3$x'TI Oӳ@R%Y 37*͂??8X.!=)%>Ic$ɻO$_U~w=M>sA&HvgZyVrw= O=y*hUժj²²z2'[!bnLɒtțmxYdHƴ5|u&@I9 eH='@$NH9e.?ٰ Zkmu060 ~}-:?$r#rQ'8 .|>q2$TB%TJ f׬Z.^xHOwK Z; 曾SUO ;7]zp1N7L'i x"?3PRxA/xiQRRJT_1}HEoA{$H㹷\K mgR-}g@g)Hn|3gPW ?2kȠ)A|m'l%.K{R&|@l- E묇@R'IN&=R6?=' rkϯoklk x>gt#Aj %%[X;v%# Fr%/'4Ai:u%+&9r:@6ٮٕ yR666ǐ(K|-91l mA^tޣ G톼Լr9 !93)*iy`mclyNzwRNIBz`z֐o7Mdu6[mA*Q`Q2_~2Cj *ε:y1'A:e'g4g*Ѥ2 ?Vxd/4VrPSy; *ھM@s"(ϪϪLkX@@$wW\*YsJjm6H?t̳*dx2+ɞ<ԆyA%N ٘o '?իZ1ȝ5ځ~]4-i =c.a,uitc-cU@eueDDkњCݱXvED=@mBً95BĚkk6-.6ͻ Uur2gj 68`}h`mǷ-NߤvnYu&j0D}tgx!p-賠0?Ϳ=z^Z)?Wb0᥇-~myHqKYR,`jjZ'h>|Lo, ܿ;VMnn ?x*]}wprq7@oiמ=+@}q3 /)d041421i'Si.p;rtl1hT_.9!GjkeBВA%A!Ka]6#秞#@c8}og-˒!s!q[S$gk 7C\BWj<>އNEӊ΁.wy+\luiSpUg[E>s6m J@YB|dic>KI myoC[dV7__IyegNN<a]N6A΂&@UV#"'AڈikպVsZo\1mc,M^}"éמniiC_f ʓ*dȮ[Gn?v٩$"3tޤp ҙT* q馠V*Pz s6){kqq@t[dz~zدHENr$ 2Z2v{UrLMyz_.0GZ<Mk3ַ9> Rӳa R=RUVr3P"?Xmf̱ woV%p+A} "˞4ͭĠ}ьf%_1Ccd\1j=3 :zNW@%~74~w;F*Upa/.ALw+S cc$N^|$7y5KG׋^ lGO~)x./7O允G'Pz77Mƪ`ylIAsӜ5KK_'f~EKIރag>-?mI2s;HO~Rwү14|Q; wI{4f zm5<% ygѵCxC|CkkUSߩِW oA}Q@/zVrnq>}Gg~ځ}dk>ou`,cTFήL)d:!a~}0G;*%7ܬF]15hvw;Û({`c4 YRTe~ۍw̪ӳ1i1u-lfBZ7d<[?tzqZ@]8(UlA@h>ySN:5|090;C̊3`Ϛ=̆ȧ'jEQ-^??ևBI9paUz[],Cc,R¥Ktmy.݇H98gxgCECCe!|q$:}8-wZVڴ-@&.Vt- =["P6yXA,\t]nύ;c?s{jgk >j0`/(dUH3n&Zl|ZZg[ƻv.%nz[@ [>3y#qGuؠwvi8p!`^pL65?ج*p9})[ج9~ijוs{dZE))RLs> 97r2!ASj59 T~DO~)Tu֞P 7ց]k;Ƿ>4fvz;??2Tx? RJDG7뇖߂G=spwݹ1 7BZ88`ܡip9ji1-bsB 5U? e(\Cٮe#<c[- 6J.y\Hr{Fjr(Pk/P#H5 dlm[ϯc<Dq2ۿ:?n׻M_ qu;eތο:mrE⋜+$5yDUǡ":Bw~D Csj$\ǫ *(<3gOO*UwS{QuJ%+eU 4Ϊ[O2=)Yyyꬴx+ctϠZ=K** jY*5Я詺$x:I~3)2,5-$MNJpvZ:TBp7e@&UB*o#/컘>'\w%J1SRG~v2^x3c9Ni04C>HJ qa7қVqx M`mcyn*4Ytl'T/((( *VU +W %du6Ap`Cыc|ݾ~@曙2׃~.=_3=J)YS{65t(ljPsq͊5 mt2$N N~wҩrlkkx򃡐>Ӊp|ZjvBCĭO`U]V'/m\jۺI`.?2TJW7o_hz 6_v~e@$df#y]_ w>M7 kr9nͼH۠vw5{{A A贐r!M7W?׬XeLeY5Vl4mlqV&,*9=%"\k+gOCpvЬGfަ{-/^ O,N:v|$Āe˗@b5Y9re}꾻%[)8^u /{Z[,A4_{=PRVʀղL@:J& Taa<`h b}94d :0t %ǀ_ 8V8F]UPE@W20X%JnB*z)0(N p0{{y@@jkLTAB@e| E$  Ρ>GiN3 9N /=a/3o,5 GlL2>ѐF jZ| @MUS4Gzw6mOmY n,`gx0 fEmfeޟ 2RFn]ݰEqqq%,.|d{ e\>KeAYM`bxIH?'3f4m?Y`65hKnH6<~O受z^M DoT@BU18ljI4IuDQ@OL064L3k==fz*4g8ԡ665 Q7.@TQ0 ޠj@%dw:Ie(Ҷζ 4ivni7@%*OJo8s;(c0za;ֱ@96٧dl UeU{{`4 c?@&cg<たXO?x@}>yE^lᶊ`ddx Z+$$F.g)b%oM/4I6o XY-;gޘvYۜ95-5WWG+, -0W^-GW[+-ߑ< eT8}tVnܦQ59BOeYV@ueZi!ArvrqFX[[@pH0A(sX":%pi=qrqOo;{{A75 W^ӗÿV_o !ޠSj >v}n|Wԫē@jr$/OAME?,yo~./;Tz~W/pWP_)j ao.H ˱hkyO.nŹEuAhUb,\e៊TBwppppppx{ esŋ/M>X*o[拯Y^ySސ7 u~O=رEOv⫤׳7(DQCr r:NAz/;82$|dG#ϫ(\ D1H _|7U;d Y) lߪ*HM)5@HY( | 橹dKRm 9"G0.%;A[gvArW% Tʀ+VQ||I;iNjj '%&dw A U!d"?V1q7I9sgIdCy3AFpJhO@Y92Z?+ 2@}Y*K `|*x014̟ 򞌑q'~usKLzjipֽoy %S<ă?~p"W('0٧IA6n<0\e | aPX~E2PRSjH)2\L P0owhkI-@I> _Pԕ:.ݤ+`//_ސׁb\y- WKJKq%߾Rf7Rospp(u*!e |%AI?y~ _W22_I?2C-1VHOV[V@Qk W[A\E@2/dƌ}`y$M>YgY\>H(}O|ټ|?*S,M+w~RI _g^( 9sdAˆ/<QlI:nN7CdgpKċ WaÊk))@wM+.vUH>|=9[[Ab+XXX@cѐ2uv'ѐCR.I!{v{|̇<gǞ{J(2Y&./L _ʗ2d~z {ʮO=N{~nh^+mxwpHL1TO^8[K+iB%Xy(_AƏ2NVB+ SMOK._ #A2;\p50W| K_h"eKUqw=Щ]ǥ@eǔJ*ر$+ Ր`x iKV]તK H\r݁PKٞsk ]5`gϮ@=ʛ9V *PP Pjb\4T44_ 5ր۝w9г90n'4z7{2H(IIP9m Hz *Uju:ޠܤrYuM_%Z~s̳,lN.]T*_2 <|:Cވy3C4xll _S NmA^,g,,we,pyG96ˬ%+eUn-~qd]ya}A{ A:0,,іǰC}gďl? TCay۳Mu[pw.q/v6`K68uU :zѺG T"_ݾ;휻+44nۺAKY3,H5,ں_ wCFF#!Ia]ҺAbĮmRJTk!޸~pyQ _Ґ^z.C]=\6? 8Ntom}סԙRK6R]b)7r}9d0kaZ(>}[꞊,=iii%dΚ6o?9N!\nLn&x>rF5׆˅ޯm}xx Eѐ~'3iNf4#y _kexNǜ8-oe? >`)ghG2.fv8tMAneyA% %)| _7_z o!᫡YfC+\CՐூO}>Aw6d2 D:к?Tu;6]>y/Nڶsr@kW˥;2ooO@D@rJЬ , UB &ؼ{h3 7]^ ΫԳVZ] p#} ,~C|P+tѳH Ř`dmz_j\ #K9ݺ @.W:tGKI j@FAen/lpppGZ3!ĸT7 x>\ 0H7{eL̨u'VR]kֆYM!¿Vݚ }MFrF.4WN(J=l % {^h7]Ty ܨu%s*Ϻ{Q&P!CAO3̗=z^Z9Vbcƾo0a6; )R )= Tɠ`h7Ԁ_7wm~| vjl|w%\ :;q`pA &8{º.gWZeAU>*?sw875JR) oڗ`hghmh#O'$Gx>n7,s,agO+#hs)t/P?^z+`Miڃ3@?U`XΰCsa!+ :2Q-B mhIxüǠI&{?N;-Xܲ{˫pͻPvƥʋ4HSOYN5aD![`@Wm~4ꑫ /}Rֱaqa9@l{mپq PP9-r?ɇXźV 9w=J<+taEkXÃWRxBdONԆ4v/KhV-.pJ:wBUprO^?i2c7bꎅΕ^5{ѮеTMk]y$3'Y0!>R[L^ӽyE,i.|`;FUS؉g'FJ_jqSYG{ O=hlPkFzrp@xHʠTH#K4,3 TO j~IDATL{w  jhI)$׭ =y=a1pA/7l>oFR3 #Sg#@\y%HZ7\\| ,2/G-rt}888!_2)w.z:3p2"s8 5 2U/U=.~-n,yVktSؐЊweo#p!bp=`fSf $}곙ˁDs'ܕ^wCCOߟlrM+?{ݦ+ ?4l~Mtͪqqq0 Xn(qs>XZYA_E;7+;ܲ>q>{<ʢIPn 3b^=왹C6[V}2+l)f H8p7 oW߾_s~ɽA ]va@Wِ Y]^ɚx3pm??Y2g-O37L  _BY3N?:EG{"%\6f^ (bL z n[Hjѹ Ѓ9Nd,iXXΘaEla A_/e|oM};FP# kS+ ׸*WhE`e+?;>!DڰТO oo1[sqs}vY؞c'42D}5`ffy6 `H!Gߍ-T`g}2νy~y_W~ _we;j? 2s2e2pKR/j-pХ/Vn.;XlHQZ@9Hrg5.=\*׀GN_8v||6d>4{.R[BwAM+v`KPѮ#\;m)I)/‚@M+:hMЮ]]X0`]t7gNdLZ"P:;#;ݱ.I37Jdyw@ i! uB`6Y414:q/,ppp,W |8?ǫ_[` i RCHu0nefe$gG9P-i@SY}~GwGC:k׫oL_'enܾƻ z>k݃K勵OIfc!5"uxjwr:2:My!e۱k ÔQM?v08yvpغsgj^*'ZAg. @8WçCx TPųnq[Zֱ8-0W, gY'{

z1 p&Lݳ <+vp>Bon#ZnZFvAryy0e,h?ȫ<3= *K*3ts\FAVY`yͰ5U= ^o򯗫GnR=J+omW4En}$NP&g}\<].>cgw'PH%߄`է UZ0sNIHl'q8w0xVYäv9zK{!Pϋk͎Fߊ*O[Yx4DZҒK'g9r?l~ILl+L  Ke* lfg>օpӍ>$-!lލ'pvfjfOs9غpmጆEAe!Щjmk՚UlzٳeBAUҪUmVXhD͠PlfؐO>۵w\n_S$Jn5o7}1rc5֬[E*UK 85 랭k>nэ$ST:u"5a^:Hgsy b-v]d4P*KiRYu=''SqJ(]ٕu,hZLA(/TrN΀mmm 1r,ʦtP}47U3>@{0Ƙ $j )֯i-k8PƁ&/uu=~0?OOFUkHQPCw;d„ 6v5  +0a >=~gϚ=z^l1DPj`-X{;{'{o7qH&]@7ЛHo7-U)P&d wAtdm6FEmp& 2P| $C! >0g%U{fkqܽu7> 4,fWѶ S;l>j W]}.gNJ ~D#@~AuY!}AZ`9p/*Z*V[>f_0NaKozjJARgRǛr UU x/,;)u߭hjwppppppwFi#aէ?] ֥u#7C;'KxQȅ樹@ܿsq<:C(|濠~>St˲t7_H t <*X.eUQK-b[jXISi !o[/+VUOőgTakwCJ+懀 Re/an RmNTAQtIvR;Vj o87t&@*Kel 'b]rrHqvJy/0͌2Wdn<c=N rGn-hoY3fD=^^\`ϲg͐C]`mc}rRNI$X]> H60sK3@Y~!ϿPԔRߪKxH#i$ /C_ڮV6 v;rO?0104vF;2{RSӚ5њb5IPA>| /Kq),8SPW-53?U?wxUڷY{I {])JE)*t^BBBHH ВH˚$x{.^{Z̚g͚M&iAh/=W8(Ͱ۸Կz RUVPv.5ѹtiGbI ig'K,ZnUv]=PPPlmkBEJ@T3տp|?##QoQU WL7.11 @ j,~xG' ih0`qD`ܒj@M!HA рqqp:ǺdA 8PR,P*c@ت>j`F1m%%ׁPh `x2~Gv_I? d0fu7f_ICa;Km  73[|$fB!!n =sh(o*(gOޕ+@N]YnA7999.]zРZaW.KzkWtJp䉬oÍ!7JU* Nw3Φvޫq/ Àݡ҆J*u:IRH9F%߂ S 灲^YhV ˖zzMkMMSOh}SQem7(w*"NxBGIF`7P8ptL}`OaDk6H"`g~ b!܅; `J`T.<$Bg,)9+1 X@ րff Ӟm@Y1% @$KT+^&X$qOFi)_(I(~N& pp?= .%A>v8P,oȼu(ÏU<>@O*Mw4M bBzRzKP9V[e|^Yذ?>!w\ FdLYy==v9C!@2C x7W,Niҕk; fM39:9 J߂o^N3S@BQ' zWc[/^A<><0mׯmT~ix0;qAfLkYA˾-,:uV՝;hc4d Ϲ*7$eoxCp_x8-GfA̜Lt]l;gÃ=\kWsA[u8q i-D**>%v*^ o7|{ꖙ1 [G'P(AJUhZn-OB'}3eo+;oYWQOvzFFm`WAm PO5uU˅r6N:||| ƚ` 7]1]u:СE. ;_KB6#TZEU2ČZx:i }}N_Yz7`Lh [o]W Jk/ CމGf?5'tKr a"U;9J{ M nw_}nWnxvc7#|s6':{wvhWi~@jz]m gzxh7 X8n1>?O;w ~٣!o"U/#?m 74(x# tlՅN!C @v|Vtޫp;y'Au#^ G\^|-?;\PP=nڸijYۦ)8gsvù лNn@8 i|H?p?!oț&OL1:_4S6diAH\CB%Hݛz(u7hk40kά3Cχ>#AMTǿs+RB(Pu]Ե:Xl+ o@ iz$y6P~:D/@/[7+|!jO1;(=xP]X:Î8NtBZ6r,wA^4 @o qvy~MA4470-A(OE*0*@"@ &>H5X@>LsAn;{3f!VP0tS4L=; d< 5\^;5<~XKɼ} D]RǷ=c~b]6Cm•ȴ oCGE'qq4pEGVgeϳp;C;qݍ^K"T\?!TtP2xT$PҕT1hiiC+̮ voUT?j=u@pwN8i x~jJSRCUU#>e8z"ěb(S{:3uqY8}:H;v+$]r:tY3xxB^T^t^Уlk(e&rCrj]MgwMo021[99.xU Bm9.9"/WX {0L ayrAcK'I ~|]n0@?T? a ~ B#φ A΄)9',!Hޝ<']Нԝ"e"ٟ-C"c4X>泚 kQnX b`€8lwOS)jL`P@8=M$@a ?MP+)@VjUk}W{Wo^ɚ'! fE.&) h=${`or3U9ǫ+d 2MGXOdy/c#(Wv4yX$v vauA4Iq3L( t%\M`f[PynInu4E!@]1-Gxܶbܫf̘3 fOs| -p{ zzc`PwQ%zԞ:Ljk8>_ohi*4tjx 5 Է Wm??Sz T5!{M֗~a۸(TrfYC[;.5O]t1v\/n<:zxlu:@m 6lCu jh uzOg$b&H!P(/koJWbqA*~*~ aH-Gk)} (Upq>4RNr'LJtuź|e1d=rCҰG_%~m%p^((魟^ >rjgw#@TDr]KgBۻS2V*Vm?שBB=ߦ|rlEi:P =d}/p^RM . FA zJ'P~4Mc'ccWP ?!~~|T+}M(O4:Pl{irM^MLLs@*ӎڠU2 u^r/{>Dkj"@{C{]Ci$0gԤo9 kGQyhKK:@k֫[M-bO<{,S)Gͧ4)$ֈub֙3#A;zX{@@.xZޭAK<<,)jz ußx?)?JKi dkL*R SMx* 44}/wHC=pdE@eQyڡ}}G O4%r{5(M78bufhҥQ U@6D<}՝Œ3`d3Ʀ7/wܽ4zZU#,߳o94fxys-4kpd:gf/p 6mm*̨0?T~'f5hѮ*oUy5_Ho@ozEPxTSz`e |§|G>T!. )`78vַpj|PC[# pN]OLd?*>

!pha/~X"^__nd#!ÜٹKu"/g*afMMj9o^Q_ |->v]ii1;;++2R^d*q+!g\m* Q:@tEEguڥ|[ߌ3¥TER124kfkiimP+)_ X|PeJ1_3c! VVV ۠T]ݧaA`db%\ VP|8(u-ZxT%9)3rb񦉠?Vƃh"`bf⺸.b`gp2x)fY e1X |ƭƽ`t4M,{054545}>K_VXMz1R D.?@ū7EG@EEJ~-\L6v\+RVUE XZ^ ;AB@;Iv,Fl![V`lele^\zlI}/c㧦y #h4 063/ƈjIgrqIYt,OQV(@].Wׂ&LsM_>@mAe`,گ_i?]. 'OO"{G-r8︚wfkz4mz 銎-h}px5,(ܜ;;&r',%!WdydEmu.;aӲS6she@. 5V<Y%(MP*&DG@ ?LU(}*VJNE-<Sy{._(J7:%,-r;Tq̘1/QPJ<\/R Rٗ,d̖9=eրЂ̓2rJd,Ϋc}҄ ##s[/-\Ų("Kf P(|e"YDK NerZ22Gvm(yx^nV.,(Zl^O);+Z3=8R^rɖ/>WY+sevP)[+%$O9zȯ9j]y1nׯR_:ޞ+e[D?"Y;ܾe}|e[N)OIs piUEмE_~U@?R~u: ]9~=̔-syp7tRR@fo` `=!рbL0x~c͍l[{qQ1c濞 |+|J\#()'҅ߔ_b[W>䳗3;2%eK^h ?_(c۱aaR}dvKBQ=zT#of.C,4/N﫣to"eaa~y3f̘1wq5D U_ ؔrs@^cċ 7-|.'h͌3f̘1cƌk?DQMJ * 's3f̘1c?@厢"P@EsќW|QGQUTU? 并݄paiD3+j}LoLq oQU_3f)OUuyWLyTDD+E{v*O9-ډ- $y` h&h'ھ<h'uo`)o}ʃx=[x omE[D QCy 'b0n72}>pDKRgQN+A<}¹,H_#L2AR8)N@>$:`6qqq.6h?=(*kU;ۧ `!Нӝ]Mzj] Ezry=˗WBF엏[ DҕPS/u?B&(ߩٔy:^ .,b׿{V^Jhн,SРبsf$UgbNX 1chhqa)R\͡,]4 EC0ez>暾Q_xUo/KFRXR:KG(%["`ll  ŠS_W"X UE5 oYڼ??x(L~*AAcdE꒍ bKeп m!I^n=rLy:bx-/ZB| 48J{nЯԯo' @k:GQq ?BI v]b]j7([ A^0Foc!`ޅX(ydZN~>~3Nntjkfpǯ{Ao,S~RC|~{0n/07&) 8~n߲RPF( DȭxxcF@iTZ eP*#^ DW0DV?F>$} DGaQr{XV[Bվ Tm\)p^ eL$=HO] "X5}`HP.54wԡ#,Uk?: xЭqZ?1WY=J_HP]uj1Gj( ڕo?BV+WDK (1vmUe& 4nMgd~սČ(kK륾Xܵx } gV1V҅KKA^%vm8.@%3Je^Y;;[`fazQp,,,*Lf]rݒ&o!G΄j C]GtqCCe^3C>rx<4=\]}1ܽ;| <{܄ vm> 0tߐ(pѹh]|WL+F;_ t;4YF 00wj7Я?qr| r6l v|32EV|񭎽^xe^=.{ $3uo B;.9<9cUnnB8 . ]EfwǮQ{/ ?~]6sԏO^X#kQWl,є(E%(W"(^UxhNhNj⡸)NlZƌPpw nv= r cUօV`PѰ~Z׮VͬZ|EM}8%NIlq`ji ַf-$qIdgdƪj!o(bP4P$hk5@ .}p2Fg;|$1'px6E`p4+('|swBc39k?y( b0yEʗRRh {h*#c@z@RW!]` Ith۾V2} !ih gz N߿jb<9*=8ENXk y]"6i--sM C}~.l8Sw< ԃϠCM?g; ~i a?[Ү]V >zc%,3eTzsjuHR x<){FУju\ʃQ >$s@wUhTi2˦1cEd߇c=u*$+*FCE}^^f.Q#F~h\z|HtyzP)=z*rtXevp4-u~7}t>tYyT*J5%PxAyOq c߀틷6p x|1٣/  MeUM#_ϸ zp!jyԝF#߃y2/;^%w"]X&ԊP~n;Vr)SX=]e24BSIJ] T!b3]v6AZK(J11 c>0Br]Ϗ\:Y^qÎwD|&V u {g̱`4; jBKhQFvdl#}-SΆ;oߤJzK&PW\y1w6Cp+t?pvۻ nhKg!u):tZh.hE Αo@pz.mdv*ʷ9s7l=.߻:^p,mX\5_GC=m Jf`<\jsi¥Ő=+ lGjpoeA$x)e| eDnl ?ˊ6fۛ6jt5u6 zK`I|65RJoV0ٜf&-Q8RK cw;&u]إ uy]^QЊ%DAvay!Ӣpql-Ap15g'@̔d{ro}sM[]GN7@n__aX 7]u,2>yg"JBAÝ˝tIm._9^j|ٺ:XѕXFtNry Rp]{50z >sb37ٯI ۞!.oyW ujoY nl9AJ01c 20KMAWrݏ ӵ1G`T7ߜ748ը~PtxਁGJ*Ubls{A޳:=$CȂЈ~qYbh~Y_a}wFUqWĉې7,i{N߁ϧ^ t-:CC.BA[-<m~ n'f&FL{E3.Ţ.nKS6Arɋi@*[.DG7^ 2OyDJ_ L4Nl*>M}JryooT_1+ЉNt1[Dm{|^!^_{._7OCVQY ))I>'+,t-u {nOYGrEo3B+x 6/lT*cʱ!ǯ@}BaX% {^S%+| ɘ$OesM.@akȌFD@*o ˶T8)vpfg;• ݡ-!^Ɠ=u@`>}gEƽVv ;Ѝ T(mMx+&f].piĥ5Ȳ˪Ssg~ )~p}a3Gmkq C# oňBࡓ:CjW23 3*7>>xH7yQË%VX)* ɃܰB5QˠNj}ˀg@vmr/W~C7Ϡފk43'2=b j=ȝئq8D+:2G=o(K^G%,k9`YAj j*+:DW9} }6'I 4d4S{AUY+R@ڲXVvYXAZ2Msb*xe\G䋞@hZfI8*e? u΂ue,$q wJ8;3!jd.k\ZsGɟʙ0Q[ X, ..ef2TYUiLy5tt_k?6P(5,>a9jTU l,x2^&j"k:bsr eZ'#p֐l+A)`K+ʼ+p.g: g=~6[{g@SIASͫxWpehsدI 3GH T*A.56`ŴʴxU?eb؝?a<8qY2jǃvvyG j:d) A`xSttߔ,|.l>q2hzۻmYAyҠ߱ %z^V^jW *bb*ؿKB s cop%&i#xhm@2\)%YXOM7*X M]] vŖ3f.j{XuHy7یpc>Pp]<PNoȬxz5NŠ8jV`wT쒳ΐ:1oN!kob} tQ:ln_%h<Ú,[ь3X)I O:Hqj}XJgf k?H !͘1^=Wh9Ѷ3h\2%2 Zthaj1\6:< ,Z A+f%Ի}5jȀ0w;k{ l^C (ܣw~;IEI`/w׀G7&c?X$|*:v;PjM9@ϛlq{_: 綞\}hۭm|ԅ"u xz=}{^{{"{ )@뻭fM. ׆oBVl  ܀ +Ud<[qo64m7݇7=Ba݊a^ʮ UZ>.3 F\GsV[C{ ?'7.\{A/nZmip @= ϨaУOSinM`a3&E-Q/b[6 ѾJ;'2e&< OF*yځPvAJ5W-AI_:Ω-Y`\dSdH_J6~ /gYb٤W y.M.24mE`M% [&M;bh( w5]2  ,Pس(\Mu nucc9b8,@u:鷂fTXǠ~,Pr ,?j-@V9Y2F&˻31@uD}()*I>1`qb}8iC½eehZw(|\Y aQb:KNWC FR_W008 4hT>G{i2MjSҰZ+xxwgMM]P)Q`\ol b!AΒ%Xl4-AAfO6X` şW w[Y =ڽ `jhjljС(5 z`7|ǂb-3`A-E@lw#Ad0ۃ6?_8C!l?yf4h Əs_CXWl6MM (_(_(:RBª UZm+P-+ZKeX6gl)ؾB)<ZJcaUv V{ K!QHP\Sh}SLML=@PA %%>%5A\S"ASIq×_dj6Ǭ7qqq hh+i=oL4f ڙ`T:%hM&@u8(j{~`1-/mmm5hy>xLzWgD?\Sy4pv@ARʂV1&߇iW'\yr?̺ .*ŁIs@ ԟRB,Υך\w'lZqW\fUmLd҈D-)*Xa$Hذ${i#Z$lݩ&Zy ~s 01cdM2`-CNXc" 3eQMx?+lk2YㅌZ}#OU_'/^@ᄃ̖2L78N5eI)>@2w@>䃗PlyM^rSیf4" dQ/dt;)I=W-i U*T) 5Kc]H TTǏqm 2\@L(?ŸdW\xhDòr*`10%DXij9f|t`9rÿ5 =|&\=yHs+ܡm0_[2U 2O̒Y2SfL0=5=*X_2`nzx9*e62Nɸb䍲o^{K,oi:}w#Y`l)@Yş0At]EG (5FE#}ǦdCqD]QG43š`0d@lGqLL#PqQAr|L"r{.@_/1b+bnMaP\Yh&X#ֈU֐5d-(^q4>`EbX`8#xR⹠=dok:d7K/-Z EӕtX9b}'VH:94P( ٣C-9! Er {(6J+HuBUʫDW@DP5Eل} 1lM[MKM=EV&>u:T vehEKI@Lgѹ,eFPש)cQ$AW$;hk9\.G*k: chj6h~yGޖ$. g8 7| PGշ@P3TP/X,|Yw54M= j{9UzZYbhMla=~YY/ͮ~~WG@TSCAV #(Jcຌ_6c濐Z+@5&U oK窳1YLQDr !ˆhbpy#끜 hʵ3P\*[hP `J 445A6 dS050Җv&ONH|$f~tp~Q\t;;`XmhG{X7{ik06^{?k /v'xEzk}PZ+ZpdGCO@fM4ڼ?f[Rހ¾? 5aСA;&A%X9Vy筄Gv&ó <][vIrjG׎lM.7>vl ufF݂BսgW{rn,ܬtʭ`mc^A=WOT/+0*8%c#b1ĀY颴QYfT3Buxy]+=: `1]7E<)H-Ȁ#׎4< "] eVM;[+ 3YnQw;@wQw_ y52Q`|l7 | E4#:qN|E?W٭EULi~V7l'O%?~?FQ]=߁GZ <<`;<{t"*g{5 |C+֯z8 g˨ ~Ud3V[>-}2J.rZ⾸)s7=ܛƒm}B :@/z,#R*T[+5D2O[nׇn3CVVV_WW(h|6Hp9q88p*cy-X􁀏̅))ΐ!Z~m[OO:u[zx{=q2l VukAdA~* V8PafIսÌ q[7@kp։'[@bPg_.FO")bCplC A̐NG?=޿z+^%L{ZY1aÆÝqQqwFͅ.]_TV*A02'fg7y{=OŁ-[7@w\r˘+`;v΁A7rnBT^aDH4~b8DU~( /_Bd;߱AB)sd9AT9^JO()P[&bPO'@.~s1ZkVaJr2dRf`ؾrMR!x>Unۮ]T8'mAEew7_{3`O=VUnq{*~7-S?: LP[ƵC_l==<%\O~z'\1rhl E WI3g˷.~Ep:A[@ŢE0È@fG Fr4%Z@kɶɾ`cgSl هOd?mWlֽWZ7VYJ>hY#dž{ 僔S$ m rZ͙n\9tqdG1 _ c,_xdfݚz75<_gϟ;-te` ^|Tflz}<#nf9d9uq/;8IfOCP>} ? e{`U3+ T\ <Z#|{W8%@餿T mjགƭSeSS{Hz=iۓƱ*O|,CvzBenlgn;v=y=:NK@+_X~SZ4>nd)6yUw 3fMq Oޱ17R};\_~]s`ؓa߽뭬nXp Pyq* 9~Ϳ<ީ B> 9R'} 6n`^)22ہ9_,C`S' ȯ\@VײV@r@5dTuPe2y0="'&CuH0tP1H| DOCtJ)t8>ڌnYW<=@#?l =# F؀VUγ"*_Jl{8D?<8cuHR~zuUWaxѰ:ѿgn\ ^]󅠓AZ1~r"<#[ Tf`.yT= ,q,k2nbh38P#ԛj<6Vg-NXk2A:X}OƀQ  Aax.4[d9U~66GW} d,95c+Rz߳g$2uY۠l V_`7ޮ]EYiM[P+UZ`h:b (}zTKf@o@Xxx$a=u-U1p:Ʊ ؎mg x>yUw3f=pv5絜>2dgނ^{%gJ@n]g5f4k4_rJ9.ցrKĀ1tڮ l.CsCgCtq]]]M+țߋl'N`[6r=s{և6ӌ`p247@nV!>=i(^vi3!!9$ :_s'ZhwĶ){gzP1B mNC6=nj!U%W G쿆g N~6RI*~2pe(J O)jR;L] ~~N%KB em!lSxZt=fwn gs f1vd3Huii1cf[D |AQ S7x1;61h)F`ohs r\PI_TV84% M>8t\8XM0!uK*dVSIKk' -|+;+m`dQ8#Ttx`pÜb6˥ TaŠW*-@tDKPsqq6Ă Wd,t:Tp jNRc-`b}z(q-*Ll]T_5@ >77(_л;댺0ٛL,k[VߙVWz1`zP|yDU%PMe*'3|bU9-GBY?G$xq!T[^}U]pҤ[P TW=ӽ{C-׵v>4]h>Oxqwk G\9zW}&@e߀܀S`Ye"xew 8kBѐ+126ټz HMx ȷG<0IHD5%mZ4)4Mmz hڽiQoB -; 66P*T=uCO||6u)M}ݒk[tS No8@ES4h1Uȿo|.kWXoK.lfx~ A5T5t`偕bշP@F@ T[-6+_ VYo6ؼ}kp?SOm<$(8_ g kd̹Û냝}+׷zZڦk]-4 *:׏ju/sN=A GO־V:g;_ B¡9DS r?5t*Ko*mNK[PЮ3KgPٿwQpp^]Ak> ͊w'*n8h<㍰9gK-ULST{Oeο8-L+ʔ'mܲaUz>l<@ޗWRP)\Fe5kX &ST#>!S)S@; JZO|"*0 05L4ݻ7AsVsX귃Pe9kʦ_j ~e5@iV@ީ| PCLc@FQh},Zx];peEoџJi  0T754iFC}R~(p*-h v|t,`!>3wD5߳a1dL{){NY8:4h!ӼV&KKu >01,{BQa 3_x* c [v@c1XE6L,~yVyA^Ur /_-[[G{_W`jmjmyjC^-Ѝҍ۟mN-]9U/B7 ?K/=47-{ jUZj+tUuH)-Xp`vҶ6}4|iL'`ȳH%\k9b;껂nmH0v51ڳړC RH y%yj^Цt`mʳJٙ_f17czíu߶6o :v oVՀh@!:3C(90pYV-O2XJ@X [^u׹4ZM6NjvxUEeq $ZԢ&pyKޒA ?Q CmjD2U4MDcшҽ}@\t5-ucD>yIװ%-it]fdh wGy#O>g<]ͯ r]u >G<" (Mc'D /k:DA@,H>I &1`=%.^Y/R͘oQyq]F܁$`/{\:׀` [ >9f6g<Xh&7A>cJ'̿H)z@g~2BF.cL&R[9`kp HFfgy%})Lf2dL+\}:kȋtC< bxS 4 ,`! @}Do`,g~)#e$f2f;f7{@i߈o@mjQ,yV A"4R:n?tQR*o@D^P t#( ʅrK8}K(G9 t#\&FU-<]&OH&ZZ:9s™g Ͼ {-ZE6< {աjE(+[8 bD.J1;f^52+ DQgb my!<>A zF <1cƌ3J_;pu: ‹?uYLXSjhvj1ȫ`czK}Xfv6'!rԹPt$wA֓?5˒({uwdNsW}f̘1c oՋj/_l@NS0N6N1N);f(#bǼfƌ3f̘1ckR/sjf̘1cƌWFyB{˷Y(Ֆm0;a%=aZB,8ELaWߌ3dE ^W ~Oی30QAYJZy3rD9qK@.$,P(2c8B8.-7wD>-r Sx WmG* (1PWWS/}<44TTԯyw0KE⽧ۂX4 XSaꅫ bzz_Ai)?X_pf̘G 7 _ZМXQ*AJ<:f 8:8vfff2\^vWZozl\ W͟MDcTj 6F@zIڦ$P(es%jZ T'Z]; F_TVD ja0P$W)ؕʴkO ydF;aqO״_PN Lm 2_LbX.@Cf&D$p4 8y@#ɷ@8j|Ums@|<%&n= e+G<L72TdG|@|+cK,o̟'gdfm mcmdkd5MMtTD"@^Q2t5]e5xE NwIhެYz(h] Bľ}TP}~W|S.zrOw_h}Z{8lqD@\1"dwC&e8(ݔb-/k"LXN2]_D oubbXB+,@KL Z'4YAƒt 6q9<3djD͸0zl1}G](Zh1<_P8FFP<wPBv^$W ggw޲ϗ7uA N˶EzzeqP#yOR@Og\=`" GTD~[1w@Ej5ADnjSwP>9q0&f. '6@f P] G UAxˁă_hP0`(?%.]wHOI QATx՝Ì3VQ&E%Y}Y~p廫&C-%~"M!$(6d2,9d] blķp1v<{!pɻ }ΆQXbk (t/ *lb[`n)oEqHH6u 9305{mhB+9F 0[_D b߻ +]| A.mCT^.> y<>ީ;< ?p3A7o]xk-`v 4J;J<n$p˦,7rsBզWWÏ Y [lB~r_tѴR I1\5:twp靉Bd o,tz$R#-Dӓߞ~һ[7rofISmO=v J2-4TZEgV2D !SOE]> }w!Ck;̇|Cu+K"Tԝ %Ek۴)xF6³ b矝7UU7=q `GnSwBA =t6o<ѶC,HESNiM-y{: 2IDAT{(~z&P|Iӥ5AAKAw&L'$^/ /rڠ6<7`)ʺף.A񌈈hbxv@uQ@tZ~ ;V Gpy >d]eI=sfay E WA<DQWZ]]A*C/CwVͬ> l9P!;?[*U><2dz@S(k!FFیPoEۊ?hF 71AF!~\iUVWt_M[Eeee.Tہ >W=% ^ /Rr' RuFU0PWEBeܩpnzԃůЏ֏ /K _wi笅))A fb(zPɇ ۄ -? ΄,+WkK0٦Y͡륮M+hkWkC=Q" ͣn QS /QJH2;u=<9O:bSDe4nPϾ}=A~a <7\!I!PKɻɟ%cFs47*,X԰'{wյ6x2q#F&(8H)PECqJI&^?{ysyhϙuqjBgZ{pkݷ<ϗ?}kůYra@rN^Ǻz۪+> 2(! {&{6y dj^*OUP55/hS)5'e9~/"R )e@Zl`lL G6+^7C}($tx]0ߩA0*ȋnn@spg> _z,UbTb$| @򺂌b3܁OYbs7 FQ `-2<\ NT_\۾9KO BGHB9X<)_/6 /~8D(X{[?̓c=;,퇠Ⰺ*>Xslnp'3RJT=j/ 9sC٬YV2"J{A#?$n;8:;97BZ )cyj,0|`chiҾH[|w<4vStEh|F!42hp`h pXa<ԅwjq_?ZVnr$4Iiq%9?J|@|/k!we={rOgdW+:_F>u U}w}G}0Wt)n~6HnK*hCZE=Q\lsY fUhW6, ,+wCDVw!HH^={wOHQJ.=T$W+Zoq뇐 E ʡr j2Bφ8,mlղbl ^[,=sl. [bݻS%/I?RjĆǾO=Yw^`~U"\q;Pml2'1;[ UCfUT*AvTBՠA;{}> X ԎjgB(!P3w5©Ngk|T*i|zh4T]eyĻgć ͆ ˇAeV "-b,~\3qp4\r|&!0',ZѲ4.tҎ{;K׾ߟ,=YXx o\oK)z}&|ݴ0t;&Ɓ{9&y$?rʅpjɻ'r(>]03O6Zwh*p|k80O???e܂ݬt{쳀g6^_f&ttG<?8AؒyUKz6$s+,4sYVnWVv 4nT]> pYWUA]-F5=ς.ǿ$_/ˊ,Ёʹ[G5"{Ⱦ d+)%@тab Q=zN"PR%=pf w A< pbdž@kD`tktK/:)6l3f1g>jZ :@`MA c eck 2Dw,K5 y^ @khu=+h5S-`@buz?K+QGlmmkL3ƀu;ϠKѽ=qL!f~`d4r&}E~.ؚzz:zzzt}tumMI[vŵ:_p{vu'(=^J 2A:>X߽9t˞GBz.] ܵqPX[?M M&e_~] z|RH!cg?D {`v>3|f-kAcZTTQ*+@X#" G2 2A B to^A0Nל@wN`NYYk}f F3@={DX-ZeeYS6P.N:hii낏t ж6XhU7 c]yo9S$Km,h ڴs*hkkf")藌b (/5eV!fYjZ PZ)qou ^g%hu: QqDQATy["RDHB ~~8N$. TavPߩ+Nk@l{n "M [?x)Fΐ@gy}q~mX%VfPϫ+ RDH1\ @E7ƀ8.# ~/C`u:DgIt1^L@eDi__>e# lr׹+잼' ޸k >zXP,J& \ExYeW3 o'9Ag g$ bk/ `ail[_\<nyjxk-h<4;&N8$}0iDG>t rܮnΉsYVV1#߇}K|T"rJY,{ϯX'I7d=Ȣrl"H,14/Ȓr hЎT5{枏h{"AΕ5)׀Xt>0& 5Bj䷠ɢ0J^{@kK$O<% S"N;Ƿ 21@| KUqRÀ֬f"M1!fPkwآ8E2 P@.$#]o+ ,Y1)eC+ &/x $@,(~p ȯ4^Cg%z'"melU`AP+fZӡΰ:Ck]tu bS }p. ԦuAd}!jSZ 9@AX x]bd-^} V&Z Dx)]&"2"{F&B%ÒQvDCy(, ^W)'։H=8+N~[) r6c0ּ6cZXj PRT)[Nnye;ii&D6RAIFelN IMk\rzR@zۍGPT}csw8,\jU ܌'7ہs3ldngUKՕG>8 /fexC}>]O:4So;L6x)^^An8 ŴJPsTR5e;9nqk \qkAm ힷ{ DM{`gN.O?gT~҇ ڲ,EY D+5QMiN ||''L95ʮy *.+\ W w֭+(!t Wlerp:3^@YO΃:u: y>jsN{;5Y Ox!!J*)O9cf5km?oTlwrN9 W=XMpϽ1]vQwIғpҧ.vv[74M]?M0 @̄-Ͼ^>~]嬋R,O}떬dZaF{#9j̇c j?U\Y!%NɆJϫK!=x]ѧƇշݣ˝/ q4|: o_p={w}l[lª˫h1eU'>: 40!so8_ߦPWk)|o\3qtSrN~s:p}7DS YyNu

`DSk1e-?>ӐxByVwf9PO9u0W᎒;<>Kda@Q*q#xVYqɘ1Kㅎ;dA҇C Eް#r缝M!}`"pk}NO1n LЦp兗 zl؅PmYMPT1]#}G&(4x|p/r-=e2ZGC  '2ng%}Kn~nXޱ|bh"nnv=Ly}],~jB쌸ጇ?%,[-[OpKjr;V~4Y8  ^BGVXVQ aV! +dkVVi]VA%;KƔ< Sfn")K$(e2LyEgm^),}bȯZP:!$oJ> ޗ{ ֔,uMt!ٔ\"^NҡTҁ 7diB+evG{=ƮKvmYcxNqrޮ}g;ζ- `Ő_"z~m8~x9вKSx0ώ_1(=( K/y Gw #*W:m n=[ jkkͅ&%9Y9?k۞G=7omBdn ~}7~  ۏ:6 dvk n]VCӇM49 Jx*.j ~Ob'%~$㓇U$7yT Oā&X 􀀓}@WCs]bCh)5@^O#@7Bzx[ Sw /dݱ( #oZ1=0V6Fs!/kW-Vn/U!Ljt 7WmQyc(|Xr: BA=e~|ʄI)=5 }[DH5vU=#  Mo{:  CŐci2el[&@?0d~9L)U yhvfK +Zh4.FV4sYjjg s~F?}h;]gE-AtXty&6D +63%nC1/*hH<1L Ctt4 .H]{^Sر+v7M>"8q<2453BzAD٢YK3BYKdXlN0O5q [B73~ŖTV{9$lY5wvqƦb?{ԲjY^'zF=5oԜUuygg:$$kz"ǁ(+ʉ2zxZ6eU@^B7܋GG y<mi< qN'>,}Jz6Oh8KWI^)pAۅͽ[p r p5Ẋ-A5EY@S/ʫP0x$p]@U4354!ř{ K֨`{`*T^H~(/1Dog1wVqfff6ȑr h b1V(l 5r94ʳr!kNjB}!?[. ՝W@nU%|}ep1;&H~h ˨J7%2bHq7(0zeH+'2x3=s1}jΧƑPg~Gs.q}НOBf5; ;?\s*ph՘Pf%`ְ(Q KNӹ{[\psJpVgѶ]N9=ӞBvׂryP[F{b_ß@m6qAA,d `IfCwIkкa+}/Za#[04N2N՝K5/oLu X0v:h=Λ@4DsX1ţlb)XLOML888pFP!І'$pȥVO).> 8T,{O[DGGW/q9qUi'̇i~b3+G@ZUN<;*yEw Mh jUZ+Npoջ 8$sVWku:ԡTR}H5?g 8XnH=+G3{c˅B\j8QִZ{"Ge֕\ ؿu%]",%D w$.iȦR/B`r p̝O W/>x 9 D8qAڍTH0:q?viO.\@KEW,X9/9,[&.AMA=m )S /?kn.m\"@I|ؾ}e5;i F_(2խs!d/8;w>^Wv{΀AL8vI]ZʵbwK P~eA,A][`hc[MO7M{U3fɟ!NˍA?GcC6Vbyݐ5? g"5+\'c=@?F;S| 'mm)% GyCzIYUA9*ȳg]+и)nX2'͹|mpmlUdgXa 0M0lyCѿ]?CkWde">wZKµQyH3ͻ'$Ott1+w>VSp\ 6/sAR1x45m5͠yAw!qJDG<{ۃGy bc oKK$sR5֫W[016MK%`h-32% ]t}ځ]2f۞AVx-ywG0$+CJhJ唶 j:x{x倬/fk-ѐ,39S@nyw>"ueu!盜9!sKk011PPHݒz&>v?RM!郤 :y s(C ϕN\ ·@YwM\6hJ >ٌ\OCOxu u{V-+D ]eM*z6n op23љYw .aJp0vLY2[ٻ5A-e+pw{ǻq |)zMl7ǀ$'QWϫ00Fz|OΖま[&@#BYN@NN${{z@g@W'`Ttmumtm=~!x_:ȓyrׁU<~~AuKXRp˽"PXvpvm* LIϥKo^}.]ю`^g^k^L v#m`+Y t)u!*p'Жj8Lд| I%?(#a&5}}}+?C,@^eAzűgeFe D1Vx@}j( H(~GHw[T+UA ?'d5YCy_ޗOH# 4)MA]=>x˓b\e2hNsTm2^PՠҖpRԉ˭ o_֬o*rL^m7/„#7[ꝿG<"m7?'x樹_˓qrqgb׬Ckÿ5G70o ojGAڤZaC(~a9?3=RH!O>y:餃2 Aq@r!d \e2jJגɿz 8^Cl[e5~7MH?ۋ-l6 .gꝆٳi`@{W )q2.Cl}ۣOxX΢& EД4%\)(.p;2( 8 < NoW*U@_DmQKT,A,RKd@@A/Eo&*RE2PH BDQQq_@W1Ws@l` J[@ ќS”-EK1MhxGCq9yV^1[3@ MwzU7y pty[x HS(sP&*_(oB"]«%r?B߁ \͍*ܾw@$ qhOJ 7$x#$@"0|,dK`,t$Yb3-i\Wp\&3/0.K41DiP[PX2gPiLe釳pNoC?y#`! |<&:37ge=X=BĀ2,QA_>p P>~G6:Oa{`+'=,d1XAFm3Ct^`!dO[h+ھ \bx9AN]SO6 Aa!2LCt]@<濻<* 6 `X2[</,⚸".IUr\,'9DkZ{=߮#d}ou*NXmܶζ|<(W{C"qm;O~ G ? AZkGiG:6 } Xk\PyX!?u@]D*Jxuե|udǮѠ\t(%0A|yZ {۳f^=I0&T[lj9JL*ipH.{x-lqDY!JkZVEqVL ')+W`"CAy4y~GautG%\уUJBRġI;iSwP<54A=6T[8'ΊR \>Q ~I#^Kxmmmܩygݩ6RRBh~踐hH/2Kk!&΁"狜ς$KO0(T4Npwu6}*z6X?+_z6q6KIs4ZdH%5+;{XС2JFα^B k~֮ R>e}j7x'p4d:v|j:* ޅuXb1m.6QЇ# M_mgiR=dG%ߑ}#:N#/ p_WQ Lަ\8? 5!ddҐLmiIL?@.|ۣ8+&E13cǦCLLlЪu-?чw%KY#xz{j=_L2 H+V0 v5h/F5חBM5^Z\oq}`mabbӠʶJ16ح{U<˯WeP*{RۨAVeM`';ur8o e8̖3u@]>G;!7uBP?~@pzjɭ [jE["n8Ҷ8ȹ/'{Y\+zjg㟝x튝.4 tG98Ȅ# *%j¥lP+QG=:3ncǸՂOڄϾ?'o|tVj]Ac՜P_| ")pg߅#O| '\:E)GSraMAkͥPVCmZڥ'zr}R85MKI+ @԰| 9J*-4.T^SLqʹ6_7}4?Hj^rTRmK}'N9֙V+9qFy3x|y𦤞$NK1)BĆk9kEϋ׍ zG5zULkjp ?)Y8E,Nw >*SAtJyS vd sVϙIPE1-StDp&_m5s畟cwDX npzݡpWܭsw=pSmxuj_z)}0\bu $)$*d1l1nge?\{J]񗠌RF(e.}LC e+W. t=5u !giƜ=ֽн݇TZ> d-,[ZO}5/W.LsJI@'GgϪ/ﭽy{w&6CظbAh"xmB8h<8e?2pDGxS.#]W6>6 ,,sA)ϐWܢܖ 5A hnjӵw#c3:{ALQ#lJxoM|TשS<*l_8܃ܷ)@3XezyX]uի؛ٛ;Rn)79wJp+z@7s@s{Q9ij{,.,\V r?,zuu+BQ~I$KJfiƂnn)&N VW;CAs49Lk߼OS;&NP|DנB^YX&=N@҄}ykp9rȼZzAdH]&-z׭׬irм'VuՇSĨc\ugnT `uȉK9 S@+y!\e`g[i {aPRUhAsca{wu?۾¬ahq)HjQxxi8^uɭMuV ~?8 la[~ ӛwKZ?ifxswD_AF̩ sQ扌`h4ԥwjȚY*9;CVyY_AfqY_RNiDީ1ʝ73ɞUtlm mK!Z+,KCfxE&O*VB *يPϨ.~3k/jˆCBARFazAb0 cUK3 t_) {)vg0uĹUw+ᦀ( ;"fN'VΗ?|\.疝nnzF(V8;'Ptᵡ^R?!D>1w{ YMjdpt5Eewu9r+q.r֯p} 8Q| ) 8šȴ"-bΎlpG'Abs |g~ kr\#7g a'jPC"BMfSh>Ѽ] AgI]*EpY$9 4d[pR.mm}-|32gRȢ@'Ta62EEpaf@[^B j{N j S[KޒKw­inJie-Ql[.s.&9}dwSv"l04n6|vNM*8.NSnuOt?mVV1Ym7`hje]_GCMPtj ڂaI:KNYg(8>1`I5zhK\^O ?~ne~GMKW/XCq\M%Mo^ ,b8myd Ogep;e;Łvf#hx};`=@Rn)[握0f݃{L[{aǦ;ցwYHo(\lo]jdkb- h0Řn΃" ]pE"`I饫mK?\~|y48;9@DY}9Z(W\4Ӹ 5^4lM0[ruɺP8pJw`y:'z y_;Nk9},XJYXF}Y.e~GPpw%h 7aF(* )>IoZS>W(#v_/ރF_m4p1x ΁_|q߃b/|Ze*>w%m7/QttZg/ ^Mx8 G>]}?%D UA>䃷=hp" g=RN; ~A~795uY,kk )R6s Kʒ\ ?9Lu)| (<979R =xbSt4574A:IHM He~e|E0u:p_6SH$mL|Y}o_߅m.B4EZSuS5S /w($'Kj ^{Vxhps?x9ʗ!m^w PdKE.\@~(?]ڋv7o&NLFɔ>I>׽A C=ٔ\z\mueqU\khrʷlձշ5S[S'S}[w@颥3K>-Ztމ`, wSnFYҷ8J z]/30l\ar.YIV3h0 244d CK<}ާ#HLm7ը I\RA y f(J)p=zubM {x'˟r0Ex&@q!ˮ ;F|B܉'ƸUPPo`Rٯo (%Ah$gSp;v,K `hXi (cxs`&gPGYXp-Zupi =qB'=0 #-i9S^yZ[J9RG 4 r#h5N'pvIkU+ԫgrFu&dMq Gr'=՞jo000m*aBc@J />sOA@L=3ӢK](+~mv011"d0i| r&?!$)eIͻ7,Wr7.m]^^4 ?) kiP0{ǴYrϹqoDI9zZ=v$GgХsYе55yC^g _ `[-eK}j>m WP %r[@nc-Ct#cMa&L@3&zG:Ox7/F;8%=YH ډ6 d9YNyI^퉓L2(K5EMQ5V} xnÛb(&o |}PxAWA;y-ƥ[A޾Y/+oۛVZ/fW  )hGHpn MIWi2]fF1b9[Ζ&aYY9^kߍ c1J(*„ d\,Al'E"{nԷx+d@?ib_988ޔ1qcz=zo&(mJ b`d ʻJ_,1YBg+b*]{x5h ΉGЕFx+Y2D,Zn !AH0v٦-Xn9A{EA8aqr*7 :Jl,+K>A?$.sK.9.1.8q"șrO%Os\4Q&\-Alŏ w=rx/(-J BDQ_ E*pNrD{^,RBa%sd!pY\l:ЛIt )jnr\#׀,S1DH ʱro% j*l[@(oț .{8  c "aOYJe@V=eoȪ8xяp`C/ oB$)/ 7@juZ%M>N)8kgTSuzx%$5I5;LWF hQ+׮j8Q6װ׶7O䯷fRx ? 4Jh ^ m@#AE⁨RAt%D#V & sAT5+ޛ@Q<V8>(nJ"YhE7(E(JD@4qG$z " >qITAE@<D[MZKyHDꠄ)DgP)mDIBCSHJX5.)O@0IBGS(H+hmAJ@t{.?KFQbJc O C zMoP){O r@l[OAf%([MH]Pr,%xG@81EA$(ABe@rX x*rr9AP8plW\W)A.$+@  (xۃ'z.طٷтŎ΅E=_>Zp;E@4MDg v8= XWmUroozPG.ٶtlw~{<;wV쯸|Nmﭺ~#Ce?OU!yv#k2auoiJw&D ޢ֩֯öl\ --@^]n#U9z}N2I)•++`aE+1cp g3`)KZ¦ћ͛@L2@7ӱ_VH+Ք*+J"(zC)(Aʇ*Z3Dã~g JwF)9`9ijL̰pG$v¢LSR&X!VT+]AHPW9a9,VV(/SyJv2|$~4,(u5W4@ynp(P)Qe o`^/0){p Rn(|ź裔!BkH=/h,pYVٗV톴) C3u=m%]u yEfZ(B\I ED(RxYŗB@rG= KHxZ2xKpj\<2uM!DX) flt.QofqppE#QRkIC25j 7Zܜu5-PxŹ+NjQ^QAKZuZ~8u$dH-i Du7$~%/Wg*?W\ !-buCc@IUR+罜J*UgxRIקvk PNl\.B %WMM5HOM '*c+7ܟ6W$||~ *TeR6I@ꂾaT}bI1*ZPN(Gpmȵ3w^ PlrzQ/~KpԾrPP͒5!(]J+Hz?Z&u ׅQã[^>*~<33/}ىXQFgTPXLa88ඃ]jxh|ej}8IZl<)OšWuݯ"tP*膀f dTMp dJ/} EJ 03CQ=Nm Jf;l-c=m]gCws~ veW@fٌS7k%s~߭_DžZ)c_dF̻\@C"ߚ!l.!! 7mJP8Ӷv~Y<6ÒأQr w ` (VԿ;.fǂ[FnN ɯ7PS:mJueԢp^tDPdNR pi"i`hp}앺YnpcYE!`JpO 拵 hhthY~Vii4 |p׿압 .?x,%Rv y8&w*'*\% 4E4>W6z}b((~/w ˢ2/  w1;BveQNpǭ4ovi3{] {8/pe #Agj r;̭K,MY6  2Tz)BJQ2*6dͺ /lq"F4|PXfL(qátTeJzvXzziವϡD~%nްtcuepj o^2 u#poC&ḑRV(K/6=DI]0w؏UtҿB!`ũ5VցC9N RI T(>fa7AH&!+uŀ.uMPP%}~98<#o(xu^i;޲rOo"L?^|fЃ{܋}n~ +]| N&>GJ-k^ m8}70M#cWZX9fSEKHr3- W齘yJZnZ^H9]X总<[gFȝ= M\k~U ) rk*fv[xAnmK5{Ip&Miӌ7BfTLx!$\ILNYFAE4442fn̼j~`A =222S{NA2 =K/ Fжv6yA^./{c@'c`DHޛ|3< 8SSC 0ݜ^3_FC¸΂5ZHg(Zhpp 7މ#~")En5ܪiA-PUH|˒PXV:8hioi<(C;!W ?([f+:vU+ /lF|+Yj8>2S'A#2hQe3`Y*_=Nݫ#{Txp _΂C}v/u~<*AT.4z*_Ϣ#<ڗaY ӠRT]_p_Pe7P!T X*ecd?b133P_,[5#hi.]z^1<\A ^;=g{~z#7q1nIc6w)Iuj3bHŁA@-V?(j ꢺ.G/l@U.7].\;xj\s c&swUO8`h;[ ti7A3&LKF*Y[VﱲgATBtHuPTK꒺ l,Y)0Ă TU H@pqr*ĸa܂SK<]=5ϗ`4B`004fa.GkW F?c5FM{j. *}XKŲj42\r圁u >Q@2??kZV< !jT@S)naк1TYa\PZ{ޝ=/|6O,xds;t+xV s.m ?}AUm_V<z4@=Tpk Tj*},߲`*nFPAِ[k ѯE}=;Op:.6ذc4xQzm鑐5Ì8AsfxSN(jNXVg kJ!/,N(ɔhDfxtTb_DAIB _S቉b≡NAX{-J҄HAT-Uk~dǛyUVuDmwy dLI <Y8s ?r r#wfYK,1K|r|C.Se+b/Ɓ+kZ d+sd;x" -i8#NS xGY d9 Fڢ'o̓9 V mb+h>ZwAqAj!d̐ b?o6M‰c'8=t3DOCS!1MLS`PЂ p%`k "E$A}B2hS$-vQLR DH'7r6٠5 Zv!*kā6Z{dq"|zi=IvDdOrAHkA4Aۧ!z[b!2ⲧ|zW1 SObx hLkL Ƒ ~w|$BVN# AZ @sIv_" ]yLЎiI_#$1 h3$v]B< y{Ă9Mz_ѐFgx"AO3$m֑y .` A7(m$жhIYK d|I: bſ)fR _[|õ ]P}~u233'j&x~"Xz~Yevmۃ7MESL]M}LtX2wwe7 Sި+ʭȄl.<18鶶 j%|/l![B)V)6)`cs6eܭ!_F) N `[aEʖ+@x7Wz~ps55^vsN rnݭ,S~ !n]W5q.e]ty3,}Ұ'Ǟ?^LⒸ(~+ޫ2Wft-R\Xέ|I(h#aڛ Ab H',@;~^ ڈ֢5$@Ү9Hk#H1ߧfiY:l+[p̃qA˂@PWH$W=iw@ ѮhA ڊ h ZvJ\.y428uDmQ`1_0Q DHRV1蜗}8mdϙG.F#mz):BMRw8.!q), jbTcswHJ$3-vɈ\1kENhU9 _o%:CI!'5P$-q"_1ţƷ {0p*Ea)!Y EE}tVEN^Er snP2T)[[b2T`>D-69 "E-8eE T.:31,Z8DE { 3"F=`ϸ=_o 9g CɌ=/CF}Uz6wB+Pl5Y OXˑQvymqňO;U6j48#1<7@Vҩ(\G'`%g~k\M쟺Y YEbI&tݱX'V'O:j`0Mhhv"jiAĤ9 S]'ͼyP1bJp{ꝥwҹ!`mf(4333i2Y&˱E;w=ցM4 ./45yRݹS#lLi :w>-?~p{ٷ խBCw''NvC#oׂ.>u}F^S/gO?N%(&씃r@}x*pF. .k\ރ]k'Com=wwލG'IUTJ-8)ek_w-4~#w8uԅ@_TmReope7փVIZk:hk1:05o r tf:.?OLF\v{Z'O=V+ ?~ -ݶt<ƫHM-NV8{IDAT<(Z=UV['oy BnUUԎV;8PVУ 4Ԭuӯ!O;A13+70Hwt9e~Տyڷ,V|vFxoC9y3M4ywT1E΍.PFMØWvR?:.V~=!Q| kx<|01׮Xh Nsdy+JH iNji3ݔ72oxzDIWStv"P+p/pe!l=vnOK0"EF0TʆG.%P`qrϪ8̷&:EuKYgd8vRzB- lO~VkH$BT9FçK7x~M 6|gԗS߂GUJj(-)C%VemO%^|+[J(c<^i\]v< G;+{Aa)lo1oFF&(;w偗 +V+V΀8`fff ސ|#)>JWj:4iTQ.}zȲ.U8INEkQr3 _.>.R4 D]t1G!Q^%;opXsl1 hD@_%܃'}Xi\;6n{u,e) >*+PB~$hdt/!JN {PzJބFhD}6Ccp|i-j߃vA;_4S? ~tQ4Uc,8]wP|#tѸ;8sEpf&FupN 0>U&Uf|i"YQQ E!$^I:'JOjL7f@;ciU%k_u J\ZB TbErL #2!5ȀA}]{$B d d>kQWQ9c2ϴ)ڇ@vI33336`²El+Zpj㨱M\.YLL#M!6-&#~u#ܪ=y6~mݿp><{  bH|x!jY\]s ]g$&KuJVAL!F|L|i؟~Ձ(/_3-I#-mh-ZCg!an^n>XX&Z֨u!Woe/{G^n={L 2B•!]޼`5%@ zR1d+B6[ hG_)n+RϤ&7l:X7^6 8w2=[ "}YVU!#1# Rsh޽Zjסba`[ͦ?4l~p8:dn< ~A[>yr޾/s^C[<YmQ;~z;Qd9mPGA6xeC7!rRX2{ ݠAB v}߳k<RU?\rrPN\q:bj \2 _RsөX e ks֍J*hAG!XL]{^)Q6*儂kj5KM+yp|ݡZey]yYs?{ޔHiם5[=9֫Ox6 1~g|F1ha"Vl] Zx a=fF`_V톱1cGO!uV1uu[?Dߏ.c:`e ݤ@ƥZ2ZXh:I5Ȳj/&l(Sp'>b+T`2Di(EwTZzr"f(WRyw`ݍ\X~zz\%BUת7<I -#auy'P5 VEmK0N<{uXAGUWawݛ+cW{-  +=q8_1b 8 PI*x9B>ק -W|P๦,\Voq=- ,zCV>N$G%W7Il\ 65mfffFa2;E,LX|pppJM-5u8DxZ$~ s3^ӞyzGJ7}<}6o {4`?8 s"Enpr/GocFQI;*X}ޏy 6d(CK'j[o]8vXP+3 cLƴU{|J*U. u]FW̯x2d ,|@pwݏ/[ ~p|'@bLbqbe8̼>pY`Q-5sBƉ %[`kibTL7[mǵk$8r yyzŀkmʮ *mԾlݳ5r7hypک /cX|뇯9r>Ae5X#0?p3 UGAk@/[ZX>a  F SSSW\zs T]>Ek ue@~Z~*-epDlpvkk FdFQgRo@fKADZ&Ȱ˨lŔ;֝X7Iݓ> \~}SW=~(&Z6m Va>ɝbtņO6 YH}+ԕ`v`ߦMs[3$/?@L]o`-N"F`caL.ӽLEHߘ~,DߎN޷{OgΛ!|j!@ =k 3KG,qPzA7 M`xnPjlAT)?ĕ7J7-Y:,{oqj:Sk@7(5G}$O!qy 06cN J-&8U؂,D ) c#x-JKv 97Drh :&TnBr=CM:ba7 ocTO+XIIF2.~D@'z;':_Mo1z P<%5mgff*[r VaX9P!>UA b.)ݦ%/QqDNW8k5A|#AVe 3`7OAS)=k2J3VmAVA<"5 'A4ME#PcⒼ"Kd 0۠Fjqrb iX֠R [&Wi or[`jaUڲ K20 TMUKREH@"WYd'^5PVd ~rF z6W@UD%A VC0PDO]tkc1fk a(H<8x8f d8/πaej 798ܷԇ ۠f[(2OOPrFUlc 紅۷n^ fxhU/s jcp5C}/D. + *WgA$; jڃ*6O<`2V /y|O|nE ;a QΨqvh 9B1QLCP*)$܅( Y]:ʫsn_y6ҒګIP6TIt N%IԌK?ih~:hlȇەo}G7o)"S2L2ַQF{qb GWD@!i3wO@!O <*ϛ, ">W\Oa-Csv1Ļ%4MrZٟd('ʉrYIK)An ˵ˈ4SI2^ƀvX;=m6$ -{vCZk51H AV #Qh!Z`;zSNāR|IHA5ĂV,Gޫi -HV Xj-n[7]z%ѣLTEt2妗WJj0z?x`>|D]QL[L{L?¥kl/OV^589(xZ[` 5H#ra†=& 7.uE#$+ @EHwMfmms 냬 Eh*@Q~QnG_u>v3*H!T8KOaǪRAԃAOkXlUUJ+ͮ v5Xjƪ`$@NX) L,%H"hz?4oSsUv@KmM]Iz6'DM&z=ih/z d}>M@ b,^F]AW_l+mFIyvme@#WH 2SXo}er\z6L{K$}>rT2AH8[x@? xsEȄԁ#g /%/YkM`ǔQaHP=.+iip!eQ~qgwh8řiV^^6ñ!Ǐoc/{e&["ZD_\|#rlg`@pVpe Wȥr1#7!2/5G'_l/ƽX*XxV#*L˳*"N>r TPŹMލ7 o䘨kڿmH(X.]v=*hyҚkM0䓐(p_@;;.րȗɑa7]kOhN⢥^Ҩ)Lpԇ_Bq~qq~1T5 xk)w7nap6 8Lęq馄MG nAToocn=e:vdxmg1mк4l䓓`  uW[z< JRSjBq ߱%L^3!@ CE OW_^ B[({ߔjpi{21 kPF AM+JAD "|6|JO%CC)DW+bE?Y &m'e\a RPJI|" xkpLΙptph[y|Ptg/Pw8~9e XEE&T/kP Kjt; FwV&8SCO9ph"9]D?ipL;Y$@HU2l,iw߼ke; >3}G633?39V~ ߅O%]RJYCf-Z}6K Yg9XJV-A3fϛ }cSkť(^U[UVr³m>>sGAhhhOF kp ږTNP`UTP>9-(_P痯SdF1U[q{QrM ?z!zD@Y E:J[~-fxԶ]h>y/!Yk Z3 <=J.}[84Zc<)--ZCZ-ZĀ{H:6Kh| o_#?kE;'t_ :y*/d#dVH:de=N/քwZ 7oֹܹsw830~p2݌7ol>U|4? /&X9=(_TfW#I`_W?0k>%yF?zn#ysHٙz= &u-O_vaYp p8xa 0ŶE fBµ+1CҌu5Tlpv(R87pG,C*:umFAP *2z@b=p!C c{@tԭT' M=[F#]ŻԵTCPUG,ڢ9;u"3ȫ M7[ld.ߥ*<^SA,q .-(cBLX,N+y&ba{.].Ane/F37er_ޥ-lr  Bf1·lj2[*Wmi4AF1LSLhФ宕;2!sTBj׍1hKi#vhԣ9u*҇d,e.4 kxp .r0{[%A}u [= z1;@Y)ke / 3Ţ 2  t(H*PDN間-ށ¬"".]#zF')XN`1 ,9~E >.mrjeU{ P~~'AMV LLMӤW "k=f<4̮TV}2,2, j:#dZ4԰{'P:3Cac#£EXv,~z<.4հ| |nxnm۱fm e6ij(G9kXΆ&!m 4tkpcj: %Axjx͗!vJױvMTtqr=vP&U\rޮ \@Z//FFjW/]=.acM=4Rj`8`,l!D+Gj ^-Q0+YzS1a"Ak Q @ HTi #~m//Hr`f;!fT܂7 7Mdd/iAs o_m/3E!޷*OzhaΊ`{u>p5&vBvR!:at#~1֩(].׈S) t=SOȾ?g% jW%[}Z_7`lNXʸ5RkN5 wz]uu}>N]'AU'pf+'lVϔHeWm\O ɁY*9Jh88*@`8[bd&S cP A7 jC[V3LPg29en*W~Aў0 rD ֶiXBB@;4;>|^Fz,Y~ 33Dam؃o?ߪِ lF%X9}U}綋G[8yډS4iga~a߄WD]bZ YIGO'dA-v9вaT:)$$%qv<kS)ȃr/bPrt0e` KuF=?@ جGj̩>2fΘ^߽nwgTPdp/~}_X~}#A !lðrJVMvv9#?0O̵+{ܷ d.̘ 4q)Y !7C] , 20M P9_*79yڇ]_G,Bg. \'gƁ{3=TBXfoۘ`ߛgǦӁL[aY3Y?f> @{=Ӟiii;k\*(<1`MFBĖK 'Ire +-B2NFQO\u^V3܀^|ȧk[ [nWE);mMz5 G>ΦaXjF p.ᦸvS`Sl(ҋl nܶQ n Ο8K,N,^WMpHˉdCjbZ1߁gApb r>pxJp8]}v춰 Wk!\|Wq \Ld XSJj{h?##p|iCf-2$LIXq<US 6!U<>#KMU|HeI"f*olٳ˲)oP)! rʝK "|p|J̀5;E D֑XpKwUe1MLGOszBc T ?o I%]> BA0 ㌯<<ݽ $8p<;xTSN*XU9 ~~aKa2уOd۱ B$@L\}l(u75˝@€ MK~#<-Ν5L)PJ]*@ׅ_(k[vVjsW!<䓧AP*^68ٸPs\Cݡc鼧/J$_`%0r *i3{D썸 I>T lllCy38ؕPV>έ!2&"";,-3 ɣ"1Zo-/- xd-vJTI* L%A֓uI6b0>4> Iq"d{RAuQݰImhF3 P7-lh@.8ʃ K:ȹrY`8H}% L`5a/Kt36? %0+A>4T- !db SybPzXBL' s[P,WSߝrYLfCܾ/`?~ "BF9<+O2X^ jFMjt sA^WiY\@9ZC\NTJCV1؀$6R7%YD0"} |U˃.x H杚i[`7A8Q8@5QMF2b`k` p!L,<n8&S_}fӽJ%uY `6xh<4's@ WՈ_Ҍqs$@@ ޠ/Y Ͱ4lX`,0~Z@}&?pn`44To[B AtE7ScxRc pWbj1$f(%ii:r"7<#O`3NN҈P j l F\WFT>*RD%sc|k,cX8QIΑWT*8'ΈӠM>Ɓڦ`,0/,j$R`<2AEOQڀkjwI#1 T7fI{FQ^g8 ܏ = &$σm ? opn\5P bX)ꮺ 6b5FS 05B"\D` 4馛Pp'o]N]zRHws[X򠼔7V (Q2@-B{^(drYڨX*c 6^/3E;@`<сR >UAeo633333n%Sy7c(T>c XnѾ$*Se /QƻFu(W)ܾvlH?&~67@WOFq0>6>)_V/+ ,Ly1jf333335RT hhhU먵)Nc :#~U:Y-ߒo&@2$vLn EtaQʺH5ew:333b&BfY#(((]fKFI.<;h~$#}H_K_Q8jO'@@  mr3VZ\Dt=A9A~( $/<ϓpRK!^9s|㛭@{Mk5B, fV~PhSTX&m-a#+ Y㲦eͅ; '-i, n>Z_'@(#|@J&l}*C|kn19=(: Gq^ԡ_UZ$XǠч@_ρ%iѠ]׮jAП@dD-Q [Zv'&ȷ0&hZWfx^,4ĝ{g sGN)ŠP6hk *ʢ2?2f(+ʊ2>VOZDe<^;<*QdHC5k]'O}* ,En* O6th ' 9 zu/qv݀-#:6y7D}tN@dnܞyvU5vsػ~} sQ(v.jPl[lU SN/om:"<ZkuV^²dX{89yJ'rl5m-N:%O3U?+a˸-O~ !:o)T ξw`ˣ=CɈ iVv|P bn/vmH9r,E($^~rlԶn,]< &g4hG??q]\W p[\vȬ8pgP<ِaNGW+ ᗙ $@LSg]_Tg8sL!7$ה[7^LfQ91Ó' knݷO$K^+Ot>]v54w*ߏ2(>95.]|y5DV&x:gi !r,%!nO\T|\S:~rKȫW+D}3l;}-}O(4nA ^unjmaQ{ÆG>< sfٙgu眲9@\vNPreM-^,Z7}iBHa;:d}WCeqٿA!&|\?8z8cCT7VS7םhwީ~A\V>d u}=ܙ|/H eVʏpɵoBõI!{B,Z=~,l-,By@ϟ9[~%?ڟ) ! eD(q~B۾mۜN[2Wfb ,-k^k No;5AKR)=-ysڎi!ѡTUH6$ئ;67 Fr˓/g@xPxsog TGqY8CU~*s nԻN[r&^ zx_0^<v~IW%$mLQWU6;pM5J#:`Rŋxi_vl4KWף!+ }wXv- ahPﵺ9uSScx dg'd ?wPzscazJ6fO /<ᒇVXglY oA,UfQVnP@0]:NS8|piͥK!>[^=^\8>s<5+:@ޘ)߃5nBmnufyL&dKZF5m-YkS<$:pe禍./doHKs0R0ãywTm6\z_ЧO_)c?+L2AwmX6(6C,#c pCw]rW/ noy@+QlT0Yzlxr`zd<5ET\rWw.]#:0Yre!;-+:\v05357;vww'w{w1bccM6m@⻉bg]OK҆jC!!ؽiҮO^}0w bz%PV}\ zlۻÑ#׎6 ߅6ef,d#S*a -Vn8+CUͭ[CV)`xj>k,SA4"RJEѝ23331ύ??s50'^w8 NN6-ˏ,߃Q=H25}6$C\ )RΦ*e$VM,&Y4  n@á|y/];n|tQinSy:N@1_aq ܔ[ m#|m V[N5Ck&\vWNnFts߈] SP ;=A^ǃhU7ڷ;~T@JU"+I d1T2A "`ƣo9;DV*ʧp32 _h8Pvte۹nN SC*'|@oީ&CԴQ(hx Vj<9˓5>֐X.1 =hf4S͆'9N\.OyRu-fA&Pl+5W@S++8?sviϵGI="<%pWIpvq9'U6S_pn~@GEjV6^0ӸcO LO/o:'tO+NNNPJ/˔IO.=y fժUs% o f'q?\Lsi Mj7o<\=ݻc B=_.U*n҃PGVo(h kp?qNFkR}"ޘƪj7o;jt72wCo :6I|>(TBŢW+N(00574y!PS閥8,q`lmv޻@%882&C6j1xXjڬG/ ,@BU>3|>e c1LόFU9s{"F_60Cx'`r2ٚJPmaec,={lwCk<ʛ\AT-U￿;jZVE:kz40zk#̯UeB *4h+i;P~{e΁o;_'$0A%F!ٸYECo2t0301{mĴ'18zC TK8Fc@P]UMFpOD'"LKI?EaCA ƈ1%ͥ8L^),uDQs`3?RH-dWO$$= "RD\O 7&ln1oC]E F֫} .q$5OꝻ/(MO+hU т8P  )~yyyE{A߈V ?=$x,OCHȹs+'?-J-xXV^ |A' ɅrMQZYG+ zvB;D4@Vdo j'AuE]"[dA_/~@MI rO>bX[7ӛA|*>Rr?-U*h.O' mTVY =B+ٿS+2У3м4O eL=Oԓ@KH1y?qjZh%dљƏ6LF:@VQ+w*}qHIfro '+aK5W.fw;E`dQN+k l :h˵%)κH+]]r*< m9TM we&HX3;1^r.X6Z{OV=CO D1G}D;D1Q4@>O?E%iR~!2ԇɵozH]f/m뀜-g_ @AՠW`QۢT ?D-Hm=[9nk)# mr<UIQ'U|4/!(y23ff^x },\-u Т hu(ki|MQVeAѮn l/ہ@XLd Q|!Yv\$M2,J[Y؃E۠-75v] wP*8㭫`˧[nnm Ϛ>|gx`t1z攛ny/7-y>|o(Z^$ĦǺǎm]>΍=-.X<&JrvN{ω/Q,?p9q/g_ʇ r;X)V D9&L&%}aʅ={{{!>?8"L<[ƭҷF[ϋ7yTg]- C5UMU3m7SO9 yo(&F~?p1Vc@ cq8s?̝K khaGNOGAk5 hjk~rA.:-A{G B MHgj, va̞x Oi w@*Zn߸mJP .m,,u@( Nϝ YrFiCqc,#`iMkE^mɊ̈́7;Cき(<w|TUϹ3J  ! *H[>6Q@PTD"(UN 5N]g^ܙ{%yg8}GEr#}"\pP'/hPzںI \|j|\8 ,JӐQٵl>ݛ`nbĚO7Z+ k{gOxd؆AL~ѯ |2rs]r5B덇K̹& zk0߻y,Vжiq0|, %8}tf5,n` OӲEZj_nAAIaLųWc[ni)jt'Vi EYE֢P8ܫoyyy>ye93Z>Ѫ''JOv<rrA-jyn@ĆtS2$N81h?@%}H_96:;hth香`3b@&$yGK(i7ѷS귄]t ''S PnW!{z1pDK.Ջ.~Uw1A8iM,]"C?ɮ\sc3AzHO Kx /.uB ȻD1"j{x8+Sm5]́ <~z:,QGG]%^\;ymХNq&hO't oLQ\Έ 55 jCa}Nn G_?X(-z wp+V­nJ9?=% ؛Xkey*.tuebh8q&: ҿ0`$,^ vSd1YSjHaݳGe ǓsnX[K>p  ?+ߡ 4 hؿ'DDUEΎ@M˨w[#L&P9b=!v_ ;ps\O^\ }x|c;i1 :X m*Ïx*9=e1Lg7::T"B yeMpʑ;=:{CE+vem5lNǛpmyұK=73dO鹪VPZI88p`pYi [g:8 rcxl9cU骃8$9Ŀ^Gkkkv%BPd L>㽻L z*?µH2#1(+W k-^;L |>~S!v+D(xx1PyZŏ0)0Ǜ/]APesXmqeܴFlVbX,jEZ) c ^61쪳N_ЊCϢG<.n }Cڒ%߭"kE[[?߸j+PV-XTD-ȿ22ݹmt06l 'q)CXڳkBAM~< Rǥ.} FW;*MY&[oSVOk1c7 SL4<ž`b5S/d7MW}J9 ( esP DB9B.VXe;n>Sklvo?i3'{x{ eGX,e#(D LNSoޱlqƎA|gÅ^OBU=Usww/LY5%pr{[3dc ds9V. K ;]!XŸ!k!o~^L3PVQAw sep~@tEpH|e^,%x]4D}U c/!-UfhЭ3~Ӓ )S޽0-NjxoK!]e76.`i11RJS!a fW8;)T& CY*0i p5Z:[(QkP|mI jA ݦw;T̓An 6w .(*}X,Ml<3ȝq4xS/zs[Pru+3ٖiiP8hVE4t9j_Q4(,2D~Ru:oA$qf}TJ{.}Bք iNq>_y N֦l9(s!.}=9\+Vd(XX.?7o87{/~ 2=2CF""כ鏧?ԘS`}:׶ 'T%< ...b~WA. ?U EȄ;Y~8~3uCahp5z]40%K6ܞr,XXږ?q+'hrɇ# ʼ"!B eGʎ! C@~/whwOwO* ;b(&1/~j0|Wއ.m8 mde2:CuluM @N]]+gAj'BcZӦMwo#u7,zj>ԉ]Q %E-q+$e4 hP 7_ k _-dگ3qF#bE#j aaa[akvbVzfOk~/ز.eCʷW;|nxg()/(>;iz&d.r._{Yp*q ~'vUS)1Уq5į?L^v|b'vy`kk2H' dGmGk e.$F\u! j n|?{[P__Y:d]T{rzgt+fCC Jcj݌cKA{P0 1 & = {2^JbғA{F+߫4W w\.8?3k(RJʊ zsr\9P]UAL( jZ.xL<*}e_ҎuR@Bg05NPTI80a6j{ D\5wb"dP$2Ak0qzLem@xǩ hR3)1~t?<ıZdtZ)e%n REH-Nk5X)R@ޖ_OAм/(*3i `PTWd,Š|lqDA]nTrQ9 2Q&ɫ EC&Џ \mr\-8b7Vƽ%T RjW jm (וkJ"  AdLdE6KUe^e00 WVZZ/O' rC?:N'&lʽ'A?(4̆RCp+\5TF~b^|A:JWP&(A}EP 12FƂR+e*>_2mI8e,([-&Юk7[kZ(9Jц O6^M]m$yU^/sIDMQSES@s579 bq:i]M)K@100WvVK aRϪA| RC!xZ=-Rkr-Gߋ d&N[}|&6e i,G|Pkj yr|{3%B@;AUӊ ˻rd:A6qZ:'l@v~A OZ-V9Fl_gEG 4[*7U~D-Q 7p#c?5~yZYzNt( nVVw*~SǾY2\ Au G)#z95bw+K[ay'p0"i@hKk:Mr_5X,'" ϹE:O=f*s yNܫO쭭fwUVRAaL8{u:NyP„H)^Y[lN*]Q ߐ ԣ\s*|1rB $pa28uz{;:-u<1р/|@L◊^//[^0XlGSHÝpY~(XVsmp9TvA[~N?&;6i9R/(hFKPUf`2Sy{~聯Nt.p3d021#=X0= X,~TCS!mCp@)Nӝ.αHR|^$>e 0aX(Q:@餒ȢGo`x0 5 Ad'j+دا$KϮ\O7ꞥ_@tw:e.qDYAv l2ADE$Lފgr{9?GW-'σcW}3'ۃQ8mtql`=뫻:N~S8 )r<4[ֱ3k┸"Po&,=:eJzͧ!z4pobz 6I"67B923D+MW,,OJ=kOx=j\+X  R}FwoL@5.e"Ce~8l895nu^t:o76ikW%eUfZ]b K56JrN9 sj5.Ğq8*"*f'C_4}szH-^(SJS5AlA+k@}9E۶>vwëJtJ (@W }lS6 el Z -aP|Y{}}% VIuwW,; {g{m(ߵu[몍9AKu7^t:o~Jަ?h2m&W[ Xaϧ@͠_Y&ӏ ?tCI+0}2" 2Z@w.6{jX6X,>Q'Q\yD8Wh|-yTomM,su'0rPWb 8Y\ jiMp`6ƁzQV·P7 unƖq/le"Y%Vw/E=K@DPIX; (V 2PvnB&P#:NzP_j:/R|8-ϔka}MBtAV wIE IÀAEP  Ae4(c V3^4͖K@sRՙj1NWH7=QBA<+m{{G2M..!ǠCe_m,p|ˈs97 `1 L4;ʮ $A>&+ȩ_;Z}=a£gW!PNJO $AgXD4A6\$V k$t%ݥ\mGO- - i߁lK[`!/ d\"JL&i@^t:) HuM'Gh3DTs;: ;Ja-hOWk@/iMA! u9S n߆∢7ɥ;([ OL@<^xBe'fZڀ)%|F4 r3[f JJ!(+;/WA$_T˓ =Wv1DsZif$#5OwOșr4r_ѕr /jcHPeu d0+)TvX)kYu7^t:o\HM+y棐jvQ,ƈ@'Bj(82g%ӜU $(UD{ PUb0~kH9`Hso sMt3TL/f68$)@,{eT]x,@YX\jD2kVE?McQExX?XA^fz{4`qTݝRE%p lD4#ط#AzX [ nNt )IDATT_Or/ȵr</g|Nrz_Igž&Oh,b.3L.yS[cm`t(7׏﷾֣oQ}6m().yK (t7pxW'f}EG?oLoĈKM^tu:N`A/@&q 촖G 6%D'y3-_@{\-퇯7n~ÌGsx"; {πLD?8ս_dܯﰺk#.*Q:N݃!xA@xuF9be8Hܪst:NtLx'ft:NX`u6K[ƌ3 sut:N=TQċbxb% ߬jm <>*侇o^Nt:ݿxUUUw]u58k0H3V^Nt:[>䃼 ˳ 31ĂrǃN`61RW,ܥWt:NbŊ1N< 2U;`~|jB}dm,`m]m UU"AH,qNt:E<'FP'k hH5/𹱷A/ơb0YM+ځh`Nt:? w"O0q $YLR/٧-9xQO d $$qwi;TZڲln-\vX]VyuZt:NRhS_ LvP;Pfh`&G4e Foj-T ?/(9s;$Evan|~'hPq; g@S;6{Nt:**`; rs "^-((xC|>܂Ӗ7yqRJ)aO&S׍~3/8VܔH|EkNt:pCc޹3maV"ЈH'zTXtCREATORxKM-HK-N.JM+/b3ğIENDB`sqlkit-0.9.5/doc/html/_images/mask-demo.png0000644000175000017500000006661711530470276020117 0ustar sandrosandroPNG  IHDR<:sRGBbKGD pHYs  tIME < IDATxw\V׹7\(8\HJ-[feٰoۆZ6 iSm5g.P9T~<׹}s3QM/'zB!Jk6BQmy泍$u]jF!U(?<IcjB!Dug*u!*s#qG!U62 !ү/pXw<"QY<7oͻk ԫ~WVM 2 ?x2W22 J w~|V"R~cv17 /WopyzjiuM:]+u;7&sKw`s㸲[5ѺWs#O?^].xTWieqwrpPEYq\u,_%_/;3sN]0a) R8[MZ1TQԲy!%9NuT0*GO_rY\.EL0z2^r81\bέ\)ӆ93#_|M\1\u9 }t_JZNdAs[\ɨwȦ?k[" q,1nKDA4MTriՖu'.Lӟcwnzf ѸNϳyYy(Nx{msfAo/s%:7'sUD.3S+&q@DT[Zw7egoG"f|';?_7rϭcomN+*~I8G9ڗAd.-\q{ynOW^i#7}} x्iyˡP{%mAu2|̼k_~?o&\6c[ >.o#KLdN٫7ct+;1sߡ`;x>+K6k{ ^31 ;5۶xz>9q+x)*Kg'\1}cL)jgo;ൟd.sk+{?_l_e֕|2JQr_|z$Ӂ1Og2>H:ձOM9e&FNK>X"t73{{Qb߈"lr]D=r}80;b ZI'%8^:0rL&Ŝng>/gk?'{v7_mώ{_,֚ty$}™t_4Y;|#y[Vηyr G)Ӆ%|ׁ ƈS,kzeAˠo<3.qVYA@w* jHg[|5Jg`Y#CY], ab_|j'rkзp9Kp3jn'MՁtѧ:sAi˩I`s)rOx{ gzȾz{DZnć1j7?26"|p]Aq@sr*v+~ t64 2^9 0W&%ǙTZ^:S|y|eP+$3GS}ϴ缯=xmR׾BTsui3tJi:*>b?iҒ޳1oޙ( ~n!M. zV2Ie,8pʧ o]\`|dg)M*Ng.x|!l@3j[0,]j䓅[IvIfx\:oy3\,_޲O>L%ƛ]1|" vʪYk_eAeot+&Վ>?m{2vN_/2wxoۯ;m=O3KG၀۷;m|MÈgy݇3M7? f8ҍtB/`zZ"yW~a1uDlb3o&nů2OZJ`~5H[w{в9_mԘQ/MOЫCh{y\-_޲aѼx:K3oq`1퉝c~.gݔZβ^2 >M_~#i)#y9%B*j-8OzX*BB!GB!}TBQ!BT#-q,[B={yxT1L%?,ϤIϻkg{jx^y9pG?Bv'|h W !f~u)/''qX/3+Y|\~!n"9$~~v=r/\ew9 LJruUb]u4MCuq]f|=?k;wsv^ïViѷhJ`@͚*53f~D;!^uW EDrrקIHn_{!nP{[^4Mf-|FAQCcc9q"$>izjꚎ+Gut@U5RN&ѲEKv#?/ktIuxx٧q0gd3f~iݦ7t v˗/g")) ፗ^shMusunıh)yVF ł|8ԬQ UUiݪ5!!>>tЉ#GqԫWHD4nܘ222jRn] XNzίwhQR?  QP5Ĥ\bwr9/i+$< uRue80Nj66rh+4"-=Bc6ps~kU[II躎}`tl66 + ;. t,v6rE[.4r0͠C`P c VYƭz/N0 ? 7*2l/.K4 o(\.* 6BUUrs1JaϾ}dgvwBN૟vP', {/kp2%28yU` !1֭䙱ͨU&fV* ;t0 $%@/*yfw*4J8r8<Ҙ6P (h4EƭQ¯#i9bMUuq yE4Iɰӑϙr8}4bZpU4ՍD&!v;@\.޹Mî+٣+GsjԨ(rCRI230MLLd18dgɲm$kЪ~mT]U'd&vPv.aVQh=a'2%ֻd;X_8 faGuutt NzzIOOZ^%UOXPAW 4pfdzl.?Buzk^~.yKgźf+[lvcYa'Y zmz. 8k%~#?ĜrR)z!$0W hJxhʣttؑDn7Yل=`Vq:lCNV&&q:G-QQTH>]@q:Z}C_<36_ ;D)t K(x<p wM/,W^^-cǎe\h!11y!niێlVͫ[WUs^?n5:FZ 0Fݿ}0|]cQW+;n6oSN$&A;vli֐VQ_hѢtlM^ɩ<V+n 2s\MHJ@USL_v% ;9vcPZpWh;|45'? pxP@9rgd)| <#S1%9;:ÇmsPwl GSOU|qX]0zIPU]1jVW3s^ aG)-\3=F)z >*1{.@(q/*(p BLJr*9yK apfc#'3#Ӆ. SFh -QL>䃱NEݷ =+Ҏ]TuG~G@@A5[ %M]V~(2^ da Cs#Vq# /4?wIJz NJ) wV .ȴT0s#Mu_Z4=<Jkʐߕe0l,MWa2ͨYtg !$xB[x.] ?̝Nt F`@lbPtU3p)Jj*(T9(zz"V`8yCH IC8sݹ;l[5*J)1 bɿl.:W& 0?y.WOI9`V\}f mm9b00I֛}{6`ņC,ݩ^~]S9K&VݓГj`x^= nPhZ%k:s0"q,֢1* v i]I[3:F]qi* II^1k¤jŞx.WQ' sQOˁ -n ?1uk뷞.{0#h /89Rӵe}vFF:~~~LKL5K[jZ0( Jŀb i;ruN^ԹD =i^>?[R{HBon#iҕ$<&ZzW0=}(Ƕ(fOŽc飞s='6F~Etb( jaϿҕ &E 23N%~_dѤ ];EB``6=MQl6!!!e[YwNgv7&6?f_]Spөs*Űv IDATufJ =*ʑc2$!v)6#]S J =a({~#I'(7XW{YZHڵ1Lc1wc[bAvvNR _:\ѣ=uNݎt*ݟF>Lۖqc#$pZ͕@-S"i>͚4&$$? =R&]@"#[b2#{޻G7}${5kg?H9u+]ݺvoޗWgB1<Zh?-]u4s#b82$[uht ·54 W !xgYE$?A>yxbf_9m /F+H)096'/}r=O=ݙn kٟ?^iǴ[طk s$_p d핱lO {SQMcqVPk+;+?15y !Uܝ;C)L9ߚ9~v3+Ҙ+q *j4gғоvn嫥&h<X }e~K Ŕ#jr;o, @[Q42q ʂ]~Gnwnoɋ]! amz+w׿R|]MN͆fTNmg_{RpdҘhTl\˩E`K4Sn,\f+&ͅguN4-/َmk)Et#SzB\*ec]ؙ .7o>>P`1ɪؿC_"O/ E) }O7.d{e'V9-z4vdB'y !|B`׻_(D [7mggu:ż>bX;]\b5YI%7a[9o 5{l-Y"B!*tK3F͢V9q0 z|;ԾZDzZ\g5_#;Ң},KsrザG}x:Ϧ?Сx̄/v)}: 6wzom׊^_EԽ]4@+iB!Pz覯]D*/r>ԫ[S*^*!cqxP!՟!BHB!#*hDަFQ*B!$ʠN2"ĉT B <2٧?7'.. UqqIs = Bj$UP5oF߁INڌ#.hNp= äBBl!mGv2ˉ唊B B <s\n!E>>>R BH&wR!Htދ&ZHg͊0Іa r!՟8ڕ`}*jڕ4Je!ԯ߀kn9\}x֗BqYj֘Ąr/RUUB\F?:UB!x2W0h]J_5`Qz-/BHU*!x.#zq+g}iIxd/?5ɮy'y4$yoO[ʉ7@.k8Ux#H!Q(;_f{}s_0gck+t,8'5]!U:'`d|{R={|ϻ>EN2^3ȈH#uī,;K'10:stG{H#3fVҴkl;k:EIԠ3 3oOk`Ւ!O""wbq~]x>OCP_|C{1^z\^]o{TЂsdW cuj)(9ƕGGAQiqt6jB!8co(q\ <tgo[ػa7denA^iǴ[طk s$_p d흹WbVrq/m8SBԷpߦ] M1g9_jCYCbhR`.1]}0goyyL^F.o!x(v:l.$ۙ󭙑wޏYKԴ17&giΤ'o}my{j;^O~Xkdů MD1eH:a;i'BWN֎bwt̀ e!V4ŕC+tǔ*Pv>Z`À0;֠V_=BQ\GKXoeƪ:_WӫfS>ٗ?gŞA+Y` ++c9l9G?uE7SVL .`{$a|sk~zb7;I jtf;V@Y+)W <["^AZ!h{d)Y!xN>̀ڙ4hcT*~GTdŇOc~FDB!ą_Ϡں+nՍKMө; QwRN^n Fj׭Kמ}ؼnME7wBxYsCdTEQ0 lҚX[՗І0AU5GjX,VzPauQP~jڠB.t~ fPMSiب1F~ F@FxP cEAY\3WwBSH|;TP$].[7$I.ꆄxBqB!^+eI1ͨZ)ꢠ,B!D !j3n]Е9q4KKY.{_c8o3_ޖɌQW3~UO~'mI=ev3+wrpLϯ#]/=yy>Zb ~,7Et^t)>+<3)?|ۮKt^t37^Wݥnz/fjilEt>{Ep`ۓW/ d>g{hgX^Dw{UNWτҋ+̵IdnRlUw _CĒW<zε>9G!Duh)/>7^ Xj1iϏ{qOn".OܿĘiތaN;4U=csݾ('6Jao8?]\pgojC.fӪ1$#ش;>̬i[ΜX~8ʹo~e◹6e6lUESs)=~d{ffq\'_?׽E+{Z5s&rdgsaV>ݙ[KW-Q=^>L/O1uQjGÿ4穧M+,B!D ousE!RX(^G-iJ %?[V/c5׻,OEV嫹/UQ?P88 NwѲjie,_:W*n@OqƓSR?|Q J9ٞu ֏xa<98 $|J?NՅg\( :{c3}Y1K_BQ\Igt(f-܆MG4S?Gk̙^gCsy_|ShKthwvZahRҾf,_-S.ܤ|'˥|˟k8].{X"r_En7hNl9s+_66 {z)[*ї4%9+,Z{ymʨAcy`}vPU!jv#A:NSm`Yr3w N>W:V1Yqwŧ<'MDS}Kۘ!ۘzˇ$8}p7o?EDss(CҞawMS"z^'ٍ^{nD)sk}yduR/o|M9*S]ir@BxUynyr_iC/L/BTs9Y7|]Z5 !{rsd9E!՞!BHH&[ !b\Ca%=Wf܄{L9BB!(Uvv6y*xxhmNaGZxBUiOu R%BH~!!s#:fYf oB!/!BxoxΙ!A˼uD$0ŗ]t z$<gZ FNNGD5fBGIx,ȉ]kcIxDxe'ݥ#BTSV)+ ļvdļس zp<;-ޘ;^@zA˂=혶x vad{ ;˹|~wphߧ\ZqYnl[‹xz R4ބBQϹBOyNW1mV=p[y{Gw>w=a hƐG&y{rbԑtb w"O\ާ9- c֒w85-FL YƁ7!BTKg̞=̰S|8i oms4KԶlJ%>_^Ӗ@Jџ:vc$B- *S>ٗ?gŞŖRބBQ=O3bROP.MaH0ơyB3)A4 ϧ f>{Eel+Jq;|̏dԱ[0$? !Bkֵ ZSiM}3/ٖ^؂#[_b٬9ÕKxﱗPG}]\?vlj-̛6۳oB!ށǧ?:?>0{4-NnIe9o^;-:{җ~ ʳ X޷M篥MT[Zy_M!J>-ݱO_MBq9~OKo,aOܓ[$`W#-fs$'T8hvp]:wEf-JLۿo֭YɤDTw^?FuC  T9ޏROum+GYb) &aF[ IDATQ*EETNg_Zmo@|\,~ao%iFwM6ASwSIejڕ4Je_um2r,+MqfsH-鶑nq~~~rTyޏRO' <֗ 1ɉ '&A%^$&#++K((TzRG49^ *X܌o㢪, 3ZlH.eT.Yjn/KM24{*m}4++r'\L3A~ :(g˹~{玦}ZMUeۀ>!t51)V7/z>| ηx}퇏D"z+GZGEAQJ0{;G((`{)RUXMEI|9+!*EBIk8_\_ºVQPIu:|옴6491im\gRI#85/$ 'OUD[ʸ%qq'#X0ZFD8 G-`S`D1~=:D8,OVQ4M'yGs2Faj93JC1kSh= &[_-;JWCynQ}E u 7.g4ǯ}yHSݵj4F{6PFOFt2 @l,He{qdn|MJa҇iz{rao@zi!3OOmͯ>/KɀxzX^/ؘd,rs׾ܕpO̝2X3rlv-"%5sP#ZƇÜ9{q v?sX2NyM#51D&rbE}P)&:<0+pЖe_0j|[gW%NˌPpfWYԦw`ꂚ]8ߖ1s'fW4Бd;KrJnkk&:}՞f'm4n[W7 #*~o2 ʀf'V9Ud ?.'$bdԘ u*-|})W284یi yW,G6*!A:~=k1֢iSi7 GV=nJ;{/O\HxT:4|* Mõpl :ٲ_"FKgxy'3~ppqR؉c=L-cǏ张>q9bInWs$,]G%5Or=^Y<՘5U$<^f`X\V=ye9I*Lx\vv:_? *Rvќٗ[lhSߧXlρgpwLn㇪h$ޙSdEJK?ЄwjRY<8wjpV}fZׅ]}ܕodD˝<!v_o6 ( ZNJB3Ptzz=_фJGW-K|y14eXp3z㙨8콌5W=vvm23R=~ f3ŅX=/m _b” BČl opޮOxg&2̙5^W|͆ ݛs:E!'7jW  dPuLןͥ³LxqklJLqOӥe雯2y _v< ȗx8Gx>Q5'2jh4JMbaBCRTJqx3*5-" >^N}Bq3D[*U鏯h]`KE!/ϊVl|v]%`X/O!$B\uyoC:E! OUXqj5N<0CxᰣV5j6jMՌgv;jM͟&q+ǣGt%] vt$Co cI\*YK"$^%TBTM:<'Ix˶M;>C\TuWҭgߢV֭ p9^Y6'bݚ/ԵF_y)SG'J!Z5TA#zǏ~MTRУkШ¹_-fSO{gQkҭgB` I8x~$h}hٺ=-[ۊ;ykЈ+'6 SO-Ix,\,鈪5î>l6vU*jժ}bCNVWd%$$ᩁZ-ǒv2O%˝P>(.i4TY|Avl!T/tه-Z|ZuH%D]HS 8MQA#yer:<̶E׍iw z7MySG#'v &Mb7n3!VIxj떁$%'((`o#EX_lAӈ gݰ#F_ j44d= _3Sd`&U"=5#~0SS~OM9Na\U^0RSsфGg\UJmFΖVIxj!ū5vG>DV=/H)O#咁U7` -8+} -}}O3يuӮiX`tjUm+nObE$jXj#DM48pB捱 S!9l#IWbB2!fF]"d*3Po"+VDQ.~sB}A}궕BT?Iv #DZ)Ydԕ ۤ5-| ss}t ؘb+UutZ4.prv[߭zEAo˘wpYN}fwE]Gz4WQP_?V̽SUjYSC#xLmdقGa<{W/d(g-y(wA}«ϕY$N ! 5{__VVflփId'Dl?1ݣvgY1|wa[q'~NiapS`ٿ \ߣkI̶5=gw׾ܔC&{G^柼c7.<;1Ihk>3{T$:~|L>vLZ~|j霘6uN3d( ҇uYURu':hi=xKfn1s%~Ul┛ÁBk!IeGsoh԰?-OH2Faj93JC vM6Lo3{ FIG>L~UzSݵ<(2n`iSn2V8w[zxrȳZr+rK–lfMvN6NsX9{i].\kPNy@ofp Dyok;m!n^1."6ߓd<+o=UbW@HsU9OD(qK~LNG6`-qD:Z4{cznuqDP ?hс&.cOMe0.y]8"ț⍟ іQ8K^F˙*5_ӧO} I>mIO9V"|y.o°(3`F>Nk?E#bKlN/CT}wמr}7lH ۹D~XwrϡRJv:|G?AmCq Gh 5# Hn[Xq9ӆwI=>~cDFŃ\|9M_[s2%]Ѫ qOf%kd\=LՌ|w8α__x${;O< $̎fƐa˪K\+v PTo<.q'>QϘ8gLC|WDܟ[hx6[g;s++~X]А7WT叶 f]q $z8V;c Az A?sVYV׈VS>1K}#ytoGw=퇏1̸,mY w+g'XuJmA;idO`( 7_?j=Oe |y mףFƾӿ-cO͠(YW:S3lgINI`mmyߕd{"` EC5d*73>j e$|xϡU$UfY@HCE;Tmz0߀cܧCL~]bJFfL6Gv֧ehkgi2Ƈ}V{Y9mGܴI{-ׇ%/^_l_]J$|ZEAc4QT & ???-& Zƿ?ƂlL}W,Q(.(h71"c8l)0RKǎoرC{k6Ny6O‡<|z֮$t ǁJ0D0w.+}ž<2`Ŝ$&<.;V ~y)h+\=K|xG2|Y,Ē9\67jig}05CMdoe[8};&CU| s.CC+!0vg^X1.ھӃܔx8|[pZ󧈺+JEAբQTtzjN^Gk0b4PT| 2?U\ )8iW%o.i6rNӗs{'%%߹B?WoZSno헞BoM[L~iFw{O=zJ?d;h= p=wa

abP_؞+ѼktRF| ]Nxl:mwmzO#Z w-)GA˗M̗:MWyX^̀W.o_.ھ]\!|U1..2339w.|RRN]J=j!X*I>T u ̖n Pެ̠Zyo2 C-` ׽ شp@m\60ep8- ݀ch-p&& ˣ%j4Sj@~0ԉ)?36DU@үǁSzt/dfz7f AT*/| S'44 3Ȼth_c~] D&=39K7 s"*5jĤÄEH=BfUj~7nlbWҧ93 pk&^Ͽ8_bڕSBT2~e雯2y ߗv< ȗxhW}~۳Fu_ PhZ@fg׮3?9JjʷGWiV@q$jIoC:&rB! OVq:^pQg`5v jݎZ#3>KN/ QdY# IDAT5!KxSSǏ%*Ӂ٦6_&Pt"fw~)7z$<5FCiٺtmmE7oђz $KprRSw$%' st D5JSCOʢjF9q\]sfMQR[oFa205( r2ؐ5?J!'!SZB!G!B!BIxB!$B!G!B!BIxB! B!$ӹSG>CԩÆ 8{lˆ SΥ';B!W#)LzZcǎeǎfǎ;>qO7-9KZ.!>yb74$<cGqU?w=*f鑥reȈd ֥FØYѭ] |AB[GRHC>K_n12nX ^ =,/ݺ KxG8u|hLa{An"zBqTu% &W.PShuh6e_g䄹6E5'P۳rYSXdJ'\B>ѬD{>͸μL;@'-uSj}ay n_2sA^K߄BIx.LzF;joPP, 6(H2H\@Hz~96^X|=tsƷ.mIMީxNB dn-Xԧhx77'UW uB!j+Ν:r=Ui,CzἏٓG^,Iy[2rg,lnb3s9ɚJἏ1B`}aK;=`GL OKr. ?B|45A B#C`wxKҖhn\ jA݅-c*tN㥮R37ɽҢHAu&:N']Ң#l;`$7ێgf"iղ57 |_a@!Dͧٵ_HMo,b5H !e雯2y _v< ȗ !擄G!!BH#B! B!$:rqrrpĚyMpptR6oƢE\\.\.NgA1<󴊊X#%-G&,yhщO~NmgaoE0uuKuvݎ΄z%9Ds=-7!Aj)ٳ.1lyyyEX#DuOxrPG^wn;3`+{1y%?ńmop׸r=*".;8y\\N.Q0*FfBjryl^\}[`)L[EztMQ] _#F1DgQc&XXȸc2`0l,ڵ~~TE,ے'f$:Ԉ>#deOފ5nEq-$<>E庠\oIF]&;.hGQcHI]աqF.ّ!'?Ü1_hjQpS6Iy%<΋';2#D j4<[qBI' fC7'K#DMHxɼ?d䑗>N" !!=sXJv瑗;gLNA,o=hr:@/TIϷcN͇/.كz)H#DMJxӎnX(yF8m/#l~e:`F]Ov "!5}B6ZAs5HwɎ$77x&;g}ÇJh,}U&OYkҎ'1ay'j T%Opp9:91@VK !.Fl6=x0)8Nrrr$QK3o"ZBM^QE:MQi}z(SbxWϾw՛ԴSn+BHptnۛVQk Ok5Mpp|g FkD(!BH#B! B!$ZOmm?ZkcUnE@MB’B'~ I ! ܹ{g}sH"   t|   p%џcUEemU0~UE0Nj1.@ UQZ=O75nDeH+|gE:^eKK}̰2|:TW[f#⻣@^* )9>(m|fΥʞq#$әtUUQUW$IH,)zs&IM`$Iב޻﹪1S'y-v[qWʇQPfEU#o/UU i4. =5YlgE$Hv;p&w$#q@(( UEއ:h$8(шN՜,X- *4<⡱É:}Uy_(:NHLg0( JALD.Rk%٦*F+9dYgD#!iE "Kr/].'No <}A|y׼M}TUr:]Ӹ.AIYk޻vz37 ӂz^"Z5/[kw[=tO6{A ?w =-n;RӍAPR7CP<l E"I:C A.h7R \TB#zuqSv[.0czAAQNtmyAA芈>:Q e jAAhah> JAԚM BF{! HgzR:\/m)AJ'q3 pQ,n@=QՕ,IsUUt•" t/H7zϮckٜԁEA.RA ?$r8r(" T$IwdEll ,У WeeH$Pw믲oI|\eYAHIn9RAPAbi *X${Ix\+^f3 )N^HKK{w/IrBBC75VѠf?{z_N<zO]=Gg$͆քA$ ut$ 8w^;LJeUeeeLG,_ yD >c aGO[o(A1n 2:Y>ǐ x|:Nme+AUUz}_UUEAUUdY,wO/\.$Im󣤤`jjOJmjFQu:t=nc!KRwn7!ux|ABgSop800av[].7GשNbx|yPs( . Y}`bv**~FA/3(xl˲s(JˉAo *:(>3vAA NgO%t.'ֺ*fw z= aP.G/`ǎ$''3}t2beQ MUUʾ}Φ뮻cǶyvؼy3dff-;4mϳ`@@ۿu\=zM6qIӹ曻8N֮]ˊ+ vČ3HIIR^ŋSTTD^6lX]__Gvv6v"((q1h λ/edYt~zF>*FرcիW_ͮ]HOOgԩ$%%g-[ƩSx:th΃pBRRR[HKKC$dΏZshsYX^ o'Xx1uuu2~x;]z56m"223f9Ga̚5!CzKNR 88I&qP]]͛nr`̙~]UU,_R&NȬYZ5k֐ǠA?~< V++Vp 9Nx)Kt;tpiL4 YW  CA.]JP+4YezwHLL_~-.T:G}Ŋvjߎطoɓ|$wg{7oǎcԄu$I455k.֭[j{NJ+d׮]st:}[nqi=Ćؗϕ2$QAat!0 &𵅼9F$q/\JUmկGx엖3b{yy9k֬|撜LBBBUHHse|ϧ5QFsA]eFo… [];].3m裏2m4_OFƎK޽3c nF#V"''rbcc;[:k,۪l'|h$##ة@'!!^{O?%K$H8p {&))$}ҿ[lwٽ{7ӻwo;Cw' g< Ó{f00`|߿qu͕[tjkkٻw/ddd wo>|/ b…D)±c%DEǞl^뽙0aaaaP__mbСC$&&vŵb뮻!CP[[˧~J~~>]*l63vX\.EEE(3z=YfG??nLR[:;=2t.gx0`F۷@UUѝ!tvMMMl޼_Mqq1Vb„ ƞsO>Ȳjѣ9rS6/: Ƅ (++R\.Wʖ$P<v;2>bIAAѥ"ǏS poZ\rrr;v,#G(PZZFSYYh4ҷo_(// ՖJvލfj$IjBIUUϒ%K?煳d"&&2YVnwf-,))nUmUuu5)))=#GR__OEEEf5js!==."x<NownQpp0]wR԰}v|kօ`޼y455? ^(І|w޽MII z뉌``oDJjڻ #99<>1DDDtKm6DFFvPҠ XnG8vn;v ^O]]111H+ Enn.Ww}T&NHHH#F` غuiZ&==SNKII 6mbpwt{@UU9|@^^L:P-S4ulڴÇqM7u8ȑ#^"0~x&Nh)$'OMTTTK;wd͚5cX0͌1h^ d\{DEEu\np|gٳ-ZĴi|>r$QWW`ر.ۛUc͚53h &L@PP3f`ҥ|'444:5f͖-[|#NJ8R߯:յ, p\p9tE% LKcڵ_dʕu]Zl555\.ԩSJ{>~~~AUUSwM,Fyy9viӦuP>omhVpp0^pRRR(((v”)SZ\J~~@EFyM6n&$̘ѣ,;((tٿ?&QFu)bs 2=z4`gΜ9]@dd$Gxƌ{;KQT_3fPXX5\Ñ#Ga 6S 8<55581LTUUvl1n8)..VUU_hrr2. BX-6{/r{Vm1ی ***mONNl6 ..AT]]N騨pZr :.bf3ƍۍl&::ǏSVV'Nd̘1.Vłdd2QQQ;dffi&_vvYQ*++Ae|UU[)1ݚmTAz^PWUnT9Rt??DE`h4RSS^;͘ԩSBƳ#:kb!((թ( vY%3v;@:/_ή]98:szH^cb<\sC`A@HBC.z;z*&@婓Vr21}1wpֹΓ8v ,n#4Z;,1{o X[NʨҫW/n,])ro\w~DEE!!!]M,cX66Zs3:pPZZ(XX-L=25R]ScZ hԩ ֯[Ka|_fFm?|,kCTq^ Yӽ5z\~KHH߯cQ:UyVNenncj4R] \X`YYY\#w(Nl IDAT v+XŚ/=LQ"C;FqP=)/DAjL*KU!WC=߆^ݻ •jԩ].cv;p-he˖f~ӟw<C_]ީ<|׌={oɽ{t:]ݻw~z=^[[K~~>=z3gI`` uuuֶXgЛ92{رc;voP!d2ά(JuvB$ILsgSU<m.b6e3{B^[d$Hppps9E-=w+ipƞ #/AG?رc?_|9Ǐg9'|{o |Ax ;ƒ%Kg7$66ŋ[o_/b2:cUWW?̔)S2e۷m6nvF:lʕ+>}:z޷f?zÁ`RPP_|biUwΒ%Ko|bVoeCή8 i^wOi7ڿo;uO^ H\utq9#.uAI'$]f'˲o/Nff&dffr-K/1iҤeL0Ç~g|Ȕp7vy $??v?ÇSUUŮ]رcy͛73o<***| gҤIEEW^_8vK::9s搒֭[xɓ'?ѣGfT:z̙Ç~ȑ#GHHHhQwOj7zx Оn]Q \rF\.n7lFE'--b enW^o1i37n'iii̝;NǦMmw,L6ӧ3vsGMM >,<# 2ķ"HNN=^\\LCC8p8L&cь=x_~el6VZŲeHIIa޼y-.**b o0fZshUwOj?Ĩέz/ ):&R7AxKQQ\222m`455Eqqq8~8ǏgƌV=P!l 2m۶1k,jjj8t&,Z |I#̙c=?p N8#;ݻf&L5=z4޽ bcc!//78̙Ӫ\V\``L:׎ѣGSTTDnn/+m^x_W8^~e !##,~i$IjD;x<\.o&/ت0BCC},_|yyרodTTTw߱tRBCC;;[{nvE@@;vv̟?|^}U$Ibȑ̛7믿VR*++[ݓt;KAAl6@DDzDNzdYfРAš*~37'11)SR{K4enV$))3j(~'&&0N~Ϗsw0rH)))aȐ!-2TWW3~xsHbb"ЧOZRSS}s9s&$$$v ko_~Opp0̟?*f}S222HOOop뭷x;EIrr춀jsj%77M7!dE!33~a0Zs lۺ EQ !..UU}p)3fo˅Hk[ee%YYY3e_(-춉 •NQ m0  _/^ ІYVv؁fl6Ldd$:tPILL$,,|l6&L 00=JEE111$%%Yt)o6)))dffMdd$TTT@bb"111ѹk.J0F"##q455*MMMTTT*6  ΁6g {{6~-III0vX\.TTT`0%:&Z$A*"A肭[/Dll,7t3f 00<^~ebcc)--eӇoz^x ޽{O(((@UU cy)//rsqwǒ%K8x &koqtSetzz}2uT8:DVVVF-ß=zş-jb28q"lNYiMMM4662vX;ҥK)))ߟ4N/+" =OA@.t:P]]jEQpbJKKϰll߾[t: N:ʕ+ٶmz|AXM6a())᫯b۶mmv:j(S^^l&((jmƮ]| 5NPTVVQC}v0 z n֬YÆ $ Ɔ (.. AA|8_[?F?~<;l/%%>?8q3gzj|CfΜ /o[&LhK\\ӦMO>n`͚5\. @||<{ocQUÇcJNN?~. EQ|{+nmŋ}ss$oOB$=ʩS@#Inw(_~fv;N% x:ڿ?˗/d2!IV/lqL dee  0 ;^AAl J W_}ٳ㣏>`0@dd$O<۶m?fÆ L:EZ:!o| ˖-c޼yőjv<&!Cp 7зo_N' EcIoTb ƎKll,Ǘ<Û%B3s/_b!==r$ EQPUJ]]/h2y睄p88pXXWA| m0`&Mo?g?˖-###'t:Yl k׮PBBB|i˽nwޔኢ 2vKgQSSCmm-lܸ_1|p999lذwf…-GM6>}O~~>ۗ)++_d֭(ÇYbncZ1 l۶E 7܀f#??<8 A.%_~ɴi0׏g}s6UAjZ;_!:{ `NB]~A=OLL {ɓ̜9z4$I"66}w^ }ǠAe^z` 88$_&)Sn'993f2o<|)((`Æ jkPu׳~z^}*C a֬YӇH&Mѣ1 8P߰Yfa2eQFѧOꈎfQPTT,z뭌7)))`0(p),[,?P^^ζmۨOS2d po>t:=/`H:w5IJ2HKKV \&jjj())bccIJJjݻw@XX ( 唔0rHjjjcİm6(9ɱcǨ&&&NGuu5ÇGUU9|0ЫW/)// !!AO=޼ռCz=7t!!!rQUUnߟ2%*:  fՊ$IDFFȩS'55$.++CUU"##b`Z `c _N'Ok$33)UUDDF\p]!(A. ~z|ANf2I;嗿%d2a裏?ABB-l6.nSO b@ H@޾};?0{%<<{y|/ fcĈ,XZn`̟?Ea&//ok'o`SOmu%عs'|'77'EQZZҥKijjg7g>ؼyg[v- >Ç7z~A UDdAh[pp0ӧO'##rssٴiǻ{ȲKq ֯_OUU6;w^Զϛ7;wo>Z$9طo_TU_~m999ȲLSSS7n?';;iӦ >\miAA͛xsQQQ^1cưe^}UHNN=<%<гms`ې8zv$Lu@ƭi Ƀ\,5䯆b};~FvpݖS?^RjyTNѨݥA}%l&88Á(0j(f̘Osτ HKKCeBCCt}X0 cZ4it:bccIHHDe0LL<nH㉉$&&Fuu52}t+˻fo߾ 6AHJJ %%%X,RRRZpL& 1yd_omnrY\)7t)WX\y\ErG?$]u HmdR/smoKc 6~ml,O^o`O޾v޿}BўgG£ 0?1<]=G ޅj` 7n+Z#6j?.ښ, BCիH~ \f ?us[e\U iO)A)8Rݠm;Q_!"nCTWo i-`:'ez^n% &бos9n'v(٭^ &j>%h׏~ؾ7D빺kJ`SPW!1ZۃӳW}L/ۧ/ym[]v\PYsA3Ϡl4/ze_P^|Ӄaϡ` T/WjZiHp\<;i5.:< >O ^3wj 5v>k`b&ggP 7 l!Y`Gim;Hڿ˘"xTA+S \*aQ_,/KfxK3},8j+UUvM'k߬iAkᾷa3>jP:@ zGw;tzZ`gEX(ޡm[O-*?y;n[v>o7hC:+Z]3ly~}aaSAn%zzv}#nӆi #o׶~o8z@!޻ߙ9>)0Z]R{qCAQw)X5t-oyIdd8< IDATO NNS-ߠOZʍZ9i9^&]˦ [ O`oٳL  ݲ>OGz=utk6O*2E{7dֹRK464  W+ GÙ sҒ]m_ ?sZJ궷p=uYk<%8xb*mq;MqpS0A-BGpZOcOQspǒXshzv  5oעt6_4[ v.z Zm 3vmMSVޝGUއg4wy ۀ &,e1|MB$]4i~_$-I$!iI{`bƀɖeɲ}Is HX6&xş9:^g>y&0z@4'=,K?άMsF:ϝ4ܖXڔF69DqkO };҅4ZN"OgMW2ؖμo?> 7>\& X: C1wuH^|C!grLgSa*59K!B\t. p6O~/=gj5&x6hސS3O{ô =.*lag?b3̒w )1!҅ }-\X4zSܜ0aһ̯zwM[,[9Kk.K}*o2}oW]{{+n R&)](Xާ/Nв6U+,'N@q 70gΜSrqq ;,B\Eڳg/'ۿ?O<).7b8p7=z뭷x뭷Ν;y ֦]͛@,#ꫯytP(EQ~R#BV~S)B("LjBk999Xڵi_)PV^e|(---|39}߶m\s͌F nb -ZDNNǎ㩧b͚5s=c=Ƒ#G;+}AT8fttdR !ޥ( hOwMg !efѢEرfJrrrhoo筷Wc6ihhCK/XiayN8AYYDZX,<3>| ,Xŋٿ?/2 n{hlB__f644fc͚5466NҥK=ݻwo}K{?7Lѽ(PAL&|,FQq8'?/NG<,fɒ%߿뮻媫:m꽢(۷og޼yڶ'xp8L2R)i Jػw/6uSOfÆ R|G'h'[ru1::ʁXn7ž}ؽ{7O=iqxx+VBryyyھH$"9 AbѢy!* 5lD^gbbBxBWhٝl2~?Bmٲף( @ ׾5mqq1\s N6x z)y뭷xG *j*ioorlٲ R,\L@^^k׮%''o~:tui}e\uUX,ơCWl6kUU%85>?䓍QU˓UUEQ >7IQQbB !.?999,X;vpavASSӴҙ0̟?nA~ݻwm6:::=鼽{NG(bھ*~߲sN֬Y٧XhllԆNU]]Mmm-Fld:D__ŚI* s}`XH&z^F A< G("77 Δh4bXP@ 'ɞ:A<|^~_SRR7|Ƴ߮B36jiiaΝ)z-x0LӂNi00O~d"0:mjII 3:-3u0Rq^x ~^x7l߾]{|yO{N4f {vvl6ȳ7m-G&mlww7x^)++駟FcXaɒ%X3+@RKvv6.BwG!8Gn9QB󢼼/~ 8@}}=-}s^uկ~G,*2::JFF;v젰ɤ-09F|r dVpR<gժU8s=Goo/U'W/..F׳w^~V|bݘf&N!LO3I;&~b8Bp88Nn7HQ:::ذa466( {% 9gΝڵkioog477swկ~@WW?@:2cZq9M8Css3ccct:x< ȭJii);wd] t:1 ٍ" Bzr4f}"B!yx}D ! Ȓ%KXp!vynJFFgX,,Y˅fl6L&fΝA4% 166F2Dk+**{8qD~H2$33|-`~x"77i%BˇQB"DZZ瓟ϊ+x>iC P*3LR\\Lee% ,j.K!';;Rønt:ݴh,\|8LB!>R$B'NW^ntR233AV\D"vE{{;zl6\q@lf^O `ppÁh$iY*ʑ#Gط\{aFFF0L8Nt:drZeVXba||_|g}뿾PS!QB"77BnVmka (**r166FGGǏghhEv<|_]vi&.]JVV\.(ŔוǾ}exx5kPSSFqZQ`0Ȟ={$H( > !“ Jq^deeqWb9vdffl2,X@4h4RQQnbpBf͚lfΜ9a?NYYs#J100@*bɒ%D"_<' aXD"b1 XbFp8?>999$ ҆ bۙ LRWWW\q!oB!. NAZa0èJUU^{-D\d3{lTVVr뭷zlrje'TdddAvSTTN#77ۍd҂LVXA(pHUR!2&AQӉq_FF999ZiL&Ln0(++mrͧӁ֗ LfyRDB!7nEQ[n`0zdeetR̙(7ĉt:馛Zlٲի477qS^Dvv6F\t&\zYkS\|$B\F# ,HW;CW^#(.@tr{sBC0(¦M,_W^y>hooct EQx1vmZ?+dZX,vsޫW_} mĦMF|c;k@˃QBsFQ3LF##L^ 3YI!>|VZE]]7n&p}1oz/M{b FFFd21w\L& ⋴טL&nfx^z W_mlT$Jqqh4n:֭[cN~?z+wGcc#ׯgΝ\.y~iQ>OC8f…_۷~3FGGٺu+W_}5կSn$B3X wVǚKpH$2|g>>Op]wŴoojAd&|祗^ⷿ-_O~8;✱fLGLt:^/`BwEq a~_裏N~/EH$X|9Ǐ׶M f:g&EEE<<G?b˖-'?S7L&%kZ뮻zRF^[V_}ھ-{饗x1vmi,.^D !)ݴ_IFН\rDdX,׾5~c2Z$ d2h"V5vrd{SoQRv?q-a߿#G|E;b`Zglwdd\ TSz<mfoI(!9o '?gUEッ !Gyy%+rrr vFvm x^|_eeeLLLhs*N<0455cnf>pwSO5388$ zVV~_;.++댟C\\.%BJQ`}8x/G>O裏N j*++& qBTVV__A"kx紿?0?0ׯڛSaFFF?{vLKK 7tܹ{ڵ<ȗeooYlDzz=V?i~s֬YC?Lgg'N{w|;1?OYz5/@UoO~nOyc=]wWV^}/4ߺױ}}}M !.O]سgmmma0(//'??kz-`m۶yXjEEEޟ0̉zr]!gb= Bs###vihh LpBn n222QXmbTtwwDZc4I&044D*M[7 I$!++T*E4edd@ @0tRXXH,^OYY6y9сrQXXnK0YfiBq Jq^L&f3`^OyyVvpp_,]@ bΝ `4q\̞=Zl6(w羚X,F$!##rFGG9t===$ Ƙ5k8tPH ΂ ZU]]dhh{( +J{{;[l+++)//fR466R[[I ! RXBqPWWG0N?>innv`~?,X@FFccc'cbb:.]JEE---q|̞=۶m BD !ι@ %##IYYg>k׮GAz+V(̈́a/^Z(۸n `Ϟ=Z 5uq_^?^*~7 >EQ%HPPPʕ+ɱccҥ455rTX,1B!.QBs~rQmndAV+gʕ8x<ӎkiiyit:z^DY,"`Ǐf,[r(((FN'dT*n+VPQQAOOG%RZZʕ+q8p{B!.,D !ι\*++ٹs';wdxx2 d2I ЎO& ňb,ZݣV IDATN$駟fڵֲg~`Xp8NL&g֭|FUUƘ;w.˖-cbb@(..&LFZ_V\ݻ7ӺP~6mD,#jϮ]H$LLLrJvB!Y~=YYY444M2@uu5֭mjj" b4ihhjrqL&NSM7DAA~2C[i`|>jjj={6K,fсbr***bppAccQBqxY~=>,ԧy122rz(.i" ן^ !>Z)MMzzz z~8#:xzdggkAB俐 řf$uZ<2ủBW%%%@J!E;6':|Im>?30L< Sq*=%%%n>O}, J!Bwt:6o/~ yW}㴴Os]wh٤_yӶ1Æ x嗉lڴ]8[dNR@"jP{+!y<q:lܸGr7j*222f`M:z(Tm'҅jkk gY?!))V2a螸 !.q:@*2@al(& u(x^BЅ2e-+ֆ^vb?!^ϭo~eɒ%l۶ax7Q?(шf;[B|bf fyJ|#D޷JB W;3i/^̶m0,Zᇵz!:::'s~M7NQQ555疬%%l2|ezR~bWŝaJu3b~BwₓuB\udd?z21>NOOLJKK/`fFgެ G(.%Yl!B\$⒐5=*J12<¶g*++k(,,ƅ,_B!%rO#%o3ٷotbϞ=tuur͊ -Hˁb||ck?68'BKdļ rmqQ1LtttK<b0::JNN.!&8N233Ocll @aa!f@ xFff&&H$B4*Lgx"瞥ob$ L&D p8`rcg鬜! ;BUUxd*N"h4D8999l6R`O"nt8  YYYXV<HۍnGQ$@@ |g}!‘ JKfv( DJSSDT*ţ>ʒ%K8zoYfq hllddffN[i&Z[[I&4-lojjۃ ve˖1w\ؽ{7hP(W_͊+،7dLLL@Oo/dϞ=A, \ M&x7pp˭Ɏ;8qv;6KRXP@{1^:ųfC `\ z{zBݙ\q25kpAb̚5c SUUcll}Aii)+V 33¡, EGOO7oڵkD^^NCk; NEEr ###l޲ƅaZtڵcTTTIoo/7ٟ ٸimpD"U yyyrX;GqFBqnI%%&??+o-[-Xܜ\Y} 99N#Lb4+bX,iYT*śoC=㝀ۍ`oDp:Z׳xb|MFFFAh"]]]TTT`2tAH?F z=T **+)//pzGZ[q8M&V+*vz2/X0:JQO^^.Td2ydvIbSʳ+!K.e||͛0<\oC! ,\uVZZZtgr͵װ`LfV&|R `~(((`0pa"sW_e||D"`֬YXVz=< &@ Nx/I<yf7ECyJKJt uDx`0hkMtLz=,z;r(-FQ0L3VSlk*=1^L `TQ:@qq1F=J<~ <'N`bblX,҂nl6E^^VʻAjjj(**رcfHR\qZp8XhFqz{{7oCCC  @ϧh4A `֬Y444h$p8(++#33^18qf͚Eyy9VD" *++;o'!B JqYV\.h f3N5k022?UW]E*bllf&QB Z ( ǏСCgllD"A8& t$IZZZ!ՅN;wק׳ f3DFGGtttY|9`vzzzba/^L~~>Xa"v9pp 0m^tvvb2x<$IJy7?>]]]}  b4Z311A2$ ݍjd2===x^̙ 33NS RGFFhD"!ABˊQBs.33=Jww7,_UU|>dGQĴv0###,_Rz{{ٺu+W]utww999\.ϟO8fƍRSSsΥ6FN'ͼ,_~Xr%GH$Binnf`` 8˗/l6kY(EEE455 M[[)( :ߏdܹo*+WDQ^|EڨrQ[[jĉZ) Xf a5k7o,q8TWW ikkCUU(!A3 ̚5 EQ匿S]]MNNp_QQ^>p8l6k&qʨpviiiB4X,F^^Q&&&|nt:x'HPTTDVV`a*%%%$ ̒ѣJvv6Gb4Zf3fYtRmeT*fbf32o< Plvg&h FL&'Պł2|]]]~z{{QUB!.jD ! DII %%%F233Ƚދ(L&-339hhhKff&Lk3Jv`0`ٰlR)m9Çl,[ q{J0Ll6 .K]דL&s:;;o||x<:-Xs\ڽl'HSVV֙B cttӉ`rQ__=^`z`0tR<L&ldffNk[ӝv(R)8qVa``@8:^p\ԐOKKSTjڹTVVRQQ8^{iT&j233_;c~odǏSPP@QQccc9rD {LR)-;0B!BJ !ιH$BGGabxgll\/^Lnn.CCC|iǝ邽TJ+PWW |A 8ڴ*w#CCCjYVN'f 7 %JQWWǼyسg?*d):R)y^|>ߴY*VÁdbœB\V$%8N']@ &M+L&1 ޻w/GUUgS9(7nDUU&&&f&IRΝ;X,Z:R&&&xx<A-$''.:::|\uUPgg':,j)++І,>s\.<ӮNm:X,od*^Mޓv^ӂSB!Grokž>~?gWBޞ%3SEף*hW^y˚5k9Nx@ 0m^0$ H$0ZL\.H` -0FzF#Hϧ6@,#c20 a-@B!mP2d||P(ӊA$ p v;~ɄlFQTUڶl9וhԎ|s$FѨO5_0JB:NԶl6zq(2Qv_vv|=:])_Kvv6.O|e !z$%8/EbpuסJ{&ȘTAL#e0wktjLi& ULX3@ =QbccS) sssZnMii)oiӦַh@ b%J nJ<>gt@p# m`<J @qGFc\@ |DY9*UYifff @ sGF9999=zTQ&T*-oQCj:xxxC@ [Ȏ9 IDATtӺukD@ /X * Z͵kׄ^o233S>@ ԄJ[GfDs@ @ %%@ 0@ F@ @ ]@ WDGG]bADDD)(@ W [.k,s;eڠRDȃ<.s/**:7;@ @ ˆ@ [@Q@ @p #J  QQQTJz*'NחoRT*;w/T*&NիWoFZZÆ [[[zž}CRѽ{{ugJoˆ@`BTT$tR}]$IR6ggdd7h4sNN< @hhh݃fbbbPxF/¢EHHH 3gϞ_zӧOsEx9r]o?33Sq\%F c˖-Hd_F@ *9vPڵ+[nѣ 2M6pBe$OLL ZUVdDmڴwyC!IdffFTaa!+Vg͚5˼y(++#22+_Iꫯr/yPtF%~ +++\$sԩS駟Xp!vvv>>u~پ};6mo߾4jHޱcG … ^#++/Beر#/"/2 J9wwwzMdd$<ڵcHom۰ccffFRR۶m3z WWWMԭR9sf$Ij*@֭여,Eư0"""Lt6ѝ0@ T"33TpuuUkZ233x{{ӽ{w &22RSSquuߟ^ϤI eϞ=IP5onݺѯ_?dddjh4$%%ѸqcZhA~~>IIIDpp0=.]IQQ 82> yG1cAAAlذRRRBFF4mڔW^y{fINNI&&sssSt:#==R|||ppp`Ѽۼ[yf@^)((ۛZl˗INN`рlоꫤ憯/m۶5QFFaa!Ze˖ǤI_σrptt2_FFIIIU XZZҫW/^yyLt @PXt:۷7EFFnnnxyyIZZ{VV+iӦ ֬^mFll,=DGGsI^u|}}͍ (//]bmm)))K.ڒF@@dgg+ݻw={bffƪU$~___<<鸺OOsLqq1ŊdĉdGV ]UVH???QT=z޽{N#88KKKLEJaa!ќ:u3fЬY:1zh&NXeGRVVV_|||'==mD6mݱ7ѝ0@ TPddd具7ꫯCegg}މN㫯B׳g._ٳg)**REЫW/8@.]9:]۶m TӴiS0x衇^^^hZZ-899mݺ5/n ͛7gT*ƍ˗' d*--+WҢE JM@@-Z ++ PVoCll,:tP~qZ͂ hҤ6X6P_|||]ի]Qwi(w>@ 0QƉ'Miii T6ڷoO۶m;v,!!!8;;|D`ɓDGGӶm[q41yӦMٺu+ׯPէ၇PG ʊGE8s /^cǎtЁ V& Ӿ}{?NNNqqq̞=m۶3d@Bvv67o&>>RRR믿8p C AAW4iB&M"++ WWWŨ0Q~cߠq\㉎gUƻmP cGхϚS\\l; %'PTo$8i޽;^^^booҥK3gN ޴ioO"00={{~VeҤIL:dzד֜={N:er旽=?~\1u놷hDM6>s䄽=Ç}Z tʄ Xf }EEE>},F'j㉋S6mڰzj:Dnn.gϞq&}qAquu777Xti(o8.;v͍du :Ylf\"((ӧOWOFFTQwڴic; aD @ ĤIt\С? ͍^z1k,ŕk1-[0l0j5999ݛ=z(@P"##M2j(СDEEѢE )//f͚EFFVVV&Jv툌Ttfȑ :tcǎ8::Ҿ}{LQCٙÇgϞ&{~m iݺ5Z{tޝVZht(lmmL"447x]*ybccQT<3h4ZhҨQ#% hڴ]՘oԩtQY3LuW_}UѝX7 2(@ 7#77k׮v|ۍo>+׽eڠRHݻVˠAT`ʕ+|9rDWUs/ ]X@ n>}Է&|g|Ԁ>ˆ@ f̘3[O @ n%ٳg3{g@pxx܋:%HXB %&Ͻu_O @ naD @ - (@ ] 윯 jxʗ rp }}*6% o_\6 epן\/wW aXuz4'Y5ylZG{ :gƍ Fʕ+ G6tt4ЬY3 DFĂÇs rssM6Qߢ ̟?ZMHH}QF$=ÇӓaÆѸqz2%%%l߾K.QTT >>>?0}DDD@AA'IIIcffڵ#<<,Vhha+o45<|q;/_ǫN lMl?/_Cyk6Bޕ뜴^[g3,`ڗuNLL =z@$bbb޽{+j_!oȲ drssYv-W\|L֭jmhԨ$!!Af<[%::ŋs ILLd;v ;wj*Z-Z+Vԓƍ9rZs)i{vء={6+RGaw}СC gggzA۶m6j,M8vZcǎq|fF^^eeexzzbkk\wqqxxx`aa^oP#GX`{E?m6FY:ҥ nnnDpp0o&ublՆ[AfŨO|w? }^N`4,z-#0I3!򴼚i[XVqE2ܸ"ci#ɲw|3 ye35܎Ee<Ɇ .Jբ;sU8Avk2[6tb5dUZ+wAryW y4Gв+W% qy7jQ ˶zsjo{P@yR)!,D!t}ht<-Tf?6B [ Sl@ u\\\9r$Ŵiޫu=Z;vTe'C%$$TϟϱcӧgȐ!L8IXt)E:tdFMΝINNV?>gĉXYYfE??~5k֘/hҤIU͂ 4hPCU[СC3~xBCC9qgΜ{򰰰ӳZ#ĉ̛7I&58C*## ^z:OV_RuRԏURwl( t'o@'ŷU2@nXHD_Ng'^wdCps#Vvɽy} 0p&Vs[gjY9y9YTߌuYO-Wf$?U6+zd_1ΥnƸ… jضmDEEVkGEEIHHGر#Faa! OjW\xꩧXtRի899ѹsg5jD֭}l2elj" c׮]B/ ꃪ&2Q7ɓ-3:uΎN:Ѯ]-\ \ň^ώ;Xv-͛7^ o֭[3nܸ*{1V^O?ڵ}fD t.T]7t}N Դ2M62TZ'1Z;@ӮJYv@{wpmSXTvi9JܫdCr+R{Hsgݼ=Ֆ569w}Qz4iI5SAlTx+hd?%SBйܛ:WN^.@pDDDХK>SfϞ͙3gXr%֭uR~>Rl'??_ٳVVVVRRRR)򟃃jhm5Y헔MC%66nݺU^&2U嬬h4`ggGII]"55++J/ 0`㏷|ҥ /"iii >t^xmF.]ؽ{7u,)'px,f ~/#GCy:c ;, Gރm{!9`Y._sgOk pr|3Vse$X8> [Gٸ0UOg /<9X/׷x^!t>rsgn yyRNi*4] 2{Fhŕ3Dn ߤUd%-?s!=)))HJBߖٳg={6'OǎԩS"++7x%=444V^MVV7nT&L)SHII!??_xUlڵ/?>+W0o<֬YT7yKRc9bݺul߾ݧQѝիWyxظqc4ۗaÆgn:ʸp>lʟ9s󉉉 ǐsSRRRdfժ.I:E ;onɑ ɮ Ӫ& %DsB_.IE^~ҕݹ\TV^*I[`eחߙ\}JNN ƳDFFJڵKzwiРA_IRIHF>#ҪUVTT$M8Qrpp'Jeeןi:N_E>|VVV&"͝;VmenݺIk׮5i_po8&F.\(J5SJqqq$U7EEEJي]{YYY˥5kHaaa4bGRw<5kH$Iࠤ?ϟT͞q|i„ dff&I>Y^E*EJ7J*c7 GJaa~kJ \v 777j}[;vpYL5U /^I&76չ[ y\n|foE&!+gOTML\^>"88 &j+h<1%AU4ַ@p_"IZm Y_ӢW>M6Za}ѣ]J gJ@ nr p IDAT\ƹ )\Jv54E% *6VhGf>lCMh٢ N7waذa̞=y;ut$ߙ;2T*CZK@ ΃wvڢR<s׺lqI%ed\Rb:wSZh}pDPK0>Asnu֜;]* Fs 4 ~5Lq1}tt4u^Q8q䓴l}#+Uʃ<.":_.󥦦j޽-&T*6oLaa!=zF!I;wM6xzz>ɆLBBvbҤIItt4ѣZ#J7$IS:[ӵSڴWPt: MJ5K!>ЎlS8r\ve_OSP^Cųy_T]=<y85oۥKk}Iw9D{wc b81ׯg_矿2UEtt2fܯn7uk;u1I87n~1F7nenn033M$It:%]ub_ iJ[{1NSz= t%%e8Γwkcɐ\ຑjIKKExuG-=%vx9vai۔ Ovnk'22k|Sߟpr潍a05j~~~4iZjUe}mnb4,,,LJTgAN޿?;LP'̟?ZMHH}CI$Cq! eȑ8;;ףĕ)))a\t"llla@e8""\~8'H||<&QС矴iӆqjٵkM6m۶XZZnFa޽lْZ1[ZZΝ͍6m`cS=*7$IBpI$ WWWڵk]7ƛ99r,h\2[KjKpeWhԸ2Ŝ;w$\/]SW|c$ƩdX^N x$ۺӣYⵄ""[bN9q7@$=_|QsȈJTiٳDs5233wӧNgϾc$S^^ ~~~>sR?冷>uEyy9w&99]X///,--ٳgZ_Lii)|Gt___rss9tbDVO5 駟: DRfƎرcy=z4O?4'Of$&&2vX8@II ǏgΝpE~iV^MIII=ߑ&$I3'l2233o^oKQQ6mbʔ)X»(ysf͚Ey'=z=>>zϊ+yNʛoFҥKl۶YFv5-ɤPl޼ӧ`nʲe(..3*bnnK'll4Vm<ŀ5eU*>).ϢR6lfff>}#FTf$&&2l0hݺ5_5Z<4jooo8W ={6|IټPuevwwʊ;* vٙ;rj>ԯjŋ+}1p@{ETTDFF2gϯ֭[cx5@*MUYZZZ[n%!!Aq.]Tmފ3a\\\9r$<~=z+c~*뺙T*͛"V:ƍ]v4m???zvv6cǎ3g|/fƌ(meo̙38p K.e|駬[NATTTqW#5@;v,?3~!*g}7dׯW^zII0UYA# eLk|AIyٓ|rΜ9àA$ 0a„[>"Ceԩdee'p1vQmYfMRR$%%UckkKXXX%wɒ%GV_ 6/2sLמ\YjJ_jՊiӦU>ԩ$&&ǰ*nߨUݻiżtI8|pe֬Y\v .0x`er{j#G/VYtСC?~;2//sss<==M أGK/Ѹ ~L<'OG)nο@hhhJkpkHDfh޼9666XYY1fo֭[ɓ'ٱcvŋ899퍳3yyy4jزe [l!//:{ $I?{ҿ233̤@鈉_k׮ݻE1yd/_Ξ={(..Q9Ts%># x {zj3f ˋK.?S^^;ϟo/Bhh(Gu֡V5j,_'Obccøq:t(cΝ899aiiI\\...<$&&syZ-_|>(+V[n̚5'O"I...h4N:Eaa!~!vvv\5k0sLzySe+~`pwsdȞ\rss0a׏~+WFQQ?#ӧO$$$pABBBܹ3.\`ڵ3l0/_9HDxx8͚5CRİA]Y ~qZ׆ qd^_ ( `L:u?11G}={ЪU+ѣᤧUٳ$$$TժUDGG PD{csssŐq &3>>Wʛȑ#GXnZҨի?`ԨQ:uZO·~Xe=7szPB#Vϝ;נtW_%++ٳgיq7g@~cn~ #`|k IIIIӺuk%]Ѱcߏ7ǑlΞ=Knn.yyyĪUIOOG$mF>}ׯal~),,dÆ i&#ӧO{n^z%~w6nȔ)SطoX[[O||<5LWlmm|77^3/((`Μ9L_d;>3/b0F`z o&FKM~R^nggJ *mggW7"UQjd> iӦ5\x1/ٸqm˶mhڴ)&L39B5 $I"##;;;?eeeҨQ#ON~ a|V#;;ŋcee_Ͳet;vL׍-[ұcG/VK^^mڴQކ'''ФI֮]'O槟~k׮deeaii#<&55ŋŴiؼy3ر ),,ҒqƱ~ziP&쬬P^^7fl߾ϓJII :|Mz-|}}v̌ƍk.>S㱳wތ1&M0sL G׊?D&NHo:i+*.%3+k?) aĈϪUԩ^^^9s۷ӯ_?LB˖-ILL$==r 033S&!r?b<<<=z4 &[OQko/Hz+Kz.aqJ-FìY0 :u͛#Iƍ3ɫػw/1C 7-^`ҥ&ƌ믿NAAż5M6qϯ6oݙ?> L>T eW\nKWVSVVFFnK[k׮)ni~M_~ʕ+J_kh?~|e.pօUgddd|%%%$%%)ӧG1ŕ+WYk<Wn\E(Lk&O|EW_}oPf͚$Im@|'KQ ͊hZKKKe2٬Y3& /rRRRXp!ݽHPvE\\O=4jHYU,**"''d5k3$&&x{{ӥKڷo$IӫW/Mưad*._?Ό3xG `0(JKKݻ7&MO>wuN(**B舳w !!ڴi=GQQW^&M`eeŹs=GOOOpuueL4`I sq%oooh׮aooυ Xz5-ZU\{`@\)++cǎbooϖ-[صkGAV3l0ә:u*mڴQ1* '''4 )))x{{IAAVVVt֍AR)JOvIr6%=:/,%zYl3h[<1HIIQΨ:x U,77N WWWF-,,4SO>$ӦM#;;,o^mf͚QPPEclllHJJQPLBJJ TƎ˺ubڵr[2*;w&==˗CzzITƕF#.ƾnM}P^^իWYl1NNN<*s7Uڵk+]z5ݺu3qsqqm۶q̙7n׿aÆ 5Lu||q)s=GIҪ۷/Æ >cݺu){/oD~n ϟ… &>:xrrr8p ǎCVӯ_?<=X\z~Q^^FW_%''?C9PRP?z:ė_~cѢE\v [[[*!!1c`aax7ߦuִjՊ\Ο?Oii)aaapٳ x"ޔsyҥK!wvv]ٷs5ONضm,Yggg q\xÇcggGpp0;}gޞ+V0l0ڶmW_}?]tQ\gΜAR1zhJKKr gΜQ7\pA Uo0e˖Ezz:3f̠}FZvw V\+<3|l,,,h4ڶmLRSSIѣG9{,]tߟǏ۬ByEj׷;E]fcٕKٽ3oM[ZZ*ROOOLA}G%??aÆ1gvA||<͛7g̙4iҤrsa˖-,Y+++f͚DӪ*ٲe _}+UxmرO?eLz6f%O?~`ҥxxxo+gkU%Í&|={ߟ.]9m\?/~}wb[/111J_t$** sss在 u(t`ӦM6sJ٥KJ1wJ{1<<<گ8| [loÃp}6z`t&)..‚˗A۶mx'Xjprrb…Uzs7̜9;r%̙hscmgg{1b,,#DD IDATjj*iiitIHIIAТE RSSIOOĉٳVQ4nXyݻ777RSSپ}] (9ر4ƌCPP%%%\|cǎBaa!\v;wCLL `aa)..fΝ\]]IHH 7ϛongΜ9DEE]+q#|g$''?Pf3455Hgg'KG'88X &%%͛7&:T*ihhח<==^uT*7t\s 477퍿ek׋/K., jZ ߻bNvv6;v <<^O@@>>>`2HIIAT"@P`2xxxHKKhىNO?:;; wwwN<ɶmۈ#** OOOQ*,\pȀ! !XhÀf͚E]]`|?~}´iA.憿?=zgggz=~~~# C*Q Hd;1–-ܶ6:-  M_ W%cǎ?<m +C~jkk;v,&M]8o$%2".._U*SLɉUUUDDD`0?%$$~3ƎK]]*Ex$̲eHNN&66B3vmL&|||pss?9QQQ̞=jjjD[{qDBBaaaDGG;Ǐ7 899qOyy9Z%KIuu5˗/Օ^R(_$JJJ AVk؈dݝ\zSB@@@׋QOEhh,]T ?f#((^Ϻu?>s= J8q"3g2P,n2UL;Ω"‡O$Mpp0$''6111^SNA\\cǎE&5\CPPcƌIcPx{{D [v9ď &x1x2I8Sk.}<$;#+PX/H?ZҘ4iӧO 2u1m4ZUW]ܹsj@LL 6 BJϏ(T(<< V+rcA&#S:UV!|A\I=[n:6nHJJ }ŌMň<<<9s&nnn/\/*SB@Pt:ƏOllX&!!Aչsc8{r_y i]i٧wqqlz+nÔ|Sǟۚo_x!@0EM7믿~mKHؘ6mӦMݐHJeJ%o3w z_+#qiy:ܾrpV(C`)CCɂB8띑\>z-Z|7Vgc̿]\\}LJJ wf,[@*yc?~c\ddʖRTdbKÅ=_ΆMLL g8EEz-EZ s7rw #QTnݔkS$*/,,|]0Jz_z6\ןvQ.5ldJ%xü&Lx3a rra}qg-fr;;ϼ'yɣ?vuVU CƟGƹ:}UǴv-S;>&3f VdM`Q$.=}8[.%fx͛n2Au:q-xurѭJJx9S$.?{Í7x?=t ]ޟƏv_Xœާ3_5v;5v /O=kF,{-8lc7E»seek#1o<_8[˷TSk:6o7Ͻv_~KiiؿX#JHH\@];QK崶RSSC{{Y455*,$;.[n.ynىPȓP>4>I$$zdl6)..}d$.d2twwS[[K[[ۥL&),, ?[=n#Q-wnw.2eMg N`v^KWgMÖUa2l&~w=pp^8ϸ@.-h91Ak'+V_>%!!!!1R\\\ؾ};1ƀv;mi!nT*)P*i˩Q~v;zh  `?v#[LsAW$ `2Ġ fHRR6>)W26 ɄRgr>uMD[WJm)cG9CFC@yǣu.mwG9GXaJ~L&ٳg~V]z%y\æxUmMśxmk4q??įN9yy8WBBBJo;s缔6.z뭳*tw2N'UW<|qG۟~Jb:y0b ЯtM ^~m/^,潑8r> w]$i&BCCO~2E%<w>O@^je{v|@T -}>?jbV[Dmb޵!.!1 n*ⷱQGCt'!!!q_h}ׯ_g:y$֭# ^ڵk/Sx{{᮷ohxe_}5}NP_Nս4 s ˋ|)٥)deOlbc·뉿RU1วՓmQɇ> %$$$O*CWj|^~eZ-{͍۷SZZ/Q(|߿n|FロGy^DmJ S(IMcǠ,X@O v;W B'[{{;uuuD޹T]]d'1Ahiq8xyń&ću+wQVLjںDe?UJB `hfNEtavwH#t?)~7w\ ~N#?ݶnO7GG?qj)dI1L{|ZJTFx x q֯_OKK |?0;\E{ʝwIXXk׮}%%%JGGXf*o}O.'p3 9{>xr F*[5+ -8*߱+^z%aillbM2)YYYL0_`[mdt7}|/Z?d,&f:m_/%FO?V{NaGIVLׁM2vb`ݽ_G?=g(..]]&:&f4*%:g .ZuZ<=\ #(/_I1IHH8YYIs]wWQˇy\szфG9f29YyGA;xxgW}wh6nHII fqx{!Z~x衇hiiח_y]]]ળpϓO>V;rє;v 7@ׯ[ʕ#?FT3LhZ= www:O>RKDl;w$//O=:9QQ^ɓquuK=J[[3goocGna˖-WV+ՊݣvE. :;8r055eO: &,< wpVCX,MR.9q۶O+++Q֌B;z+⋘f׿}vyPNJww7Ņy1iҤ~_?ĉOz=(\2455QZZUVÎ^^h}tT@aa!'Od +b7߰l2J%A7tGEֆ^NGee&S7닫+pzz̘L&ǜ9sdxyy@r(UvCgg'F3jzɽ9x "˩w77v]]]l6\]]▖4Z-ʽdj!W),,NN"W(P*؄Lh5ZrG.n 8i 544cb6ꊋhE~^ꂷ7555eXhkknjZBVRQ)UTUWZ>>v''mm0b%;;;RHJJbٲe{P(j5-ۿRF#ZrP*S|[{;]hWCP` Lqqvd2щSM`0{]]]L=V'F#l6,r3o澉Pn,)_趚[yK-''Xc,t*!!!#رr9vK-iooq^%j޽І Xxhv!=(\d|Wh4B? ӫ̘A#\wu##5558qa(NNNK,@V?SV+]_UFss3,X> QUTWW;霹[hll#*DILL"::NGss3cY%>>gmFPPtvvĄ /]QZV?j\Nbbӻ(bNȢWW7Z aω'8qtttOGG;#9D`A"-- h1{ R}}=YYhZ||} :* ;3Çs<3x" bX(,,$++D믣Ʀ&V@OOҌ\.G׳xb*Hz:ed2J%'o03;-'??kniŜnMs K|F :u*6m6nԩSP*FF؛Ekk+O====#|_yl߾)Sxb4 899ˬYb`2jCネ=Wt:cǎÃT]hDlݎ v J{\L&MV͆L&(yoA6vl6GJKK)//ooo4MJKKiii!&&3~~X,t:]?FYy9Ǐ@&@GG'yy24BDՠRhkkɣ{WEV+ TWWP(quu㸸8Ovv6LII1cǎEI`` j]8}N#MnsIu%??SEEQ:&qơT*9~8eeeL2???\\PbtXftwwjt8vjkkطoAdffؠhZØ1T*tR)iii!--MTjqAh4TUU؀7W]u(FMm- [ww~~MEE|tLL2Vۇfgy(JjkHOOˋ1cNѣGٷo/|3.Δ״jqa>s jگ9YL>zS=g?kyi}\K|6^BBBJaݺulذZ XnرzkyIAA @B$9tyyy(Jd2s%''ll6 P__~;lZZ;Vc޼y}y'7oEۍJ 3UVQVVٳq}a:_çA(..JQ(DEER(--]U@nn.v777ƌ&&NKN--|7ÃjjjXp!b  \phWX̜*x45Jw+ d2;ʵ^Rf I wޥyi%~̘1'''>#  RIxxsU2~{{FVV61vl)qX,f222P(̟?OOOZ[[FØ1l6&$L 2"D~~>--XT*%vۋcǢbN@.ՅAo0P(D% 蠽O|]G`Xr8q|@t"## FPT*d„~8c2MA~>SMӓQV{zzijj&)) 4 9(zj5<,o濉j0Wcߜ u{%$$$~*\{l6NtD7]f .IAD<֬Y(J;+/Y<F׳tRfMLL ݻwvap"))kf@gP\kJjZ***>p/{M118hĻ^ÇdTVVRSSCGGGۛu֡RHKK#''Orq*ˢEML  fOPX &N v=V"ʿj1+XQ%͔t@bb"!(g޽Jj[TWW/?%ijll6 WT456ή., AT yOOL&O#u"!a-477Q[[˞={hĉvm\W77hinAV;E'mmmv***8q87|3 SBDb6Յ` *2@rr2F'OB8i{ې1axr >'!!!qqRX|9˗/pnpp0,YDtuD?|||Xx1/߰;}wFž DsNvM}}=99Ĝ>Ç @\Npp0!!!xxxT*Ѵ~ vtCCQQ 09yOхUTb1qa$[]5OF]]=8; O//qK9 \FAAkFڋձ<.L6@(iD!w .>H?{؉ "::V7ǏGl6S[[Ǫ2:&N<<_~ {_G=ve2gfl6r9 ,`ܹ]`h4R]]ի)Esgq :gg\LT֌.\]]P((/H&zwlvjpt "0 6 @]d2sstx{{Ott4--tvvVZ[q:uѿۛz:;;1LЀ ZVjpϯ/ 'nNLQQAA8vU$.ӉuZ[qqqlvA0fi:QVV---|TWWֆ#S.vhhuub0XzgJ%3g?4eXWJKKQ5z.& NG{[^gA{#88*+*'|zK 8;NѩS___i#Z_ѣ(Jf̘Cfq]oM\VT"#hiia=m2 0n8K򤸤&<qNѩS^}5 ܓ]= Yhn}Dhh(k?B RSSCLL .$77Hqq---r…(ΐRRvPxVٳ DŽB.|8Q&s|NLLlRRR th4xӍ477Tw:{#gU*'<6ߐJlf„ DDDl8ýf|@??P*L2esACV=== L<l6۷FUUϯ7w7Z[Hl ˗#10m4),,Ņx"""F}ksI^n2y2GSVVJBb"Ǐ2Ǐba}`$((c(+-ߟ3gk?v\,.bBtF#K,! @K %88n(U*r I0ጚd\u˻4zx~B$$$$$J3/F 0&B#+>|w}wv+ -[F֭!w_:l;Y_Hcqd %۶ar$?)$%&SLsM7d`0{~߰&W*YYY;z$.=,{2K#ꮡp!/f]`:_Ji ˃ e^s>U@J:wMNNN*n#--- Z>N;co.9}-^k.F3!-<_̾^gbYbx[yD [vB@R p O)]C-q rbQ%t(\*FZ-ɢЙh4VX_' BB6H ︸8VeWOiB$y z!^y♕Er4JE~~>| ]]]C''㞞+F=JGG}%joсZLv}&v OGn]AyYY}SMGGǏDvz rYmWY=455bQBg>f͚ō7ވ Y &?{М umkǘddd Hz1+PzIZ:K崵QXX8Tr,9~ła6 i ZD~G]]ǡyzJ05uNku2w7$E/ h-GszeS:Ҷ_c *UUUUoBBG!77ZJJJxZ|ŋ夥 1*xZZhZN:\.'11cǒlj'0 \___GwwwMƄ PdddP__ORR>>3ȶ6q=1Mvv6Ge@]}),,$556mvٿ?sk)RQQM(--ۛ3g2n8v;| fYbŠ׳dKGG[osׯ_ϼy7oޠm-]FCZZh4N:ә;w.l޼ycuVl68F3^t)ƍCTbc߾}455'oP+k}ewwwVXߐwLټy3999 ~{3f}&ׯjSO抲Ì31p:Dhh($&&P__OII SN'/T__/˙㔓þ}hnn&$$LPPА|`̙ G|F%#Iuu56mbƌL6mrtv܉R$99I&" vBBBXr%+7gyLƋ/ȽτW^y￟rOUUՈ2x9s^Zru׍TY7bP!0U~-[]ϳdҤWKKRjJ eFP(P$z屦Akn_qr* www $X1c >3իW_|ƍŔ111DFFD=*Q̛7JECC999xzz ٳfy^}Ur9f<PT$$$LJJ %Km6=ӯ#GgAb />,j^ߟ"lBEE^^^{( -o ߗ,j(*QwfÆ ͖-[#66vq'xɄNbk.bcc;w-\ 7@ZZ;v`TVVƶmhoop~yxx1m۷oGRKQQ[n ѣGw*Q_5GPICC=ذuɓ&ߣSWW7,9r455h"ygxwh4`J_… Ey9snJEEcǎƢET6n܈`Fj5#>#;vƤ_.f̘ \?VKCCeeed2Ν+ 6 W_} vmgUww|wƍĉźKJJ +V>wws˅Rl6>>>jLDNOՊ;xeHR3pTk?]w]%$$$.&deeLrrEMG^ R~K/Ē%K :thė0 ,] &P]]O?Myy9='NdϞ=]GyOOO-ZD@@;9r8tGeɒ%455oۅ_ɓINNu@]}پ};< ˗/\TWWzjyy嗇Uz=˗/')) >CK/Ĝ9s;h4|:thT/Hqqq,_Vݻ9z(sZWRILLDVi&x'=l޼qG#<"^C[o%11JҥKg]vP%j~-ݻ/K x?ׯxWV;Ƈ~8frlٲEw^RSS9vӧOg׮]\rqEy9sHHH 33SuX֮]+C;=zt1ɉx`/Cgp<37x#UUUK3;7̟?Fjj*O?4v۠u0|{wygڵ,X@{7/~ѣG)++n`#yXizU{uf\RB7ҝB&'%JBBjl6_TtAhmm~;mL: d2h2tA}1իyϹ8QTfcĉT*fϞMee%˖-#55*GrG^^^$''gaOc֬Yڌ&##ue͚5?~j:u*zĉt:V^=bbx$v!۵k tuu ӗnI4;w.JOO7Ѵ!_f ~)h4m;v ooo`N8qT߷IIIT*ΝKuu+g}ܯߣu%''* c޽x㍼{L>}~tsstILL4nt&?Opvve; 5&"00z_ʡ̄+滹>[,Ωn?#;K,W^!;; &M}}=]wojI/_W^!hd*;w3f EW]);{ptrEJDgW.6V*++ꂷ)*TX,Z[[黜PB\#O#|uwc2V`u]Q;7bcc|u8Z,˨T*T rw*J\x|jb1͘fdz=lذu։l۶!wSL>s̲ rvvMFJqaapjєAM3 _޹d2M|{wyG׳l2Z www}E/.筷2>ESXv;|CȝNˌфcX0c.+k^L99vDd%$$.=dffbncQ*_?hNr9NNNgC̙3ϻfRN8AMM :iӦ\։6WNOO6RD^z\ZDV2|󋉓3Si&֞NqFqՕb 8ܑظq#W_}5yyy;ͤIx'ru:++V>BBBpW3qDyXt)>|cǎƍG\ !##D233im.󉌌[nÃrT*1F1`mٳg:BCCINNfݺu֬YCuuu1Mb*1^k `~~l6rssMp&---;vΫB`ɼ $''E\\/"SL9hmmmb rrr>gϙ33g~{֯_OjjQ3Xh]]]{S۹pBx7ؿ l&OY[JsFjk0Ph4hzC=ƮTFFFfxWhkkBvvvRZZ 22SbZя~6&Սv $cڴi.mMcc#o6|Lsٹs' npIOOgƍdf|D?OOO111( vɔ)SXx1^^^DFF"6DDDKYY{ӓ4< tvvb0v8}<==Yѣ\{Nds% M&wyן.~ixᇯtudD&7a3e쁯L@vM\aO=O=5zf\QJntqq- ,##sB`XX,RPz"|'""B9w ۿ~~~477K/Q\\;3kpS( s&// V\ƍQ*yX` ~vEAA4551g"##СCݷ~;UUUV)++c̙\wuhZΜ9Cnnȸ" 77C/^|mqj.]G}D}}Zdd_!jnnMC5S|3y뭷Onnn;KIKKnc\zzzj@bb"999X,l6v}5泥Cw?)A3ddddddBg@`ۥ(iNAKPH%Aaa!999dʔ)s7LPXXWf1}tҤdxxxTj,Y /mƱc8s )))tgl>Nӧ;`0 `޽;A跕oJ og`ż %%eu&fu8VINNߟ&=B\\TUUݍV^xFkk+hN4 eBBB0͔K233rKaTxbL`B)Qdi޾-|2222_}dMxxx-=3T.fΜIGG<#DGGafΜIgg'?Oh4 T*=|}}/Y 妛n"33~qqq(J=u Fgg'1SNeΜ9jLBQQuuu$&&tRV+gϞ%//B`ƍIqq1dzxb,Y‡~VEP`jxyyn׉+!* P'###3,DLy{)))pͬYPT̝;^OCC>>> IKKC! !!Yf,iq֬Y# iBDDׯ'**EEEϧBArr2IIIc٘9s&^^^ Rdƌz/22???`4X^`j5,YnT*.BSrr2j RBjjj8qqףj cRQQAyy9*%ܼy3ׯɓi&rrrɡooo-ZDJJ f\}l61HDD󾩩TVVrAF#+VHM*++9qmmmDFFl2"""p8cf̘W_- c'ǎ@n&BBBؼy3c2edddBō;2Q`` s{~\Z8bccٽ{7s̑aNMԔ)S]DRRҸDc7o57Ϲ׳k.jkkfǎ(Jnv;*ӧNOOE-[/E4HOOGjNj53gwy#77 Q_h4c2صk$Dرz(++#$$ٱc曨T* Qf^j%33N~^e!JFFۆ,D|K&Ԍ|9ur-̟?;v3p-puO?ƍc͘17J;w.J^{5rrr&srrY.Xݻws 7R̔kllog޼y466JSNŸgOcxt:{o+M%##mCdddd( ,XpqE'//FCFF9sFx=pwex̙yn66F[odj+ɓ'y7`Μ9Hu]SUUE`` 6lf޼yxxx(ojjGw]6m5mNMʕ+?SEYqIBBΦ.###s(JT*ebn/RB(ÃGyD~``AXXVUx1 ߻vBH GFF?%++*++)..^.6$U*V8y)Zv w/####3% Q*rssZU'o &q8dggƗJ/u.&&sr50w\9z(###$&&Gꫜ>}e˖CCCޣ~h(--๋-b۶ms=QTTtA-ɓ'Yf 8pOL&gΜq1狋#<<ӧOK>Qo6 .4Faa!%%%NȄ!D|۸$!JqBDRj)--ܹsW: RIcc#V`ƍSO)So~~-[Ɩ-[馛عs'X___~_]4V%-- ;v?!l 0W_}˛?d!JFFۆ";3PPP/8FNVV֭cŊW*WIM l{) Ȝjf]&//9*`҄o#̕D痌WIrrr8x ұ^;FNNEl PVVѣGinn2?':Dnng:J-܅Rȑ#瞣 {~***Ohllp###9r?O|I-Immg y֒Auu%us5ڵI)|ʖL|YtZZZy&, c}3|a!wޡ@{RWW7)_EE>tuu]4ߌf61(///c ==ǏOjNx''\NUU識:!l6m۶4&b֭}CCCBe-[P(G#gBe͚5qa<==Zl2.]fܹsNRRXVΝ;Gtt4@҂SLqOqq1łhfCll,---tttP('&&wwwf3tttUW]Epp0466' Ũs]](t:f3Oll,( Z-jd2VdԩhZ۩OOObbb q( z=ꈍ%** OOO`tHcc#jiӦI9zhkkJ?6Q(>}/n:bcc fddDj%990J_HJJ"22Njjjח 044N#66&yhnn&$$$ &, $%%-:zzzd2h& _(9sF{T* !:://qו? 6`4Eӡhiiѣ βeˤ$҂Vex{{{V6۷S__ƍYf SLAPHII Jp"##QT鉛jڥOSVVj%<<(MMMt:qss:ϟ !gϞ%&&믿www455aXfxxx677STTt:&j^Ouu5zΝ ahhhjDDDkh4RVVFoo/L:4 hZijj"!!ooo*++CR6}PSSVXM__ ԩSGPHꦦ&4 ӦMcʔ)yf0 \uUĸSTOuu5fP Hss3 ("""Zdo۩!::Duu5 bbbi&9:߷M,͏Mo|x5sΥ<HJJ̙3fHWWr-L:^v) FFƍ  ٳVIs=|z=dee҂rn6fbpwwgѢE.#22vvލdBTҥKs֩]b0gq8?~t̟?KEKK Ff3---|R8~8fX CCCرCz9rmp8(++ܹsa6$55///|MÉ={OOOfhmmeń_p,Ȍ2 +V*رch4V\ٳ qhnn&??Xv-!!!deeJZZ/\"&&իWMNNxzzRrt`]jj*~~~>}uRI__Ν#<qj顦ZŋIMM={0m4MٳYd !!!Ekk+ӧOgŜ9sCP($Cll,k׮%((gϒKpp0˖-LJ"9qvZVX &&ӧ%ALfϞMjj*-L&IMMedffR^^NJJ ju֑Fdd}bZ`ʕ[iӦC[[CCCF<<f͢ӧOV%==ݥ]vNYY'N`0l2¨F'L&n#66R$""FC~~>p 000@EE̟?NbBT*Q`v.\ IDAT hYv-fIBىRddd&HKKcΜ9ӃVJErr2.Za2jR]]MLL gϖBHZ:u*jYz5ttt+PYYɊ+\4_BJ%fb f3466T*)((@p8h4PVVƜ9sRDբVQdzj*RSSQT@LL $''h">̹sncXX,۷n 777,YBZZ^^^K]]SNjb646 ZMpp0DGG%5k֠T*lRRRBrr2CCCl6fΜ9b0XjtvvRUU>yz^꣤$6l@HH!(,,8 a,_"Xlk׮ӓ~a`Ԅȑ#y "$RRR\wss#::V^MPPow}7k֬jj9qdw7j!inn~Yv-GeŊ9s3gJ}/ZMtt4-bʕj+Uپ}; ,`ڵ8ʺ,,,k0, hmmd2, sInF9B`` ]wkӁQ T~~>lذYf6 OOOjq&mQQQ( V^-sj.]ʪUPTzvAKK g RI?RWWm݆Z&55^?SzM71sL`Tϧ oooQ*&PKK ロrwCOOYYY\{۷ LȈ%t\wu|F2_-222 55 'D裏^t\Y, rJ-[&?/X4_e..DB@ӡP(BJۛD#NGX,L>`Xxxk>Na:ǓCWWV6BCC 'QSS.ȃ///RRR8p& ^Obb"AAA_AAAxzz;HR1uT BFBA?Cr ((hB5'کP(c###X,Vt]WWCCCxzzRVV@pp0!$&&J~D###tuuaX줵ł/~~~$$$I^^z̙ZL5K!fNJHH0}[2e $22O q88ǥfsnKDDdKF#B?.NXf]ii)˗/0T*&Bdooox{{c1$&&CEE̛7u/RWWGnn. .DTB???j4ߝcO憇p/iZ4 X,b0Q7##F}}= .$Zܹ͛s+V BBBcGBR +qssfIii)$9g.vN' P0*{zzJBOee4Z$\̔r;::X,L&Μ9f`0z|8O0J~~>J`` lٲD}\oB^~e)85\ >WolܸiӦa4پ};>>>,[rT#ٳdffG||X`h4Dj4駟&11͛7Kcdxx'xիW3m4ib{9nfI;w.0۱c… IJJXf$?,ovASS!Y߸źufxx[h m---pzill$::{Ww?ngÆ ,Z}Mbb"Vu3ZFIee%vfQ]] '33.]*իeǎj{* Ebaل#;;jt$''O(Dt:8t^^^4551sL"##Q*$&&RRRQՒDJ%:FZFպ,t:IALL eee ͛Gll,Fr)̛7Nl<==9s&Ǐȑ#R7Z̙3tX`ze޺IBBB dh4FF}ܹs%-' b\};OOOΜ9Cff$ԷIY@ s5Zf֬YL&񑄵 ̕iҷrJO<'ӦMzpFFFxOl6sAy$AKر?8}QnV B`4裏xfٲeL&vIzzdRQQQR:VZ[[_8233IOOgxx{{2m4x뭷?))){n}YfϞW_}W2vEWWr 8pv`4cnnȖ-[fp, tvvꫯRQQ{زe D|)&MMM_08~8%%%wc͜;wg}8nΝ;{ CC'|'eeeEEET+H444.F:p8DccfY׋7|S|ܹsZTVVJ777ob֭m.++xEqq(--uuujvQSS#z{{ؿx7Ş={DooTFOO8yxđ#GDGGDŽlNSN7xC ~m6QPPXDeehllZI744,l6Gǎ8r###"==][bǎ^YYY"??_Iɓ'Eooʥ;;;Euu`_fdd_UUUG8\o721444/?Ck.QUU%l6lITWWA)N@l۶M"=:[X,QQQ!v%}]- DYY5MMM⣏>gϞ裏D^^rG}Tdee]{lڴIlڴ鲕e%wB"))Il۶M!޽{… … ž}mC?صkbt<7*J|spss?τh߄xBlٲE?lb֬YiۅNjZ1:/q뭷 !կZVOg'oJg͚5رcRBBB Jh4"==]19s\v]lٲEh4=;jM6Mޭ[ !(CIx"99Y|. uij}#Γ&]%|Z-3f`ƌ^@BB¸yn+dN"##/޽{$55%`' EQs~ aѢEjR"** ;B5cqww'..Υ>r}˚5kƕ4S1b…?&&f\~KtRL!jk^,YӺ!K}WTHoʕ+?>֭s~3Qݜ-2y欻ႻR bg0m4ZJ+~~~Ɂ,bbb S___?/υ!pssYXTdlض^___nV:sCAPPwqDŽt:•k,t EsIv .F˗/CMM .BMee%/!3eo6i<̷$I0tww+Uعs'RX8ӜBٳg)**BV /H[la޼y޽Yfq VZ%qawO믿;JM4 O7tNW_;ĉl޼ 7'?ڛn48u;v̎^ZZ:n_i&5ב@qJFF泳a6nw]wErrWTFS(TUU~~~Bd2a2\/D9w;dMHZ&'O~A'? ?8>}P}YIh;Vh: g_;D-^4`xqBkƽ 7@tt4>(.2o>vIBB8reII IDAT[[\}8pkcӃRtD?LJd Ν"9v!꥗^ټy3^^^<hcc\4N)DQqqqw]owysi&)Os 4Q;wdR5J/KmO?;wfƌfRF8̿/`/vM'&jvuu?sӦM\XF1-q>ӧy'V1c:**jH_&Ǒg=ʵ^+L6˱,flz/o|mm=p5Qc/=b\Q'|SO=@uu5۷od2}2/vK'|r\+ˤ;(fl}}}Rb5MRQUUŶmۈ'880z"9 R[[lh/u蠭 ???f3CCChDZ-f)yn@@AAACRGGGKjAZZZۛ<<|{{{bbb8v&ʈjb޽{t 8*++9p#F 88zV[~6lذqRSSo~67T]?u쪬Q&Trss0a &))[AII ,\"""g̘1T*@*Ʉ PTֆ3r\]]Yn-2F#O2h rss9tz)lmmEPPP7xzz҂=EEEddd0m4ZT*%''VBBB%==g^d2]4Nz^L&#,,qNee%_|J &w^ƏO[[hD%''w;6lذaÆ 6lq͌('DVc6DXX_ ̙C߾}C.$'zs,M455N8ѥryr9qqqGRR)\" Ϗ :?Kss3Æ h%J)--ERRh4rYjkkijjB"\b(HJF!88BJ'''R)JR)o)jÆ 6.ȑ#o]UW_ Vvzu͌(ш`` :VKѣ fxQ9\n]$--M T(d2>.+t:RXON4:Ø1c8~8UUU=zӧO3bz= yTxz;;;ȑd( ZZZD}2x,r8d2lvlذaÆ 6lظ\ zСC8q""(++337E?娪Ç3yd&MBҀZ-@Gӵ싚tF\\\O>M6a4 "!!SrJx z)> &Lh4#b HJJߟ3g"=x~Jcc#:N,ns,wwwYd ˖-~ۯմٰamhdǎL<wwwՅۛF/_neZK/QZZJaa!w裂XRR \.gҤI߿_|=55aDuֵb6???-[vU|W!ׯ=@&Mikk YF<$o*cNJ:]w}w|MsMK3gRSS)))G$&&"J2d"sXp!7od2P(cʔ)b<{HMM'''^bcc/ s///N O&..3gJDD3g$<<\ +**BT@BB3fRF \.gϞ̙3}QYYI`` IIIDDD zbѢEܹZ<==0aθBpp'F ((4DƎߵ66lظٸq#?wu?xGxYju3LTWW#Jsozz:}X}1tP0`5 usYf̘_WΝ;U{}888:c4?>>ER?v,f͚żyhoog٬X//oƲRRz \f={$,, ɄD"A"`6 g…DEEYـ0`dBR)III' ٳgc6/*Jhh(,6m'O@*!}%::ZR$,,wyG4R)|rMO?M~7oލVwV/ҽ,+O>}\#ʆ 6l ŨXIظ}1 !z+#*//={ffjeDՋ{[h0D9-2vV@*ҫW/opKy:SVVeC{w߱yfx 0`[lD]H [*,!(((`ݺu~m\$ ª%d2ݶϟEGˏ dz.C#GP__߃FՉSSL/ 44430gΜk~݋'OAMM @P0rHƍOONZZ{E3wܫU-@>}5jTPT >\0|p0ELL  &͟?_ {غu+888лwoQ*ǣQT]8{lKP(!11Q ۋw,^X͙3={0`Zطo_Z-gΜA*,2b0ekw%::Frssh ٢j,̝;{h$..>}Ftb}c< ۷o`00qD3]aзo_9y$ޖVA+olڴ7|^m%qKP\\3<ڵkoE|w;wرcoH$deeq!oRmF`` 'NmH$8ps1{lίe$##2V3a⦤[+VtvAn移byήG:^#^^|}sιs(..&(([9.//7`ڵxxxlq9b٘Fg||<^^^7I26lذTTTp݉DTڊ7ϟ?E#*;;/~JM6oWwl6s ^}U$ 7-5 WёXшڸq#GfW_}EAA<$$$ ]ׁ __~_~xzzRRR7|C{{;?u_/lذ(1@XXAAAW<۷~fΜI=sl`2h4338NFF$ !,,WDpڍ<{ft\.\xcÆ 6l\j#R޼ylݺ4z]w݅F???#"{yΜ9CLL Fjkkˣ"Ż%Jol6ݻߟǏS\\,Q& Aı5bxxMM $''#1 HRN>MSSr\ 7H$]ΓeA,0™3ghiiٳӯ_?\zWz[ ǵsxTdd$899a2 77wwFu)t:$''s䄷hTYd1V&j5b=-ϲ`ou} 1PYYIA^.GӨ9ɜ |=Lj B/բ߅i9y+)fFTJE wz]=cNhZۙ9s6I%55}vnSSS9r5nZ`Aٙʨ͍ҿ`ZD>|իWSYYÇիQy'==: ĝwILLE]mt l߾˗#/0wy)S0zhػw/?f…111=^zYҥKgٲeݛwyM6l2BBBDOVeժU| >,HOOחz6gL&Ņ3m4zy ǒkZ7MFbb"SYQɮ]8qRɀ4h޽{{{ƌiii_+L&|MR)bcc3fuϴsޤ'-ͣ6xW j=9|9]ظ4mo0n>Kj#3uuu@``UN]]ׯb@Dr9D"!77^xf̙C@@+VGyߔJ~~>;wGy7j9<ϟg۶mblH^^9\q IDAT١ݻpܹc0 {{{۷/c޽8::h_ٳ̙3(6n7?SQQ}8uÇg„ .,P׳~6meeel۶JF#J;wjyQ(j* 770ƌJܹs?^kRPPN`0P^^Nkk+T஬$..TJmm-& \=J>#zINNo zAPPDDD0{lQT@G=܃nnnݻӧ@[[[ z!®Jؽ{7%%5 \.ΝO>AT2f멩F9BPPQ;w999bdž:III$z7/;D [O>! 6lذq#QG]]T*z!zcVIJJpttdܸq >fxyyG}ĦMx뭷;PTرcm3lСCdddhTUUM6477SWWJŸq=zx&f̘''jF+oO?ҥKH$pBptt`00h qqpp)u)j5L0},)--HKΞ=ʕ+ח!C`ooT*%::BN<`رcTVV2i$<==t455!dUkIuu5v"??_4zͼy8p ӧO>-x g͍MF@@@TTd`ܸqr1ɉlΜ9Cmmu7P98u@G2gd2k֬hhhh0RXPD"innWWWqL3z)x$ ---`6qssC"`6l63i$+,[coo]f7ԣ>gRXX(+۷YfRDThtsszprt{êj3NVE@wXߐqi^y6np'6lvl2XԠqww8//NGttg2hjj_\L755hӓsQPP%KxWyyyIHHyh$++ z=;v`żS__OQQUUUFݑJ466RUU\.LV???|||}NN~~~b6EcRd2ODD=zeC.D}}=vvv ɺ]_|mFEEk֬aɒ%^D"aРAp1֭[ZOl޼w} f͚ҥK>}:!H ** BAuu5WWW\]]ioo Fs]z;v mdСCBξI^J~.i94igggYfxNmm-IIIVw2n K```Ka3/**B.hrqww*l4'00f3䄧hpىI&HDDW ??;vxYr%O=K.%99G*ܑ'QQQA^^Rwww1į^AǏ(V4؋ʲ$ ٌ+ [yM&[l!33e˖|r.]c=ƈ#hnnhfķ~˗_~),Zk1a?0x`<<<h4 Ν# @jݝtz) 'N󚺢~'OH{{;s FZ[Wppx-NGjj*,Xɓ'`@.bUZc6ioo%@``c5LpK0ƤR)UₓS'J`gU1x`6[q+讫fqq1---瓙)?|0IIIW+nLqq1YF:rͮԩS1͜:uA_gϞ%+ ڀ;wh0Qaa!G~E`${9y$}ujt֭ 4-[k.yǀMٳgbŊn2`eƏϑ#GXn]Ka cPTT7111:*륥1zhz-J~ikk۷/:`ٲeb7$<<ەNֲuVN:ŬYXh(,,dՔ֭[Fhh(gΜxT*KQQ+VՕ?:t8z)zpww' |Mls=G@@%%%R݅{3d,XO?2;w.}>>>Zp#'';wR__Okk+iiiKvv6yyybo%hF^K]OTFF---۷ɄBחV6oތNO>ˋ_~!C0e AVqFN>͈#S Pȁg>s|||8w'N ((Hk%5''Gx?ό30 ̙3 M0 ;vp< ikk#88777#LC q^;FolPЫɓ'ѣ=z@VgnYz5uuu8::wADDogΜahZJJJcݺut:{r:İtRT*JJEZZt:L&'OfرVj/D"e>|8>>>gW;g0LՑ̟'= t~!,Xwww.]zsu9y$f^SɴiXx1#Fё>}pKn:̙/>(+V 64كNl6STTD|||ײ,#M=ӧ>}:j;vg}dbь=l^z%+T*~IFFbꄅ0?F#w7JJJ 2*~ ''8;;@~pttٳP('((۷uVVZe]ȸH 篽[_W\|ٳ4mߎ6#}E* 55Hyy!ƮgOǍq$$oŅ#F鈊*;ܹsz ?0eeebNJ777,X@hh(L&T*qqqB 2w\vZR)Æ (<==iiiRIBB>>>+=bHdh42a"##ƻ?@l:m4<<zhJ%( qgPICCgذaXFGG3o< #̙32f*++`yvpp !!;S΃>H~~ĉ𠦦Dd"::z=z4z^\$%%zBXXhFϞ=Vh6:u*rԈ=^^^8::P(HJJ m\F\\...DGG888+x 13w4?Wŕ!-.zyy9 0yd~'z=rGki&x'IHH999]Qnnn3j(Q{޳mv8j:Dpp0/FѰd 4h^K w7L&%%%߿f]@BB:u Rz聋 vvv\L& gܸq <^ qwT*EX.rcǎ ш...( d2Ǐ+u/(qrrsקOBBBVA0`(J1yCaƬK,YӻwoJ2o]]{]p߲..X >K]EW}\/A@.ʀ0]4gg}s>>ח3gRZZʇ~HRRW^`O?~7yyy̟?e6oL{{;3f̰W]F.?QL.eLxC FWP@MT>iڶ]}M.noÆ B!VvUZƿߠЂԎ/`~+xQQQd2hkkC ٲe }ݘf9::c#Ì9b6n܈ Nj($11J(**"((RYF Xj 'OF/^cĉV,zuuugW=C&h@T~z󉌌ȩSصkwww1Ǭ;QW*Ԑg5'c7c.JOOo@ZZ;wGۗ#G444p1L+Y/4ıoiiaϞ=h"@II 錌 j5+WD"ˉ'UcםPyhHMM%%%I&1sn|!Vk2`h4ܹ^ONV>3;w.=h9s[>y<<<5k o67m154[`שwʕPx,YBW_QKK)Z#oz~tRMX*ظڹH$TIJń ;֏X0a:Z EhUZ7 8;;ϲo>fvuu%&&k#fߏ^G"0j("##IIIl6[-R:ă>huDquuEY]GVy, bggGNNvvv]z:oua>|꼗_~{ƒ{GHHgΜ!((gggƏwRRRpqqnURRBFF555ر(4rH/뮌KOO'00W^yT*7n$&&RQQ1+#+Y-m": ׋rd2><==qss~~~GP(P*ӓRb_w~ע/)) 6aÆn97QM;vp~:RWWNq 3\2cE Ҵu+BѼyr]V&!ɺ]9k dnWZcݟ%[N_ ={e"#p5vs߲zZ -H%P񺝝*^zQ[[Kxx#>(z+lܸQ 8޽{P(G[dϟ?ߪ2@= IDAT~#=zX]gV^^xF#aaaDEEeyŋ_M?>cǎ% Chjڈ!,,L j| }aԨQb pN,:r9fϞ-j; xr9MRRR!їuСֆJbhZѳz]whwbްaÐ?~JE||7Wܔrv"BQzQY0SY**!\рR7ܷ|-yyv}g~|BB8xy?{Gu/gfbuYdY^  b/=%$$7!@\H $`B3X-7Y꽗ݕVwbaK1yvfNsg6}xDŽ$H c9+!׿_G;BrQXX?uGꂹ~޽,>zz P?1:::i|.ȴ}{i,^<>fϞӯ6tvuL˗/Gv|U)~L&ݎn^k|W^e_8J%qW]Eŵ~o~Z=== A rYsF!N/~@]waX,$I z9jf+ eFױ +'ȅ&4ɇ:bRW3"$zu cum#@wCmMHe:_:;5;ǘ\<6/^[>L*_2weŲe©ŴZmP $H 3 QnG'dkrr7{i,?}$2_'͖laZZ$;^XrI9^e!]&/3v˲L[[-,8!g YIҩ1-2klD0 $ Y>{/Yq8 9ex II)ߥ3ى ..\]gxd0PBַyUly׎ZT:y5_W8} A Rqf!0~s(I:; 9zMĽx{{nJwt4kkklMCQ38hd> @||  ӃBqa{{!**XnUUU466~fF:_A`fkR/m2$ł8)ʹ CTW';'Ivsb?٦g[XⶴW\yx|b+,qoWznx.Ӯl8F ]{7m۶Djjj 'z&..ą!NUUŞcb={6jT*'1) 2<<ŋՆ00`4J%.R 21l!244ܹs7+~a*ttt頿s97U_prVI|>fLr>C%%%-)DEEyʎT)ɻBr!JJJ̢eZرc111.D:̏|4Ex;3$ErX Gb#M95|`dO?Wz']\55uۿ~^/III|>۶mcڴi' QVdtzzaxx R^\LZZ7p6(+;ʁ gJjzz{1<<̶mۨ@ o#>>>{5Afb0466HEEB%-[NatJ%o+I-;;;e6Ν;c׮̝;˗344Dtt 7n$֬YhvSWWGii)}$$s[Z{CSc#_ӎ0̜5C1nO288Ho_gIFIJJ"229sf<@II zOرcTTTEee%NlfӦp {˵]7Ya_I mm,Y>8zCAT A!P(x#̛7>J%W_}5:MYQ((( ..~RS׿;߹cǎRI҈M++rxaΜ zJJ!*DrNpa<73f̠;vP(رc'IȲLEE%S$\nZ-J3f0uGooh4ȅ6L"##h4/QXX9sfSPP~Hk[QQSIMMhLr;FOΔ)SG}$IbժULu)Tˮw9a}QZZ(̜9s%Ibǎرc[˖-;Gz\.ٻw/ ”)S7oy{nn7᭪| _KW}ƽ;wڊ&66 0mڴs.g'U TrOxr{{VÇX,zgΜ9?FAeHe…dB^(((@T#;;xP*a6III!;; . FͶm%::ՊBimm%%%IBPp(YU*@`#j AKK3LNjFTV0ǓFJ GT?jex؆%7w*iii"""xhnn&ʬٳI>}}Ԣ A!q8̛;!GˎqCOxx8 -":* Nm]t:z{{hjjd2a0'=wa! ޞ^fΜZ2(**BRGccHh4xj\wuD{KssW]5Dt:==L%,4Vb'2N.j5zNKXܵ )(GQ[WHLOO/HHH$44ϋb!22 rHO 66~o%Ԍ$Ih>c<>C~=tg/_8vAf͚u ́l6vEGGk֬!==uQ {^y1ZNwmww7O?4!!!T*|>uuuġC&ϗSKtt4+VLG}D}}=XV~mDQ;r>=zbf3,YdR^'YdӦMf"##߅oԜ`ҥKIJJRUUŖ-[PT\wu~~sW_};#0]NHٿ?pwb4*JJJhhh ::o90]V 3ܕef=JTT---\.l66""IGMXXHrDGQ1 car$DFF `ާTr91hDFF`jRimm""LOCBHUbhhBAXhDZZH0jbcc/=|>& Rcʔηo$dhMll))SϙV <ƐJua/>fvٳ1455ͲKIKKghh0("aaĒ ddd~CFFznMfWZ޽{; Grr2fcttt $#Iall,EEE$&&2`ȑèT*Ba6Δ)Ȳ." gi% qXVz{{U&$ Fq*5 }hmi!))y摐R && i̜9^ tvvQ]]CRr2EEEtZ$9Vr#".]v]0l6~ӟiӦ1ҥKYtfr}WUٰapBqTWWsg͚5NmmmT*>1<<̞={ؼy3CE^|Eo+\EOOO@ڱc <8~k.p8dt:}e˖1w\mۆRdѢv?ȭJFF(¡CBF#se鄄088ȻK]]jŋsAϟR_zΝˌ3 (~: D555E1sLZ{5׌T|_믧iկXh}+_a֢֭ۑe%K 2?<7tTVVRRRw|0؏n0_ix<nJkkIn 36cǎaZz:t ֯_$I?oz7|K{{;/y饗zK]lݺۍ$I8p1ޛoICCFcǎ1<<̷K\+HՈOlFBxȊ+(**d2188ȴiӘ5k _3T*|+(ʀd]w݅n'99HB $--fyLIHfNɄtcpp) ZhԬ] hjB&\YEPMEE*n0jjjlnBB244DkkFf-Z0xl6T*n+ D) Ncvv.6ȵ :SfDQlBHJJf|Gt:JJJP(,^r"2%JZFաR0 X,V|>! p8IN||< Et2:::hgppP*tuucimmEdbccIH/bu:6Z IKKd EFdYt fjjjhootlHtZ!DBNN t:*:;;?TVJaj"333vNOEE9>G^n.&h4b/*ߜ=ŏոiN^i~]ATs m۶ӧs׎ljj:0LH͛Xv-%%%l߾/,~GX,tV عs' SZZJ~~>JÇS__:$&&8< {/?~[?~e˖z鱗E}}=CCC \.[l Q' Q555C333f#I*.ۑ$Yf~zt8N^yJJJNˎꪫN\(sn:fl6oތ륨h믿>ny)))|6<mrpp{~L&|>O?4fH{{{/ꪀŋ)))'`B[[O{Y&O/$%%ֆV%**RWHHH6zpM7~zG7޽{'y!2ouuu|[ߺ,PU3gSRR|ֲfΝ޽{y!jDp֢-:9S( Brr2 XLBnnn`I_aa! rk|ȲLoo/H55Dzz: 3 /"""p:8]NT**ˍj%55ud@ʔ)X,f$YFTKL6f^MȈO7(JIKK#,K f3vR餡&rrrx( FbbIOO#""&q:tvvz1[MM-q\n4@\\BVKWw7*Ev3mZ'AS &S#im襭ԘT9d yRwSq~l#Ilٲ/}K'B|_fڵr0f&o8v7qv ͒R l裏2m4DQիWi& _=dee100n'!!˗b?9./0̞=3fobOII {/Yg$==}̆ ̽uuu2k,/^Fanv]V g`} QM O}R3#-N80z}fet֒J"&&lä$IDEEcwq\8PbeJ :mHI{DĠE<yy0ՁAzX qLt4))) 9t0]LjSs8r_КP* l55ܵThinvla䖛R>4͈Ip:]L餿A CQ HP 9r&̙B;=v顱|'Tl6Crr2FКFL^o )]; IDAT)A`dSFF#2UUUDDDАn"h4fM/o$2PpA{{;HKWW:+$΀_hh2Ն "$C[[YYYw r9E ,eJJ:hepphv;NYJ1MH^BOc3L2=ٳ\Ϣo40|زeI9N:::ƘUWW×d2a4)--d, uuu[Պd# ػw/.^`BD'^x,ӟZ楓R֭[ǜ9s瓑hʈb޼yv^}UjxwkNQ+Qb4QYYkj%""+_p\#|!+++Z~… ٸq#z-[rwO4>|zA@P0{+G__ߧ D j5n׿bݰs^PP@||iMx sFE"""HmiAx RS7o 8|]]DLGcpʢ~!6&RŊ+(++BAdd$aa(?1EH$*+ZKVf&* Iij2^Faݻn -Bkk+f&Sy.F&%e qqDGE$Z["<<̬L YYDFE~>$MMHLkk+6 } OII o&_K݈Jjk.n7SNERkF__?7xEx(+;FeU hB4 $''FSYYASSj AZZIIITTT(,,$44R%6&6tvGNF75 an hllł6h@1uj$a0詨GΓB]w|c?|>cĤrrrho>h0PIbb"|GT*DQ pvCq+ Q]]]1䪫:&!!ZDQ я~4n]|>9Oeʮ]Pqe2 js1uT某fdTߏN ttt$ Q,6ґ#GGkk+>xڸ{? d_ ҥKYd)˻PO9.7x#_36vSٳO*ky|G$:;;ͱcǸgM#G{_:_hlld˖-O#QpXp! . _>``A㤥 ^Pٽ{7r˥l)|{dffсR#ڵ3(l.'(9qWM 2s6R|ta %wq Ld޼y4$kd'l9Vx ?&#״i+LkCR2k,LѣGYl-ĂY0ĮOyʕc}=wLL +W~ii)&朜Ρs%Ғط?PDGE ,$++Ɉl&*2 ZM^^X,fG)tvvr]_ &DNaŊ ۱̙30ߜ*""G+ DGG~p"N'FA%K܌OhF&&˗? b0Yd)!!!*H*DfHyy9䐘^o j*f3$a2 Ƙaj4k23ӉV,T*9s&f\n7J)pzg=28~ƌcoָffÙ1cF~' qqq477fwnYɭJTT/2\.DQ el6xnz{Μq1Xa$j~4L֯u8 䓓 291w`˘QFO2e̎( ŒK||<JEXѨeԩŢYȾK̜9YArrQQQ#Z@EA III呓Mlllt)66v$y5FZ6[&''qq1$&&0uTrrGKizF)))b4EIOrRQd;5 DEF1N 8F;a4IOO'//J222%;;KcFP(: Ш'5QEYKLLdڵ,^3o<1ڔoKAAze˖~֬Y_E V1}S@?oN6`7A0ho}[;DDDzjV~b#j WfӦMss5`0OC6mژDvqe'͌3?en޽4AYv-7t7Cp'bXؼy3&S(7x[oͦ _„LG(;zӧupp(QB{{ۋILH8?n˅JBd˖-摙q;\|"|;w?):D1s0Are iML<}կ~ţ>? o^^>EBx;rO?47o&""uP(شi?O8v3f`ݺu~{Yre@/p8馛|Af14'#22򗿰~zl6\s ʤZLP$;+GtRWgҐeOK]ݨT@ A ybի)񏱽EB>8O4D>{cʴiM6rItѐ #΋\'葘<'| y,_yJ6}VmMXJ ډg۷/t(..FKqq1p>X\\|֑oυ )cw>Cᅬ{$<8@h/Zk1^IsτوiD![ #l|Y"璒! Gߣ0&R>T(Y%FY+wҨ4P* 8g4md,N/'gS 7DHR9Ok+==xZ11(cbP#t]A<9 I>n7,# JQ/AFvX/$$ Q@ Iz>JjNJw1Q!8wuuRG8{,xcr:|Pd $1y*DIBp[@?mm$'͛.u­R/+'#qg>>N<}t+vW5H A=5LʪMY/h _MM 7ERa0ɡȨ(D磳Z( 9u6RY]FikSr뭷RTTZfppb*++eժU_`q8ȲV $xZ-zÁnGQ}윈_Bss3J-]DFFs\^ EB4!( \nW@EFrlz|>h4 nD__۷opEV(XV^/jшb$ "*괚$YߧnZ-ZVHFE<,c2eÁEEj5j3˅DT"I~z[RVa].EBNCE\.ޑ$)P^gWT#!q8 vjT*$Ir$ R(vP(0 2dYp8B蘎gu=Gep\t:j$Il=tuu1\.<O\Imxnj \CT".+) juA>{ [F,fΜI||YD 66=r|g…wef(̜9Yfa4o~sƗNcҥ̝;%%%FjǾ}$))o0/))Füy1cF`Nomm7߿5^'ֿ7[,lܸ4 sΥhl߾!M^VVF?DEMy~+MҥKj?SUU1Lyz%էϴeXlDFFRh4j*fժUtttgq$&&r7j1 4550{\ṙmǎ$&&@MM GN7#V[[ˆ 3g&22k׎1bv ٳgdF#GrAIJJbjvݻHD}}={졭 iӦq@ee%^v;'77={pqtꪫ9}^/===X,"""`JJ  ?!z<aZ ѢT* ᠡ^DQ$DBBbx=^**+ho$P)UӃ&55tTus:qFBBBX,0o<o660/_N^^(˱cFsR(//wk!66H-[0%u\qABVb illdhh!f^#G~PTtwwhdhhBXXEEErصkuuuȌ3"h``IFE)..f EHHNNFE۩; 2|NJWWtvvt:h4dee0^@X,}.0z<8zh. ̙3 h;Cpq<h4|>---ٳϿIv!++0JKK`0 2$ԩS줺Պ!44x<MMM`ZsFFiiix^JKKfKbb"s9fE+ sw里no| eT:VBBB͝t l6sAzXV|X;v젨(pw}7 @m۶W^yXT*}|ߦwy#G`4 b@(?Aii)Wf֭n,˸jjj7ߤFcF#tuuP(eUVV?H4 ;wf?{Nvv ̞=iӦ͛7OT< xb'\\Ȳ̮]OJFFK.| ቎FaZgeժU|_Eey^6mڄ餻Dz=w ''={~?r~Ò-ZDjj*_kײm6}Qyo`2Nqq1!!!|_g0VX(mݺzo}k ? Hoo/GFF{/YYY( |+H}< ?c$vNK)ظX4͛7 IDATƼgx}^srp:UjV,_, نصs-\h0MM)QޣYd5kLiӦ1sLrrr۰lܹ={p=֭[yGͲep8t!d륺fn݊#** NGBBj>JKKyWXf 7|3qqqW^gʕtvvRQQ( tk7ޠuqSOeΝ˚5k8|0/"|[f144իگ ~dzzz8x ˗/g͚5Mذaw xMF 6{n֭[fW_%22ޙEy :. nW\h554hYoooΟ?OII *իWsjTUUqQ*++IJJL&%?.֯_ٳYx8VUU믿'|c=ba<1|pѽ{wqOJJ V_|QXxŷWٳQSS=Ù3gd޼y̞=Jŋ/Hrr2M]]%%t,))CQ__ٳ?>m۶ 7nڵkΦ^xŋO}vz)f3GfhZF#IIIw}߿9sˊ+8vXh:u*SNڭ=_}wuO>?-+-[h6m̘1ӧO l&&&y駟RUUŋ /Ν;:t(EEE/@1r3z=+VϏ_|S]̘1N',ٵkPx֬Y#BՊFqSl6,[ 'zj/^; |c}VǓKII ꫯҫW/fΜ)̎;78n3eeNqq1_|һwo-[[O?d"++;3hkkJ wyǏ_hŝ{…޽ V\ɖ-[~Y2:uSm6$R^^Nyy9 ,\ZMjj*/J-m2"Rl6{xzztҫa:`񴶶RWWFċJBVZؽ{e2vXZZZhjjb„ הb؈V%--ML`ӦMl6z-QG>}Z-^XXHMMUfEZZqqq>VgyIϤT6Zm~Ӟ3rH9{;v`_z̡E ۛ2 (+/hq5NkĈ477DTT^kߥKέ @\U֬V+/_CxhW5LJaÆQUUŞ={ٳg'66V+iii{ov._ٲe  `ڴiOu &Э[7fhwZ~=z^h4$bqIDDDKkk+v"''fW2|pf3ĸ677p8&((cǎa8y$ 994 W^wj[[UUU䐗'&J|V.g&..N,l6QTD"kMM >>>8jt:Eζ6l"b*err29r\"##5jƍ#::1c_/+V]322dL-n̙3dddZ"6q߾}C G1a><x{{FChh(TVVIJJY[yFŔ)S;v,GҥKFN';v梎^{w LLL 臨V /{?__BNe"''ļyDy,**"%%w=/'O/#??_(Mv`0裏amX,9r233tpxpA~~\,|'Z{`M{[Hy# S"11VmWZ[[Gkk+|(k#&&={ޞ0Jxx L0AXhrssϧYf1dF#W\!44DZZZD\2ɰZv?aaa466ꫯbXXp!iiiuV=*JVf3MMM$''R/Yz5DFF~>S"##QX{])9|%cԩ???Ю>[*jЫ=<<8<Çcٻw/;vogԩ+BBB*rXVt:{Dh4KLL fv;QQQ=$dΜ9_$P^Zm݆FСC?Nƍq8̘1~4N>Mqq1ΝcʕhG_ĉbvzRR/o5ʻD)qUUU+! (//gժUZfҥFbccEuu5 wA.A׬Yө|_|=Ju]{nߦKW石b ;Y}Q~ōMywL&7`z5m4\Œ3!==Cjy뭷bU|I:e2rKZWii,477$],}Y2dH.8N![aa!squl Jluu5[ne֭hZ&OLϞ=HMMڟlv_-EFFg#---<#,^FCLL 111BS,Q*;u^^({˪1~xq؛#G2rHĉ9z(G)iX,f$d2 |JJ ݺu #$$h4Vh4"×Kܽ*VXX455 }kk+555"V+)...B7nÃ=zEaa!!!!V1LX,Z[7VB) EoN';w$??<<oK.hHFFcǎ%!!ñZXVBCCE[)--%==9s`҄RJ^^Vz ~6 Npp0~~~fL&h4l6_5 4@z=#** J%\&l6 e?&aÆ@UUyyyfHCRQ__O}}=:tO>0bbbDWUU6֊L*J(Qo?>>Aii)Zٳg_ f1yd/^̒%Kӧ&wMrX,l߾mFcc#=Ⱦ)SN Q&Gw7HIZj=ᆖQyvUZK%KN(_<V^-dEEEӭKGf3o&:{/"00 .0c oLUU!!!455ƈ#. ==tRh… Ÿ`0f:t(/v/)n6 Ç)S_`4innn26oLTT՜:u@4HcGTTqqq>|X(b&IܸI}}1V\I^AӱaN8sp8<QYY|>(kp88{,'NG=z`1W2Ls"z˅QdKII.Ǐ*^Ċ£(A:]{DBCCQThZΟ?ϻKxx8'ObZ,QLngƍFJJJ8y$*wN~8z(}p8 %..???FG}$,fdIHH0XVzyyj%''HMM "ˋg|2Νʙٳgٴizݻw@xxsMABBf֭ 3g֭[ )+**HIIo߾TVVCxx8jZd(:th-[Ç3}t}!pcKnؽ{7}&Mŋ;<hZN>ܹscTTT㏻=d.]صk*quJ۷OOOC]]k׮%11#Fx 4[t)rhZrrr/2dWwСC@SG7$~3f .]rWY|9<35Kwyf7eɓ"Skڄ^~e-ZDqq1?0gϦo㮻BR{2sLLFF|GfO^v^hWz)0;;{K^q&NHFF65kְm6L¸q;&j#ɫ\›oѣG|k*XVtx{{V{ *MMM aCCCݻ7ZJJJ%11^OCC0F J˗)**JE||<)))Tڎ( )1` .@XXHmm-n+X'N`0 --ݻS__`Z֖ѣ߸Q\uS*))ĉXVN%ϣ %((K.v466 ja}Sd|2z"##xgPϝ;dfR]]Mnӧxql6j]fk%]IDAToT58 EPYYyyyl۶xdu&Kռ&1f2<>>^dЫFC=0L+b, .\ &&hF#:N/bf%xZZZ$''z" @&pv;$&& e`0xw8q" ,I(.^`p@TT111Z_~t:9y$Af0 eooo'ŚhDRBBP ĕ+WHLLGl6"OUSSΝm Aբhk`Inwh(--͆'aaat޽˘CWx]_d۶m|WL4;RwqݛHIw߻wo#63gpqjkkeذabK/`T*El}q ;)uuu|t:ݻ7 "**Jsw^Sb4ٳ"6p&W;ʩK=zJjjHTl2͛/Fh~qСC۷O( Ce~z4[Zq%+55jPQQAHH :T|KN:Ekk+dddRXr%fv˗/l6_S..e)OONuF6n܈` %%L:SSSCXX "33d6:PVVF߾}صkv&{)**BV3rH233\dٲeXVz)8z(里=z`۹t=S2?N")+]vQ\\/KfRR&L@Ѱj*~߰dYFF~_Y]ۻpBO'q/{e޽=}w^Ν;'5]Y*Q+{̝;W(O]26ytUfI}U~׍b]פ e*kǩRT*pEC:7tqV~716+.\KV>Wu\\qAyn;k?vlkK/DI~ܹ۷3vX&NkI[n%;;b̙ի9tP%[֝Oɒmu+efd6鍯Uǵ&`תzu0;W|x̘yꩧIH~,|/% >%ZHI'H$ODyyyq֯_/-H$u //W+KH$Dd*^JV%,,'N]%D" OOOjkkvtH$Oz|畕444гgODI$xzzRWWGCCF#H$ɏl~|tD"uD"H$?EdtD"H$D"RH$D"H$@*QD"H$DrH%J"H$D"HnDI$D"H$M (D"H$D" %H$D"H$7T$D"H$&JD"H$D"RH$D"H$@*QD"H$Drx_\nFRpH$_<==Q8D"|' dmIENDB`sqlkit-0.9.5/doc/html/_images/menu.png0000644000175000017500000002442411421620210017154 0ustar sandrosandroPNG  IHDRI\sRGBbKGD pHYs  tIME : IDATxwXS]A@D mŭ8q8nĺWujkUܭuU "R IL$ y=y=7q{dd@RTA! $:&fukEkS^!A@RsqiM魅y!$]?=Nz#Dy[NŋhӾ3rss!Hfzj^6s/79%&OKx5Agf*(]P"[Ϛo'pu]\y+\4+[%ϥWЫx4n~ݱ`D,e_LR.EEEXr5Zim/֛V[A*ʷm޺cF Gp5x ͨ+J*,,D.q5jo\zUɯM^uޫW2*},]>+O]4qz;;2"ʾdR._⬴KWkX:6mEФ 022z=zS؟b{VY qh  :6P&?TF(]>Wٱu3lہ[ YݺVHt-KF_ceHݰnf|n݋5a668qOi3hM?;a\|s/­wWΗ/o^ml)4//zj a}db$>z3x 0`!%5 Y3|+ 3+ L,]Jq !?fx6Me+W6s `R[.C#|rBNN.Xtp8pqv ۃ&MDV-1rX4ngg5+W`5hҴ7*-[6k̙5йvm(kϤ 1p0Me+Wݳ;NCXZ_]KSٗ\fL 7W70@֛փt4?敠z "Jq 0'%TDA+y6Alٴl6|h=RV$ʅW Ե R SNm+}aLyWX=EgQ+D"K\!j+**!Rtw^%̀ǣ!.*a'!.5Jf%0`XɅ QJgO1rl<O>E8'Oh]v D")~W,ɓ'HOOGfЬY3όE߽[oI&07;8eZJuk5|qVqEjԝtٛasqqйsgx$<c /&>NaU`r!;8KQFϞ=Sd⓺uP(*?Fli:Ż}hwwyO@{__wU-FMNuSc8U 9k6\=дYZMIJJF@X{x_ >gѴ+V§Mŭ"W#~'h4TP􆇧7΃P(Tcк/:G>xr2ǏPؖ-sM]56y69{{# p,^gd_az0 ^ƥ{(5rW(ϐ *\g~u7ƍ ]¿~-233qz$Ο;W^18qbcsnXT龲˴Gdl޲ܙS #˗Ph;s /RƽzHOkWpeRWQQQ<~} $tr1}֭ߠb߰'ƴkS,0ѽ;s1&+3IMMf&3]Tm ?Wg_|旟fRSR⟏1d֝elO&+;}TU0dgA~.gܘĄx>^^^L|}RpM,kMexJ~W駟2cߓ?e5jOS>OOOXyyy)2-E!?ZϏ^F<x{{3YZ]S9jҟ{J_MMMzJW~üj405c1f՘d hVpzكo-*Eff&]Km߃oY@Æ-NN͵ܾ}V@\\ *+O錠 [[[q|<995Khii)bX͘> +Vu<Z]rԔ7?ڶU;SUby p9fŬYl9EGçg(,,D]kkEll8?͛^ÆiO #,<X$LaMfxFF"99sCUH9'Oyb1>DPT4h /Y>>o/ I3ŒӰ}9c N{ec 2t~m/5jr?SSSH$Y,4j>>>߿?1Æ ðaC1bp1puuEbb"l0%k$>z4]F`n]@u`^h,,,ѪM;t*)4֭EXx8ybѺU ))Aӷ,SѶm|1K{ 8k[x |woйvp8`kJ{e\"R(88rZlv]0oj;pJfw}OD"!,{KKK|h! HzJq4Zp^>VBehزe+Μ^^^J1C$,\9Rm|]uE&M ~~~011A~~>YHRc?={]*U M},23`jj(ĉwį*07Dsss̟ T 333:G'8:6;sR"kT%* ǃ z?\={B!8  H A!ļI=1'b^^TOZZ6mی4]p5&,ĞiO`Qбŗ8j.j\+>ʖбØ5\]1%k:vlIфXJ hύs#r2 Eb!FH,DiHH,$&ǡ*~a̚3S3s.**fc_mTAq$ذi-OwlMk$Ś1u:fLQwOlD0Mhp"vGCgWn~@WT%K3xz7;wôQgNdKS\>cgRl6ӛjY=yFFT$F Nً^FkW\m.rbYXX,cK Kisc[8,sf̈́H$BXRӇJȡG178whL?|K+޹XZXԪ_4@!W^ !TWkؾ}'Vü.s2? bDUw*u`HIME־01+~޻HCԞ.eP+Tcu8אiA@B[ 5 ~'Z ^~)H)`-0QX5y|%\0@Z[*m5թ1۷9XK0ds&;jXD7d֤D^Rׂ0""''G-ł :9_MTakW'$ZRGn[X@,VEj2kQXX={[w$Y`9E`9uO?Ų_bw4Y5 Z4%a! aQ$**F~?\rE]Y@?,BX$cz]b‡>YXMXׯaee%.Qd'i'6l܄ Kѫgy\T 5 XmPlzlǓic?ZΝ:)('<;;;ˬ1kvb 77W-x5!rqqP1@2IJb9{{# p,^gd_q:fL)k~q5?S&g@ @\\/ þ?#tWR5Y5 D'6m܌YgEZ bcc`aaѣƨ),0شy=rrŦ1ƴ;8~̧fΚCCCsBCY}|DBSU+((]}(ݹM (9om [9@Ec~ rykV U|Bg0(vM5 Mw11PO^^^fd"eOz CX._Ƹq}hZ-vo[M?t*FaD!4nL6D-bmܴ[lřKk!"0jHB$4tRRRԧ9 yl0%9q Aaa+LJ 8{4Q{XA!A@rRѯ㫮[nMM8QO.?0Lzrӣ)dCQ`85,zˇ`r20̛4vQw ̓,'; R$l >̓k`$E`}-X~ gD5L .jD"sw070 0,?07@zl9 WZ[Վ&+e;8~WgSr_uCT "P􆇧7΃P(zJ/R>6):ˡҨ Z{B+v2^`wk\N\Ç0>C QqD؏Ą8s -Sd[ܖhh )rn Y <rNڒ-[ȡQ“'Oѱsyx붾copw+^>##{8h3wpDƒr@HIzoZnSзOo۫OVmq`Rf-Zȡ5lܻscJsX3@ JI]A:$ҳ7B}9U%MV;Xh:*: ">|>T6Xy64dz'p`^q b V`g'k)*: "kkk|%%%4r͛7`ƎAB@CQ/B PPހTXXid9{Qg9TuD %K%4ƍc;! H Z4IDFş'Yk0v|MV;ZO B|ZPiY- aggsƳ58u <cءG';H'ӳX1v21B!U佝F!b H A!A@B$mv?$ hO`In\Ч믓zU.K5 Oa/йS'D^Gr2;q B]Ӧ#`hXYYEǝ W -ZAlkIJJF@X{x_-[OEQui1Ձ>}׮aРsgNEj2>c1q 6ݰ$l)V?aκGoԥ6";d[]8ypWQgOG߽ [[[6]}ZE\lWFEź,i1H-c8_:^zw疼2-E0.0dgh {eV?aɺGo4Rquuh^o?P]φukFpnJʬ~*.HuhJkmfN-ATWٳݻ/-`g_c:C: Z%M7cxXXX`1*_F;)((]}d+ 1io`A?ڶU@C -5bNH(N ټ<B/aUӂ\|CEFTgjjJ5CԞA:DD˜18x`?ZVqcoB.MeV97c3HXpܨ#F ;[˯Vn :)))h3{,,,WKfȫW xaG*_Kisaii)ë1R)o4q<{%Wt*Nً^FP ".ٓ(((StaCG3ue_*m!pk{G{ ++Kis<8bXe|3O!Hu</%'"6mBX:A], X,ap\qCB xoB͛~[rٳTX[[=VU/V㩣w000Uq޶2#TC}'DFF$ ^x+WE7ƎvB  ==|OXx8233%aa2_4,ӧa0s ʻ< D=k&8N] GAA~a|u߯g F:i~zu[_XY}й!0}X*MÁ 1La{yӛ ҉8v *WKo:DJ~<{hI!ʄ``$(3 4H'A@B$:T Q .j4o^Q ?-~x3W+4qo;,6Q&z+;kS|bi7y8+Z&j@;h&(K!e6 o_Ύ6`8LGp ;vTu|=}^xQkn qnn?D綞H"π #C$R"8\;pnZXby2d牰p,|:GP Cn0uwA`7gqadȁH<Z7u†ñUȉn]je9u!fqPP(͂,g Hpt+$ Bt@اY0Av?HQ~ aO3QfţNi{G޳m} ~][8p0:u7CyC QqD؏Ą8s &GEPI*6 UF!"@>#GW|缿yOC.uxKF4xR$Q(¹1^ǹ5T%{YߔJ}#baee%?YhcSr[E[|̜>տ.֩SՆؙ!/f\$IQ P !F^bY,B8ۙi]oQf_#ʶ{;m KNNkh]`c? Z9Y%[*ą5ec.j!w@eVdZ(o+mTcux\,9n?@ĥg QD. /G+ <no title> — sqlkit v0.9.5 documentation


sqlkit-0.9.5/doc/html/objects.inv0000644000175000017500000004410511714210376016260 0ustar sandrosandro# Sphinx inventory version 1 # Project: sqlkit # Version: 0.9.5 sqlkit.fields mod sqlkit/advanced/fields.html sqlkit.layout.image_widget mod sqlkit/advanced/field_widgets.html sqlkit.widgets.table.columns mod sqlkit/advanced/views.html sqlkit.misc.conf mod misc/sqledit.html sqlkit.widgets.table.totals mod sqlkit/totals.html sqlkit.db.utils mod sqlkit/advanced/dbutils.html sqlkit.widgets.mask.miniwidgets mod sqlkit/advanced/field_widgets.html sqlkit.widgets.common.completion mod sqlkit/completion.html sqlkit.db.django_syntax mod sqlkit/constraints.html sqlkit.misc.printing mod sqlkit/printing.html sqlkit.layout.dateedit mod sqlkit/advanced/field_widgets.html sqlkit.widgets.common.sqlfilter mod sqlkit/filters.html sqlkit.misc.oootemplate mod printing/contents.html sqlkit.widgets.table.modelproxy mod sqlkit/advanced/views.html sqlkit.misc.datetools mod sqlkit/filters.html sqlkit.fields.Field.format_value method sqlkit/advanced/fields.html sqlkit.widgets.common.completion.SimpleCompletion.autostart attribute sqlkit/completion.html sqlkit.misc.printing.PrintTool.prepare_context method sqlkit/printing.html sqlkit.widgets.common.sqlwidget.SqlWidget.add_temporary_item method sqlkit/sqlwidget.html sqlkit.db.utils.get_differences function sqlkit/advanced/dbutils.html sqlkit.widgets.common.sqlfilter.FilterTool class sqlkit/filters.html sqlkit.widgets.common.sqlfilter.FilterPanel.add_column method sqlkit/filters.html sqlkit.misc.oootemplate.Template.search attribute printing/contents.html sqlkit.widgets.common.sqlfilter.FilterPanel.clear method sqlkit/filters.html sqlkit.widgets.common.completion.SimpleCompletion.group_by attribute sqlkit/completion.html sqlkit.widgets.SqlTable.add_record method sqlkit/table.html sqlkit.misc.printing.PrintTool.add_menu_entry method sqlkit/printing.html sqlkit.widgets.SqlTable.totals attribute sqlkit/table.html query attribute sqlkit/sqlwidget.html field_list attribute sqlkit/sqlwidget.html sqlkit.widgets.common.sqlwidget.SqlWidget.add_not_null_error method sqlkit/sqlwidget.html sqlkit.widgets.mask.miniwidgets.TimeWidget class sqlkit/advanced/field_widgets.html layout attribute sqlkit/sqlwidget.html filter_panel attribute sqlkit/sqlwidget.html sqlkit.fields.Field.clear_value method sqlkit/advanced/fields.html sqlkit.fields.IntervalField class sqlkit/advanced/fields.html button_press_cb method sqlkit/table.html sqlkit.widgets.common.sqlwidget.SqlWidget.commit method sqlkit/sqlwidget.html lay_obj attribute sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Template class printing/contents.html sqlkit.misc.printing.PrintTool.remote_output_dir attribute sqlkit/printing.html sqlkit.widgets.common.sqlwidget.SqlWidget.sb method sqlkit/sqlwidget.html sqlkit.widgets.mask.miniwidgets.TimeTZWidget class sqlkit/advanced/field_widgets.html sqlkit.db.utils.get_layout function sqlkit/advanced/dbutils.html pre_display_cb method sqlkit/mask.html sqlkit.misc.printing.PrintTool.port attribute sqlkit/printing.html sqlkit.misc.oootemplate.TableWithStyles class printing/contents.html sqlkit.widgets.common.completion.SimpleCompletion.query attribute sqlkit/completion.html sqlkit.widgets.SqlTable.select_path method sqlkit/table.html sqlkit.widgets.common.sqlwidget.SqlWidget.is_table method sqlkit/sqlwidget.html sqlkit.misc.printing.PrintTool.template_dir attribute sqlkit/printing.html sqlkit.widgets.common.sqlfilter.FilterPanel.get_tools method sqlkit/filters.html sqlkit.fields.DateField.format attribute sqlkit/advanced/fields.html sqlkit.widgets.SqlTable.create_view method sqlkit/table.html sqlkit.widgets.SqlTable.record_in_mask method sqlkit/table.html sqlkit.fields.DecimalField.format attribute sqlkit/advanced/fields.html sqlkit.widgets.common.sqlwidget.SqlWidget.get_current_obj method sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Table.__init__ method printing/contents.html sqlkit.widgets.mask.miniwidgets.EnumWidget class sqlkit/advanced/field_widgets.html sqlkit.fields.EnumField.values attribute sqlkit/advanced/fields.html sqlkit.layout.image_widget.ImageWidget.set_image method sqlkit/advanced/field_widgets.html sqlkit.fields.DecimalField class sqlkit/advanced/fields.html sqlkit.widgets.common.sqlwidget.SqlWidget.add_constraint method sqlkit/sqlwidget.html sqlkit.widgets.table.totals.TotalObj.set_value_and_colors method sqlkit/totals.html sqlkit.misc.oootemplate.Template.oo_context attribute printing/contents.html sqlkit.db.utils.TableDescr.search attribute sqlkit/advanced/dbutils.html sqlkit.fields.FloatField.format attribute sqlkit/advanced/fields.html sqlkit.widgets.common.sqlwidget.SqlWidget class sqlkit/sqlwidget.html sqlkit.widgets.common.sqlfilter.FilterPanel.set_page method sqlkit/filters.html sqlkit.misc.oootemplate.TableWithStyles.__init__ method printing/contents.html defaults attribute sqlkit/sqlwidget.html sqlkit.widgets.common.sqlfilter.FilterPanel class sqlkit/filters.html sqlkit.fields.Field.validate method sqlkit/advanced/fields.html on_activate__field_name function sqlkit/sqlwidget.html on_init function sqlkit/sqlwidget.html sqlkit.misc.printing.PrintTool.__init__ method sqlkit/printing.html sqlkit.widgets.common.completion.SimpleCompletion.set_values method sqlkit/completion.html sqlkit.widgets.SqlTable.set_opts method sqlkit/table.html sqlkit.layout.image_widget.ImageWidget class sqlkit/advanced/field_widgets.html sqlkit.fields.EnumField.lookup_value method sqlkit/advanced/fields.html completions attribute sqlkit/sqlwidget.html sqlkit.widgets.SqlTable.edited_path attribute sqlkit/table.html sqlkit.widgets.SqlTable.get_selected_path method sqlkit/table.html sqlkit.fields.ImageField class sqlkit/advanced/fields.html sqlkit.widgets.common.completion.SimpleCompletion.filtered_query attribute sqlkit/completion.html related attribute sqlkit/sqlwidget.html sqlkit.misc.printing.PrintTool.obj_proxy_class attribute sqlkit/printing.html sqlkit.widgets.mask.miniwidgets.Widget.__init__ method sqlkit/advanced/field_widgets.html sqlkit.widgets.table.modelproxy.ModelProxy.__init__ method sqlkit/advanced/views.html sqlkit.widgets.common.sqlfilter.FilterTool.set_value method sqlkit/filters.html sqlkit.widgets.mask.miniwidgets.FloatWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlMask.set_frame_label method sqlkit/mask.html sqlkit.layout.dateedit.DateTimeEdit class sqlkit/advanced/field_widgets.html get_differences function sqlkit/sqlwidget.html sqlkit.db.utils.register_layout function sqlkit/advanced/dbutils.html sqlkit.widgets.SqlMask.get_widget method sqlkit/mask.html sqlkit.widgets.common.sqlfilter.FilterPanel.tree attribute sqlkit/filters.html session attribute sqlkit/sqlwidget.html relationship_leader attribute sqlkit/sqlwidget.html get_history function sqlkit/sqlwidget.html current attribute sqlkit/sqlwidget.html sqlkit.db.utils.unregister_layout function sqlkit/advanced/dbutils.html sqlkit.fields.Field.clean_value method sqlkit/advanced/fields.html sqlkit.widgets.table.modelproxy.ModelProxy class sqlkit/advanced/views.html sqlkit.misc.oootemplate.Template.document attribute printing/contents.html sqlkit.widgets.common.sqlfilter.FilterTool.get_operator method sqlkit/filters.html sqlkit.misc.oootemplate.Context class printing/contents.html sqlkit.widgets.SqlTable.views attribute sqlkit/table.html sqlkit.widgets.mask.miniwidgets.IntervalWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.common.sqlfilter.FilterTool.destroy method sqlkit/filters.html on_validation function sqlkit/sqlwidget.html sqlkit.fields.ImageField.get_value method sqlkit/advanced/fields.html sqlkit.layout.image_widget.ImageWidget.set_pixbuf method sqlkit/advanced/field_widgets.html sqlkit.fields.ForeignKeyField.add_related_object method sqlkit/advanced/fields.html sqlkit.misc.oootemplate.Template.set_pattern method printing/contents.html sqlkit.misc.oootemplate.Template.VARIABLE_PATTERN attribute printing/contents.html sqlkit.widgets.common.sqlwidget.SqlWidget.__init__ method sqlkit/sqlwidget.html sqlkit.widgets.table.columns.View.add_column method sqlkit/advanced/views.html on_after_flush_callback function sqlkit/sqlwidget.html sqlkit.fields.TimeField class sqlkit/advanced/fields.html order_by attribute sqlkit/sqlwidget.html sqlkit.fields.ImageField.scale_file method sqlkit/advanced/fields.html sqlkit.widgets.mask.miniwidgets.Widget.get_value method sqlkit/advanced/field_widgets.html sqlkit.widgets.mask.miniwidgets.BooleanWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.table.totals.Totals.add_break method sqlkit/totals.html sqlkit.fields.EnumField class sqlkit/advanced/fields.html sqlkit.fields.ImageField.create_thumbnail method sqlkit/advanced/fields.html sqlkit.widgets.SqlMask.clear_mask method sqlkit/mask.html sqlkit.fields.VarcharField class sqlkit/advanced/fields.html sqlkit.misc.printing.ObjProxy class sqlkit/printing.html sqlkit.db.django_syntax.django2sqlalchemy function sqlkit/constraints.html on_completion__field_name function sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Template.MULTI_LINE_MARKER attribute printing/contents.html sqlkit.fields.Field.get_value method sqlkit/advanced/fields.html sqlkit.widgets.common.sqlfilter.FilterPanel.reload method sqlkit/filters.html sqlkit.db.django_syntax.django2query function sqlkit/constraints.html sqlkit.fields.ImageField.get_save_path method sqlkit/advanced/fields.html sqlkit.widgets.table.totals.Totals.add_total method sqlkit/totals.html sqlkit.db.utils.TableDescr.attrs attribute sqlkit/advanced/dbutils.html sqlkit.db.utils.register_hook function sqlkit/advanced/dbutils.html sqlkit.misc.printing.PrintTool.odt_viewer attribute sqlkit/printing.html sqlkit.widgets.table.columns.View.select_path method sqlkit/advanced/views.html sqlkit.widgets.table.modelproxy.modelstore attribute sqlkit/advanced/views.html sqlkit.widgets.common.sqlwidget.SqlWidget.set_value method sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Template.__init__ method printing/contents.html sqlkit.widgets.common.sqlwidget.SqlWidget.is_mask method sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Template.render method printing/contents.html sqlkit.fields.std_cleanup function sqlkit/advanced/fields.html sqlkit.widgets.mask.miniwidgets.TextWidget class sqlkit/advanced/field_widgets.html sqlkit.fields.VarcharField.blank_ok attribute sqlkit/advanced/fields.html sqlkit.widgets.mask.miniwidgets.Widget class sqlkit/advanced/field_widgets.html sqlkit.widgets.table.totals.Totals.totals attribute sqlkit/totals.html sqlkit.widgets.table.modelproxy.ModelProxy.order_by method sqlkit/advanced/views.html gui_fields attribute sqlkit/sqlwidget.html sqlkit.fields.ForeignKeyField.lookup_value method sqlkit/advanced/fields.html sqlkit.widgets.SqlTable.set_editable method sqlkit/table.html sqlkit.misc.oootemplate.Table class printing/contents.html sqlkit.layout.image_widget.ImageWidget.scale_pixbuf method sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlTable.hide_fields method sqlkit/table.html sqlkit.widgets.common.sqlwidget.SqlWidget.get_value method sqlkit/sqlwidget.html sqlkit.misc.oootemplate.Template.VARIABLE_PATTERN_OO attribute printing/contents.html sqlkit.fields.Field class sqlkit/advanced/fields.html sqlkit.widgets.common.sqlfilter.FilterPanel.replace_column method sqlkit/filters.html sqlkit.widgets.common.sqlfilter.FilterPanel.hide method sqlkit/filters.html sqlkit.fields.IntegerField class sqlkit/advanced/fields.html ui_manager attribute sqlkit/sqlwidget.html sqlkit.widgets.table.columns.View.start_editing method sqlkit/advanced/views.html sqlkit.fields.ImageField.thumbnail_size attribute sqlkit/advanced/fields.html sqlkit.widgets.mask.miniwidgets.ImageWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.common.sqlwidget.SqlWidget.set_mode method sqlkit/sqlwidget.html sqlkit.fields.Field.get_human_value method sqlkit/advanced/fields.html sqlkit.misc.oootemplate.Context.__init__ method printing/contents.html sqlkit.layout.image_widget.ImageWidget.show_image method sqlkit/advanced/field_widgets.html sqlkit.fields.ImageField.default_size attribute sqlkit/advanced/fields.html sqlkit.db.utils.unregister_hook function sqlkit/advanced/dbutils.html sqlkit.db.utils.TableDescr class sqlkit/advanced/dbutils.html sqlkit.fields.ImageField.scale_pixbuf method sqlkit/advanced/fields.html sqlkit.misc.printing.PrintTool.pdf_viewer attribute sqlkit/printing.html sqlkit.widgets.common.sqlwidget.SqlWidget.reload method sqlkit/sqlwidget.html sqlkit.fields.DateField class sqlkit/advanced/fields.html sqlkit.fields.ImageField.set_value method sqlkit/advanced/fields.html sqlkit.widgets.common.completion.M2mCompletion class sqlkit/completion.html sqlkit.db.utils.register_class function sqlkit/advanced/dbutils.html sqlkit.widgets.common.completion.FkeyCompletion class sqlkit/completion.html sqlkit.widgets.table.totals.Totals class sqlkit/totals.html sqlkit.widgets.table.modelproxy.ModelProxy.tree_field_name attribute sqlkit/advanced/views.html sqlkit.layout.dateedit.DateEdit class sqlkit/advanced/field_widgets.html sqlkit.fields.ImageField.get_thumbnail method sqlkit/advanced/fields.html sqlkit.fields.Field.has_changed method sqlkit/advanced/fields.html sqlkit.fields.CollectionField class sqlkit/advanced/fields.html sqlkit.fields.FloatField class sqlkit/advanced/fields.html sqlkit.widgets.common.completion.SimpleCompletion.filter method sqlkit/completion.html sqlkit.misc.printing.PrintTool class sqlkit/printing.html sqlkit.misc.printing.PrintTool.add_to_table method sqlkit/printing.html sqlkit.widgets.SqlTable.set_fk_layout method sqlkit/table.html sqlkit.widgets.table.totals.TotalObj class sqlkit/totals.html sqlkit.fields.Field.set_widget method sqlkit/advanced/fields.html sqlkit.widgets.common.sqlwidget.SqlWidget.add_validation_error method sqlkit/sqlwidget.html sqlkit.fields.IntegerField.format attribute sqlkit/advanced/fields.html sqlkit.fields.Field.get_default method sqlkit/advanced/fields.html sqlkit.widgets.mask.miniwidgets.DateTimeTZWidget class sqlkit/advanced/field_widgets.html sqlkit.fields.DateTimeField class sqlkit/advanced/fields.html sqlkit.fields.TextField class sqlkit/advanced/fields.html sqlkit.widgets.table.totals.Totals.compute method sqlkit/totals.html sqlkit.widgets.mask.miniwidgets.DecimalWidget class sqlkit/advanced/field_widgets.html sqlkit.misc.printing.PrintTool.output_dir attribute sqlkit/printing.html mode attribute sqlkit/sqlwidget.html delete_event_callback function sqlkit/sqlwidget.html sqlkit.widgets.common.completion.SimpleCompletion.force_enum attribute sqlkit/completion.html sqlkit.widgets.mask.miniwidgets.Widget.add_completion method sqlkit/advanced/field_widgets.html sqlkit.widgets.mask.miniwidgets.DateTimeWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlTable class sqlkit/table.html sqlkit.fields.Field.set_value method sqlkit/advanced/fields.html sqlkit.fields.ImageField.get_thumbnail_path_with_size method sqlkit/advanced/fields.html sqlkit.fields.BooleanNullField class sqlkit/advanced/fields.html sqlkit.widgets.table.columns.View class sqlkit/advanced/views.html sqlkit.widgets.common.sqlfilter.FilterTool.get_value method sqlkit/filters.html sqlkit.fields.ImageField.clean_value method sqlkit/advanced/fields.html sqlkit.misc.oootemplate.Template.save_as method printing/contents.html sqlkit.widgets.table.totals.Totals.sum method sqlkit/totals.html sqlkit.widgets.mask.miniwidgets.DateWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.mask.miniwidgets.Widget.set_value method sqlkit/advanced/field_widgets.html sqlkit.layout.image_widget.ImageWidget.set_stock method sqlkit/advanced/field_widgets.html sqlkit.fields.ForeignKeyField class sqlkit/advanced/fields.html sqlkit.layout.dateedit.DateEdit.set_date method sqlkit/advanced/field_widgets.html sqlkit.widgets.common.sqlwidget.SqlWidget.set_format method sqlkit/sqlwidget.html sqlkit.fields.DateTimeTZField class sqlkit/advanced/fields.html my_values function sqlkit/completion.html sqlkit.widgets.common.completion.SimpleCompletion class sqlkit/completion.html sqlkit.fields.ImageField.base_dir attribute sqlkit/advanced/fields.html sqlkit.db.utils.get_hook function sqlkit/advanced/dbutils.html sqlkit.widgets.common.sqlwidget.SqlWidget.add_filter method sqlkit/sqlwidget.html sqlkit.widgets.common.sqlwidget.SqlWidget.resize method sqlkit/sqlwidget.html sqlkit.fields.BooleanField class sqlkit/advanced/fields.html sqlkit.widgets.table.totals.Totals.subtotals attribute sqlkit/totals.html sqlkit.widgets.SqlMask class sqlkit/mask.html gui_field_mapping attribute sqlkit/sqlwidget.html sqlkit.widgets.mask.miniwidgets.Widget.set_editable method sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlTable.add_row_filter method sqlkit/table.html sqlkit.widgets.mask.miniwidgets.CollectionWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlTable.fkey_record_in_mask method sqlkit/table.html sqlkit.widgets.SqlTable.set_field_list method sqlkit/table.html sqlkit.widgets.mask.miniwidgets.VarcharWidget class sqlkit/advanced/field_widgets.html sqlkit.fields.EnumField.keys attribute sqlkit/advanced/fields.html sqlkit.misc.printing.PrintTool.server attribute sqlkit/printing.html sqlkit.layout.image_widget.ImageWidget.sb method sqlkit/advanced/field_widgets.html sqlkit.db.utils.TableDescr.__init__ method sqlkit/advanced/dbutils.html sqlkit.misc.oootemplate.Template.preserve_styles attribute printing/contents.html sqlkit.widgets.table.totals.Totals.add_date_break method sqlkit/totals.html sqlkit.widgets.common.completion.SimpleCompletion.attrs attribute sqlkit/completion.html sqlkit.widgets.mask.miniwidgets.ForeignKeyWidget class sqlkit/advanced/field_widgets.html sqlkit.db.utils.get_class function sqlkit/advanced/dbutils.html sqlkit.widgets.table.modelproxy.ModelProxy.copy method sqlkit/advanced/views.html sqlkit.widgets.mask.miniwidgets.IntegerWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.common.sqlwidget.SqlWidget.set_records method sqlkit/sqlwidget.html sqlkit.widgets.common.sqlfilter.FilterPanel.show method sqlkit/filters.html sqlkit.widgets.common.sqlfilter.FilterTool.set_operator method sqlkit/filters.html sqlkit.fields.TimeTZField class sqlkit/advanced/fields.html sqlkit.db.utils.TableDescr.format attribute sqlkit/advanced/dbutils.html title attribute sqlkit/sqlwidget.html noup attribute sqlkit/sqlwidget.html sqlkit.widgets.mask.miniwidgets.BooleanNullWidget class sqlkit/advanced/field_widgets.html sqlkit.widgets.SqlTable.get_obj_at_path method sqlkit/table.html after_flush_callback function sqlkit/sqlwidget.html sqlkit-0.9.5/doc/misc/0000755000175000017500000000000011714210425014067 5ustar sandrosandrosqlkit-0.9.5/doc/misc/tutorials.rst0000644000175000017500000000027711714210425016655 0ustar sandrosandroMailing list ============ You can join our mailing list_ Tutorials ========= .. toctree:: :maxdepth: 2 tutorial sqledit tour .. _list: http://groups.google.com/group/sqlkit sqlkit-0.9.5/doc/misc/tutorial.rst0000644000175000017500000003755011714210425016476 0ustar sandrosandro.. _tutorial: ================== Sqledit Tutorial ================== Intended audience =================== This is meant as a tutorial for the :command:`sqledit` command that is part of sqlkit. It's intended audience is anybody who is interested in editing data in a database (as opposed to editing the *structure* of the database). No programming skill is required, but if you are supposed to install it yourself, you may need to understand at least a little bit of your operating system (but that may be as simple as a double click if you use bundles). Installation ============ According to you operating system and setup you may find the very easy way for you. You may not event need to know which are the dependancies that are explained below for the curious ones. .. _ubuntu-install: Installing under Debian/Ubuntu ------------------------------- On Ubuntu lucid (10.04) and probably also others >= 9.10 you can prepare dependencies:: sudo add-apt-repository ppa:toobaz/sqlkit On Debian:: echo deb http://ppa.launchpad.net/toobaz/sqlkit/ubuntu lucid main | sudo tee /etc/apt/sources.list.d/sqlkit.list sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 39012CF8 and install it as follows (this installs python drivers for PostgreSQL and Mysql as well):: sudo apt-get update sudo apt-get install python-sqlkit python-psycopg2 python-mysqldb I'll try to keep this updated as the official package .. _windows-install: Installing under Windows ------------------------ The easiest way is to use `Windows Package Manager`_ that handles dependencies (that can turn usefull for other software too). The installation process becomes as easy as: * download and install `Windows Package Manager`_ * start it, select sqlkit and install (you may want to select/inistall also the database drivers for PostgreSql and MySQL) If you want to go the "hard" way you can install all peaces separately but still use the Pygtk all-in-one installer (thanks to Dieter Verfaillie): * download and install Python_ * download and install Pygtk-all-in-one_ * download sqlkit-VER.zip_ unzip and install by doing:: python setup.py install Note that python may not be in the PATH so you may need to write it explicetly. Installig under MacOS --------------------- You can install and run Sqlkit under MacOS also. You can use the all-in-one boundle_ (thanks to Anders F. Björklund) or install from MacPorts. Dependencies ------------ Sqlkit depends on the following packages: :Python: sqledit it's a Python script. It should work with python 2.4 and following. But if you used 2.4 be sure to add the driver for sqlite that was added to the main distribution only starting from 2.5. :PyGTK: any Linux desktop distribution has it already installed, in case it does not have it, it should be trivial to do. In Debian-derived systems (e.g.: Ubuntu), you simply run:: apt-get install python-gtk2 In a Windows system it used to be a difficult task but now it's very simple. Please follow the instructions on the `pygtk site`_ :sqlalchemy: this is the core of the sql staff. It's a layer that builds SQL statements and invokes the backend drivers. It takes care of inspecting the database and so on. You need at least release 0.5 but 0.5.4 would be much better. :python-dateutil: needed for computation on dates, used in filters (e.g: date >= 'm-1', means date >= starting of last month), see :ref:`date_filters`. :babel: needed for localization of numbers and dates :setuptools: needed for the installation and to check version of sqlalchemy :drivers: don't forget to add the driver for the backend of your choice, the only driver included is for ``sqlite``, that is the database of the demo and is included in the python distribution since python 2.5. If you have setuptools installed in your system, you can install whatever you need simply with:: easy_install sqlkit and probably you'd have better results using ``pip``:: easy_install pip pip install sqlkit remember to install the backend driver, these are the examples for postgresql and mysql:: pip install psycopg2 pip install MySQL-python sqledit/sqlkit ============== .. image:: ../img/sqledit.png :align: right Now you should have a working setup. The command we are going to familiarize with is ``sqledit`` that is based on a library named ``sqlkit``. If you are a programmer and are interested in the sqlkit package you can find extensive documentation in the `web site`_ Programming with sqlkit is a pretty simple experience that allows you to use many more features than available with ``sqledit``, nevertheless you can do a lot of simple tasks by using ``sqledit`` alone. .. versionadded:: 9.1 Sqledit has a flexible :ref:`configuration system ` that allows you to add many code snippets w/o writing a true program, so that even if you decide to start with sqledit due to it's simplicity you can add more configurations as far as you needs them. I personally started using that as my preferred way. Sqledit can be used: * from command line, possibly adding arguments and options * from a menu entry interactively writing the URL of the database you want to edit. If you start it with no arguments you are presented a dialog with an entry and 3 buttons: * you can write the url of a database of yours in the entry, e.g.:: postgres://localhost/dbname # sqlalchemy 5 postgresql://localhost/dbname # sqlalchemy 6+ sqlite:///db.sqlite mysql://name:pass@host/dbname .. note:: the URL for a sqlite database has 3 '/'if the database is in your current directory, 4 if you need to pass a file starting with '/'. * start the demo tour Sqledit table listing ===================== The demo tour is meant for developers, so that it shows source code as well, but it's also suitable for our introduction and is a living database, so we will use that in this tutorial. The demo presents you some examples on the left. Let's start with... the last one! We start with the last one because it's the window you will see when you start sqledit with an address of a real database (the demo one in this case). The table listing ----------------- The table listing of the database is shown above: clicking on a table name pops a menu that lets you choose between: * table view: representation of the table in a spreadsheet fashion * mask view: a form with each field is displayed * table reflection: sqledit reads the definition for that table Tables ======= Let's choose a table view: .. image:: ../img/table.png each field of the table is represented in a column, each type has different representations: :text: a simple cell will render the text :numbers: each number is adjusted to the right :dates: dates are represented in you preferred locale that is argued from LANG variable or from locale module information :boolean: a checkbox is used. It the NULL value is accepted, clicking the checkbox will loop between True, False and undefined :intervals: intervals are really poorly rendered at the moment... :foreign keys: foreign keys are represented via the value they point to in the remote table. At present only simple (not compound keys) are allowed. To help you detect that that's a ForeignKey it's drawn in blue. Just to be pedantic: you won't see the real value (that may happen to be an id, normally not very interesting), you will rather see the value it points to... As you can realize there is not real *value* where is points. An id points to a record of a table (e.g.: director id 1 may point to the record in director table where ``last_name`` is *Fellini*), but *Fellini* is not the value of the id: it's rather a representation of the record that in many circumstances may be enough (and in many other is not). So I introduced a rule: I represent it with the value of the first character field of the line. Clearly this rules is doomed to fail in some cases and you can correct it forcing a representation of the line we will call a format field. You can go in the main window of sqledit, select databases and 'edit sqlkit field' and you will be presented a mask to edit the value you prefer. .. image:: ../img/sqledit_config.png filtering ---------- you may have a lot of data and what sqlkit will help you at is to :ref:`filter ` in a simple way. Each column has a clickable header that pops a menu entry. The first menu entry pops a filter widget: .. image:: ../img/filter-panel.png in the image we have clicked on three column's header: the filter on each column is composed of 4 parts: the label with the name, the operator for the filter, the checkbox to disable the filter and the entry for a value. Some operators have pretty intuitive operators ('>' as bigger than or later that for dates) text have also regular expression (normally much more useful so that it's the default) or ``like``. .. note:: you can select more filter for column, click on the label in the filter panel. You can for example say that you want all the films produced between 2000 and 2005, that means having 2 filter on the field year. Pressing ``Enter`` on a field or the reload button will run the query and present the selected records in the TableView. Dates are special in that you often have to filter with dates relative to the moment you do the query (today, this month,...) so that i added some shortcuts to accomplish this task (e.g.: 'm' means the beginning of the month). You can read more on this feature in :ref:`date_filters`. totals ------ .. image:: ../img/totals.png One more feature of sqlkit that comes very handy is the ability to make totals in the fashion of a spreadsheet. This only works on numbers of course, and you can trigger this feature from the column menu. Since our test database does not have numbers other than for *year* of production, in the example I joked and computed the total on the column of the year of production. In real cases you will do sum with more interesting data... Subtotals are a very useful feature of any total, so you can ask sqlkit to create subtotals when some value change (e.g: date, month, year, director...). completions ----------- When you enter data in a text entry or in a foreign key, you may find yourself typing something that is already in the database. In this cases you can have sqledit to search that text for you. Really that's a must for Foreign Keys where you can only pick the data among those proposed. Since the possible values may be a lot and we don't want to wast time waiting to retrieve data that would only confuse us, we will require sqledit to show possible values pressing enter in the entry. In this case the text that we may have already entered will be used to filter the possible values and to be more precise: :Shift Enter: will trigger a search using the text at the beginning of the field :Control Enter: will trigger a search using a *regexp*. If you don't know what a regexp is, consider that as a minimum it will do a search of the string in any position, but can do much more and really also depends on the database backend. :Control Shift: will disregard what you have already written and do a search on all possible values, thus emulating an ``enum`` field. You can find complete information on how to configure :ref:`completion` in the docs. changing view ------------- When in a table view, you may want to jump on a *mask view* or even keep the two open simultaneously. That can be simply done by clicking with right button in a row: the menu that appears lets you edit the row with a mask. If that's a ForeignKey column you can even edit the value the foreign key points to. Mask ===== .. image:: ../img/mask.png :align: right The other view we can use is the *mask view*. The records are presented by default in a form with the labels on the right and the forms on the left. .. note:: This is just a default and the only one possible at the moment, but programmatically you can choose any fancy layout you want, but I won't digress as I want to limit the information for non developers in his context. completion ------------ In this mask you can see that foreign keys use a combo with a completion element popdown. Same shortcut as for the table one are used to complete. A double click on the arrow let you use it as an enum field. filters -------- Filters can be activated clicking on the label. the filter panel will be presented as usual. The difference is that when the query is issued the result is presented in a tab of the filter panel and you browse the results clicking in the output tab or clicking the forward and backward arrows of the mask. layout ------- If the table has many fields, you may get a layout that is not very usable. This is a limit of the interfaces at the moment, not of the sqlkit package that can handle any fancy layout as you can see looking at the examples of the demo. The library also allows you to edit related tables (i.e.: director and movies) with no effort, in order to do this you need at least a minimum of programming, namely: * defining the model (as per SqlAlchemy) * defining the layout (this is very easy and demo has plenty of examples) These 2 definitions can be written in the configuration for the a nick of sqledit, please read :ref:`sqledit manual ` for details on nick configuration. The Demo ======== The demo is a pretty simple way to be introduced to more advanced features that you would only have with a little of programming. I hope it will encourage you to do it and possibly to approach Python. The very important thing to understand when reading the snippets of the demo is that each time you write the table as a string (e.g: table='movies') you will trigger an inspection of the database, but no assumption is made on the relationships between tables. When you pass a mapper or a class (e.g. class_=model.Movie) you are passing possibly more information. The model in fact (you can go and see in :file:`demo/sql/model/movies.py`) has lines as:: class Director(Base): __tablename__ = 'director' id = Column(Integer, primary_key=True) last_name = Column(String(60), nullable=False) first_name = Column(String(60)) nation = Column(String(6)) movies = relation('movie', backref='director', cascade='all, delete-orphan',) where the last line instructs sqlalchemy of the relation existent between the tables, and more: it adds an attribute on the class ``Director`` that holds all the movies produces by that director (and vice verse thanks to the argument ``backref``). Adding these information makes it possible to used the layout in a mask to produce a mask with director and all the movies, if you are interested in this part... let me know and I will add more info. For the moment I suggest you to go and read more about :ref:`relationships` Feedback ======== I hope you found this tutorial useful. If you like this piece of software, have suggestion on how to improve it or improve the tutorial I'd be `happy to know`_ cheers sandro \*:-) .. _`download page`: http://sqlkit.argolinux.org/misc/download.html .. _`pygtk site`: http://www.pygtk.org .. _`sqlalchemy site`: http://www.sqlalchemy.org .. _page: http://sqlkit.argolinux.org/sqlkit/filters.html#module-sqlkit.misc.datetools .. _`web site`: http://sqlkit.argolinux.org .. _`happy to know`: mailto:sandro@e-den.it .. _`Windows Package Manager`: http://code.google.com/p/windows-package-manager .. _download: http://code.google.com/p/windows-package-manager/downloads/detail?name=Npackd-1.14.1.msi&can=2&q= .. _Python: http://www.python.org/download/ .. _pygtk-all-in-one: http://ftp.gnome.org/pub/GNOME/binaries/win32/pygtk/2.22/ .. _sqlkit-VER.zip: http://sqlkit.argolinux.org/download/sqlkit-VER.zip .. _boundle: http://afb.users.sourceforge.net/zero-install/PyGTK.pkg sqlkit-0.9.5/doc/misc/contents.rst0000644000175000017500000000021511714210425016454 0ustar sandrosandro================== Download & more... ================== .. toctree:: :maxdepth: 3 download missing backward_incompatibilities sqlkit-0.9.5/doc/misc/tour.rst0000644000175000017500000002110511714210425015611 0ustar sandrosandro======================= Sqlkit & Sqledit ======================= Sqlkit is a tool to edit **data** of a database (as opposed to schemas) in the easiest possible way. By **easy** we mean both: easy to write for the *user* since completion helps you, and easy to write for the *programmer* as a lot of features are there to help you customize it. It's based on PyGTK_. It provides: * a GUI named :ref:`sqledit ` that can be used standalone and does not require programming knowledge * a Python package that mainly provides two megawidgets: :ref:`mask` and :ref:`table`, very powerful classes to build your application. A typical usage can be:: from sqlkit.widgets import SqlTable, SqlMask from sqlkit import DbProxy db = DbProxy(engine='sqlite:///movies.sqlite') t = SqlMask('movie', dbproxy=db, single=True) that will pop up an editor for the record. Filter panel will be available just clicking on the label of the fields. .. _sqlkit: Features ======== **Main features of sqlkit**: - Editor of databases in 2 modes: :ref:`table` & :ref:`mask`. Mask can embed tables of :ref:`related tables `. - Based on sqlalchemy_: can cope with many different database backends (PostgreSQL, MySQL, sqlite and firebird the ones sqlkit was tested on). Can be used to edit any selectable you can build with sqlalchemy. - Very powerful :ref:`filtering ` capabilities: * each field can be used to filter visible records * date filtering possible also on relative basis (good for saved queries) * works on expressions too * works on fields in :ref:`related tables ` embedded in mask - :ref:`completion` both on normal fields and foreign key fields - Very easy way to draw a layout for mask views - :ref:`sqledit`: python script to edit db - Completely effortless editing of :ref:`relationships` - Very easy way to set :ref:`defaults` - Possibility to display :ref:`totals` in numeric fields - :ref:`constraints`: any possible sql constraint can be attached to a Mask or a Table. It can be expressed as a normal sqlalchemy query or with django-like syntax. Works on expressions too. - SqlMask and SqlTable are widgets with several :ref:`signals` - :ref:`hooks` for a very easy customization of behavior and for validation - More than 70 snippets of code to demonstrate each feature .. _sqlalchemy: http://www.sqlalchemy.org .. image:: ../img/table-demo.png :align: right :scale: 50% :class: preview :alt: table opened on movies Table ----- You can see data in a tabular format using the :ref:`table` widget. The code is as simple as:: t = SqlTable('movie', dbproxy=db, ) you can customize which columns to show, possible filters or constraints (see below), and a lot of others details Mask ---- .. image:: ../img/mask-demo.png :align: right :scale: 50% :class: preview Records can be displayed one record at a time with the SqlMask widget. Tables can be embedded in mask to edit :ref:`relationships`. that where requested by the following instructions:: lay = """ varchar10 varchar200 - - - {N { %time {>>.general date interval datetime time } {>.hidden_boolean bool bool_null } } { %numbers integer float numeric } { %text text uni_text } } - - - """ t1 = SqlMask('all_types', dbproxy=db, layout=layout, ) Filters ------- .. image:: ../img/filter-panel.png :align: right :scale: 50% :class: preview Each label of both views can be clicked to get a :ref:`filter panel ` through which we can choose an operator and filter records. Filter let you use a "human" representation of foreign keys, that means that if ``director_id`` points to a numeric id, sqlkit will let you write the (last) name of the director instead when filtering. The filter panel will let you navigate in the output list, that can be completely customized. Completion ---------- :ref:`completion` is triggered by F1 key, Ctrl-Enter or Shift-Enter. If the field is a foreign key it will pop up a list of foreign values otherwise it will show values currently used (just for varchar fields). The search for completion is done using the (possibly) already written letters as filter: Control will match them at the beginning of the field, Shift (and F1) will match them in any part. The search is made using ``LIKE`` operators or ``regexp`` according to whatever is available in the db engine in use. Layout ------ Very easy way to draw a layout. See :ref:`layout` widgets for a tour. The language used relates to glade as a markup language relates to html. This GUI description language lets you draw a layout using field_name as place holders for the widget that you will use to edit it:: title director_id will be replaced by a label 'title' followed by an entry and a title 'director_id' followed by a widget suitable to edit a foreign key. Le language gets more complicated to let you use main gtk widgets as frames, notebooks, scrollable widgets, tables.... Relationships ------------- .. image:: ../img/o2m.png :align: right :scale: 50% :class: preview This is probably the most impressive feature. You can build a layout in which related data are displayed in a totally natural way. The following layout requires the code:: lay = """ first_name last_name nation o2m=movies:title,description,year,date_release """ SqlMask(Movie, layout=lay, dbproxy=db) now you can edit director and films. The demo has plenty of working examples for there cases: :many2one: are just recognized automatically with simple introspection :many2many: is very simply added to SqlMask declaring in the layout and attribute with that name :one2many: same as many2many Many more detail in :ref:`relationships` .. image:: ../img/totals-embedded.png :align: right :scale: 40% :class: preview Totals ------ It's possible to display totals and subtotals in a table view. In this picture you can see how a table embedded into a mask can display totals. More in :ref:`totals` Constraints ----------- A :ref:`constraint ` can be as simple as:: t = SqlTable('movie', dbproxy=db) t.add_constraints(actors__country='ITA', genres__name='drama', year__gte=1950, year__lte=1970) And browsing of movies will be constrained into dramas with at least one italian actor, produced between 1950 and 1970. The double underscore '__' will allow spanning from one table to a related (provided a relation was established at sqlalchemy level) and from attribute name to operator to e used. Sqledit ------- A full featured program (python script) that can browse a database. Many options (``sqledit -h``). .. image:: ../img/sqledit.png :align: right :scale: 50% :class: preview Just try it out on your preferred database using the url in a form as one of the examples:: sqledit postgres://localhost/mydb sqledit postgres://sandro:passwd@host:5444/mydb sqledit mysql://localhost/mydb sqledit sqlite:///movies.sqlite the last is a very minimal db present in ``doc/db/movies.sqlite`` .. _basic_limitations: Basic assumptions and limitations ================================= 1. You use PrimaryKeys and ForeignKeys throughout the db. If you don't use ForeignKeys sqlkit will just work w/o any special behavior. If you don't use PrimaryKeys sqlkit will not even open the table. 2. ForeignKeys are *simple*. Compound ForeignKeys are not yet supported, that means that you can't use:: class MyTable(Base): ... ForeignKeyConstraint('first_field, second_field], [referenced1, referenced2]) This will be addressed in a future release 3. You are using one single metadata. This is a limit but it's my normal environment. There's not really anything that cannot be changed easily about this, simply I didn't have need for this, nor working cases. (While I was plenty of ideas on other features...) 4. Spaces are not allowed in field names. This comes from the layout syntax definition. .. _fkey: ForeignKeys =========== Everywhere there's a ForeignKey I try to represent it in the best possible way. More info in the completion chapter: :ref:`description` sqlkit supported backends ========================= Sqlkit is built on sqlalchemy that allows editing db with many `different engines`_. I use it with PostgreSQL, MySQL, sqlite and Firebird. Other engines are supported but may need a very simple addition that I'd be willing to do on demand. .. _`different engines`: http://www.sqlalchemy.org/trac/wiki/DatabaseNotes .. _PyGTK: http://www.pygtk.org sqlkit-0.9.5/doc/misc/missing.rst0000644000175000017500000000122411714210425016271 0ustar sandrosandro====================================== What's Missing and how to contribute ====================================== Well, this is a pretty large chapter... there are tons of things that are missing from this package and that are really needed but I want to focus on the following: - saving queries - good field render for CellRenderer (notably TEXT fields and DateTime) - good system to warn people of all orphans when deleting - integrate a permission system I'd be happy to accept any suggestion, patch or contributed feature on anything that can make sqlkit more powerful. sandro .. _PyGtkMVC: http://pygtkmvc.sourceforge.net/index.php sqlkit-0.9.5/doc/misc/backward_incompatibilities.rst0000644000175000017500000000523411714210425022173 0ustar sandrosandro.. _backward_incompatibilities: ================================= Backward Incompatibilities ================================= From version 0.8.6 (first public release) I keep track of backward incompatibilities: 0.9.3 ===== :requirements: sqlalchemy now needs to be at least rel 0.5.4 as we're using AttributeExtension to provide better MVC support 0.9.2 ===== :hooks: ``on__field_change`` now pass also the field. The demo snippet already had this arguments but was always None and was not documented :totals: added model, path, iter to ``sum`` method of totals. Impact only on customized :ref:`totals` 0.9.1 ===== :hooks: ``on_completion`` is now triggered when you press Return, both for tables and masks. Previously was triggered on Return for Masks and on click for table. 0.9.0 ===== :Mask: a fix in the layout machinery make setting width specs working... that means you may need to fine tune the width of your entries. You set the width of an entry as follows:: last_name:40 first_name:30- sex:1> the first sets 40 chars, the second sets 30 that would grow to use available space, the latter would use 2 chars and would right align it (compared to other entries) :mappers: if a relationship has ``cascade=delete-orphan`` set, and you add an object by completion Sqlalchemy will complain that there's a missing object. With SA 0.5 this is automatically done, with SA 0.6 this is no longer supported by sqlkit. You are supposed to explicitly add info on the Column as explained in :meth:`sqlkit.fields.ForeignKeyField.add_related_object` 0.8.7 ===== :Table: * ``self.current`` points to ``self.get_current_obj()``, no longer ``get_selected_obj()`` * button-press-event now has one more argument: treeview. This is necessary due to the fact that table now have views and t.treeview only points to the main view's treeview. :Table & Mask: * now keyword table/class/mapper are deprecated in favor to setting it as first argument. :Hooks: arguments in hooks of related widgets where not as documented: a hook on a sqlwidget acting on a related widget would receive the related widget as first argument rather that the main one. Look at the the following case:: lay = """ user o2m=addresses """ class Hook(object): def on_change_value__addresses__domain(self, sqlwidget, field_name, value, fkvalue): pass m = SqlMask(User,... hooks=Hook()) m_address = m.related.addresses the hook instance will receive ``m`` as argument where previously received ``m_addresses`` sqlkit-0.9.5/doc/misc/sqledit.rst0000644000175000017500000001152311714210425016270 0ustar sandrosandro.. _sqledit: ========================================================== Sqledit - the standalone program to browse and edit data ========================================================== Sqledit is an application that can be used by anybody without any programming skill. Basically is just needs a target database that can be fed throught a GUI or on the command line. .. image:: ../img/sqledit.png :align: right :scale: 40 :class: preview .. image:: ../img/sqledit_setup.png :align: left :scale: 40 :class: preview Sqledit is a data editor/browser. At startup it will present a list of all tables that can be selected for editing (choosing :ref:`mask` or :ref:`table`) or for introspection. When run from command line it offers several options: .. docusage:: ../../sqlkit/scripts/sqledit.py :verbatim: If called without argument it will present a connection dialog: If sqledit can find a demo in your systems, it will give you the chance to start it. If you want to type the URL of your db you can directly attempt a connection or disable it via a checkbox on the right of the entry. The autoconnect mode is really nice but you may experiment hangs till a connection timeout if you write a wrong host name. Since database urls are not nice to write, you can store data in a configuration file and call nick names instead. .sqledit ========== You can write configurations in a file in your home called ``.sqledit/nicks`` and start sqledit on that configuration using a nickname:: [invitati] URL = postgres://sandro:xxx@my_host/2giugno table = partecipazioni_invitato dev = True field_list = nome, cognome, and all other fields order_by = cognome, nome [brasile] copy = invitati field_list = nome, cognome, email, ludo Valid option are URL and any other option for sqledit. The special option ``copy`` force sqledit to read the other definition first and then overwrite. In this case 'brasile' shares all options with '2giugno' but overwrites field_list. Even if you don't want to program in Python you may want to add configuration in a more rich way that allowed in ``.sqledit/nicks``. That can add for example layout information or information on the relations between tables so that a Mask can present a ecord and data related to it. Let's say that that's a gentle introduction to programming with sqlkit... .. automodule:: sqlkit.misc.conf schema browser ============== Introspection of the database will give you the possibility to see all fields of a table showing all fields, with type, primary keys, foreign keys and indexes. If you configured a nick to jump directly on a table or any other configuration allowed by sqledit customization, you'll need the -b (--browser) option to get to the schema browser. Options ======= Calling `sqledit` from a command line under a Linux system with bash completion you can benefit from the completion that will look for completion in the .sqledit/nicks file and will suggest some common url (postgresql://, sqlite://...) When primary keys are numeric you probably don't want/need to see them, you can switch off the visualization with the ``primary key`` toggle button The ``Load`` toggle button determines if you want to load data when opening a table. ``Blank`` toggle button determines if you want to cast blank string fields to NULL values. When you decide to cast it you may be prompted several times if you want to save changes that you are not even aware of. Configuring sqlkit ================== .. image:: ../img/sqledit_config.png Sqlkit looks for possible configuration options in some tables, that may or not be present: _sqlkit_table, and _sqlkit_field. These tables can be edited directly from the database menu, or via ```` shortcut. Completion will help yo configure the fields. Here is the meaning: table's field ------------- :name: the table's name :search field: this is the **string** field that will be used when searching via foreign key. Suppose you are editing a table of movies, and you must fill in the director's field. You write some letters and trigger a completion, that means you want sqlkit to use that text (e.g. "Fel"), select which directors are present that has that string in... well you surely want to search in the last name, but you need to tell sqlkit. *search_field* is here for that. :format: ok, you get back from completion a list of directors you still need to show them in a nice way (e.g. first_name, last_name). Here you are supposed to used the syntax "%(field_name)s". attributes'fields ----------------- :name: the field_name :description: the label you want to be used. (Note that when using :ref:`related tables ` you may indicate relation.field_name) :help_text: this is the tooltip that will be added to the entry :autostart: you can set an :ref:`autostart` value for the completion sqlkit-0.9.5/doc/misc/download.rst0000644000175000017500000000674211714210425016441 0ustar sandrosandro======================================= Download, requirements & googlegroup ======================================= Requirements ============ Sqlkit depends on: * Python (>= 2.5, < 3) * Pygtk * sqlalchemy (>=0.5.4). Rel 0.9.5 is the first sqlkit release that works with sqlalchemy 0.7+. * python-dateutils * setuptools * the correct driver for your database of choice among the backend `supported by sqlalchemy`_ * babel (localization) Changelog =========== this is the Changelog_ .. _Changelog: http://sqlkit.argolinux.org/download/Changelog Download ======== The code is available under an hg repository:: hg clone http://hg.argolinux.org/py/sqlkit You can download sqlkit package VER from here_ in tar or zip format. +--------------------------------------+-------------------------------------+ | Python package |* sqlkit-VER.tar.gz_ | | |* sqlkit-VER.zip_ | +--------------------------------------+-------------------------------------+ Sqlkit is used in a production environment and great care is put in fixing any bug as soon as possible. The first stable version has been 0.8.6 in 2008. I really appreciate any bug report particularly if based on a repeatable example, possibly starting from the demo. Windows ======= Read the detailed instructions in the tutorial: :ref:`windows-install`. Debian/Ubuntu ============= Packages are available, read the :ref:`instructions ` on how to add the repository. Sqlkit on Pypi ================= Sqlkit is available via Pypi (Python Package Index), so -if you have already installed setuptools that provides the command easy_install- you can install it via ``easy_install`` or better ``pip``:: easy_install pip pip install sqlkit Beware that that will fail if you don't already have PyGTK installed. You can also install directly with ``easy_install`` that often will fail understanding already installed packages. Should you have problems with ``pip`` you can revert to:: easy_install sqlkit No one of these command will install the backend driver (psycopg2 for postgresql, MySQLdb for mysql,...) that you are supposed to install by yourself. Sqlite is included in Python stadard library. Localization ============ We need the help from some translator to localize in different languages. It takes some 40 minutes to provide a complete set of translations for each language. Please visit the launchpad_ 's site or contact me directly. Author ====== Sqlkit is developed by `Alessandro Dentella`_ .. _list: http://groups.google.com/group/sqlkit .. _here: http://sqlkit.argolinux.org/download/ .. _Experimental: http://packages.debian.org/experimental/python-sqlalchemy .. _sqlkit-VER.tar.gz: http://sqlkit.argolinux.org/download/sqlkit-VER.tar.gz .. _sqlkit-VER.zip: http://sqlkit.argolinux.org/download/sqlkit-VER.zip .. _python-sqlkit_DEBVER_all.deb: http://sqlkit.argolinux.org/download/python-sqlkit_DEBVER_all.deb .. _sqledit-binary-LNXVER.tar.gz: http://sqlkit.argolinux.org/download/sqledit-binary-LNXVER.tar.gz .. _sqledit-setup-WINVER.exe: http://sqlkit.argolinux.org/download/sqledit-setup-WINVER.exe .. _sqlkit-doc_VER_all.deb: http://sqlkit.argolinux.org/download/sqlkiy-doc_VER_all.deb .. _`Alessandro Dentella`: mailto:sandro@e-den.it .. _launchpad: https://launchpad.net/sqlkit .. _`supported by sqlalchemy`: http://www.sqlalchemy.org/docs/dialects/index.html sqlkit-0.9.5/doc/conf.py0000644000175000017500000001361511714210425014441 0ustar sandrosandro# -*- coding: utf-8 -*- # # sqlkit documentation build configuration file, created by # sphinx-quickstart on Sat Jul 5 21:05:37 2008. # # This file is execfile()d with the current directory set to its containing dir. # # The contents of this file are pickled, so don't put values in the namespace # that aren't pickleable (module imports are okay, they're removed automatically). # # All configuration values have a default value; values that are commented out # serve to show the default value. import sys import os # If your extensions are in another directory, add it here. If the directory # is relative to the documentation root, use os.path.abspath tosphinx.ext.autodoc make it # absolute, like shown here. #sys.path.append(os.path.abspath('some/directory')) # General configuration # --------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. cur_dir = os.path.dirname(__file__) sys.path += [cur_dir] sys.path += [os.path.dirname(cur_dir)] import docusage import sqlkit extensions = ['sphinx.ext.autodoc', 'docusage'] #extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['templates'] templates_map = { 'misc/tour' : 'tour.mako' , 'misc/sqledit' : 'tour.mako' , } template_bridge = "builder.builders.MakoBridge" # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'hidden' # General substitutions. project = 'sqlkit' copyright = '2008, Sandro Dentella' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. # # The short X.Y version. version = sqlkit.__version__ # The full version, including alpha/beta/rc tags. release = version # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. #unused_docs = [] # List of directories, relative to source directories, that shouldn't be searched # for source files. #exclude_dirs = [] # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # Options for HTML output # ----------------------- # The style sheet to use for HTML and HTML Help pages. A file of that name # must exist either in Sphinx' static/ path, or in one of the custom paths # given in html_static_path. html_style = 'sqlkit.css' # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (within the static path) to place at the top of # the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. html_sidebars = {'index': 'indexsidebar.html'} # Additional templates that should be rendered to pages, maps page names to # template names. html_additional_pages = {'index': 'index.html'} # If false, no module index is generated. html_use_modindex = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, the reST sources are included in the HTML build as _sources/. html_copy_source = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = 'sqlkitdoc' # Options for LaTeX output # ------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, document class [howto/manual]). latex_documents = [ ('sqlkit/contents', 'sqlkit.tex', 'sqlkit Documentation', 'Sandro Dentella', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = True sqlkit-0.9.5/doc/builder/0000755000175000017500000000000011714210425014562 5ustar sandrosandrosqlkit-0.9.5/doc/builder/__init__.py0000644000175000017500000000000011714210425016661 0ustar sandrosandrosqlkit-0.9.5/doc/builder/util.py0000644000175000017500000000027111714210425016111 0ustar sandrosandroimport re def striptags(text): return re.compile(r'<[^>]*>').sub('', text) def strip_toplevel_anchors(text): return re.compile(r'\.html#\w+-toplevel').sub('.html', text) sqlkit-0.9.5/doc/builder/builders.py0000644000175000017500000001425111714210425016750 0ustar sandrosandro## origginally found in sphinx docs of sqlalchemy by Michael Bayer. ## modified by Alessandro Dentella to use templates_map from sphinx.application import TemplateBridge from sphinx.builders.html import StandaloneHTMLBuilder from sphinx.highlighting import PygmentsBridge from pygments import highlight from pygments.lexer import RegexLexer, bygroups, using from pygments.token import * from pygments.filter import Filter, apply_filters from pygments.lexers import PythonLexer, PythonConsoleLexer from pygments.formatters import HtmlFormatter, LatexFormatter import re from mako.lookup import TemplateLookup from mako.template import Template class MakoBridge(TemplateBridge): def init(self, builder, *args, **kw): self.layout = builder.config.html_context.get('mako_layout', 'html') self._map = builder.config.templates_map self.lookup = TemplateLookup(directories=builder.config.templates_path, format_exceptions=True, input_encoding='utf-8', imports=[ "from builder import util" ] ) def render(self, template, context): template = template.replace(".html", ".mako") context['prevtopic'] = context.pop('prev', None) context['nexttopic'] = context.pop('next', None) context['mako_layout'] = self._map.get(context.get('current_page_name'), 'layout.mako') # sphinx 1.0b2 doesn't seem to be providing _ for some reason... context.setdefault('_', lambda x:x) return self.lookup.get_template(template).render_unicode(**context) def render_string(self, template, context): context['prevtopic'] = context.pop('prev', None) context['nexttopic'] = context.pop('next', None) context['mako_layout'] = self._map.get(context.get('current_page_name'), 'layout.mako') return Template(template, lookup=self.lookup, format_exceptions=True, imports=[ "from builder import util" ] ).render_unicode(**context) class StripDocTestFilter(Filter): def filter(self, lexer, stream): for ttype, value in stream: if ttype is Token.Comment and re.match(r'#\s*doctest:', value): continue yield ttype, value class PyConWithSQLLexer(RegexLexer): name = 'PyCon+SQL' aliases = ['pycon+sql'] flags = re.IGNORECASE | re.DOTALL tokens = { 'root': [ (r'{sql}', Token.Sql.Link, 'sqlpopup'), (r'{opensql}', Token.Sql.Open, 'opensqlpopup'), (r'.*?\n', using(PythonConsoleLexer)) ], 'sqlpopup':[ ( r'(.*?\n)((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|ALTER|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?(?:{stop}\n?|$))', bygroups(using(PythonConsoleLexer), Token.Sql.Popup), "#pop" ) ], 'opensqlpopup':[ ( r'.*?(?:{stop}\n*|$)', Token.Sql, "#pop" ) ] } class PythonWithSQLLexer(RegexLexer): name = 'Python+SQL' aliases = ['pycon+sql'] flags = re.IGNORECASE | re.DOTALL tokens = { 'root': [ (r'{sql}', Token.Sql.Link, 'sqlpopup'), (r'{opensql}', Token.Sql.Open, 'opensqlpopup'), (r'.*?\n', using(PythonLexer)) ], 'sqlpopup':[ ( r'(.*?\n)((?:PRAGMA|BEGIN|SELECT|INSERT|DELETE|ROLLBACK|COMMIT|ALTER|UPDATE|CREATE|DROP|PRAGMA|DESCRIBE).*?(?:{stop}\n?|$))', bygroups(using(PythonLexer), Token.Sql.Popup), "#pop" ) ], 'opensqlpopup':[ ( r'.*?(?:{stop}\n*|$)', Token.Sql, "#pop" ) ] } def _strip_trailing_whitespace(iter_): buf = list(iter_) if buf: buf[-1] = (buf[-1][0], buf[-1][1].rstrip()) for t, v in buf: yield t, v class PopupSQLFormatter(HtmlFormatter): def _format_lines(self, tokensource): buf = [] for ttype, value in apply_filters(tokensource, [StripDocTestFilter()]): if ttype in Token.Sql: for t, v in HtmlFormatter._format_lines(self, iter(buf)): yield t, v buf = [] if ttype is Token.Sql: yield 1, "
%s
" % re.sub(r'(?:[{stop}|\n]*)$', '', value) elif ttype is Token.Sql.Link: yield 1, "sql" elif ttype is Token.Sql.Popup: yield 1, "" % re.sub(r'(?:[{stop}|\n]*)$', '', value) else: buf.append((ttype, value)) for t, v in _strip_trailing_whitespace(HtmlFormatter._format_lines(self, iter(buf))): yield t, v class PopupLatexFormatter(LatexFormatter): def _filter_tokens(self, tokensource): for ttype, value in apply_filters(tokensource, [StripDocTestFilter()]): if ttype in Token.Sql: if ttype is not Token.Sql.Link and ttype is not Token.Sql.Open: yield Token.Literal, re.sub(r'(?:[{stop}|\n]*)$', '', value) else: continue else: yield ttype, value def format(self, tokensource, outfile): LatexFormatter.format(self, self._filter_tokens(tokensource), outfile) def autodoc_skip_member(app, what, name, obj, skip, options): if what == 'class' and skip and name == '__init__': return False else: return skip def setup(app): app.add_lexer('pycon+sql', PyConWithSQLLexer()) app.add_lexer('python+sql', PythonWithSQLLexer()) app.connect('autodoc-skip-member', autodoc_skip_member) app.add_config_value('templates_map', {}, 'html') print "TEMPLATES_MAP" PygmentsBridge.html_formatter = PopupSQLFormatter PygmentsBridge.latex_formatter = PopupLatexFormatter sqlkit-0.9.5/doc/templates/0000755000175000017500000000000011714210425015132 5ustar sandrosandrosqlkit-0.9.5/doc/templates/tour.mako0000755000175000017500000000144111714210425016777 0ustar sandrosandro## coding: utf-8 <%inherit file="layout.mako"/> <%def name="extrahead()"> <%def name="prevnextheader()"> <%def name="sidebar()">

Release 0.9 is out

I'm happy to announce that on September, 8 2010 I released version 0.9 of sqlkit that adds a huge quantity of new features and bug fixes (see changelog). This release works with sqlalchemy 0.5 and 0.6 that is now a fully supported release.
${next.body()} sqlkit-0.9.5/doc/templates/sidebar.mako0000644000175000017500000000427511714210425017424 0ustar sandrosandro## coding: utf-8 <%inherit file="layout.mako"/>

Navigazione

% if display_toc and not current_page_name.startswith('index'): ${toc} % endif

Download

ReteIsi offre una distribuzione live scaricabile dal sito di Argolinux L'ultima versione è la newisi-libata-beta6 basata su etch.

Domande? Problemi?

Iscriviti al Google group:

sqlkit-0.9.5/doc/templates/layout.mako0000644000175000017500000002202111714210425017315 0ustar sandrosandro## coding: utf-8 ##<%inherit file="${context['mako_layout']}"/> <%inherit file="static_base.mako"/> <%def name="headers()"> % if hasdoc('about'): % endif ## % if hasdoc('copyright'): ## ## % endif % if parents: % endif % if nexttopic: % endif % if prevtopic: % endif % for scriptfile in script_files + self.attr.local_script_files: ## jquery.js, doctools.js % endfor
${self.testata()} ${self.related()} ${self.sidebar()}
${next.body()}
<%def name="footer()">
${self.footer()} <%def name="prevnext()">
% if prevtopic: Previous: ${prevtopic['title']} % endif % if nexttopic: Next: ${nexttopic['title']} % endif
<%def name="prevnextheader()"> % if prevtopic:
  • Prev: ${prevtopic['title']}
  • % endif % if nexttopic:
  • Next: ${nexttopic['title']}
  • % endif <%def name="show_title()"> % if title: ${title} % endif <%def name="sidebar()">
    ## <%include file="sidebar.mako"/>
    % if display_toc and not current_page_name.startswith('index'):

    Table of contents

    ${toc} % endif

    Questions?

    Subscribe to out mailing list Google group:

    <%def name="related()"> <%def name="testata()">

    sqlkit-0.9.5/doc/templates/site_base.mako0000644000175000017500000000142411714210425017742 0ustar sandrosandro<%text>#coding:utf-8 <%inherit file="/base.html"/> <%page cache_type="file" cached="True"/> <%! in_docs=True %>
    Quick Select: 0.6 | 0.5 | 0.4
    PDF Download: download
    ${'<%text>'} ${next.body()} ${''} <%text><%def name="style()"> ${self.headers()} <%text>${parent.style()} <%text> <%text><%def name="title()">${capture(self.show_title)|util.striptags} — ${docstitle|h}<%text> <%! local_script_files = [] %> sqlkit-0.9.5/doc/templates/genindex.mako0000644000175000017500000000332711714210425017611 0ustar sandrosandro<%inherit file="layout.mako"/> <%def name="show_title()">${_('Index')}

    ${_('Index')}

    % for i, (key, dummy) in enumerate(genindexentries): ${i != 0 and '| ' or ''}${key} % endfor
    % for i, (key, entries) in enumerate(genindexentries):

    ${key}

    <% breakat = genindexcounts[i] // 2 numcols = 1 numitems = 0 %> % for entryname, (links, subitems) in entries:
    % if links: ${entryname|h} % for link in links[1:]: , [${i}] % endfor % else: ${entryname|h} % endif % if subitems:
    % for subentryname, subentrylinks in subitems:
    ${subentryname|h} % for j, link in enumerate(subentrylinks[1:]): [${j}] % endfor
    % endfor
    % endif <% numitems = numitems + 1 + len(subitems) %> % if numcols <2 and numitems > breakat: <% numcols = numcols + 1 %>
    % endif % endfor
    % endfor <%def name="sidebarrel()"> % if split_index:

    ${_('Index')}

    % for i, (key, dummy) in enumerate(genindexentries): ${i > 0 and '| ' or ''} ${key} % endfor

    ${_('Full index on one page')}

    % endif ${parent.sidebarrel()} sqlkit-0.9.5/doc/templates/page.mako0000644000175000017500000000012211714210425016712 0ustar sandrosandro<%inherit file="${context['mako_layout']}"/> ${body| util.strip_toplevel_anchors} sqlkit-0.9.5/doc/templates/indexsidebar.html0000644000175000017500000000111611714210425020460 0ustar sandrosandro

    Download

    Googlegroup

    Subscribe Google group:

    License

    GPLv3 sqlkit-0.9.5/doc/templates/associazioni.mako0000644000175000017500000000241711714210425020502 0ustar sandrosandro## coding: utf-8 <%inherit file="home.mako"/>

    Associazioni

    ReteISI

    logo reteisi

    La soluzione ReteISI è stata commissionata nel 2002 dalla omonima associazione di scuole (Intranet scolastiche integrate). Rimandiamo al sito dell’associazione per ogni approfondimento.

    Massimo Mancini, resposabile IT della scuola capofila (ITCG Saraceno di Morbegno - SO), è tuttora uno degli sviluppatori del progetto.

    ReteIdra

    logo reteidra

    nel 2008 e 2010 L’associazione di scuole ReteIdra (Informatica per la didattica in reti aperte) ha supportato lo sviluppo del progetto. Rimandiamo al loro sito per ulteriori approfondimenti.

    sqlkit-0.9.5/doc/templates/home.mako0000755000175000017500000000020211714210425016730 0ustar sandrosandro## coding: utf-8 <%inherit file="layout.mako"/> <%def name="extrahead()"> <%def name="related()"> ${next.body()} sqlkit-0.9.5/doc/templates/search.mako0000644000175000017500000000116411714210425017252 0ustar sandrosandro<%inherit file="layout.mako"/> <%! local_script_files = ['_static/searchtools.js'] %> <%def name="show_title()">${_('Search')}

    Enter Search Terms:

    <%def name="footer()"> ${parent.footer()} sqlkit-0.9.5/doc/templates/static_base.mako0000644000175000017500000000515311714210425020270 0ustar sandrosandro ${metatags and metatags or ''} ${capture(next.show_title)|util.striptags} — ${docstitle|h} ${self.headers()} ${self.extrahead()} ${next.body()} <%def name="extrahead()"> ##<%def name="body()"> <%! local_script_files = [] %> sqlkit-0.9.5/doc/templates/macros.html0000644000175000017500000000400611714210425017304 0ustar sandrosandro{%- macro sidebar %} {%- if builder != 'htmlhelp' %} {%- endif %} {%- endmacro %} sqlkit-0.9.5/doc/templates/index.mako0000644000175000017500000002665111714210425017124 0ustar sandrosandro## coding: utf-8 <%inherit file="layout.mako"/> <%def name="extrahead()"> <%def name="prevnextheader()"> <%def name="sidebar()">

    Release 0.9.4 is out

    I'm happy to announce that on April, 11 2011 I released version 0.9.4 Sqlkit now provides a very powerful template system that allows to compose the layout with OpenOffice.org. No programming know-how is required to create an effective template, no more headache to get the right layout using OpenOffice's writer.

    Sqledit for end users

    The easiest possible way to browse the data of your database

    You can customize the way data are presented in a very simple way. The ideal tool to edit your personal databases or to browse data of an application you're developing with other languages/tools.

    Filtering data has never been so easy, no SQL knowledge required. Read more...

    Sqlkit for Python developers

    A powerful framework to create any application from simple to very rich and complex ones.

    Sqlkit provides 2 widgets to edit data as form or table. It's based on PyGTK and sqlalchemy to provide maximum flexibility.

    Key points are the way to design form layout with relationship, completions, validation and filter capabilities that can be done w/o any effort. More than 80 examples ready to use! Read more...

    • Sqledit

      The application 'sqledit' can open a great variety of different backends as it's base on SqlAlchemy: PostgreSQL, MySQL, sqlite, firebird... You can use it to browse your data or to debug an application you're developing. The rich configuration capability of sqledit can make it grow in a gentle way toward a true application what starts as a simple shortcut to some data.
    • Table's list

      Each table of the database can be opened in Mask or Table way or introspected.
    • The model (optional)

      Table fields can be automatically reflected from the database or set using sqlalchemy's standard way. Here the example used in the demo for directors and the way to set the relation with 'movie' table.
    • Table view

      Each database table can be opened in table or mask mode. The image shows table mode where each column can be sorted and it's field can be added to a filter tool.
    • Relationships


      Create a form to edit relations is as easy as writing a text layout (clearly you must define relations in the model):
      	 lay = """last_name 
       	          first_name nation
      	          o2m=movies"""
      	 SqlMask(Movie, layout=lay, dbproxy=db)
            
    • Filters

      Each field of a table/mask even of a related table can be filtered on. Just click on the label and a filter panel will be presented. A smart and efficent way to express dates in a relative way lets you great flexibility. Here date_release >= 'y-5' means a film released after Jan, 1^ 5 years ago, whenever you run the filter.

    • Contraints

      Filters and constraints can be programmatically added using a syntax derived from django orm. User are allowed to play with filters while constraints are used to limit the visibility of some records.

    • Mask View

      The SqlMask widget shows one record at a time. Any field type will be rendered in a proper way. Images are varchar field for which a render='image' is set when defining the model. The layout can be set in a incredibly simple way w/o any programming knowledge.
    • Mask view from any row

      Right click on a record offers a variety of different actions. The most important is the ability to open a SqlMask to view/edit that record the record pointed to by the foreign key.

    • Registered layout

      A mask that displays a single record can use a registered layout and will follow the selection of the underneath table.

    • Completion

      Any foreign key is automatically detected and a widget that implements completion on the foreign table is used. Completion is triggered by 'Return' and the search on the foreign key is done in a customizable field and represented in a customizable way (here: first_name + last_name)
    • Group by on completion

      Completion can be programmed in a group-by fasion or constrained in a dinamic way so that the value of a field is used to filter the possible completions:
      	t = SqlTable(Invoice, ...)
      	t.completions.project_id.filter(client_id='$client_id') # dynamic
      	t.completions.client_id.group_by = 'category' # group-by
            
    • Totals

      Table can display totals and subtotals. Here subtotals w/o totals where requested.
    • Trees

      Rows can be displayed with a hierarchy

    • Editing joins

      Tables and Masks can show any selectable that you may define with sqlalchemy. Here a join between two columns is displayed and fields from both tables retain the possibility to be edited.

               m = mapper(Join, model.Movie.__table__.join(model.Director.__table__),
                 properties={
                       'movie_id' : model.Movie.__table__.c.id
                  })
               t = SqlTable(m, dbproxy=db )
      
              
    • Computed fields

      You can add computed fields as in this case where the number of movie is computed for each director, you can further sum and sort on those fields.

    • Printing

      Sqlkit provides a very powerful template system that allows to compose the layout with OpenOffice.org. No programming know-how is required to create an effective template, no more headache to get the right layout using OpenOffice's writer.

    • HOOKS & signals

      Many signals and hooks are available to the programmer for a powerful level of customization.

      A "hook" class can be registered globally so that its attached to any SqlWidget open in any situation, both in form or table view. That's the "controller" part in the MVC implementation.

      Hooks allow you to customize almost any part as are called on validation, on completion, on record display, on value change so that it's very easy to create a very interactive interface.

    sqlkit-0.9.5/sqlkit/0000755000175000017500000000000011714210425013676 5ustar sandrosandrosqlkit-0.9.5/sqlkit/__init__.py0000644000175000017500000000114411714210425016007 0ustar sandrosandroimport os import sys import gettext import pygtk pygtk.require('2.0') from sqlkit.misc import utils __version__ = '0.9.5' cwd = os.path.abspath(os.path.dirname(__file__)) path = os.path.join(cwd, 'locale') if not os.path.exists(path): # this is true when we are in the pyinstaller executable path = os.path.join(os.path.dirname(sys.executable), 'sqlkit', 'locale') # t = gettext.translation('sqlkit', path) # _ = t.lgettext gettext.bindtextdomain('sqlkit', path) gettext.textdomain('sqlkit') _ = gettext.gettext from db.proxy import DbProxy import exc utils.check_sqlalchemy_version('0.5.4') sqlkit-0.9.5/sqlkit/exc.py0000644000175000017500000000472311714210425015035 0ustar sandrosandrofrom sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound from sqlalchemy.exc import DBAPIError from sqlkit import _ class FieldNotInLayout(Exception): pass class FieldValueNotSet(Exception): pass class NotHandledField(Exception): pass class NotHandledDefault(Exception): pass class MissingPrimaryKey(Exception): pass class MissingWidget(Exception): pass class NoSyncError(Exception): pass class SyncNotAvailable(Exception): pass class MissingEngine(Exception): pass ### Validation class ValidationError(Exception): """ Each hook or field.validate_value() can raise a ValidationError that will cause the saving/deleting procedure to abort and present a Dialog to inform the user of the error present, the old and the new value. """ pass class DialogValidationError(ValidationError): """ A validation error that makes the outer procedures to abort. It's trapped by delete_event_cb and record_save_cb. """ pass class ValidationWarning(ValidationError): """ This is not a real Error. It will make a different popup window to be presented with a message and a possibility to abort or continue. """ pass class NotNullableFieldError(ValidationError): """ An error that specifies which field cannot be empty """ def __init__(self, field_name, message=None, master=None): self.field_name = field_name if master: self.field_name = master.get_label(field_name) self.message = message or _("Field '%s' cannot be NULL") % self.field_name def __str__(self): return self.message class InvalidValue(ValidationError): def __init__(self, field_name, value, message=None, master=None): self.field_name = field_name if master: self.field_name = master.get_label(field_name) self.value = value self.message = message or _("Field '%s' cannot have value %s") % ( self.field_name, value) class CommitError(ValidationError): pass class HandledRollback(CommitError): pass class NoCurrentObjError(Exception): pass class CancelledAction(Exception): pass class LookupValueError(Exception): pass class LookupValueMissingValue(LookupValueError): pass class LookupValueMultipleValues(LookupValueError): pass class MissingDescriptorField(Exception): pass class UnhandledMultipleForeignKeys(Exception): pass class ParseFilterError(Exception): pass class ColumnWithoutTable(Exception): pass class ConnectionError(Exception): pass sqlkit-0.9.5/sqlkit/widgets/0000755000175000017500000000000011714210425015344 5ustar sandrosandrosqlkit-0.9.5/sqlkit/widgets/__init__.py0000644000175000017500000000012311714210425017451 0ustar sandrosandro from mask import SqlMask from table.table import SqlTable #, Header, ModelProxy sqlkit-0.9.5/sqlkit/widgets/mask/0000755000175000017500000000000011714210425016277 5ustar sandrosandrosqlkit-0.9.5/sqlkit/widgets/mask/__init__.py0000644000175000017500000000006111714210425020405 0ustar sandrosandro__all__ = [ 'SqlMask'] from mask import SqlMask sqlkit-0.9.5/sqlkit/widgets/mask/mask.py0000644000175000017500000006506011714210425017613 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re import datetime import decimal import gtk import gobject from sqlalchemy.exc import InvalidRequestError from sqlalchemy.orm.exc import ObjectDeletedError import sqlkit from sqlkit import debug as dbg, exc, _, layout, fields from sqlkit.widgets.common import completion, sqlwidget FK_COLOR = gtk.gdk.color_parse('navyblue') LBL_HOOVER_STYLE = gtk.gdk.color_parse('red') class SqlMask(sqlwidget.SqlWidget): """ SqlMask is the widget that displays one record at a time and buttons to browse data, save/delete records. SqlMask is inherited from :class:`sqlkit.widgets.common.sqlwidget.SqlWidget` """ __metaclass__ = None if not dbg.debug else dbg.LogTheMethods labels = None # correspondance Label <-> EventBox row_count = 0 # number of records in the mapper current_idx = 0 current = None current_mode = None __gsignals__ = { 'pre-display' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # (gobject.TYPE_PYOBJECT,), ), } def __init__(self, *args, **kw): """ Prepare for a new record """ #current_idx = dbg.TraceIt(0, name='current_idx', mode='rw') sqlwidget.SqlWidget.__init__(self, *args, **kw) ## idle so that possible defaults get a chanche to be effective gobject.idle_add(self.record_new_idle) self.run_hook('on_init') ###### Filters/constraints ###### Layout def _get_layout(self, lay, naked): self.lay_obj, self.widgets = sqlwidget.SqlWidget._get_layout(self, lay, naked) self._add_click_for_filter() self._add_color_to_labels() self._add_tips() self._add_wheel_support() return self.lay_obj, self.widgets def _add_wheel_support(self): """ add mouse wheel support """ if not self.widgets['Window']: return def scroll_event_cb(widget, event): if not self.actiongroup_browse.get_sensitive(): return if event.direction == gtk.gdk.SCROLL_UP: return self.record_display(incr=-1) else: return self.record_display(incr=1) self.widgets['Window'].connect('scroll_event', scroll_event_cb) def _add_color_to_labels(self): """ add bg colors and fonts to signal needed fields """ def ene_cb(eb, ev): eb.get_child().set_state(gtk.STATE_PRELIGHT) return True def lne_cb(eb, ev): eb.get_child().set_state(gtk.STATE_NORMAL) return True for field_name in self.mapper_info.fields.keys(): if field_name in self.labels: lbl = self.labels[field_name] ev_wdg = self.labels[field_name].parent ev_wdg.connect('enter-notify-event', ene_cb) ev_wdg.connect('leave-notify-event', lne_cb) lbl.modify_fg(gtk.STATE_PRELIGHT, LBL_HOOVER_STYLE) ## label FK_COLOR to tell it's a foreign key if self.mapper_info.fields[field_name]['fkey'] : #dbg.write("FK", field_name) self.labels[field_name].modify_fg(gtk.STATE_NORMAL, FK_COLOR) else: #dbg.write("NO", field_name) pass def _add_tips(self): """ Add tips to labels starting from label_map """ for field_name, values in self.label_map.iteritems(): label, help_text = values key = "l=%s" % field_name if label: if key in self.widgets: self.widgets[key].set_text(label) if help_text: if key in self.widgets: self.widgets[key].set_tooltip_text(help_text) def _add_click_for_filter(self): """ make labels clickable for filter panel """ ## all EventBox self.labels = {} for field_name in self.mapper_info.fields.keys(): lbl_name_wdg = "l=%s" % field_name if lbl_name_wdg in self.widgets: self.labels[field_name] = self.widgets[lbl_name_wdg] #dbg.write("%s -> %s"% (field_name, self.labels[field_name])) else: #dbg.write("Missing event_box for %s" % (field_name)) pass ## add click that pops filter_panel for field_name, label in self.labels.iteritems(): event_box = label.parent event_box.connect('button-press-event', self.button_press_event_cb, field_name) def _set_arrows_sensitivness(self, display_idx=None): """ set sensitivness of back and forward arrows according to records length and position """ if display_idx is None: display_idx = self.current_idx or 0 self.ui_manager.get_action('/Main/Go/Forward').set_sensitive(display_idx+1 < len(self.records)) self.ui_manager.get_action('/Main/Go/Back').set_sensitive(display_idx > 0) def setup_field_validation(self, field_list=None): """ create fields.Field objects gui_fields: validation fields -------------------------- is a dict: :key: is the field_name :value: is a field from sqlkit.widget.mask.fields """ ## gui_fields can be passed as argument of the class. In that case widgets ## used to represent the data are provided by another SqlMask if not self.gui_fields: raise Exception("I shouldn't be here...") self.gui_fields = {} field_chooser = fields.FieldChooser(self.mapper_info, self.widgets, gui_field_mapping=getattr(self, 'gui_field_mapping', None)) for field_name, widget_key in self.laygen.fields_in_layout.iteritems(): widget = self.widgets[widget_key] db_spec = self.mapper_info.fields.get(field_name, None) Field = field_chooser.get_field(field_name, db_spec, widget_key) field = Field(field_name, db_spec) field.set_master(self) # the widget may already be created (by layout.Layout) gtk_widget = self.widgets.get(widget_key, None) field.set_widget(gtkwidget=gtk_widget, def_str=widget_key) self.field_widgets[field_name] = field.widget self.gui_fields[field_name] = field def button_press_event_cb(self, widget, event, field_name): """ callback of click on labels """ if event.button == 3: self.show_field_info(None, field_name) else: self.filter_panel.add(widget, event, field_name, self) def set_frame_label(self, frame_name, markup_label, opts='bi'): """ Add a label to the frame whose key in self.widgets is 'frame_key'. :param frame_name: the key in :attr:`self.widgets` :param markup_label: the label with possible markup :param opts: a combination of b (bold) and i (italic). Default: 'bi' In example 15:: LAYOUT = ''' {|.number .. } ... ''' m.set_frame_label('number', 'Number') """ frame = self.widgets['F.%s' % frame_name] frame.set_label('a') if 'i' in opts or '': markup_label = "%s" % markup_label if 'b' in opts or '': markup_label = "%s" % markup_label frame.get_property('label-widget').set_markup(markup_label) ###### Actions/UiManager def prepare_actions(self): """ Prepare action needed by UIManager """ sqlwidget.SqlWidget.prepare_actions(self) self.actiongroup_insert.add_actions([ ('New', gtk.STOCK_NEW, None, '', _('Add new record'), self.record_new_cb), ('Save-as', gtk.STOCK_SAVE_AS, None, None, None, self.record_save_new_cb), ]) self.actiongroup_select.add_actions([ ('Undo', gtk.STOCK_UNDO, None, None, _('Discard changes'), self.record_undo_cb), # TIP Modify menu entry in the mask to reread a single record from database ('RefreshRecord', gtk.STOCK_REFRESH, _('Refresh this record'), None, _('Reread this record from db'), self.record_refresh_cb), ]) self.actiongroup_delete.add_actions([ ('Delete', gtk.STOCK_DELETE, None, '', _('Delete this record'), self.record_delete), ]) def prepare_uimanager(self): """ Prepare UIManager, actions, and accelerators """ from sqlkit.widgets.common import uidescription sqlwidget.SqlWidget.prepare_uimanager(self) self.ui_manager.add_ui_from_string(uidescription.MASK_UI) ###### Saving def set_value(self, field_name, field_value, fkvalue=None, initial=False, shown=False): """ set the value of any field present in ``gui_fields``. Uses field.set_value if ``initial`` is False, run ``on_change_value`` :param field_name: the field_name to be changed :param field_value: the new value :param fkvalue: a possible foreign key value. It's here just for compatibility with SqlTable's one :param initial: a boolean indicating if it's an initial value (passed to field) :param shown: a boolean indicating if the value is the displayed value (passed to field) """ if field_name not in self.gui_fields: raise exc.FieldNotInLayout("field_name: %s" % (field_name)) field = self.gui_fields[field_name] field.set_value(field_value, initial=initial, shown=shown) if not initial: fkvalue = fkvalue if fkvalue is not None else field.get_value(shown=True) self.run_hook('on_change_value', field_value, fkvalue, field, field_name=field_name) def get_value(self, field_name, shown=False): """ return the value from the widget :param field_name: the field_name :param shown: boolean: true if the value we want is the dislayed one """ if field_name not in self.gui_fields: dbg.write("%s Non in gui_fields %s" % (field_name, self.gui_fields.keys())) raise exc.FieldNotInLayout("field_name: %s" % (field_name)) field = self.gui_fields[field_name] return field.get_value(shown=shown) def clear_value(self, field_name): """ clear the value to the default value """ if field_name not in self.gui_fields: raise exc.FieldNotInLayout("field_name: %s" % (field_name)) self.gui_fields[field_name].set_default() def record_new(self, widget=None, clear=True): """ prepare a new object and set clear = True/False. clear=False is used when 'saving as' """ if self.current: if clear and not self.record_has_changed(): if self.last_new_obj: self.sb(_("Already at new record")) return if clear: try: if self.current: ## this is important to trigger validation in time to prevent ## a change in self.current. No need if all we want is to ## 'save as new' self.record_save() self.clear_mask(check=False) except exc.ValidationError: return self._set_arrows_sensitivness() self.current = self.get_new_object() self.last_new_obj = self.current self.records += [self.current] self.current_idx = len(self.records) -1 self.sb(_("New record %s") % (self.current_idx +1)) if 'i' in self._mode: self.actiongroup_update.set_sensitive(True) ## the following lines ensure that in presence of ## related tables any 'related.records' is exactly 'current.related' ## (e.g. m.related.movies.records is m.current.movies). This is ## important to use loops in a natural way when you add a record and ## want to loop on already written rows that are only present in the ## related table for key in self.related.keys(): r = self.related[key] r.records = getattr(self.current, key) self.current_mode = 'INSERT' self.emit('record-new') def record_new_idle(self): """ record_new has been asked with idle_add from __init__ when no info on possible .reload() was available. create a new record only if no records are present """ if not self.records: self.record_new() def record_undo_cb(self, menuItem): """ undo possible modification to the mask """ self.discard_changes() self.record_display(check=False) def record_refresh_cb(self, menuItem): """ undo possible modification to the mask """ self.discard_changes() if not self.current in self.session: return self.session.expire(self.current) try: self.record_display(check=False) except ObjectDeletedError: # TIP message issued when a refresh is done on a deleted record self.dialog(text=_('The record is no longer present in the database')) self.record_display_next() def record_mask2obj(self, obj=None, force=False): """ collect data from the mask and set into the object if record_has_changed()) force is used when saving a new obj as in record_new """ if not obj: return obj if obj not in self.session: ## it should never happen, really... why do we need? self.session.add(obj) # mask2obj ensures that the object used for validation is updated from the mask # record_valildate that is run just after mask2object requires validation_errors/warnings # be present self.validation_errors = {} self.validation_warnings = {} if force or self.record_has_changed(): for field in self.gui_fields: field_name = field.field_name try: value = field.get_value() if field.editable: field.validate( value , clean=True) if field.persistent: # persisted fields, mask2obj has already cleaned them field.set_value(self.get_value(field_name), initial=False, obj=obj, update_widget=False) except exc.ValidationError, e: self.add_validation_error(e, field_name=field_name) except exc.MissingWidget, e: pass return obj def record_save_cb(self, widget, *args): """ record save callback... """ self.grab_focus(widget) try: self.record_save(None) except (exc.DialogValidationError, exc.CancelledAction), e: pass def record_save(self, ask=True, new_obj=False, *args): """ invokes the validator before UPDATE/INSERT a record ask: ask if save is required new_obj: the record is new. It forces mask2obj to skip check and save all fields """ focus_widget = self.get_toplevel().get_focus() if focus_widget and isinstance(focus_widget, gtk.Entry): focus_widget.activate() self.get_toplevel().set_focus(None) changed = self.record_has_changed() if changed or new_obj or ( self.current in self.session.new and self.session.is_modified(self.current) ) or (self.unsaved_changes_exist(skip_new=self.current)): pass else: self.sb(_('Nothing to save'), seconds=4) return if ask: response = self.save_unsaved(skip_check=True, proceed=False) if response == gtk.RESPONSE_CANCEL: raise exc.CancelledAction if response == gtk.RESPONSE_NO: return obj = self.record_mask2obj(self.current, force=new_obj) self.record_validate(obj, single_fields=False) try: self.commit() except exc.HandledRollback, e: return self.last_new_obj = None self.session.expire(self.current) self.record_display(check=False, delay_message=2) def record_save_new(self, obj=None, commit=True, ask=True): """ create a new record by duplicating the obj :param obj: the obj to be cloned :param commit: (boolean) commit after duplicating :return: the new object """ ## if PKey is editable we should add a check that is not the same if isinstance(self.tables, list): tbl = self.tables[0] else: tbl = self.tables pkey_changed = False for key in self.mapper_info.get_pkeys(tbl): if key in self.gui_fields: if self.is_editable(key): if self.gui_fields[key].has_changed(): pkey_changed = True else: pkey_changed = True if not pkey_changed: self.dialog(text=_("Primary key (%s) didn't change. Refusing to save as new" % key)) return o2m_list = [name for name, table in self.related.iteritems() if not table.relationship_mode == 'm2m'] save_as_is_managed = self.hooks and getattr(self.hooks, 'on_save_as', None) if o2m_list and not save_as_is_managed: msg = "Tables expressing one to many relationship will be left empty (%s)\n" + \ "Read the docs for more informations" response = self.dialog(type='ok-cancel', text=_(msg) % o2m_list, icon='gtk-dialog-warning') if response == gtk.RESPONSE_CANCEL: return elif ask: msg = _("Do you want to copy all data to a new record?") response = self.dialog(type='ok-cancel', text=_(msg) % o2m_list, icon='gtk-dialog-question') if response == gtk.RESPONSE_CANCEL: return records_m2m = {} for table_name, table in self.related.iteritems(): if table.relationship_mode == 'm2m': records_m2m[table_name] = table.records old_obj = self.current self.record_new(clear=False) for table_name, table in self.related.iteritems(): if table_name in records_m2m: table.set_records(records_m2m[table_name]) else: table.set_records([]) obj = obj or self.current self.record_mask2obj(obj, force=True) for table_name, table in self.related.iteritems(): setattr(obj, table_name, table.records) self.run_hook('on_save_as', old_obj, obj) ## record_refresh() is not really needed here. You should implement it # in your own on_save_as, but you should know you need to... for table in self.related: if table.modelproxy.tree_field_name and not table.relationship_mode == 'm2m': table.record_refresh() if commit: try: self.record_save(ask=False) except exc.ValidationError, e: pass self.emit('record-selected', ) return obj def record_delete(self, widget=None, interactive=True): """ delete a record """ if interactive: text = _("Delete this record?\n(%s)") % self.current response = self.dialog(type='ok-cancel', text=text) if response == gtk.RESPONSE_CANCEL: return obj = self.records[self.current_idx] if obj in self.session.new: self.session.expunge(obj) else: self.session.delete(obj) self.last_new_obj = None try: self.commit(_('Deleted')) self.emit('record-deleted', obj) except exc.HandledRollback, e: return self.record_display_next() def record_display_next(self): """Display the next (or previous) record after deleting a record """ self.records.pop(self.current_idx) self.clear_mask(check=False) if not self.records: self.record_new() else: if self.current_idx > len(self.records) -1: self.current_idx -= 1 self.record_display(index=self.current_idx) ###### Validation ###### Record browsing def reload(self, **kw): """ Reload the data """ changed = self.record_has_changed() if changed: skip_new = False else: skip_new = self.current response = self.save_unsaved(skip_check=changed, skip_new=skip_new) if response == gtk.RESPONSE_CANCEL: return self.current_idx = 0 length = sqlwidget.SqlWidget.reload(self, **kw) if not length: self.record_new() self.set_mode() return length def record_display(self, record=None, incr=0, index=None, check=True, delay_message=0): """ display an object record in the mask. Defaults to self.records[self.current + incr] :param record: the record to be displayed :param index: the index in self.records to be displayed :param incr: increment to current index :param check: boolean. If False prevent checking if saving is needed :param delay_message: int. Number of seconds to wait before writing in the status bar. This is needed to prevent normal display message from hiding "Saved" message. """ if not self.records: ## TIP: message in the statis bar self.sb(_('No record present'), seconds=5) return if check: try: self.record_save(ask=True) except (exc.CancelledAction, exc.DialogValidationError): return if index is not None: display_idx = index else: display_idx = (self.current_idx or 0) + incr try: assert display_idx >= 0 # no negative indexes obj = self.records[display_idx] self.clear_mask(check=False) self.current_idx = display_idx self.current = obj msg_info = "%s/%s" % (display_idx +1, len(self.records)) except (IndexError, AssertionError), e: msg_info = "%s/%s" % (self.current_idx +1, len(self.records)) if incr >= 0: self.sb(_('Already last record') + msg_info) else: self.sb(_('Already first record') + msg_info) return None gobject.timeout_add(delay_message * 1000, self.sb, msg_info) self.emit('pre-display', obj) for field in self.gui_fields: if field.persistent: #if field.field_name in self.mapper_info: value = getattr(obj, field.field_name) else: value = field.clean_value(obj) self.set_value(field.field_name, value, initial=True) self._set_arrows_sensitivness(display_idx) self.emit('record-selected', ) return True def set_records(self, records=None, index=0, pk=None): """ set records to browse, and display number idx :param records: set records as self.records. records must be a list :param index: which record to display after settings self.records :param pk: display record with this PrimaryKey. The record is retrieved with self.get_by_pk that doesnnot flush the session """ self.clear_mask() if not records and pk: records = self.get_by_pk(pk) self.records = records or [] # it's handy to set set_records([table.current]) # that may also be empty if records and not records == [None]: self.record_display(index=index) self.record_refresh() def clear_cb(self, accell_group, window, ord, flags): self.record_new() def clear_mask(self, check=True): """ clear the mask for a new record NOTE: this does NOT prepare for a new record (missing obj) :param widget: :param check: boolean: false prevent a check """ if check and not self.save_unsaved(): return self.current = None for field in self.gui_fields: self.clear_value(field.field_name) clear = clear_mask def record_forward_cb(self, widget): """ display the next record in the mapper""" self.grab_focus(widget) return self.record_display(incr=1) def record_back_cb(self, widget): """ display the previous record in the mapper""" self.grab_focus(widget) return self.record_display(incr=-1) ####### Misc def get_widget(self, field_name): """ get the widget that renders field_name """ return self.gui_fields[field_name].widget.gtkwidget def get_current_obj(self): """ Return the object that is currently edited """ return self.current def fkey_is_valid(self, field_name): """ return True if current editable -if exists- has a value that does not need validation """ return self.gui_fields[field_name].widget.is_valid() sqlkit-0.9.5/sqlkit/widgets/mask/miniwidgets.py0000644000175000017500000011305211714210425021176 0ustar sandrosandro# Copyright (C) 2008-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Fields Representation ===================== +--------------------+--------------------------------------------+--------------------+ | Field or Property | Mask |Table | +--------------------+--------------------------------------------+--------------------+ ||varchar| |:class:`VarcharWidget` | | +--------------------+--------------------------------------------+--------------------+ ||text| |:class:`TextWidget` | | +--------------------+--------------------------------------------+--------------------+ ||integer| |:class:`IntegerWidget` | | +--------------------+--------------------------------------------+--------------------+ ||float| |:class:`FloatWidget` | | +--------------------+--------------------------------------------+--------------------+ ||numeric| |:class:`DecimalWidget` | | +--------------------+--------------------------------------------+--------------------+ ||date| |:class:`DateWidget` | | +--------------------+--------------------------------------------+--------------------+ ||datetime| |:class:`DateTimeWidget` | | +--------------------+--------------------------------------------+--------------------+ ||datetimetz| |:class:`DateTimeTZWidget` | | +--------------------+--------------------------------------------+--------------------+ ||interval| |:class:`IntervalWidget` | | +--------------------+--------------------------------------------+--------------------+ ||time| |:class:`TimeWidget` | | +--------------------+--------------------------------------------+--------------------+ ||timetz| |:class:`TimeTZWidget` | | +--------------------+--------------------------------------------+--------------------+ ||boolean| |:class:`BooleanWidget` | | +--------------------+--------------------------------------------+--------------------+ ||boolnull| |:class:`BooleanNullWidget` | | +--------------------+--------------------------------------------+--------------------+ ||enum| (varchar or |:class:`EnumWidget` | | |integer with | | | |render='enum') | | | +--------------------+--------------------------------------------+--------------------+ ||image| (varchar |:class:`ImageWidget` | | |with render='image')| | | +--------------------+--------------------------------------------+--------------------+ ||fkey| |:class:`ForeignKeyWidget` | | +--------------------+--------------------------------------------+--------------------+ ||relation| |:class:`CollectionWidget`. This is just a | | | |SqlTable that represents the related field | | +--------------------+--------------------------------------------+--------------------+ .. autoclass:: Widget :members: __init__, set_value, get_value, set_editable, add_completion, set_not_null_style .. autoclass:: VarcharWidget .. autoclass:: TextWidget .. autoclass:: IntegerWidget .. autoclass:: FloatWidget .. autoclass:: DecimalWidget .. autoclass:: DateWidget .. autoclass:: DateTimeWidget .. autoclass:: DateTimeTZWidget .. autoclass:: IntervalWidget .. autoclass:: TimeWidget .. autoclass:: TimeTZWidget .. autoclass:: BooleanWidget .. autoclass:: BooleanNullWidget .. autoclass:: EnumWidget .. autoclass:: ImageWidget .. autoclass:: ForeignKeyWidget .. autoclass:: CollectionWidget Signals ------- :value-set: a signal emitted each time a value is set (via method :attr:`Widget.set_value`). This signal does not trigger a general change in value as would be for entries a ``changed`` signal. Callback function: .. function:: value_set_cb(widget, value, initial): :param widget: the Miniwidget that issued the signal :param value: the value that is being set :param initial: (boolean) True if the value is set with initial=True Colors ------ You can change default color for not nullable object globally:: from sqlkit.widgets.mask import miniwidgets miniwidgets.NOT_NULL_COLOR = gtk.gdk.color_parse('green') You can change the foreground of unsensitive widgets setting NOT_EDITABLE_COLOR .. |varchar| replace:: :class:`Varchar ` .. |text| replace:: :class:`Text ` .. |integer| replace:: :class:`Integer ` .. |float| replace:: :class:`Float ` .. |numeric| replace:: :class:`Numeric ` .. |date| replace:: :class:`Date ` .. |datetime| replace:: :class:`Datetime ` .. |datetimetz| replace:: :class:`Datetime w/ TZ ` .. |interval| replace:: :class:`Interval ` .. |time| replace:: :class:`Time ` .. |timetz| replace:: :class:`Time w/ timezone ` .. |boolean| replace:: :class:`Boolean ` .. |boolnull| replace:: :class:`Boolean with null ` .. |enum| replace:: :class:`Enum ` .. |image| replace:: :class:`Image ` .. |fkey| replace:: :class:`ForeignKey ` .. |relation| replace:: :class:`relation (o2m/m2m) ` """ import re import os from decimal import Decimal from datetime import timedelta, time import gtk import gobject from sqlkit import debug as dbg, _, exc from sqlkit.widgets.common.completion import SimpleCompletion, FkeyCompletion NOT_NULL_COLOR = gtk.gdk.color_parse('#f6ebba') NOT_EDITABLE_COLOR = gtk.gdk.color_parse('#333') class Widget(gobject.GObject): """ A proxy between the :ref:`field ` and a real gtk widget """ __gsignals__ = { 'value-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, # args: widget, value, initial (gobject.TYPE_PYOBJECT, gobject.TYPE_BOOLEAN,), ) } def __init__(self, gtkwidget, field): """ setup all what is needed in the widget: completion if any and much more for complex cases as :class:`CollectionWidget` :param gtkwidget: the gtk widget used to display the data :param field: the :ref:`field ` from ``sqlkit.fields`` """ self.__gobject_init__() self.gtkwidget = gtkwidget self.field = field self.master = field.master self.field_name = field.field_name if not self.field.nullable: self.set_not_null_style(not self.field.nullable) self.gtkwidget.connect('destroy', self.destroy) self.set_editable(self.field.editable) self.set_text(state=gtk.STATE_INSENSITIVE, color=NOT_EDITABLE_COLOR) def set_not_null_style(self, value): if value: if self.field.editable: self.set_bg(state=gtk.STATE_NORMAL, color=NOT_NULL_COLOR) else: style = gtk.Entry().get_style() color = style.base[gtk.STATE_NORMAL] self.set_bg(state=gtk.STATE_NORMAL, color=color) def set_bg(self, state, color): self.gtkwidget.modify_base(state, color) def set_text(self, state, color): self.gtkwidget.modify_text(state, color) def set_value_cb(self, field, value, initial=False): self.set_value(value, initial=initial) def set_value(self, value, shown=False, initial=False): """ Set the value in the widget :param value: the value to be set :param shown: (boolean) only memaningfull for foreign key. if True, the displayed value is returned also in any case. The difference is only for ForeignKey The displayed value may be used for completion purpouses :param initial: not used by miniwidget. Needed for compatibility with tablewidgets where it needs to propagate to master's set_value """ pass def get_value(self, shown=False): """ Return the displayed value :param shown: (boolean) return the shown value or the real value (only meaningful for foreign keys) """ pass def get_entry(self): """ return the entry on which you want to apply completion for this object """ return self.gtkwidget def destroy(self, widget=None): for attr in ('field', 'master', 'completion',): try: delattr(self, attr) except AttributeError: pass def set_editable(self, editable): """ set the widget editability if the fields is not editable, the widget cannot be editable """ editable = self.field.editable and editable self.gtkwidget.set_property('sensitive', editable) def add_completion(self): """add completion bindings and liststore This adds completion capabilities to varchar fields and to foreign keys. You can add it to other widgets as well provided you also use completion's :meth:`set_values ` method to provide values to be used in completion. """ try: self.completion = SimpleCompletion(self.master, self, self.field_name) except exc.ColumnWithoutTable, e: return self.completion.add_completion(self.gtkwidget) self.master.completions[self.field_name] = self.completion self.completion.add_callbacks(self.gtkwidget) def pop_completion(self): ## self.emit('changed') is needed as the only way to pop the completion ## when you click on the icon to pop the completion you should not invalidate ## self.real_value as it's not an editing action self.gtkwidget.emit('changed') def __repr__(self): return "<%s - %s >" % (self.__class__.__name__, self.field_name) class VarcharWidget(Widget): # to prevent to change NULL to '' and viceversa # we store the value when setting it # and compare it when getting def __init__(self, *args): Widget.__init__(self, *args) self.add_completion() self.gtkwidget.connect('key-press-event', self.on_key_press) self._id_changed = self.gtkwidget.connect('changed', self.on_changed) if self.field.length: self.set_max_length(self.field.length) def on_key_press(self, widget, event): ksym = gtk.gdk.keyval_name(event.keyval) if not ksym == 'Return': return if event.state & (gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK): return self.master.run_hook('on_activate', self, field_name=self.field_name) def on_changed(self, widget): ## this function is not called when the value is set as initial value try: value = self.get_value() except exc.ValidationError: self.master.sb("Value '%s' is not valid" % self.gtkwidget.get_text()) return self.master.run_hook('on_change_value', value, value, self.field, field_name=self.field_name) def set_value(self, value, initial=False): # Entry self.gtkwidget.handler_block(self._id_changed) self.gtkwidget.set_text( str(value or '') ) self.gtkwidget.handler_unblock(self._id_changed) self.emit('value-set', value, initial) def get_value(self, shown=False): value = self.gtkwidget.get_text( ) ## don't change from None to '' return value def set_max_length(self, length=None): if length is None: length = self.field.length self.gtkwidget.set_max_length(length) class ReadOnlyWidget(Widget): def __init__(self, *args): Widget.__init__(self, *args) self.gtkwidget.set_property('xpad', 0) self.gtkwidget.set_property('sensitive', True) self.gtkwidget.set_property('selectable', True) if self.master.is_fkey(self.field_name): self.completion = FkeyCompletion(self.master, self, self.field_name) else: self.completion = None def set_value(self, value, shown=True, initial=False): self.gtkwidget.set_data('value', value) string_value = self.field.get_human_value(value) # if self.completion: # string_value = self.completion.lookup_value(value) # else: # string_value = str(self.field.format_value(value)) or '' self.set_label(string_value) self.emit('value-set', value, initial) def set_label(self, value): """ this is meant to be overloaded for particular needs """ self.gtkwidget.set_text(value or '') def get_value(self, shown=False): return self.field.initial_value def set_max_length(self, max): pass class IntegerWidget(VarcharWidget): def __init__(self, *args): Widget.__init__(self, *args) self.gtkwidget.set_alignment(1) self.gtkwidget.connect('key-press-event', self.on_key_press) self.gtkwidget.connect('key-press-event', self.master.digits_check_input_cb ) self._id_changed = self.gtkwidget.connect('changed', self.on_changed) def set_value(self, value, initial=False): self.text_value = self.field.format_value(value) self.gtkwidget.handler_block(self._id_changed) self.gtkwidget.set_text(self.text_value) self.gtkwidget.handler_unblock(self._id_changed) self.emit('value-set', value, initial) def get_value(self, shown=False): text = self.gtkwidget.get_text( ) return self.field.clean_value(text) class FloatWidget(IntegerWidget): def get_value(self, shown=False): ## the representation of a float normally trims it, so, if I know I have not changed ## anything I return the old value text = self.gtkwidget.get_text( ) if self.text_value == text: return self.field.initial_value return self.field.clean_value(text) class DecimalWidget(IntegerWidget): pass class DateWidget(Widget): """ A widget that uses :class:`DateEdit ` widget to represent a date. A button that pops a calendar is provided to pick a date """ def __init__(self, *args): Widget.__init__(self, *args) self.gtkwidget.connect('date-changed', self.date_changed_cb, ) self._date_changed_id = self.gtkwidget.connect('date-changed', self.date_changed_cb, ) self.gtkwidget.date_format = self.field.format_value self.gtkwidget.date_parse = self.field.clean_value def wrong_format_cb(self, widget, date_string): self.invalid = date_string def date_changed_cb(self, widget): self.invalid = None date = self.gtkwidget.props.date self.master.run_hook('on_change_value', date, date, self.field, field_name=self.field_name) self.emit('value-set', date, False) def set_value(self, value, initial=False): self.invalid = None self.gtkwidget.handler_block(self._date_changed_id) self.gtkwidget.set_property('date', value) self.emit('value-set', value, initial) self.gtkwidget.handler_unblock(self._date_changed_id) def get_value(self, shown=False): value = self.gtkwidget.get_property('date') if isinstance(value, basestring): raise exc.ValidationError(_("Wrong date format: %s") % value) else: return value class TimeWidget(Widget): """ A timezone unaware widget to set time. Very poor widget indeed. """ def set_value(self, value, initial=False): self.gtkwidget.set_text(self.field.format_value(value)) self.emit('value-set', value, initial) def get_value(self, shown=False): value = self.gtkwidget.get_text() if not value: return None value = time(*[int(i) for i in re.split('[.:]', value)]) return self.field.clean_value(value) class TimeTZWidget(Widget): """ A timezone aware widget to set time. Should have a TimeZone chooser but does not yet """ class IntervalWidget(Widget): def set_value(self, value, initial=False): if value and not isinstance(value, timedelta): msg = "value for %s must be a timedelta (now: %s)" % (self.field_name, value) raise AttributeError(msg) self.gtkwidget.set_property('interval', value) self.emit('value-set', value, initial) def get_value(self, shown=False): ## if all values are 0 and initial value is None, return None value = self.gtkwidget.get_property('interval') if value == timedelta(0) and self.field.initial_value == None: return None return value class DateTimeWidget(DateWidget): """ A timezone unaware widget to set datetime. Very poor widget indeed. Uses :class:`DateTimeEdit `. """ def __init__(self, *args): Widget.__init__(self, *args) self._datetime_changed_id = self.gtkwidget.connect('datetime-changed', self.date_changed_cb, ) self._date_changed_id = self.gtkwidget.connect('date-changed', self.date_changed_cb, ) self._time_changed_id = self.gtkwidget.connect('time-changed', self.date_changed_cb, ) self.gtkwidget.set_property('show-time', True) self.initial_delta = timedelta(0) def date_changed_cb(self, widget): self.invalid = None datetime = self.gtkwidget.props.datetime self.master.run_hook('on_change_value', datetime, datetime, self.field, field_name=self.field_name) self.emit('value-set', datetime, False) def set_value(self, value, initial=False): self.gtkwidget.handler_block(self._date_changed_id) self.gtkwidget.handler_block(self._datetime_changed_id) self.gtkwidget.handler_block(self._time_changed_id) ## FIXME: datetime has only time? self.gtkwidget.set_property('datetime', value) self.emit('value-set', value, initial) self.gtkwidget.handler_unblock(self._date_changed_id) self.gtkwidget.handler_unblock(self._datetime_changed_id) self.gtkwidget.handler_unblock(self._time_changed_id) def get_value(self, shown=False): value = self.gtkwidget.get_property('datetime') value = self.field.clean_value(value, add_second=True, add_microsecond=True) return value class DateTimeTZWidget(DateTimeWidget): """ A timezone aware widget to set datetime. Should have a TimeZone chooser but does not yet """ class TextWidget(Widget): def __init__(self, *args): Widget.__init__(self, *args) self.gtkwidget.set_property('wrap-mode', gtk.WRAP_WORD) def set_value(self, value, initial=False): buff = self.gtkwidget.get_buffer() buff.set_text( value or '') self.emit('value-set', value, initial) def get_value(self, shown=False): buff = self.gtkwidget.get_buffer() ts = buff.get_iter_at_offset(0) te = buff.get_iter_at_offset(-1) text = buff.get_text(ts, te) return text class BooleanWidget(Widget): """ Null value is not admittable A boolean widget can be represented via CheckButton (default) or radio button (just use r=field_name when setting layout). In the latter case you can safely group widgets toghether with normal gtk tools ``.set.group()`` to gain a switch between different alternatives:: m.SqlMask(..., layout='r=male, r=female') m.widgets['r=female'].set_group(m.widgets['r=male']) """ def __init__(self, *args): Widget.__init__(self, *args) self.gtkwidget.connect_after('toggled', self.on_toggled_cb) def on_toggled_cb(self, widget): self.master.run_hook('on_activate', self, field_name=self.field_name) self.master.run_hook('on_change_value', self.get_value(), self.get_value(), self.field, field_name=self.field_name) def set_value(self, value, initial=False): self.gtkwidget.set_active(bool(value)) self.emit('value-set', value, initial) def get_value(self, shown=False): return self.gtkwidget.get_active( ) class BooleanNullWidget(BooleanWidget): """ This gtkwidget is placed in inconsinstent state to represent NULL """ def __init__(self, *args): Widget.__init__(self, *args) self._clicked_id = self.gtkwidget.connect('clicked', self.on_clicked_cb) self.gtkwidget.connect_after('toggled', self.on_toggled_cb) def set_value(self, value, initial=False): if value == None: self.gtkwidget.set_inconsistent(True) else: ## This is needed to work around the fact that set_active(...) triggers ## a clicked signal self.gtkwidget.handler_block(self._clicked_id) self.gtkwidget.set_inconsistent(False) self.gtkwidget.set_active(value) self.gtkwidget.handler_unblock(self._clicked_id) self.emit('value-set', value, initial) def get_value(self, shown=False): if self.gtkwidget.get_inconsistent(): return None else: return self.gtkwidget.get_active( ) def on_clicked_cb(self, button): """ a toggle function that loops through inconsinstent state also """ active = not button.get_active() null = button.get_inconsistent() ## - -> True -> False if null: # - next is True button.set_active(True) button.set_inconsistent(False) else: if active == True: button.set_inconsistent(False) button.active = False else: button.active = True button.set_inconsistent(True) return True class EnumWidget(Widget): """ A proxy for a ComboBox """ def __init__(self, gtkwidget, field): Widget.__init__(self, gtkwidget, field) self.model = gtk.ListStore(gobject.TYPE_PYOBJECT, gobject.TYPE_STRING) gtkwidget.set_model(self.model) gtkwidget.props.width_request = -1 cell = gtk.CellRendererText() gtkwidget.pack_start(cell, True) gtkwidget.add_attribute(cell, 'text', 1) self.notify_id = gtkwidget.connect('notify::active', self.notify_active_cb) self.fill_model() def notify_active_cb(self, widget, param): self.master.run_hook('on_change_value', self.get_value(), self.get_value(shown=True), self.field, field_name=self.field_name) self.emit('value-set', self.get_value(), False) def fill_model(self): if self.field.nullable: self.model.append((None, '')) for key, value in self.field.values.iteritems(): self.model.append([key, value]) def _find_value(self, model, path, iter, (value, i)): if model.get_value(iter, 0) == value: return True i[0] += 1 def set_value(self, value, initial=False): i = [0] self.model.foreach(self._find_value, (value, i)) self.gtkwidget.handler_block(self.notify_id) self.gtkwidget.set_active(i[0]) self.emit('value-set', value, initial) self.gtkwidget.handler_unblock(self.notify_id) def get_value(self, shown=False): index = self.gtkwidget.get_active_iter() if index is not None: if shown: return self.model.get_value(index, 1) else: return self.model.get_value(index, 0) return None class ImageWidget(Widget): """ A proxy for :ref:`image_widget` to represent fields that hold file name as images """ def __init__(self,gtkwidget, field): Widget.__init__(self, gtkwidget, field) gtkwidget.props.strip_dir = field.base_dir.rstrip('/') + '/' gtkwidget.connect('image-selected', self.on_image_selected) def on_image_selected(self, imagewidget, filename, new_filename): """ Check if the file already exists and in case tell the user. Mark the file for copy when the record will be effectively saved. """ if not filename: return save_path = self.field.get_save_path(name=new_filename or filename, obj=self.field.master.current) ## existing path if os.path.exists(save_path): response = self.master.dialog( type="ok-cancel", text=_("A file with name '%s' already exists. Overwrite?" % save_path), title=_('Upload name conflict')) if response == gtk.RESPONSE_CANCEL: return self.copy_file = self.field.clean_path(filename) self.new_filename = save_path def set_value(self, value, initial=False): self.copy_file = None self.gtkwidget.set_image(value) self.emit('value-set', value, initial) def get_value(self, shown=False): return self.field.clean_path(self.gtkwidget.props.image_path) class ForeignKeyComboboxWidget(Widget): """a ComboBoxEntry with 2 columns In any case for the moment we need to click on a choice from the completion (we cannot just write a correct value), since completion also retrieves the id """ def __init__(self, *args): Widget.__init__(self, *args) ## see completion.append_to_model() to understand the meaning of these 4 columns # add to a model that has 4 cols: # 0: the value to be used as foreign key # 1: the value to be shown in the entry # 2: data to be shown in the dropdown # 3: a dictionary of all fields in the returned row # this can be used to set additional fields self.model = gtk.ListStore(object, str, str, object) # self.gtkwidget.connect('changed', self.clean_real_value) self.gtkwidget.set_model(self.model) self.gtkwidget.set_text_column(2) self.gtkwidget.connect( 'button-press-event', self.completion.pop_menu, self.field_name, 3) self.gtkwidget.connect( 'key-press-event', self.completion.keypress_event_cb, self.field_name) self.entry = self.gtkwidget.child completion = self.completion.prepare_completion(self.field_name, mode='regexp', match_col=2) completion.set_model(self.model) self.entry.set_completion(completion) self.gtkwidget.connect('notify::popup-shown', self.on_popup) def on_popup(self, widget, params): """the popup should be empty. We force the completion instead """ self.completion.show_possible_completion(self.gtkwidget, mode='regexp') return True def clean_real_value(self, *args): ## the EntryText has been changed. The value may no longe be valid self.real_value = False def set_value(self, value, initial=False): """We have a 'real_value' that is normally an id or a key and a 'display_value' that is the value we want to show """ display_value = self.field.lookup_value(value) if value == None: if self.field.type == str: value = '' self.model.clear() self.model.append([value, display_value, display_value,'']) self.gtkwidget.set_active(0) # which column self.emit('value-set', value, initial) def get_value(self, shown=False): #dbg.write() if shown == True: ## this is for completion. The value needed is the value in the entry not in the model return self.entry.get_text() idx = self.gtkwidget.get_active() value, display_value, descr, obj_dict = self.model[idx] return value class ForeignKeyWidget(Widget): """A ComboBoxEntry that displays a represention of the referenced record rather that the referece. I.e.: it shows last_name/first_name of a director rather than the `id` of the record in the directors' table. It also enforces the foreign key constraint. Completion occurs on the foreign (referenced) table according to what is detailed in :ref:`completion` documentation. """ def __init__(self, *args): Widget.__init__(self, *args) self.entry = self.gtkwidget.entry if hasattr(self.gtkwidget, 'entry') else self.gtkwidget self.add_completion() self.real_value = True self.master.completions[self.field_name] = self.completion self.value = None self._dont_change_real_value = False self.entry.connect('changed', self.on_entry_changed) def add_completion(self): """add completion bindings and liststore """ self.completion = FkeyCompletion(self.master, self, self.field_name) self.completion.add_completion(self.entry) self.master.completions[self.field_name] = self.completion ## button connect self.gtkwidget.connect('completion-requested', self.completion_requested_cb, self.field_name) #self.button.connect('clicked', self.on_button_clicked) ## entry connect self.completion.add_callbacks(self.entry) def completion_requested_cb(self, widget, event, field_name): """ Double click -> mode enum Simple click <3> -> menu """ if event.button == 3: self.completion.pop_menu(widget, event, self.field_name) elif event.button == 1: if event.type == gtk.gdk._2BUTTON_PRESS: # cancel reques by first simple click gobject.source_remove(self.idle_id) mode = 'enum' else: mode = 'regexp' self.idle_id = gobject.idle_add( self.completion.show_possible_completion, self.entry, mode) return True def on_key_press(self, widget, event, when='press'): return self.completion.keypress_event_cb(self.entry, event, self.field_name, when) def on_entry_changed(self, editable): if not self._dont_change_real_value: self.real_value = False def pop_completion(self): ## self.emit('changed') is needed as the only way to pop the completion ## when you click on the icon to pop the completion you should not invalidate ## self.real_value as it's not an editing action self._dont_change_real_value = True self.entry.emit('changed') self._dont_change_real_value = False def is_valid(self): return self.real_value def set_value(self, value, shown=False, initial=False): """ We have a real 'value' that is normally an id or a key and a 'display_value' that is the value we want to show shown: the passed value is a value to be shown. a shown value is not cleaned (e.g.: for completion) """ if shown: display_value = value self.entry.set_text(display_value) else: self.value = value display_value = self.field.lookup_value(value) self.entry.set_text(display_value) self.real_value = True # we can trust this value, has been set by procedures self.emit('value-set', value, initial) def get_value(self, shown=False): if shown: return self.entry.get_text() if self.real_value: return self.value else: if self.entry.get_text(): msg = _("'%s' may have an invalid value: try completion on that") % self.field_name raise exc.ValidationError(msg) else: return None class CollectionWidget(Widget): """ A SqlTable widget that represents a collection of records. This is used to represent a property that in turn holds a relation. E.g.: ``director.movies`` movies being a list. The contructor setups a SqlTable that has :attr:`relationship_leader ` pointing to the main SqlMask. It maintains the same session so that a single commit will save parent and children. """ def __init__(self, *args): Widget.__init__(self, *args) table = self.field.table from sqlkit.widgets import SqlTable loader_spec = self.master.laygen.loaders[self.field_name] direction = self.field.property.direction.name if direction == 'MANYTOMANY': direction = "m2m" elif direction == 'ONETOMANY': direction = "o2m" self.table = SqlTable( table.name, geom = (-1, -1), rows = loader_spec.get('rows', 5), mapper = self.field.mapper, session=self.master.session, metadata=self.master.metadata, dbproxy=self.master.dbproxy, naked=True, mode=self.master.mode, format = filter_related(self.master._format_dict, self.field_name), col_width = filter_related(self.master.col_width, self.field_name), field_list = loader_spec.get('field_list', None), addto=self.gtkwidget, ignore_fields = [c.name for c in self.field.property.remote_side], dev = self.master.dev, relationship_leader = self.master, relationship_mode = direction, relationship_path = self.master.relationship_path + [self.field_name], label_map = self.prepare_label_map() ) # We generally want a table to expand if possible parent = self.gtkwidget.get_parent() if parent: prop_names = [prop.name for prop in parent.list_child_properties()] if 'y-options' in prop_names: parent.child_set_property(self.gtkwidget, 'y-options', gtk.EXPAND | gtk.FILL) if isinstance(parent, gtk.VBox): parent.child_set_property(self.gtkwidget, 'expand', True) self.master.related.add_element(self.field_name, self.table) def set_value(self, value, initial=False): self.table.set_records(value) self.emit('value-set', value, initial) def get_value(self, shown=False): return self.table.records def prepare_label_map(self): """ Implement the possibility to specify a label key as:: related_field.field_name so that related field can be personalized in different ways. As an example a project can have tho relations to the user table, one is under 'staff' attribute and the second via 'manager'. Bothe these point to 'username' that should appear in the mask with staff and manager label. """ from copy import copy label_map = copy(self.master.label_map) for key, value in label_map.items(): if key.startswith('%s.' % self.field_name): key = key.replace('%s.' % self.field_name, '') label_map[key] = value return label_map class FileWidget(Widget): pass def filter_related(my_dict, relation): """ filters a dict to use only width info for relation 'relation' e.g: a = {'ind.cap' : 5, 'username' : 7} >>> filter_related(a, 'ind') {'cap': 5} """ new = {} strip = '%s.' % relation if my_dict: for key in my_dict: if key.startswith(strip): nkey = re.sub('^%s' % strip, '', key) new[nkey] = my_dict[key] return new sqlkit-0.9.5/sqlkit/widgets/common/0000755000175000017500000000000011714210425016634 5ustar sandrosandrosqlkit-0.9.5/sqlkit/widgets/common/__init__.py0000644000175000017500000000000011714210425020733 0ustar sandrosandrosqlkit-0.9.5/sqlkit/widgets/common/dialogs.py0000644000175000017500000001277611714210425020645 0ustar sandrosandro# Copyright (C) 2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os import re import gtk from sqlkit import _ class SaveDialog(object): FILTER_FILES = ( ( _('All files'), '*.*'), ) ACTION = gtk.FILE_CHOOSER_ACTION_SAVE ACTION_BUTTON = gtk.STOCK_SAVE def __init__(self, title="Save dialog", default_filename='export.csv', run=True): """ A dialog to export data into a csv file :param default_filename: the default """ self.dialog = gtk.FileChooserDialog(title, None, self.ACTION, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, self.ACTION_BUTTON, gtk.RESPONSE_OK)) self.dialog.set_default_response(gtk.RESPONSE_OK) if self.ACTION in (gtk.FILE_CHOOSER_ACTION_SAVE, gtk.FILE_CHOOSER_ACTION_SAVE): self.dialog.set_current_name(default_filename) self.filename = None self.add_filters() self.customize() if run: self.run() def run(self): self.dialog.set_current_folder(self.current_folder()) ret = self.dialog.run() if ret == gtk.RESPONSE_OK: self.write() self.dialog.destroy() def customize(self): pass def add_filters(self): for name, pattern in self.FILTER_FILES: file_filter = gtk.FileFilter() file_filter.set_name(name) file_filter.add_pattern(pattern) self.dialog.add_filter(file_filter) def current_folder(self): import platform import os CURRENT_FOLDER = '' if 'HOME' in os.environ: CURRENT_FOLDER = os.environ['HOME'] elif platform.system() == "Windows": CURRENT_FOLDER = os.path.join(os.environ['USERPROFILE'], 'Desktop') return CURRENT_FOLDER def write(self): self.filename = self.dialog.get_filename() return class OpenDialog(SaveDialog): ACTION = gtk.FILE_CHOOSER_ACTION_OPEN ACTION_BUTTON = gtk.STOCK_OPEN FILTER_FILES = ( ( _('All files'), '*.*'), ) def add_filters(self): SaveDialog.add_filters(self) image_filter = gtk.FileFilter() image_filter.set_name(_('Images')) image_filter.add_pixbuf_formats() self.dialog.add_filter(image_filter) self.dialog.set_filter(image_filter) file_filter = gtk.FileFilter() file_filter.set_name(_('Documents')) for ext in ('odt', 'ods', 'odp', 'ott', 'ots', 'doc', 'dot', 'xls', 'xlsx', 'ppt', 'pps'): file_filter.add_pattern("*." + ext) self.dialog.add_filter(file_filter) def customize(self): preview = gtk.Image() preview.show() self.dialog.set_preview_widget(preview) self.dialog.connect("update-preview", self.update_preview_cb, preview) tbl = gtk.Table() self.entry = gtk.Entry() # TIP: possible new name for an uploaded file label = gtk.Label(_('Preferred new filename:') + " ") label.set_tooltip_text(_('If not empty, the file will be uploaded with this new name')) tbl.attach(label, 0, 1, 0, 1, xoptions=gtk.FILL) tbl.attach(self.entry, 1, 2, 0, 1) tbl.show_all() self.dialog.set_extra_widget(tbl) def update_preview_cb(self, file_chooser, preview): filename = file_chooser.get_preview_filename() if not filename: file_chooser.set_preview_widget_active(False) return ext = os.path.splitext(filename)[1] ## openoffice previewer if filename and re.match('\.(odt|odp|ods|odg|ots|ott)', ext): return self.openoffice_preview(filename, file_chooser, preview) ## image previewer try: pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 128, 128) preview.set_from_pixbuf(pixbuf) have_preview = True except Exception, e: have_preview = False file_chooser.set_preview_widget_active(have_preview) return def openoffice_preview(self, filename, file_chooser, preview): import zipfile import tempfile oozip = zipfile.ZipFile(filename, 'r') thumb = oozip.read('Thumbnails/thumbnail.png') fh, fname = tempfile.mkstemp(suffix='.png', prefix='thumb-') f = open(fname, 'w') f.write(thumb) f.close() pixbuf = gtk.gdk.pixbuf_new_from_file(fname) preview.set_from_pixbuf(pixbuf) file_chooser.set_preview_widget_active(True) try: os.close(fh) finally: os.remove(fname) def write(self): self.filename = self.dialog.get_filename() self.new_filename = self.entry.get_text() sqlkit-0.9.5/sqlkit/widgets/common/uidescription.py0000644000175000017500000000700311714210425022067 0ustar sandrosandro""" General uimanager description. Divided in: 1. general purpose 2. table only 3. mask only 4. browse only (when a mask is only ment to show fixed data) 5. print (not ready) 6. debug (gtk + table browse) """ GENERAL_UI = ''' ''' PRINT_UI = ''' ''' TABLE_UI = ''' ''' TREEPOPUP = ''' ''' MASK_UI = ''' ''' BROWSE_UI = ''' ''' DEBUG_UI = ''' ''' sqlkit-0.9.5/sqlkit/widgets/common/completion.py0000644000175000017500000014274311714210425021372 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Public API ========== .. autoclass:: SimpleCompletion :members: autostart, force_enum, group_by, filtered_query, query, filter, set_values, attrs .. autoclass:: FkeyCompletion .. autoclass:: M2mCompletion """ import re from copy import copy import gtk from gobject import markup_escape_text from sqlalchemy import text, select, Table, exceptions from sqlalchemy.sql import and_, or_, not_ from sqlalchemy.orm import class_mapper, mapper import sqlkit from sqlkit import debug as dbg, _, exc from sqlkit.db.proxy import table2mapper from sqlkit.db.utils import tables, get_description, DictLike from sqlkit.db.django_syntax import django2query from sqlkit.db import utils from sqlkit.debug.sql import debug_inline_params ####### Completion # EntryCompletion widgets are added on the fly triggered by F1, C-F1 or menu items # select_values will get data from the db and fill a listore # match_function filters using re.search from the listore to fill in the completion # OP = { 'sqlite' : 'LIKE', 'postgres' : '~*', 'postgresql' : '~*', 'mysql' : 'ILIKE', } class SimpleCompletion(object): """ An object that hold all the information on how to retrieve possible completions in simple cases """ __metaclass__ = None if not dbg.debug else dbg.LogTheMethods autostart = False autostart_last_length = 0 VALUE_COL = 0 DESCRIPTION_COL = 0 MARKUP_COL = 0 OBJ_COL = 1 MATCH_COL = 2 SEARCH_COL = 0 group_by = None """ The field_name on which the output should be grouped. ``field_name`` will be added to the request and used to group completion options. """ autostart = None """A possible interger stating after how many chars completion should start automatically""" query = None """The query that will be issued upon completion request if ``Alt`` is pressed (i.e.: not all filters applied). You can add filters width :meth:`filter` or via SqlAlchemy syntax.""" filtered_query = None """The query that will be issued upon completion request. You can customize it as for :attr:`query`""" force_enum = None "Modify output: show all options regardless of what has been already written in the field" column = None "the database column it will use when searching the values. It's a Sqlalchemy Column" attrs = None """a list of attributes that will be added to the object retrieved to fill the completion choices. You can add attributes if you need to have more data e.g. in :ref:`completion hooks ` """ def __init__(self, master, widget, field_name): """completion is the way we can find possible completions of text in a cell. In case the edited field has a Foreign Key constraint completion will follow the table/field to retrieve the possible values, in case we don't have an fk, completion will suggest values already present in the db. A completion must be 'requested' (is only issued on demand), unless autocompletion is set to n (chars inputted after which it starts). Normal bindings are F1 Shift_Enter, Control_Enter or right-click and will fill the gtk.EntryCompletion associated to the entry with values accordingly. Bindings are set in SqlText inside text_editing_started_cb while in SqlMask inside miniwidgets (actually only VarcharWidget and FKeyWidget) widget.connect('button-press-event',self.completion.keypress_event_cb, self.field_name) It requires quite a lot of object be already setup in the master (actually SqlMask or SqlTable?) self.master.mapper_info.fields: an InspectMapper Object self.master.engine self.master.mapper self.master.values in case of an id, completion will substitute it with a description field, for the user benefit. The real value of the field will be hold there. self.master.gui_fields (mask wdgets): the mapping field_name -> widget """ self.master = master self._ids = {} hid = self.master.connect('delete-event', self.destroy) self._ids['master_delete_event'] = (master, hid) self.lookup_values = {} self.MATCH_OPERATOR = OP.get(self.master.metadata.bind.name, 'ILIKE') self.widget = widget self.field_name = field_name self.completion = self.prepare_completion() self.column = self.master.mapper_info.fields[field_name]['col'] try: self.table = self.column.table except AttributeError: raise exc.ColumnWithoutTable("Column %s does not have .table attribute" % field_name) self.format = get_description(self.table, attr='format') self.description = tables[self.table.name].description self._attrs = tables[self.table.name].attrs # self.columns = [self.master.mapper_info.fields[field_name]['col'] for field_name in self.attrs] self.columns = [self.master.mapper_info.fields[self.field_name]['col']] # self.attrs = tables[self.table.name].attrs self._group_by = None self._order_by = [self.column] self._dynamic_filters = None self.mapper = self.master.mapper # used to track path relationship self.force_enum = False self.displaying_completion = False # self.query = self.master.session.query(getattr(self.master.mapper.class_, self.field_name)).autoflush(False) self.query = self.master.session.query(*self.columns).autoflush(False) self.filtered_query = None self.match_dict = {} def destroy(self, sqlwidget): for obj, hid in self._ids.values(): obj.disconnect(hid) del self._ids for attr in ('master', 'query', 'mapper', 'filtered_query', 'widget',): try: delattr(self, attr) except AttributeError, e: pass def set_group_by(self, field_name): """ Modify the way output is presented adding a group field. If group_by is not already in table.attrs, add it runtime :param field_name: the field name """ if not field_name in self.attrs: self.attrs += [field_name] group_column = self.table.columns[field_name] self.columns += [group_column] if hasattr(self.query, 'add_columns'): self.query = self.query.add_columns(group_column) # SqlAlchemy 0.6 else: self.query = self.query.add_column(group_column) # SqlAlchemy 0.5 if self.filtered_query: self.filtered_query = self.filtered_query.add_columns(grup_column) self._group_by = field_name self._order_by = [self.table.columns[f] for f in [field_name, self.description]] def get_group_by(self): return self._group_by def get_entry(self): return self.widget.get_entry() group_by = property(get_group_by, set_group_by) entry = property(get_entry) def _get_attrs(self): return self._attrs def _set_attrs(self, attrs): self._attrs = attrs self.columns = [getattr(self.mapper.class_, field_name) for field_name in attrs] self.query = self.master.session.query(*self.columns).autoflush(False) attrs = property(_get_attrs, _set_attrs) def prepare_completion(self): """ Prepare an EntryCompletion suitable for... sqlcompletion """ completion = gtk.EntryCompletion() completion.set_property('popup-set-width', False) completion.set_property('minimum-key-length', 0) # completion.set_property('popup-single-match', False) ??? completion.connect('match-selected', self.match_selected_cb, self.field_name) renderer = gtk.CellRendererText() completion.pack_start(renderer) completion.add_attribute(renderer, 'markup', self.MARKUP_COL) ## we match against the description field ## from the reference docs: # The match function is used by the entry completion to determine # if a row of the associated tree model should be in the completion list completion.set_match_func(self.match_func) self.model = self.prepare_model() completion.set_model(self.model) return completion def prepare_model(self): return gtk.ListStore(str, object, bool) def add_completion(self, entry): """ add a completionEntry to an Entry """ entry.set_completion(self.completion) def add_callbacks(self, entry): entry.connect('button-press-event', self.pop_menu, self.field_name, 3) entry.connect('key-press-event', self.keypress_event_cb, self.field_name) entry.connect('key-release-event', self.keypress_event_cb, self.field_name, 'release') ### Filling & popping the completion def show_possible_completion(self, widget, mode, apply_filter=True, pop=True, sb=True, value=None, return_sql=False, one=False): """ fill in a ListStore to use with completion, fetch data from db :param mode: determines the match of the already typed text: :start: only matching data starting with typed text :regexp: typed text is a regexp (*~ or ILIKE) :enum: all values :exact: triggers the completion machanism but just for the exact match :param apply_filter: filter added with .filter() will be applied :param return_sql: boolean only the sql is returned (debug purpose) :param value: value to use as "displayed value". debug purpose :param pop: don't try to pop the selection (debug purpose) :param one: return the result that should be just one (used internally within check_existance) """ self._apply_filter = apply_filter if self.force_enum: mode = 'enum' self.mode = mode if sb: # TIP: status bar self.master.sb(_("search mode: %s") % mode) self.model.clear() self._prev_group_by_value = None result = self.select_values(mode, return_sql=return_sql, value=value) self._needs_recalculating = True if return_sql: return debug_inline_params(result, bind=None) self.completion.set_model(None) if not result: if not hasattr(self, 'column_lookup'): # Nothing to do if it's not a foreign key return try: f = self.master.gui_fields[self.field_name] q = self.filtered_query or self.query result = q.filter_by(**{self.column_lookup.name : f.get_value()}) except exc.ValidationError: # ValidationError occurs when f.get_value() fails finding a possible completion return for obj in result: self.append_to_model(obj) self.completion.set_model(self.model) if one and mode == 'exact': ## 'exact' is used by self.check_existance just one result will ## be returned even if no check is done that more that one exists ## 'obj' may not be defined here. It happens when 1. you have a ## filter on completion, 2. you bypass it with Control-Alt, ## 3. select a match that activates the cell correctly. A second ## pass on the same cell, if you try to complete, exact will be ## used an will fail. try: return DictLike(obj) except UnboundLocalError: return if pop: self.make_completion_to_pop(self.get_entry()) else: # doctest purpose for row in self.model: print self.model.get_value(row.iter, 1) def debug_completion_sql(self, mode='start', value='', apply_filter=True): """ Debug version of show_possible_completion """ return self.show_possible_completion(None, mode, return_sql=True, value=value, apply_filter=apply_filter) def match_func(self, completion, key, iter): """ When editing, this function limits the visible entry of the model """ ## I want to have also group_by field in the completion, but only when ## thare's a match to be shown under that group ## so I need to passs twice, this is done just once each completion ## popup text = self.model.get_value(iter, self.SEARCH_COL) prev = getattr(self, '_prev_match_value', None) if self.mode == 'enum': return True if key != prev or self._needs_recalculating: self.match_dict = {} self._prev_match_value = key ## recalculate if the text (key) in the entry was changed self.model_set_match_col(key, ) self._needs_recalculating = False return self.match_dict.get(self.model.get_path(iter), False) def model_set_match_col(self, key): """ set MATCH_COL to be returned by match_func in a 2 pass process: """ miter_dict = {} i = 0 for row in self.model: value = self.model.get_value(row.iter, self.VALUE_COL) text = self.model.get_value(row.iter, self.SEARCH_COL) obj = self.model.get_value(row.iter, self.OBJ_COL) descr = self.model.get_value(row.iter, self.DESCRIPTION_COL) path = self.model.get_path(row.iter) if value is None: ## it's a group_by field #self.model.set_value(row.iter, self.MATCH_COL, False) # store it's iter but set to false for the moment (will not match) miter_dict[descr] = [path, False] else: match = False ## start mode try: if not key: match = True elif text is None: match = False elif self.mode == 'start' and re.search("^%s" %(key), text, re.IGNORECASE): match = True elif re.search(key, text, re.IGNORECASE): # regexp mode match = True except re.error, e: match = False if match and self._group_by: ## switch ON the display of group by label in completion try: miter_dict[obj[self._group_by]][1] = True except IndexError, e: print "Probable missing value for %s" % text except KeyError: ## FIXME but I have not uderstood why I need it. # I get here when -after accepting a completion- I go back and delete some chars pass self.match_dict[path] = match for path, val in miter_dict.values(): self.match_dict[path] = val def append_to_model(self, obj): """ How each selected record gets to the model """ m_iter = self.model.append(None) if hasattr(self, 'possible_values'): value = obj else: value = markup_escape_text(getattr(obj, self.field_name)) self.model.set(m_iter, self.VALUE_COL, value, self.MATCH_COL, True) def make_completion_to_pop(self, entry): """ Create an event that will cause completion to pop """ self.displaying_completion = True entry.grab_focus() # entry.emit('changed') ## needed to make completion to pop # read note in widget.pop_completion() self.widget.pop_completion() ## we don't want selection to prevent autocompletion from deleting already written text entry.select_region(-1,-1) ### Call back def match_selected_cb(self, wdg, model, iter, field_name): """ after selecting an entry store the value in self.values and set the entry with the correct value. """ # the completion model has 1 or 4 columns (in case of FKey): # 0: the real value to be put in the db # 1: the value to be shown in the field # 2: the value to be shown in the drop down completion menu # 3: the row return by the SELECT when building the completion # if more fields are present more columns get written value = model.get(iter, self.VALUE_COL)[0] obj = model.get_value(iter, self.OBJ_COL) self.master.set_value(field_name, value, fkvalue=None, initial=False) ## here field.set_value is not yet needed. It's there just in case the field has some ## validation logic self.master.gui_fields[field_name].set_value(value, initial=False) self.displaying_completion = False self.master.run_hook('on_completion', obj, field_name=self.field_name) return True ### Sql SELECT def select_values(self, mode, return_sql=False, value=None): """ execute the SELECT DISTINCT and return values """ oper = self.get_match_operator() if hasattr(self, 'possible_values'): return self.get_values(mode, value) if self._apply_filter: query = self.filtered_query or self.query else: query = self.query if not mode == 'enum': filtering_value = self.get_displayed_value(value=value) self.autostart_last_length = len(str(filtering_value)) if filtering_value: query = self.compose_select_statement(query, oper, filtering_value) query = self.add_dynamic_filters(query) query = query.order_by(*self._order_by).distinct() if return_sql: return query return query.all() def get_values(self, mode, value): """ return all completion values when a list was supplied explicitey using set_values """ if isinstance(self.possible_values, (list, tuple)): if not value or mode == 'enum': return self.possible_values else: if mode == 'regexp': return [v for v in self.possible_values if re.search(value, v)] else: return [v for v in self.possible_values if re.match(value, v)] else: return self.possible_values(value) def set_values(self, values): """ Set explicitely the possible completion values. :param values: a list of values or a callable that will return a list of possible values Can be a list or a callable that will be called to get the real list of values passing the (possible) text in the entry as parameter """ if not callable(values): sorted(values) self.possible_values = values def compose_select_statement(self, query, oper, value): if oper: if oper == 'ILIKE': query = query.filter(self.column.ilike(value)) else: query = query.filter(self.column.op(oper)(value)) return query def get_displayed_value(self, value=None): if value is not None: val = value else: val = self.master.get_value(self.field_name, shown=True) or '' self.autostart_last_length = len(str(val)) if isinstance(val, basestring): return self.add_for_LIKE_and_REGEXP(val.strip('\\')) def get_match_operator(self): ## FIXME: pretty stupid at the moment... if self.mode == 'start': oper = self.MATCH_OPERATOR if self.mode == 'regexp': oper = self.MATCH_OPERATOR if self.mode == 'exact': oper = '=' else: oper = self.MATCH_OPERATOR return oper def add_for_LIKE_and_REGEXP(self, value): """add a % simbol to get expansion if completion is attempted in LIKE mode""" if self.MATCH_OPERATOR in ('LIKE', 'ILIKE'): if self.mode == 'start': if not value.endswith('%'): value += '%' if self.mode == 'regexp': if not value.startswith('%'): value = '%' + value + '%' elif self.MATCH_OPERATOR in ('~*',): if self.mode == 'start': if not value.startswith('^'): value = '^' + value return value def add_dynamic_filters(self, query): """ Add dinamic filters set with ``.filter`` """ if not self._dynamic_filters or not self._apply_filter: return query ## substitute values kw = {} OR = self._dynamic_filters[0] dynamic_filters = self._dynamic_filters[1:] for key, val in dynamic_filters: value = self.parse_value(val) # get a possible dynamic filter if value: kw[key] = value if kw: return django2query(query, self.mapper, OR=OR, **kw) else: return query ### Misc def autostart_completion(self): """ Completion starts on demand or: 1. when more that self.autostart chars have been written 2. if text in entry has fewer chars than chars that triggered it """ if self.displaying_completion: return if self.autostart: text = self.master.get_value(self.field_name, shown=True) length = len(text or '') if (not self.autostart_last_length and length >= self.autostart) \ or (length + 1 < self.autostart_last_length): autostart = self.autostart self.autostart = False self.show_possible_completion(self.widget.get_entry(), 'regexp') self.autostart = autostart return True def pop_menu(self, wdg, ev, field_name, num_button=3): """pops a menu with chooices of different completion:: - regexp - start """ from sqlkit.widgets.table.tablewidgets import CellWidget from sqlkit.layout.misc import StockMenuItem if not ev.button == num_button: return False try: self.w['M=popup'].destroy() except: pass self.master.widgets['M=popup'] = gtk.Menu() menu = self.master.widgets['M=popup'] ## info on field item = StockMenuItem(label="info on %s" % field_name.replace('_','__'), stock='gtk-info') gtk.MenuItem(label="info on %s" % field_name.replace('_','__')) item.connect('activate', self.master.show_field_info, field_name) label = _("Show all info on this field, db type & Co.") item.set_tooltip_text(label) menu.append(item) ## possible values regexp # TIP: menu enty in menu on right click on down arro in fkey completion widget item = gtk.MenuItem(label=_("Show possible values: regexp - Ctrl-Enter")) if not isinstance(self.widget, CellWidget): item.connect('activate', self.show_possible_completion, 'regexp') # TIP: yellow tip to menu entry in down arrow in completion widget label = _("Show all values in the db that match your string") item.set_tooltip_text(label) menu.append(item) ## possible values starting item = gtk.MenuItem(label=_("Show possible values, starting - Shift-Enter")) if not isinstance(self.widget, CellWidget): item.connect('activate', self.show_possible_completion, 'start') label = _("Show all values in the database starting with your string") item.set_tooltip_text(label) menu.append(item) ## edit table referenced as foreign key if self.master.is_fkey(field_name): item = StockMenuItem(label=_("Edit the referenced table in 'table' mode"), stock='gtk-leave-fullscreen') item.connect('activate', edit_foreign_key_table, self.master, field_name, 'table') label = _("Edit the table from which admitted values are taken in 'table' mode") item.set_tooltip_text(label) menu.append(item) if self.master.is_fkey(field_name): item = StockMenuItem(label=_("Edit the referenced table in 'mask' mode"), stock='gtk-leave-fullscreen') item.connect('activate', edit_foreign_key_table, self.master, field_name, 'mask') label = _("Edit the table from which admitted values are taken in 'mask' mode") item.set_tooltip_text(label) menu.append(item) menu.show_all() menu.popup(None, None, None, ev.button, ev.time) return True def keypress_event_cb(self, wdg, event, data=None, when='before'): """trigger various callback to keypress Set in miniwidgets or in text_editing_started_cb A callback connected with connect_after should set when to after """ if isinstance(wdg, gtk.TreeView): pre = 'tree' else: #self.length_control(wdg, data) pre = 'entry' ksym = gtk.gdk.keyval_name(event.keyval) mods = ['key_' + pre] ## mod2 gets in the way when you press the NumLock: I don't want ## to depend on that, as I'm not usng it!!! for prefix, mask in [('ctrl', gtk.gdk.CONTROL_MASK), ('shift', gtk.gdk.SHIFT_MASK), ('alt', gtk.gdk.MOD1_MASK), #('mod2', gtk.gdk.MOD2_MASK), ('mod3', gtk.gdk.MOD3_MASK), ('mod4', gtk.gdk.MOD4_MASK), ('mod5', gtk.gdk.MOD5_MASK),]: if event.state & mask: mods.append(prefix) ksym = '_'.join(mods + [ksym]) #dbg.write('ksym', ksym, 'mods', mods) if when == 'release': self.autostart_completion() return if ksym in dir(self): return getattr(self, ksym)(wdg, event, data) return False def key_entry_F1(self, wdg, event, data): self.show_possible_completion(wdg, mode='regexp') return False def key_entry_shift_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='start') return True def key_entry_shift_alt_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='start', apply_filter=False) return True def key_entry_ctrl_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='regexp') return True def key_entry_ctrl_alt_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='regexp', apply_filter=False) return True def key_entry_ctrl_shift_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='enum') return True def key_entry_ctrl_shift_alt_Return(self, wdg, event, data): self.show_possible_completion(wdg, mode='enum', apply_filter=False) return True def key_entry_Return(self, wdg, event, data): # return not self.check_existance(wdg) def key_entry_Tab(self, wdg, event, data): # waiting for a complete handling of validation return False check = self.check_existance(wdg) return check def check_existance(self, entry): """ don't accept false fkey... Return True if field exists or validates """ text = entry.get_text() if not text: return True if self.master.is_fkey(self.field_name) or ( self.master.relationship_mode == 'm2m' and not self.master.m2m_editable): ## check if the text entered was in fact a complete and legal fkey ## or unique field (for m2m) if self.master.fkey_is_valid(self.field_name): ## see note below on 'on_completion' obj = self.show_possible_completion(None, 'exact', one=True) self.master.run_hook('on_completion', obj, field_name=self.field_name) return True try: if self.master.is_fkey(self.field_name): clean_value = self.master.gui_fields[self.field_name].clean_value(text, input_is_fkey=False) if clean_value == None: raise exc.NoResultFound self.master.sb(_("Good match"), seconds=4) self.master.set_value(self.field_name, clean_value, fkvalue=text) ## since on_completion needs an obj that must have all attributes defined in ## self.attrs we trigger the complete tour obj = self.show_possible_completion(None, 'exact', one=True) self.master.run_hook('on_completion', obj, field_name=self.field_name) return True else: self.master.sb(_("No exact match, trying regexp completion"), seconds=4) self.show_possible_completion(entry, mode='regexp', sb=False) self.master.cell_entry.valid = False return False except (exc.NoResultFound, exc.MultipleResultsFound), e: self.master.sb(_("No exact match, trying regexp completion"), seconds=4) self.show_possible_completion(entry, mode='regexp', sb=False) return False return True def get_obj_no_flush(self, value): """ check if the object exists and .one(), but don't trigger .flush() """ filter_cnd = {str(self.field_name) : value} query = self.master.session.query(table2mapper(self.table)).autoflush(False) return query.filter_by( **filter_cnd ).one() def filter(self, OR=False, main_query=False, **kwargs): """ :param main_query: add the filter to the main query Add filters to the completion. Filters must be expressed in django-like syntax Value can be in the form ``$field_name`` (see below):: t = SqlMask(...) t.completions.field1.filter(cod='$field2') would request 1. to retrieve the value of field2 via t.get_value(field2) 2. to add .filter(field2 = value) to the query that retrieves the possible completions Filter conditions are relative *to the mapper of the completion*: for a completion on a ForeignKey it's the referenced table's mapper. To state it again and referring to example 40 of the demo: if you edit movie table and complete on diector_id, the following code would select the ``nation`` of the director in table ``director``:: t.completions.director_id.filter(nation='IT') In point 1. above ``t.get_value()`` is relative to the SqlWidget in which the completion is requested. This is relevant when related tables are present in the mask. Field value of the main mask can be referred to as ``$main.field_name`` 1. each token starting with ``$`` as in ``$title`` is stripped from the ``$`` and the remaining part is used as field_name and t.get_value(field_name) is used instead 2. if the field_name part starts with ``main.`` (as in ``$main.title``) t.get_value(field_name) is not issued in the active SqlWidget but in the SqlWidget pointed to by ``relationship_leader`` This in general is the main SqlMask holding possibly different m2m tables. This makes it possible for rows in an m2m table to complete only with values related to the referring header. """ kw_dyn_list = [OR] keys = [] # firstly parse the args for 'dynamic' args ($) for key, val in kwargs.iteritems(): if isinstance(val, basestring) and val.startswith('$'): keys += [key] kw_dyn_list += [(key, re.sub('^\$', '', val))] for key in keys: kwargs.pop(key) # now 'static' filters if kwargs: if main_query: self.query = django2query(self.query, self.mapper, OR=OR, **kwargs) else: self.filtered_query = django2query(self.filtered_query or self.query, self.mapper, OR=OR, **kwargs) self._dynamic_filters = kw_dyn_list def parse_value(self, value): """ return value after 2 substitutions as described in .filter() """ if isinstance(value, str): if value.startswith('main.'): value = value.replace('main.','') value = self.master.relationship_leader.get_value(value) else: value = self.master.get_value(value) return value def __str__(self): return "" % self.field_name class FkeyCompletion(SimpleCompletion): """ A completion that follows foreign key to get values to complete and to substitute for lookup. Inherits from :class:`SimpleCompletion` """ VALUE_COL = 0 DESCRIPTION_COL = 1 MARKUP_COL = 2 OBJ_COL = 3 MATCH_COL = 4 SEARCH_COL = 5 def __init__(self, master, widget, *args, **kw): SimpleCompletion.__init__(self, master, widget, *args, **kw) self.table, self.column = get_foreign_info(self.master, self.field_name, names=False) self.table_lookup, self.column_lookup = self.table, self.column self.description = get_description(self.table, attr='description') self.search_field = self.description self._attrs = get_description(self.table, attr='attrs', metadata=self.master.metadata) if self.search_field not in self._attrs: self._attrs += [self.search_field] self.format = get_description(self.table, attr='format', metadata=self.master.metadata) self._order_by = [self.table_lookup.c[self.description]] self.widget = widget class_ = utils.get_class(self.table_lookup) if class_: self.mapper = class_mapper(class_) else: class Tbl(object): pass self.mapper = mapper(Tbl, self.table_lookup) self.columns = [getattr(self.mapper.class_, field_name) for field_name in self.attrs] self.query = self.master.session.query(*self.columns).autoflush(False) def get_entry(self): return self.widget.entry def prepare_model(self): ## last col is for markup return gtk.ListStore(object, str, str, object, bool, str) def compose_select_statement(self, query, oper, value): if oper: lookup_col = self.table_lookup.columns[self.search_field] if oper == 'ILIKE': query = query.filter(lookup_col.ilike(value)) else: query = query.filter(lookup_col.op(oper)(value)) return query def match_selected_cb(self, wdg, model, iter, field_name): """ after selecting an entry store the value in self.values and set the entry with the correct value. """ # the completion model has 1 or 4 columns (in case of FKey): # 0: the real value to be put in the db # 1: the value to be shown in the field # 2: the value to be shown in the drop down completion menu # 3: the row return by the SELECT when building the completion # if more fields are present more columns get written value = model.get(iter, self.VALUE_COL)[0] obj = model.get_value(iter, self.OBJ_COL) fkvalue = model.get(iter, self.DESCRIPTION_COL)[0] self.master.set_value(field_name, value, fkvalue=fkvalue, initial=False) ## here set_field IS needed. Read the note in fields.FKeyField.set_value self.master.gui_fields[field_name].set_value(value, initial=False) ## just handier to accept it already if not self.master.is_mask(): self.master.cell_entry.entry.emit('activate') self.displaying_completion = False self.master.run_hook('on_completion', obj, field_name=self.field_name) return True ### What to show def append_to_model(self, obj): """ add to the model that has 6 cols """ obj_list = [] ## FIXME what if we have a multiple pkey? fvalue = getattr(obj, self.attrs[0]) # fdescr = getattr(obj, self.description) fsearch = getattr(obj, self.search_field) Obj = DictLike(obj) description = unicode((self.format % Obj)).strip() m_iter = self.get_iter(obj) ## FIXME: what if the pkey is composed? self.model.set(m_iter, self.VALUE_COL, fvalue, self.DESCRIPTION_COL, description, self.MARKUP_COL, " %s" % markup_escape_text(description), self.OBJ_COL, Obj, self.MATCH_COL, True, self.SEARCH_COL, fsearch, ) def get_iter(self, obj): # based on self.__prev_group_by_value # self.__prev_group_by_iter if not self._group_by: return self.model.append(None) else: prev = getattr(self, '_prev_group_by_value', None) new_value = getattr(obj, self._group_by) self._prev_group_by_value = new_value ## the row is NOT a ?? if new_value is not None and new_value == prev: return self.model.append(self._prev_group_by_iter) else: ## add to completion's model m_iter = self.model.append(None) self._prev_group_by_iter = None self.model.set(m_iter, self.DESCRIPTION_COL, new_value, self.MARKUP_COL, "%s" % markup_escape_text(new_value), self.MATCH_COL, True, ) m_iter = self.model.append(None) return m_iter def get_obj_no_flush(self, value): """ check if the object exists and .one(), but don't trigger .flush() """ filter_cnd = {str(self.description) : value} query = self.master.session.query(table2mapper(self.table)).autoflush(False) return query.filter_by( **filter_cnd ).one() # def compose_binary_expression(self, **kwargs): # return compose_binary_expression(self.table, **kwargs) def get_filtered_clause_list(self, **kw): """ apply filter rules to this particular completion request. Note: mapper is not available in the general situation, so we use table to build the clause. That means we cannot follow any further relationship. """ clause_list, path = django2sqlalchemy(table=self.table, **kw) return clause_list def __str__(self): return "" % self.field_name class EnumCompletion(SimpleCompletion): """ A completion that shows enum alternatives. Inherits from :class:`SimpleCompletion` """ VALUE_COL = 0 DESCRIPTION_COL = 1 SEARCH_COL = 1 MARKUP_COL = 1 def __init__(self, master, widget, *args, **kw): SimpleCompletion.__init__(self, master, widget, *args, **kw) self.widget = widget self._needs_recalculating = False def get_entry(self): return self.widget.entry def prepare_model(self): ## last col is for markup return gtk.ListStore(object, str, ) def match_selected_cb(self, wdg, model, iter, field_name): """ after selecting an entry store the value in self.values and set the entry with the correct value. """ # the completion model has 1 or 4 columns (in case of FKey): # 0: the real value to be put in the db # 1: the value to be shown in the field # 2: the value to be shown in the drop down completion menu # 3: the row return by the SELECT when building the completion # if more fields are present more columns get written value = model.get(iter, self.VALUE_COL)[0] fkvalue = model.get(iter, self.DESCRIPTION_COL)[0] self.master.set_value(field_name, value, fkvalue=fkvalue, initial=False) ## just handier to accept it already if not self.master.is_mask(): self.master.cell_entry.entry.emit('activate') self.displaying_completion = False #self.master.run_hook('on_completion', obj, field_name=self.field_name) return True ### What to show def show_possible_completion(self, widget, mode, apply_filter=True, pop=True, sb=True, value=None, return_sql=False, one=False): """ fill in a ListStore to use with completion, fetch data from db :param mode: determines the match of the already typed text: :start: only matching data starting with typed text :regexp: typed text is a regexp (*~ or ILIKE) :enum: all values :exact: triggers the completion machanism but just for the exact match :param apply_filter: filter added with .filter() will be applied :param return_sql: boolean only the sql is returned (debug purpose) :param value: value to use as "displayed value". debug purpose :param pop: don't try to pop the selection (debug purpose) :param one: return the result that should be just one (used internally within check_existance) """ self._apply_filter = apply_filter if self.force_enum: mode = 'enum' self.mode = mode if sb: # TIP: status bar self.master.sb(_("search mode: %s") % mode) self.model.clear() self.completion.set_model(None) field = self.master.gui_fields[self.field_name] for idx, text in field.values.iteritems(): self.append_to_model(idx, text) self.completion.set_model(self.model) if one and mode == 'exact': # used by self.check_existance ## just one result will be returned even if no check is done that more that one exists # I'm not sure that obj is *always* defined!... I believe so! return DictLike(obj) if pop: self.make_completion_to_pop(self.get_entry()) else: # doctest purpose for row in self.model: print self.model.get_value(row.iter, 1) def append_to_model(self, idx, text): """ add to the model that has 6 cols """ miter = self.model.append(None) self.model.set(miter, self.VALUE_COL, idx, self.DESCRIPTION_COL, text, ) def get_obj_no_flush(self, value): """ check if the object exists and .one(), but don't trigger .flush() """ filter_cnd = {str(self.description) : value} query = self.master.session.query(table2mapper(self.table)).autoflush(False) return query.filter_by( **filter_cnd ).one() # def compose_binary_expression(self, **kwargs): # return compose_binary_expression(self.table, **kwargs) def get_filtered_clause_list(self, **kw): """ apply filter rules to this particular completion request. Note: mapper is not available in the general situation, so we use table to build the clause. That means we cannot follow any further relationship. """ clause_list, path = django2sqlalchemy(table=self.table, **kw) return clause_list def __str__(self): return "" % self.field_name class M2mCompletion(FkeyCompletion): """ A completion that completes on the whole line: each field can be used to choose the same record. It's not possible in this mode to compose a new record field by field. The lookup of foreign key values needs a different table than the one used to complete. This is called table_lookup/column_lookup. Inherits from :class:`FkeyCompletion` """ VALUE_COL = 0 DESCRIPTION_COL = 1 MARKUP_COL = 2 OBJ_COL = 3 MATCH_COL = 4 SEARCH_COL = 5 def __init__(self, *args, **kw): SimpleCompletion.__init__(self, *args, **kw) self.query = self.master.session.query(self.master.mapper.class_).autoflush(False) if self.master.is_fkey(self.field_name): table_lookup_real, column_lookup = get_foreign_info(self.master, self.field_name, names=False) self.table_lookup = table_lookup_real.alias() self.column_lookup = self.table_lookup.c[column_lookup.name] self.search_field = get_description(table_lookup_real.name, attr='description', metadata=self.master.metadata) self.sql_join = (self.column == self.column_lookup) else: self.table_lookup, self.column_lookup = self.table, self.column self.search_field = self.column.name self.description = self.search_field self._order_by = [self.table.c[self.description]] self.attrs = copy(get_description(self.table, attr='attrs', metadata=self.master.metadata)) self.columns = [getattr(self.master.mapper.class_, field_name) for field_name in self.attrs] self.search_column = self.table_lookup.c[self.search_field] self.columns += [self.search_column] self.query = self.master.session.query(*self.columns).autoflush(False) if self.master.is_fkey(self.field_name): self.query = self.query.join((self.table_lookup, self.sql_join)).reset_joinpoint() def match_selected_cb(self, wdg, model, iter, field_name): """ after selecting an entry store the value in self.values and set the entry with the correct value. """ value = model.get(iter, self.VALUE_COL)[0] obj = model.get_value(iter, self.OBJ_COL) if self.master.is_fkey(field_name): fkvalue = model.get(iter, self.DESCRIPTION_COL)[0] else: fkvalue = None ## when the Table is used to render a m2m field ## we don't want to "create" a new match, just pick an old one ## and use that one try: self.master.set_current(pk=value) except Exception, e: print 'match selected debug', e pass self.displaying_completion = False self.master.run_hook('on_completion', obj, field_name=self.field_name) return True def filter(self, **kwargs): """ """ # Filtering on an m2m acts on the resulting row, not on single column # so we need to have the same filter on all columns for completion in self.master.completions: completion._filter_single(**kwargs) def _filter_single(self, **kwargs): FkeyCompletion.filter(self, **kwargs) def __str__(self): return "" % self.field_name ### Functions def debug_liststore(listore): j = 0 for l in listore: iter = listore.get_iter_from_string(str(j)) print listore.get(iter,0), listore.get(iter,1), listore.get(iter,3) j += 1 def edit_foreign_key_table(wdg, master, field_name, mode): """open a SqlMask to edit the table referenced by this ForeignKey""" ## FIXME - works only with dbproxy from sqlkit.widgets import SqlMask, SqlTable ftable, fkey = get_foreign_info(master, field_name) if mode == 'mask': m = SqlMask(ftable, dbproxy=master.dbproxy) try: fkvalue = master.get_value(field_name, shown=False) m.set_records(pk=fkvalue) except exc.ValidationError, e: pass else: SqlTable(ftable, dbproxy=master.dbproxy) def get_foreign_info(master, field_name, names=True): from sqlkit.db.minspect import get_foreign_info foreign_keys = master.mapper_info.fields[field_name]['fkey'] return get_foreign_info(foreign_keys, names=names) sqlkit-0.9.5/sqlkit/widgets/common/sqlwidget.py0000644000175000017500000025276611714210425021233 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys import re import os import datetime import decimal import inspect import gc import shutil import warnings import gtk import pango import gobject from babel import numbers import sqlalchemy from sqlalchemy import exceptions, Table, orm from sqlalchemy.orm import scoped_session, sessionmaker, class_mapper, attributes from sqlalchemy.exc import InvalidRequestError from sqlalchemy.orm.mapper import Mapper from pkg_resources import parse_version import sqlkit from sqlkit import _, exc, layout, debug as dbg, fields from sqlkit import layout, debug as dbg from sqlkit.layout import map_labels, get_label from sqlkit.db import minspect, defaults from sqlkit.widgets.common import completion, sqlfilter, layoutgenerator, dialogs from sqlkit.misc.utils import Container, str2list from sqlkit.misc import printing NUMBERS = (int, float, decimal.Decimal) class SqlWidget(gobject.GObject): """ The main widget used to edit a table or sqlalchemy selectable, i.e. almost everything you can express as table or for which you can provide a mapper or a mapped class. You won't use this as such: this is just the parent of SqlTable an SqlMask. """ #### Signals __gsignals__ = { 'record-selected' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (), ), 'record-saved' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (), ), 'record-new' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (), ), 'record-deleted': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (gobject.TYPE_PYOBJECT,), ), 'records-displayed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (), ), 'after-flush' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # object, session (gobject.TYPE_PYOBJECT , gobject.TYPE_PYOBJECT ), ), 'after-commit' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # object, session (gobject.TYPE_PYOBJECT , gobject.TYPE_PYOBJECT ), ), 'delete-event' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, (), ), } gui_fields = None values = None # real value for foreign key # (when lookup is displeyed instead) __metaclass__ = None if not dbg.debug else dbg.LogTheMethods records = None last_new_obj = None def __init__(self, what=None, mapper=None, ## geometry layout = None, layout_nick = 'default', naked=False, geom=(-1,-1), rows=10, addto=None, gui_field_mapping = None, title=None, show=True, icons='', col_width = None, # SQL table=None, tables=None, class_=None, join=None, ro=False, noup=[], sql=None, constraint={}, dbproxy = None, order_by=None, limit=200, field_list=None, session=None, metadata = None, ignore_fields = None, hide_fields = None, ## more single=False, nick=None, about_dialog = None, dev=False, xml=False, relationship_mode = 'SINGLE', relationship_path = None, relationship_leader = None, format=None, label_map = None, hooks = None, mode='biud', ): """ :param what: this is the table/mapper/class to be displayed. You are encouraged to use it as first argument. The keyword 'what' is just here for backward compatibility as the first versiona had different keywork for table/classes/mappers :param table: the table to be edited. May be a string in which case it will be autoloaded or a `sqlalchemy.Table` object. When using table argument no info on relationship to other table are passed except those contained in the Foreignkey, so you won't be able to show one2many or many2many relationships If you pass 2 table as in ``movie director`` sqlkit will try to join them and build a proper mapper. This keyword is now **deprecated** and will disappear in future releases. You are encouraged to use the table as first argument istead. :param class\_: a sqlalchemy mapped class (eg: that defined via declarative layer). The mapper will be automatically found This keyword is now **deprecated** and will disappear in future releases. You are encouraged to use the class as first argument istead. :param mapper: a mapper. All fields will be desumed by introspection This keyword is now **deprecated** and will disappear in future releases. You are encouraged to use the mapper as first argument istead. :param session: the session to be used :param dbproxy: a :ref:`dbproxy` object that holds info on metadata and can create sessions :param metadata: a metadata object needed for introspection if reflection is required :param hooks: the :ref:`hooks` class to be used for this widget :param ro=False: if set to True the table is not updateable. Old way to set mode='b' :param mode: mode for the widget. See :ref:`set_mode ` for more explanations :param noup=[]: a list of field names that will not be allowed to update. The list may be also a string space or comma separated :param order_by=None: order by column to be used when selecting. Can be * a list of sqlalchemy order_by clause elements * a string as would be set in ordinary SQL statements * a string of fields as would be used in filters: ``user_id__username`` (note the double_underscore). If you also have a relation ``user`` that points to the same table you can use ``user__username`` with the same effect. :param limit=200: limit attribute to be used selecting :param field_list=None: a field_list to be shown. This can also be set via "set_field_list". Operate via toggling visibility of the columns in Tables or preventing autogenerated layout to show. :param ignore_fields = None: field to be ignores (will be deleted from field list to field_list). Can be a list or a comma separated string. :param hide_fields = None: Only valid for SqlTable. List of columns to be hidden (but can be interactively made to show up from menu). Can be a list or a comma separated string. :param single=False: if a table is opened in single mode, destroying the table quits the gtk main loop. :param format=format_dict: equivalent to using: .set_format(format_dict). See :ref:`localization` for more info :param col_width: a dictionary of field_name/width for columns (e.g: {'zip_code' : 6}). It can be passed to masks also that will handle on to nested tables. In this case if you have a relation with attribute 'address' you may use: {'address.zip_code': 6} :param gui_field_mapping: a dict that maps field names to Field classes. You only need to set this for non persisted fields, i.e. field that are not already defined in the mapper :param title: the title for the gtk.Window. :param show: a boolean to tell if the Window will be visible :param dev: if a sqlwidget is opened in dev mode and no field_list is passed, the primary key is shown even if it's a serial, if it's not in dev mode it will be hidden :param naked=False: the table will be rendered without any buttons. It's used in layout when you want to add the table/mask to another layout (eg: in relationships). Default: False. :param addto=None: if set must be a container to which the sqlwidget will be added :param about_dialog: Pass a dialog to invoke when info is called. A gtk.AboutDialog info will be created by default if no about_dialog is passed :param label_map: argument: label_map dictionary as explained in :ref:`localization` Adds a mapping between field_names and msgid/translation for gettext and tooltips :param layout: string description of the layout as described in :ref:`mask`. It's used to generate the layout for SqlMask also for table's :ref:`recordinmask`. :param layout_nick: nick name of a layout to be used. Defaults to ``default``. A layout can be registered under a nick via :func:`register layout `` function :param relationship_mode: SINGLE, m2m, m2o, o2m :param relationship_path: the path needed to join two tables. See sqlalchemy on 'quering with joins'. Used by filter_panel to setup join condition when filtering when relationship_mode is not SINGLE. May be a list or a string: 'genres', 'director nationality', ['director','nationality'] :param relationship_leader: the main sqlmask (sqltable) that "owns" the filter_panel """ gobject.GObject.__init__(self) self._ids = {} # handler for connect to gobjects if what: if isinstance(what, (basestring, sqlalchemy.Table)): table = what elif isinstance(what, Mapper): mapper = what else: class_ = what else: frame = sys._getframe(2) info = "Called by '%s' in %s:%s" % (frame.f_code.co_name, frame.f_code.co_filename, frame.f_lineno) warnings.warn("Use of table/tables/class_/mapper keyword is now Depecated\n" +\ "place table/class_/mapper as first argument\n%s" % info, DeprecationWarning, stacklevel=3) if gui_field_mapping: self.gui_field_mapping = gui_field_mapping self._format_dict = format self.related = Container() self.gui_fields = Container(self.gui_fields) self.field_widgets = Container() self.completions = Container() self.label_map = map_labels(buf=label_map, private=True) self.icons = icons self.col_width = col_width self.xml = xml self.dev = dev # used in self.reload() to prevent expose loop self.reloading = dbg.TraceIt(False, name='reloading') self.geom = geom self.addto = addto self.dbproxy = dbproxy self.mapper = self._get_mapper(mapper, table or tables, class_) self.metadata = self._get_metadata(metadata, self.mapper) ## we can use singular or plural as an option, plural in the code if table and not tables: tables = table self.tables = self._get_table(tables) self.session = self._get_session(session) self.hooks = hooks or defaults.get_hook(self.tables, instance=True, nick=layout_nick) self.nick= self.get_nick(nick) self.set_mode(mode, delay=True) self.ro = ro if ro: self.set_mode('b', delay=True) self.noup = noup self.rows = rows self.hidden_fields = hide_fields or [] self.mapper_info = self.get_fields() self.layout = layout or self._get_layout_from_nick(layout_nick) self.field_list = self._parse_fields(field_list) self.ignored_fields = self._set_ignored_fields(ignore_fields) self._title = self._create_title(title) ## create the interface and add obvious constraints self.naked = naked self.sql = sql self.single = single self.limit=limit self.relationship_mode = relationship_mode self.relationship_path = self._get_relationship_path(relationship_path) self.relationship_leader = relationship_leader if relationship_leader: hid = relationship_leader.connect('delete-event', self.delete_event_cb) self._ids['relationship_leader'] = (relationship_leader, hid) self.records = [] self.about_dialog = about_dialog or self.create_about_dialog() try: self.show(show) except exc.DBAPIError, e: msg = _("Sorry, problems connecting to remote db. Original error was: \n\n%s" % e) raise exc.ConnectionError(msg) if not relationship_leader: self._filter_panel = sqlfilter.FilterPanel(self, visible=False) if hide_fields: self.hide_fields(hide_fields) self.set_format(format) self.query = self.session.query(self.mapper) self.defaults = defaults.Defaults(self.tables, metadata=self.metadata, local=True) self.order_by = order_by self.printing = printing.PrintTool(self) def _get_session(self, session): """ return a session suitable for this widget """ from sqlkit.db.proxy import SKSessionExtension if not session: session = self.dbproxy.get_session() if parse_version(sqlalchemy.__version__) < parse_version('0.7'): for extension in session.extensions: if isinstance(extension, SKSessionExtension): extension.sk_widgets = (getattr(extension, 'sk_widgets') or []) + [self] else: try: session._sk_emitter.add(self) except AttributeError: warning.warn("Session does not have '_sk_emitter', signals after-flush " "after-commit and after-flush-postexec are not implemented") return session def _clean_session_extensions(self, *args): """ get rid of any mechanism that was built for """ if parse_version(sqlalchemy.__version__) < parse_version('0.7'): from sqlkit.db.proxy import SKSessionExtension for extension in self.session.extensions: if isinstance(extension, SKSessionExtension): extension.sk_widgets.pop(extension.sk_widgets.index(self)) else: self.session._sk_emitter.remove(self) def _get_mapper(self, mapper, tables, class_=None): from sqlkit.db.utils import get_class if not mapper and not class_ and get_class(tables): class_ = get_class(tables) if mapper: return mapper elif class_: mapper = class_mapper(class_) ## this grants that a class_ that was registered *before* a binding ## was active (eg: from sqledit when you only have models.py in nick dir) ## gets a correct one. Mailiy needed to automante sqledir nicks if not mapper.local_table.metadata.bind and self.dbproxy.metadata: mapper.local_table.metadata.bind = self.dbproxy.metadata.bind else: mapper = self.dbproxy.get_mapper(mapper, tables) #self.tables = [mapper.local_table.name] ## FIXME: what when we have a join? if parse_version(sqlalchemy.__version__) < parse_version('0.7'): mapper.compile() # this insures that all attribute gets compiled else: orm.configure_mappers() return mapper def _get_metadata(self, metadata, mapper): if metadata: return metadata if self.dbproxy: return self.dbproxy.metadata if mapper: ## FIXME: no good in general? what if you have more that ## one metadata in the same session?... boh return mapper.local_table.metadata def _get_table(self, tables): from sqlalchemy.sql import Join if isinstance(tables, Table): return tables.name if tables: return tables.split()[0] ## FIXME: works for 1 single table if self.mapper: if isinstance(self.mapper.local_table, Join): name = "%s_%s" % (self.mapper.local_table.left, self.mapper.local_table.right) else: name = self.mapper.local_table.name return name def _parse_fields(self, field_list): if not field_list: ### if no layout was defined and no field_list was passed ### we don't add PropertyLoader (ManyToMany relations): ### we would't know where to represent them. ### ### If a layout was defined, we add propertyLoader only for SqlMask ### if self.is_mask() and self.layout: get_the_keys = self.mapper_info.all_keys else: get_the_keys = self.mapper_info.keys if self.dev: new_field_list = get_the_keys() else: ## I want all fields not ending with _id ## *AND* all fields that REFERENCE other keys ## i.e.: if you have city_id that references a city I want ## to see it, but I don't want to see a sequence normally. new_field_list = [f for f in get_the_keys() if not f == 'id' or self.mapper_info.fields[f]['fkey'] ] else: new_field_list = str2list(field_list) return new_field_list def _get_relationship_path(self, path): """ relationship_path is the path needed to get from the main sqlwiget to here: if movie has an attributes 'genres' that is set as a relation to it, the 'path' of the SqlTable of the genres will be: ['genres']. It's used in filter widgets """ self.m2m_editable = False if not path: return [] return str2list(path) def get_pk(self): """ return the pkeys of the primarytable """ return self.mapper_info.pkeys[self.mapper_info.get_primary_table()] def setup_field_validation(self): """ fill self.gui_fields with fileds.Field objects """ raise NotImplementedError ###### Filters/constraints def add_constraint(self, OR=False, **kwargs): """ Add :ref:`constraints`. A constraint may be expressed via keyworks in django-like syntax. Eg.:: name_like='uno%' genres__name__regexp='sto' director_id__birth_date__gte='1/1/1970' if multiple conditions are passed, they will be ANDed unless 'or_=True' A constraint may also be build directly into the self.query object. :param OR: the condition will be ORed """ from sqlkit.db.django_syntax import django2query self.query = django2query(self.query, self.mapper, OR=OR, **kwargs) def add_filter_cb(self, event=None, visible=True): """ Pop a Filter Panel, called from menu item """ self.filter_panel.show() def add_filter(self, active=True, **kw): """ :param active: boolean: make the filter active/inactive see :ref:`filters` and ``add_constraint`` above. Note that filter are always ANDed (OR arameter is not available). If you have a field_name named 'active', the active parameter will hide it. Use keyword ``active__eq`` to bypass it. """ self.filter_panel.add_filter(active, **kw) ###### Layout def _get_layout(self, lay, naked): """ lay may be a definition string or another SqlMask in case the layout has just been build by another SqlMask object and we just need to add controls and so on """ self.run_hook('on_pre_layout') self.get_table_labels() self.laygen = layoutgenerator.LayoutGenerator(self, naked=naked, lay=lay) lay = self.laygen.layout self.lay_obj = layout.Layout(lay, addto=self.addto,label_map=self.label_map, title=self._title, opts='T-') if self.xml: self.lay_obj.xml('/tmp/m_%s.glade' % self.nick) if 'Window' in self.lay_obj.elements: self.lay_obj.prop('Window', 'visible', False) self.widgets = self.lay_obj.show() if 'A.external' in self.widgets: external_align = self.widgets['A.external'] table = external_align.get_parent() table.child_set_property(external_align, 'y-options', gtk.EXPAND | gtk.FILL) if 'S=tree' in self.widgets: internal_align = self.widgets['S=tree'] table = internal_align.get_parent() table.child_set_property(internal_align, 'y-options', gtk.EXPAND | gtk.FILL) if self.is_toplevel(): self.lay_obj.connect(('Window', 'delete-event', self.delete_event_cb)) if self.geom: self.resize(*self.geom) self.setup_field_validation() return (self.lay_obj, self.widgets) def _get_layout_from_nick(self, nick): """ return a layout for table/nick nick """ if self.is_table(): # No way at the moment to register a layout for Tables return from sqlkit.db import get_layout ## FIXME: self.master.tables is not really defined. Mau be a string or a list return get_layout(self.tables, nick=nick or 'default') def show(self, show): """ create the widget and go on displaying the layout """ self.lay_obj, self.widgets = self._get_layout(self.layout, self.naked,) self.prepare_actions() self.prepare_uimanager() self.set_mode() ## only render when dimentions are complete lcontainer_name = self.lay_obj.container().el_def el_def = re.sub(':.*','', lcontainer_name) if show: self.widgets[el_def].show() self.set_mode() def _set_ignored_fields(self, ignored_list): """ Ignored fields will not even be in self.field_list """ ## TODO: verify if I'm really using this. I don;t think I ever documented it... ignored_list = str2list(ignored_list) if not ignored_list: return None for field_name in ignored_list: if field_name in self.field_list: self.field_list.remove(field_name) return ignored_list def hide_fields(self, *args): """ """ self.dialog(type="ok", text=_("Hiding field is only supported for Tables")) def _get_noup(self, noup=None): """return noup set of field_name that are not updateable""" return self._noup or set() def _set_noup(self, noup): if isinstance(noup, (tuple, list, set, frozenset)): self._noup = set(noup) else: if not hasattr(self, '_noup'): self._noup = set() for x in str2list(noup or ''): if x.startswith('-'): x = x.replace('-', '') if x in self._noup: self._noup.remove(x) elif x.startswith('-'): x = x.replace('+', '') self._noup.add(x) else: self._noup.add(x) try: self.set_mode() # forces tables to reset editable on columns except AttributeError: ## actiongroup is not yet ready, but mode will be forced later anyhow pass noup = property(_get_noup, _set_noup) def get_nick(self, nick): if not nick: if self.tables: nick = self.tables else: nick = self.mapper.tables[0].name return nick def get_fields(self): """Fields is a dictionary with info on the fields derived from the db via the mapper (inspecting it) """ info = minspect.InspectMapper(self.mapper, noup=self.noup) ## fix column_width that may have been passed if self.col_width: for field_name, value in self.col_width.items(): if field_name in info: info.fields[field_name]['length'] = value return info def _get_filter_panel(self): if self.relationship_leader: return self.relationship_leader.filter_panel else: return self._filter_panel filter_panel = property(_get_filter_panel) def _set_title(self, title): if self.is_toplevel(): self.widgets['Window'].set_title(title) self._title = title def _get_title(self): if self.is_toplevel(): return self.widgets['Window'].get_title() else: return getattr(self, '_title', None) title = property(_get_title, _set_title) def grab_focus(self, widget): """ make sure the focus is grabbed so that all validation-related triggers update correctly. If the widget cannot get focus, try tb=gtk-save """ sqlwidget = self.relationship_leader or self save_widget = sqlwidget.ui_manager.get_widget('/Main/File') save_widget.grab_focus() def sb(self, text, seconds=10, delay=False): """ Write on the status bar if present in this sqlwidget or in the ``relationship_leader`` Adds a message in the stack of messages of the status bar, and removes it after seconds seconds. If delay=True it uses gobject.idle_add to give more chance to be visible (not hidden by other automatic messages) :param text: the text to be written :param seconds: how may seconds the text should stay visible :param delay: boolean. If True message is added in an idle cicle. It means it will be shown after other possibly scheduled automatic messages that would hide its visibility. """ try: if self.relationship_leader: self.relationship_leader.sb(text, seconds=seconds) else: try: if delay: gobject.idle_add(self.lay_obj.sb, text, seconds) else: self.lay_obj.sb(text, seconds=seconds) except Exception, e: #print e, vars(e) pass except AttributeError, e: ## The widget has been deleted and no self.relationship_leader already emptyed pass def _create_title(self, title=None): if not title: try: title=self.table except: title= self.mapper_info.get_primary_table() return gobject.markup_escape_text(title) def get_table_labels(self): """ get labels and tips from the db is they exists """ from sqlkit.db.utils import get_labels_and_tips_from_sqlkit if self.tables: for table in [t.name for t in self.mapper.tables]: self.label_map.update( get_labels_and_tips_from_sqlkit(table, metadata=self.metadata)) def resize(self, width, height): """ Resize the window. It accepts also -1 as value in which case sets the current value, so it can be used to change just one dimention. """ Window = self.widgets['Window'] w, h = Window.get_size() if width <= 0: width = w if height <= 0: height = h gobject.idle_add(Window.resize, width, height) def gquit(self, *args): gtk.main_quit() ###### Actions/UiManager def prepare_actions(self): """ Prepare action needed by UIManager """ self.actiongroup_general = gtk.ActionGroup('General') self.actiongroup_update = gtk.ActionGroup('Update') self.actiongroup_insert = gtk.ActionGroup('Insert') self.actiongroup_delete = gtk.ActionGroup('Delete') self.actiongroup_select = gtk.ActionGroup('Select') self.actiongroup_browse = gtk.ActionGroup('Browse') self.actiongroup_print = gtk.ActionGroup('Print') self.actiongroup_debug = gtk.ActionGroup('Debug') tip = _('Show all the differences that should be saved to database') self.actiongroup_general.add_actions([ ('File', None, 'File' ), ('Modify', None, _('Modify') ), ('Go', None, _('Go') ), ('Tools', None, _('Tools') ), ('Help', None, _('Help') ), ('Quit', gtk.STOCK_QUIT, None, 'q', None, self.delete_event_cb), ('About', gtk.STOCK_ABOUT, None, None, None, self.about_dialog_cb), ('PendingDifferences', None, _('Pending Differences'), None, tip, self.pending_differences_cb), ]) self.actiongroup_update.add_actions([ ('Save', gtk.STOCK_SAVE, None, 's', _('Save current record'), self.record_save_cb), ]) self.actiongroup_browse.add_actions([ ('Filters', gtk.STOCK_FIND, _('Filter panel'), None, _('Add filter panel'), self.add_filter_cb), ('Reload', gtk.STOCK_REFRESH, _("Reload"), 'r', _('Reload from the database'), self.reload_cb), ]) self.actiongroup_select.add_actions([ ('Forward', gtk.STOCK_GO_FORWARD, None, 'f', _('Go to next record'), self.record_forward_cb), ('Back', gtk.STOCK_GO_BACK, None, 'b', _('Go to previous record'), self.record_back_cb), ]) self.actiongroup_print.add_actions([ ('Print', gtk.STOCK_PRINT, None ), ]) self.actiongroup_debug.add_actions([ ('Gtk-tree', gtk.STOCK_EXECUTE, _('Inspect widgets'), 'i', None, self.show_widget_info), ]) def prepare_uimanager(self): """ Prepare UIManager, actions, and accelerators """ import uidescription self.ui_manager = gtk.UIManager() if self.is_toplevel(): self.accel_group = self.ui_manager.get_accel_group() self.widgets['Window'].add_accel_group(self.accel_group) self.ui_manager.add_ui_from_string(uidescription.GENERAL_UI) self.ui_browse_id = self.ui_manager.add_ui_from_string(uidescription.BROWSE_UI) self.ui_debug_id = self.ui_manager.add_ui_from_string(uidescription.DEBUG_UI) self.ui_print_id = self.ui_manager.add_ui_from_string(uidescription.PRINT_UI) self.ui_manager.insert_action_group(self.actiongroup_general, 10) self.ui_manager.insert_action_group(self.actiongroup_insert, 11) self.ui_manager.insert_action_group(self.actiongroup_delete, 12) self.ui_manager.insert_action_group(self.actiongroup_update, 13) self.ui_manager.insert_action_group(self.actiongroup_browse, 14) self.ui_manager.insert_action_group(self.actiongroup_select, 15) self.ui_manager.insert_action_group(self.actiongroup_print, 16) self.ui_manager.insert_action_group(self.actiongroup_debug, 17) self.ui_manager.connect('connect-proxy', self.uimanager_connect_proxy) ## pack menu and toolbar if it's a toplevel if 'V.header' in self.widgets: header = self.widgets['V.header'] menu = self.ui_manager.get_widget('/Main') toolbar = self.ui_manager.get_widget('/TbMain') header.add(menu) header.add(toolbar) def uimanager_connect_proxy(self, uimgr, action, widget): tooltip = action.get_property('tooltip') if isinstance(widget, gtk.MenuItem) and tooltip: widget.set_tooltip_text(tooltip) def set_mode(self, mode=None, reset=False, delay=False): """ .. _set_mode: :param mode: the mode as explained below. If None, mode will be refreshed to last *declared* state (i.e.: reguardless of what you may have changed by hand acting on actiongroups). :param reset: if True the mode will be completely reset. Needed to make the mode of a related table independent from the mode of the master :param delay: if True, the mode is set but interface is not immediately updated Set mode for this widget. Mode can be a string composed with the following letters that correspond to permissions possibly preceded by ``+`` or ``-``. :s: SELECT. The user can view the records *already selected* (i.e. use Forward/Backward) or set by ``set_records``. This is *always* granted and as such it's pointless to set it (or revoke it) :i: INSERT. The user can insert new records :u: UPDATE. The user can update records :d: DELETE. The user can delete records :b: browse. The user can use the filter panel If mode start with + or - the following permissions are granted/revoked for the widget by adding or removing from the modes already present. If no sign is used the mode is set. ``mode`` is a property, you can set it directly: ``self.mode = 'b'`` The mode influences permission by setting menu entries active or not. It's not acting on the session. If an object has been inserted in the session a simple update operation can let it be inserted. This is by design. Related table inheritate the same mode but you can programmatically reset it and make it independent from the master using option reset=True. *Implementation* Mode are implemented acting on uimanager/actionsgroup. You may read :ref:`uimanager` .. note:: at present it's not possible to insert a record in a table that is not updatable """ known_modes = 'iudbD' if not hasattr(self, '_mode'): self._mode = set([x for x in known_modes]) if reset: if not mode or (isinstance(mode, basestring) and re.match('[-+]', mode)): model_mode = self._mode else: model_mode = mode self._mode = set(x for x in model_mode) if mode: if isinstance(mode, set): if not reset: self._mode = mode elif mode.startswith('+'): ## add a permission for x in mode.replace('+', ''): self._mode.add(x) elif mode.startswith('-'): ## deletes a permission for x in mode.replace('-', ''): if x in self._mode: self._mode.remove(x) else: ## entirely resets the permission for x in known_modes: if x in mode: self._mode.add(x) else: if x in self._mode: self._mode.remove(x) if not delay: self.actiongroup_insert.set_sensitive('i' in self._mode) self.actiongroup_update.set_sensitive('u' in self._mode) self.actiongroup_delete.set_sensitive('d' in self._mode) self.actiongroup_browse.set_sensitive('b' in self._mode) self.actiongroup_debug.set_sensitive('D' in self._mode) def get_mode(self): return self._mode mode = property(get_mode, set_mode) ###### Saving def record_delete(self, widget=None): """invokes the validator before DELETing a record""" pass def record_save_cb(self, widget, event=None): """Save the record and invokes the validator before UPDATE/INSERT a record """ self.get_toplevel().set_focus(None) try: self.record_save() except (exc.DialogValidationError, exc.CancelledAction), e: pass def record_new_cb(self, widget, event=None): """Save the record and invokes the validator before UPDATE/INSERT a record """ try: self.record_new() except (exc.DialogValidationError, exc.CancelledAction): return True def record_save_new_cb(self, action): """ Save the record as a new record, ie letting pk not be set """ self.record_save_new() def record_add(self, record): """ Add a record to the SqlWidget """ self.records += [record] self.session.add(record) def commit(self, message=_('Saved')): """ run session.commit() and take care of possible exceptions :param message: a message to be written in the status bar (default: Saved) """ if self.ro: return try: ## when session.autocommit = False, a transaction is automatically started if self.session.autocommit: self.session.begin() self.session.commit() self.emit('record-saved', ) self.run_hook('on_record_saved') self.sb(message, seconds=3) except (exceptions.DBAPIError, exceptions.FlushError), e: if not self.commit_error_handler(e): self.session.rollback() raise exc.HandledRollback(str(e)) def commit_error_handler(self, e): """ communicate the problem to the user return True to prevent rollback """ if isinstance(e, exceptions.IntegrityError): # TIP: message in the status bar when a commit error is handled msg = _("Error while writing to the database. \nOriginal error was: \n%s") % e self.message(type='error', text=_(msg)) self.sb(str(e)) elif isinstance(e, exceptions.ProgrammingError): # TIP: Error while saving into a table w/o permission msg = _("A programming error was received from the backend:\n%s") % e if re.search('permission denied', msg, re.I): msg = "Access was denied while accessing one of the tables (%s)" % e self.message(type='error', text=msg) self.sb(e.orig) elif isinstance(e, exceptions.DBAPIError): # TIP: message in the status bar when a commit error is handled self.message(type='error', text=_("%s - Dirty objects: %s - New objects: %s") % \ (str(e.orig),len(self.session.dirty),len(self.session.new))) self.sb(e.orig) elif isinstance(e, exceptions.FlushError): # TIP: message in the status bar when a commit error is handled msg = _("Error while writing to the database: \n" \ "are you 'saving as new' an already known record?\n" + \ "original error was: \n%s") % e self.message(type='error', text=_(msg)) self.sb(e) return False # i.e.: rollback def get_value(self, field_name, shown=False): """ return the value from the widget :param field_name: the field_name :param shown: boolean: in case field is a foreign key, ``True`` indicates we want the dislayed value rather than the real one """ ## to be overwritten by Mask & Table return NotImplementedError def set_value(self, field_name, field_value, fkvalue=None, initial=False, shown=False): """ set the value of any field present in ``gui_fields``. Uses field.set_value if ``initial`` is False, run ``on_change_value`` :param field_name: the field_name to be changed :param field_value: the new value :param fkvalue: a possible foreign key value. It's here just for compatibility with SqlTable's one :param initial: a boolean indicating if it's an initial value (passed to field) :param shown: a boolean indicating if the value is the displayed value (passed to field) """ #to be overwritten by Mask & Table return NotImplementedError def has_changed(self, field_name): """ return the value from the widget """ if field_name not in self.gui_fields: dbg.write("%s Non in gui_fields %s" % (field_name, self.gui_fields.keys())) raise sqlkit.exc.FieldNotInLayout("field_name: %s" % (field_name)) field = self.gui_fields[field_name] return field.has_changed() def record_has_changed(self): """ check if any data has been written in the fields after record display. Uses field.has_changed() """ ## this is needed since it's hard to say if an instance has been modified ## eg: a new item is added to the session, no editing is done. A reload is issued ## I want to avoid the dialog: do you want to save? ## session.dirty would (correctly) say a commit is needed if not self.current: return False obj = self.current for sqlwidget in self.related: if sqlwidget.record_has_changed(): return True for field in self.gui_fields: if not field.editable: continue try: if field.has_changed(): return True except exc.ValidationError, e: return True return False def save_unsaved(self, refresh=True, skip_new=False, skip_check=False, proceed=True): """ Check if it needs saving. In case ask for confirmation and proceed. This triggers both session changes *AND* changes to the current mask that may not be in the session. :param skip_check: we already know it needs saving. Passed to unsaved_changes_exists :param skip_new: skip_new may be an object whose changes we disregard (see unsaved_changes_exist) Return False if cancel is pressed """ response = True if self.unsaved_changes_exist(skip_new=skip_new) or skip_check: dialog = UnsavedDataDialog(self) response = dialog.run() if response == gtk.RESPONSE_CANCEL: pass if response == gtk.RESPONSE_YES: if proceed: self.record_save(ask=False) ## you don't want to save. Let's prevent a delayed commit ## we don't need to care about deleted becouse when you delete ## the commit is immediate if response == gtk.RESPONSE_NO: self.discard_changes() return response def discard_changes(self): """ Discard any changes in the session, refreshing all objects """ for obj in self.session.dirty: if obj in self.session: self.session.refresh(obj) for obj in self.session.new: # when you have several objects in the session.new this test *IS* needed since # the first one expunge may cause another one to be expunged too. if obj in self.session.new: self.session.expunge(obj) self.last_new_obj = None ## FIXME: unsure... def unsaved_changes_exist(self, skip_new=False, skip_check=False): """ check if session needs committing by looking dirty, deleted and new skip_new: skip_new may be an object that we are not interested in saving new object that where not changed but have default values fall here """ # skip_check is passed to Mask.unsaved_changes_exists if self.session.dirty: for dirty in self.session.dirty: if self.session.is_modified(dirty, passive=True): return True if self.session.deleted: return True if self.session.new: for obj in self.session.new: if obj is skip_new: self.sb(_("Discarding new obj"), seconds=3) continue if self.session.is_modified(obj, passive=True): return True return False ###### Validation def digits_check_input_cb(self, widget, event): """ prevents the possibility of inputting wrong chars """ key = gtk.gdk.keyval_name (event.keyval) if key in ('KP_Decimal',) and numbers.get_decimal_symbol() == ",": event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = gtk.keysyms.comma event.window = widget.window widget.emit('key-press-event', event) return True if key in ('KP_Enter',): event = gtk.gdk.Event(gtk.gdk.KEY_PRESS) event.keyval = gtk.keysyms.Return event.window = widget.window widget.emit('key-press-event', event) return True if (event.state | gtk.gdk.CONTROL_MASK): return KEYPAD_KEYS = '(KP_[0-9]|KP_Add|KP_Decimal|KP_Subtract|comma' ONLYDIGITS="%s|[0-9.,]|BackSpace|Left|Right|F1|period|Tab|Up|Down|Return|Esc)" if not re.match (ONLYDIGITS % KEYPAD_KEYS, key): self.sb(_('Char %s is not accepted in numeric context') % key) return True def record_validate(self, obj=None, single_fields=True, related=None): """ any validation should live here single_fields=bool validation of single fields is done here for table but in mask2obj for Masks single_fields determines if check for single fields is done returns the validation_error dict to propagate the error info upward """ # if not hasattr(self, 'validation_errors') or related: # masks pass throught mask2obj that creates self.validation_errors. I must not discard it # in the other cases I neeed to start from zero if single_fields or related: self.validation_errors = {} self.validation_warnings = {} if obj is None: obj = self.get_current_obj() if obj is None: return {} for sqlwidget in self.related: self.validation_errors.update( sqlwidget.record_validate(related=sqlwidget.relationship_path)) self.validation_warnings.update(sqlwidget.validation_warnings) if single_fields: self.record_validate_fields(obj) try: self.run_hook('on_validation') except exc.ValidationError, e: self.add_validation_error(e) if related: return self.validation_errors if self.validation_errors: # just one error dialog dialog = ValidationErrorDialog(self) dialog.run() #self.validation_error_dialog() if self.validation_warnings: # just one error dialog dialog = ValidationWarningDialog(self) dialog.run() def add_validation_error(self, error, field_name=None): """ keep track of the error in self.validation_errors/validation_warnings so that a further process can collect them and present them to the user :param error: the ValidationError or an error message string :field_name: the field_name to which the error refers. Defaults to 'record validation' """ if not field_name: ## TIP: the general validation for the record field_name = _('record validation') # a key to be used in self.validation_errors if isinstance(error, basestring): error_message = error else: error_message = str(error) if isinstance(error, exc.ValidationWarning): error_dict = self.validation_warnings else: error_dict = self.validation_errors if not field_name in error_dict: error_dict[field_name] = [] error_dict[field_name] += [error_message] self.sb(error_message, seconds=30) def add_not_null_error(self, field_name): """ simple way to add a not nullable field error :param field_name: the field_name that cannot be nullable """ error = exc.NotNullableFieldError(field_name, master=self) self.add_validation_error(error, field_name) def record_validate_fields(self, obj): """ Validate any fields in self.gui_fields """ for field in self.gui_fields: if not field.editable: continue field_name = field.field_name try: if field.mapper: # persisted fields, mask2obj has already cleaned them value = getattr(obj, field_name) self.gui_fields[field_name].validate( value , clean=True) else: # fields only present in the GUI, value is not in obj value = self.gui_fields[field_name].get_value() self.gui_fields[field_name].validate( value , clean=True) except exc.ValidationError, e: self.add_validation_error(e, field_name=field_name) ###### Hooks def run_hook(self, mode, *args, **kw): """ look for a possible hook and call it. :param mode: the hook type (eg.: set_value, completion...) :param climb: boolean: should I climb relationship_path to get other sqlwidgts' hooks? """ # climb: it's a flag that determines if I must climb the relationship and find # others possible hooks in relationship_leaders climb = kw.pop('climb', True) if self.relationship_leader in (None, self) or climb == False: hook_name = "%s" % (mode) sqlwidget = self else: self.run_hook(mode, climb=False, *args, **kw) # hooks defined for the related table hook_name = "%s__%s" % (mode, '__'.join(self.relationship_path)) sqlwidget = self.relationship_leader if 'field_name' in kw: hook_name += "__%(field_name)s" % kw if hasattr(sqlwidget.hooks, hook_name): if 'field_name' in kw: return getattr(sqlwidget.hooks, hook_name)(sqlwidget, kw['field_name'], *args) else: return getattr(sqlwidget.hooks, hook_name)(sqlwidget, *args) ###### Callback def clear_cb(self, *args): self.clear() def reload_cb(self, widget): """ issue a reload operation on master. Call back of reload button """ self.grab_focus(widget) try: self.reload( ) except exc.DialogValidationError, e: return True def record_forward_cb(self): """ it doesn't make any sense here, but we need it to prevent _add_actions_to_button from failing """ pass def record_back_cb(self): """ it doesn't make any sense here, but we need it to prevent _add_actions_to_button from failing """ pass def destroy(self): """ Destroy the widget """ if 'Window' in self.widgets: ## This sqlwidget owns a toplevel Window. Destroying that one triggers ## delete_event_cb, that emits 'delete'event' event = gtk.gdk.Event(gtk.gdk.NOTHING) self.widgets['Window'].emit('delete-event', event) else: self.emit('delete-event') def delete_event_cb(self, *args): """ Quit operations: check if need to save first """ try: self.record_save() except (exc.DialogValidationError, exc.CancelledAction), e: return True self._clean_session_extensions() for obj, hid in self._ids.values(): obj.disconnect(hid) del self._ids self.emit('delete-event') ## get rid of records automatically created by record_new if they're ## not yet persisted if self.current and self.current in self.session.new: self.session.expunge(self.current) if 'Window' in self.widgets: self.widgets['Window'].destroy() if self.single: self.gquit() attrs = [ 'about_dialog', 'accel_group', 'actiongroup_browse', 'actiongroup_debug', 'actiongroup_delete', 'actiongroup_general', 'actiongroup_insert', 'actiongroup_print', 'actiongroup_select', 'actiongroup_update', 'addto', 'col_width', 'completions', 'constraint', 'current', 'current_idx', 'dbproxy', 'defaults', 'dev', 'field_list', 'field_widgets', 'filter_panel', 'geom', 'gui_fields', 'hidden_fields', 'hooks', 'icons', 'ignored_fields', 'label_map', 'labels', 'laygen', 'lay_obj', 'layout', 'limit', 'm2m_editable' 'mapper', 'mapper_info', 'metadata', 'modelproxy', 'last_new_obj', '_mode', 'naked', 'nick', '_noup', '_order_by', '_order_by_join_args', 'printing', 'query', 'records', 'related', 'relationship_leader', 'relationship_mode', 'relationship_path', 'reloading', 'ro', 'rows', 'session', 'single', 'sql', # 'tables', '_title', '_filter_panel', 'ui_browse_id', 'ui_debug_id', 'ui_manager', 'ui_print_id', 'widgets', 'views', 'xml', ] for at in attrs: try: delattr(self, at) except (NameError, AttributeError), e: pass # ## .sb() may have added a collback with # self.relationship_leader = None gc.collect() def pending_differences_cb(self, menu=None): dialog = UnsavedDataDialog(self, type_='ok', msg=_('Unsaved differences'), expanded=True) response = dialog.run() ###### Record browsing def clear(self, widget=None): """clear the mask for a new record""" pass def reload(self, limit=None, display=True, order_by=None, OR=False, **kwargs): """ reload the data from the database taking all filter/constraints into consideration :param limit: add a LIMIT clause (integer) to limit number of returned records. Permanent effect :param order_by: reset order_by and apply when reloading. Permanent effect. :param OR: (boolean, default False) as in ``add_constraint`` you can specify if conditions as in kwargs below should be ORed or ANDed (default) :param kwargs: any filters accepted by the :ref:`django-syntax` and by add_filter sql may be a tuple (sql_statement, bind_params) or just a sql_statement """ from sqlkit.db.django_syntax import django2query if limit: self.limit = limit query = self.query.filter(None) ## order_by if order_by: self.order_by = order_by if self.order_by: if self._order_by_join_args: query = query.reset_joinpoint().join(self._order_by_join_args) query = query.order_by(*self.order_by) ## filters try: query = self.filter_panel.add_filter_conditions(query) except exc.ParseFilterError, e: self.records = [] return if kwargs: query = django2query(query, self.mapper, OR=OR, **kwargs) if self.limit: query = query.limit(self.limit) self.session.expunge_all() self.last_new_obj = None if self.is_mask(): self.current = None self.clear(check=False) try: self.records = query.all() except exceptions.ProgrammingError, e: # TIP: reloading data from the database msg = _("A programming error was received from the backend:\n%s") % e if re.search('permission denied', msg, re.I): msg = "Access was denied while accessing one of the tables (%s)" % e self.message(type='error', text=msg) self.sb(e.orig) return except exceptions.InterfaceError, e: ## the connection may be closed and a retry could restore it if re.search('closed', str(e), re.I): self.records = query.all() if display: self.record_display(check=False) length = len(self.records) ## after a reload the transaction will stay opend if session.autocommit=False ## in postgres this will be evident by "idle in transaction" state of he process ## that will inhibit a number of operation (eg.: altering tables) ## FIXME: whe we have m2m relation this is not enought, I open some table ## and I don't understand where... ## nice info here: http://wiki.dspace.org/index.php/Idle_In_Transaction_Problem self.session.rollback() self.set_mode() return length def set_records(self, records=None, pk=None): """ set 'records' as the list of record to manage :param records: use 'records' as the list of records :param pk: use primary key pk as te pk to show (TODO make it work with composite pks) """ if not records and pk: records = self.get_by_pk(pk) self.records = records self.last_new_obj = None self.record_refresh() def get_by_pk(self, pk): """ Return an object of self.class_ filtered by pk """ query = self.query.autoflush(False) record = query.filter_by(**{str(self.get_pk()[0]) : pk}).all() return record def record_refresh(self): """to be overloaded: """ self.emit('records-displayed') pass # def reload_one(self,field, value): # """compact way to view a single record or records with a single property""" # sql = "%s = :%s" % (field, field) # bind_parameters = {field : value} # self.reload( (sql, bind_parameters) ) def get_new_object(self, session=True, obj=None): """create a new object from the class of the mapper and place it in the session :param session: (boolean) if True, add to session :param obj: the new obj (default None: it will be created an empty self.mapper.class_ instance) """ new_obj = obj or self.mapper.class_() if session: self.session.add(new_obj) return new_obj def _set_order_by(self, order_by): """ May be an order_by clause element or a string, with comma separated fields + ASC|DESC """ from sqlalchemy.sql.expression import desc from sqlkit.db.django_syntax import django2components self._order_by_join_args = None if isinstance(order_by, basestring): order = [] for tocken in order_by.split(','): order_list = tocken.split() field_name = order_list[0] #col0 = getattr(self.mapper.class_, field_name) ## try to add a join for an order by on a field in a related table joins, op, op_str, value, col, join_args = django2components(self.mapper, { field_name: None}) #assert col0 == col, "django2components does not lead to the same result as getattr" if join_args: ## it will be evaluated at reload-time self._order_by_join_args = join_args if len(order_list) > 1: direction = order_list[1] if direction.upper() == 'DESC': col = desc(col) order += [col] self._order_by = order else: # assume its already a column if isinstance(order_by, list): self._order_by = order_by else: self._order_by = [order_by] def _get_order_by(self): return self._order_by order_by = property(_get_order_by, _set_order_by) ####### Dialog def dialog(self, type='ok-cancel', text='', title='', layout=None, icon='gtk-dialog-question'): """ pop a dialog modal and transient :param type: ok, ok-cancel, yes-no-cancel, ok-apply-cancelx :param text: the text that should be used (uses markup) :param title: the title of the dialog :param layout: a possible layout.Layout specification :param icon: the icon (defaults to 'gtk-dialog-question') """ if not layout: layout = 'i=%s:6 l=message' % icon dialog, l = self.lay_obj.dialog(layout=layout, title=title, type=type, show=False) if 'l=message' in l.widgets: l.prop(('l=message', 'use_markup', True)) l.widgets['l=message'].set_text(text) # l.widgets['l=a'].set_markup( # '%s' % text) dialog.show_all() response = dialog.run() dialog.destroy() return response def message(self, text='', type='question'): self.response = None dialog = self.lay_obj.message(text=text, type=type) #dialog.connect('response', self.response_cb ) self.response = dialog.run() dialog.destroy() return dialog def about_dialog_cb(self, menuItem): """ The about dialog """ self.about_dialog.run() self.about_dialog.destroy() def create_about_dialog(self): """ Create a default AboutDialog """ def func(dialog, link, user_data): import webbrowser webbrowser.open(link) about_dialog = gtk.AboutDialog() gtk.about_dialog_set_url_hook(func, None) about_dialog.set_authors(["Sandro Dentella "]) about_dialog.set_license("GPL v.3") about_dialog.set_version(sqlkit.__version__) about_dialog.set_name("sqlkit") about_dialog.set_website("http://sqlkit.argolinux.org") about_dialog.set_website_label("http://sqlkit.argolinux.org") return about_dialog #dialog.show() ###### Misc def export(self, menuItem): self.dialog(type='ok', text=_("Sorry, export is implemented only for table view")) def get_field_type(self, field_name): try: return self.gui_fields[field_name].type except AttributeError: return self.mapper_info.fields[field_name]['type'] def get_label(self, field_name, gtk_label=False): """ return field_name translated according to map_label + gettext :param gtk_label: double any occurrence of '_' as gtk labels 'eat' them """ label = get_label(field_name, layout=self.lay_obj) if gtk_label: return _(label).replace('_', '__') return _(label) def set_current(self, pk=None): """retrieve a record and set it as current """ raise NotImplemented def set_format(self, format_dict): """ set_format(format_dict). format_dict is a dictionary whose key is the field_name and the value is the format of the column. Format_dict can have keys that do not correspond to any field in the widget. That makes it possible to reuse a format dict. The format dict is passed to related widgets as well. See :ref:`localization` for more info :param format_dict: a dict of field_name/format strings """ self._format_dict = format_dict if not format_dict: return for field_name, format in format_dict.items(): if field_name in self.gui_fields: if isinstance(format, tuple): format, locale = format self.gui_fields[field_name].locale = locale self.gui_fields[field_name].format = format for widget in self.related: widget.set_format(format_dict) def add_temporary_item(self, item, menu, position=0, separator=False): """ .. _add_temporary_item: Adds an action to a menu and removes it with 'selection-done' :param item: the gtk.MenuItem that must be added :param menu: the menu where the item needs to be added :param position: the position where to insert the item (default: 0) :param separator: boolean: add a separator (not implemented) """ if not menu: return def remove_widget_cb(menu, item): menu.remove(item) item.show() menu.insert(item, position) menu.connect('selection-done', remove_widget_cb, item) if separator: self.add_temporary_item(gtk.SeparatorMenuItem(), menu, position=position+1) ###### Checks def is_mask(self, obj=False): """ Return True if the sqlwidget is a Sqlmask """ ## bad way to prevent chicken and eggs import problems from sqlkit.widgets import SqlMask if obj == False: ## obj may be an empty str: '' obj = self return isinstance(obj, SqlMask) def is_table(self, obj=False): """ Return True if the sqlwidget is a SqlTable """ ## bad way to prevent chicken and eggs import problems from sqlkit.widgets import SqlTable if obj == False: ## obj may be an empty str: '' obj = self return isinstance(obj, SqlTable) def is_fkey(self, field_name): """ return True if this field is a foreign key """ try: return self.mapper_info.is_fkey(field_name) except AttributeError: return None def is_primary(self, field_name): """ return True if this field is a Primary Key """ return self.mapper_info.is_primary(field_name) def is_loader(self, field_name): """ True if field_name is a loader for a related table """ return self.mapper_info.is_loader(field_name) def is_string(self, field_name): """ Return true if this field is a String type """ try: return self.mapper_info.is_string(field_name) except AttributeError, e: return isinstance(self.gui_fields[field_name], fields.VarcharField) def is_date(self, field_name): """ Return true if this field is date """ try: return self.mapper_info.is_date(field_name) except AttributeError: return self.gui_fields[field_name].type == datetime.date def is_datetime(self, field_name): """ Return true if this field is datetime """ try: return self.mapper_info.is_datetime(field_name) except AttributeError: return self.gui_fields[field_name].type == datetime.datetime def is_time(self, field_name): """ Return true if this field is time """ try: return self.mapper_info.is_time(field_name) except AttributeError: return self.gui_fields[field_name].type == datetime.time def is_interval(self, field_name): """ Return true if this field is time """ try: return self.mapper_info.is_interval(field_name) except AttributeError: return self.gui_fields[field_name].type == datetime.interval def is_integer(self, field_name): """ Return true if this field is integer """ try: return self.mapper_info.is_integer(field_name) except AttributeError: return self.gui_fields[field_name].type in (int, long) def is_float(self, field_name): """ Return true if this field is float """ try: return self.mapper_info.is_float(field_name) except AttributeError: return self.gui_fields[field_name].type == float def is_numeric(self, field_name): """ Return true if this field is numeric (rendered as Decimal) """ try: return self.mapper_info.is_numeric(field_name) except AttributeError: return self.gui_fields[field_name].type is decimal.Decimal def is_number(self, field_name): """ Return true if this field is a number (int, float or Decimal) """ try: return self.mapper_info.is_number(field_name) except AttributeError: return self.gui_fields[field_name].type in NUMBERS def is_boolean(self, field_name): """ Return true if this field is boolean """ try: return self.mapper_info.is_boolean(field_name) except AttributeError: return self.gui_fields[field_name].type == bool def is_enum(self, field_name): """ Return true if this field is to rendered as enum """ try: return self.mapper_info.is_enum(field_name) except AttributeError: return isinstance(self.gui_fields[field_name], fields.EnumField) def is_image(self, field_name): """ Return true if this field is image """ try: return self.mapper_info.is_image(field_name) except AttributeError: False def is_file(self, field_name): """ Return true if this field is image """ try: return self.mapper_info.is_file(field_name) except AttributeError: False def is_nullable(self, field_name): """ Return true if this field is nullable """ return self.gui_fields[field_name].nullable def is_editable(self, field_name): """ Return true if this field is date """ #edit = self.mapper_info.fields[field_name]['editable'] edit = self.gui_fields[field_name].editable if not edit: # you probably explicitely set it (via noup?) return False ## now if the field is editable, it depends on mode of the widget ## and state of self.current if self.current in self.session.new: return 'i' in self._mode elif 'u' in self._mode: return 'u' in self._mode return False def is_text(self, field_name): """ Return true if this field is Text or was forced gtk.Text """ import sqlalchemy.types as sqltypes try: if self.laygen.fields_in_layout[field_name].startswith('TX'): return True except: pass try: return self.mapper_info.is_text(field_name) except AttributeError: return isinstance(self.gui_fields[field_name], fields.TextField) def is_toplevel(self): """ return a boolean indicating if the widget is a toplevel widget """ return 'Window' in self.widgets def get_toplevel(self): """ return the toplevel for this SqlWidget """ try: return self.widgets['Window'] except KeyError, e: try: return self.relationship_leader.get_toplevel() except AttributeError, e: return None def fkey_is_valid(self): """ return True if current editable -if exists- has a value that does not need validation """ ## Overridden by SqlMask/SqlTable raise NotImplementedError def get_current_obj(self): """ Return the corrently edited obj. Note that in Table widgets, selection can already be elsewhere (as in on_selection_change). """ ## Overridden by SqlMask/SqlTable raise NotImplementedError ###### debug def show_mapper_info(self, *args): from sqlkit.debug import gtk_dbg gtk_dbg.ShowMapperInfo(self.mapper_info) def show_widget_info(self, *args): from sqlkit.debug import gtk_dbg gtk_dbg.show_widgets(self.widgets['Window']) def show_class_info(self, *args): from sqlkit.debug import gtk_dbg gtk_dbg.ShowClassInfo(self) def mostra(self, what): """ debug: show all attribute of an object """ if isinstance(what, int): obj = self.records[what] else: obj = what for attr in self.field_list: print attr, getattr(obj, attr) def show_field_info(self, wdg, field_name): """ Pop a dialog to give info on the selected field """ dialog, lay = self.lay_obj.dialog(layout="TVS=info", type='ok', title="Info on %s" % field_name) tv = lay.widgets['TV=info'] model = gtk.ListStore(str, str) tv.set_model(model) tv.set_property('width-request', 210) tv.set_property('height-request', 320) cell1 = gtk.CellRendererText() cell2 = gtk.CellRendererText() tc1 = gtk.TreeViewColumn(_("Property"), cell1, text=0) tc2 = gtk.TreeViewColumn(_("Value"), cell2, text=1) tv.append_column(tc1) tv.append_column(tc2) tv.show_all() for key in ('table_name', 'name', 'db_type', 'col_spec', 'fkey', 'default', 'pkey', 'nullable', 'editable', 'length'): miter = model.append() try: if key == "db_type": model.set(miter, 0, key, 1, self.mapper_info.fields[field_name][key].__name__) elif key == "fkey": fkeys = self.mapper_info.fields[field_name]['fkey'] model.set(miter, 0, key, 1, ",".join(x.target_fullname for x in fkeys)) else: model.set(miter, 0, key, 1, self.mapper_info.fields[field_name][key]) except AttributeError: model.set(miter, 0, key, 1, getattr(self.gui_fields[field_name], key, '')) try: cell = self.tvcolumns[field_name].get_cell_renderers()[0] model.set(model.append(), 0, 'width', 1, cell.get_data('width')) except: pass try: model.set(model.append(), 0, 'format', 1, self.gui_fields[field_name].format) except: pass dialog.show_all() response = dialog.run() dialog.destroy() def session_show(self, k=None): """just for debugging """ l = [] j = 0 for obj in self.session: if k is not None: print "%.2d %s %s" % (j, obj, hex(id(obj))) l += [(hex(id(obj)), obj)] j += 1 l += [('dirty', self.session.dirty)] l += [('new', self.session.new)] l += [('delete', self.session.deleted)] if k is None: return l else: return l[k] def record_show_changes(self): if self.record_has_changed(): for field in self.gui_fields: if field.has_changed(): print "OLD: %s NEW: %s" % (field.initial_value, field.get_value()) def __repr__(self): return '<%s: %s>' % (self.__class__.__name__, self.tables) def __str__(self): try: return '<%s: %s>' % (self.__class__.__name__, self.tables) except AttributeError, e: return '<%s>' % (self.__class__.__name__) class UnsavedDataDialog(object): """ A dialog that allows to see which data have been modifiedx """ def __init__(self, master, type_="yes-no-cancel", msg=None, expanded=False): self.master = master general_msg = msg or _("Save unsaved data?") layout = "{v { i=gtk-dialog-question:6 l=msg} {>%s.details TVS=details} }" self.dialog, l = master.lay_obj.dialog(title=_("Unsaved data"), type=type_, text=general_msg, layout=layout % (expanded and '>' or '')) tv = l.widgets['TV=details'] expander = l.widgets['X.details'] ## expanding the details will possibly trigget mask2obj that ## in turn will trogger validation. That's why I want to delay as much as possible self.model = gtk.TreeStore(str, str, str, str) if expanded: self.fill_model() else: expander.connect("notify::expanded", self.fill_model) tv.set_model(self.model) tv.set_property('width-request', 600) tv.set_property('height-request', 150) cell0 = gtk.CellRendererText() cell1 = gtk.CellRendererText() cell2 = gtk.CellRendererText() cell3 = gtk.CellRendererText() tc0 = gtk.TreeViewColumn(_("State"), cell0, markup=0) tc1 = gtk.TreeViewColumn(_("Field name"), cell1, markup=1) tc2 = gtk.TreeViewColumn(_("Original value"), cell2, markup=2) tc3 = gtk.TreeViewColumn(_("Present value"), cell2, markup=3) tv.append_column(tc0) tv.append_column(tc1) tv.append_column(tc2) tv.append_column(tc3) tv.show_all() self.dialog.show_all() def run(self): response = self.dialog.run() self.dialog.destroy() return response def fill_model(self, *args): """ Dialog to allow analysis of changed data """ from sqlkit.db import utils if self.master.is_mask(): try: self.master.record_mask2obj(self.master.get_current_obj()) except Exception, e: exc.ValidationError(e) if self.master.session.dirty or ( self.master.record_has_changed() and self.master.current not in self.master.session.new): dirty_iter = self.model.append(None) self.model.set(dirty_iter, 0, '%s' % _('Modified')) for item in self.master.session.dirty: for field_name, old, new in utils.get_differences(item): if item.__class__ is self.master.mapper.class_ and ( field_name in self.master.gui_fields): field = self.master.gui_fields[field_name] old_value = old and utils.clean_for_markup(field.get_human_value(old)) new_value = new and utils.clean_for_markup(field.get_human_value(new)) else: ## FIXME: we could also look for better representation in related widgets old_value = utils.clean_for_markup(old) new_value = utils.clean_for_markup(new) iter = self.model.append(None) self.model.set(iter, 0, "%s" % (utils.clean_for_markup(repr(item)[:25]) or utils.clean_for_markup(item)[:25]), 1, self.clearer("%s" % self.master.get_label(field_name)), 2, self.clearer(old_value), 3, self.clearer(new_value), ) if self.master.session.deleted: deleted_iter = model.append(None) self.model.set(dirty_iter, 0, '%s' % _('Deleted')) for item in self.master.session.deleted: iter = self.model.append(None) self.model.set(iter, 0, "%s" % utils.clean_for_markup(item.__class__.__name__), 2, "%s" % utils.clean_for_markup(item)) if self.master.session.new: new_iter = self.model.append(None) self.model.set(new_iter, 0, '%s' % _('New')) for item in self.master.session.new: iter = self.model.append(None) self.model.set(iter, 0, "%s" % utils.clean_for_markup(item.__class__.__name__), 3, "%s" % utils.clean_for_markup(item)) def clearer(self, value): """ return a string representation of '' vs. None so that user understands what is changing when just a """ from sqlkit.db import utils if value == '': return utils.clean_for_markup('<%s>' % _("empty string: ''")) if value is None: return utils.clean_for_markup('<%s>' % _('NULL value')) return value class ValidationErrorDialog(object): GENERAL_MSG = _('Errors are present in the record. \nCorrect them now, to continue \nor delete the record') TITLE = _("Validation errors") TYPE = 'ok' ERROR_DICT = 'validation_errors' IMAGE = 'gtk-dialog-error' def __init__(self, master): """ represent the errors in self.validation_errors and raise ValidationError to correctly propagate the error upwards """ self.master = master layout = """i=%s:6 l=general:< TVS=details -""" % self.IMAGE self.dialog, lay = master.lay_obj.dialog(layout=layout, type=self.TYPE, title=self.TITLE) lay.widgets['l=general'].set_text(self.GENERAL_MSG) tv = lay.widgets['TV=details'] model = gtk.ListStore(str, str) tv.set_model(model) tv.set_property('width-request', 420) tv.set_property('height-request', 150) cell1 = gtk.CellRendererText() cell2 = gtk.CellRendererText() tc1 = gtk.TreeViewColumn(_("Field name"), cell1, markup=0) tc2 = gtk.TreeViewColumn(_("Error"), cell2, markup=1) tv.append_column(tc1) tv.append_column(tc2) tv.show_all() self.error_dict = getattr(self.master, self.ERROR_DICT) self.fill_model(model) self.dialog.show_all() def run(self): response = self.dialog.run() self.dialog.destroy() self.error_dict = {} raise exc.DialogValidationError def fill_model(self, model): """ Fill the model of the treeview that presents the validation errors """ for key in self.error_dict.keys(): for i, err in enumerate(self.error_dict[key]): iter = model.append(None) if i == 0: model.set(iter, 0, "%s" % self.master.get_label(key)) model.set(iter, 1, "%s" % err) class ValidationWarningDialog(ValidationErrorDialog): GENERAL_MSG = _('You can continue or go back editing. \nRead the following warnings to decide') TITLE = _("Validation Warnings") TYPE = 'ok-cancel' ERROR_DICT = 'validation_warnings' IMAGE = 'gtk-dialog-warning' def run(self): response = self.dialog.run() self.dialog.destroy() self.error_dict = {} if response == gtk.RESPONSE_CANCEL: raise exc.DialogValidationError else: pass sqlkit-0.9.5/sqlkit/widgets/common/sqlfilter.py0000644000175000017500000012207611714210425021223 0ustar sandrosandro# Copyright (C) 2005--2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. _filter_panel: ================ FilterPanel ================ The filter panel is the panel where all filter conditions can be written (remeber that constraints are different in the sense that are filters applied w/o possibility to remove them). It opens as a window separate from the main window so that it's easy to hide or keep it at hand. Each sqlwidget has a FilterPanel even if it doesn't show it .. autoclass:: FilterPanel :members: hide, show, get_tools, reload, set_page, tree, replace_column, add_column, clear .. autoclass:: FilterTool :members: set_value, get_value, set_operator, get_operator, destroy When the filter is used from a SqlTable, the output is displayed directly into the SqlTable. When the filter is used from a SqlMask the output is shown in a special tab of the FilterPanel that is really a :ref:`View ` on the output that can be customized to a good degree. Default representation is ``str(obj)`` unless a you have defined a ``format`` in the :ref:`database attribute description ` for that table. Customizing the output tab ========================== The default representation of records is a :ref:`View ` with a single column named ``__obj__`` that is a field that creates a str(obj) as explained above. If you want to change that representation you just need to substitute the column in the treeview:: from sqlkit.import fields from sqlkit.db.utils import DictLike class CountMovies(fields.IntegerField): ''' A field that counts the movies ''' def clean_value(self, value): ## missing a field_name attribute on obj the objct itselt is passed return len(value.movies) my_mask.filter_panel.replace_column(CountMovies) Alternatively you can :ref:`add a column ` to the output view after creating the field and the column you would add it to the view as follows:: count = CountMovies('n_movies') col = columns.VarcharColumn(t, 'n_movies', 'Movie Count', field=count) my_mask.filter_panel.view.add_column(col) At this point you can sort the output on each column and even get totals in it. """ import re import gtk import warnings from datetime import * import gobject import sqlalchemy from sqlalchemy.sql import select, Join from babel import dates from sqlkit import debug as dbg, _, exc, fields from sqlkit.db import minspect from sqlkit.db.utils import tables, get_description, DictLike from sqlkit.db.django_syntax import django2components from sqlkit.misc import datetools, utils from sqlkit.layout import layout, misc from sqlkit.widgets.table import columns, modelproxy import completion FILTER_MENU = ''' ''' OP_MSGID = { 'REGEXP' : _('Match as regexp'), 'match' : _('Match as LIKE, "%" automatically added'), 'ICONTAINS' : _('Match as LIKE case insensitive, "%" automatically added'), '~' : _('Match as regexp'), '~*' : _('Match as regexp, case insensitive'), '!~' : _('Negation of match as regexp'), '!~*' : _('Negation of match case insensitive'), '=' : _('Equal'), '!=' : _('Not equal'), '>' : _('Greater than (after than)'), '>=' : _('Greater or equal'), '<' : _('Less than (before then)'), '<=' : _('Less than or equal'), 'LIKE' : _('LIKE: a "%" means any char - case sensitive'), 'NOT LIKE' : _('Negation of LIKE'), 'ILIKE' : _('As LIKE but case insensitive'), 'NOT ILIKE' : _('Negation of ILIKE'), 'IS TRUE' : _('The boolean is True'), 'IS FALSE' : _('The boolean is False'), 'IS NOT TRUE' : _('The boolean is not True'), 'IS NOT FALSE' : _('The boolean is not False'), 'IS NULL' : _('The value is not set'), 'IS NOT NULL' : _('The value is set'), 'ID' : _("ID equality (don't follow foreign table)"), # '' : _(''), } class FilterPanel(object): """A panel that manages filter conditions of a query: number of records, field names and output to point & click in case of a panel of a SqlMask """ tree = None """The name of an attribute that will work as grouping attribute. The output TreeView will show records grouped by the same attribute as parent/child. It should be improved as the parent is a the record and not a row with the only grouping attribute. """ def __init__(self, master, visible=True): """constructor need to know who is the 'master' ie the widget (a mask or a table instance) that will display the result. The filter for a SqlMask has a TreeView to show the results and to easy browse them. """ self.master = master self._ids = {} self.dont_record_display = False handler_id = self.master.connect('delete-event', self.destroy) self._ids['master_delete_event'] = (master, handler_id) self.get_layout(visible=visible) self.row = 0 # counter for position of fields self.default_reload = True self.search_filter_widgets = [] ## list of FilterTools instances self.search_filter_field_names = [] ## field_names used self.bind_name = self.master.metadata.bind.name self.tree = False self._last_focused_entry = None if master.is_mask(): self._id_record_selected = master.connect('record-selected', self.record_selected_cb) h1 = self.master.connect('record-deleted', self.record_deleted_cb) h2 = self.master.connect('record-new', self.record_added_cb) self._ids['master_record_selected'] = (master, self._id_record_selected) self._ids['master_record_deleted'] = (master, h1) self._ids['master_record_new'] = (master, h2) self.prepare_actions() def record_selected_cb(self, mask): """ callback on record selection """ self.select_output_path(obj=mask.current, block=True) def record_deleted_cb(self, mask, obj): """ callback on record deletion """ path = self.get_path_for_obj(obj) if path is not None: self.model.remove(self.model.get_iter(path) ) def record_added_cb(self, mask): """ callback on record deletion """ try: self.model.append([mask.current]) except: ## FIXME: I should consider also self.model.append(None, [mask.current]) def get_layout(self, visible=True): """create the filter panel widget""" if self.master.is_mask(): lay = """ {H.a {V.menu } {O.a tb=gtk-refresh ts=limit tb=gtk-close } } {N.0 %filter {|T.fld } %output { TVS=out} } """ else: lay = """ {H.a {V.menu } {O.a tb=gtk-refresh ts=limit tb=gtk-close } } {|T.fld } """ self.l = layout.Layout(lay, title=_('Filter Panel'), opts="s", label_map=self.master.label_map) self.l.prop('Window', 'visible', visible) self.l.prop('O.a', 'show_arrow', 'FALSE') self.l.prop('A=.fld', 'yscale', '0') self.l.elements['H.a'].pack_properties['y-options'] = '' self.l.prop('O.a', 'toolbar_style', 'GTK_TOOLBAR_ICONS') if self.master.is_mask(): ## TIP: filter page of the filter panel self.l.tip('filter', _('Add filters for your query')) ## TIP: output page of the filter panel self.l.tip('output', _('Result page for your query')) self.w = self.l.show(x=self.hide) # self.w['s=limit'].set_value(self.master.limit) adj = self.w['s=limit'].get_adjustment() adj.set_all(self.master.limit, 0, 10000, 10, 100, 0) self.l.connect( ('tb=gtk-close', 'clicked', self.hide), ('tb=gtk-refresh', 'clicked', self.reload_cb) ) self.w['label-cursor'] = gtk.gdk.Cursor(gtk.gdk.HAND1) if 'TV=out' in self.w: self._add_treeview() if 'N.0' in self.w: self.w['N.0'].connect_after('switch-page', self.on_switch_page) def _add_treeview(self): class ObjField(fields.VarcharField): """ A field that represents the obj for filter_panel """ def clean_value(self, value): return str(value) field = ObjField('__obj__') self.master.gui_fields['__obj__'] = field col = columns.VarcharColumn(self.master, '__obj__', _('output'), field=field) self.view = self.create_view() self.view.add_column(col) self.selection = self.view.treeview.get_selection() self.selection.connect('changed',self.on_selection_changed) def prepare_actions(self): self.actiongroup = gtk.ActionGroup('Filter') self.actiongroup.add_actions([ ('FilterMenu', None, _('Filter actions')), ('Reload', gtk.STOCK_REFRESH, _('Reload from db'), 'r', None, self.reload_cb), ('Close', gtk.STOCK_CLOSE, _('Close the panel'), 'q', None, self.hide), ], ) self.actiongroup.add_actions([ ('GoFilter', None, _('Go to filter panel'), 'l', None, self.set_page),], 'filter') self.actiongroup.add_actions([ ('GoOutput', None, _('Go to output panel'), 'o', None, self.set_page),], 'output') self.ui_manager = gtk.UIManager() self.accel_group = self.ui_manager.get_accel_group() self.l.widgets['Window'].add_accel_group(self.accel_group) self.ui_manager.insert_action_group(self.actiongroup, 10) self.ui_manager.add_ui_from_string(FILTER_MENU) ## pack menu and toolbar if it's a toplevel header = self.l.widgets['V.menu'] menu = self.ui_manager.get_widget('/FilterMain') header.add(menu) #header.add(toolbar) def go_to_filter(self, action): self.set_page('filter') def replace_column(self, field_class, field_name='__obj__'): """ Replace the column of the output treeview with a customized one :param field_class: a subclass of ``sqlwidget.fields.Field`` with a proper ``clean_value`` method :param field_name: the field_name of the column, Default: ``__obj__`` (i.e. the name used for the default column of the output treeview """ field = field_class(field_name) col = columns.VarcharColumn(self.master, '__obj__', _('output'), field=field) self.view.treeview.remove_column(self.view.tvcolumns['__obj__']) self.view.add_column(col, 0) self.master.gui_fields[field_name] = field def add_column(self, field_name): """ Add a column among already defined fields in gui_fields :param field_name: the field_name of the field """ self.view.setup_columns([field_name]) def sb(self, message, seconds=None): """ Write messag on the status bar """ self.l.sb(message, seconds=seconds) def add_filter(self, active=True, **kwargs): """ add a filter using django-like syntax :param active: make the filter active (default True). This hides the possibility to add a filter on a field named ``active``. In this case you can use the longer version that is ``active__eq=True`` :param kwargs: any paramenter allowed per syntax of the django_like syntax as explained in the :ref:`chapter on constraints ` """ for key, val in kwargs.iteritems(): path, op, op_str, value, col, join_args = django2components(self.master.mapper, {key: val}) master = self.get_related_master(self.master, path) filter_tool = self.add(None, None, col.name, master, force=True) if not filter_tool: return filter_tool.set_active(active) if filter_tool.is_filter_value(value): filter_tool.set_value(value) filter_tool.set_operator(op, value) def get_related_master(self, master, path=None): """ Return the SqlTool that manages the data at 'path'. E.g.: if the mapper is the one for movie, and the 'path' is 'genres' as in the doc example, ``get_master`` would return the SqlTable that manages the collection of records returned by the relation 'genres' """ for elem in path: master = master.related[elem] return master def add(self, wdg, ev, field_name, master, after=None, show=True, force=False, value=None): """ add a FilterTool to the filter panel + operator and string entry after: entry will be positioned after widget 'after' """ if not self.master.ui_manager.get_action('/Main/File/Filters').is_sensitive(): return ## FIXME: after and show? search_path = "%s__%s" % ("__".join(master.relationship_path), field_name) if not search_path in self.search_filter_field_names or force: widget = get_filter_widget(field_name, self, master, value) widget.search_path = search_path self.search_filter_widgets += [ widget] self.search_filter_field_names += [search_path] self.set_page(name='filter') if show: self.w['Window'].show_all() self.w['Window'].present() try: return widget except: return None def set_page(self, action=None, name=None): """ set the tab in the filter. Tab name can be 'filter' or 'output'. Sqlmasks only have 'filter'. :param name: name of the tab in the filter widget: *filter* or *output* """ if 'N.0' in self.w: if name == 'filter': self.w['N.0'].set_current_page(0) if name == 'output' and self.master.is_mask(): self.w['N.0'].set_current_page(1) def hide(self, widget=None, event=None): """ Hide the Filterpanel :param widget: not neeeded: it' here to allow using it in callback :param event: not needed: see above """ self.w['Window'].set_property('visible', False) return True # stops further processing... def show(self): """ Present the filter panel """ self.w['Window'].present() def get_tools(self, field_name): """ return a list of FilterTool for *field_name* :param field_name: name of the field """ return [w for w in self.search_filter_widgets if w.field_name == field_name] def destroy(self, window=None): for obj, hid in self._ids.values(): obj.disconnect(hid) del self._ids if window: self.w['Window'].destroy() for attr in ('easytv', 'search_filter_widgets', 'l', 'model', 'selection', 'w'): try: delattr(self, attr) except AttributeError, e: pass def entry_focus_cb(self, entry, event=None): self._last_focused_entry = entry def on_switch_page(self, notebook, junk, page_n): if page_n == 0 and self._last_focused_entry: gobject.idle_add(self._last_focused_entry.grab_focus) ##### reload & TreeView def reload(self): """issue a reload operation on master. Callback of reload button""" length = 0 if 'TV=out' in self.w: display = False else: display = True if self.default_reload: try: length = self.master.reload(limit=self.get_limit(), display=display ) except (exc.DialogValidationError, exc.ValidationError), e: return if 'TV=out' in self.w: if length: self.master.modelproxy.fill_model(clear=True) self.set_page(name='output') self.select_output_path(0, block=False) self.view.treeview.grab_focus() self.tv_resize() self.view.on_zoom_fit() else: self.model.clear() self.l.sb(_('Total N. of records: %s' % length)) def select_output_path(self, path=None, obj=None, block=False): """ Select path ``path``. If path is null and obj is not null try finding path at which is obj :param block: block record display (when selction is already an answer to record_display) """ if block: self.dont_record_display = True if path is None and obj: path = self.get_path_for_obj(obj) if path is not None and len(self.model): self.selection.select_path(path) self.view.treeview.scroll_to_cell(path) self.dont_record_display = False def get_path_for_obj(self, obj): """ return path for object :param obj: the object for which we neeed the path """ path_list = [None] def compare(model, path, iter): row_obj = model.get_value(iter, 0) if row_obj == obj: path_list[0] = path return True return False self.model.foreach(compare) return path_list[0] def reload_cb(self, widget): """callback from reload button """ self.reload() def set_reload(self, mapper=None, join=None, tables=None): """A different method to filter & select is provided the scenario is that you have many synced sqlwidgets, you want to provide the possibility to select a record in a table with different records in other tables and be able to select one particular pair of them from the filter tree. You need to pass a join to issue the select on and a method in actions called 'on_filter_reload' to wich is passed only an argument: a dictionary with all the attributes of the collected record. If you pass tables to set_reload, each table is autoloaded and joined to the preceding. The returned join is used. """ self.default_reload = False if tables: if isinstance(tables, str): T = {} tables = re.split('[ ,]+',tables) for tbl in tables: T[tbl] = sqlalchemy.Table( tbl, self.master.metadata, autoload=True) for tbl in tables[1:]: join = T[tables[0]].join(T[tbl]) if join: class Join(object): pass mapper = sqlalchemy.mapper(Join, join) self.mapper = mapper self.map_fields = minspect.InspectMapper(mapper) def create_view(self, field_list=None): """ create a sqlkit.widgets.table.column.View to represent the objects """ if not hasattr(self.master, 'views'): self.master.views = utils.Container() treeview = self.w['TV=out'] view = columns.View(master=self.master, name='filter_panel', treeview=treeview, field_list=field_list, ro=True) self.master.modelproxy = modelproxy.ModelProxy(self.master, treeview=treeview) view.treeview.set_model(self.master.modelproxy.modelstore) self.master.views['filter_panel'] = view return view def _get_model(self): return self.master.modelproxy.modelstore model = property(_get_model) def tv_resize(self): tv = self.w['TV=out'] tv.realize() X, Y, width, height, bit = tv.window.get_geometry() if height < 100: tv.set_property('height-request', 300) def on_selection_changed(self, *args): # when clicking on the teeview we get 2 args: treeView, event # when changing selection we only have 1 arg: gtk.treeSelection # when clicking 'Refresh' we get e gtk.TreeSelection but no # iter get selected ## first guess: we selected w/ pointer if self.dont_record_display: return model, iter = self.selection.get_selected() ## we don't have a selection if iter is None: try: ## we moved along the treeview w/ arrows wdg, ev = args path = wdg.get_path_at_pos(int(ev.x), int(ev.y))[0] iter = model.get_iter(path) except (ValueError, TypeError), e: ## ValueError: deselecting when clicking 'reload' ## TypeError: no data in the treeview return if self.default_reload: try: idx = self.master.records.index(model.get_value(iter, 0)) except ValueError: return self.master.handler_block(self._id_record_selected) self.master.record_display(index=idx) self.master.handler_unblock(self._id_record_selected) def clear(self): "Destroy all filter widgets matered by this FilterPanel" for w in list(self.search_filter_widgets): w.destroy() ##### SQL def get_limit(self): """ Get limits of the select we are issuing """ self.w['s=limit'].update() return self.w['s=limit'].get_value_as_int() def add_filter_conditions(self, query): """Adds to the query all filter conditions """ for tool in self.search_filter_widgets: query = tool.add_filter_condition(query) return query def sql_dbg(self, bool_conds): for c in bool_conds: print "DBG: ", c.left, c.operator, c.right class FilterTool(object): """ A tool that handles the the filter and provides a mean to modify the query of the master (sqlwidget). With the FilterTool you can programmatically set the filter active/inactive, change the operator and the filter values. You will normally do all this with the ``.add_filter`` method of sqlwidget, but you may occasionally need to fine tune the filter in a second time """ STR_STD_OPERATORS = [ 'LIKE', 'NOT LIKE', 'ILIKE', 'NOT ILIKE'] NULL_OPERATORS = [ 'IS NULL', 'IS NOT NULL' ] STD_OPERATORS = [ '>=','<=', '=', '!=', '>', '<', ] + NULL_OPERATORS def __init__(self, field_name, panel, master): self.panel = panel self.master = master self.field_name = field_name self.master.connect('delete-event', self.destroy) self.db_spec = self.master.mapper_info.fields[field_name] ## db_spec['table'] may not be defined if the column correspond to an expression if self.db_spec['table'] is not None and self.master.relationship_path: ## use an alias in case we have another filter to the same table self.table = self.db_spec['table'].alias() else: self.table = self.db_spec['table'] prop = self.master.mapper.get_property(self.field_name) cols = prop.columns if len(cols) >= 2: msg = """I'm not able to understand which column you want here: field_name '%s' has several columns: %s""" % (self.field_name, prop.columns) raise NotImplementedError(msg) if isinstance(self.master.mapper.local_table, Join): ## FIXME: no aliasing in this case self.col = cols[0] else: ## Note: here self.table can be the alias of self.master.mapper_info.field[field_name].table if self.table is not None: self.col = self.table.c[self.field_name] else: ## no aliasing here: is any possible to alias a _Label/expression? self.col = self.db_spec['col'] self.bind_name = self.master.metadata.bind.name self.widget = FilterWidget(self) def destroy(self, window=None): "Destroy the FilterToold and related widgets and de-register from FilterPanel" try: self.panel.search_filter_widgets.pop( self.panel.search_filter_widgets.index(self)) self.panel.search_filter_field_names.pop( self.panel.search_filter_field_names.index(self.search_path)) except AttributeError: pass self.widget.destroy() del self.widget ##### handling def is_filter_value(self, value): """ return True if this value can be written This may be dependant on the FilterTool but for the moment is not. """ ## NOTE!!! value in (False, True) is WRONG as '0 == False' is True # and so 0 in (False,) is True!... if value is False or value is True: return False return True def set_value(self, value): """ set the value of the filter. It can be a string or an object (eg. a date()) """ self.widget.set_value(value) def get_value(self): """ return the current value of the filter """ value = self.widget.get_value() f = self.master.gui_fields[self.field_name] return f.clean_value(value) def get_row_value(self): """ return the current value of the filter """ return self.widget.get_value() def set_active(self, active): """make the filter active""" self.widget.set_active(active) def get_active(self): """set the filter inactive""" return self.widget.get_active() def set_operator(self, op, value): """ Set the active operator entry in ComboBox/OptionMenu for operator choice :param op: the operator :param value: the value of the operator """ self.widget.set_operator(op, value) def get_operator(self): """ return the active operator entry in ComboBox/OptionMenu """ return self.widget.get_operator() def get_suitable_operators(self): """return list of operators meaningfull for engine/data type """ return self.STD_OPERATORS def string_representation(self, value): """ return a string representation suitable for filter entry """ field = self.master.gui_fields[self.field_name] return field.get_human_value(value) def add_filter_condition(self, query): if not self.get_active(): return query op_value = self.get_operator() query = query.reset_joinpoint() if self.master.relationship_path: # query = query.join(*self.master.relationship_path) if len(self.master.relationship_path) == 1: query = query.join( (self.table, self.master.relationship_path[0])) else: ## NOTE: if relationship_path is longer that 1, the table is ## not aliased, i.e.: if you place 2 search conditions to ## the same table (e.g. searching on staff and manager of a ## project- and both manager and staff are m2m relation to ## User) will most surely lead to no result query = query.join(*self.master.relationship_path) if op_value in ('IS NULL', 'IS NOT NULL', 'IS TRUE', 'IS FALSE', 'IS NOT TRUE', 'IS NOT FALSE'): # NULL if op_value == 'IS NULL': return query.filter(self.col == None) elif op_value == 'IS NOT NULL': return query.filter(self.col != None) # True/False if op_value == 'IS TRUE': return query.filter(self.col == True) elif op_value == 'IS FALSE': return query.filter(self.col == False) if op_value == 'IS NOT TRUE': return query.filter(self.col != True) elif op_value == 'IS NOT FALSE': return query.filter(self.col != False) else: try: value = self.get_value() except exc.ValidationError, e: msg = _("value '%s' cannot be used for field '%s'") % ( self.get_row_value(), self.field_name) self.master.sb(msg) self.panel.sb(msg) raise e if value not in (None, ''): if not re.match('postgres|mysql', self.bind_name) and \ op_value in ('ICONTAINS', 'MATCH') and \ not isinstance(value, list): ## value is a list in FilterEnum op_value = 'ILIKE' value = '%' + value + '%' query = self._compose_field_whereclause(query, value, op_value) return query def _compose_field_whereclause(self, query, value, op_value): """ return a sql whereclause for field """ if op_value == "ILIKE": return query.filter(self.col.ilike(value)) else: return query.filter(self.col.op(op_value)(value)) def add_filter_cb(self, menuitem, event, n ): self.panel.add(None, None, self.field_name, self.master, force=True) def __repr__(self): return "<%s - %s>" % (self.__class__.__name__, self.master.get_label(self.field_name)) class FilterStringTool(FilterTool): def get_suitable_operators(self ): STD_OPERATORS = self.STR_STD_OPERATORS + self.STD_OPERATORS if self.bind_name.startswith('postgres'): return ['~*', '~', '!~*', '!~' ] + STD_OPERATORS elif self.bind_name.startswith('mysql'): return [ 'REGEXP' ] + STD_OPERATORS else: return ['ICONTAINS'] + STD_OPERATORS class FilterBooleanTool(FilterTool): def get_suitable_operators(self): return [ 'IS TRUE', 'IS FALSE', 'IS NOT TRUE', 'IS NOT FALSE' ] + self.NULL_OPERATORS class FilterDateTool(FilterTool): def _compose_field_whereclause(self, query, value, op_value): """ Implement the mini date algebra to allow for relative dates """ try: start, stop = datetools.string2dates(value) except datetools.WrongRelativeDateFormat: msg = _("value '%s' does not seem a valid date and cannot be transformed into a date" % value) self.master.dialog(type='ok', icon='gtk-dialog-error', text=msg) raise exc.ParseFilterError # If 'stop' is not None it means I have a period (eg.: 'y > M') # and I use operators to delimit this # period ignoring the operator from the widget op_value = self.get_operator() if not stop: query = query.filter(self.col.op(op_value)(start)) else: ## start query = query.filter(self.col.op(">=")(start)) ## stop query = query.filter(self.col.op("<=")(stop)) return query def get_value(self): # no cleaning of value until in _compose_field_whereclause return self.widget.get_value() def string_representation(self, value): """ when date algebra is used we don't need to do anything """ if isinstance(value, date): return FilterTool.string_representation(self, value) else: return value class FilterFKeyTool(FilterTool): def __init__(self, field_name, panel, master): FilterTool.__init__(self, field_name, panel, master) foreign_keys = self.master.mapper_info.fields[self.field_name]['fkey'] self.ftable, self.fcol = minspect.get_foreign_info(foreign_keys, names=False) def get_suitable_operators(self): if self.bind_name.startswith('postgres'): return ['~*', '~', '!~*', '~*' ] + self.STD_OPERATORS + ['ID'] elif self.bind_name.startswith('mysql'): return ['REGEXP' ] + self.STD_OPERATORS + ['ID'] else: return ['ICONTAINS' ] + self.STD_OPERATORS + ['ID'] def get_value(self): # no need to clean this value return self.widget.get_value() def string_representation(self, value): """ a boolean must be always a string """ return str(value) def _compose_field_whereclause(self, query, value, op_value): """return a sql whereclause for field In case field is a fkey, and the destination lookup field is not in the mapper, it composes a subselect """ if op_value == 'ID': return query.filter(self.col == value) fdescr = get_description(self.ftable, attr='description') if op_value == "ILIKE": ids = select([self.fcol], self.ftable.columns[fdescr].ilike(value)) else: ids = select([self.fcol], self.ftable.columns[fdescr].op(op_value)(value)) query = query.reset_joinpoint() if self.master.relationship_path: query = query.join(*self.master.relationship_path) return query.filter(self.col.in_(ids)) class FilterEnumTool(FilterTool): def __init__(self, field_name, panel, master): FilterTool.__init__(self, field_name, panel, master) def get_suitable_operators(self): return ['~', '!~' ] + ['IS NULL', 'IS NOT NULL', 'ID'] def get_value(self): op_value = self.get_operator() text = self.widget.get_value() if not text or op_value == 'ID': return text field = self.master.gui_fields[self.field_name] return [key for key, val in field.column.info['values'] if re.search(text, val, re.IGNORECASE)] def string_representation(self, value): """ a boolean must be always a string """ return str(value) def _compose_field_whereclause(self, query, value, op_value): """return a sql whereclause for field In case field is a fkey, and the destination lookup field is not in the mapper, it composes a subselect """ from sqlalchemy.sql import not_ if op_value == 'ID': return query.filter(self.col == value) elif op_value == '~': return query.filter(self.col.in_(value)) elif op_value == '!~': return query.filter(not_(self.col.in_(value))) return query def get_filter_widget(field_name, panel, master, value=None): """ return an instance of a proper FilterTool according to type """ args = field_name, panel, master if master.is_date(field_name) or master.is_datetime(field_name): return FilterDateTool(*args) elif master.is_fkey(field_name): return FilterFKeyTool(*args) elif master.is_enum(field_name): return FilterEnumTool(*args) elif master.is_boolean(field_name): return FilterBooleanTool(*args) elif master.is_number(field_name): return FilterTool(*args) else: return FilterStringTool(*args) # _('output') _('filter') class FilterWidget(object): def __init__(self, tool): ## I'd like *not* to .present the widget for any successive field added ## but I have not been able to get focus on the window w/o .present() self.tool = tool self.label_text = tool.master.get_label(tool.field_name) ## labels self.field_name_event_box = gtk.EventBox() self.field_label = gtk.Label(self.label_text) # TIP: appears in the menu in the filter panel to add a second entry of the same field label_str = _("Add a new filter on this field '%s'") % self.label_text.replace('_', '__') self.field_name_event_box.set_tooltip_text(label_str) self.check_button = gtk.CheckButton() self.check_button.set_active(True) #self.tool.panel.l.tooltips.set_tip(self.check_button, _("Use this filter")) self.check_button.set_tooltip_text( _("Use this filter")) self.field_name_event_box.add(self.field_label) self.field_label.set_property('xalign', 0) self.field_name_event_box.connect('button-press-event', self.tool.add_filter_cb, 1) ## combo for operator self.operator = self.get_operator_chooser() ## get_active_text per riprenderlo... ## entry self.field_widget = gtk.Entry() self.field_widget.connect('focus-in-event', self.tool.panel.entry_focus_cb) self.field_widget.connect('activate', self.tool.panel.reload_cb) if isinstance(self.tool, FilterDateTool): self.field_widget.connect('changed', self.date_changed_cb) Tbl = self.tool.panel.w['T.fld'] r = self.tool.panel.row c = 0 self.widgets = [ self.field_name_event_box, self.operator, self.check_button ] ftype = self.tool.master.get_field_type(self.tool.field_name) if self.tool.master.is_enum(self.tool.field_name) or not issubclass(ftype, bool): self.widgets += [self.field_widget] for wdg in self.widgets: Tbl.attach(wdg, c, c+1, r, r+1, xpadding=4) c += 1 self.tool.panel.row += 1 Tbl.show_all() self.field_widget.grab_focus() def get_operator_chooser(self): """ Build the chooser for the operator. I used deprecated OptionMenu since ComboBox does not give the opportunity to add tooltips """ operators = self.tool.get_suitable_operators() warnings.simplefilter('ignore', DeprecationWarning) operator = gtk.OptionMenu() operator.set_tooltip_text(_('Click here to select an operator')) warnings.resetwarnings() menu = gtk.Menu() operator.set_menu(menu) for op in operators: entry = gtk.MenuItem(op) menu.append(entry) entry.set_data('value', op) entry.set_tooltip_text(OP_MSGID[op]) operator.set_history(0) return operator def destroy(self): "Destroy all gtk widgets contained in self.widgets" for w in self.widgets: w.destroy() def set_active(self, active): self.check_button.set_active(active) def get_active(self): return self.check_button.get_active() def set_operator(self, op, value): """ Set the active operator entry in ComboBox for operator choice """ operator_list = self.tool.get_suitable_operators() if op == 'IS NULL' and value is False: op = 'IS NOT NULL' if op in '=' and isinstance(self.tool, FilterBooleanTool): if value == True: op = 'IS TRUE' else: op = 'IS FALSE' try: index = operator_list.index(op.upper()) self.operator.set_history(index) except ValueError, e: print "operator '%s' not available in this context" % op.upper() print "used: %s -- list: %s" % (op, operator_list) raise pass def get_operator(self): """ return operator set in widget """ return self.operator.get_menu().get_active().get_data('value') def get_value(self): """ return value of filter string if any """ return self.field_widget.get_text() def set_value(self, value): """ set string for FilterWidget """ self.field_widget.set_text(self.tool.string_representation(value)) def date_changed_cb(self, entry, ): """ date changed: show the new date and set a tooltip """ try: dates_ = datetools.string2dates(entry.get_text()) except: entry.set_tooltip_text('') self.tool.panel.l.sb(_('incomplete date format')) return dates_ = [dates.format_date(d, locale=dates.default_locale()) for d in dates_ if d] text = " - ".join(dates_) entry.set_tooltip_text(text) self.tool.panel.l.sb(text) # def pop_menu(self, wdg, ev, num_button): # print "POP_MENU", self, self.menu # if ev.button == num_button: # self.menu.popup(None, None, None, ev.button, ev.time ) sqlkit-0.9.5/sqlkit/widgets/common/layoutgenerator.py0000644000175000017500000003222711714210425022440 0ustar sandrosandro# Copyright (C) 2005-2006-2007-2008, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ ================ Layoutgenerator ================ LayoutGenerator ================ build up the layout definition suitable for layout.Layout Building layout from scratch ---------------------------- In case no layout is passed, a simple layout is built consisting of all fields vertically packed based on fields in field_list Passing a layout ------------------- In case a layout is passed as argument, the content is parsed to add the labels and to set the type of widget used to represent the data (using the mapping in map_types) unless a type is passed in the layout attributes ---------- ``fields_in_layout`` is a dict that maps field_names to widget specification ie: the key to be used in widgets dictionary * {'first_name': 'e=first_name', 'date': 'd=date', 'year': 'e=year'} *without any specification* BUGS ---- At the moment there is redoundancy: here we decide the gtk widget but fields decide the validation field that on turn set the widget, that on turn pretends a gtkwidget, this make very diffiocult to add a way to plug in a different rendere for a field. .. Reference to other tables .. --------------------------- .. .. A name of a field can also be of the form: table.field_name, in which case .. the table is reflected to get the type of the column, to adjust for the .. proper widget representation """ import re import datetime import decimal from sqlalchemy import * import sqlkit as sk from sqlkit import debug as dbg from sqlkit.layout import widgets for mod in ('m2m', 'm2o', 'o2m', 'o2o'): widgets.register_alias(widgets.Alignment, mod) class FkEdit(widgets.LWidget): gtkClass = 'FkEntry' class IntervalEdit(widgets.LWidget): gtkClass = 'IntervalEdit' class GreedyTreeView(widgets.LWidget): gtkClass = 'GreedyTreeView' widgets.register(FkEdit, 'fk') widgets.register(IntervalEdit, 'delta') widgets.register(GreedyTreeView, 'TV', force=True) class LayoutGenerator (object): map_types = { datetime.datetime : 'dt', datetime.date : 'd', datetime.time : 'ae', datetime.timedelta : 'delta', bool : 'c', int : 'ae', float : 'ae', str : 'ae', unicode : 'ae', decimal.Decimal : 'ae', } def __init__ (self, master, naked=False, lay=None): """ fields: an InspectMapper object """ self.master = master if lay: lay = re.sub('#.*\n','', lay) self.loaders = {} self.fields_in_layout = {} # {'first_name': 'e=first_name', 'date': 'd=date'} if self.master.is_mask(): if not lay: lay = self.create_default_layout() self._look_for_explicit(lay) self.layout = self._parse_and_complete(lay) else: self.layout = lay or self.create_for_table() if not naked: self._add_default_buttons() def _parse_and_complete(self, lay): """parse the layout and substitute all field_name with: L=field_name mod=field_name where mod is the appropriate modifier ([edc]) field_name must be preceded and followed by a space or newline """ p = re.compile(r"""^(\s*#.+)""", re.MULTILINE) lay = p.sub('', lay) # strip any comment # (?[a-zA-Z0-9]+)(?P=+))? # a possible modifier (?P[a-z_0-9A-Z\.]+)) # the field + a possible table (table.field) (?![-]) # the row above is only matched if not followed # by - o = (b=gtk-save-as *must not* match) (?P:[-<>.,a-zA-Z\d]*)? (?P\b) """, re.VERBOSE | re.MULTILINE) return p2.sub(self._sub, lay) def _sub(self, m): """called from inside regexp """ field_name = m.group('field') mod = m.group('mod') if not self.needs_parsing(field_name, mod): return m.group(0) ## check if we defined already a Field via gui_field_mapping if hasattr(self.master, 'gui_field_mapping'): Field = self.master.gui_field_mapping.get(field_name, None) else: Field = None widget = mod or self.get_widget(field_name, Field) spec = m.group('spec') or '' if self.master.is_loader(field_name): self.loaders[field_name] = spec if not spec: spec = self.create_spec(field_name, Field) self.fields_in_layout[field_name] = "%s=%s" % (self.get_edit_string(widget), field_name) if m.group('equal') == '==': ## implement the request to provide a way prevent layoutgenerator from creating the label ## == inhibits creation of label. return m.group(0).replace('==', '=') return "L=%s %s=%s%s" % (field_name, widget, field_name, spec) def needs_parsing(self, field_name, mod): # if field_name not in self.master.field_list or mod in ('L','le', 'm2m', 'o2m','m2o'): # return m.group(0) needs = False if mod in ('L','le', 'm2m', 'o2m','m2o'): needs = False elif field_name in self.master.field_list: needs = True elif hasattr(self.master, 'gui_field_mapping') and field_name in self.master.gui_field_mapping: needs = True return needs def get_edit_string(self, mod): """ return the real modifier that handle editing """ if mod in ('ae'): return 'e' if mod in ('aC'): return 'C' if mod in ('TXS'): return 'TX' else: return mod def get_widget(self, field_name, Field=None): """ choose appropriate widget to represent this field check if a particular field was already defined in gui_fields """ ## check if we defined already a Field via gui_field_mapping if Field: widget_str = getattr(Field, 'default_def_string') if widget_str: return widget_str #self.used_field_dict[field_name] = if self.master.is_fkey(field_name): ## ForeignKey for foreign key return "fk" if self.master.is_loader(field_name): return "m2m" if self.master.is_image(field_name): return 'img' if self.master.is_enum(field_name): return 'aC' ftype = self.master.get_field_type(field_name) widget = self.map_types[ftype] if self.master.is_text(field_name): widget = 'TXS' return widget def create_spec(self, field_name, Field): """ create a spec string that decides how long en entry must be reflects fom db """ try: length = self.master.mapper_info.fields[field_name]['length'] col_spec = self.master.mapper_info.fields[field_name]['col_spec'] or '' db_type = self.master.mapper_info.fields[field_name]['db_type'] except AttributeError: length = Field.length if hasattr(Field, 'length') else None if self.master.is_fkey(field_name) or self.master.is_loader(field_name): ### FIXME: should I set the length of the foreign key? return '' if not length: if self.master.is_integer(field_name): return ":8" elif self.master.is_float(field_name): return ":8" elif self.master.is_number(field_name): return ":10" elif self.master.is_text(field_name): dim = ":250.100" m = re.search('VARCHAR.(\d+).', col_spec) if m: length = int(m.group(1)) if length: if self.master.is_image(field_name): return '' ## really long field should not eat up all the space ## (but you can override this by hand!...) if length > 30: length = 30 ## now alignment: this will ensure it will grow ## if more space is given to the window via rezising spec = ":%s" % length if length> 20: spec += "-" # else: # spec += "<" return spec return '' def register(self, field_name): """regiter field_name as a field_name of a table to be found and reflected field_name must be of the form table.field_name """ p = re.compile(""" ([-_A-Za-z0-9]+) # table name \. # separator (\S+) # field_name """, re.VERBOSE) m = p.search(field_name) if not m: #dbg.write("%s not of the form table.field" % field_name ) return (field_name, None, None) table_name = m.group(1) #dbg.write('table_name:', table_name) tbl_field_name = m.group(2) engine = self.master.mapper_info.mapper.local_table.engine table = Table(table_name, engine, autoload=True) t1 = type(table.c[tbl_field_name].type) col_spec = table.c[tbl_field_name].type.compile(dialect=engine.dialect) t = self.master.mapper_info.fields.pytype[t1] return (tbl_field_name, t, col_spec) def _add_default_buttons (self): # a 'naked' mask is a mask w/o buttons. Maybe buttons are define by the # programmer inside the layout, maybe are not desired self.layout = """ {V.header } {A.external { %s}} sb=StBar """ % (self.layout) # {A.external {S {p { %s}}}} def create_for_table(self): """ A minimal layout for nested sqlwidget """ lay = """ TVS=tree {A.icons {H.icons %s}} """ % (self.master.icons) return lay def _look_for_explicit(self, lay): """ Look for names that represent loaders, if any parse it for: * rows (eg: m2m=actors:5) * field_list (eg: m2m=actors:5:first_name,last_name) set it in self.loaders['movies'] = {'rows': 5, 'field_list': 'first_name,last_name'} """ pat = re.compile(""" (?P[a-zA-Z0-9]+)= # the modifier, but not the = sign (?P[a-zA-Z0-9_\.]+) # the possible loader: # genre genre.movies (:(?P\d*))? # number of rows for the table (:(?P[\w,]*))? # number of rows for the table $ """, re.VERBOSE | re.MULTILINE) for token in lay.split(): m = pat.match(token) if m: field_name = m.group('field_name') # to be verified mod = m.group('mod') spec = m.group('spec') field_list = m.group('field_list') if field_name not in self.master.field_list: continue if self.master.is_loader(field_name): self.loaders[field_name] = {} if spec: self.loaders[field_name]['rows'] = int(spec) if field_list: self.loaders[field_name]['field_list'] = field_list self.fields_in_layout[field_name] = "%s=%s" % ( self.get_edit_string(mod), field_name) def create_default_layout(self, rows=20): """ Create a default layout. Don't display more that 20 attributes in a column. """ n_fields = len(self.master.field_list) columns = int(n_fields / rows) + 1 lay = [] for i in range(0, n_fields, columns): lay += [" ".join(self.master.field_list[i:i+columns])] return "\n".join(lay) sqlkit-0.9.5/sqlkit/widgets/table/0000755000175000017500000000000011714210425016433 5ustar sandrosandrosqlkit-0.9.5/sqlkit/widgets/table/tablewidgets.py0000644000175000017500000000626311714210425021472 0ustar sandrosandrofrom sqlkit.widgets.common import completion from sqlkit import _, exc class CellWidget(object): def __init__(self, field): self.field = field self.master = master = field.master self.field_name = field_name = field.field_name self.add_completion() self._dont_change_real_value = False self.master.field_widgets[field_name] = self def set_not_null_style(self): # FIXME: should set label in bold italics... pass def pop_completion(self): ## self.emit('changed') is needed as the only way to pop the completion ## when you click on the icon to pop the completion you should not invalidate ## self.real_value as it's not an editing action self._dont_change_real_value = True self.entry.emit('changed') self._dont_change_real_value = False def get_entry(self): # CellWidget has an editable only if we're editing... if self.field_name == self.master.currently_edited_field_name: return self.master.cell_entry.entry else: msg = _("No current obj for field '%s' in %s") raise NotImplementedError(msg % (self.field_name, self.master)) entry = property(get_entry) def add_completion(self, editable=False, force=False): """completion is set just for fkey and string fields :param editable: the editable widget to which completion should be added :param force: (boolen) if True a SimpleCompletion is added even if field is not a Varchar. Used to add completion to other fields, possibly in conjunction with enum. """ if self.master.relationship_mode == 'm2m' and editable == False: Completion = completion.M2mCompletion elif self.master.is_fkey(self.field_name): Completion = completion.FkeyCompletion elif self.master.is_enum(self.field_name): Completion = completion.EnumCompletion elif force or self.master.get_field_type(self.field_name) in (str, unicode): Completion = completion.SimpleCompletion else: self.completion = None return try: self.completion = Completion(self.master, self, self.field_name) self.master.completions[self.field_name] = self.completion except exc.ColumnWithoutTable, e: ## function columns do not have .table so that won't complete pass def set_value_cb(self, field, value, shown=False, initial=False): self.set_value(value, initial=initial) def set_value(self, value, shown=False, initial=False): self.master.set_value(self.field_name, value, shown=shown, initial=initial) def get_value(self, shown=False): return self.master.get_value(self.field_name) def set_max_length(self, length=None): if length is None: length = self.field.length try: self.entry.set_max_length(length) except: pass def __str__(self): return "" % self.field_name sqlkit-0.9.5/sqlkit/widgets/table/utils.py0000644000175000017500000002466011714210425020155 0ustar sandrosandroimport sys import gobject import gtk import pango from sqlkit import debug as dbg from cell_renderers import CellRendererVarchar def unset_real_size(window, event): """Fields 'real width' and 'real height' become obsolete (or at least useless) when a resize actually takes place. """ window.set_data('real width', None) window.set_data('real height', None) handler = window.get_data('configure handler') if handler: # The callback is now useless; remove it. window.disconnect(handler) window.set_data('configure handler', None) def ensure_configure_handler(window): """Ensure that unset_real_size is called. """ if not window.get_data('configure handler'): new_handler = window.connect('configure-event', unset_real_size) window.set_data('configure handler', new_handler) def get_width(window): width = window.get_data('real width') if not width: width = window.get_size()[0] return width def get_height(window): height = window.get_data('real height') if not height: height = window.get_size()[1] return height def set_width(window, width): ensure_configure_handler(window) window.set_data('real width', width) height = get_height(window) window.resize(width, height) def set_height(window, height): ensure_configure_handler(window) window.set_data('real height', height) width = get_width(window) window.resize(width, height) def set_height_request(treeview, rows=None): """ Show "rows" rows in the treeview, or a reasonable number if "rows" is not set. Untested with multiline rows. """ if not rows: rows = len(treeview.get_model()) if rows == 0: rows = 10 if rows > 30: rows = 30 column = treeview.get_column(0) cell_height = column.cell_get_size()[4] if not cell_height: while gtk.events_pending(): gtk.main_iteration() cell_height = column.cell_get_size()[4] spacing = treeview.style_get_property('vertical-separator') needed_height = cell_height * rows + (spacing) * (rows-1) top_widget = column.get_property('widget') if top_widget: top_height = top_widget.get_allocation().height else: top_height = cell_height needed_height += top_height dbg.write('needed', needed_height, 'spacing', spacing) treeview.set_property('height-request', needed_height) toplevel = treeview.get_toplevel() visible_rect = treeview.get_visible_rect() missing_height = needed_height - visible_rect.height new_height = toplevel.get_allocation().height + missing_height # print "toplevel:", toplevel, "old:", toplevel.get_allocation().height, "new:", new_height set_height(toplevel, new_height) def get_string_exact_width(context, string): """ (render and) Return width of a string in given context. """ description = context.get_font_description() layout = pango.Layout(context) layout.set_text(string) width = layout.get_pixel_extents()[1][2] return width def get_string_width(context, string=None, chars_num=80): """ Find how much space is needed to render a string of "chars_num" characters in pango context "context". If "string" is given, compute this number exactly, actually recomputing rendering of the string. Otherwise, use the "approximate_chars_width" of the font. """ if string != None: dbg.write("calculating width of", string[:chars_num], chars_num, type(string)) return get_string_exact_width(context, string[:chars_num]) else: # Perfection would imply setting this to actual language language = None description = context.get_font_description() metrics = context.get_metrics(description) # Pango uses its own internal measure unit. "pango.PIXELS" converts it. pango_width = metrics.get_approximate_char_width()*chars_num width = pango.PIXELS(pango_width) return width def get_screen_width(effective = True): """ Ugly hack. Couldn't find anything better. And in fact it's not strange, since GNOME HIGs apparently don't like this. """ root = gtk.gdk.get_default_root_window() prop = gtk.gdk.atom_intern('_NET_WORKAREA') fields = root.property_get(prop) if sys.platform.startswith('win') or not fields: # Notice Windows will fallback here and assume there are no lateral panels. screen = root.get_screen() return screen.get_width() else: fields = root.property_get(prop) sizes = fields[2] if effective and sizes: width = sizes[2] return width def set_optimal_width(treeview, max_pixels=None): """ Set optimal width of the treeview, based on width request by cellrenderers. This means that the window containing the treeview is resized accordingly (with care taken not to exit screen limit, and anyway not making the treeview larger that max_pixels, if set). If called before the treeview is realized (and with "resize" set), this function assumes that there is nothing else on the right and on the left of the treeview (e.g. the the treeview is in a bare sqltable). If called after, it supposes that, if there is something on the left and on the right, the treeview and all its parents have horizontal expand property set. """ widget = treeview scrolledwindows = [] scrolledwindows_policies = [] scrolledwindows_hscrollbars = [] while widget: # The ScrolledWindows stop propagation of queue_redraws, unless they # have the scrollbars disabled, so here we disable them... if gobject.type_is_a(widget, gtk.ScrolledWindow): scrolledwindows.append(widget) scrolledwindows_policies.append(widget.get_policy()) widget.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER) scrolledwindows_hscrollbars.append(widget.get_hscrollbar()) widget = widget.get_parent() toplevel = treeview.get_toplevel() # ... then we can take the size... new_width = toplevel.size_request()[0] # ... add the size of bars... bars_width = 0 for index in range(len(scrolledwindows)): # (not needed if the policy hides it) if scrolledwindows_policies[index][0] == gtk.POLICY_ALWAYS: bars_width += scrolledwindows_hscrollbars[index].size_request()[0] scrolledwindows[index].set_policy(*scrolledwindows_policies[index]) # print "width:", new_width # ... and put together the total size. new_width += bars_width available = get_screen_width() if toplevel.window: allocation = toplevel.window.get_frame_extents() border = allocation.width - get_width(toplevel) # print "border", border else: # Guess (Ubuntu's metacity's default theme and settings) border = 10 extra = new_width + border - available if extra > 0: new_width -= extra # print "extra", extra set_width(toplevel, new_width) return def zoom_to_fit(treeview, resize=True): """Take a treeview (presumably from a sqlmask) and, for each column asking a fixed size, reset space request criterion to content only (not database lenght introspection). Then, if "resize" is True, resize window accordingly. """ columns = treeview.get_columns() for column in columns: renderers = column.get_cell_renderers() for renderer in renderers: if hasattr(renderer, 'fixed_width') and renderer.fixed_width: # Let's fall back to the usual size method... renderer.fixed_width = False column.set_expand(True) column.set_min_width(0) if resize: set_optimal_width(treeview) treeview.columns_autosize() def set_all_nonfilling(wid, level): """ Set all widgets under the same toplevel as "wid" as non filling and non expanding (where this packing property has sense). """ if not level or not wid: return try: if wid.seen: return except: pass # print "browsed to", wid next_level = [] try: parent = wid.get_parent() if hasattr(parent, 'child_set_property'): try: parent.child_set_property(wid, 'expand', False) parent.child_set_property(wid, 'fill', False) except: pass try: import gtk parent.child_set_property(wid, 'y-options', None) except: pass next_level.append(wid.get_parent()) except: pass try: for child in wid.get_children(): next_level.append(child) except: pass wid.seen = True for other in next_level: set_all_nonfilling(other, level-1) def set_this_filling(wid): """ Set all widgets over (parents, or grandparents... of) wid as filling and expanding (where those packing properties make sense). """ while wid: newwid = wid.get_parent() if gobject.type_is_a(wid, gtk.Alignment): wid.set_property('yscale',1) dbg.write("setting yscale:", wid) if gobject.type_is_a(newwid, gtk.Table): newwid.child_set_property(wid, 'y-options', gtk.FILL|gtk.EXPAND) dbg.write("setting y-options:", wid) wid = newwid def print_debug(wid): """ Print debug information about packing of a widget and its superiors. """ dbg.write("starting with", wid) number = 1 while wid: newwid = wid.get_parent() number += 1 dbg.write("number", number, ":", newwid) try: dbg.write(newwid.get_property('xscale'), newwid.get_property('yscale'), "alignment") print "changing" except: pass try: dbg.write(newwid.get_allocation().width, newwid.get_allocation().height) except: pass try: dbg.write(newwid.get_property('n-rows'), newwid.get_property('n-columns')) dbg.write(newwid.child_get_property(wid, 'left-attach'), newwid.child_get_property(wid, 'top-attach'), newwid.child_get_property(wid, 'right-attach'), newwid.child_get_property(wid, 'bottom-attach')) dbg.write(newwid.get_children()) except: pass wid = newwid sqlkit-0.9.5/sqlkit/widgets/table/__init__.py0000644000175000017500000000023511714210425020544 0ustar sandrosandro# module sqltreelist #__all__ = ['SqlTable', 'SqlTreeTable', 'Header', 'ModelProxy'] from modelproxy import ModelProxy, Header #from table import SqlTable sqlkit-0.9.5/sqlkit/widgets/table/table.py0000644000175000017500000020505411714210425020102 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ SqlTable: a widget to edit sql in table mode a model (self.listore) holds one single instance of the class is linked with the mapper. column.set_cell_data_func on each column will set the value from there. """ import sys import re import os import gtk import weakref import shutil import datetime import gobject import gtk import sqlalchemy import sqlkit from sqlkit import debug as dbg, _, exc, fields from sqlkit.layout import widgets, get_label from sqlkit.misc.utils import Container, str2list, open_file import utils as tv_utils import cell_renderers import totals from sqlkit.widgets.common import sqlwidget, dialogs from modelproxy import ModelProxy from sqlkit.widgets.table.columns import View from sqlkit.widgets.table.tablewidgets import CellWidget class SqlTable(sqlwidget.SqlWidget): """ Main widget to represent data of a sqlalchemy selectable (SELECT of a TABLE or JOIN & similar). A SqlTable can have different :ref:`views ` of the same data represented in different TreeViews. A SqlTable can be viewed alone or as part of a composite widget, that is normally a mask with :ref:`relationships`: be sure to understand this part as it's probably one of the more powerfull features of Sqlkit. SqlTables can have both ListStores or TreeStores. The latter allows to represent rows in a hierarchy. SqlTable inherits from :class:`sqlkit.widgets.common.sqlwidget.SqlWidget` """ #### Signals __gsignals__ = { 'button-press-event' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # event, obj, field_name, treeview, view (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_STRING, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT), ), 'context-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # current (or None) (gobject.TYPE_PYOBJECT,), ), } views = None "A container for the possible :ref:`views`. It always have a default view named ``main``" totals = None "class for :ref:`totals`: see the specific documentation for further info" edited_path = None ## For some reason :meth: is not turned in a link... """The path of the edited row (a tuple). It's not the output of :meth:`get_selected_path` since when you change the selected row the validation is triggered on the *previuos* selected_row that is pointed to by ``edited_path``. """ # __metaclass__ = dbg.LogTheMethods currently_edited_field_name = None mapper = None cell_entry = None cell_renderers = None # commit_allowed = dbg.TraceIt(True, name='commit_allowed', mode='rw') #### Init & Layout def __init__(self, *args, **kwargs): self.views = Container() #self.views['main'] = View(self, 'main', ) #self.treeview = None self.commit_inhibited = False self.deleting_row = False #self.edited_row_model_path = dbg.TraceIt(True, name='edited_row_model_path', mode='rw') self.edited_row_model_path = None self.modelproxy = ModelProxy(self, treeview=None) sqlwidget.SqlWidget.__init__(self, *args, **kwargs) self.modelproxy.treeview = self.treeview self.modelproxy.update() self.totals = totals.Totals(self) self._fk_layout_nick = {} self.run_hook('on_init') self._new_record = None def _get_layout(self, lay, naked): sqlwidget.SqlWidget._get_layout(self, lay, naked) self.treeview = self.widgets["TV=tree"] self.widgets['S=tree'].set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) ## from now on, the real field_list is the one in the View self.views['main'] = self.create_view(name='main', field_list = self.field_list, treeview=self.treeview) ## let tree use all the available space... if not self.naked: self.lay_obj.prop('A.external','yscale',1) if self.rows: gobject.idle_add(self.set_rows) self.icon_setup() self.treeview.hide() self.treeview.show() gobject.idle_add(tv_utils.set_optimal_width, self.treeview) return (self.lay_obj, self.widgets) def icon_setup(self): """ setup icons at the botton of the table if any 'icons' was passed to sqlwidget """ if 'A.icons' not in self.widgets: return self.lay_obj.prop( ('A.icons', 'xscale', 0), ('A.icons', 'xalign', 0), ('A.icons', 'top-padding', 0), ('A.icons', 'bottom-padding', 0), ('A.icons', 'left-padding', 0) ) ## just icons, no text if self.icons: for b in self.icons.split(" "): self.widgets[b].set_label = None self.widgets[b].get_image().set_property('icon-size', 1) ## in windows I can't see it... self.widgets[b].get_image().show() alignment = self.widgets[b].get_children()[0] hbox = alignment.get_children()[0] image, label = hbox.get_children() label.set_text('') def setup_field_validation(self, field_list=None): """ Create sqlkit.fields.Field object: one for each handled field """ if not field_list: return field_chooser = fields.FieldChooser(self.mapper_info, gui_field_mapping=getattr(self, 'gui_field_mapping', None)) for field_name in field_list or self.field_list: if field_name not in self.gui_fields: db_spec = self.mapper_info.fields.get(field_name, None) Field = field_chooser.get_field(field_name, db_spec) field = Field(field_name, db_spec) self.gui_fields[field_name] = field field.set_master(self) field.set_widget(widget=CellWidget) self.field_widgets[field_name] = field.widget def create_view(self, name='main', treeview=None, field_list=None): """ Create a :ref:`view `, i.e.: a list of columns one for each field in self.field_list. Columns are setup according to the type of the database field, introspected from the mapper. There may be more than one view in a SqlTable. The first one is created mandatorily; possible further ones are created by hand, passing a view name, a treeview and -normally- a field_list. The filter panel output page is a view of the filtered records. All the views of a SqlTable share the same model. :param view: the name of the view :param treeview: the treeview :param field_list: a list of field_name for which we want the view """ view = View(master=self, name=name, treeview=treeview, field_list=field_list) view.treeview.set_model(self.modelproxy.modelstore) self.set_rows(view=view) self.views[name] = view return view def show(self, show): sqlwidget.SqlWidget.show(self, show) self.views['main'].create_column_menu() self.views['main'].menu.fill_menu_hide_fields() def set_rows(self, rows=None, view=None, view_name=None): """ set number of treeview visible rows to "rows" :param rows: number of rows (defaults to self.rows) :param view: the view. Defaults to None (all views) :param view_name: the name of the view. Defaults to None (all views) """ if rows: self.rows = rows views = self.views if view: views = (view,) if view_name: views = (self.views[view_name]) if not view: for v in views: tv_utils.set_height_request(v.treeview, self.rows) def hide_fields(self, field_name_list='', view_name='main'): """ Hide a column and add the entry in the menu to make it show up again :param field_name_list: may be a list or a string (in which case it is split with, and spaces) :param view: the view for which we set the list of hidden fields """ assert view_name in self.views, "Missing view_name: %s" % view_name self.views[view_name].hide_fields(field_name_list) def set_field_list(self, field_list, view_name='main'): """ Set field of visible columns. Accepts both a list or a string (space or comma separated) :param field_list: the field name list :param view: the view for which we set the field_list """ assert view_name in self.views, "Missing view_name: %s" % view_name self.views[view_name].set_field_list(field_list) def _get_edited_path(self): "return the curretly edited path as a tuple" try: return self.edited_row_model_path and \ tuple(int(x) for x in self.edited_row_model_path.split(':')) except AttributeError: ## edited_row_model_path may also be already a tuple... if isinstance(self.edited_row_model_path, tuple): return self.edited_row_model_path edited_path = property(_get_edited_path) ###### Actions/UiManager def prepare_actions(self): """ Prepare action needed by UIManager """ sqlwidget.SqlWidget.prepare_actions(self) self.actiongroup_table = gtk.ActionGroup('Table') self.actiongroup_insert.add_actions([ ('Duplicate', gtk.STOCK_SAVE_AS, _('Duplicate'), None, _('Create a new row as a duplicate of this one'), self.record_save_new_cb), ('New', gtk.STOCK_NEW, None, '', None, self.record_new_cb), ('NewChild', gtk.STOCK_NEW, _('New child row'), '', _("Create e new row as child of this one"), self.record_new_child_cb), ]) self.actiongroup_delete.add_actions([ ('Delete', gtk.STOCK_DELETE, None, 'k', None, self.record_delete), ('RecordDelete', gtk.STOCK_DELETE, None, None, None, self.record_delete3), ]) self.actiongroup_table.add_actions([ ('Zoom-fit', gtk.STOCK_ZOOM_FIT, None, 'z', None, self.on_zoom_fit), ('ShowColumns', None, _('Show field')), # menuitem ('HideColumns', None, _('Hide field')), # menuitem # ('VisibleColumns', None, None), # menu # ('HiddenColumns', None, None), # menu ('Records', None, 'Records'), ('MaskView', gtk.STOCK_ZOOM_IN, _('View this record in a Mask'), 'm', None, self.record_in_mask), ('MaskViewFKey', gtk.STOCK_LEAVE_FULLSCREEN, _('View this ForeignKey in a Mask'), None, None, self.fkey_record_in_mask), ('UploadImage', None, _('Upload Image'), None, None, self.upload_image), ('UploadFile', None, _('Upload File'), None, None, self.upload_file), ('DeleteFile', None, _('Delete File/Image'), None, None, self.delete_file), ('ShowFile', None, _('Show File/Image'), None, None, self.show_file), ('SaveFile', None, _('Save File/Image'), None, _('Save file locally'), self.save_file), ]) self.actiongroup_table.add_actions([ ('Export', None, _('Export'), None, None, self.export), ], 'main') def prepare_uimanager(self): from sqlkit.widgets.common import uidescription sqlwidget.SqlWidget.prepare_uimanager(self) self.ui_manager.insert_action_group(self.actiongroup_table, 1) self.ui_manager.add_ui_from_string(uidescription.TABLE_UI) self.ui_manager.get_action('/Main/Go/Forward').set_visible(False) self.ui_manager.get_action('/Main/Go/Back').set_visible(False) def set_mode(self, mode=None, reset=False, delay=False): sqlwidget.SqlWidget.set_mode(self, mode, reset=reset, delay=delay) if not delay and hasattr(self, 'columns'): for field_name, col in self.columns.iteritems(): if field_name not in self.noup: col.set_editable(True and 'u' in self.mode) else: col.set_editable(False) mode = property(sqlwidget.SqlWidget.get_mode, set_mode) #### callbacks treeview def selection_changed_cb(self, treeselection): """ Main callback triggered when the TreeSelection changes. When run this function tries to record_save() """ try: del self._clicked_obj except AttributeError: pass # selection changes in several different ways: # 1. no selection -> selection of existing row # 2. no selection -> selection on new row # 3. no selection -> reload (does it change?) # 4. selected existent record -> reload # 5. selected existent record -> other record # 6. selected existent record -> new record # 7. selected existent record -> delete # 8. selected existent LAST record -> delete if self.edited_row_model_path is None: self.emit('record-selected') obj = self.get_selected_obj() if not isinstance(obj, self.mapper.class_): # we have visited a total or something else return if obj or len(self.records): self.emit('context-changed', obj or self.records[0]) ## commit_allowed is a flag that prevents .commit() if a new object ## has been created but not yet filled: it wouldn't validate if not self.commit_inhibited: try: if self.relationship_mode == 'm2m' and not self.m2m_editable: ## a new record means a record that has not been substituted by completion ## after completion-match the record is in the session but not in session.new self.record_save(discard_unchanged=False, discard_new=True) else: self.record_save(discard_unchanged=True) self.edited_row_model_path = None except exc.ValidationError, e: path = self.edited_row_model_path self.edited_row_model_path = None self.treeview.get_selection().select_path(path) self.edited_row_model_path = path return True self.emit('record-selected') if self.get_selected_obj() or len(self.records): self.emit('context-changed', self.get_selected_obj() or self.records[0]) return False def select_cursor_row_cb(self, treeview, start_editing, user_data): return False def cursor_changed_cb(self, treeview, view): return False def show_column(self, menuItem, field_name, view_name='main'): view = self.views[view_name] view.show_column(field_name) def button_press_event_cb(self, treeview, event, view): """ Block browsing if last edited cell didn't validate """ if self.cell_entry and not self.cell_entry.is_valid(): if not self.cell_validate(): return True ## emit button-press-event of the sqlwidget: event, field_name, value # this happens before the selection gets changed so we cannot use get_selected_obj() try: path, column, x_in_cell, y_in_cell = treeview.get_path_at_pos(int(event.x),int(event.y)) iter = self.modelproxy.modelstore.get_iter(path) obj = self.modelproxy.modelstore.get_value(iter,0) if isinstance(obj, self.totals.total_class): obj = None field_name = column.get_data('field_name') self._enforce_first_click_just_selects(path, column, field_name) except TypeError, e: obj = None field_name = None path = None self._clicked_obj = obj # should I officially document it? self._clicked_path = path # should I officially document it? self._field_name = field_name # should I officially document it? menu = view.pop_menu(treeview, event, obj, field_name) self.emit('button-press-event', event, obj, field_name, menu, treeview) if event.button == 3: return True return False def _enforce_first_click_just_selects(self, path, column, field_name): """ if selection has changed with a click on a toggle cell, turn that cell into insensitive to prevent changeing the value """ ## I want the first click on the table to just change the selection, not the value ## the problem is that toggled signal happens *before* change in selection, so that ## all normal checks based on selection_changed are not yet at work selection = self.treeview.get_selection() model, rows = selection.get_selected_rows() if rows: selected_path = rows[0] else: selected_path = None if not path == selected_path and self.gui_fields[field_name].type == bool: if column.get_data('cell').get_property('mode') == gtk.CELL_RENDERER_MODE_ACTIVATABLE: column.get_data('cell').set_property('mode', gtk.CELL_RENDERER_MODE_INERT) gobject.idle_add(column.get_data('cell').set_property, 'mode', gtk.CELL_RENDERER_MODE_ACTIVATABLE) def on_zoom_fit(self, widget, *args): tv_utils.zoom_to_fit(self.treeview, resize=True) #### event handling def keyrelease_event_cb(self, wdg, event, data=None): return self.length_control(wdg, data) def keypress_event_cb(self, wdg, event, view=None): if isinstance(wdg, gtk.TreeView): pre = 'tree' else: #self.length_control(wdg, data) pre = 'entry' ksym = gtk.gdk.keyval_name(event.keyval) mods = ['key_' + pre] ## mod2 gets in the way when you press the NumLock: I don't want ## to depend on that, as I'm not usng it!!! for prefix, mask in [('ctrl', gtk.gdk.CONTROL_MASK), ('shift', gtk.gdk.SHIFT_MASK), ('alt', gtk.gdk.MOD1_MASK), #('mod2', gtk.gdk.MOD2_MASK), ('mod3', gtk.gdk.MOD3_MASK), ('mod4', gtk.gdk.MOD4_MASK), ('mod5', gtk.gdk.MOD5_MASK),]: if event.state & mask: mods.append(prefix) ksym = '_'.join(mods + [ksym]) if ksym in dir(self): return getattr(self, ksym)(wdg, event) def key_tree_shift_ISO_Left_Tab(self, treeview, event, data): path, col = self.treeview.get_cursor() columns = self.treeview.get_columns() colnum = columns.index(col) if colnum - 1 >= 0: gobject.idle_add(self.treeview.set_cursor, path, columns[colnum - 1], True) else: path = (path[0] - 1,) if path[0] < 0: path = (len(treeview.get_model()) - 1,) gobject.idle_add(self.treeview.set_cursor, path, columns[-1], True) return True def key_tree_Tab(self, treeview, event): path, col = self.treeview.get_cursor() if not path: return False ## only visible columns!! columns = [c for c in self.treeview.get_columns() if c.get_visible()] colnum = columns.index(col) if colnum + 1 < len(columns): #cell = col.get_cell_renderers()[0] #cell.emit('edited', path, ) #self.treeview.set_cursor( path, columns[colnum + 1], True) #gobject.idle_add(self.treeview.set_cursor, path, columns[colnum + 1], True) next_column = columns[colnum + 1] next_field_name = next_column.get_data('field_name') gobject.idle_add(self.treeview.set_cursor, path, next_column, self.get_direct_edit(next_field_name) ) else: tmodel = self.treeview.get_model() titer = tmodel.iter_next(tmodel.get_iter(path)) if titer is None: titer = tmodel.get_iter_first() path = tmodel.get_path(titer) next_column = columns[0] next_field_name = next_column.get_data('field_name') gobject.idle_add(self.treeview.set_cursor, path, next_column, self.get_direct_edit(next_field_name) ) return False def key_tree_ctrl_n(self, treeview, event): if not 'i' in self.mode: self.sb(_("Insertion of new records is disabled. Sorry")) return try: self.record_new(treeview) except exc.ValidationError, e: return True return False def key_tree_ctrl_c(self, treeview, event): if not self.modelproxy.is_tree(): return if not 'i' in self.mode: self.sb(_("Insertion of new records is disabled. Sorry")) return try: self.record_new(treeview, child=True) except exc.ValidationError, e: return True return False def key_tree_ctrl_s(self, treeview, event): if self.current in self.session.new and 'i' not in self.mode: self.sb(_("Insertion of new records is disabled. Sorry")) return if self.current in self.session.dirty and 'u' not in self.mode: self.sb(_("Update of records is disabled. Sorry")) return try: self.record_save() except exc.ValidationError: pass return False def key_tree_ctrl_k(self, treeview, event): if self.current not in self.session.new and 'd' not in self.mode: self.sb(_("Deletion of records is disabled. Sorry")) return False self.record_delete(treeview) return False def key_tree_ctrl_x(self, treeview, event): return self.key_tree_ctrl_k(treeview, event) def key_tree_ctrl_r(self, treeview, event): if 'b' not in self.mode: self.sb(_("browsing of new records is disabled. Sorry")) return self.reload_cb(treeview) return True def key_tree_Down(self, treeview, event): if self.modelproxy.tree_field_name: return False else: row = self.get_selected_path() if row is None: row = -1 else: row = row[0] if row + 1 == self.len_model(): try: self.record_new(treeview) except exc.CommitError, e: return False except exc.ValidationError: return True return False def key_entry_ctrl_n(self, widget, event): try: widget.emit('activate') except: ## CellTextView does not have activate signal pass self.record_new() return True def key_entry_Tab(self, widget, event): """ Trigger the validation when moving around the cells """ ksym = gtk.gdk.keyval_name(event.keyval) if ksym in ('Tab',): # ## FIXME: Down and Return should behave differently if the completion is visible # if ksym in ('Down'): # and widget.get_completion().get_property('visible'): # return False if self.cell_entry and not self.cell_entry.is_valid(): if not self.cell_validate(): return True def get_direct_edit(self, field_name): """ return the next column and if browsing must enter in edit mode """ # from datetime import datetime, date, time, timedelta if not self.is_editable(field_name): return False NO_EDIT = (bool,) if not self.mapper_info.fields[field_name]['type'] in NO_EDIT: return True return False #### callbacks columns def editable_keypress_cb(self, widget, event): """ Trigger the validation when moving around the cells """ ksym = gtk.gdk.keyval_name(event.keyval) if ksym in ('Tab',): if self.cell_entry and not self.cell_entry.is_valid(): if not self.cell_validate(): return True def record_new_child_cb(self, widget, event=None): """Save the record and invokes the validator before UPDATE/INSERT a record """ try: self.record_new(parent=self._clicked_path) except (exc.DialogValidationError, exc.CancelledAction): return True ###### Saving def set_value(self, field_name, value, path=None, fkvalue=None, initial=False, shown=False): """ Set ``value`` in the model for ``field_name`` at path (default self.current) :param field_name: the name of the field we want to set :param value: the value we want to set :param path: the modelstor path at which we want to set a value. Default is the edited object. :param fkvalue: the value is handled as a primary key. Used from completion and from gui_fields/tablewidget :param initial: if initial=False, the possible ``on_change_hook`` is not called """ ## note: self.current cannot be used. We don't want the currently selected obj ## but the one that is to be saved. This operation can be triggered ## by a change in selection in which case self.current already points ## to the new selection if path is None: obj = self.get_current_obj() else: obj = self.get_obj_at_path(path=path) if not obj: return ## casting of value field = self.gui_fields[field_name] if self.is_fkey(field_name): value = field.clean_value(value, input_is_fkey=True, obj=obj) else: value = field.clean_value(value, obj=obj) if field.editable: field.set_value(value, initial=initial, obj=obj, update_widget=False) ## text would come up automatically but only after ## loosing the focus... if fkvalue: self.cell_entry.entry.set_text(fkvalue) self.cell_entry.valid = True self.cell_entry.value = value self.cell_entry.text = fkvalue self.fkey_value = fkvalue else: if value is not None and not self.is_fkey(field_name): ## editable, if any, points to the last used editable ## that may not be the one that belogs to field_name... if self.cell_entry and field_name == self.cell_entry.field_name: #self.cell_entry.entry.set_text(str(value)) self.cell_entry.entry.set_text(self.gui_fields[field_name].format_value(value)) if not initial: self.run_hook('on_change_value', value, fkvalue, field, field_name=field_name) def get_value(self, field_name, shown=False): """ return the value of the cell **currently edited*** The name is possibly misleading due to parallelism with mask. shown: show the lookup value instead of the eal value """ obj = self.get_current_obj() if obj is None: raise exc.NoCurrentObjError(_("no current obj: maybe no record has yet been edited")) if field_name == self.currently_edited_field_name: if self.cell_entry: text = self.cell_entry.get_text() if self.is_fkey(field_name): if shown: ## we can be here becouse a clean_value triggered it. So don't pass there a ## second time. If you ask for 'shown' I give you shown. return text else: #return self.gui_fields[field_name].clean_value(text, input_is_fkey=False) return self.cell_entry.value else: return self.gui_fields[field_name].clean_value(text) value = getattr(obj, field_name) if shown == True and self.is_fkey(field_name): return self.gui_fields[field_name].get_human_value(value) else: return value def record_display(self, index=None, check=False): """ Display all records (appending them to modelproxy.modelstore) check=False - not used here but present becouse is used in Mask """ if index == None: self.modelproxy.fill_model(clear=True) # TIP: status bar message self.sb(_("Total N. of records: %s") % (len(self.records)), seconds=None) def record_has_changed(self): if self.edited_row_model_path is None: return False return sqlwidget.SqlWidget.record_has_changed(self) def record_save_new(self, origin=None, interactive=True): """ Create a new record and copy all data from an origin :param origin: the obj that must be duplicated, it can also belong to a different session :param interactive: (boolean) if True start editing and use set_value :return: the new object This can be used as follows in a on_save_as hook:: for row in old.righe: rel_table.record_save_new(origin=row, interactive=False) to copy all rows from one record to the other """ orig_obj = origin or self.get_obj_at_path(self._clicked_path) path, new_obj = self.record_new(start=interactive) for field in self.gui_fields: if not field.pkey and field.persistent: value = getattr(orig_obj, field.field_name) if interactive: self.set_value(field.field_name, value, initial=False) # this way is alternative to editing directly, for some reason it # deletes the first edited field:: #field.set_value(value, initial=False, obj=new_obj) else: setattr(new_obj, field.field_name, value) self.run_hook('on_save_as', orig_obj, new_obj) return new_obj def record_save(self, discard_unchanged=False, discard_new=False, ask=False, skip_check=True): """save the record Detects if any editing has been done consulting self.edited_row_model_path set by text_editing_started_cb. see record_validate for constraint explanation :param discard_empty: when record_save is called by reload, an empty record does not need validation :param ask: if True a dialog box will ask if you really want to save :param skip_check: (boolean) if ask=True and skip_check=True save_unsaved will skip check of real need to save """ if self.edited_row_model_path is None: # no editing done dbg.write('no self.edited_row_model_path, nothing to save', self.edited_row_model_path) return if ask: response = self.save_unsaved(skip_check=skip_check, proceed=False) if response == gtk.RESPONSE_CANCEL: raise exc.CancelledAction if response == gtk.RESPONSE_NO: return True # Only used in record_in_mask, better would be a exc.NegativeAnswer obj = self.get_current_obj() if discard_unchanged or (discard_new and obj in self.session.new): if not self.record_has_changed(): self.sb(_('Nothing to save')) if obj in self.session.new: self.commit_inhibited = True ## removing the modelproxy.modelstore element triggers edited_cb ## so I need to set the path to None to prevent errors path = self.edited_row_model_path self.edited_row_model_path = None self.modelproxy.modelstore.remove(self.modelproxy.modelstore.get_iter(path)) self.commit_inhibited = False self.session.expunge(obj) self.records.remove(obj) self._new_record = False del obj self.last_new_obj = None self.edited_row_model_path = None return ## if the obj is new, you need to validate it anyhow: it was not changed but didn't start ## from a validated point if not self.record_has_changed() and obj not in self.session.new: self.edited_row_model_path = None return self.record_validate(obj) ## in m2m case we don't want to commit now. We would trigger a commit of the ## record represented in the SqlMask that may not yet be validated if self.relationship_leader and ( self.relationship_leader.current in self.session.new): # TIP: when saving m2m, we delay till leader record will be saved self.sb(_('delaying saving when main record will be saved')) self.delayed = True else: try: self.commit() self.totals.compute() except exc.HandledRollback, e: iter = self.modelproxy.modelstore.get_iter(self.edited_row_model_path) self.edited_row_model_path = None self.modelproxy.modelstore.remove(iter) self.sb(_("Record has NOT been saved"), seconds=100) self.delayed = True self.last_new_obj = None self._new_record = False if not self.delayed: self.session.expire(self.current) if self.relationship_leader: self.session.expire(self.relationship_leader.current) self.relationship_leader.record_display(check=False, delay_message=2) def record_new(self, widget=None, view_name='main', start=True, obj=None, parent=None, *args): """Add a new record :param widget: the treeview :param view_name: the name on the view :param start: start editing the first non default field :param obj: :param parent: if defined it's a tuple representing a path. The new row must be a child of ``parent`` """ ## I want to avoid generating a new obj if an object was created ## and not modified (as when you press Down Arrow several times ## beyond the lower row) if not self.record_has_changed(): #if self.last_new_obj: if (self.current and self.current in self.session.new) and self._new_record: self.sb(_("Already at new record")) return self.record_save() self.treeview.get_toplevel().set_focus(None) self.last_new_obj = self.get_new_object(obj=obj) # focus the row & start editing ## scroll_to_cell will trigger selection_changed_cb ## when the new_obj has been added but not yet edited. ## we inhibit this self.commit_inhibited = True treeiter = self._get_next_iter(self.last_new_obj, parent=parent) path = self.modelproxy.modelstore.get_path(treeiter) if start: self.views[view_name].start_editing(path) ## set defaults if not obj: for field_name in self.field_list: self.gui_fields[field_name].set_default(obj=self.current) self.records += [self.last_new_obj] ## adding to records is vital to get ## fk correctly, in case ## this table is a M2M of M2O self.commit_inhibited = False self._new_record = True self.emit('record-new') return path, self.last_new_obj def record_add(self, obj, parent_path=None): """ Add a record to the SqlTable :param obj: the object to be added :param parent_path: a possible path of the parent :returns: the new path """ sqlwidget.SqlWidget.record_add(self, obj) path, obj = self.record_new(obj=obj, start=False, parent=parent_path) self.edited_row_model_path = path self.select_path(path) self.record_save() self.totals.compute() return path def unsaved_changes_exist(self, skip_check=False, skip_new=False): """collect possible changes and test if save is needed :param skip_check: we already know it needs saving. Passed to unsaved_changes_exists :param skip_new: skip_new may be an object whose changes we disregard (see unsaved_changes_exist) :return: False if cancel is pressed """ if not skip_check and not self.record_has_changed(): if self.last_new_obj: self.session.expunge(self.last_new_obj) del self.last_new_obj self.last_new_obj = None return sqlwidget.SqlWidget.unsaved_changes_exist(self, skip_new=skip_new) def record_delete(self, widget=None, interactive=True): """ Delete a row and corresponding record """ if self.ro: return True if not self.get_selected_obj(): self.sb(_('No record selected')) return True sel = self.treeview.get_selection() model, paths = sel.get_selected_rows() if interactive: text = _("Delete this record?\n(%s)") % self.current iter = model.get_iter(paths[0]) if model.iter_has_child(iter): text += "\n" + _("All children will be deleted as well") response = self.dialog(type='ok-cancel', text=text) if response == gtk.RESPONSE_CANCEL: return self.deleting_row = True def do_del(model, path, iter): """ potentially can delete a selection of many rows """ ## Recursively delete all children, if any if model.iter_has_child(iter): for i in range(model.iter_n_children(iter), 0, -1): # backward it = model.iter_nth_child(iter, i-1) do_del(model, model.get_path(it), it) obj = model[path][0] deleted = True if self.relationship_mode == 'SINGLE': if obj in self.session.new: self.session.expunge(obj) else: self.session.delete(obj) #self.session.flush([obj]) try: self.commit() except exc.HandledRollback, e: deleted = False else: ## we're in M2M relationship or equivalent: ## we don't want to delete the object, just delete the relationship ## with the referred father if obj in self.session.new: self.session.expunge(obj) if deleted: self.edited_row_model_path = None model.remove(iter) self._new_record = None if obj in self.records: self.records.remove(obj) sel.selected_foreach(do_del) ## select the same row or the preceding row_n = paths[0][0] # path is a tuple here if row_n >= len(model): row_n -= 1 if len(model): # prevent warning for empty models sel.select_path(row_n) self.deleting_row = False self.totals.compute() self.sb(_("Total N. of records: %s") % len(self.records), seconds=None) return True def record_delete3(self, item): """ Delete a record clicked with left click from menu item. In this case self._clicked_obj is used rather than selected one. """ self.select_path(obj=self._clicked_obj) self.record_delete() def _get_next_iter(self, new_obj, parent=None): """ create a new row in the next reasonable place: * the next row for simple liststores * the next sibling for treestores :param new_obj: the obj to be placed there :param parent: if defined it's the path of the parent row for the new object """ path = parent or self.get_selected_path() or getattr(self, '_clicked_path', None) if not path: try: return self.modelproxy.modelstore.append([new_obj]) except TypeError: return self.modelproxy.modelstore.append(None, [new_obj]) else: current_iter = self.modelproxy.modelstore.get_iter(path) if parent: new_iter = self.modelproxy.modelstore.insert_after(current_iter, None, [new_obj]) self.treeview.expand_row(path, True) return new_iter parent_iter = self.modelproxy.modelstore.iter_parent(current_iter) if parent_iter: new_iter = self.modelproxy.modelstore.insert_after(parent_iter, current_iter, [new_obj]) else: try: return self.modelproxy.modelstore.insert_after(current_iter, [new_obj]) except TypeError: return self.modelproxy.modelstore.insert_after(None, current_iter, [new_obj]) return new_iter ###### Validation def cell_validate(self): """Validate cell value and invoke field.set_value() or invoke completion. Return True if ok. The cell to be validated is the last visited cell, ie: the one whose field_name, path and cell where set inside text_editing_started_cb ForeignKey that do not validate should trigger completion This must be called by: - Return - Tab - Button1 on other cell - edited callback IMPORTANT: validation is not a way to enforce True validation. This bypasses normal completion mechanism!!! """ new_text = self.cell_entry.get_text() field_name = self.currently_edited_field_name if self.relationship_mode == 'm2m' and not self.m2m_editable: # in m2m we don't want to edit this field but just to add/remove objects # I'm not sure if this should change if not new_text: return True try: # The next statement sets a value even if it could not pass completion!!! self.set_current(filter_by={field_name :new_text}) return except (exc.NoResultFound, exc.MultipleResultsFound), e: ## if there's no match, try completion dbg.write("No match: try completion") show = self.gui_fields[field_name].widget.completion.show_possible_completion show(self.cell_entry.entry, 'start') return True except (sqlalchemy.exceptions.DBAPIError), e: self.sb(e) return True else: ## if self.is_fkey(field_name): if self.cell_entry and self.cell_entry.is_valid(): return True if not new_text: # casting '' to None => you're not allowed to use '' as FK... self.set_value(field_name, None, initial=False) # self.gui_fields[field_name].set_value(None, initial=False, # obj=self.current) return True self.sb(_('Value is not valid, trying completion')) show = self.gui_fields[field_name].widget.completion.show_possible_completion show(self.cell_entry.entry, 'start', sb=False) return False else: try: if self.cell_entry.text == new_text: return True self.set_value(field_name, new_text, initial=False) # self.gui_fields[field_name].set_value(new_text, initial=False, # obj=self.current) # A hook in "on_change_value" may trigger a deletion of cell_entry # e.g. a totals.compute(), so let's check again if self.cell_entry: self.cell_entry.text = new_text self.cell_entry.valid = True return True except exc.ValidationError, e: self.cell_entry.valid = False self.sb(str(e), seconds=8) return False def length_control(self, wdg, field_name): if self.mapper_info.fields[field_name]['type'] is not str: return False length = self.mapper_info.fields[field_name]['length'] text = wdg.get_text() lstr = len(text) if length: if lstr > length : wdg.set_text(re.sub('.$', '', text)) # TIP: check in input field length %s %s -> field_name length self.sb(_("Input exceeded max %s length (%s)") % (field_name,length)) return True return False def fkey_is_valid(self, field_name): """ return True if current editable -if exists- has a value that does not need validation """ if self.cell_entry and self.cell_entry.is_valid(): return True return False ###### Record browsing def initialize_record_editing(self, path, force=False, obj=None): """ register the path of the editing and initialize all validation fields called when entering a new row or when a new record is added in m2m """ # in Masks this is done when set_field is used on record_display # here the display is automatic via set_cell_data_func so we have to set # initial value when starting editing a record if not self.edited_row_model_path or force: if not obj: obj = self.get_selected_obj() if obj: for field in self.gui_fields: if field.persistent: # only mapped fields need initialize value = getattr(obj, field.field_name) self.gui_fields[field.field_name].set_value(value, obj=obj, initial=True) self.edited_row_model_path = path def clear(self, *args, **kwargs): self.modelproxy.modelstore.clear() def reload_cb(self, widget, *args): """ this is the real callback: C-r and toolbutton """ self.grab_focus(widget) if hasattr(self, 'filter_panel'): self.filter_panel.reload_cb(None) else: self.reload() def reload(self, *args, **kwargs): try: self.record_save(discard_unchanged=True) except exc.ValidationError: return # self.session.clear() self.commit_inhibited = True self.clear() start_time=datetime.datetime.now() ret = sqlwidget.SqlWidget.reload(self, *args, **kwargs) self.edited_row_model_path = None self.commit_inhibited = False self.totals.compute() self.cell_entry = None end_time=datetime.datetime.now() delta=end_time-start_time msg = "Startup: %s,%s secondi" % (delta.seconds, delta.microseconds/100000) self.sb(msg, seconds=3) self.session.rollback() if self.get_selected_obj() or len(self.records): self.emit('context-changed', self.get_selected_obj() or self.records[0]) self._new_record = False return ret def record_refresh(self): """ when self.refresh is called other programs have selected the records """ self.edited_row_model_path = None # must be set *before* clear() # to inhibit selection_changed_cp self.modelproxy.fill_model(clear=True) self.totals.compute() self.treeview.columns_autosize() sqlwidget.SqlWidget.record_refresh(self) if self.get_selected_obj() or len(self.records): self.emit('context-changed', self.get_selected_obj() or self.records[0]) self._new_record = False def record_in_mask(self, widget=None): """ Open a SqlMask to show self.current if any and to follow selection. Hooks are set on the newly created mask so that validation is retained Remember to set any possible configuration of the table in an ``on_init`` hook so that it will be propagated. If there are pending modification, a save dialog is opened. This function is normally invoked by the first menu entry of the menu popped by right click on a table *view this record in a mask*. :param widget: the menu entry that invoked it, can be ignored when calling it by hand :returns SqlMask: the SqlMask widget """ from sqlkit.widgets import SqlMask, SqlTable try: quit = self.record_save(ask=True, skip_check=False) except exc.CancelledAction: quit = True if quit: self.sb(_('Unsaved data prevent opening a Mask to show the (unsaved) record')) return if hasattr(self, '_mask'): self._mask().widgets['Window'].present() return mask = SqlMask(self.mapper, session=self.session, layout=self.layout, metadata=self.metadata, dbproxy=self.dbproxy, hooks=self.hooks) mask.set_mode(self.mode, reset=True) mask.mode = '-bd' obj = getattr(self, '_clicked_obj', self.get_selected_obj()) try: del self._clicked_obj except AttributeError: pass self.select_path(obj=obj) mask.set_records([obj]) ## update value in the table, when updated in mask def update_table_treeview(mask, table): # emitting a 'row-changed', forces the update of the treeview model = self.modelproxy.modelstore path = table.get_selected_path() if path: # seems selection sometimes is lost model.emit('row-changed', path, model.get_iter(path)) record_save_id = mask.connect('record-saved', update_table_treeview, self) def disconnect_table(table, id): mask.disconnect(id) self.connect('delete-event', disconnect_table, record_save_id) ## signals to keep selection in sync def follow_selection(table, obj, mask): if mask.record_has_changed(): mask.record_save(ask=True) mask.set_records([obj]) handler_id = self.connect('context-changed', follow_selection, mask) def disconnect(mask, id): self.disconnect(id) del self._mask mask.connect('delete-event', disconnect, handler_id) self._mask = weakref.ref(mask) return mask def fkey_record_in_mask(self, widget=None, field_name=None): """ Open a SqlMask to show the fkey of this field :param widget: the menu entry that invoked it, can be ignored when calling it by hand :param field_name: the foreign key attribute name that should be displayed :returns SqlMask: the SqlMask widget """ from sqlkit.widgets import SqlMask, SqlTable if not field_name and not hasattr(self, '_field_name'): return field_name = field_name or self._field_name ftable = self.completions[field_name].table mask = SqlMask(ftable.name, session=self.session, dbproxy=self.dbproxy, metadata=self.metadata, layout_nick=self._fk_layout_nick.get(field_name, 'default'), ) mask.set_mode(self.mode, reset=True) mask.mode = '-b' obj = getattr(self, '_clicked_obj', self.get_selected_obj()) if obj: mask.set_records(pk=getattr(obj, field_name)) ## signals to keep selection in sync def follow_selection(table, obj, mask): if obj and self.get_selected_obj(): if mask.record_has_changed(): mask.record_save(ask=True) mask.set_records(pk=getattr(self.get_selected_obj(), field_name)) handler_id = self.connect('context-changed', follow_selection, mask) def disconnect(mask, id): self.disconnect(id) if hasattr(self, '_fmask'): del self._fmask mask.connect('delete-event', disconnect, handler_id) self._fmask = weakref.ref(mask) # just for debug try: del self._field_name del self._clicked_obj except AttributeError: pass return mask def set_fk_layout(self, field_name, layout_nick): """ set a layout to be used if opening a mask to edit the foreign key :param field_name: the field_nme of the column :param layout_nick: the nick name of the layout to be used a 'layout_nick' """ self._fk_layout_nick[field_name] = layout_nick def upload_image(self, action, field_name=None): """ Open a dialog to upoad a file and check if destination already exists. Setup for a copy when record will be saved """ field_name = field_name or self._field_name dialog = dialogs.OpenDialog(title=_('Upload image'), default_filename='') filename = dialog.filename if not filename: return self.initialize_record_editing(self._clicked_path) self.select_path(self._clicked_path) save_path = self.gui_fields[field_name].get_save_path( name=dialog.new_filename or filename, obj=self.current) if os.path.exists(save_path): response = self.dialog( type="ok-cancel", text=_("A file with name '%s' already exists.\nOverwrite?") % save_path, title=_('Upload name duplication')) if response == gtk.RESPONSE_CANCEL: return self.gui_fields[field_name].widget.copy_file = filename # delay the real copy self.gui_fields[field_name].widget.new_filename = dialog.new_filename self.set_value(field_name, save_path) return save_path def upload_file(self, action): print action def delete_file(self, action, field_name=None): field_name = field_name or self._field_name self.initialize_record_editing(self._clicked_path) self.set_value(field_name, None) def show_file(self, action, field_name=None): from sqlkit.layout import image_widget field_name = field_name or self._field_name self.select_path(self._clicked_path) self.initialize_record_editing(self._clicked_path, force=True, obj=self.get_obj_at_path(self._clicked_path)) # if you don't select it, the table mix up values of different rows, probably # _clicked_path gets used in wrong ways... filename = self.gui_fields[field_name].get_value(complete_path=True) if not os.path.exists(filename): self.message(text=_("File '%s' is not present in the filesystem" % filename), type='error') return if not self.gui_fields[field_name].is_image(filename): open_file(filename) return w = gtk.Window() image = image_widget.ImageWidget() image.props.width = 700 image.props.height = 700 w.add(image) w.show_all() def change_file(table, obj): if obj: try: img_path = table.gui_fields[field_name].get_value(complete_path=True) except exc.NoCurrentObjError, e: if table.records: table.select_path((0,)) img_path = table.gui_fields[field_name].get_value(complete_path=True) else: img_path = None image.set_image(img_path) change_file(self, self.current) self.connect('context-changed', change_file) def save_file(self, action, field_name=None): import shutil field_name = field_name or self._field_name self.initialize_record_editing(self._clicked_path) orig_filename = self.gui_fields[field_name].get_value(complete_path=True) dialog = dialogs.SaveDialog(default_filename=os.path.basename(orig_filename)) if dialog.filename: shutil.copyfile(orig_filename, dialog.filename) ###### Misc def get_selected_path(self): """Return the path of the selected row or None NOTE: treeselection 'changed' signal arrives when the selection change has already occurred. Use :attr:`edited_path` to get the path that is being currently edited """ selection = self.treeview.get_selection() model, rows = selection.get_selected_rows() ## we use just the first selected # >>> sel.get_selected_rows() # (, [(1,)]) if rows: return rows[0] else: return None def get_current_obj(self): """ Return the corrently edited (not just selected!!) obj or obj at path. Selection can already be elsewhere (as in on_selection_change) Read comment in set_value Return None if no selection """ path = self.edited_row_model_path if path is None: #return None return self.get_selected_obj() else: obj = self.modelproxy.modelstore[path][0] return obj def get_selected_obj(self): """Return the corrently selected obj. Return None if no selection""" path = self.get_selected_path() if path is None: return None else: obj = self.modelproxy.modelstore[path][0] if not isinstance(obj, self.mapper.class_): return None return obj def get_obj_at_path(self, path, all=False): """ Return the object at path. Return None if no selection. If the object at path is a total object (see :ref:`totals`) it will be discarder unless you use param all=True :param path: the modelproxy.modelstore path :param all: boolean: only return object of the main class (no totals) """ obj = self.modelproxy.modelstore[path][0] if isinstance(obj, self.totals.total_class): return None return obj current = property(get_current_obj) def set_current(self, pk=None, filter_by=None): """ retrieve object with primary key pk and set it as current object ie: in the row currently selected This is mainly used in completion callback for m2m relationship_mode """ ## I really need not to flush, since in this moment there is a new object in the ## row that started completion that is not ready for flushing ## more: it may be due for substitution!! path = self.get_selected_path() assert path is not None obj = self.get_record_no_flush(pk=pk, filter_by=filter_by) expired_obj = self.modelproxy.modelstore[path][0] if expired_obj in self.session: self.session.expunge(expired_obj) self.records.remove(expired_obj) ## assigning modelproxy.modelstore value will trigger 'editing-canceled' self.cell_entry.substitute = True self.cell_entry.valid = True self.modelproxy.modelstore[path][0] = obj if not (self.relationship_mode == 'm2m' and not self.m2m_editable): path = self.edited_row_model_path self.initialize_record_editing(path, force=True, obj=obj) self.records += [obj] def get_record_no_flush(self, pk=None, filter_by=None): """ Get the record in a way so that no flushing is performed """ query = self.session.query(self.mapper).autoflush(False) if pk: obj = query.get(pk) elif filter_by: obj = query.filter_by(**filter_by).one() return obj def set_opts(self, icons=True, scroll='xy', width=None): """ Modify default appearance of some features: :param scroll: eliminate scrollbars (values: x|y|xy|None) :param icons: allows to disable icons in a m2m relationship :param width: set the maximum width of the widget :type icons: boolean :type scroll: boolean """ if not icons: self.widgets['A.icons'].hide() if not scroll: self.widgets['S=tree'].set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) else: if 'x' not in scroll: xpolicy = gtk.POLICY_NEVER else: xpolicy = gtk.POLICY_AUTOMATIC if 'y' not in scroll: ypolicy = gtk.POLICY_NEVER else: ypolicy = gtk.POLICY_AUTOMATIC self.widgets['S=tree'].set_policy(xpolicy, ypolicy) if width: self.treeview.set_property('width_request', width) def export(self, menuItem, view_name='main'): """ export visible data in csv format """ def foreach_handler(model, path, iter, ret): ## NOTE: when this function return a True value, iteration terminates!!! obj = model.get_value(iter, 0) view = self.views[view_name] field_list = [c.get_data('field_name') for c in view.treeview.get_columns() if c.get_property('visible')] ## getattr(obj, f, obj): obj is used when the column does not have a 1-1 mapping ## to an attribute as happens for FunctionColumn that show a function out of the ## record ret += [[self.gui_fields[f].get_human_value(getattr(obj, f, obj)) for f in field_list ]] def get_all_rows(): ret = [] self.modelproxy.modelstore.foreach(foreach_handler, ret) return ret dialog = ExportDialog(get_all_rows, title=_("Export in csv file"), default_filename="%s-export.csv" % self.nick) return def select_path(self, path=None, obj=None): """ Select path ``path``. If path is null and obj is not null try finding path at which is obj :param path: the path to be selected or None :param obj: if path is None, the object to be searched for and selected """ return self.views['main'].select_path(path=path, obj=obj) def add_record(self, obj): """ Add record to the model and setup all what is needed :param obj: an object of type ``table.mapper.class_`` """ self.records += [obj] try: self.modelproxy.modelstore.append([obj]) except: self.modelproxy.modelstore.append(None, [obj]) self.session.add(obj) # self._record_new self.totals.compute() def len_model(self, model=None): a = [0] if not model: model = self.modelproxy.modelstore def count(model, path, iter, counter): counter[0] += 1 model.foreach(count, a) return a[0] def add_row_filter(self, func): """ Add a filter that selects which rows should be displayed :param func: the function that will be called with the obj as argument and must return a bool """ self.modelproxy._filter_rows = True self.modelproxy._check_func = func #### completion def set_editable(self, editable=True): """ Turn this table into a normal editable table. A tables is not normally editable if it has ``relationship_mode = m2m`` :param editable: Only meaningful for table that represent an m2m relationship: turns the table from non editable to editable, read the explanation in :ref:`relationships` :type editable: boolean """ self.m2m_editable = editable for field in self.gui_fields: field.widget.add_completion(editable=editable) class ExportDialog(dialogs.SaveDialog): FILTER_FILES = ( ( _('Csv files'), '*.csv'), ( _('All files'), '*.*'), ) DELIMITER = ',' QUOTE = None DIALECT = 'excel' def __init__(self, get_rows_func, *args, **kwargs): """ A dialog to export data into a csv file :param get_rows_func: a function to be called by csv.writer.writerows :param default_filename: the default """ self.get_rows_func = get_rows_func dialogs.SaveDialog.__init__(self, *args, **kwargs) def write(self): import csv filename = self.dialog.get_filename() f = open(filename, "wb") writer = csv.writer(f, dialect=self.DIALECT, delimiter=self.DELIMITER, quoting=self.QUOTE or csv.QUOTE_MINIMAL) writer.writerows(self.get_rows_func()) f.close() sqlkit-0.9.5/sqlkit/widgets/table/columns.py0000644000175000017500000015716311714210425020502 0ustar sandrosandro# Copyright (C) 2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Classes that implement creation of column and cell renderers This should allow to be as generic as possible while adding new renderers Each Column widget need a 'master' argument that is the SqlWidget in editing_started_cb and edited_cb to set state variables on SqlWidget .. autoclass:: View :members: add_column, select_path, start_editing """ import gtk import gobject import pango from decimal import Decimal import cell_renderers, utils as tv_utils from sqlkit import debug as dbg, _, exc, fields from sqlkit.layout import widgets from sqlkit.misc.utils import str2list from sqlkit.db.utils import DictLike COLUMN_MENU = ''' ''' LBL_HOOVER_STYLE = gtk.gdk.color_parse('red') class GenericColumn(object): RENDERER = gtk.CellRendererText RESIZE = True REORDER = True CLICK = True EXPAND = True MIN_DEFAULT_WIDTH = 4 MAX_DEFAULT_WIDTH = 30 PROPERTY = 'text' editable = False __metaclass__ = None if not dbg.debug else dbg.LogTheMethods def __init__(self, master, field_name, title, field=None): """ Generic column with cell render setup :param master: the SqlWidget or which this Column is created, maybe a SqlMask if the Column is created for FilterPanel :param field_name: the field_name used to add the field_name into master.gui_fields :param title: the title used for the column :param field: the field (if missing is looked for in master.gui_fields[field_name] """ self.master = master if not field: field = self.master.gui_fields[field_name] self.field = field self.title = title self.field_name = field_name self.column = self.setup_column(title) cell = self.setup_cell_renderers() self.column.set_cell_data_func(cell, self.cell_data_func_cb, self.PROPERTY) self.column.cells_data_func = [(cell, self.cell_data_func_cb, self.PROPERTY) ] def setup_column(self, title): """ setup column """ column = gtk.TreeViewColumn(title.replace('_',' ')) column.set_data('field_name', self.field_name) # used when reordering columns column.set_resizable(self.RESIZE) column.set_expand(self.EXPAND) column.set_reorderable(self.REORDER) column.set_clickable(self.CLICK) column.set_expand(self.is_expandable()) column.connect('clicked', self.clicked_cb) self.add_header_widget(column) return column def setup_cell_renderers(self, cell_renderer=None): """ setup cell renderers """ cell = cell_renderer or self.RENDERER() self.column.pack_start(cell, True) self.cell_set_property(cell) ## connect cell.connect('edited', self.edited_cb) cell.connect('editing-started', self.editing_started_cb) cell.connect('editing-canceled', self.editing_canceled_cb) return cell ### callback def edited_cb(self, cell, path, new_text): """ callback on signal 'edited' on cell_renderer. """ ## NOTE: modelstore.clear will trigger text_edited_cb!!! if self.master.deleting_row: return True if self.master.edited_row_model_path is None: return True if self.master.cell_entry and self.master.cell_validate(): self.master.cell_entry = None self.on_activate() else: return True def editing_started_cb(self, cell, editable, path): self.master.currently_edited_field_name = self.field_name self.master.initialize_record_editing(path) return True def cell_data_func_cb(self, column, cell, model, iter, prop_name): """ function set by set_cell_data_func gets the attribute from the record and returns it """ if not column.get_visible(): return obj = model.get_value(iter, 0) ## when the column is not representing a db-column (eg. is a python function) ## we pass the object itself to the function if self.field.persistent: value = getattr(obj, self.field_name) else: value = obj if isinstance(obj, self.master.mapper.class_): value = self.field.clean_value(value) cell.set_property('visible', True) ## DB RECORDS if isinstance(obj, self.master.mapper.class_): cell.set_property('visible', True) formatted_value = self.field.format_value(value) cell.set_property(prop_name, formatted_value) cell.set_property('foreground-set', False) cell.set_property('cell-background-set', False) cell.set_property('editable', self.editable) ## TOTALS elif hasattr(self.master, 'totals') and isinstance(obj, self.master.totals.total_class): if self.field_name in self.master.totals.totals: if self.field.persistent: formatted_value = self.field.format_value(value) obj.set_value_and_colors(cell, formatted_value, self.field_name) else: value = getattr(obj, self.field_name, None) obj.set_value_and_colors(cell, self.field.format_value(value), self.field_name) else: cell.set_property('visible', False) cell.set_property('editable', False) ## HEADERS else: cell.set_property(prop_name, value) cell.set_property('editable', False) def editing_canceled_cb(self, widget): """ clean-up of cell_entry and restore value """ if self.master.cell_entry and self.master.cell_entry.substitute: # assigning a value to modelstore, triggers 'editing-canceled' self.master.cell_entry.substitute = False return self.master.set_value(self.field_name, self.field.initial_value) self.master.cell_entry = None self.master.sb(_("Editing canceled. Restoring original value"), seconds=3) def editable_connects(self, editable): """ add here possible connect """ editable.connect('activate', self.on_activate) return def on_activate(self, editable=None): # this is no longer connected to 'activate' signal since I prefere the changed value # be available. I prefere to keep similarity with what happens in a normal # entry where 'activate' means "press Enter". Pressing enter here make it edited. # That means it will also be triggered via Tab... self.master.run_hook('on_activate', self.field, field_name=self.field_name) def clicked_cb(self, column): """ popup the menu on the header of the column """ event = gtk.gdk.Event(gtk.gdk.BUTTON_PRESS) event.button = 1 treeview = column.get_tree_view() view = treeview.get_data('view') view.menu.popup_col_menu(event, treeview, self.field_name) return True #### auxiliary def set_editable(self, editable): """ set cells of this column editable or not :param editable: boolean """ self.editable = editable for cell in self.column.get_cell_renderers(): cell.set_property('editable', editable) def cell_set_property(self, cell): """ set cell properties """ if self.field.editable and 'u' in self.master.mode: self.set_editable(True) cell.set_property('editable-set', True) else: cell.set_property('editable', False) def add_header_widget(self, column): """ Add a header that accepts tooltips and bold labels for mandatory columns """ # Label label_text, help_text = self.master.label_map.get(self.field_name, (None, None)) label_text = self.master.get_label(label_text or self.title) label = gtk.Label() label.set_markup(label_text) ## bold italic if self.field.persistent and not self.field.nullable and not self.field.default: label.set_markup('%s' % label_text) # Tooltip if help_text: label.set_tooltip_text(help_text) # Header column.set_widget(label) label.show() def realize_cb(self, widget): watch = gtk.gdk.Cursor(gtk.gdk.HAND2) widget.window.set_cursor(watch) def is_expandable(self): """ make expandable only Strings with n_chars > 20 or Text """ # if self.master.is_text(self.field_name): if isinstance(self.field, fields.TextField): return True if isinstance(self.field, fields.VarcharField): #if self.master.mapper_info.fields[self.field_name]['length'] > 20: if self.field.length and self.field.length > 20: return True return False def get_width(self): """ Find the width looking in default database info or in col_width """ if self.master.col_width and self.field_name in self.master.col_width: width = self.master.col_width.get(self.field_name, ) else: width = self.field.length if not width or width < self.MIN_DEFAULT_WIDTH: width = self.MIN_DEFAULT_WIDTH if width > self.MAX_DEFAULT_WIDTH: width = self.MAX_DEFAULT_WIDTH return width def __repr__(self): return "<%s: %s >" % (self.__class__.__name__, self.field_name) class BooleanColumn(GenericColumn): RENDERER = gtk.CellRendererToggle RESIZE = False EXPAND = False def set_editable(self, editable): """ set cells of this column editable or not :param editable: boolean """ self.editable = editable for cell in self.column.get_cell_renderers(): cell.set_property('activatable', editable) def setup_cell_renderers(self, cell_renderer=None): """ setup different cell_cb according to nullable value """ cell = cell_renderer or self.RENDERER() self.cell_set_property(cell) self.column.pack_start(cell, True) self.column.set_data('cell', cell) ## connect cell.connect('editing-canceled', self.editing_canceled_cb) # ??? cell.connect('toggled', self.toggled_signal_cb, self.master.modelproxy.modelstore) self.column.set_cell_data_func(cell, self.cell_data_func_cb) self.column.cells_data_func = [(cell, self.cell_data_func_cb)] cell.fixed_width = True return cell def cell_set_property(self, cell): if self.field.editable: self.set_editable(True) else: self.set_editable(False) def toggled_signal_cb(self, cell, path, model): """ callback that cicle into True/False/(inconsistent if nullable) """ self.master.currently_edited_field_name = self.field_name self.master.initialize_record_editing(path) obj = self.master.modelproxy.modelstore[path][0] status = getattr(obj, self.field_name) if self.field.nullable: # status may be True/False/None - I want to cicle if status == True: status = False elif status == False: status = None elif status == None: status = True else: status = not status #self.field.set_value(status, initial=False, obj=obj) self.master.set_value(self.field_name, status, initial=False) return True def cell_data_func_cb(self, column, cell, model, iter, prop_name): """ function set by set_cell_data_func for nullable boolean """ if not column.get_visible(): return obj = model.get_value(iter, 0) value = getattr(obj, self.field_name) ## DB RECORDS if isinstance(obj, self.master.mapper.class_): cell.set_property('active', value) cell.set_property('visible', True) cell.set_property('activatable', self.editable) if value is None: cell.set_property('inconsistent', True) else: cell.set_property('inconsistent', False) ## TOTALS elif isinstance(obj, self.master.totals.total_class): cell.set_property('visible', False) cell.set_property('activatable', False) class VarcharColumn(GenericColumn): """ A renderer that expands only large fields and that centers text if it's little Cell renderer is choosen according to VarChar or Text """ __metaclass__ = dbg.LogTheMethods def setup_cell_renderers(self, cell_renderer=None): width = self.get_width() ## look for a possibly defined width ## choose cell renderer if width: cell = cell_renderers.CellRendererVarchar(width) cell.fixed_width = True else: cell = gtk.CellRendererText() GenericColumn.setup_cell_renderers(self, cell_renderer=cell) cell.set_property('ellipsize', pango.ELLIPSIZE_END) cell.set_data('width', width) self.set_alignment(cell) return cell def set_alignment(self, cell): """ Set alignment of text in the cell """ if self.field.length: try: ## if field has less then 4 chars, position it in the middle if int(self.field.length) <= 4: cell.set_property('xalign', 0.5) except: pass return cell def editing_started_cb(self, cell, editable, path): """ prepare the editable, completion and validation """ self.master.currently_edited_field_name = self.field_name self.master.cell_entry = CellEntry(editable, cell, path, self.field_name) editable.connect('remove-widget', self.remove_widget_cb) editable.connect('key-press-event', self.master.keypress_event_cb) editable.connect('focus-out-event', self.focus_out_cb, cell, path) self.editable_connects(editable) self.master.fkey_value = editable.get_text() if hasattr(self.master.field_widgets[self.field_name], 'completion'): completion = self.master.field_widgets[self.field_name].completion #if completion and not self.master.is_text(self.field_name): if completion and not isinstance(self.field, fields.TextField): completion.add_completion(editable) completion.add_callbacks(editable) ## MAX LENGTH: not enforce for fkey as the field is not the real value but the shown one if not self.field.fkey: self.master.field_widgets[self.field_name].field.widget.set_max_length() self.master.initialize_record_editing(path) self.master.commit_inhibited = False return True def remove_widget_cb(self, widget): """ If the call has invalid data we don't want to remove the widget so that we can edit the wrong data rather then loosing what was already typed """ if self.master.cell_entry and not self.master.cell_entry.is_valid(): widget.stop_emission('remove-widget') return True self.master.cell_entry = None def focus_out_cb(self, widget, event, cell, path): if self.master.cell_entry: widget.activate() class MultilineColumn(VarcharColumn): """ A renderer able to show and edit in multiline mode. Very basic... """ MIN_DEFAULT_WIDTH = 40 def setup_cell_renderers(self, cell_renderer=None): width = self.get_width() cell = cell_renderers.MultilineCellRenderer(self.master) GenericColumn.setup_cell_renderers(self, cell_renderer=cell) cell.set_property('ellipsize', pango.ELLIPSIZE_END) cell.set_data('width', width) return cell def remove_widget_cb(self, widget): """ If the call has invalid data we don't want to remove the widget so that we can edit the wrong data rather then loosing what was already typed """ if self.master.cell_entry: self.master.cell_validate() self.master.cell_entry = None def focus_out_cb(self, widget, event, cell, path): if self.master.cell_entry: self.master.cell_validate() cell.emit("edited", cell.editor.get_data("path"), cell.editor.get_text()) cell.editor.remove_widget() def editable_connects(self, editable): pass class NumericColumn(VarcharColumn): """ A renderer similar to varchar but with different expand options and with right justification """ def get_width(self): if self.master.col_width and self.field_name in self.master.col_width: width = self.master.col_width.get(self.field_name, ) else: if self.field.type == int: width = 8 else: width = 10 return width def set_alignment(self, cell): cell.set_property('xalign', 1.0) def editable_connects(self, editable): #VarcharColumn.on_activate(self, editable) editable.connect('key-press-event', self.master.digits_check_input_cb ) class FKeyColumn(VarcharColumn): """ A renderer that shows the value instead of the id """ MIN_DEFAULT_WIDTH = 15 def cell_set_property(self, cell): VarcharColumn.cell_set_property(self, cell) cell.set_property('foreground', 'navyblue') def cell_data_func_cb(self, column, cell, model, iter, prop_name): """ cell_data_function used by column that have fkey """ if not column.get_visible(): return obj = model.get_value(iter, 0) if not obj: return cell.set_property('visible', True) ## DB RECORDS if isinstance(obj, self.master.mapper.class_): fkey_value = getattr(obj, self.field_name) if fkey_value is not None: path = model.get_path(iter) value = self.field.lookup_value(fkey_value) cell.set_property('text', value) else: cell.set_property('text', '') cell.set_property('editable', self.editable) ## TOTALS elif hasattr(self.master, 'totals') and isinstance(obj, self.master.totals.total_class): cell.set_property('text', '') cell.set_property('editable', False) ## HEADER else: cell.set_property('text', getattr(obj, self.field_name)) cell.set_property('editable', False) def editing_started_cb(self, cell, editable, path): VarcharColumn.editing_started_cb(self, cell, editable, path) model = self.column.get_tree_view().get_model() obj = model.get_value(model.get_iter(path), 0) ## I trust this value as it comes from the obj self.master.cell_entry.value = getattr(obj, self.field_name) class DateColumn(VarcharColumn): """ Will have bindings to change the date in a faster way possibly popping a calendar """ MIN_DEFAULT_WIDTH = 10 def editable_connects(self, editable): pass #editable.connect('key-press-event') class DateTimeColumn(DateColumn): MIN_DEFAULT_WIDTH = 16 def editable_connects(self, editable): pass #editable.connect('key-press-event') class EnumColumn(VarcharColumn): RENDERER = gtk.CellRendererText RESIZE = True REORDER = True CLICK = True EXPAND = False MIN_DEFAULT_WIDTH = 4 MAX_DEFAULT_WIDTH = 30 PROPERTY = 'text' editable = False def cell_data_func_cb(self, column, cell, model, iter, prop_name): """ cell_data_function used by column that have fkey """ if not column.get_visible(): return obj = model.get_value(iter, 0) if not obj: return cell.set_property('visible', True) ## DB RECORDS if isinstance(obj, self.master.mapper.class_): value = getattr(obj, self.field_name) if value is not None: show_value = self.field.lookup_value(value) cell.set_property('text', show_value) else: cell.set_property('text', '') cell.set_property('editable', self.editable) ## TOTALS elif hasattr(self.master, 'totals') and isinstance(obj, self.master.totals.total_class): cell.set_property('text', '') cell.set_property('editable', False) ## HEADER else: cell.set_property('text', getattr(obj, self.field_name)) cell.set_property('editable', False) class ImageColumn(GenericColumn): RENDERER = gtk.CellRendererPixbuf RESIZE = True REORDER = False CLICK = True EXPAND = False MIN_DEFAULT_WIDTH = 4 MAX_DEFAULT_WIDTH = 30 PROPERTY = 'pixbuf' editable = False def __init__(self, master, field_name, title, field=None): GenericColumn.__init__(self, master, field_name, title, field=None) try: master.treeview.props.has_tooltip = True master.treeview.connect('query-tooltip', self.return_image_path, ) except AttributeError: pass def return_image_path(self, treeview, x, y, keyboard_mode, tooltip): "Set a tooltip with the file/image name" try: path, column, x_in_cell, y_in_cell = treeview.get_path_at_pos(x,y) except TypeError: return idx = path[0] if not idx: #header return path = tuple([idx-1]) model = treeview.get_model() obj = model.get_value(model.get_iter(path), 0) file_path = getattr(obj, self.field_name) if file_path and (column.get_data('field_name') == self.field_name): tooltip.set_text(file_path) return True def set_editable(self, editable): pass def cell_set_property(self, cell): pass def setup_cell_renderers(self, cell_renderer=None): """ setup cell renderers """ cell = cell_renderer or self.RENDERER() self.column.pack_start(cell, True) self.cell_set_property(cell) return cell def cell_data_func_cb(self, column, cell, model, iter, prop_name): """ function set by set_cell_data_func gets the attribute from the field and render an image thumbnail """ if not column.get_visible(): return obj = model.get_value(iter, 0) ## when the column is not representing a db-column (eg. is a python function) ## we pass the object itself to the function if self.field.persistent: value = getattr(obj, self.field_name) else: value = obj if isinstance(obj, self.master.mapper.class_): value = self.field.clean_value(value) cell.set_property('visible', True) ## DB RECORDS if isinstance(obj, self.master.mapper.class_): cell.set_property('visible', True) if value: if not self.field.exists(value): cell.set_property('pixbuf', None) cell.set_property('stock-id', 'gtk-missing-image') cell.set_data('thumbnail_path', None) return if self.field.is_image(value): thumbnail_path = value and self.field.get_thumbnail(value, complete_path=True) or None if thumbnail_path: if cell.get_data('thumbnail_path') != thumbnail_path: pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail_path) if thumbnail_path else None cell.set_property('pixbuf', pixbuf) cell.set_property('stock-id', None) cell.set_data('thumbnail_path', thumbnail_path) else: cell.set_property('pixbuf', None) cell.set_property('stock-id', gtk.STOCK_MISSING_IMAGE) cell.set_data('thumbnail_path', thumbnail_path) else: cell.set_property('pixbuf', None) cell.set_property('stock-id', 'gtk-file') cell.set_data('thumbnail_path', None) else: cell.set_property(prop_name, None) cell.set_data('thumbnail_path', None) cell.set_property('stock-id', None) ## TOTALS elif hasattr(self.master, 'totals') and isinstance(obj, self.master.totals.total_class): cell.set_property(prop_name, None) cell.set_data('thumbnail_path', None) cell.set_property('stock-id', None) ## HEADERS else: cell.set_property(prop_name, None) cell.set_data('thumbnail_path', None) cell.set_property('stock-id', None) class CellEntry(object): """ An object to store info on editable status Mainly needed to check if validation is required with FKey fields and m2m nested tables Completion sets valid to True, any manual edit invalidates, so that a field.validate() is triggered. Any movement (click of Tab or Down) away from a CellRenderer is inhibited if * a cell exists * the cell is not valid CellEntry is destroyed within 'remove-widget' signal callback of the editable FIXME: should probably be simplified being it only needed for FKey Columns/m2m """ def __init__(self, editable, cell, path, field_name): self.entry = editable self.cell = cell self.path = path self.field_name = field_name self.valid = True self.text = editable.get_text() if isinstance(editable, gtk.Entry): # Multiline does not have 'canged' and does not need invalidating self.entry.connect('changed', self.on_editable_change) else: ## this is just a hack to force cell_validate() when browsing away self.valid = False self.substitute = False def on_editable_change(self, widget): self.valid = False def is_valid(self): valid = self.valid or self.text == self.entry.get_text() return valid def get_text(self): return self.entry.get_text() def __repr__(self): return "" % (self.valid, self.get_text()) class ColumnMenuProxy(object): def __init__(self, master, view): self.master = master self.view = view #### column menu def popup_col_menu(self, ev, treeview, field_name): from sqlkit.layout.misc import StockMenuItem try: self.master.widgets['M=popup'].destroy() except: pass menu = self.master.widgets['M=popup'] = gtk.Menu() field = self.master.gui_fields[field_name] ## add to filter field_name_localized = self.master.get_label(field_name, gtk_label=True) if field.persistent: ## TIP: menu entry to add a filter in the filter panel label_str = _("Add a filter on '%s'") % field_name_localized item = StockMenuItem(label=label_str, stock='gtk-find') # item.connect('activate', self.filter_panel.add, ev, field_name ) item.connect('activate', self.master.filter_panel.add, ev, field_name, self.master ) menu.append(item) ## sort item = gtk.ImageMenuItem('gtk-sort-ascending') item.connect('activate', self.reload_sort_cb, field_name, 'ASC' ) if field.persistent: item.set_tooltip_text(_("Sort on this column reloading from the database, " + \ "you can use 's' to sort locally")) else: item.set_tooltip_text(_("Sort on this column locally (w/o touching the database)")) menu.append(item) item = gtk.ImageMenuItem('gtk-sort-descending') item.connect('activate', self.reload_sort_cb, field_name, 'DESC' ) menu.append(item) ## hide # TIP: column menu opt item = StockMenuItem(label=_("Hide this column"), stock='gtk-close') item.connect('activate', self.hide_fields_cb, field_name, self.view ) menu.append(item) ## totals if field.type in (int, float, Decimal) and not isinstance(field, fields.ForeignKeyField): # TIP: column menu opt item = gtk.MenuItem(label=_("Create total")) item.connect('activate', self.add_total_cb, field_name ) menu.append(item) menu.append(gtk.SeparatorMenuItem()) ## subtotals if isinstance(field, fields.DateField): self.menu_add_date_total_break(menu, field_name) else: # TIP: column menu total item = gtk.MenuItem(label=_("Subtotal on %s") % (field_name_localized)) item.connect('activate', self.add_subtotal_cb, field_name ) menu.append(item) ## info # TIP: column menu opt item = gtk.ImageMenuItem('gtk-info') item.connect('activate', self.master.show_field_info, field_name ) menu.append(item) menu.show_all() menu.popup(None, None, None, ev.button, ev.time) #menu.popup(None, None, None, 1, 1) def fill_menu_hide_fields(self): """ Add menu for showing/hiding field columns """ if not 'M=modify' in self.master.widgets: return menu = self.master.widgets['M=modify'] # TIP: modify menu entry item_show = gtk.MenuItem(label=_("Show field")) item_hide = gtk.MenuItem(label=_("Hide field")) menu.append(item_show) menu.append(item_hide) self.menu_show = gtk.Menu() self.menu_hide = gtk.Menu() item_show.set_submenu(self.menu_show) item_hide.set_submenu(self.menu_hide) for field_name in self.master.field_list: field_name_localized = self.master.get_label(field_name, gtk_label=True) m = gtk.MenuItem(field_name_localized) m.connect('activate', self.hide_fields_cb, field_name) self.menu_hide.append(m) item_hide.set_submenu(self.menu_hide) menu.show_all() def hide_fields_cb(self, menuItem, field_name, view): view.hide_fields(field_name) def menu_add_date_total_break(self, menu, field_name): def add_break_date_cb(menu_item, field_name, period): self.master.totals.add_date_break(field_name, period) periods = [('day',_('day')), ('week',_('week')), ('month',_('month')), ('quarter',_('quarter')), ('year',_('year'))] for period, trad in periods: item = gtk.MenuItem(label=_("Subtotals by %s") % trad) item.connect('activate', add_break_date_cb, field_name, period) menu.append(item) def add_total_cb(self, menu_item, field_name): try: self.master.totals.add_total(field_name, quiet=True) except AttributeError: # mmh this is from filter panel! from . import totals self.master.totals = totals.Totals(self.master, treeview=self.master.filter_panel.view.treeview) self.master.totals.add_total(field_name) def add_subtotal_cb(self, menu_item, field_name): self.master.totals.add_break(field_name) def menu_add_toggle_column(self, field_name, view): """ Add a menu entry to show columns in the menu 'modify' or change it's visibility according to real state of the column. A column can be toggled back using the view menu (left click on the TreeView) """ action_name = 'field_%s' % field_name action = view.ui_manager.get_action('/TreePopup/Columns/ShowColumns/%s' % action_name) if not action: view.actiongroup_view.add_actions([ (action_name, None, self.master.get_label(field_name, gtk_label=True), None, None, view.show_column,), ], field_name) menu_def = ''' ''' % action_name view.ui_manager.add_ui_from_string(menu_def) else: action.set_visible(not action.get_visible()) view.hide_column(field_name) def reload_sort_cb(self, widget, field_name, direction): from sqlkit.db.utils import tables, get_description if self.master.relationship_leader or field_name not in self.master.mapper_info: ## related table are ordered locally via modelproxy.order_by if direction == "DESC": field_name = "-" + field_name self.master.modelproxy.order_by(field_name) else: ## main tables are ordered in the db, a local order can be obtained ## via 's' (sort) key press self.master.modelproxy.order_by(None) order_by = field_name if self.master.is_fkey(field_name): ftable = getattr(self.master.mapper.c, field_name ).foreign_keys.copy().pop().column.table fdescr = get_description(ftable, attr='description') order_by = "%s__%s" % (field_name, fdescr) if direction == 'DESC': order_by += ' DESC' self.master.order_by = order_by self.master.reload_cb(None) class View(object): """ A View is a set of columns of the SqlTable's that offers a (possibly partial) view of the model. Each SqlTable has one mandatory view named ``main`` and possibly other views. Attribute :attr:`sqlkit.widgets.SqlTable.views` (a sqlkit.utils.Container) stores them. It can be used to provide different way to look at the same data (e.g. different cell-renderers) or different columns for long ones """ def __init__(self, master, name, treeview=None, field_list=None, cell_renderers=None, ro=False): """ :param master: the SqlTable this view belongs to :param treeview: the treeview where the columns live :param cell_renderers: a dict of cell_renderers for each column :param ro: make it read-only (default: False) """ # assert field_list is not None, "Field_list cannot be None" assert treeview is not None, "You need to set the Treeview" self.master = master self.name = name self.treeview = treeview self.read_only = ro treeview.set_data('view', self) self.field_list = str2list(field_list) self.tvcolumns = {} self.columns = {} self.cell_renderers = {} if self.field_list: self.master.setup_field_validation(self.field_list) self.setup_columns() if not name in ('main',): ## for 'main' I do that later so that master.actiongroup_* will be ready self.create_column_menu() if hasattr(self.master, 'cursor_changed_cb'): self.treeview.connect('cursor-changed', self.master.cursor_changed_cb, self) self.treeview.connect('key-press-event', self.master.keypress_event_cb, self) self.treeview.connect('button-press-event', self.master.button_press_event_cb, self) self.treeview.set_enable_search(False) self.treeview.show_all() self.treeview.set_rules_hint(True) # set selection mode treeselection = self.treeview.get_selection() treeselection.set_mode(gtk.SELECTION_SINGLE) # callback for selection changes if name == 'main': treeselection.connect('changed', self.master.selection_changed_cb) ## sync the different selections treeselection.connect('changed', self.selection_changed_cb, self) treeview.connect('row-collapsed', self.row_collapsed_cb, self) treeview.connect('row-expanded', self.row_expanded_cb, self) treeview.connect('key-press-event', self.keypress_event_cb) def keypress_event_cb(self, wdg, event, view=None): pre = 'tree' ksym = gtk.gdk.keyval_name(event.keyval) mods = ['key_' + pre] ## mod2 gets in the way when you press the NumLock: I don't want ## to depend on that, as I'm not usng it!!! for prefix, mask in [('ctrl', gtk.gdk.CONTROL_MASK), ('shift', gtk.gdk.SHIFT_MASK), ('alt', gtk.gdk.MOD1_MASK), #('mod2', gtk.gdk.MOD2_MASK), ('mod3', gtk.gdk.MOD3_MASK), ('mod4', gtk.gdk.MOD4_MASK), ('mod5', gtk.gdk.MOD5_MASK),]: if event.state & mask: mods.append(prefix) ksym = '_'.join(mods + [ksym]) if ksym in dir(self): return getattr(self, ksym)(wdg, event) def key_tree_b(self, treeview, event): "brake: sort the treeview on this column and add a subtotal for this field" field_name = self.get_field_name_from_pointer(treeview) if self.master.modelproxy._order_list == [(field_name, True)]: field_name = "-" + field_name self.master.modelproxy.order_by(field_name) self.master.totals.add_break(field_name) def key_tree_shift_B(self, treeview, event): "brake: get rid of subtotals" field_name = self.get_field_name_from_pointer(treeview) if self.master.modelproxy._order_list == [(field_name, True)]: field_name = "-" + field_name self.master.modelproxy.order_by(field_name) self.master.totals.break_func = None self.master.totals.compute() def key_tree_f(self, treeview, event): "filter: add a filter for this column" field_name = self.get_field_name_from_pointer(treeview) if field_name and field_name in self.master.mapper_info: self.master.filter_panel.add(None, None, field_name, self.master) def key_tree_h(self, treeview, event): "hide: hide this column" field_name = self.get_field_name_from_pointer(treeview) self.hide_fields(field_name) def key_tree_s(self, treeview, event): "sort: sort the treeview on this column" field_name = self.get_field_name_from_pointer(treeview) if self.master.modelproxy.get_order() == (field_name, True): field_name = "-" + field_name self.master.modelproxy.order_by(field_name) def key_tree_t(self, treeview, event): "totals: add total to the column" field_name = self.get_field_name_from_pointer(treeview) self.menu.add_total_cb(None, field_name) def key_tree_v(self, treeview, event): """toggle row visibility if a function has been set via :meth:`sqlkit.common.SqlTable.add_row_filter` """ self.master.modelproxy._filter_rows = not self.master.modelproxy._filter_rows self.master.record_refresh() def key_tree_shift_Z(self, treeview, event): """zoom-fit I would have preferred Control-z but it's kidnapped by global action shortcut """ self.on_zoom_fit(self) def get_field_name_from_pointer(self, treeview=None): x, y, modifier_state = self.treeview.window.get_pointer() try: path, column, x_in_cell, y_in_cell = self.treeview.get_path_at_pos(x,y) #iter = self.master.modelproxy.modelstore.get_iter(path) field_name = column.get_data('field_name') return field_name except TypeError, e: return def selection_changed_cb(self, treeview, view): """ used to keep selections in sync """ for view in self.master.views: if view != self: view.select_path(self.get_selected_path()) def row_expanded_cb(self, treeview, iter, path, view): """ used to keep selections in sync """ for view in self.master.views: if view != self: view.treeview.expand_row(path, True) def row_collapsed_cb(self, treeview, iter, path, view): """ used to keep selections in sync """ for view in self.master.views: if view != self: view.treeview.collapse_row(path) def set_field_list(self, field_list, ): """ Set field of visible columns. Accepts both a list or a string (space or comma separated) :param field_list: the field name list :param view: the view for which we set the field_list """ from copy import copy field_list = str2list(field_list) orig_field_list = copy(self.field_list) for field_name in field_list: if field_name in self.master.mapper_info.fields: ## is already known, just make it visible self.tvcolumns[field_name].set_property('visible', True) else: raise NotImplementedError("Column %s is not kown in self.mapper_info.fields" % field_name) for field_name in orig_field_list: if field_name not in field_list: self.tvcolumns[field_name].set_property('visible', False) prev = None for field_name in field_list: self.treeview.move_column_after(self.tvcolumns[field_name], prev) prev = self.tvcolumns[field_name] self.field_list = field_list def hide_fields(self, field_name_list): """ Hide a column and add the entry in the menu to make it show up again :param field_name_list: may be a list or a string (in which case it is split with, and spaces) :param view: the view for which we set the list of hidden fields """ field_name_list = str2list(field_name_list) if len(field_name_list) == 0: field_name_list = self.hidden_fields self.hidden_fields = field_name_list if not self.tvcolumns: return for field_name in field_name_list: self.menu.menu_add_toggle_column(field_name, self) def on_zoom_fit(self, widget=None, *args): tv_utils.zoom_to_fit(self.treeview, resize=True) def fix_expandable_columns(self): """ If no column is expandable, make all columns expandable: it's very ugly to see just the last one expand """ fix_it = True for key, col in self.tvcolumns.iteritems(): if col.get_property('expand'): fix_it = False if fix_it: for key, col in self.tvcolumns.iteritems(): col.set_property('expand', True) def hide_column(self, field_name): self.tvcolumns[field_name].set_property('visible', False) self.master.sb(_("Right click on this table to show the column again"), seconds=10) def show_column(self, item, field_name): self.menu.menu_add_toggle_column(field_name, self) self.tvcolumns[field_name].set_property('visible', True) def create_column_menu(self): from sqlkit.widgets.common import uidescription self.ui_manager = gtk.UIManager() self.ui_manager.connect('connect-proxy', self.master.uimanager_connect_proxy) self.accel_group = self.ui_manager.get_accel_group() if self.master.is_toplevel(): self.master.widgets['Window'].add_accel_group(self.accel_group) self.actiongroup_view = gtk.ActionGroup('View') self.actiongroup_view.add_actions([ ('ExportView', None, _('Export'), None, _('Export these data into csv format'), self.master.export), ], self.name) self.actiongroup_view.add_actions([ ('Zoom-fit', gtk.STOCK_ZOOM_FIT, None, 'z', _('Adapt width of columns to data'), self.on_zoom_fit), ('ShowColumns', None, _('Show field')), # menuitem ('HideColumns', None, _('Hide field')), # menuitem ]) try: self.ui_manager.insert_action_group(self.master.actiongroup_table, 10) self.ui_manager.insert_action_group(self.master.actiongroup_insert, 12) self.ui_manager.insert_action_group(self.master.actiongroup_delete, 13) self.ui_manager.add_ui_from_string(uidescription.TREEPOPUP) except AttributeError: pass self.ui_manager.insert_action_group(self.actiongroup_view, 14) self.ui_manager.add_ui_from_string(COLUMN_MENU) self.menu = ColumnMenuProxy(self.master, self) def pop_menu(self, treeview, event, obj, field_name): ## enable/disable menu entries action = self.ui_manager.get_action('/TreePopup/MaskView') action.set_sensitive(obj and True or False) action_fk = self.ui_manager.get_action('/TreePopup/MaskViewFKey') if field_name and self.master.gui_fields[field_name].fkey: action_fk.set_visible(True) label = self.master.get_label(field_name, gtk_label=True) action_fk.set_property('label', _("View '%s' in a Mask") % label) else: action_fk.set_visible(False) action_upload_image = self.ui_manager.get_action('/TreePopup/UploadImage') if obj and field_name and self.master.is_image(field_name): action_upload_image.set_visible(True) # don't show missing images... action_show_image = self.ui_manager.get_action('/TreePopup/ShowFile') filename = getattr(obj, field_name) if obj and filename and self.master.gui_fields[field_name].exists(filename): action_show_image.set_sensitive(True) else: action_show_image.set_sensitive(False) else: action_upload_image.set_visible(False) action_new_child = self.ui_manager.get_action('/TreePopup/NewChild') if self.master.modelproxy.is_tree(): new = self.ui_manager.get_action('/TreePopup/New').get_visible() action_new_child.set_visible(new) else: action_new_child.set_visible(False) action_delete_file = self.ui_manager.get_action('/TreePopup/DeleteFile') action_show_file = self.ui_manager.get_action('/TreePopup/ShowFile') action_save_file = self.ui_manager.get_action('/TreePopup/SaveFile') if obj and field_name and (self.master.is_image(field_name) or self.master.is_file(field_name)) and \ getattr(obj, field_name): action_delete_file.set_visible(True) action_show_file.set_visible(True) action_save_file.set_visible(True) else: action_delete_file.set_visible(False) action_show_file.set_visible(False) action_save_file.set_visible(False) action_upload_file = self.ui_manager.get_action('/TreePopup/UploadFile') if field_name and self.master.is_file(field_name): action_upload_file.set_visible(True) else: action_upload_file.set_visible(False) action_delete = self.ui_manager.get_action('/TreePopup/RecordDelete') action_delete.set_sensitive(obj and True or False) menu = None if event.button == 3: self._field_name = field_name menu = self.ui_manager.get_widget('/TreePopup') menu.popup(None, None, None, event.button, event.time) return menu def setup_columns(self, field_list=None, position=-1): """ :param field_list: list of field_names or string to be splitted (comma or space separated) :param position: determine the position in the columns where the column will be inserted """ for field_name in field_list or self.field_list: if field_name in widgets.info: label = widgets.info[field_name][0] else: label = field_name ## first check if you have a Custom Renderer if self.cell_renderers and field_name in self.cell_renderers: col = self.cell_renderers.get(field_name)(field_name, label) elif self.master.is_fkey(field_name) : ## FKey col = FKeyColumn(self.master, field_name, label) elif self.master.is_image(field_name): ## image col = ImageColumn(self.master, field_name, label) elif self.master.is_enum(field_name): ## image col = EnumColumn(self.master, field_name, label) elif self.master.is_boolean(field_name): ## boolean col = BooleanColumn(self.master, field_name, label) elif self.master.is_date(field_name): ## Dates col = DateColumn(self.master, field_name, label) elif self.master.is_datetime(field_name): ## Datetime col = DateTimeColumn(self.master, field_name, label) elif self.master.is_number(field_name): ## Number col = NumericColumn(self.master, field_name, label) elif self.master.is_text(field_name): ## Multiline col = MultilineColumn(self.master, field_name, label) elif self.master.is_string(field_name): ## Varchar col = VarcharColumn(self.master, field_name, label) else: col = VarcharColumn(self.master, field_name, label) self.add_column(col, position=position) self.treeview.columns_autosize() self.fix_expandable_columns() def add_column(self, column=None, position=-1, field=None): """ Add a column to the view. :param column: the column to be added. If empty, a field must be provided :param position: where should the column be inserted :param field: an instance of the Field that should be used. In this case a column is guessed and built on the fly by :meth:`setup_columns` """ if not column: assert field is not None, \ "You must pass a sqlkit.fields.Field instance" self.master.gui_fields[field.field_name] = field self.setup_columns([field.field_name], position=position) return self.tvcolumns[column.field_name] = column.column self.columns[column.field_name] = column if position < 0: self.treeview.append_column(column.column) else: self.treeview.insert_column(column.column, position) if self.read_only: column.set_editable(False) def get_first_editable_col(self): """ return the first visible col to position the cursor on """ columns = self.treeview.get_columns() second_choice = [] for field_name in [c.get_data('field_name') for c in self.treeview.get_columns()]: if self.tvcolumns[field_name].get_visible() and self.master.is_editable(field_name): try: has_usable_default = self.master.gui_fields[field_name].get_default() except exc.NotHandledDefault, e: # it's not necessary to fill this value as a default exists even if we're # not able to handle it has_usable_default = True if has_usable_default is not None: second_choice += [self.tvcolumns[field_name]] else: return self.tvcolumns[field_name] if second_choice: return second_choice[0] def start_editing(self, path, field_name=None): """ Edit a path/column. Column is passed by its field_name :param path: the path to be edited :param field_name: the field_name of the column, if None, the first editable field that does not have a default """ if field_name: col = self.tvcolumns[field_name] else: col = self.get_first_editable_col() self.treeview.scroll_to_cell(path, col) self.treeview.set_cursor(path = path, focus_column = col, start_editing = True) def get_selected_path(self): """Return the path of the selected row or None NOTE: treeselection 'changed' signal arrives when the selection changed has alreadu occurred """ selection = self.treeview.get_selection() model, rows = selection.get_selected_rows() ## we use just the first selected # >>> sel.get_selected_rows() # (, [(1,)]) if rows: return rows[0] else: return None def __str__(self): return "" % self.name def select_path(self, path=None, obj=None, sync=True): """ Select path ``path``. If path is null and obj is not null try finding path at which is obj :param path: the path to be selected or None :param obj: if path is None, the object to be searched for and selected :param sync: syncronize all views but run just one selection_cb :type sync: boolean """ def search_obj(model, path, iter, (obj, path_list)): if model.get_value(iter, 0) == obj: path_list += [path] return True ## let's find out the real path for the object. The object may be in a ## TreeStore's deeper level or some totals may shift it if path is None and obj: path_list = [] self.master.modelproxy.modelstore.foreach(search_obj, (obj, path_list)) try: path = path_list[0] except IndexError: pass if path is not None: self.treeview.get_selection().select_path(path) sqlkit-0.9.5/sqlkit/widgets/table/modelproxy.py0000644000175000017500000003504111714210425021212 0ustar sandrosandro# Copyright (C) 2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ =========== ModelProxy =========== A proxy to the treeview model that can handle both listore and treemodels and allows to switch easily from one to the other A second purpouse is to allow reorderable treeviews (i.e.: those wehere you can set order by drag&drop) via self.order_store attribute/function. If position of rows need to be memorized in a visual way, a field must be set (order_store) that will be reset each time a new record is added or a row is dragged. Clearly if that means something at all, entirely depends on the application. In these cases the order is set in the records currently handled by the model. Sorting ======= A modelproxy can be sorted via it's :attr:`order_by [+-]?)(?P.*)', tk) asc, field_name = m.groups() fkey = self.master.gui_fields[field_name].fkey if asc == '-': output += [(field_name, False, fkey)] else: output += [(field_name, True, fkey)] return output def get_order(self): try: return self._order_list[0][:2] except: return None class Header(object): def __init__(self, master, field_name, value): self.master = master value = self.master.gui_fields[field_name].get_human_value(value) self.field_name = field_name setattr(self, field_name, value) def __getattribute__(self, name): try: return object.__getattribute__(self, name) except AttributeError: return None def __str__(self): return getattr(self, self.field_name) sqlkit-0.9.5/sqlkit/widgets/table/multiline_cell.py0000644000175000017500000000465411714210425022017 0ustar sandrosandroimport gtk class CellTextView(gtk.TextView, gtk.CellEditable): __gtype_name__ = "CellTextView" def do_editing_done(self, *args): self.remove_widget() def do_remove_widget(self, *args): pass def do_start_editing(self, *args): pass def get_text(self): text_buffer = self.get_buffer() bounds = text_buffer.get_bounds() return text_buffer.get_text(*bounds) def set_text(self, text): if text: self.get_buffer().set_text(text) class MultilineCellRenderer(gtk.CellRendererText): __gtype_name__ = "MultilineCellRenderer" def __init__(self, sqlwidget): gtk.CellRendererText.__init__(self) self._in_editor_menu = False self.treeview = sqlwidget.treeview self.sqlwidget = sqlwidget def _on_editor_focus_out_event(self, editor, *args): if self._in_editor_menu: return def _on_editor_key_press_event(self, editor, event): if event.state & (gtk.gdk.SHIFT_MASK | gtk.gdk.CONTROL_MASK): return if event.keyval in (gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab): self.emit("edited", editor.get_data("path"), editor.get_text()) editor.remove_widget() if event.keyval in (gtk.keysyms.Tab,): self.treeview.emit('key-press-event', event ) elif event.keyval == gtk.keysyms.Escape: editor.remove_widget() self.emit("editing-canceled") def _on_editor_populate_popup(self, editor, menu): self._in_editor_menu = True def on_menu_unmap(menu, self): self._in_editor_menu = False menu.connect("unmap", on_menu_unmap, self) def do_start_editing(self, event, widget, path, bg_area, cell_area, flags): editor = CellTextView() editor.modify_font(self.props.font_desc) editor.set_text(self.props.text) editor.set_size_request(cell_area.width *2, cell_area.height * 2) editor.set_border_width(min(self.props.xpad, self.props.ypad)) editor.set_data("path", path) editor.connect("focus-out-event", self._on_editor_focus_out_event) editor.connect("key-press-event", self._on_editor_key_press_event) editor.connect("populate-popup", self._on_editor_populate_popup) editor.show() self.set_data('editor', editor) self.editor = editor return editor sqlkit-0.9.5/sqlkit/widgets/table/cell_renderers.py0000644000175000017500000002531411714210425022002 0ustar sandrosandrofrom datetime import datetime import gtk import gobject import pango from sqlkit import debug as dbg from multiline_cell import MultilineCellRenderer class CellEditableCalendar(gtk.TextView, gtk.CellEditable): """An editable that allows to edit dates via a calendar popup Can also edit datetime but the popup will just change the date, not allow to edit the time """ datetime = None def __init__(self, datetime, pos=None, wdg=None, cell_area=None, fmt_func=None): self.datetime = datetime self.fmt_func = fmt_func gtk.TextView.__init__(self) #self.set_has_window(False) self.cal = gtk.Calendar() #self.cal.show() self.cal.select_month(self.datetime.month - 1, self.datetime.year) self.cal.select_day(self.datetime.day) self.w = gtk.Window(gtk.WINDOW_POPUP) self.set_editable(True) self.w.add(self.cal) #self.w.connect("delete_event", self.delete_event) self.cal.connect("day-selected-double-click", self.delete_event) self.cal.connect("event", self.cal_event) self.position_popup(wdg, self.w, cell_area) self.show_all() self.grab_focus() self.w.show_all() self.connect('key-press-event', self.keypress_event) def position_popup(self, widget, popup, ca): # the get_origin method returns the origin comprising the headers # the get_cell _area don't!!! (so I add cell_height doing 2*...)) # you need child to show_all() to get the dimentions correctly child = popup.get_children()[0] child.show_all() popup_width, popup_height = popup.size_request() x, y = gtk.gdk.Window.get_origin(widget.window) # path, col, x1, y1 = widget.get_path_at_pos(event.x, event.y) # ca = widget.get_cell_area(path, col) X = x + ca.x Y = y + ca.y + 2*ca.height + 4 # border case: the cell is too close to the border to allow the window # I don't want the mouse to be over the popup since an undesired click # would change the date... if Y + popup_height> gtk.gdk.screen_height(): Y = y + ca.y + ca.height - popup_height if X + popup_width> gtk.gdk.screen_width(): X = gtk.gdk.screen_width() - popup_width popup.move(X, Y) def keypress_event(self, w, event): accelname = gtk.accelerator_name(event.keyval, event.state) if accelname == 'Delete': self.date = None self.emit('editing-done') return True return False def cal_event(self, w, e): text = self.fmt_func(self.get_calendar_date()) #text = self.get_calendar_date().strftime("%d/%m/%y") tb = self.get_buffer() tb.set_text(text) return False def do_start_editing(self, event): pass def do_remove_widget(self): pass def do_stop_editing(self): self.w.destroy() self.destroy() def do_editing_done(self): self.w.destroy() self.destroy() pass def delete_event(self,w = None, e = None): date = self.get_calendar_date() try: ## in case self.datetime is a date, we preserve the time self.datetime = datetime.combine(date, self.datetime.time()) except Exception, e: ## self.datetime may just be a date... self.datetime = date self.emit('editing-done') self.w.destroy() self.destroy() return True def get_calendar_date(self): """calendare return a tuple: genuary is 0!""" calendar_tuple = list(self.cal.get_date()) calendar_tuple[1] += 1 return datetime(*tuple(calendar_tuple)) gobject.type_register(CellEditableCalendar) class CellRendererDate(gtk.GenericCellRenderer): date = None ## editable = None __gproperties__ = { 'date': (gobject.TYPE_PYOBJECT, 'date of cell', 'date object contained in cell', gobject.PARAM_READWRITE) } __gsignals__ = { 'date-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) ) } def __init__(self, master=None, field_name=None, *args, **kwargs): #dbg.write() self.date = None self.master = master self.field_name = field_name return gtk.CellRenderer.__gobject_init__(self, *args, **kwargs) def do_pango_layout(self, widget): pc = widget.get_pango_context() pl = pango.Layout(pc) pl.set_text(self.date_formatter(self.date)) return pl def do_get_size(self, widget, cell_area): #dbg.show_caller() xpad = self.get_property('xpad') ypad = self.get_property('ypad') xalign = self.get_property('xalign') yalign = self.get_property('yalign') layout = self.do_pango_layout(widget) width, height = layout.get_pixel_size() x_offset = xpad y_offset = ypad if cell_area: x_offset = xalign * (cell_area.width - width) x_offset = max(x_offset, xpad) x_offset = int(round(x_offset, 0)) y_offset = yalign * (cell_area.height - height) y_offset = max(y_offset, ypad) y_offset = int(round(y_offset, 0)) width = width + (xpad * 2) height = height + (ypad * 2) return x_offset, y_offset, width, height def date_formatter(self, date): if date is None: return "" return self.master.gui_fields[self.field_name].format_value(date) #return date.strftime("%d/%m/%y") def do_start_editing(self, event, widget, path, background_area, cell_area, flags): if self.date is None: date = datetime.now() else: date = self.date #dbg.ddir(cell_area) ce = CellEditableCalendar(datetime = date, cell_area=cell_area, wdg=widget, fmt_func=self.master.gui_fields[self.field_name].format_value) # ce = CellEditableCalendar(date = date, pos=(event.x, event.y)) ce.show() ce.grab_focus() ce.connect('editing-done', self.editing_done, widget, path) return ce def editing_done(self, widget, treeview, path): self.stop_editing(True) self.emit('date-changed', path, widget.datetime) return True def do_render(self, window, widget, background_area, cell_area, expose_area, flags): x_offset, y_offset, width, height = self.do_get_size(widget, cell_area) pl = self.do_pango_layout(widget) widget.get_style().paint_layout(window, gtk.STATE_NORMAL, True, cell_area, widget, 'foo', cell_area.x + x_offset, cell_area.y + y_offset, pl) def do_set_property(self, gprop, value): setattr(self, gprop.name, value) if gobject.pygtk_version < (2, 8): gobject.type_register(CellRendererDate) class CellRendererDatetime(CellRendererDate): datetime = None ## editable = None __gproperties__ = { 'datetime': (gobject.TYPE_PYOBJECT, 'datetime of cell', 'datetime object contained in cell', gobject.PARAM_READWRITE) } __gsignals__ = { 'datetime-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) ) } def __init__(self, master, field_name, *args, **kwargs): self.datetime = None self.master = master self.field_name = field_name return gtk.CellRenderer.__gobject_init__(self, *args, **kwargs) def do_pango_layout(self, widget): pc = widget.get_pango_context() pl = pango.Layout(pc) pl.set_text(self.date_formatter(self.datetime)) return pl def do_start_editing(self, event, widget, path, background_area, cell_area, flags): if self.datetime is None: date_time = datetime.now() else: date_time = self.datetime #dbg.ddir(cell_area) ce = CellEditableCalendar(datetime = date_time, cell_area=cell_area, wdg=widget, fmt_func=self.master.gui_fields[self.field_name].format_value) # ce = CellEditableCalendar(date = date, pos=(event.x, event.y)) ce.show() ce.grab_focus() ce.connect('editing-done', self.editing_done, widget, path) return ce def editing_done(self, widget, treeview, path): self.stop_editing(True) self.emit('datetime-changed', path, widget.datetime) return True MAX_CHARS = 30 class CellRendererVarchar(gtk.CellRendererText): fixed_width = True def __init__(self, lenght): gtk.CellRendererText.__init__(self) self.lenght = lenght self.fixed_width = True dbg.write("new varchar, lenght", lenght) def do_get_size(self, widget, cell_area): # We start from the standard dimension... ellipsize = self.get_property('ellipsize') self.set_property('ellipsize', False) size_tuple = gtk.CellRendererText.do_get_size(self, widget, cell_area) self.set_property('ellipsize', ellipsize) if not self.fixed_width: # We just return the standard text size. return size_tuple size = list(size_tuple) context = widget.get_pango_context() description = context.get_font_description() metrics = context.get_metrics(description) effective_lenght = min(self.lenght, MAX_CHARS) or MAX_CHARS pango_width = metrics.get_approximate_char_width() * effective_lenght # Pango uses its own internal measure unit. "pango.PIXELS" converts it. width = pango.PIXELS(pango_width) # ... then modify the width. FIXME: consider xpad. xpad = self.get_property('xpad') size[2] = width + xpad * 2 # dbg.write("lenght", self.lenght) return tuple(size) gobject.type_register(CellRendererVarchar) sqlkit-0.9.5/sqlkit/widgets/table/totals.py0000644000175000017500000003152111714210425020315 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. autoclass:: Totals :members: totals, subtotals, add_total, add_break, add_date_break, compute, sum .. autoclass:: TotalObj :members: """ from datetime import date, datetime import gtk import gobject from sqlkit import debug as dbg, _ from sqlkit.misc import utils from sqlkit.widgets.common import sqlwidget class TotalObj(object): """ A simple class that represents the object holding the totals and a way to represent it personalization You can change the look of totals inheriting from TotalObj and placing the new class as ``class`` attribute of Totals """ _sub_color = 'gray92' _total_color = 'gray80' _fg_color = 'indianred' def __init__(self, fields, sub=True): """ all fields will be initialized to None _sub will be set to True for subTotals, to render with different colors """ for name in fields: setattr(self, name, None) if sub: self._color = self._sub_color else: self._color = self._total_color def set_value_and_colors(self, cell, value, field_name): """ set the color cell and possibly background """ if value is not None: cell.set_property('markup', "%s" % (value)) # set color only if we have a value if hasattr(self, field_name): cell.set_property('foreground', self._fg_color) def __getattr__(self, key): try: object.__getattr__(self, key) except AttributeError: return None class Totals(gobject.GObject): """ An object whose 'compute' method adds TotalObjets to show partial totals/grand totals to a table. A TotalObject is inserted each time total_by changes. If total_by is a callable, it's evaluated to detect if a partial total must be inserted """ __gsignals__ = { 'computed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,), ) } total_class = TotalObj totals = None """ A dict whose keys are the field names for which a total/subtotal is to be computed. The values are the totals. It's filled by :meth:`sum` that can be customized. """ subtotals = None """ A dict whose keys are the field names for which a total/subtotal is to be computed The values are the subtotals. It's filled by :meth:`sum` that can be customized. """ def __init__(self, table, treeview=None): """ :param table: the tabe for which a total must be generated :param treeview: in case 'table' does not have a treeview, you can specify it. Used when total is made for filter_panel. """ self.__gobject_init__() self.table = table self.totals = {} self._break_field_name = None self.subtotals = {} self.break_func = None self.prev = None self.treeview = treeview or table.treeview self.table.modelproxy.connect('model-sorted', lambda proxy, model: self.compute()) self.table.modelproxy.connect('model-pre-sort', self.delete_total_rows) # self.table.connect('context-changed', self.update) # self.table.connect('records-displayed', self.update) def update(self, table, obj=None): self.compute() def add_total(self, *field_names, **kw): """ Add a total object each time subtotal changes :param field_names: a list of field_names for which we want a total :param quiet: boolean. If True silently fail if the field is not a number :param hide_total: boolean. If True gran total will not be shown (only subtotals) """ quiet = kw.get('quiet', False) self.hide_total = kw.get('hide_total', False) for field_name in field_names: if not self.table.get_field_type(field_name) in sqlwidget.NUMBERS: self.table.sb(_("Field '%s' is not numeric!!!" % field_name)) if not quiet: raise NotImplementedError("%s is not a number" % field_name) return self.totals[field_name] = 0 self.subtotals[field_name] = 0 self.compute() def del_total(self, field_name): del self.totals[field_name] def add_break(self, field_name, func=None): """ If no ``func`` is provided, a break_func will be setup to insert a subtotal each time field_value changes. Othewise ``func`` will be set as break_function. Break function must have this signature:: def break_func(obj, field_name, path) see ``func_date_*`` is this module for examples """ if not func: def subtotal_diff(obj, path): "default subtotal when func not defined" try: return getattr(obj, field_name) except: return self.table.gui_fields[field_name].clean_value(obj) if self.break_func and self._break_field_name == field_name: self.break_func = None else: self.break_func = subtotal_diff self._break_field_name = field_name else: def subtotal_diff(obj, path): "default subtotal - func IS defined" return func(obj, field_name) self.break_func = subtotal_diff self.compute() def add_date_break(self, field_name, period): """ Set date break period can be: ``day``, ``week``, ``month``, ``quarter``, ``year`` """ func = globals()['func_date_same_%s' % period] self.add_break(field_name, func) def is_different(self, obj, path): if self.prev is None: self.prev = self.break_func(obj, path) return False if self.prev != self.break_func(obj, path): return True return False def initialize(self): self.prev = None for field_name in self.totals.keys(): self.totals[field_name] = 0 self.subtotals[field_name] = 0 def sum(self, obj, model, path, iter): """ sum values and store result in :attr:`totals` and :attr:`subtotals` :param obj: the obj that should be added. It's an instance of ``table.mapper.class_`` :param model: the model of the treeview :param path: the path at which the obj is :param iter: the iter at which the obj is To customize the behaviour of total you can just customize this method, suppose you want to make it sum flagged objects in table t:: class BoolTotals(totals.Totals): def sum(self, obj, model, path, iter): if obj.flag: for field_name in self.totals.keys(): self.totals[field_name] += getattr(obj, field_name, 0) or 0 self.subtotals[field_name] += getattr(obj, field_name, 0) or 0 t. = SqlTable(...) t.totals = BoolTotal(t) """ for field_name in self.totals.keys(): if field_name in self.table.mapper_info: value = getattr(obj, field_name, 0) else: value = self.table.gui_fields[field_name].clean_value(obj) self.totals[field_name] += value or 0 self.subtotals[field_name] += value or 0 def reset_subtotals(self): for field_name in self.totals: self.subtotals[field_name] = 0 def insert(self, iter, after=None, sub=False): tot = self.total_class(self.table.field_list, sub=sub) if after: try: piter = self.table.modelproxy.modelstore.insert_after(iter) except TypeError: piter = self.table.modelproxy.modelstore.insert_after(None, iter) else: try: piter = self.table.modelproxy.modelstore.insert_before(iter) except TypeError: piter = self.table.modelproxy.modelstore.insert_before(None, iter) self.table.modelproxy.modelstore.set_value(piter, 0, tot) newrow = self.table.modelproxy.modelstore[piter] return newrow, tot def insert_subtotal(self, iter, after=False): row, tot = self.insert(iter, after=after, sub=True) for field_name in self.totals: setattr(tot, field_name, self.subtotals[field_name]) return row, tot def insert_total(self, row, after=False): if not row: try: # if model[-1] does not exists, no total should be needed... # sometime it happens it it tempted anyhow... row = self.table.modelproxy.modelstore[-1] except IndexError: return if self.hide_total: return row row, tot = self.insert(row.iter, after=after, sub=False) for field_name in self.totals: setattr(tot, field_name, self.totals[field_name]) return row def remove(self, iter): self.table.modelproxy.modelstore.remove(iter) def preserve_selection(func): def new_func(self): self.table.commit_inhibited = True selection = self.treeview.get_selection() model, rows = selection.get_selected_rows() if rows: path = rows[0] #remember = gtk.TreeRowReference(self.table.modelproxy.modelstore, path) func(self) if rows: #selection.select_path(remember.get_path()) selection.select_path(path) self.table.commit_inhibited = False new_func.__doc__ = func.__doc__ return new_func @preserve_selection def compute(self): """ Go, add subtotals and total """ if not self.totals.keys(): return self.initialize() if not self.table.records: return self.delete_total_rows() self.table.modelproxy.modelstore.foreach(self.foreach_ins_func) row = self.insert_total(row=None, after=True) if self.break_func: self.insert_subtotal(row.iter, after=True if self.hide_total else False) self.emit('computed', utils.ObjLike(self.totals)) def delete_total_rows(self, *args): iter_del_list = [] self.table.modelproxy.modelstore.foreach(self.foreach_del_func, iter_del_list) for iter in iter_del_list: self.remove(iter) def foreach_del_func(self, model, path, iter, iter_del_list): """ """ obj = self.table.modelproxy.modelstore.get_value(iter, 0) if isinstance(obj, self.total_class): iter_del_list += [iter] def foreach_ins_func(self, model, path, iter): """ """ obj = self.table.modelproxy.modelstore.get_value(iter, 0) if self.break_func and self.is_different(obj, path): self.insert_subtotal(iter) self.prev = self.break_func(obj, path) self.reset_subtotals() if isinstance(obj, self.table.mapper.class_): self.sum(obj, model, path, iter) def func_date_same_day(obj, field_name): date = getattr(obj, field_name) if isinstance(date, datetime): return date.date() else: return date def func_date_same_week(obj, field_name): date = getattr(obj, field_name) if date: return date.year, getattr(obj, field_name).strftime('%U') else: return None def func_date_same_month(obj, field_name): date = getattr(obj, field_name) if date: return date.month, date.year else: return None def func_date_same_year(obj, field_name): date = getattr(obj, field_name) if date: return getattr(obj, field_name).year else: return None def func_date_same_quarter(obj, field_name): date = getattr(obj, field_name) if date: return date.year, divmod(date.month -1, 3)[0] else: return None sqlkit-0.9.5/sqlkit/fields.py0000644000175000017500000021716711714210425015534 0ustar sandrosandro# -*- coding: utf-8 -*- # Copyright (C) 2008-2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. _fields: ======= Fields ======= Mask and Tables display a collection of data that may belong to the database or not. Each of the data represented in the GUI that needs to partecipate to any updating/saving process should have it's own handler that is a Field. A Field is an entity that knows how to handle a piece of data, be it on the database (a field_name) or just in the GUI. It's a layer that allows gui widget to be simpler and easier to interchange. As an example, both varchars and integers are normally displayed by a gtk.Entry: the entry.get_text() will retrieve in both cases a string, it's the Field (IntegerField) that knows how to turn that string into an integer using the :meth:`Field.clean_value` method. In more complex cases ``clean_value`` can return data computed from related records. cthe field knows: - if it belongs to the database (attribute ``persistent`` is True) and in case how it is defined ( i.e.: sqlalchemy property/column) - if it is nullable/editable - which widget must be used to represent it (see below, not requested) - how to produce a formatted representation of the field. It provides functions to - set/get value - get default value (but will not cope with server_side defaults) - tell if it changed from the default - clean value according to it's logic: i.e. return a value of the correct type - validate values (possibly according to other criteria) The association between a database field and a Field is dome by :ref:`field_chooser`, but you can force a particular Field when defining the model class setting the key ``field`` of the column's ``info`` dictionary:: class MyClass(Base): ... foo = Column(..., info={'field' : MyField}) A last method is to use :ref:`on_pre_layout hook ` that allows to set ``sqlkit.Fields`` even on non persisted fields. Relation with the master sqlwidget ---------------------------------- Currently there are a number of operation that require that the field know wich is the sqlwidget it is acting for. I'd like to loosen this connection in future but at present it's used in the following situations: * to keep updated the list of field_widgets * to get default values (that can be local to a sqlwidget) * for validation purposes: to issue master.run_hooks and NonNullableException * FKey: to handle addition of related_obj when cascading policy requires it In the meanwhile I add master to the Field via set_master() and add a widget to the Field via set_widget() Db attributes ------------- The costructor can be passed a dict of field_attributes * field_name: the name of the field * nullable: True if field is nullable or False. The related widget will get a background color that reflects the fact that it is required (currently yellow). This can be set at any time. * editable: True if field is editable or False. The related widget will be set unsensitive. This can be set at any time. * length: the desired length in chars * default: a possible default option * type: the python type * mapper: defined as None for field that are mapped * table: the SA Table object * column: the SA Column object * property: the SA Property - if any * fkey: the SA ForeignKey or None * pkey: True is field is a PRIMARY KEY or False Other attributes ---------------- * widget: the widget used to represent it. May be sqlkit.widgets.mask.miniwidget or similar * format: the format used to represent the field (numeric or date/time) * locale: the locale to use to represent the field (numeric or date/time) * DecimalFields also have precision/scale * Varchar/TextFields also have blank=True/False (default: False). It determines if an empty string is a valid value. Empty strings are differerent from NULL values future ------- This should provide a way to set the possible observers validation based on the type should live here possibly more validation may live here formatting/locale ----------------- see decimal_ to have an intro on formatting numbers .. _decimal: http://java.sun.com/docs/books/tutorial/i18n/format/decimalFormat.html .. _field_chooser: FieldChooser =============== This class implements a way to determine which Field should be associated to each field_name in a mapper (used in setup_field_validation so that it can be easily overwritten). It's important to understand that it already receives a gtkwidget from layoutgenerator; that widget has been set by introspection of the layout description and of the field in the database. You can overwrite the decision of the field redefining the gui_field on a derived sqlwidget or passing it as argument (see code snippet 34):: class Movie(SqlMask): gui_field_mapping = {'date_release' : VarcharField} t = Movie(table='movie', dbproxy=db, layout=lay) It's up to the field defined in this way to be able to handle the type of data. This setting can be used to add field constraints (eg: mail address validation) or to completely change the widget that represent data. Widgets ======== A Field does not create a gtk widget to represent data. Layout definition is normally enought to create the correct GTK widget. If a gtk widget exists that represent a field, it is handled by a proxy called Miniwidget that offers a common interface to possbly different gtk widgets. If a MiniWidget exists the Field will instantiate it and set values thought it. A notable exception to this rule is represented by any m2m/o2m relation, that in the layout is only present as a gtk.Alignment widget to which a children is added by mask.Widget. :ref:`Miniwidgets ` for all main types are provided. Global variables ================ Varchar fields will try to cast an empty value to None unless blank_ok is set in the field:: t.gui_field.field_name.blank_ok = True or globally: from sqlkit.widgets.common import fields fields.BLANK_OK = True Default value for BLANK_OK is False. This is only enforced for NEW records, for already persisted records the default behaviour is to let it as-is, to prevent a very annoying flood of dialog "do you want to save?" when you just need to browse some data. Available Fields ================= .. autoclass:: Field :members: set_widget, set_value, get_value, clear_value, format_value, clean_value, has_changed, get_default, set_default, validate, get_human_value, persistent, nullable, editable All other field inherit from Field .. autoclass:: VarcharField :members: blank_ok .. autoclass:: IntegerField :members: format .. autoclass:: FloatField :members: format .. autoclass:: DecimalField :members: format .. autoclass:: TextField .. autoclass:: DateField :members: format .. autoclass:: TimeField .. autoclass:: TimeTZField .. autoclass:: IntervalField .. autoclass:: DateTimeField .. autoclass:: DateTimeTZField .. autoclass:: BooleanField .. autoclass:: BooleanNullField .. autoclass:: EnumField :members: keys, values, lookup_value .. autoclass:: ImageField :members: base_dir, thumbnail_size, default_size, get_value, set_value, clean_value, get_save_path, scale_pixbuf, scale_file, create_thumbnail, get_thumbnail, get_thumbnail_path_with_size .. autoclass:: ForeignKeyField :members: lookup_value, add_related_object .. autoclass:: CollectionField .. autofunction:: std_cleanup """ import re import os import types import shutil import warnings import weakref from datetime import datetime, time, date, timedelta from decimal import Decimal import gobject import babel import sqlalchemy from sqlalchemy.schema import ColumnDefault from sqlalchemy.orm.interfaces import AttributeExtension from pkg_resources import parse_version from sqlkit import exc, _ from sqlkit.misc import localtimezone from sqlkit.misc.babel_support import numbers, dates from sqlkit.db import minspect from sqlkit.exc import ValidationError, NotHandledField, NotHandledDefault BLANK_OK = False class ZoomException(Exception): pass class SignalEmitter(gobject.GObject): """A minimalist gobject used to trace value setting in AttributeExtension """ __gsignals__ = { 'value-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, )), # 'value-appended': (gobject.SIGNAL_RUN_FIRST, # gobject.TYPE_NONE, # (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, # gobject.TYPE_PYOBJECT)), # 'value-removed': (gobject.SIGNAL_RUN_FIRST, # gobject.TYPE_NONE, # (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT, # gobject.TYPE_PYOBJECT)), } def __init__(self): self.__gobject_init__() def set_cb(self, obj, value, oldvalue, initiator): "Callback triggered by SQLA events (>= 0.7)" self.emit('value-set', value, oldvalue, weakref.ref(obj)) class AttrExt(AttributeExtension): """An AttributeExtension used to trace value setting. Fields of a mask connect to the signal 'value-set' and propagate it. Table's fields don't need to connect as they're always tracing object modification via the cell_data_func callback. Only used or SQLA version < 0.7 """ def set(self, state, value, oldvalue, initiator): #print 'value-set', state.obj().__class__.__name__, initiator.key, value self._sk_emitter.emit('value-set', value, oldvalue, state.obj, ) return value def append(self, state, value, initiator): #self._sk_emitter.emit('value-appended', state.obj(), value) return value def remove(self, state, value, initiator): #self._sk_emitter.emit('value-removed', state.obj(), value) return value class FieldChooser(object): """here we wrap the gtk widget in a double layer: * field for validation * widget for edit w: the widget dictionary retuner by Layout """ def __init__(self, info, widgets=None, gui_field_mapping=None): """ info: a InspectMapper objet widgets: the widget dictionary retuned by Layout gui_field_mapping: a dictionali-like store where to look for custom mapping of field-name and fields. Used when the field cannot be devised from the mapper as the fields are not persisted. """ self.info = info self.widgets = widgets or {} self.gui_field_mapping = gui_field_mapping or {} def get_field(self, field_name, field_attrs=None, def_str=None, test=False): """ return a field field_name: the name of the field in the db field_attrs: the dict for this field as worked out by InspectMapper def_str: the definition string in the layout that determined the gtk.Widget (eg.: c=married, e=first_name) as extracted by layoutgen and set in laygen.fields_in_layout dictionary """ if field_name in self.gui_field_mapping: return self.gui_field_mapping.get(field_name) assert field_attrs is not None, \ "field_name '%s' has no attributes and is not present in gui_fields" % field_name col = field_attrs.get('col', None) if col is not None: try: return col.info['field'] except (AttributeError, KeyError), e: # AttributeError: column (as Label) that don't have .info # KeyError when info does not have 'field' key pass def_str = def_str or '' if self.info.is_fkey(field_name): return ForeignKeyField if self.info.is_loader(field_name): return CollectionField if self.info.is_enum(field_name): return EnumField if self.info.is_integer(field_name): return IntegerField if self.info.is_image(field_name): return ImageField if self.info.is_float(field_name): return FloatField if self.info.is_numeric(field_name): return DecimalField if self.info.is_integer(field_name): return IntegerField if self.info.is_date(field_name): return DateField if self.info.is_boolean(field_name): if self.info.is_nullable(field_name): return BooleanNullField else: return BooleanField if self.info.is_interval(field_name): return IntervalField if self.info.is_time(field_name): if field_attrs['col'].type.timezone: return TimeField else: return TimeField if self.info.is_datetime(field_name): if field_attrs['col'].type.timezone: return DateTimeTZField else: return DateTimeField if (self.info.is_text(field_name) and not def_str.startswith('e=')) or (def_str.startswith('TX') ): return TextField if self.info.is_string(field_name): return VarcharField else: return VarcharField # raise NotHandledField("Field '%s' is not handled in any way: COL_SPEC: %s" % # (field_name, filter_attr['col_spec'])) class Field(gobject.GObject): __gsignals__ = { 'value-set': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT)), } Widget = None default_def_string = 'ae' type = None def __init__(self, field_name, field_attrs=None): """ :param field_name: the name of the field in the db :param field_attrs: the dict as worked out by InspectMapper. Can be created by hand: that is useful when field is not mapped in the database. These attributes may be present: :nullable: boolean. If True None is accepted as value :editable: boolean. If True the value can be edited, if 'column' is not passed the field is tuned into not editable :length: used to determine how many chas are needed to represent it :default: a possible default value :mapper: the mapper the field is defined into, if any :pkey: true if this field is a primary key :fkey: a sqlalchemy ForeignKey ogject if this field is a foreign key :property: the sqlalchemy property the field has in the mapper :column: the sqlalchemy column the field maps to :table: the table the field is defined in, if any :param def_str: the definition string in the layout that determined the gtk.Widget (eg.: c=married, e=first_name) :param master: the sqlkit widget: an instance of SqlMask or SqlTable """ self.__gobject_init__() self._ids = {} self.field_name = field_name self._add_info(field_attrs or {}) self.initial_value = None def set_widget(self, gtkwidget=None, def_str=None, widget=None): """ :param def_str: the definition string in the layout that determined the gtk.Widget (eg.: c=married, e=first_name) :param widget: the miniwidget to be used. Defaults to class-defined self.Widget the widget can be a string of a Widget derived from ``miniwidget.Widget`` :param gtkwidget: the gtk widget to be used (already created by Layout) """ from sqlkit.widgets.mask import miniwidgets from sqlkit.widgets.table import tablewidgets ## when set_widget is used to replace try: obj, hid = self._ids['gtkwidget'] obj.disconnect(hid) except KeyError: pass ## widget can be a string in which case it it searched for in ## sqlkit.widgets.mask.miniwidgets ## this avoids importing miniwidgets at import time and delays it to this function if isinstance(widget, basestring): Widget = getattr(miniwidgets, widget) if def_str and def_str.startswith('ro='): widget = miniwidgets.ReadOnlyWidget Widget = widget or getattr(miniwidgets, self.Widget) if gtkwidget: ## used in SqlMask self.widget = Widget(gtkwidget, self) self._ids['gtkwidget'] = (gtkwidget, gtkwidget.connect('destroy', self.destroy)) self.connect('value-set', self.widget.set_value_cb) else: ### used in SqlTable ## tables are inherently already listening to changes in the object # via the cell_data_func_cb, so don't need to listen to modifications self.widget = tablewidgets.CellWidget(self) def destroy(self, widget): for obj, hid in self._ids.values(): obj.disconnect(hid) self._ids.clear() try: del self.widget del self.master except AttributeError, e: pass def set_master(self, master): """ add a sqlwidget as master of this field. Add a listener to any change to the object so that it can be reflected in the widget. The master is also used to * get defaults * validate * add related objects """ self.master = master if self.master.is_table(): return ## make this field listen to modification in the sqlalchemy object (record) so ## that any change will be reflected in the UI. Here we implement the mask part ## as the table do that inherently via the cell_data_func_cb if self.persistent: emitter = SignalEmitter() if parse_version(sqlalchemy.__version__) < parse_version('0.7'): FieldAttrExt = type(str("attrext_%s" % self.field_name), (AttrExt,), {'_sk_emitter': emitter}) extensions = getattr(self.master.mapper.class_, self.field_name).impl.extensions found = False if extensions is not None: for ext in extensions: if isinstance(ext, AttrExt): found = True emitter = ext._sk_emitter break if not found: ext = FieldAttrExt() extensions.append(ext) #self._emitter = emitter else: from sqlalchemy import event # SQLA 0.7 drops support for AttributeExtensions in favor of # signals but does not yet implement event.remove as of # 0.7.2, so an indirection level is still needed instrumented_attr = getattr(self.master.mapper.class_, self.field_name) if not hasattr(instrumented_attr, '_sk_emitter'): instrumented_attr._sk_emitter = emitter event.listen(instrumented_attr, 'set', emitter.set_cb) else: emitter = getattr(instrumented_attr, '_sk_emitter') hid = emitter.connect('value-set', self.value_set_cb) self._ids['emitter_value_set'] = (emitter, hid) def set_nullable(self, value): self._nullable = value try: self.widget.set_not_null_style(value) except AttributeError: pass def get_nullable(self): return self._nullable nullable = property(get_nullable, set_nullable) def set_editable(self, value): self._editable = value try: self.widget.set_editable(value) except AttributeError: pass def get_editable(self): return self._editable editable = property(get_editable, set_editable) def _add_info(self, field_attrs): """ read docs in __init__ """ self._nullable = field_attrs.get('nullable', True) # don't trigger now widget.set_bg() self._editable = field_attrs.get('editable', 'property' in field_attrs and field_attrs['property'] or False) self.length = field_attrs.get('length', None) self.default = field_attrs.get('default', None) self.mapper = field_attrs.get('mapper', None) self.pkey = field_attrs.get('pkey', None) self.fkey = field_attrs.get('fkey', None) self.property = field_attrs.get('property', None) self.column = field_attrs.get('col', None) self.table = field_attrs.get('table', None) if self.pkey: self._nullable = False def value_set_cb(self, emitter, value, oldvalue, obj): """Callback when value is set in the SQLA object""" # the signal we received from the record is not emitted when the # record is firstly built so we are sure it's not 'initial', we # advertise it as 'initial' so that mask won't complain it # was changed and ask if it needs saving. It surely does. # 2 situation reveal this: # ./demo -i 02, change d.t.current and commit, next forward, we don't # want to be prompted to save # ./demo 03, right click -> record in mask, change the date in the table # and change selected record, we don't want to be prompted to save try: if obj() is self.master.current and value != oldvalue: self.emit('value-set', value, True) self.initial_value = value except AttributeError, e: pass def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): """ Set the value :param value: the value to be set. It will be cleaned. :param initial: if True the ``self.initial_value`` is set as. ``self.initial_value`` is used to know if a field has changed :param obj: if passed the attribute is set on the object when the field is set :param update_widget: if True (default) the widget that renders the field is set as well. """ value = self.clean_value(value, obj=obj) if initial: self.initial_value = value if obj and self.persistent: if not value == getattr(obj, self.field_name): # this check shouldn't be needed but I verified that occasionally it would # make session.is_modified(obj) to get wrong ideas on the occurred changes # ./demo 02 -> ceate a new (empty record) # d.t.current.title = None -> d.t.current.date_release = None # d.t.session.is_modified(d.t.current) => True setattr(obj, self.field_name, value) else: self.emit('value-set', value, initial) def get_value(self, shown=False): """ Return the current value. Look for it in the widget and returns a cleaned value """ if not hasattr(self, 'widget'): return exc.MissingWidget("%s has no defined 'widget' " % self) value = self.widget.get_value(shown=shown) return self.clean_value(value) def clear_value(self): """ sets a value that clears the corresponding widget, can be None or [] """ self.set_value(None, initial=True) def set_completion(self, regexp): pass def has_changed(self, verbose=False): """ return True if field has changed after last set """ if not self.editable: return False try: return not self.get_value() == self.initial_value except exc.ValidationError: # a validation error is a clear sign the values have been changed, but we propabli don't # want to cope with them now return True except exc.NoCurrentObjError, e: # if no current Obj, no editing was done return False def get_default(self): """ return the default value for this object """ if hasattr(self, 'master'): value = self.master.defaults.get(self.field_name) else: value = None if not value: try: value = self.default except KeyError: pass if isinstance(value, ColumnDefault): if not callable(value.arg) and type(value.arg) == self.type: return value.arg else: ## FIXME: I'm not able to handle this now. raise NotHandledDefault if not isinstance(value, self.type): return return value def set_default(self, obj=None): try: self.set_value(self.get_default(), obj=obj) except NotHandledDefault, e: pass def clean_value(self, value, obj=None): """ return a value of the correct type, if needed parse it with locale parsers (numbers, date...) :param value: the value to be cleaned (i.e. casted to correct type). It's the attribute of a persisten object or **the object itself** for non persisted fields. I.e.: if you create a custom field to count how many movies has directed each director, the Director instance will be passed as ``value``. :param obj: the object to which the field belongs. It's normally disreguarded but it can be used by special fields (as image fields) to create customized property (e.g.: the save path) This function is used while sorting a column """ return value def format_value(self, value, format=None): """ return a **string representation** of the value according to current locale value is a"cleaned" value :param value: the value to be formatted (must already be casted to correct type) """ return value def validate(self, value, clean=False): """ check if the current value is accepted and call :ref:`on_field__validation ` on the master's hook. """ if not clean: try: value = self.clean_value(value) except Exception, e: msg = "Field %s could not validate value '%s': error was: %s" raise exc.ValidationError(_(msg % (field_name, e) )) if value is None and not self.nullable and not self.default: raise exc.NotNullableFieldError(self.field_name, master=self.master) self.master.run_hook('on_field_validation', value, self, field_name=self.field_name) return True def get_human_value(self, value, format=None): """ return the value or a translation in human readable for foreign key """ return self.format_value(value, format=format) def _get_persistent(self): try: return self._persistent except AttributeError: self._persistent = hasattr(self, 'property') and self.property return self._persistent persistent = property(_get_persistent) def __repr__(self): return "<%s - %s >" % (self.__class__.__name__, self.field_name) @classmethod def std_cleanup(cls, fn): """ A decorator that will handle standard cases: value is None, is a string or is already cleaned. This is handy when building new Fields as it allows to keep the ``.clean_value`` method as simple as possible, i.e. no need to check for standard cases:: class CountMovies(fields.IntegerField): ''' A field that counts the movies ''' @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the object itselt is passed return len(value.movies) """ def new_func(self, value): ## standard cleanup: values can if isinstance(value, (basestring, types.NoneType, self.type)): return super(self.__class__, self).clean_value(value) ## custom one return fn(self, value) new_func.__doc__ = fn.__doc__ return new_func class VarcharField(Field): """ The field to represent Strings """ Widget = 'VarcharWidget' type = str blank_ok = None """ The widget return an epty string on empty values. This variable determines if that value will be set NULL or left empty. Regardless of this value the value is left untouched if it already exists. """ def __init__(self, *args, **kw): Field.__init__(self, *args, **kw) self.blank_ok = BLANK_OK def clean_value(self, value, obj=None): if value == '' and not self.blank_ok: if not self.initial_value == '': value = None return value def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): if initial: self.initial_value = value value = self.clean_value(value) # if hasattr(self, 'widget') and update_widget: # self.widget.set_value(value or '', initial=initial) if obj and self.persistent: if not value == getattr(obj, self.field_name): setattr(obj, self.field_name, value) else: self.emit('value-set', value, initial) def get_value(self, shown=False): value = Field.get_value(self, shown) if value == '' and (self.initial_value is None or ( ## it's very annoying that a db with '' value is turned into None if I don't do anything! ## self.blank_ok should only set behaviour of NEW objects self.blank_ok is False and not self.initial_value == '')): return None return value ##### Numbers class IntegerField(Field): """ The fields to handle interegers """ Widget = 'IntegerWidget' type = int length = 8 format = None """How to represent integers. Default: '#,###' """ def __init__(self, *args, **kw): Field.__init__(self, *args, **kw) self.format = "#,###" self.locale = babel.default_locale('LC_NUMERIC') def clean_value(self, value, obj=None): if value in (None, ''): return None else: try: if isinstance(value, basestring): value = numbers.parse_number(value, locale=self.locale) elif isinstance(value, (int, long)): value = int(value) else: value = int(value) except Exception, e: raise exc.ValidationError(str(e)) return value def format_value(self, value, format=None): if value is None: return '' return numbers.format_decimal(value, format=format or self.format, locale=self.locale) @classmethod def std_cleanup(cls, fn): """ A decorator that will handle standard cases: value is None, is a string or is already cleaned. This is handy when building new Fields as it allows to keep the ``.clean_value`` method as simple as possible, i.e. no need to check for standard cases:: class CountMovies(fields.IntegerField): ''' A field that counts the movies ''' @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the object itselt is passed return len(value.movies) """ def new_func(self, value): ## standard cleanup: values can if isinstance(value, (basestring, types.NoneType, self.type, long)): return super(self.__class__, self).clean_value(value) ## custom one return fn(self, value) new_func.__doc__ = fn.__doc__ return new_func class FloatField(IntegerField): """ The fields to handle floats """ Widget = 'FloatWidget' type = float length = 10 format = None """How to represent integers. Default: None """ def __init__(self, *args, **kw): Field.__init__(self, *args, **kw) #self.format = "#.###,000" self.format = None self.locale = babel.default_locale('LC_NUMERIC') def clean_value(self, value, obj=None): if value in (None, ''): return None else: try: if isinstance(value, basestring): value = numbers.parse_decimal(value, locale=self.locale) elif isinstance(value, float): pass else: value = float(value) except Exception, e: raise exc.ValidationError(e) return value def format_value(self, value, format=None): if value is None: return '' return numbers.format_decimal(value, format=format or self.format, locale=self.locale) class DecimalField(IntegerField): """ The fields to handle Numeric Fields """ Widget = 'DecimalWidget' type = Decimal length = 10 scale = 2 """the number of decimals. Default is desumed by introspection""" format = None """How to represent integers. Default: '#,###.00' (The number of 0 determined by ``scale`` """ def __init__(self, *args, **kw): """ If column is defined precision and scale from column will be used. You need to set precision and scale after the field is created otherwise """ Field.__init__(self, *args, **kw) if self.column is not None: self.precision = self.column.type.precision or 8 self.scale = self.column.type.scale or 2 else: self.precision = 8 self.scale = 2 self.format = "#,###.%s" % ('0' * self.scale) self.format_f = "%%.%sf" % self.scale self.locale = babel.default_locale('LC_NUMERIC') def clean_value(self, value, obj=None): if value in (None, ''): return None else: try: if isinstance(value, basestring): ## why there's no parse_Decimal in babel? fl = self.format_f % numbers.parse_decimal(value, locale=self.locale) value = Decimal(fl) elif isinstance(value, Decimal): pass elif isinstance(value, int): value = Decimal(value) #value = self.clean_value(str(value)) elif isinstance(value, float): fl_str = numbers.format_decimal(value, locale=self.locale) value = self.clean_value(fl_str) else: raise exc.ValidationError(_("value is not Decimal nor string: %s" % value)) except Exception, e: raise exc.ValidationError(e) return value def format_value(self, value, format=None): if value is None: return '' return numbers.format_decimal(value, format=format or self.format, locale=self.locale) @classmethod def std_cleanup(cls, fn): """ A decorator that will handle standard cases: value is None, is a string or is already cleaned. This is handy when building new Fields as it allows to keep the ``.clean_value`` method as simple as possible, i.e. no need to check for standard cases:: class CountMovies(fields.IntegerField): ''' A field that counts the movies ''' @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the object itselt is passed return len(value.movies) """ def new_func(self, value): ## standard cleanup: values can if isinstance(value, (basestring, types.NoneType, self.type, int)): return super(self.__class__, self).clean_value(value) ## custom one return fn(self, value) new_func.__doc__ = fn.__doc__ return new_func class TextField(VarcharField): Widget = 'TextWidget' default_def_string = 'TXS' type = str def set_max_length(self): pass ##### Time/Dates class DateField(Field): """ The fields to handle datets """ Widget = 'DateWidget' default_def_string = 'd' format = None """The format used to represent dates. Default: ``short``""" type = date def __init__(self, *args, **kw): Field.__init__(self, *args, **kw) self.format = 'short' self.locale = babel.default_locale('LC_TIME') #if format not in ('short', 'long', 'default', 'medium', 'full'): # pass def clean_value(self, value, obj=None): if value in (None, ''): return None try: if isinstance(value, basestring): value = dates.parse_date(value, locale=self.locale) elif isinstance(value, date): pass else: raise exc.ValidationError(_("value is not date nor string: %s" % value)) except Exception, e: raise exc.ValidationError(e) return value def format_value(self, value, format=None): if not value: return '' return dates.format_date(value, format=format or self.format, locale=self.locale) class TimeField(DateField): """ The fields to handle times w/o timezone """ Widget = 'TimeWidget' default_def_string = 'ae' type = time def clean_value(self, value, obj=None): if value in (None, ''): return None if isinstance(value, basestring): try: value = dates.parse_time(value) except IndexError, e: try: value = dates.parse_time("%s:00" % value) except Exception, e: raise exc.ValidationError(e) return value def format_value(self, value, format=None): if not value: return '' if not isinstance(value, time): value = self.clean_value(value) return dates.format_time(value, format=format or self.format, locale=self.locale) class TimeTZField(TimeField): """ The fields to handle times with timezone """ Widget = 'TimeTZWidget' def __init__(self, *args, **kwargs): TimeField.__init__(self, *args, **kwargs) self.TZ = None def clean_value(self, value, obj=None): if value in (None, ''): return None if isinstance(value, basestring): value = dates.parse_time(value) return value def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): TimeField.set_value(self, value, initial=initial, obj=obj, update_widget=update_widget) if initial: if value: self.TZ = value.tzinfo self.microsecond = value.microsecond else: self.TZ = None self.microsecond = 0 def get_value(self, shown=False): value = Field.get_value(self, shown) if not self.TZ: ## Local will return a tzinfo with the timezone of the system ## it will be added to bypass the limitation of dateedit self.TZ = localtimezone.Local self.microsecond = 0 if value is None: return None value = time(*[int(i) for i in re.split('[.:]', value)]) return value.replace(tzinfo=self.TZ, microsecond=self.microsecond) class IntervalField(Field): """ The fields to handle times with interval """ Widget = 'IntervalWidget' type = timedelta class DateTimeField(DateField): """ The fields to handle datetimes w/o timezone """ Widget = 'DateTimeWidget' type = datetime def __init__(self, *args, **kwargs): DateField.__init__(self, *args, **kwargs) self.microsecond = 0 self.second = 0 def clean_value(self, value, add_second=False, add_microsecond=False, obj=None): """ parse and return a clean value (ie: a datetime) if add_second/add_microsecond, second and microsecond as found in self.(micro)second and stored there by set_value are attached to work around widgets that may not be able (or willing) return seconds and microseconds """ if value in (None, ''): return None if isinstance(value, basestring): tmp_date = re.split('\s+', value.strip()) try: date_value = dates.parse_date(tmp_date[0], locale=self.locale) except Exception, e: raise exc.ValidationError(_('Wrong date format: %s' % tmp_date[0] )) try: if len(tmp_date) > 1: time_list = [int(i) for i in re.split('[:.]', tmp_date[1]) ] else: time_list = [] time_value = time(*time_list) except Exception, e: raise exc.ValidationError(_('Wrong time format' )) value = datetime.combine(date_value, time_value) if isinstance(value, datetime): if add_second: value = value.replace(second=self.second) if add_microsecond: value = value.replace(microsecond=self.microsecond) return value def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): Field.set_value(self, value, initial=initial, obj=obj, update_widget=update_widget) if initial: if value: self.TZ = value.tzinfo self.microsecond = value.microsecond self.second = value.second else: self.TZ = None self.microsecond = 0 self.second = 0 def format_value(self, value, format=None): if not value: return '' return dates.format_datetime(value, format=format or self.format, locale=self.locale) def get_value(self, shown=False): value = Field.get_value(self, shown) if value and value.microsecond: self.microsecond = value.microsecond value = value.replace(microsecond=self.microsecond) return value class DateTimeTZField(DateTimeField): """ The fields to handle datetimes with timezone """ Widget = 'DateTimeTZWidget' def __init__(self, *args, **kwargs): DateTimeField.__init__(self, *args, **kwargs) self.TZ = None self.microsecond = 0 def clean_value(self, value, add_second=False, add_microsecond=False, obj=None): value = DateTimeField.clean_value(self, value, add_second, add_microsecond) if not self.TZ: ## Local will return a tzinfo with the timezone of the system ## it will be added to bypass the limitation of dateedit self.TZ = localtimezone.Local if value: if value.tzinfo: return value else: return value.replace(tzinfo=self.TZ) return None def get_value(self, shown=False): """ Implement a hack around the fact that you normally don't need TZ and I don't provide a cell_renderer that gives you the opportunity to change it Attach the same timezone as the original data """ value = Field.get_value(self, shown) return self.clean_value(value) ##### Boolean class BooleanField(Field): """ A field to handle booleans that does not allow NULL """ Widget = 'BooleanWidget' default_def_string = 'c' type = bool def clean_value(self, value, obj=None): if value is None: value = False if value not in (True, False): msg = "Null value not admittable for field %s" % self.field_name raise ValidationError(msg) return value def clear_value(self): ## should I check the default here? self.set_value(False) class BooleanNullField(Field): """ A field to handle booleans that allows NULL """ Widget = 'BooleanNullWidget' type = bool def clean_value(self, value, obj=None): if value not in (True, False, None): msg = "Null value not admissable for field %s" % self.field_name raise ValidationError(msg) return value class EnumField(Field): """ A field to handle a set of allowed values. You set ``values`` in the ``info`` column dict or setting :attr:`values`. Setting info column's key ``render`` to 'enum' triggers this field and a widget based on ComboBox. It doesn't currently use Sqlalchemy Enum type as it's not yet supported in sqlalchemy 0.5. """ Widget = 'EnumWidget' type = (str, int) keys = None """A list of allowe values in the orederd set by the programmer. Used to set the order of the Combo Box""" values = None """A dict: keys are the allowed values, values are te corresponding descriptions. It's up to the programmer to set this list appropriately. """ def __init__(self, *args, **kw): Field.__init__(self, *args, **kw) self.values = {} values = self.column.info.get('values', []) self.keys = [x[0] for x in values] for key, value in values: self.values[key] = value def clean_value(self, value, obj=None): """ Check if value is in the admitted values """ if value in (None, ''): return None if not value in self.keys: raise exc.ValidationError(_("Value '%s' is not accepted") % value) return value def lookup_value(self, value): """ Return the value to be shown """ return self.values[value] def get_value(self, shown=False): if not hasattr(self, 'widget'): return exc.MissingWidget("%s has no defined 'widget' " % self) return self.widget.get_value(shown=shown) class ImageField(VarcharField): """ Imge field suitable for VarcharField that hold an image path and should be rendered as image (icon in tables). It's never used when autoloading the database schema (no info on the database tells that a string represent an image path), it can be forced setting info values on the Column:: render: image base_dir: a directory thumbnail_size: tuple (width, height) default_size: tuple (width height) as in:: image = Column(String(100), info={'render' : 'image', 'base_dir' : '/path/to/images'} Image field that can create a thumbnail and resize an image into jpeg/png format using gtk.gdk.pixbuf* functions """ Widget = 'ImageWidget' type = str thumbnail_size = (50, 50) """size of the thumbnail used to render in treeview. Default 50,50. Can be set in Column.info as explained above.""" base_dir = os.getcwd() """base directory under which all files will be saved. Default is ``os.getcwd()`` unless is set at Schema time as shown above. :meth:`get_save_path` will use this to create a complete path. This can be set when defining the Column as shown above. """ default_size = None """If set it must be a tuple (width, height) and all files uploaded will be default to these dimentions if bigger. """ def __init__(self, *args, **kw): import gtk Field.__init__(self, *args, **kw) ## set base_dir from Column.info or os.getpwd() base_dir = self.column.info.get('base_dir', self.base_dir or os.getcwd()) base_dir = self.clean_path(base_dir) if base_dir and not os.path.isabs(base_dir): base_dir = os.path.abspath(os.path.join(os.getcwd(), base_dir)) self.base_dir = self.clean_path(base_dir) if not os.path.exists(self.base_dir): os.makedirs(self.base_dir) self.thumbnail_size = self.column.info.get('thumbnail_size', self.thumbnail_size) self.default_size = self.column.info.get('default_size', self.default_size) self.image_formats = ["." + x['name'] for x in gtk.gdk.pixbuf_get_formats()] + ['.jpg'] def clean_path(self, path): """ Return a path w/o backslashes as to be used from windows and linux interchangeably ``base_dir`` needs to be "cleaned" in this sense. """ #return path and re.sub(r"\\", '/', os.path.abspath(path)) path = path and os.path.normpath(path) # strip possible ./ return path and re.sub(r"\\", '/', path) def get_value(self, shown=False, complete_path=False): """ Return the path to the image :param shown: boolean: not meaningful for this field :param complete_path: boolean: compose it with base path """ if not hasattr(self, 'widget'): return exc.MissingWidget("%s has no defined 'widget' " % self) value = self.widget.get_value(shown=shown) if value and complete_path: value = value if os.path.isabs(value) else os.path.join(self.base_dir, value) else: value = value and re.sub('^%s/?' % self.base_dir, '', value) return self.clean_path(value) or self.initial_value def value_set_cb(self, emitter, value, oldvalue, obj): """ Propagate value set from the attribute extension in the mapped class """ if obj() is self.master.current: if value == oldvalue: self.set_value(value, initial=False) else: self.set_value(value, initial=True) def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True, new_name=None): """ Set the value and -if needed- copy the image file inside :attr:`base_dir` possibly resizing it to :attr:`default_size`. When resizing the only possible format is *jpeg* and the name is changed accordingly. :param value: the value to be set. It will be cleaned. :param initial: if True the ``self.initial_value`` is set as. ``self.initial_value`` is used to know if a field has changed :param obj: if passed the attribute is set on the object when the field is set :param update_widget: if True (default) the widget that renders the field is set as well. """ if initial: # an intial value comes from db and *is* correct by definition self.initial_value = value else: value = self.clean_value(value) or self.initial_value if hasattr(self, 'widget'): if initial: self.widget.copy_file = None # Needed only for table if update_widget: #path = value and os.path.join(self.base_dir, value) or None path = value and "%s/%s" % (self.base_dir, value) or None self.widget.set_value(path, initial=initial) if hasattr(self.widget, 'copy_file') and self.widget.copy_file: # Note if it has 'copy_-file' it also has new_filename that may be empty save_path = self.get_save_path(name=new_name or self.widget.new_filename or value, obj=self.master.current) if not os.path.splitext(save_path)[1]: save_path += os.path.splitext(self.widget.copy_file)[1] if not self.widget.copy_file == save_path: if self.default_size and self.is_image(self.widget.copy_file): ## I only resize into jpeg format save_path = os.path.splitext(save_path)[0] + '.jpeg' try: self.scale_file(self.widget.copy_file, save_path, *self.default_size) except ZoomException, e: # Don't resave if not needed. shutil.copyfile(self.widget.copy_file, save_path) else: shutil.copyfile(self.widget.copy_file, save_path) value = self.clean_value(save_path, obj=obj) # It can be changed if obj and self.persistent: if not value == getattr(obj, self.field_name): setattr(obj, self.field_name, value) def is_image(self, filename): """ Return if filename seems an image file, depending on file extension. """ ext = os.path.splitext(filename)[1] return ext.lower() in self.image_formats def exists(self, value=None): "Return True if the file pointed at value exists" value = value or self.get_value() if not value: return False return os.path.exists(os.path.join(self.base_dir, value)) def clean_value(self, value, obj=None): """ strip self.base_dir from the file if present """ if value in (None, ''): return None if value: return re.sub('^%s/?' % self.base_dir, '', self.get_save_path(value, obj=obj)) else: return None # def pil_create_thumbnail(self, filename): # try: # import Image # except ImportError: # return # path = os.path.join(self.base_dir, filename) # name, ext = os.path.splitext(filename) # i = Image.open(path) # i.thumbnail(self.thumbnail_size) # self.thumbnail_path = os.path.join(self.base_dir, name + "_thumb" + ext) # i.save(self.thumbnail_path) def get_save_path(self, name=None, obj=None): """ Return a standard save path. You may want to customize it. :param name: the name of the file as picked up from the filesystem or the desired new name for the file. :meth:`clean_value` call this also for already save path. :param obj: the object. This may be used to work out the path. It's not used by default. """ if not name: return None obj = obj or self.master.current if os.path.isabs(name): # if name is relative, it's used in clean_value on already correct values # and only base_dir should be added name = os.path.basename(name) return self.clean_path(os.path.join(self.base_dir, name)) def scale_pixbuf(self, pixbuf, width=None, height=None): """ scale pixbuf image with the same ratio so that it fits into self.w/self.h :param pixbuf: the pixbuf to scale :param width: the desider width :param height: the desider height """ import gtk pw, ph = pixbuf.get_width(), pixbuf.get_height() ratio = min(width/float(pw), height/float(ph)) if ratio > 1: raise ZoomException("Ratio is > 1 (%s)" % ratio) return pixbuf.scale_simple(int(pw*ratio), int(ph*ratio), gtk.gdk.INTERP_BILINEAR) def scale_file(self, image_path, dst_path, width, height): """ Scale input file into output file. Keep width/height ratio :param image_path: path of the image to resize :param dst_path: new name of the image :param width: the desired width :param height: the desired height """ import gtk pixbuf = gtk.gdk.pixbuf_new_from_file(image_path) pixbuf = self.scale_pixbuf(pixbuf, width, height) pixbuf.save(dst_path, 'jpeg') def create_thumbnail(self, filename, thumbnail_path=None): """ Create a thumbnail that will be used when rendering images in tables. Uses attribute :attr:`thumbnail_size` :param filename: the filename to create a thumbnail for """ import gtk if not self.is_image(filename): return path = os.path.join(self.base_dir, filename) thumbnail_path = thumbnail_path or self.get_thumbnail_path_with_size(filename) pixbuf = gtk.gdk.pixbuf_new_from_file(path) pixbuf = self.scale_pixbuf(pixbuf, *self.thumbnail_size) pixbuf.save(thumbnail_path, 'jpeg') def get_thumbnail(self, path=None, complete_path=False): """ Return a thumbnail *path* and create the thumbnail image, if it doesn't already exists :param path: the path of the image file. If missing it's found using :meth:`get_value` :param complete_path: return a complete path. Default is to return the path stripped from the :attr:`base_dir` """ ## first build the abs path of the image if path and not os.path.isabs(path): path = os.path.join(self.base_dir, path) path = path or self.get_value(complete_path=True) if not os.path.exists(path): ## Image does not exists, don't bother with thumbnail return thumbnail_path = self.get_thumbnail_path_with_size(path) if not os.path.exists(thumbnail_path): self.create_thumbnail(path) if complete_path: return thumbnail_path else: return self.clean_value(thumbnail_path, thumbnail=True) def get_thumbnail_path_with_size(self, filename): """ Return the thumbnail name for filename using default size. Place the thumbnail in a subdir of image dir '.thumbnail' used by :meth:`get_thumbnail` and :meth:`create_thumbnail`. The name contains the :attr:`thumbail_size` used to generate it. :param filename: the complete filename of the image """ path = os.path.join(self.base_dir, filename) dir, name = os.path.split(path) name, ext = os.path.splitext(name) thumbnail_dir = os.path.join(dir, ".thumbnail") if not os.path.exists(thumbnail_dir): os.mkdir(thumbnail_dir) thumbnail_path = os.path.join(thumbnail_dir, "%s-%sx%s%s" % (name, self.thumbnail_size[0], self.thumbnail_size[1], ext)) return self.clean_path(thumbnail_path) ##### Misc class ForeignKeyField(Field): """ A field to handle foreign keys """ Widget = 'ForeignKeyWidget' type = str def __init__(self, *args, **kwargs): Field.__init__(self, *args, **kwargs) from sqlkit.db.minspect import get_foreign_info self.lookup_values = {} self.broken_lookup = False self.table, self.column = get_foreign_info(self.fkey, names=False) self.table_lookup, self.column_lookup = self.table, self.column def lookup_value(self, field_value): """ retrieve the value in a lookup table in case of foreign_key. It means: "given the foreign key return a value describing at the best the referenced record". This implies some guessing of the best representation of the record or using information given to site-wide database configuration via _sqlkit_table. The details of such mechanism are described in :ref:`completion` and :ref:`TableDescr`. Since field_value may be incorrectly casted (it is used in completion) errors are catched and None is returned (rather than raising an Error) """ from sqlkit.db.utils import tables, get_description, DictLike from sqlalchemy import text, select, Table, exceptions if field_value is None: return '' ## broken_lookup is needed just for cases when a referenced column is not unique (FKey) # I think this is not regular but fiebird sample database do have such references... if self.broken_lookup: return field_value try: return self.lookup_values[self.field_name][field_value] except Exception, e: descr_fields = get_description(self.table_lookup) format = get_description(self.table_lookup, attr='format') fields = [self.table_lookup.columns[f_name] for f_name in descr_fields] sql = select(fields, self.column_lookup == field_value) try: all = self.table.metadata.bind.execute(sql).fetchall() except (exceptions.DBAPIError), e: if hasattr(self, 'master'): self.master.sb(e) return None if len(all) == 0: raise exc.LookupValueMissingValue("%s returns no values for '%s': '%s'" % ( sql, self.field_name, field_value)) elif len(all) > 1: msg = "%s returns multiple values for '%s': '%s'" % ( sql, self.field_name, field_value) warnings.warn(msg) self.editable = False self.broken_lookup = True return field_value #raise exc.LookupValueMultipleValues() else: value = format % DictLike(all[0]) try: self.lookup_values[self.field_name][field_value] = value return value except KeyError: self.lookup_values[self.field_name] = {} self.lookup_values[self.field_name][field_value] = value return value self.lookup_values[self.field_name][field_value] = value return value def set_master(self, master): Field.set_master(self, master) self._props_for_delete_orphan = minspect.get_props_for_delete_orphan( master.mapper, self.field_name) def clean_value(self, value, input_is_fkey=False, obj=None): """ Return a cleaned value (i.e.: the foreign key) or None if no value exists. :param input_is_fkey: If True the text is considered an id, else it's considered a 'search' value Note that if input_is_fkey=True not cleaning is performed. This operation is the opposite of lookup_value(). Note that clean_value only works if the widget with completion is present. """ if input_is_fkey: return value query = self.widget.completion.compose_select_statement( self.widget.completion.query, '=', value) ret = query.all() #ret = self.master.metadata.bind.execute(sql).fetchone() return ret and ret[0][0] or None def validate(self, value, clean=False): """ for an fkey field a 'clean' value is an fkey, by definition... """ input_is_fkey = clean try: value = self.clean_value(value, input_is_fkey=clean) except Exception, e: msg = "Field %s could not validate value '%s': error was: %s" raise exc.ValidationError(_(msg % (self.field_name, value, e) )) if value is None and not self.nullable: raise exc.NotNullableFieldError(self.field_name, master=self.master) self.master.run_hook('on_field_validation', value, self, field_name=self.field_name) return value def get_value(self, shown=False): if not hasattr(self, 'widget'): return exc.MissingWidget("%s has no defined 'widget' " % self) value = self.widget.get_value(shown) ## a shown value is not to be transformed if not shown and value == '' and self.initial_value is None: return None return value def get_human_value(self, value, format=None): if isinstance(value, (list, tuple)): return str([self.lookup_value(v) for v in value]) return self.lookup_value(value) def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): if not shown: value = self.clean_value(value, input_is_fkey=True) if initial: self.initial_value = value # if hasattr(self, 'widget') and update_widget: # self.widget.set_value(value, shown=shown, initial=initial) if value and not initial: ## Rationale: See ex_03 (that uses class_=Movie # i.e. with definition of relation with backref with delete-orphan). this function # is called all the time we enter a record (not just *new* records). ## in sqla >= 0.6 therés no way to understand this in an authomatic way, so that ## you are supposed to add 'attach_instance' key to Column's info. ## read more info in add_related_object's docstring for prop in self._props_for_delete_orphan: self.add_related_object(prop, value) if obj and self.persistent: if not value == getattr(obj, self.field_name): setattr(obj, self.field_name, value) else: self.emit('value-set', value, initial) def has_changed(self): ## special case, when is empty if self.initial_value is None and self.widget.get_value(shown=True) == '': return False if hasattr(self.widget, 'real_value') and self.widget.real_value == False: return True else: return Field.has_changed(self) def get_default(self): """ return the default value for this object """ ## The only difference from super is that I don't get out if there's a default if hasattr(self, 'master'): value = self.master.defaults.get(self.field_name) else: value = None if not value: try: value = self.default except KeyError: pass if isinstance(value, ColumnDefault): if not callable(value.arg) and type(value.arg) == self.type: return value.arg else: ## FIXME: I'm not able to handle this now. raise NotHandledDefault ## This has no meaning as self.type has no real meaning in a foreign key if not isinstance(value, self.type): pass return value def add_related_object(self, prop, key): """ Add an object to fullfill the constraint on delete-orphan This is not meant to be used directly, it is used by :meth:`set_value` If you have a relation with a delete-orphan constraint that would complain that is not attached to anybody configure the Column adding in the info keyword the ``attach_instance`` key pointing to the property of the relation to be added. In the demo you can find this example:: class Movie(Base): __tablename__ = 'movie' ... director_id = Column(Integer, ForeignKey('director.id'), nullable=False, info={'attach_instance': 'director'}) class Director(Base): __tablename__ = 'director' ... movies = relation('Movie', backref='director', cascade='all, delete-orphan',) Attaching a director_id via completion, requires that you attach a director instance as well. """ if not prop: return # if the backref that requires this is composed, probably things get more difficoult... assert len(prop.mapper.primary_key) == 1, "Sorry, sqlkit doesn't cope with " + \ "composed primary key in relations with delete-orphan" q = self.master.session.query(prop.mapper).autoflush(False) obj = q.get(key) if self.master.is_mask(): row_obj = self.master.current else: row_obj = self.master.get_selected_obj() setattr(row_obj, prop.key, obj) class CollectionField(Field): """ A field that manages a collection of objects Used in OneToMany or ManyToMany db fields. it's default widget is a collectionWidget that uses a SqlTable """ Widget = 'CollectionWidget' def __init__(self, *args, **kwargs): Field.__init__(self, *args, **kwargs) self.initial_value = [] def value_set_sb(self, *args): pass def set_widget(self, gtkwidget=None, def_str=None, widget=None): Field.set_widget(self, gtkwidget=gtkwidget, def_str=def_str, widget=widget) self.widget.table.connect('after_commit', self.set_initial_value) def set_initial_value(self, widget, obj, session): """ after_commit the 'new' initial value is the new value... """ self.initial_value = widget.records def get_default(self): ## FIXME: is this always correct? return None def clean_value(self, value, obj=None): from copy import copy if value is None: value = [] return copy(value) def set_value(self, value, initial=True, shown=False, obj=None, update_widget=True): self.initial_value = self.clean_value(value) if value is None: value = [] if hasattr(self, 'widget') and update_widget: self.widget.set_value(value, initial=initial) if obj and self.persistent: setattr(obj, self.field_name, value) def std_cleanup(fn): """ A decorator that will handle standard cases: value is None, is a string or is already cleaned. This is handy when building new Fields as it allows to keep the ``.clean_value`` method as simple as possible, i.e. no need to check for standard cases:: class CountMovies(fields.IntegerField): ''' A field that counts the movies ''' @fields.std_cleanup def clean_value(self, value): ## missing a field_name attribute on obj the object itselt is passed return len(value.movies) """ def new_func(self, value, obj=None): ## standard cleanup: values can if isinstance(value, (basestring, types.NoneType, self.type)) or \ self.type == int and isinstance(value,long): return super(self.__class__, self).clean_value(value) ## custom one return fn(self, value) new_func.__doc__ = fn.__doc__ return new_func sqlkit-0.9.5/sqlkit/db/0000755000175000017500000000000011714210425014263 5ustar sandrosandrosqlkit-0.9.5/sqlkit/db/utils.py0000644000175000017500000004307311714210425016004 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. _dbutils: Utilities ========= Hooks and Layout are the core for any customization. If you customize a Mask, chances are you would like to use that customization, any time you open that table, or you may need several different customization (eg. employees, manager may share some fields and differ on other fields). The following utilities give you the possibility to register Hooks and Layout to make them available to any sqlwidget to which no Hook/Layout is passed. Each time you register a hook/layout/class you can specify a nickname (eg.: customer/provider) Hooks ----- .. autofunction:: register_hook .. autofunction:: unregister_hook .. autofunction:: get_hook Layout ------ .. autofunction:: register_layout .. autofunction:: unregister_layout .. autofunction:: get_layout Classes ------- Classes can be registered as well. If you register a class, all the times you pass a table to a sqlwidget, the class will be used, so that ll relations will be available as well. This is particularly usefull in case you use :ref:`RecordInMask` that can open a table that may use a layout with m2m/m2o nested table that would result as unknown if the table was reflected from the db. .. autofunction:: register_class .. autofunction:: get_class Database -------- .. autofunction:: get_differences Descr ----- This module provide a bare simple Class to help creating __str__ and __repr__ for tables, using _sqlkit_table.format field tables ------ tables dictionary is a place where sqlkit looks for search_field and format for tables (see: ref:`sqlkit_model`). You can set values directly using TableDescr class or implicitely via database editing (``sqledit -c url``) .. _TableDescr: Table Description ----------------- .. autoclass:: sqlkit.db.utils.TableDescr :members: format, attrs, search, __init__ """ import re from sqlalchemy import Table, orm, select from sqlalchemy.exc import OperationalError, ProgrammingError, InterfaceError from defaults import register_hook, get_hook, register_layout, get_layout, register_class, \ get_class, unregister_hook, unregister_layout ######## descriptor from sqlkit.misc.utils import Container, DictLike, ObjLike class MissingMetadata(Exception): pass class Descr(object): """ Simple class that provide default __init__ and __str__ to be mixed when building classes with declarative layer __str__ will use 'format' description from database _sqlkit_fields """ def __init__(self, **kw): for key, val in kw.iteritems(): setattr(self, key, val) if not hasattr(self, '__tablename__'): self.__tablename__ = self.__table__.name def __str__(self): from sqlkit.db.utils import tables if not hasattr(self, '__tablename__'): self.__tablename__ = self.__table__.name try: if not self.__tablename__ in tables: format = get_description(self.__table__, attr='format') return tables[self.__tablename__].format % DictLike(self) except: return "<%s - %s>" % (self.__class__.__name__, hex(id(self))) def __getitem__(self, key): return getattr(self, key) or '' __repr__ = __str__ class Tables(Container): def __getitem__(self, key): try: return getattr(self, key) except AttributeError: try: table = TableDescr(key, metadata=db.metadata) return table except Exception, e: raise MissingMetadata("No metadata defined in sqlkit.db.utils") tables = Tables() class TableDescr(object): """ Handler for table search/format fields. This is an important component of :ref:`completion` as it determines: 1. what is searched for (`search` attribute) 2. how it will be represented (`format` attribute) TableDescr is automatically built from within :func:`get_description`. In __init__ it queries the database' sqlkit_table to see if any site-wide configuration is available. In turn :func:`get_description` is called within completion classes and :meth:`sqlkit.fields.ForeignKeyField.lookup_value` """ format = None """The format string used to represent the record. When no format string is passed to the constructor, the first string field of the table is used. That clearly may be totally wrong in some cases, that's why you can change it. May be eather a normal python format string (eg.: "%(title)s - %(year)s") in which case each prenthesized token must be a *field_name* or a simple string that again must be a *field_name* (e.g.: ``last_name``) The representation of a director as in our demo could be: ``%(first_name)s %(last_name)s`` Format string passed to TableDescr take precedence over format string present in the database. """ attrs = None """List of attributes required by the format string. Always includes pkey.""" search = None """this is the field that will be searched for in a foreign key. When using completion in a ForeignKey the database is queried using this field as a filter. E.g.: if in a SqlMask for a Movie you enter ``fe`` and then complete (Control-Enter) a query is build on the fly and sent to the database that selects the director's table with an additional ``"SELECT DISTINCT %(attrs) FROM director WHERE %s = 'fe'" % (attrs, search_field)``. It has the same default as :attr:`format` above. """ # FIXME: here description is an alias for search_field, in completion # description is an alias for format_field... pattern = '%\((\w+)\)' def __init__(self, table, format=None, pk=None, metadata=None, register=True): """ Handler for table search/format fields :param table: the table_name or the sqlalchemy Table :param format: a possible format string, used to represent the record. See :attr:`format` above. :param pk: suggests which pk are to be used :param metadata: necessary to pick primary key or when autoloading is required unless a Table object is given :param register: (boolean) if True (default) this Table Description will be registered as default """ if register: global tables else: tables = {} if isinstance(table, Table): self.table = table table_name = table.name elif isinstance(table, basestring): table_name = table elif hasattr(table, 'original'): # A Table.alias() self.table = table.original table_name = table.original.name self.table_name = table_name self.metadata = metadata tables[table_name] = self self.description, dbformat = self.guess_description() if format: self.set_format(format) elif dbformat: self.set_format(dbformat) else: self.set_format(self.description) ## I want self attr to have pkey, that's handy for completion if not pk: self.add_pk() else: if pk not in self.attrs: self.attrs.insert(0, pk) def get_attrs_from_format(self, format): """ Used when format is """ m = re.findall(self.pattern, format) if m: return m else: return [format] def set_format(self, format): m = re.findall(self.pattern, format) ## self.attrs is use to get the minimum possible info when completing if m: self.attrs = m self.format = format else: self.attrs = [format] self.format = "%%(%s)s" % format self.search = format def add_pk(self): table = self.get_table() pkeys = table.primary_key.columns for field in pkeys: if field.name not in self.attrs: self.attrs.insert(0, field.name) def get_table(self): """ return the sqlalchemy.Table or try to get it from metadata """ try: return self.table except AttributeError: if not self.metadata: raise MissingMetadata("Description for '%s' cannot be guessed" % self.table_name) ## first time we meet this table -> inspect w/ autoload return Table(self.table_name, self.metadata, autoload=True) def guess_description(self): """look if a description (format) field is defined, if not use introspection return the description """ table = self.get_table() description, format = get_description_from_sqlkit(table) if description: return description, format for field_name in table.c.keys(): if re.search("string|text|char", table.c[field_name].type.__class__.__name__, re.I): break return field_name, format def __str__(self): return "table: %s - description: %s\n format: %s" % (self.table_name, self.description, self.format) def __repr__(self): return "table: %s - format: %s" % (self.table_name, self.format) def get_table_name(table): if isinstance(table, Table): table_name = table.name elif isinstance(table, basestring): table_name = table elif hasattr(table, 'original'): # A Table.alias() table_name = table.original.name return table_name def get_description(table, metadata=None, attr='attrs'): """ return info on table according to data already available or guessing by introspection of the table :param table: the sa table or table_name for which we search the description :returns: 'attrs' or what defined by 'attr' keywrord arg """ table_name = get_table_name(table) try: return getattr(tables[table_name], attr) except MissingMetadata, e: if isinstance(table, Table): return getattr(TableDescr(table), attr) return getattr(TableDescr(table_name, metadata=metadata), attr) def get_description_from_sqlkit(table, metadata=None): """ get the description to use in completion and __str__ from database table _sqlkit_table :param table: the sa table or table_name for which we search the description :returns: a tuple (search_field, format) that may be (None, None) """ if isinstance(table, Table): metadata = table.metadata from sqlkit.db.sqlkit_model import get_classes table_class, field_class = get_classes(metadata.bind) if not metadata.bind.has_table(table_class.__tablename__): return None, None try: tbl = table_class.__table__ sql = select([tbl.c.search_field, tbl.c.format], tbl.c.name == table.name) res_proxy = metadata.bind.execute(sql) descr = res_proxy.fetchone() if not descr: descr = None, None except (OperationalError, ProgrammingError), e: #_sqlkit_table is not defined descr = (None, None) return descr def get_labels_and_tips_from_sqlkit(table, metadata=None): """ get the labels and tips to use in label_map from table _sqlkit_field """ if isinstance(table, Table): metadata = table.metadata table_name = table.name else: table_name = table from sqlkit.db.sqlkit_model import get_classes table_class, field_class = get_classes(metadata.bind) if not metadata.bind.has_table(field_class.__tablename__): return {} descr = {} try: tbl = field_class.__table__ sql = select([tbl.c.table_name, tbl.c.name, tbl.c.description, tbl.c.help_text], tbl.c.table_name == table_name) res_proxy = metadata.bind.execute(sql) for record in res_proxy.fetchall(): descr[record.name] = (record.description, record.help_text) except (OperationalError, ProgrammingError), e: #_sqlkit_field is not defined pass return descr ################### def get_differences(obj): """ show differences between old and new version of an object this is a generator, you should use as in:: for field_name, old_value, new_value in get_differences(obj): print 'field %s changed from %s, to %s' % (field_name, old_value, new_value) :param obj: the object to look for changes this function uses ``sqlalchemy.orm.attributes.get_history`` but differs in 2 ways: * it only yield *changed* values * it returns the simple value (not a list) if the property is not a RelationProperty with direction MANYTOMANY or ONETOMANY (i.e.a collection) """ session = orm.object_session(obj) mapper = orm.class_mapper(obj.__class__) for prop in mapper.iterate_properties: new = None old = None try: new, unchanged, old = orm.attributes.get_history(obj, prop.key) except AttributeError, e: # sqla 0.5.0 -> 0.5.1 new, unchanged, old = orm.attributes.get_history( orm.attributes.instance_state(obj), prop.key) if new or old: ## Not sure about this code. I don't want to have to cope with ## a list if there is no need for that (i.e. is not a collection) if isinstance(prop, orm.properties.RelationProperty) and ( prop.direction.name in ('MANYTOMANY', 'ONETOMANY')): yield prop.key, old, new else: try: old = old[0] except (TypeError, IndexError): old = None try: new = new[0] except (TypeError, IndexError): new = None yield prop.key, old, new def get_history(obj, field_name, session=None): """ show the history of a field of an object :param obj: the object to look for history :param field_name: the field name of which we want to know the history :return: new, unchanged, old """ mapper = orm.class_mapper(obj.__class__) for prop in mapper.iterate_properties: if prop.key == field_name: try: try: new, unchanged, old = orm.attributes.get_history(obj, prop.key) except AttributeError, e: # sqla 0.5.0 -> 0.5.1 new, unchanged, old = orm.attributes.get_history( orm.attributes.instance_state(obj), prop.key) if isinstance(prop, orm.properties.RelationProperty) and ( prop.direction.name in ('MANYTOMANY', 'ONETOMANY')): return new, unchanged, old else: return new and new[0] or '', unchanged and unchanged[0] or '', old and old[0] or '' except Exception, e: pass def clean_for_markup(text): """ quote string from undesired chars in pango markup """ from gobject import markup_escape_text if isinstance(text, (tuple, list)): text = [markup_escape_text(str(item)) for item in text] else: text = markup_escape_text(str(text)) return str(text) def get_fields(table, metadata=None, name=True): """ return all field_names for this table table may be a sqlalchemy.Table or a table_name in which case a metadata must be provided as well. If a registered class is found, all properties are returned, i.e. also relation properties (PropertyLoaders). This may change...? """ # first try if a registered class exists class_ = get_class(table) if class_: mapper = orm.class_mapper(class_) if name: return mapper.c.keys() else: return [c for c in mapper.c] else: if not isinstance(table, Table): if not metadata: raise MissingMetadata("A metadata must be provided as autoload is needed") try: table = Table(table, metadata, autoload=True) except Exception, e: e.message = "Missing table: %s" % table raise e if name: return table.c.keys() else: return [c for c in table.c] if __name__ == '__main__': from sqlkit.db import proxy db = proxy.DbProxy(engine="sqlite://///misc/src/hg/py/sqlkit4/doc/db/movies.sqlite") movie = Table('movie', metadata=db.metadata, autoload=True) #t = TableDescr('movie', '%(dirctor_id)s - (%(nation)s)', metadata=db.metadata) tables['movie'].attrs print get_description(movie, attr='format') print tables.movie print get_description('movie', metadata=db.metadata) print get_description('movie', metadata=db.metadata, attr='format') sqlkit-0.9.5/sqlkit/db/__init__.py0000644000175000017500000000011311714210425016367 0ustar sandrosandrofrom defaults import register_hook, get_hook, register_layout, get_layout sqlkit-0.9.5/sqlkit/db/django_syntax.py0000644000175000017500000003423211714210425017511 0ustar sandrosandro# Copyright (C) 2008-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Django syntax utilities ======================= This module provides some functions to allow use of django syntax in filters. You can look an introduction on lookup filters on `django's site`_ but mind that we don't even share the same syntax. We just borrowed the idea that the operator and possibly the join can be wired in the keyword *name*. The aim is not at all compatibility with django (that has nothing to do with this framework) but a way to: * make it easy to add filters and constraints * make it easy to store queries (that need to store filters and constraints) These function are used in sqlfilter and in completion beside ``.add_constraint(...)`` method of sqlwidget. You never *need* to use these functions. If you prefere you can changed the query directly with SQLAlchemy syntax, and surely there are situations where that's necesssary, nevertheless there are situations where this syntax allows to obtain the same result in less and more readeable code, probably just one line. All of these functions act on a mapper becouse it has info on PropertyLoaders. In completion, the mapper is used to follow the *join_path* even if the query (a ``sqlalchemy.select``), will not be issued on a ``session.query`` object .. _`django's site`: http://docs.djangoproject.com/en/dev/topics/db/queries/#field-lookups-intro supported lookup ---------------- :like/ilike: use like operator, ilike if available :notlike/notilike: negate like operator, ilike if available :regexp/iregexp: now ~/~* for postgres and REGEX for mysql like again for the rest :notregexp/notiregexp: now !~/!~* (only for postgresql) :lt/gt: less than / grater than (<, >) :lte/gte: less than equal/ grater than equal(<=, >=) :eq/neq: equal / not equal ( = , !=) :in/notin: IN (argument must be a list)/ negated :null: IS NULL / IS NOT NULL (depending if arg is True/False) conjunctions ------------ :func:`django2sqlalchemy` and :func:`django2query` connect all arguments with AND operator by default. It's possible to use ``OR`` operator adding ``OR=True`` argument. .. autofunction:: django2sqlalchemy .. autofunction:: django2query """ from copy import deepcopy from sqlalchemy.sql.expression import ColumnElement, ClauseList from sqlalchemy.sql import and_, or_, not_ from sqlalchemy import Table from sqlalchemy.orm import ColumnProperty, PropertyLoader, class_mapper from sqlalchemy.types import Date, DateTime from sqlkit.misc.datetools import string2dates from sqlkit.db import utils OPERATORS = { 'icontains' : 'icontains', 'notlike' : 'not like', 'like' : 'like', 'ilike' : 'ilike', 'notilike' : 'not ilike', 'iregexp' : '~*', 'regexp' : '~', 'notiregexp' : '!~*', 'notregexp' : '!~', 'lt' : '<', 'gt' : '>', 'lte' : '<=', 'gte' : '>=', 'eq' : '=', 'neq' : '!=', 'equal' : '=', 'in' : 'IN', 'notin' : 'NOT IN', 'null' : 'IS NULL', 'notnull' : 'IS NOT NULL', } class DjangoParser(object): """ Parser for django-like syntax. Parses a django-like argument to compose a sqlalchemy ColumnList/ColumnElement object to be used in queries """ def __init__(self, mapper_or_table=None, query=None, OR=False, aliased=False, outer=False): """ A sqlalchemy.Table object :param mapper_or_table: the mapper or table to be parsed :param query: the query that will be use as base :param OR: (boolean) it True all conditions will be ORed :param aliased: (Boolean) passed to join conditions if any :param outer: the join will be an OUTER JOIN """ self.aliased = aliased self.outer = outer if isinstance(mapper_or_table, Table): self.mapper = None self.table = mapper_or_table class_ = utils.get_class(self.table.name) if class_: self.mapper = class_mapper(class_) else: self.mapper = mapper_or_table self.table = self.mapper.local_table self.query = query self.OR = OR self.expressions = [] self.joins = [] def parse(self, kw): """ must be a single condition parse a django-like condition """ if self.mapper: join_path, op, op_str, value, col, join_args = django2components(self.mapper, kw=kw) else: join_path, op, op_str, value, col, join_args = django2components(self.table, kw=kw) self.joins += [join_path] if op_str == 'icontains': eng_name = self.table.metadata.bind.name if eng_name.startswith('postgres'): sql = col.op('~*')(value) elif eng_name.startswith('mysql'): sql = col.op('REGEXP')(value) else: sql = col.ilike("%%%s%%" % value.strip('%')) elif op_str == 'null': sql = col == value ## True | False elif op_str == 'ilike': # SA manages difference where there is no ILIKE operator sql = col.ilike(value) elif op_str == 'notilike': # SA manages difference where there is no ILIKE operator sql = not_(col.ilike(value)) elif op_str == 'in': sql = col.in_(value) elif op_str == 'notin': sql = not_(col.in_(value)) elif self.is_date(col): if isinstance(value, basestring): start, stop = string2dates(value) else: start, stop = value, None if stop: sql = col.op('>=')(start) & col.op('<=')(stop) else: sql = col.op(op)(start) else: sql = col.op(op)(value) self.expressions += [sql] def is_date(self, col): if isinstance(col.type, (Date, DateTime)): return True return False def sql(self): if self.OR: sql = or_(*self.expressions) else: sql = and_(*self.expressions) return sql def compose(self): for path in self.joins: if path: if self.outer: self.query = self.query.outerjoin(aliased=self.aliased, *path).reset_joinpoint() else: self.query = self.query.join(aliased=self.aliased, *path).reset_joinpoint() return self.query.filter(self.sql() ) ####### Functions ############ def django2components(mapper_or_table, kw=None): """ Split a single argument/value into a tuple of 5 components: :path: what should be used in a join to reach the column of the expression :op: simbolic version of the operator (eg: <=) :str_op: string version of the operator (eg: lte) :value: value of the argument :col: sqlalchemy column on which the expression will operate :join_args: args for a query.join. At present is only returned for first_step joins and is a tuple (Table, join_condition). Is None when no join is needed and is False if the len(path) > 1 (as in address__city__country__in=...) return: joins, op, op_str, value, col, join_args Es.:: >>>> djs.django2components(Address.__mapper__, {'date_created__gte' : 'm'}) ([], '>=', 'gte', 'm', Column('date_created', Date(), table=
    ), None) >>>> djs.django2components(Address.__mapper__, {'user_id__first_name' : 'ed'})[:-1] (['user_id'], '=', 'eq', 'ed', Column('first_name', String(length=50, convert_unicode=False, assert_unicode=None), table=), (Table('user', MetaData(Engine(sqlite://)), ...), ,))) """ if isinstance(mapper_or_table, Table): mapper = None table = mapper_or_table else: mapper = mapper_or_table table = mapper.local_table name = kw.keys()[0] value = kw[name] tokens = name.split('__') joins = [] join_args = None if len(tokens) == 1: # field_name='abc' op_str = 'eq' attr = tokens[0] try: col = getattr(table.columns, attr) except AttributeError, e: col = getattr(mapper.c, attr) value = kw[attr] else: # data_gte=today() -- address__city__country__in=['Italy','France'] if tokens[-1] in OPERATORS: op_str = tokens.pop() else: op_str = 'eq' attr = tokens.pop() if tokens: # may be consumed by now... joins = deepcopy(tokens) col = get_foreign_column(mapper, path=tokens, attr=attr) if len(joins) > 1: ## I'm not going to calculate join conditions for longer path... too complex now join_args = False else: ## this is used in 2 cases: the join on a foreign key, so that order_by can work ## correctly, and possibly filter can avoid a subselect try: col0 = getattr(mapper.columns, joins[0], None) except AttributeError, e: # i think this is never really tested. It was here when I used to pick the # element from the table but when using mapper/table expressing joins # the name of the column changes while mappers have the name we expect col0 = getattr(table.columns, joins[0], None) if col0 is not None: join_args = (col.table, col0 == col0.foreign_keys.copy().pop().column) else: join_args = False else: join_args = None try: col = getattr(table.columns, attr) except AttributeError, e: col = getattr(mapper.c, attr) #col = table.columns[attr] op = OPERATORS[op_str] if op_str in ('regexp', 'iregexp'): op, value = get_regexp_op(table.metadata.bind.name, op_str, value) return joins, op, op_str, value, col, join_args def get_foreign_column(mapper, path=None, attr=None): """ follow PropertyLoaders chain to get the last referenced column """ path = deepcopy(path) if not mapper: raise NotImplementedError("Can't cross relationship if you don't provide a mapper") prop = mapper.get_property(path.pop(0)) for elem in path: prop = prop.mapper.get_property(elem) return get_column(prop, attr) def django2query(query, mapper_or_class, OR=False, aliased=False, OUTER=False, **kw): """ return a new query with new constraints expressed in 'django-style' :param query: the query to which filters will be applied :param mapper_or_class: a mapper or a class from which the class will be devised :param aliased: passed to :class:`DjangoParser` :param OUTER: :class:`DjangoParser` will be instantiated as an OUTER join """ from sqlalchemy.orm import class_mapper, Mapper mapper = mapper_or_class if isinstance(mapper_or_class, Mapper): mapper = mapper_or_class else: mapper = class_mapper(mapper_or_class) parser = DjangoParser(mapper, query, OR=OR, aliased=aliased, outer=OUTER) for key,val in kw.iteritems(): parser.parse( {key:val}) return parser.compose() def django2sqlalchemy(mapper=None, table=None, OR=False, **kw): """ Return a tuple (ClauseList, join path). :param mapper: the mapper that will be used in the query :param table: the mapper that will be used in the query :param OR: if True the conditions will be ORed :param kw: the conditions as per django-like syntax """ if table: parser = DjangoParser(table, query=None, OR=OR) else: parser = DjangoParser(mapper, query=None, OR=OR) for key,val in kw.iteritems(): parser.parse( {key:val}) return parser.sql(), parser.joins def get_column(prop, attr): """ return the column(s?) referenced from a property """ if isinstance(prop, ColumnProperty): col = prop.columns[0] if col.foreign_keys: ## FIXME: Should I consider multiple fkey? return getattr(col.foreign_keys.copy().pop().column.table.c, attr) else: return prop.columns[0] ### FIXME: why colums is a list? elif isinstance(prop, PropertyLoader): return getattr(prop.mapper.local_table.c, attr) def path2join(path, mapper): table = mapper.local_table join = table for elem in path: for token in elem: prop = mapper.get_property(token) if isinstance(prop, ColumnProperty): col = prop.columns[0] if col.foreign_keys: ## FIXME: Should I consider multiple fkey? table = col.foreign_keys.copy().pop().column.table elif isinstance(prop, PropertyLoader): table = prop.remote_side[0].table join = join.join(table) return join def get_regexp_op(bind_name, op_str, value): """ return the best approximation to regexp operator in sqlite fall back to (I)LIKE """ if bind_name.startswith('postgres'): if op_str == 'regexp': return '~', value else: return '~*', value elif bind_name.startswith('mysql'): return 'REGEXP', value else: return 'like', '%' + value + '%' sqlkit-0.9.5/sqlkit/db/proxy.py0000644000175000017500000002242611714210425016024 0ustar sandrosandro# Copyright (C) 2008-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re from sqlalchemy import Table, MetaData, sql, __version__ from sqlalchemy.orm import sessionmaker, mapper from sqlalchemy.orm.interfaces import SessionExtension from pkg_resources import parse_version try: from sqlalchemy import event except ImportError: pass from sqlkit import _ from sqlkit.db import minspect, utils from sqlkit.exc import MissingPrimaryKey class DbProxy(object): """ A proxy to the database... it's meant to easy the way we pass information to the sqlwidget in the simpler situation: * if no metadata is passed, MetaData() will create it * if engine is passed it will be put in metadata as 'bind' attribute * if no session is passed a session is created via sessionmaker db = proxy.DbProxy(engine='postgres://localhost/fossati') session = db.get_session() at that point sqlwidget will retrieve info this way:: SqlTable(table="ticket_project", dbproxy=self.dbproxy) session defaults are: autoflush=False, autocommit=False """ def __init__(self, metadata=None, engine=None, bind=None, expire_on_commit=False, Session=None, autocommit=True, autoflush=False): if metadata: self.metadata = metadata else: self.metadata = MetaData() if engine or bind: self.metadata.bind = engine or bind if not Session: try: self.Session = sessionmaker(bind=self.metadata.bind, expire_on_commit=expire_on_commit, autoflush=autoflush, autocommit=autocommit, ) self.Session() # test if expire_on_commit is accepted except: self.Session = sessionmaker(bind=self.metadata.bind, autoflush=autoflush, autocommit=autocommit, ) else: self.Session = Session self.engine = self.metadata.bind def get_session(self): sess = self.Session() if parse_version(__version__) < parse_version('0.7'): extension = SKSessionExtension() sess.extensions += [extension] else: emitter = Emitter() sess._sk_emitter = emitter event.listen(sess, 'after_flush', emitter.after_flush) event.listen(sess, 'after_flush_postexec', emitter.after_flush_postexec) event.listen(sess, 'after_commit', emitter.after_commit) return sess def get_table(self, name): """return a sqlalchemy.Table object from a table_name. Uses self.metadata""" if name in self.metadata.tables: return self.metadata.tables[name] else: return Table(name, self.metadata, autoload=True) def get_mapper(self, mapper_obj, tables): """If no mapper is provided it will be built from the table(s) tables may be: a list of table_names a list of sqlalchemy.Tables a single table_name a single sqlalchemy.Table a string of table_names when given as strings, table names will be joined automatically if a || sign is between 2 tables, outer join will be used, if nothing or | is used .join will be used. """ from sqlalchemy.sql import Join if mapper_obj: return mapper_obj ## tables may be 'table1, table2, table3' if isinstance(tables, (Table, Join)): tables = [tables] elif not isinstance(tables, list): tables = re.sub("(\|\|?)", r" \1 ", tables) tables = re.split('[ ,]+', tables) tables = [self.get_table(tbl) for tbl in tables] if len(tables) == 1: return table2mapper(tables[0]) else: return self.join_tables(tables) def join_tables(self, tables, master=None): """ join two or more tables in a mapper sqlalchemy.sql.Join tables can be string or sqlalchemy.Table object to force outer join || can be used, default is simple join (|) """ class JoinAuto(object): pass j = None t1 = tables[0] mode = 'join' for t in tables[1:]: if t == '|': mode == 'join' continue elif t == '||': mode = 'outerjoin' continue if j: t1 = j j = self.join2tables(t1, t, mode=mode) m = mapper(JoinAuto, j) return m def join2tables(self, t1, t2, mode='outerjoin'): """return a join of 2 tables. Accept strings or sqlalchemy.Table""" tbl = {} ## if isinstance(t1, str): T1_ret = True T1 = Table(t1, self.metadata, autoload=True) else: T1_ret = False T1 = t1 if isinstance(t2, str): T2_ret = True T2 = Table(t2, self.metadata, autoload=True) else: T2_ret = False T2 = t2 if mode == 'outer': return T1.outerjoin(T2) else: return T1.join(T2) class Emitter(object): """Indirection component, needed to propagate session signals to widgets SQLA as of 0.7.2 doen not have a way to remove a listener, so an indirection layer is needed """ sk_widgets = None def after_flush(self, session, flush_context): """ implement the after-flush signal """ loop_run_hooks_over_widgets(self, session, 'on_after_flush', 'after-flush') def after_flush_postexec(self, session, flush_context): """ implement the after-flush-postexec signal """ loop_run_hooks_over_widgets(self, session, 'on_after_flush_postexec') def after_commit(self, session): """ implement the after-commit hook """ loop_run_hooks_over_widgets(self, session, 'on_after_commit', 'after-commit') def add(self, widget): "Add a sqlkit widget to the list of listenes" self.sk_widgets = self.sk_widgets or [] self.sk_widgets += [widget] def remove(self, widget): "Add a sqlkit widget to the list of listenes" self.sk_widgets.remove(widget) class SKSessionExtension(Emitter, SessionExtension): pass def loop_run_hooks_over_widgets(ext, session, hook_name, signal_name=None): widgets = getattr(ext, 'sk_widgets', []) or [] for sqlwidget in widgets: current= sqlwidget.get_current_obj() if sqlwidget: sqlwidget.run_hook(hook_name, current, session) if signal_name: sqlwidget.emit(signal_name, current, session) def table2mapper(table): """ return a mapper for a class created on the fly. table is a sqlalchemy.Table obj """ X = get_default_class(table) if table.primary_key: m = mapper(X, table) else: primary_key = search_unique_keys(table) if primary_key: m = mapper(X, table, primary_key=primary_key) else: ## TIP: when assembling a mapper that does not have primary key msg = _("Table %s doesn't have a primary key, editing is not possible") % table.name raise MissingPrimaryKey(msg) return m def get_default_class(table): """ Create on the fly a proper class. utils.get_description() is used to set appropriate __str__ representation :param table: a sqlalchemy.Table object """ name = "%s_class" % (table.name) format = utils.get_description(table, metadata=table.metadata, attr='format') def __str__(self): try: return format % vars(self) except KeyError: ## empt classe miss attributs... return "< %s: %s >" % (table.name.title(), hex(id(self))) def __repr__(self): return "< %s: %s >" % (table.name.title(), str(self)) if isinstance(table, str ): table = self.get_table(name) name = name.encode('utf-8') X = type(name, (object,), {'__repr__' : __repr__, '__str__' : __str__}) return X def search_unique_keys(table): """ return a column with unique index to be used if no other primary keys """ for index in table.indexes: if index.unique: return index.columns sqlkit-0.9.5/sqlkit/db/sqlkit_model.py0000644000175000017500000000640411714210425017330 0ustar sandrosandro# Copyright (C) 2008-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. _sqlkit_model: SQLkit models ============== Sqlkit does not need any private table, but can take advantage from the presence of a couple of them to handle via database some trivial jobs. tables ------- The jobs that can be esied by the presence ot these tables are: 1. definition of format string to be used when showing a completion on foreign key fields: this can be eg: ``%(first_name)s %(last_name)s`` to use composition of first and last name to represent a person. If this is not set, the description field of the foreign table is used (see below) 2. search_format: field_name that should be searched for in completion. This must be a single field. In case this is not set the first chhar field is taken. fields ------- 1. name that should appear in the layout for each field 2. autostart completion (not yet) 3. help_text: text to be represented in a tooltip sqledit hook ------------ you can edit these tables directly from sqledit with the option --configure:: sqledit --configure sqlite:// """ import re from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Table, Column, String, Integer, ForeignKey from sqlalchemy.orm import relation import utils Base = declarative_base() def get_classes(bind): global SqlkitTable, SqlkitFields try: return SqlkitTable, SqlkitFields except: if re.match('firebird', bind.name): SQLKIT_TABLE = 'sqlkit_table' SQLKIT_FIELD = 'sqlkit_field' else: SQLKIT_TABLE = '_sqlkit_table' SQLKIT_FIELD = '_sqlkit_field' class SqlkitTable(Base, utils.Descr): __tablename__ = SQLKIT_TABLE name = Column(String(100), primary_key=True) search_field = Column(String(50)) format = Column(String(150)) class SqlkitFields(Base, utils.Descr): __tablename__ = SQLKIT_FIELD table_name = Column(String(100), ForeignKey('%s.name' % SQLKIT_TABLE), primary_key=True) name = Column(String(100), primary_key=True) description = Column(String(100)) help_text = Column(String(300)) regexp = Column(String(100)) autostart = Column(Integer) default = Column(String(200)) table = relation('SqlkitTable', backref='attributes', primaryjoin = table_name == SqlkitTable.name) return SqlkitTable, SqlkitFields sqlkit-0.9.5/sqlkit/db/minspect.py0000644000175000017500000004631111714210425016464 0ustar sandrosandroimport sys import re import datetime import decimal import sqlalchemy from sqlalchemy.orm import properties import sqlalchemy.types as sqltypes from sqlkit import debug as dbg, exc from sqlkit.misc.utils import Container from sqlalchemy.sql import expression SA_TYPE_MAP = { sqlalchemy.types.String : str, sqlalchemy.types.Unicode : str, sqlalchemy.types.Integer : int, sqlalchemy.types.SmallInteger : int, sqlalchemy.types.Float : float, sqlalchemy.types.DateTime : datetime.datetime, sqlalchemy.types.Date : datetime.date, sqlalchemy.types.Interval : datetime.timedelta, sqlalchemy.types.Time : datetime.time, sqlalchemy.types.Numeric : decimal.Decimal, sqlalchemy.types.Text : str, sqlalchemy.types.UnicodeText : str, sqlalchemy.types.Boolean : bool, sqlalchemy.types.Binary : str, # ???? TODO: this is probably useless # ???? but w/o you get errors browsing a db that uses them... } try: SA_TYPE_MAP[sqlalchemy.types.LargeBinary] = str except AttributeError: pass # sqlalchemy < 0.6 PROPERTIES = properties class MissingPrimaryKeyError(Exception): pass class InspectMapper(Container): """inspect a mapper and gather all info on fields normally used. Adds attributes for each field_name pointing to a dictionary with: table_name (string) name (string) table (sqlalchemy Table obj) db_type (db type) type (python-type) col_spec (string) eg: INTEGER, STRING length None or a Number property the property used to access these data """ field_list = None # list of field_names in the mapper (all possible tables) props = None # dict of properties pkeys = None # dict table:[pkey1,...] def __init__(self, mapper, noup=None): """ noup: set of fields that will get a rw=False field ro: bool. if True no update/insert/delete will be possible """ global SA_TYPE_MAP try: # FIXME: these import should be avoided!... import sqlalchemy.dialects.postgresql import sqlalchemy.databases SA_TYPE_MAP.update({ sqlalchemy.databases.postgres.PGInet : str, sqlalchemy.databases.postgres.PGInterval : datetime.timedelta, sqlalchemy.dialects.postgresql.base.INTERVAL : datetime.timedelta, }) except Exception, e: pass self.field_list = [] self.fields = Container() self.primarytable = self.get_primary_table self.noup = noup or set() self.mapper = mapper self.props = [] self.pkeys = {} self.table_mappers = {} # mappers to tables self.n = 0 self.loop_on_props() def get_py_type(self, Type, field_name): for typ in (Type,) + Type.__mro__: try: return SA_TYPE_MAP[typ] except KeyError: continue msg = "Couldn't resolve SA type for field_name '%s' %s" % (field_name, Type) raise NotImplementedError(msg) def loop_on_props(self): """Add field gathering info from the mapper At the mome we only use properties that are """ props = self.mapper.iterate_properties for prop in props: if isinstance(prop, properties.ColumnProperty): self._add_field(prop.key, self.mapper, property=prop) elif isinstance(prop, properties.PropertyLoader): self._add_loader_field(prop.key, self.mapper, property=prop) def _add_field(self, field_name, mapper, property=None): if field_name in self: dbg.write("%s precedentemente definito, cerchi guai..." % field_name) return self.field_list += [ field_name ] col = mapper.c[field_name] try: col_spec = mapper.c[field_name].type.compile( dialect=mapper.local.table.metadata.bind.engine.dialect) except (NotImplementedError, AttributeError),e: col_spec = None d = Container({ 'name' : str(field_name), 'col' : col , 'db_type' : type(col.type), 'type' : self.get_py_type(type(col.type), field_name), 'col_spec' : col_spec, 'property' : property, 'fkey' : False, 'mapper' : mapper, }) try: d['length'] = col.type.length except: d['length'] = None if isinstance(col, (expression._Label, expression.Function)): d['table'] = None d['table_name'] = None d['default'] = None d['pkey'] = False d['nullable'] = True d['editable'] = False else: d['table'] = col.table d['table_name'] = col.table.name d['default'] = col.default d['pkey'] = col.primary_key d['nullable'] = col.nullable d['editable'] = field_name not in self.noup if d['table'] is not None and col.table.name not in self.table_mappers: #dbg.write("%s %s" % (mapper.local_table.name, self.table_mappers)) self.table_mappers[col.table.name] = (self.mapper.class_, self.mapper) self.pkeys[col.table.name] = [] if d['pkey']: #dbg.write("si %s -> %s" % (field_name, mapper.local_table.name )) self.pkeys[col.table.name] += [field_name] d['fkey'] = mapper.c[field_name].foreign_keys self.fields.add_element(field_name, d) #setattr(self.fields, field_name, d) def _add_loader_field(self, field_name, mapper, property=None): """ Add db_specs for a field that correspond to a Loader of a foreign field This is the loader of a field in a table connected via an intermediate one or via a OneToMany relation No real attribute is present in the db table SqlMask will use CollectionWidget to represent it and will explicily be asked to via a field in the layout """ if field_name in self: dbg.write("%s precedentemente definito, cerchi guai..." % field_name) return self.field_list += [ field_name ] d = { 'mapper' : property.mapper , 'name' : str(field_name), 'table' : property.target, 'table_name' : property.target.name, # 'col' : property.mapper.c[field_name] , 'db_type' : None, ## FIXME: this is used tor table 'type' : str, # FIXME 'col_spec' : None, # 'is_serial' : False, 'property' : property, 'fkey' : False, 'default' : None, 'pkey' : None, 'nullable' : True, # 'mapper' : mapper, 'editable' : True, # 'direction' : mapper.props[field_name].direction # 'remote_attr' : [c.name for c in property.remote_side] } try: d['length'] = mapper.c[field_name].type.length except: d['length'] = None table_name = getattr(mapper.local_table, 'name', None) if table_name and table_name not in self.table_mappers: self.table_mappers[table_name] = (mapper.class_, mapper) self.pkeys[table_name] = [] self.fields.add_element(field_name, d) #setattr(self.fields, field_name, d) def keys(self): """Return all the field_names in the mapper but not loaders """ ret = [] for m in self._tables(mode='mapper' ): ret += m.c.keys() return ret def all_keys(self): """Return all the field_names in the mapper comprising loaders """ ret = [] for prop in self.mapper.iterate_properties: ret += [prop.key] return ret def __iter__(self): return self def __contains__(self, item): if item in self.field_list: return True return False def __getitem__(self, item): #print "item", item, type(item) return getattr(self, str(item)) def __len__(self): return len(self.field_list) def _tables(self, mode='name'): """return the table_names of the tables in the mapper if mode='mapper', returns the mappers the main table will be in position '0' """ #dbg.write(self.table_mappers) if mode == 'name': first_table = self.mapper.local_table.name ret = [t for t in self.table_mappers.keys() if t != first_table ] return [ first_table ] + ret #dbg.write(self.table_mappers.keys()) #return self.table_mappers.keys() else: first_mapper = self.mapper ret = [t[1] for t in self.table_mappers.values() if t[1] != first_mapper] #dbg.write([t[1] for t in self.table_mappers.values()]) return [ first_mapper ] + ret def table_fields(self, table_name): """list of all field_names in 'table' """ return [f for f in self.field_list if self[f]['table_name'] == table_name ] def prop_fields(self, table_name): """list of all field_names in 'table' linked as propperty prop""" table_name = [p[2] for p in self.props if p[0] == table_name][0] return self.table_fields(table_name) def get_mapper(self, table_name): """return the mapper of that table""" dbg.write('table_mappers', self.table_mappers) return self.table_mappers[table_name][1] # def mapper(self, table_name): # """return the mapper of that table""" # return self.table_mappers[table_name][1] def get_class(self, table_name): """return the class of that table""" return self.table_mappers[table_name][0] def get_pkeys(self, table_name): """return the list of the pkeys for that table""" try: return self.pkeys[table_name] except KeyError: raise MissingPrimaryKeyError("%s does not seem to have a PKey" % table_name) def get_join_cnd(self): tables = self._tables(mode='mapper') return get_join_cnd(tables) def get_primary_table(self, name=True): primarytable = self.mapper.local_table while isinstance(primarytable, sqlalchemy.sql.Join): primarytable = primarytable.left if name: return primarytable.name else: return primarytable def next(self): """returns the next item in the data set, or tells Python to stop""" if self.n is None: self.n = -1 try: self.n += 1 r = getattr(self, self.field_list[self.n]) except: raise StopIteration if not r: raise StopIteration return r def is_fkey(self, field_name): """ return True if this field is a foreign key """ return bool(self.fields[field_name]['fkey']) def is_primary(self, field_name): """ return True if this field is a Primary Key """ return bool(self.fields[field_name]['pkey']) def is_loader(self, field_name): """ True if field_name is a loader for a related table """ if field_name not in self.field_list: return False prop = self.fields[field_name]['property'] if isinstance(prop, properties.PropertyLoader): return True return False def is_string(self, field_name): """ Return true if this field is a String type """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.String)) return False def is_date(self, field_name): """ Return true if this field is date """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Date)) return False def is_datetime(self, field_name): """ Return true if this field is datetime """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.DateTime)) return False def is_time(self, field_name): """ Return true if this field is time """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Time)) return False def is_interval(self, field_name): """ Return true if this field is time """ db_type = self.fields[field_name]['db_type'] if db_type: if issubclass(db_type, (sqltypes.Interval)): return True elif self.fields[field_name]['type'] is datetime.timedelta: return True return False def is_integer(self, field_name): """ Return true if this field is integer """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Integer)) return False def is_float(self, field_name): """ Return true if this field is float """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Float)) return False def is_numeric(self, field_name): """ Return true if this field is numeric """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Numeric)) return False def is_number(self, field_name): """ Return true if this field is a number """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Numeric, sqltypes.Integer, sqltypes.Float)) return False def is_boolean(self, field_name): """ Return true if this field is date """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, sqltypes.Boolean) else: return False def is_text(self, field_name): """ Return true if this field is date """ db_type = self.fields[field_name]['db_type'] if db_type: return issubclass(db_type, (sqltypes.Text)) return False def is_enum(self, field_name): """ Return true if this field is date """ from sqlkit import fields col = self.fields[field_name]['col'] try: field = col.info.get('field', None) if field: return issubclass(field, fields.EnumField) return col.info.get('render') == 'enum' except AttributeError: pass def is_image(self, field_name): """ Return true if this field is date """ from sqlkit import fields col = self.fields[field_name]['col'] try: field = col.info.get('field', None) if field: return issubclass(field, fields.ImageField) return col.info.get('render') == 'image' except AttributeError: pass def is_file(self, field_name): """ Return true if this field is date """ from sqlkit import fields col = self.fields[field_name]['col'] try: field = col.info.get('field', None) if field: return issubclass(field, fields.FileField) return col.info.get('render') == 'filename' except AttributeError: pass def is_nullable(self, field_name): """ Return true if this field is date """ return self.fields[field_name]['nullable'] def __str__(self): return "InspectMapper for keys: %s" % self.keys() def __repr__(self): return "" % self.keys() def get_foreign_info(ForeignKey, names=True): """ return a tuple (fk_table, fk_column) :param names: return names instead of objects """ foreign_keys = ForeignKey if len(foreign_keys) > 1: raise exc.UnhandledMultipleForeignKeys(foreign_keys) else: fkcol = foreign_keys.copy().pop() # since 0.7 it's a set, no indexing ## table/column name are unicode but they must be string to be ## used as key in dicts if names: return (str(fkcol.column.table.name), str(fkcol.column.name)) else: return (fkcol.column.table, fkcol.column) def get_props_for_delete_orphan(mapper, field_name): """ discover if field_name setting needs to set an object to fullfill the request for a delete_orphan cascading on a possible relation returns the generator for the properties or () """ ## TODO: FIX this. this is not working correctly tests must be added ## eg. Movies.__mapper__, director_id # when setting director_id you neeed to set also director so that # the backref relation from Director to Movie (that has delete_orphan set) # will not complain that Movie has no parent # find out if director_id is implied in some relation (director for us) # look in local_remote_pairs prop = mapper.get_property(field_name) assert isinstance(prop, properties.ColumnProperty) assert len(prop.columns) == 1 # I don't handle mapper with two columns in a property column = prop.columns[0] if hasattr(column, 'info'): if sqlalchemy.__version__ >= '0.6' or column.info.get('attach_instance'): prop_names = column.info.get('attach_instance') if isinstance(prop_names, basestring): prop_names = (prop_names,) if not prop_names: return () return [mapper.get_property(prop_name) for prop_name in prop_names if mapper.has_property(prop_name)] else: props = [] for pr in mapper.iterate_properties: if isinstance(pr, properties.RelationProperty): if pr.direction.name in ('MANYTOONE',): for col in pr.local_remote_pairs[0]: # I can't use col in p.local_remote_pairs # as it uses 'col == p.local_remote_pairs' that evaluates # to a BinaryExpression if column is col: try: if pr.backref.prop.cascade.delete_orphan: props += [pr] except AttributeError, e: pass return tuple(props) sqlkit-0.9.5/sqlkit/db/defaults.py0000644000175000017500000002201311714210425016442 0ustar sandrosandro# Copyright (C) 2005-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ ========= Defaults ========= """ import datetime import sqlalchemy tables = {} hooks = {} layout_dict = {} class_dict = {} def set_default(table_name, tbl_dict): """ Add a dictionary with one entry for each field of a table """ global tables if table_name in tables: tdict = tables[table_name] tdict.update(tbl_dict) else: tables[table_name] = tbl_dict class Defaults(object): def __init__(self, table_name, metadata=None, local=False): """ :param table_name: the name of the table for which we set default values :param metadata: the metadata in which the table lives :param local: boolean: if the default is global (False) or local to a single widget """ self.table_name = table_name self.metadata = metadata self.local = local if local: self.tdict = {} else: global tables if self.table_name in tables: self.tdict = tables[self.table_name] else: self.tdict = tables[self.table_name] = {} def set_defaults(self, tbl_dict): """ set defaults using a dict as input :param tbl_dict: a dict of field_name:default_value. Default value can be a callable """ self.tdict.update(tbl_dict) def set_default(self, **kw): """ set a single default """ for field_name, value in kw.iteritems(): tbl_dict = {field_name: value} self.set_defaults(tbl_dict) def fk(self, field_name, attr, value): """ Set a default and find at runtime (when setting default) which is the correct value (pk) based on attr and value :param field_name: the field_name for wich we set the default :param attr: the attribute to look for in the foreign table :param value: the value in the foreign key """ ## when we define defaults, metadata may not yet have loaded the table ## let's delay the moment... def get_by_description(): from sqlkit.db.minspect import get_foreign_info from sqlalchemy.sql import select global tables table = self.metadata.tables[self.table_name] col = table.columns.get(field_name) fkey = col.foreign_keys fk_table, fk_col = get_foreign_info(fkey, names=False) s = select([fk_col], getattr(fk_table.c, attr) == value) conn = self.metadata.bind.connect() result = conn.execute(s) row = result.fetchone() if row: #tables[self.table_name][field_name] = row[fk_col.name] self.tdict[field_name] = row[fk_col.name] else: #tables[self.table_name][field_name] = None self.tdict[field_name] = None return self.tdict[field_name] self.set_defaults( {field_name : get_by_description}) today = datetime.date.today now = datetime.datetime.now def get(self, field_name): """ Return the default value for a field_name if the value is a callable... call it """ try: value = self.tdict[field_name] if callable(value): return value() else: return value except KeyError, e: return get_default(self.table_name, field_name) def get_default(table_name, field_name): """ Return the default value for a field_name if the value is a callable... call it :param table: the table for which we want defaults :param field_name: the name of the field """ if isinstance(table_name, sqlalchemy.Table): table_name = table_name.name try: value = tables[table_name][field_name] except KeyError, e: return if callable(value): return value() else: return value def get_table_name(table): """ return a table name. input may be a tablename or a sqlalchemy.Table """ if isinstance(table, sqlalchemy.Table): table_name = table.name else: table_name = table return table_name def register_hook(table, hook, nick='default'): """ Register a hook for table. Each time a sqlwidget is instantiated for the table a hook is searched for here to initialize the sqlwidget :param table: a table for which we want to register the hook :param hook: the hook to be registered (a class or an instance) """ from inspect import isclass global hooks if not isclass(hook): hook = hook.__class__ table_name = get_table_name(table) hook_dict = hooks.get(table_name, {}) hook_dict[nick] = hook hooks[table_name] = hook_dict def unregister_hook(table, nick='default'): """ unregister a hook :param table: the table or table_name for which you unregister the hook :param nick: the possible nick (default: 'default') """ table_name = get_table_name(table) del hooks[table_name] def unregister_layout(table, nick='default'): """ unregister a layout :param table: the table or table_name for which you unregister the layout :param nick: the possible nick (default: 'default') """ table_name = get_table_name(table) del layout_dict[table_name] def get_hook(table, instance=False, nick='default'): """ Return a hook tor table or None :param table: the table for which we want the hook :param instance: a boolean: True is we want an instance False if we want the class """ global hooks table_name = get_table_name(table) table_hooks = hooks.get(table_name, None) try: hook = table_hooks[nick] except (KeyError, TypeError), e: return None if instance: hook = hook() return hook def register_layout(table, layout, nick='default', persistent=False): """ Register a layout for table with a nick (default nick is 'default') Each time a sqlwidget is instantiated for the table a layout is searched for here :param table: the table to register the layout for. May be a sqlakchemy.Table or a string :param layout: the layut to be registered :param nick: the name of a nick to use for this layout (used for SqlMask(... nick=nick) :param persistent: register in the _sqlkit tables (not yet implemented) """ global layout_dict table_name = get_table_name(table) table_layout_dict = layout_dict.get(table_name, {}) table_layout_dict[nick] = layout layout_dict[table_name] = table_layout_dict if persistent: raise NotImplementedError("registering of layout is not yet implemented") def get_layout(table, nick='default'): """ Return a layout for table for nick 'nick' :param table: the table to register the layout for. May be a sqlakchemy.Table or a string :param nick: the nick used for this layout """ global layout_dict table_name = get_table_name(table) table_layout_dict = layout_dict.get(table_name, None) try: return table_layout_dict[nick] except (KeyError, TypeError), e: return None def register_class(class_, table=None, nick='default'): """ Register a layout for table with a nick (default nick is 'default') Each time a sqlwidget is instantiated for the table a layout is searched for here :param table: the table to register the layout for. May be a sqlakchemy.Table or a string. class_.__table__ is used as default :param layout: the layout to be registered :param nick: the nick """ global class_dict if not table: table = class_.__table__ table_name = get_table_name(table) table_class_dict = class_dict.get(table_name, {}) table_class_dict[nick] = class_ class_dict[table_name] = table_class_dict def get_class(table, nick='default'): """ Return a layout for table for nick 'nick' :param table: the table to register the layout for. May be a sqlakchemy.Table or a string :param nick: the nick used for this layout """ global class_dict table_name = get_table_name(table) table_class_dict = class_dict.get(table_name, None) try: return table_class_dict[nick] except (KeyError, TypeError), e: return None sqlkit-0.9.5/sqlkit/debug/0000755000175000017500000000000011714210425014764 5ustar sandrosandrosqlkit-0.9.5/sqlkit/debug/__init__.py0000644000175000017500000000011111714210425017066 0ustar sandrosandroimport debug __doc__ = debug.__doc__ from debug import * #import gtk_dbg sqlkit-0.9.5/sqlkit/debug/gtk_dbg.py0000644000175000017500000013760711714210425016755 0ustar sandrosandroimport gtk import gobject, pango from sqlkit.layout import layout import debug as dbg import re, os import inspect import tokenize import keyword import time from gobject import markup_escape_text class wshow(object): def __init__(self, widget, lvl=0, mode='print', model=None, parent=None): self.mode = mode self.parent = parent self.model = model self.lvl = lvl if not widget: dbg.write("Manca widget") return if isinstance(widget, dict): widget = widget['Window'] if model: if lvl == 0: self.parent = model.append(None) self.add_to_model(widget, iter=self.parent) self.scan(widget) def printa(self, widget, iter=None, parent=None): if self.mode == 'print': print "%s%s %s %s" % (" "*self.lvl*3, widget.name, widget.__class__, widget) return elif self.model: return self.add_to_model(widget, iter=iter, parent=parent) def add_to_model(self, widget, iter=None, parent=None): if iter: new = iter else: new = self.model.append(parent) try: text = "(%s)" % widget.get_text() except: text = '' self.model.set(new, 0, "%s %s" % ((widget.name or '').strip(), text), 1, self.get_class_str(widget), 2, self.get_class_img(widget), 3, widget, 4, widget.get_property('visible'), ) return new def get_class_str(self, obj): c = str(obj.__class__) m = re.search('gtk.([A-Z][A-Za-z]+)',c) if m: return m.group(1) else: m = re.search('dateedit.([A-Z][A-Za-z]+)',c) if m: return m.group(1) else: dbg.write("Fallisce re.search per ", c) def get_class_img(self, obj): c = str(obj.__class__) pat = '(?:gtk|dateedit).([A-Z][A-Za-z]+)' re_result = re.search(pat,c) if re_result: name = re_result.group(1) else: # If it isn't a gtk or dateedit object, take all the class name name = c pix_name = "/usr/share/gazpacho/resources/base/%s.png" % (name.lower()) if not os.path.exists(pix_name): return pixbuf = gtk.gdk.pixbuf_new_from_file(pix_name) return pixbuf def scan(self, widget): """ descend a widget and get all it's children """ d = {} children = [] try: ## I want first higher widget in layout children = widget.get_children() if isinstance(widget, gtk.Table): children = reversed(widget.get_children()) except: pass for sun in children: new = self.printa(sun, parent=self.parent) ogg = wshow(sun, lvl=self.lvl+1, mode=self.mode, model=self.model, parent=new) d[sun] = ogg self.d = d class show_widgets(object): """ pops a TreeView to show widget hierarchy""" def __init__(self, widget=None, lay=True, window=None): """show tree hierarchy of widgets and allows for manipulating it create a toplevel window or uses tv if tv= parameter is provided Lay = False prevents creating a widget. Used when other programs provide the widget. """ self.l, self.w = self._get_layout(lay) if widget: self.add_tab_and_tree('', widget) if window: self.add_tab_and_tree(None, window) def _get_layout(self, lay): if not lay: return (False, False) lay = """ {B m=file } {O tb=gtk-quit } {N.external } - """ self.l = layout.Layout(lay, opts="s", title='Widget Tree') self.w = self.l.show() self.w['Window'].set_size_request(300, 500) self.notebook = self.w['N.external'] table = self.notebook.get_parent() table.child_set_property(self.notebook, 'y-options', gtk.FILL | gtk.EXPAND) self.page_number = 1 self.l.connect(('tb=gtk-quit', 'clicked', lambda l: self.w['Window'].destroy())) # key accelerators self.accel_group = gtk.AccelGroup() self.accel_group.connect_group(ord('q'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_LOCKED, self.delete) self.w['Window'].add_accel_group(self.accel_group) # menu #self.l.menu('m=file', ('Toplevels', lambda f: self.delete())) menu_item = gtk.MenuItem("Main Toplevels") menu_item2 = gtk.MenuItem("Minor Windows") # dbg.dshow(self.w) self.w['M=file'].append(menu_item) self.w['M=file'].append(menu_item2) sub_menu = gtk.Menu() sub_menu2 = gtk.Menu() menu_item.set_submenu(sub_menu) menu_item2.set_submenu(sub_menu2) subsub = {} for window in gtk.window_list_toplevels(): lbl = window.title if not lbl: class_ = window.child.__class__.__name__ if not class_ in subsub: ## creo il menu 'Label', 'Frame'... menu = gtk.Menu() subsub[class_] = menu ## creo l' item di ingresso nel menu "Minor Windows" Item = gtk.MenuItem(class_) sub_menu2.append(Item) Item.set_submenu(menu) lbl = "%s" % (window.child.name) m = gtk.MenuItem(lbl) menu.append(m) else: m = gtk.MenuItem(lbl) sub_menu.append(m) m.connect('activate', self.add_tab_and_tree, window) self.w['M=file'].show_all() return self.l, self.w def create_model(self, tv): model = gtk.TreeStore(str, str, gtk.gdk.Pixbuf, gobject.TYPE_OBJECT, bool) tv.set_enable_search(True) tv.set_search_column(0) tv.set_model(model) # tvc0 = gtk.TreeViewColumn('Show') tvc1 = gtk.TreeViewColumn('Name') tvc2 = gtk.TreeViewColumn('Class') cell_bool = gtk.CellRendererToggle() cell_bool.set_property('activatable', True) cell_bool.connect('toggled', self.toggle_visibility, model) cell_text1 = gtk.CellRendererText() cell_text2 = gtk.CellRendererText() cell_pix1 = gtk.CellRendererPixbuf() tvc1.pack_start(cell_pix1, False) tvc1.pack_start(cell_text1, False) tvc1.pack_start(cell_bool, False) tvc2.pack_start(cell_text2, False) tvc1.set_attributes(cell_bool, active=4) tvc1.set_attributes(cell_pix1, pixbuf=2) tvc1.set_attributes(cell_text1, text=0) tvc2.set_attributes(cell_text2, text=1) tv.connect('button-press-event', self.button_press_cb) # tv.append_column(tvc0) tv.append_column(tvc1) tv.append_column(tvc2) return model def toggle_visibility(self, cell, path, model): widget = model[path][3] show = model[path][4] widget.set_property('visible', not show) model[path][4] = not show def add_tab_and_tree(self, menu_item, toplevel): #dbg.write(toplevel) lbl = toplevel.title if not lbl: lbl = str(toplevel) tv = self.add_new_book(toplevel, lbl) model = self.create_model(tv) self.fill_model(toplevel, model) tv.expand_all() return True def add_new_book(self, widget, lbl): self.page_number += 1 tv = gtk.TreeView() scrolled = gtk.ScrolledWindow() scrolled.add(tv) eventBox = self.create_custom_tab(lbl, scrolled) self.notebook.append_page(scrolled, eventBox) # Set the new page pages = gtk.Notebook.get_n_pages(self.notebook) self.notebook.set_current_page(pages - 1) self.notebook.show_all() return tv def create_custom_tab(self, text, child): #create a custom tab for notebook containing a #label and a button with STOCK_ICON eventBox = gtk.EventBox() tabBox = gtk.HBox(False, 2) tabLabel = gtk.Label(text) tabButton=gtk.Button() tabButton.connect('clicked', self.remove_book, child) #Add a picture on a button self.add_icon_to_button(tabButton) iconBox = gtk.HBox(False, 0) eventBox.show() tabButton.show() tabLabel.show() tabBox.pack_start(tabLabel, False) tabBox.pack_start(tabButton, False) # needed, otherwise even calling show_all on the notebook won't # make the hbox contents appear. tabBox.show_all() eventBox.add(tabBox) return eventBox def add_icon_to_button(self, button): iconBox = gtk.HBox(False, 0) image = gtk.Image() image.set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) gtk.Button.set_relief(button, gtk.RELIEF_NONE) #gtk.Button.set_focus_on_click(button,False) settings = gtk.Widget.get_settings (button) (w,h) = gtk.icon_size_lookup_for_settings (settings, gtk.ICON_SIZE_MENU) gtk.Widget.set_size_request (button, w + 4, h + 4) image.show() iconBox.pack_start(image, True, False, 0) button.add(iconBox) iconBox.show() return # Remove a page from the notebook def remove_book(self, button, child): page = self.notebook.page_num(child) if page != -1: self.notebook.remove_page(page) # Need to refresh the widget -- # This forces the widget to redraw itself. self.notebook.queue_draw_area(0, 0, -1, -1) def remove_current_book(self, *arguments, **keywords): page = self.notebook.get_current_page() if page != -1: self.notebook.remove_page(page) return True # Remove a page from the notebook def delete(self,*args): #dbg.write() self.w['Window'].destroy() return True def pop_menu(self, widget, ev): try: self.w['M=popup'].destroy() except: pass self.w['M=popup'] = gtk.Menu() menu = self.w['M=popup'] ## info on properties item = gtk.MenuItem(label="properties") item.connect('activate', self.show_widget_properties, widget ) menu.append(item) ## info on pack item = gtk.MenuItem(label="packing") item.connect('activate', self.show_widget_packing, widget ) menu.append(item) ## info on properties item = gtk.MenuItem(label="signal") item.connect('activate', self.show_widget_signal, widget ) menu.append(item) menu.show_all() menu.popup(None, None, None, ev.button, ev.time) def show_widget_properties(self, menu, widget ): props = gobject.list_properties(widget) cols = ['name','value','owner_type', 'value_type','nick' ] a = TreeInfoLayout(cols) cell = a.tvc[1].get_cell_renderers()[0] cell.set_property('editable', True) #tv.set_property('resizable', True) cell.connect('edited', self.edited_cb, (widget, a.model, 1)) a.w['Window'].set_title("%s - %s" % ( widget.name or '', re.search("'(.*)'",str(widget.__class__)).group(1) ) ) m = a.model for i in props: #dbg.write(i) iter = m.append(None) m.set(iter, 0, i.name, 4, i.nick, 2, re.search(' ([a-zA-Z]+)', str(i.owner_type)).group(1), 3, re.search(' ([a-zA-Z]+)', str(i.value_type)).group(1), ) default = getattr(i, 'default_value', '') try: ## eg: pattern is write only, so no get_property()... val = widget.get_property(i.name) except: pass try: if val != default and default != '': val = "%s (%s)" % (val, default) except: # default may have unideco decode errors... pass if i.name not in 'user-data': m.set(iter, 1, val, ) iter = m.append(None) m.set(iter, 0, 'self', 1, widget) def show_widget_signal(self, menu, widget, model=None ): if not model: cols = ['name'] a = TreeInfoLayout(cols) a.tv.expand_all() model = a.model signals = get_widget_signals(widget) prev_class = None for cls, signal in signals: if not cls == prev_class: iter = model.append(None) cls_iter = iter model.set(iter, 0, cls ) iter = model.append(cls_iter) model.set(iter, 0, signal ) prev_class = cls def edited_cb(self, cell, path, new_text, user_data): widget, model, column = user_data model[path][column] = new_text prop = model[path][0] gtype = model[path][3] #dbg.write('widget.set_property(%s, %s)' % (new_text,prop) ) py_type = { 'gboolean' : bool, 'gint' : int, 'guint' : int, 'gchararray' : str, 'gfloat' : float, } if new_text == "False": new_text = False widget.set_property(prop, py_type[gtype](new_text)) return def show_widget_packing(self, menu, widget): parent = widget.get_parent() # This was the first version: # if not gobject.type_is_a(parent, gtk.Container): # I now think it's OK to assume that every "parent" widget is Container: if not parent: dialog, l = self.l.message(type='warning', text="Only widgets inside a gtkContainer have packing properties.") self.response = dialog.run() dialog.destroy() return dialog props = parent.list_child_properties() cols = ['name','value','owner_type', 'value_type','nick' ] a = TreeInfoLayout(cols) cell = a.tvc[1].get_cell_renderers()[0] cell.set_property('editable', True) cell.connect('edited', self.edited_cb_packing, (parent, widget, a.model, 1)) a.w['Window'].set_title("%s - %s" % ( widget.name or '', re.search("'(.*)'",str(widget.__class__)).group(1) ) ) m = a.model for prop in props: iter = m.append(None) m.set(iter, 0, prop.name, 4, prop.nick, 2, re.search(' ([a-zA-Z]+)', str(prop.owner_type)).group(1), 3, re.search(' ([a-zA-Z]+)', str(prop.value_type)).group(1), ) try: default = prop.default_value except: default = '' val = parent.child_get_property(widget, prop.name) try: ## in the eventuality of write only properties val = parent.get_child_property(prop.name) except: None if val != default and default != '': val = "%s (%s)" % (val, default) if prop.name not in 'user-data': m.set(iter, 1, val, ) def edited_cb_packing(self, cell, path, new_text, user_data): parent, widget, model, column = user_data prop = model[path][0] gtype = model[path][3] def enforce_int(x): if int(x) in range(8): return int(xx) else: return '' py_type = { # Ugly way to require a number smaller than 8. 'GtkAttachOptions' : enforce_int, 'gint' : int, 'guint' : int, 'gchararray' : str, 'gfloat' : float, 'gboolean' : bool, } # No real edit was made if new_text == model[path][column]: return if new_text == "False": new_text = False try: value = py_type[gtype](new_text) except: # The text can not be interpreted as the given type! value = self.edited_fallback_dialog(gtype, widget, prop, parent.child_get_property(widget, prop)) # Now the value is valid. parent.child_set_property(widget, prop, value) # Whatever happened, just show (nicely) the actual value: model[path][column] = str(parent.child_get_property(widget, prop)) return def edited_fallback_dialog(self, gtype, widget, prop_name, current_value): """If the value for a particular property is not valid, this method is called to get an appropriate value. It can be tought how to act with different gtypes by adding "elif" tests. """ if gtype == 'GtkAttachOptions': dialog = gtk.Dialog(title=prop_name, flags=gtk.DIALOG_MODAL, buttons =('gtk-close',4)) dialog.set_size_request(200,-1) checkbuttons = [gtk.CheckButton('Expand'), gtk.CheckButton('Shrink'), gtk.CheckButton('Fill')] for index in range(3): dialog.vbox.pack_start(checkbuttons[index]) single_value = bool(int(current_value) & 2**index) checkbuttons[index].set_active(single_value) checkbuttons[index].show() response = dialog.run() value = 0 for index in range(3): if checkbuttons[index].get_active(): value += 2**index dialog.destroy() return value # If we don't know kow to treat the gtype, just stick to current value return current_value def show_widget_connect(self): pass def button_press_cb(self, wdg, ev): if ev.button == 3: model = wdg.get_model() iter = model.get_iter(wdg.get_path_at_pos(int(ev.x), int(ev.y))[0]) widget = model.get_value(iter,3) #dbg.write("Type: %s" % type(widget)) self.pop_menu(widget, ev) def fill_model(self, toplevel, model): model.clear() i = wshow(toplevel, mode='', model=model) class TreeInfoLayout(object): def __init__(self, cols, tv=None, model=None): if tv: self.tv = tv else: self.l = self._get_layout() self.w = self.l.show() self.w['Window'].resize(500, 500) scr_treeview = self.w['S=a'] table = scr_treeview.get_parent() table.child_set_property(scr_treeview, 'y-options', gtk.FILL | gtk.EXPAND) self.tv = self.w['TV=a'] self.l.connect(('tb=gtk-quit', 'clicked', lambda l: self.w['Window'].destroy())) if not model: self.model = gtk.TreeStore(*[str for i in cols]) else: self.model = model self.tv.set_enable_search(True) self.tv.set_model(self.model) self.tvc = {} for i,col in enumerate(cols): self.tvc[i] = gtk.TreeViewColumn(col, gtk.CellRendererText(), text=i) self.tvc[i].set_sort_column_id(i) self.tv.append_column(self.tvc[i]) def _get_layout(self): lay = """ {O tb=gtk-quit} TVS=a """ #dbg.write() return layout.Layout(lay, opts="s") SimpleTreeView = TreeInfoLayout class ShowMapperInfo(object): def __init__(self, InspectMapper): self.info = InspectMapper cols = ['name', 'col__spec', 'pkey', 'fkey', 'Null', 'editable', 'table', ] self.treeLayout = TreeInfoLayout(cols) self.tv = self.treeLayout.tv self.model = self.treeLayout.model self.fill_model() self.tv.expand_all() def fill_model(self): from sqlkit.db.minspect import get_foreign_info iter = {} for table_name in self.info.tables(): iter[table_name] = self.model.append(None) self.model.set(iter[table_name], 0, table_name) for f in self.info.table_fields(table_name): I = getattr(self.info, f) sun = self.model.append(iter[I['table_name']]) self.model.set(sun, 0, I['name'], 1, I['col_spec'], 5, I['editable'], ) if I['pkey']: self.model.set(sun, 2, I['pkey'], ) if I['fkey']: self.model.set(sun, 3, "%s.%s" % (get_foreign_info(I['fkey'])) ) if not I['nullable']: self.model.set(sun, 4, 'NOT NULL', ) self.model.set(sun, 6, I['table']) class ShowClassInfo(object): def __init__(self, obj): cols = ['what', 'value', 'type' ] self.colors = { 'dict' : 'red', 'list' : 'blue', 'int' : 'seagreen', 'NoneType' : 'gray60', 'tuple': 'darkslategray', } self.exclude = [ "wrapper_descriptor", # "module", "method_descriptor", "member_descriptor", # "instancemethod", "builtin_function_or_method", "frame", "classmethod", "classmethod_descriptor", "_Environ", # "MemoryError", "_Printer", "_Helper", "getset_descriptor", "weakref", "cell", "staticmethod" ] self.model = gtk.TreeStore(str, str, str, object, str) self.treeLayout = TreeInfoLayout(cols, model=self.model) self.tv = self.treeLayout.tv #self.tv.set_property('fixed-height-mode', True) self.tv.set_enable_search(True) self.tv.set_search_column(4) self.treeselection = self.tv.get_selection() #self.model = self.treeLayout.model self.add_to_model(obj) self.tv.expand_all() key_col = self.tv.get_column(0) cell = key_col.get_cell_renderers()[0] key_col.set_attributes(cell, markup=0) type_col = self.tv.get_column(2) cell = type_col.get_cell_renderers()[0] cell.set_property('style', pango.STYLE_ITALIC) type_col.set_attributes(cell, markup=2) value_col = self.tv.get_column(1) value_col.set_max_width(200) value_col.set_expand(True) for i in range(2): col = self.tv.get_column(i) col.set_resizable(True) self.tv.connect('button-press-event', self.add_to_model_cb) self.treeselection.connect('changed', self.selection_changed_cb) def selection_changed_cb(self, ts): model, iter = ts.get_selected() if not iter: return value = model.get_value(iter,4) if not value: return txt = [] txt.insert(0, value) while True: iter=model.iter_parent(iter) if not iter: break path = self.model.get_path(iter) value = model.get_value(iter,4) if value: txt.insert(0,value) msg = ".".join(txt) self.treeLayout.l.sb(msg) def add_to_model(self, obj, position=None): iter = {} if obj == '': for key in dir(): it = self.model.append(position) self.model.set(it, 0, key, 1, eval(key), ) class_name = obj.__class__.__name__ if class_name in ('NoneType','str', 'bool', 'type','datetime.date', 'datetime.datetime', 'datetime.time','int'): ## it reset the value but not trimmed to 100 chars self.model.set(position, 1, str(obj)) return if position is not None and self.model.iter_has_child(position): return if class_name in ('dict'): for key, val in obj.iteritems(): it = self.model.append(position) self.add_row(it, val, str(key), val.__class__.__name__, type(val)) elif class_name in ('list','tuple'): for pos,val in enumerate(obj): it = self.model.append(position) self.add_row(it, val, pos, val.__class__.__name__, type(val)) elif inspect.ismethod(obj): self.info_method(obj) else: for i in ['module','callable', 'globals', 'attribute']: iter[i] = self.model.append(position) self.model.set(iter[i], 0, i, ) iter['builtin'] = self.model.append(iter['callable']) self.model.set(iter['builtin'], 0,'builtin') iter['methods'] = self.model.append(iter['callable']) self.model.set(iter['methods'], 0,'methods') for j in dir(obj): self.show_obj(obj, j, iter) for k in ['builtin', 'methods'] + iter.keys(): try: if not self.model.iter_has_child(iter[k]): self.model.remove(iter[k]) except: pass def show_obj(self, obj, attr_name, iter): attr = getattr(obj, attr_name, None) class_name = attr.__class__.__name__ t = type(getattr(obj, attr_name)) if class_name == 'module': it = self.model.append(iter['module']) elif class_name in ['instancemethod', 'method-wrapper']: it = self.model.append(iter['methods']) #dbg.write(attr_name, class_name, self.model.get_path(it)) elif class_name in self.exclude: it = self.model.append(iter['builtin']) #dbg.write(attr_name, class_name, self.model.get_path(it)) elif callable(attr): it = self.model.append(iter['callable']) else: it = self.model.append(iter['attribute']) #dbg.write(attr_name, class_name, self.model.get_path(it)) self.add_row( it, attr, attr_name, class_name, t) def add_row(self, iter, attr, attr_name, class_name, t): fmt = "%s" self.model.set(iter, 0, self.add_color(attr_name, class_name, attr), 1, str(attr)[0:100], # str TreeColumn 2, fmt % t.__name__, 3, attr, # object TreeColumn 4, attr_name, ) def add_color(self, attr_name, class_name, attr): fmt = "%(attr_name)s" fmt_dict = { 'col' : self.colors.setdefault(class_name,'slategray'), 'attr_name' : re.sub('[<>]', '', str(attr_name)) } return fmt % fmt_dict def add_to_model_cb(self, wdg, ev): if ev.button != 3: return False iter = self.model.get_iter( wdg.get_path_at_pos(int(ev.x), int(ev.y))[0]) obj = self.model.get_value(iter,3) self.add_to_model(obj, position=iter) path = self.model.get_path(iter) self.tv.expand_to_path(path) def info_method(self, obj): """show info on method 'obj' using text widget""" l = layout.Layout('TXS=a:700.400',opts="V") w=l.show() buff = w['TX=a'].get_buffer() self.configure_buffer(buff) txt = [] try: txt.extend("# defined at line %s of file:\n# %s\n# module: %s\n\n" %( inspect.getsourcelines(obj)[1], inspect.getfile(obj), inspect.getmodule(obj), )) except TypeError: txt.extend("Built-in function/method or class") txt.extend(inspect.getsource(obj)) #buff.set_text(txt) self.insert_source("".join(txt), buff) def insert_source(self, data, buf): buf.delete(*buf.get_bounds()) iter = buf.get_iter_at_offset(0) #print buf.get_tag_table() last_erow, last_ecol = 0, 0 was_newline = False # multiline statement detection old_str = None for x in tokenize.generate_tokens(layout.misc.InputStream(data).readline): # x has 5-tuples tok_type, tok_str = x[0], x[1] srow, scol = x[2] erow, ecol = x[3] #dbg.write("type %s -- src: .%s." % (tok_type, tok_str)) # The tokenizer 'eats' the whitespaces, so we have to insert this again # if needed. if srow == last_erow: # Same line, spaces between statements if scol != last_ecol: buf.insert_with_tags_by_name(iter, ' '*(scol-last_ecol), 'source') else: # New line. # First: Detect multiline statements. There is no special in the tokenizer stream. if was_newline is False and last_erow != 0: buf.insert_with_tags_by_name(iter, ' \\\n', 'source') # new line check if it starts with col 0 if scol != 0: buf.insert_with_tags_by_name(iter, ' '*scol, 'source') last_erow = erow last_ecol = ecol if tok_type == tokenize.COMMENT: was_newline = True # newline is in tok_str included. buf.insert_with_tags_by_name(iter, tok_str, 'source', 'comment') old_str = tok_str continue if tok_str.startswith('"""'): buf.insert_with_tags_by_name(iter, tok_str, 'source', 'docstring') old_str = tok_str continue if old_str == 'def': buf.insert_with_tags_by_name(iter, tok_str, 'source', 'function') old_str = tok_str continue if old_str == 'class': buf.insert_with_tags_by_name(iter, tok_str, 'source', 'class') old_str = tok_str continue elif tok_type == tokenize.NAME or tok_str == 'self': if tok_str in keyword.kwlist + ['self']: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'keyword') old_str = tok_str continue elif tok_type == tokenize.STRING: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'string') old_str = tok_str continue # No special format for use. Check for newline. was_newline = tok_type in (tokenize.NEWLINE, tokenize.NL) buf.insert_with_tags_by_name(iter, tok_str, 'source') old_str = tok_str def configure_buffer(self, buf): #print "Configuro %s" % buf tag = buf.create_tag('title') tag.set_property('font', 'Sans 18') tag = buf.create_tag('source') tag.set_property('font', 'monospace') tag.set_property('pixels_above_lines', 0) tag.set_property('pixels_below_lines', 0) tag = buf.create_tag('keyword', foreground='mediumorchid', weight=pango.WEIGHT_BOLD) tag = buf.create_tag('string', foreground='royalblue4') tag = buf.create_tag('function', foreground='blue') tag = buf.create_tag('docstring', foreground='indianred') tag = buf.create_tag('class', foreground='#007F00') tag = buf.create_tag('comment', foreground='red', style=pango.STYLE_ITALIC) class InputStream(object): """ Simple Wrapper for File-like objects. [c]StringIO doesn't provide a readline function for use with generate_tokens. Using a iterator-like interface doesn't succeed, because the readline function isn't used in such a context. (see /tokenize.py) """ def __init__(self, data): self.__data = [ '%s\n' % x for x in data.splitlines() ] self.__lcount = 0 def readline(self): try: line = self.__data[self.__lcount] self.__lcount += 1 except IndexError: line = '' self.__lcount = 0 return line class ShowLogs(object): def __init__(self, model=None, show=False): if model is None: self.get_layout(show) def get_layout(self, show): try: from layout import misc except: from sqlkit.layout import misc global iter self.last_indent = 0 iter = {} self.indent = {} ## method, args, caller, line cols = ['meth','class', 'time', 'caller', 'line', 'ret code', 'class', 'args', 'all_args', 'tooltips'] self.easytv = misc.EasyTreeView( cols, sort=False, show=show, but='tb=gtk-close tb=gtk-open', title="GTK debug") self.time = time.time() self.last_time = time.time() self.model = self.easytv.model cell = self.easytv.tvc['meth'].get_cell_renderers()[0] self.easytv.tvc['meth'].set_attributes(cell, markup=0) try: self.easytv.tvc['tooltips'].props.visible = False except: import ipdb; ipdb.set_trace() self.easytv.tv.set_tooltip_column(9) self.easytv.l.connect( ('tb=gtk-open', 'clicked', lambda w, : self.easytv.tv.expand_all()), ('tb=gtk-close', 'clicked', lambda w, : self.easytv.tv.collapse_all()), ('tb=gtk-clear', 'clicked', self.model_clear), ) self.iter = {} self.easytv.w['Window'].set_geometry_hints( self.easytv.w['Window'], min_width=500, min_height=500) scr_window = self.easytv.w['S=a'] table = scr_window.get_parent() table.child_set_property(scr_window, 'y-options', gtk.EXPAND | gtk.FILL) self.model.connect('row-inserted', self.get_there) self.conf() self.easytv.tv.connect('button-press-event', self.button_press_cb) self.easytv.tv.set_reorderable(True) try: self.add_tooltips() except: pass def get_tooltip(self, meth, args, ret, caller, line, time): ret = re.sub('[<>]','', str(ret)) args = re.sub('[<>]','', str(args)) try: caller = ".".join(caller.split('.')[-2:]) except: pass caller = re.sub('[<>]','', str(caller)) txt = "%(meth)s(%(args)s)\n%(ret)s\n" + \ "%(caller)s:%(line)s\n%(time)s" return txt % (locals()) def button_press_cb(self, wdg, ev): if ev.button != 3: return False iter = self.model.get_iter( wdg.get_path_at_pos(int(ev.x), int(ev.y))[0]) obj = self.model.get_value(iter,6) meth = self.model.get_value(iter,0) meth = re.sub('<[^>]*>', '', meth) Hmeth = "_H_%s" % meth if Hmeth in dir(obj): self.info_method(obj, Hmeth) return else: print "No %s in %s" % (Hmeth, obj) if meth in dir(obj): self.info_method(obj, meth) return def info_method(self, obj, meth): """show info on method 'obj' using text widget""" from sqlkit.layout import layout try: attr = getattr(obj, meth) inspect.getsourcelinesattr(attr)[1] except: for i in obj.__bases__: try: attr = self.info_method(i, meth) inspect.getsourcelinesattr(attr)[1] return except: print "NOT FOUND in __bases__", i return pass obj = attr l = layout.Layout('TXS=a:700.400',opts="V") w=l.show() buff = w['TX=a'].get_buffer() self.configure_buffer(buff) txt = [] try: txt.extend("# defined at line %s of file:\n# %s\n# module: %s\n\n" %( inspect.getsourcelines(obj)[1], inspect.getfile(obj), inspect.getmodule(obj), )) except TypeError: txt.extend("Built-in function/method or class") txt.extend(inspect.getsource(obj)) #buff.set_text(txt) self.insert_source("".join(txt), buff) def add_tooltips(self): tips = DebugTreeView(self.model) tips.add_view(self.easytv.tv) def insert_source(self, data, buf): from sqlkit.layout import misc buf.delete(*buf.get_bounds()) iter = buf.get_iter_at_offset(0) #print buf.get_tag_table() last_erow, last_ecol = 0, 0 was_newline = False # multiline statement detection old_str = None for x in tokenize.generate_tokens(misc.InputStream(data).readline): # x has 5-tuples tok_type, tok_str = x[0], x[1] srow, scol = x[2] erow, ecol = x[3] #dbg.write("type %s -- src: .%s." % (tok_type, tok_str)) # The tokenizer 'eats' the whitespaces, so we have to insert this again # if needed. if srow == last_erow: # Same line, spaces between statements if scol != last_ecol: buf.insert_with_tags_by_name(iter, ' '*(scol-last_ecol), 'source') else: # New line. # First: Detect multiline statements. There is no special in the tokenizer stream. if was_newline is False and last_erow != 0: buf.insert_with_tags_by_name(iter, ' \\\n', 'source') # new line check if it starts with col 0 if scol != 0: buf.insert_with_tags_by_name(iter, ' '*scol, 'source') last_erow = erow last_ecol = ecol if tok_type == tokenize.COMMENT: was_newline = True # newline is in tok_str included. buf.insert_with_tags_by_name(iter, tok_str, 'source', 'comment') old_str = tok_str continue if tok_str.startswith('"""'): buf.insert_with_tags_by_name(iter, tok_str, 'source', 'docstring') old_str = tok_str continue if old_str == 'def': buf.insert_with_tags_by_name(iter, tok_str, 'source', 'function') old_str = tok_str continue if old_str == 'class': buf.insert_with_tags_by_name(iter, tok_str, 'source', 'class') old_str = tok_str continue elif tok_type == tokenize.NAME or tok_str == 'self': if tok_str in keyword.kwlist + ['self']: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'keyword') old_str = tok_str continue elif tok_type == tokenize.STRING: buf.insert_with_tags_by_name(iter, tok_str, 'source', 'string') old_str = tok_str continue # No special format for use. Check for newline. was_newline = tok_type in (tokenize.NEWLINE, tokenize.NL) buf.insert_with_tags_by_name(iter, tok_str, 'source') old_str = tok_str def configure_buffer(self, buf): #print "Configuro %s" % buf tag = buf.create_tag('title') tag.set_property('font', 'Sans 18') tag = buf.create_tag('source') tag.set_property('font', 'monospace') tag.set_property('pixels_above_lines', 0) tag.set_property('pixels_below_lines', 0) tag = buf.create_tag('keyword', foreground='mediumorchid', weight=pango.WEIGHT_BOLD) tag = buf.create_tag('string', foreground='royalblue4') tag = buf.create_tag('function', foreground='blue') tag = buf.create_tag('docstring', foreground='indianred') tag = buf.create_tag('class', foreground='#007F00') tag = buf.create_tag('comment', foreground='red', style=pango.STYLE_ITALIC) def model_clear(self, butt): self.model.clear() self.iter = {} def get_there(self, model, path, iter): self.easytv.tv.scroll_to_cell(path) self.easytv.tv.expand_to_path(path) def show(self): self.easytv.show() def hide(self): self.easytv.hide() def conf(self, but='^$', only='.*'): self.but = re.compile(but) self.only = re.compile(only) def log(self, obj, but='^$', only='.*'): #but = re.compile(but) #only = re.compile(only) for attr in dir(obj): if attr.startswith('__') and attr != '__init__': continue if but.search(attr): continue # if not only.search(attr): # continue item = getattr(obj, attr) if callable(item): anInstance = item.im_self anInstance.__dict__['_H_%s' % attr] = item anInstance.__dict__[attr] = new.instancemethod( logmethod(attr), anInstance, anInstance.__class__) def add(self, txt='', Class='', line=None, caller='', meth=None, indent=0, args=[], ret=None, class_obj=None): """adds to the TreeModel. If a method is called from another, its line will be a sun of the previous and the color will be changed with markup """ col = {0: 'navyblue', 1:'slateblue', 2:'red', 3: 'seagreen', 4: 'gray50', 5: 'gray60', 6: 'gray65', 7:'gray70'} if self.but.search(meth): return # if not self.only.search(but): # print 'KO only', meth # return meth_str = meth meth_str = "%s" % ( col.get(indent, 'palevioletred'), markup_escape_text(meth)) self.last_indent = indent if indent == 0: self.iter[indent] = self.model.append(None) else: self.iter[indent] = self.model.append(self.iter.get(indent-1, None)) try: txt = args[0] except IndexError: txt = '' now = time.time() # elapsed time is totally wrong!!! elapsed_time = "%.3f - %.4f" % (now - self.time, now - self.last_time) args = ", ".join([str(f) for f in args]) self.model.set( self.iter[indent], 0, meth_str, 1, Class, # 2, txt, 2, elapsed_time, 3, caller, 4, line, 6, class_obj, 7, args, 8, args, ) self.last_time = now ## args to the functions are written one per line, the first is already ## written, now the other come... # for arg in args[1:]: # self.model.set(self.model.append(self.iter[indent]), # 2, arg[0:100] # ) self.indent[meth] = indent #print "self.indent[%s] = %s" % (meth, indent) return self.iter[indent] def write(self, niter=None, txt='', line=None, mode=None, parent=None, caller='', meth=None, indent=None, args=[], ret=None, Class=''): # I want to try to indent all logs (write) as the caller try: indent = self.indent[caller] except: indent = self.last_indent if parent: iter = self.model.append(parent) else: #self.iter[indent] = self.model.append(None) try: iter = self.model.append(self.iter[indent-1]) except: try: iter = self.model.append(self.iter[indent]) except KeyError: iter = self.model.append(None) color={ 'dict' : 'green', 'value' : 'seagreen', 'write' : 'blue', } meth = re.sub('(.*)\.(.*)',r'\2', str(meth)) meth_str = "%s" % ( color[mode], markup_escape_text(meth)) now = time.time() self.model.set( iter, 0, "(%s)" % meth_str, 1, Class, 7, txt, 3, caller, 4, line, 2, "%.3f - %.4f" % (now - self.time, now - self.last_time) ) self.last_time = now return iter def ret(self, iter, txt): meth, args, caller, line, time = self.model.get( iter, 0, 7, 3, 4, 2) self.model.set(iter, 5, txt[0:100], 9, self.get_tooltip(meth, args, txt[0:100], caller, line, time), ) return iter def get_widget_signals(item, result=None): """ get all widget signals of an object and return a list of tuples: (class, signal_name) """ from inspect import isclass import gobject result_list = result or [] if isclass(item): class_name = item.__class__.__name__ else: class_name = type(item).__class__.__name__ for base in item.__class__.__bases__[1:]: get_widget_signals(base, result_list) signals = list(gobject.signal_list_names(item)) signals.sort() for signal in signals: result_list += (class_name, signal) try: item = gobject.type_parent(item) get_widget_signals(item, result=result_list) except RuntimeError: pass def get_widget_signals(item, result=None, indent=0): """ get all widget signals of an object and return a list of tupes: (class, signale_name) """ from inspect import isclass import gobject DONT_GO =( object, type, gobject.GType, gobject.GInterface, gobject.GObjectMeta, ) result_list = result or [] # if item in DONT_GO or type(item) is gobject.GType: if type(item) is gobject.GType: # signals_list_names segfaults on gobject.GInterface return result offset = " " * indent if isclass(item): class_name = item.__name__ else: class_name = item.__class__.__name__ signals = list(gobject.signal_list_names(item)) signals.sort() for signal in signals: result_list += [(class_name, signal)] if not isclass(item): for base in item.__class__.__bases__: result_list = get_widget_signals( base, result=result_list, indent=indent+4) try: item = gobject.type_parent(item) return get_widget_signals(item, result=result_list, indent=indent + 4) except RuntimeError: return result_list sqlkit-0.9.5/sqlkit/debug/debug.py0000644000175000017500000003200311714210425016422 0ustar sandrosandro""" This module provides easy way to debug with print commands. This may not be the best way to debug, many times ``pdb`` will be much more powerfull. Nevertheless I found mysel in need of these functions... The way to use this is 1. in you library:: dbg.write('text message', 'text2') 2. in you application:: dbg.debug(True) write() - caller() ------------------ from now on, each time you use dbg.write()/dbg.show_caller() you'll see the text you want to log only if you enabled it with dbg.debug(True) If you want the log to happen in a gtk.TreeView window, you can specify:; import dbg dbg.debug(True, gtk=True) Logging methods --------------- Following recepe 198078 in the ASP Python CookBook (see in the code) this module provides also a metaclass to trace the use of methods of a particular class. It will be instantiated as:: class A(object): __metaclass__ = dbg.LogTheMethods for each method call a line will be logged (unless configured to be ignored) with function called caller class arguments caller function line of code return code calling class time elapsed IMPORTANT: since the logging occurs with metaclasses you need to import dbg and set debugging *before* importing the class you want to trace:: import dbg dbg.debug(True, gtk=True) dbg.trace_class('SqlTable2') # optional dbg.trace_function(exclude='cell_default_cb|cell_bool_cb') # optional import sqlkit TraceIt ------- in case you need to understand who is changeing the value of a variable, you can use TraceIt as:: commit_allowed = dbg.TraceIt(True, name='commit_allowed', mode='rw') and use it as a normal variable. You'll get lines in your output stating who changed the value of that variable, in the form:: __str__/function_name: old_value => new_value __get__/function_name: value """ import sys, re import new # I had to hide this import inside functions otherwise import of sqlkit broke. #from sqlkit.layout import misc import pango import inspect import time import inspect from pdb import set_trace import gobject CLASS_INCLUDE = '^.*$' CLASS_EXCLUDE = '^$' FUNCTION_INCLUDE = re.compile('.*') FUNCTION_EXCLUDE = re.compile('^$') class FakeLogger(object): def add(self, *args,**kw): pass def write(self, *args, **kw): write(*args, **kw) def ret(self, iter, value): return value DBGLogger = FakeLogger() #DBGLogger = None DBG = False def test(): global DBG print "DBG:", DBG def debug(x=None, gtk=False): global DBGLogger global DBG if gtk == True: DBGLogger = get_gtk_logger(show=True) if x is True: DBG = 1 if x is False: DBG = 0 if isinstance(x , int): DBG = x def write(*text, **kwargs): global DBG global DBGLogger if 'filter' not in kwargs: filter = None if 'sfilter' not in kwargs: sfilter = None if 'direct' not in kwargs: kwargs['direct'] = False if 'meth' not in kwargs: meth = caller() else: meth = kwargs['meth'] if 'called_by' not in kwargs: called_by = '' else: called_by = kwargs['called_by'] if DBG == 0: return if not text: ### FIXME: should go to DBGLogger...? print "%s (line %s) locals(): \n%s" % (caller(), caller(mode='lineno'), dshow(caller(mode='loc', level=2), ret=True, filter=filter, sfilter=sfilter,mute=True)) return txt = "" for i in text: txt += " " + str(i) if not kwargs['direct'] and DBGLogger: try: instance=caller(mode='instance') Class_str = instance.nick except Exception, e: Class_str = '' DBGLogger.write(txt=txt, line=caller(mode='lineno'), meth=meth, mode='write', Class=Class_str, caller=called_by) else: #print "dbg.write: QUI 2, DBGLogger:", DBGLogger, "direct:", kwargs['direct'] print "%s (line %s): %s" % (caller(), caller(mode='lineno'), txt) # def single_write(txt, called_by='', meth='', direct=False, **args): # """similar to write but accept just one text arg and some more args""" # global DBG # global DBGLogger # if 'filter' not in args: # filter = None # if 'sfilter' not in args: # sfilter = None # if DBG == 0: # return # if direct and DBGLogger: # DBGLogger.write(txt=txt, line=caller(mode='lineno'), meth=meth, # mode='write', caller=called_by) # else: # print "%s (line %s): %s" % (caller(), caller(mode='lineno'), txt) def caller(mode=None, level=2): """ mode can be: lineno: just return line number for the calling frame loc: return the local variables as dict None: return (class_name, caller_func) Class : return class instance : return class if applicable """ frame = sys._getframe(level) if mode == 'lineno': return frame.f_lineno if mode == 'loc': return frame.f_locals caller = frame.f_code.co_name class_name = '' try: a = frame.f_locals['self'].__class__ class_name = re.search("'(.*)'", str(a)).group(1) + "." if mode == 'Class': return a if mode == 'instance': return frame.f_locals['self'] except: pass return "%s%s" % (class_name, caller) def show_caller(filter=None, sfilter=None, loc=True): if DBG != 0: if loc: Locals = dshow(caller(mode='loc', level=3), ret=True, filter=filter, sfilter=sfilter,mute=True) else: Locals = '' print "%s called from: %s (line %s) \n%s" % ( caller(), caller(level=3), caller(level=3, mode='lineno'), Locals ) def dshow(diz, filter=None, sfilter=None, mute=False, ret=False, name=None): """print a dictionary in a friendly way filter: only print keys that match 'filter' sfilter: only print keys that _don't_ match 'filter' """ global DBG if DBG == 0: return txt = [] if not mute: txt += [caller()] filtered = {} for k,v in diz.iteritems(): if filter: if not re.search(filter, k): continue if sfilter: if re.search(sfilter, k): continue txt += [" %s: %s" % (k, v)] filtered[k] = v if ret: return "\n".join(txt) else: try: iter = DBGLogger.write(meth=caller(), txt=name, mode='dict') for k,v in filtered.iteritems(): DBGLogger.write(meth=k, txt=v, mode='value', parent=iter) except: print "\n".join(txt) def ddir(obj, filter=None, sfilter=None, mute=False, ret=False): """print dir of an object in a readable fasion filter: only print keys that match 'filter' sfilter: only print keys that _don't_ match 'filter' """ global DBG if DBG == 0: return txt = [] if not mute: txt += [caller()] for k in dir(obj): if filter: if not re.search(filter, k): continue if sfilter: if re.search(sfilter, k): continue v = getattr(obj, k) txt += [" %s: %s" % (k, v)] if ret: return "\n".join(txt) else: print "\n".join(txt) def warning(text): print text def sql_debug(sql, params): msg = re.sub(':(?P\S+)', '\'%(\g)s\'', sql) #sd.debug("%s %s" % (DBG, params)) print msg % params #write(msg % params) indent = 0 ### Trace class TraceIt(object): """A data descriptor that sets and returns values normally and prints a message logging their access. Usi it in code like this:: rowCommit = dbg.TraceIt(True, name='rowCommit', mode='rw') and use it as a normal variable. You'll get lines in your output stating who changed the value of that variable, in the form:: __str__/function_name: old_value => new_value __get__/function_name: value """ def __init__(self, initval=None, name='var', mode='rw'): self.read = False if re.search('r', mode): self.read = True self.write = False if re.search('w', mode): self.write = True self.val = initval self.name = name def __get__(self, obj, objtype): if self.read: Caller = re.sub('.*\.([^\.]+$)', r'\1', caller(level=2)) write("%s read %s" % (self.name, self.val), called_by=Caller, meth='%s - r/%s' % (self.name, Caller)) return self.val def __set__(self, obj, val): if self.write: Caller= re.sub('.*\.([^\.]+$)', r'\1', caller(level=2)) write("%s: %s -> %s" % (self.name, self.val, val), called_by=Caller, meth='%s - w/%s' % (self.name,Caller)) self.val = val ### from a recepe in ASP: # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/198078 def trace_class(ok=None, ko=None): """set pattern for classes that should be traced by LogTheMethods Rememeber that this command need to be issued before importing the package that should be traced """ global CLASS_INCLUDE global CLASS_EXCLUDE if ok: CLASS_INCLUDE = ok if ko: CLASS_EXCLUDE = ko def trace_function(exclude=None, include='.*'): """set pattern for functions/methods that should not be traced""" global FUNCTION_EXCLUDE global FUNCTION_INCLUDE if exclude: FUNCTION_EXCLUDE = re.compile(exclude) if not include == '.*': FUNCTION_INCLUDE = re.compile(exclude) def logmethod(methodname): def _method(self,*argl,**argd): global indent global DBGLogger global DBG #print "DBG", methodname, argl, argd #parse the arguments and create a string representation #import pdb; pdb.set_trace() args = [] for item in argl: args.append('%s' % str(item)) for key,item in argd.items(): args.append('%s=%s' % (key,str(item))) argstr = ','.join(args) caller1 = caller(level=1).replace('._method','') caller2 = caller(level=2) caller_class = caller1 caller_meth = caller2 try: class_str = self.nick except: class_str = caller_class if DBG: iter = DBGLogger.add(Class=class_str, meth=methodname, indent=indent, caller=caller_meth, line=caller(mode='lineno'), args=args, class_obj=caller(mode='Class')) indent += 1 # do the actual method call try: f = getattr(self,'_H_%s' % methodname) returnval = f(*argl,**argd) indent -= 1 except Exception, e: DBGLogger.add(Class=class_str, meth=str(e.__class__), indent=indent, caller=methodname, line=caller(mode='lineno'), args=[e], class_obj=caller(mode='Class')) indent -= 1 raise if DBG: ## iter is not defined if the method si not traced if iter: DBGLogger.ret(iter, str(returnval)) return returnval return _method class LogTheMethods(gobject.GObjectMeta): def __new__(cls, classname, bases, classdict): global DBG if re.search(CLASS_INCLUDE, classname): if not re.search(CLASS_EXCLUDE, classname): pass else: return type.__new__(cls,classname,bases,classdict) meths = {} meths.update(dict(classdict.items())) for base in bases: for attr in dir(base): # don't override the methods... if attr not in meths: meths[attr] = getattr(base, attr) for attr, item in meths.items(): if not DBG: continue if attr.startswith('_H_') or attr.startswith('__'): continue if callable(item) and FUNCTION_INCLUDE.match(attr) and not \ FUNCTION_EXCLUDE.match(attr): if '_H_%s'%attr not in meths: # prevent infinite loop classdict['_H_%s'%attr] = item # rebind the method classdict[attr] = logmethod(attr) # replace by wrapper classdict[attr].__doc__ = item.__doc__ # replace by wrapper ret = type.__new__(cls, classname, bases, classdict) return ret def get_gtk_logger(show=True): import gtk_dbg return gtk_dbg.ShowLogs(show=show) def get_logger(): global DBGLogger return DBGLogger sqlkit-0.9.5/sqlkit/debug/sql.py0000644000175000017500000000571111714210425016141 0ustar sandrosandro## based on code found in Internet and in the mailing list import re import sqlalchemy from sqlalchemy import orm, Table from sqlkit.db import utils def get_create_statement(meta, dialect='postgres'): """ return the create statement of a obj may be a table or a metadata :param meta: the metadata from which we want to get the create statement :param dialect: the engine type """ # http://www.sqlalchemy.org/trac/wiki/FAQ#HowcanIgettheCREATETABLEDROPTABLEoutputasastring from sqlalchemy import create_engine, Table from StringIO import StringIO def executor(s): buf.write(str(s.compile(dialect=engine.dialect))) buf.write(";\n") dialect = dialect or 'postgres' buf = StringIO() if sqlalchemy.__version__ <= '0.6': executor = lambda s, p="": buf.write(s+p) engine = create_engine("%s://" % dialect, strategy='mock', executor=executor) meta.create_all(engine) return buf.getvalue() def debug_inline_params(stmt, bind=None): """ Compiles a query or a statement and inlines bindparams. WARNING: Does not do any escaping. """ if isinstance(stmt, orm.Query): if bind is None: bind = stmt.session.get_bind(stmt._mapper_zero_or_none()) stmt = stmt.statement else: if bind is None: bind = stmt.bind compiler = bind.dialect.statement_compiler(bind.dialect, stmt) compiler.bindtemplate = "%%(%(name)s)s" compiler.compile() return compiler.string % dict((k,repr(v)) for k,v in compiler.params.items()) def get_table_definition(table, sql=None, meta=None, dialect=None): """ Return the table definition that would be issued by a metadata.create statement :param table: the table (sqla >= 0.6) or table_name. In case Table is provided no other args are needed :param sql: the sql statement as generated by metada.create_all(). If missing, meta must be provided :param meta: the metadata to be used to get the table's creation statement :param dialect: the dialect. Used by meta option (default: postgres) """ # SQLAlchemy 0.6 has clause constructs representing DDL operations, so the operation is much simpler: # # from sqlalchemy.schema import CreateTable # print CreateTable(mytable) if sqlalchemy.__version__ >= '0.6' and isinstance(table, Table): from sqlalchemy.schema import CreateTable return CreateTable(table) if isinstance(table, Table): meta = meta or table.metadata table = table.name if not sql: assert meta is not None sql = get_create_statement(meta, dialect) # ?= does not consume the matched string PAT = "(?PCREATE TABLE (?P\w+).*?(?=CREATE TABLE|$))" tables = {} def sub(m): tables[m.group('name')] = m.group('table_def') c=re.compile(PAT, re.DOTALL) c.sub(sub, sql) return tables[table] sqlkit-0.9.5/sqlkit/scripts/0000755000175000017500000000000011714210425015365 5ustar sandrosandrosqlkit-0.9.5/sqlkit/scripts/__init__.py0000644000175000017500000000000011714210425017464 0ustar sandrosandrosqlkit-0.9.5/sqlkit/scripts/sqledit.py0000755000175000017500000003354111714210425017415 0ustar sandrosandro#!/usr/bin/python # Copyright (C) 2005-2006-2007-2008-2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ usage: %prog [options] [[url|nick] table] -u, --url = URL: an url to open (eg postgres://user:pass@host/dbname) -n, --nick = nick: a nick in ~/.sqledit/nicks -t, --table = table: open table 'table' -m, --mask: open a SqlMask (default is SqlTable) -b, --browser: open the table browser reguardless of nick configuration -T, --sqltable: open a SqlTable (default when -t is used) -d, --dev: open in 'dev' mode -D, --debug: print debug -g, --gtk-debug: use LogTheMethods -S, --sql=statement: execute statement (requires -t) -a, --all-tables: read all table on startup (very slow) -f, --field_list=fields: comma (or space) separated field list -o, --order_by=fields: comma separated field list -c, --configure: open SqlMasq on _sqlkit_table or create it -v, --version: prin version and exit -L, --load: load data when opening a table (if no table is directly opened, set LoadData) -l, --limit=LIMIT: limit to LIMIT rows -i, --ipython: if ipython is present it opne an ipython shell -C, --create-templates=mode: create in one of these modes: layout, hooks, programm, models, all , --create-tables: Create all tables in models define in metadata (models.metadata or Base.metadata) """ import sys import os import re import datetime import user import locale import pygtk pygtk.require("2.0") import gtk if not 'LANG' in os.environ: my_locale, my_code = locale.getdefaultlocale() LANG = "%s.%s" % (my_locale, my_code) os.environ['LANG'] = LANG import locale locale.setlocale(locale.LC_ALL, '') import sqlalchemy as sa from sqlalchemy.orm import sessionmaker from sqlalchemy.exc import OperationalError from sqlalchemy.engine import url import sqlkit from sqlkit import fields, DbProxy, _ from sqlkit.misc import conf, optionparse RESPONSE_DEMO = 1 DEMO_DIRS = [ os.path.join('demo', 'sql'), os.path.join( os.path.dirname(os.getcwd()), 'demo', 'sql'), '/usr/share/doc/python-sqlkit/demo/sql', ] try: ## this is needed so that this file can be 'execfile-ed' in the 'docusage' ## directive of the documentation. In that context __file__ is not defined DEMO_DIRS += [ os.path.join( os.path.dirname(__file__), 'demo', 'sql'), os.path.join( os.path.dirname(os.path.dirname(__file__)), 'demo', 'sql'), os.path.join( os.path.dirname(sqlkit.__file__), 'demo', 'sql'), os.path.join( os.path.dirname(os.path.dirname(sqlkit.__file__)), 'demo', 'sql'), ] except: pass class ConnectionDialog(object): TITLE = _('Connection setup') + "(sqlkit %s)" % sqlkit.__version__ URL_DB_BACKEND='http://www.sqlalchemy.org/docs/dialects/index.html' URL_SQLKIT='http://sqlkit.argolinux.org/misc/sqledit.html' GENERAL_MSG = _(""" You can indicate the database you want to connect to using an URI in the form of: postgres://localhost/dbname sqlite:///dbname or using password and username: mysql://sandro:pass@host/dbname """) def __init__(self): """ represent the errors in self.validation_errors and raise ValidationError to correctly propagate the error upwards """ lay = """ l=general:< { lb=backend_help lb=sqledit } { e=url:30 c=auto} l=errors:< """ from sqlkit.layout import layout self.wdict = {} self.dialog, self.l = layout.dialog(mode=0, layout=lay, type=False, butts=None, title=self.TITLE) BUTTS = ( (_('Run Demo'), RESPONSE_DEMO), (gtk.STOCK_OK, gtk.RESPONSE_OK), (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL), ) for icon, response in BUTTS: self.wdict[response] = self.dialog.add_button(icon, response) self.wdict[gtk.RESPONSE_OK].props.sensitive = False self.wdict[RESPONSE_DEMO].props.sensitive = False self.dialog.set_default_response(gtk.RESPONSE_OK) self.l.widgets['l=general'].set_markup(self.GENERAL_MSG) self.l.widgets['l=general'].props.selectable = True self.l.widgets['l=general'].props.xpad = 10 self.l.widgets['l=general'].props.ypad = 10 self.l.widgets['l=errors'].props.selectable = True #l.widgets['a=url'].set self.dialog.show_all() self.l.widgets['e=url'].grab_focus() self.l.widgets['lb=backend_help'].set_uri(self.URL_DB_BACKEND) # self.l.widgets['lb=backend_help'].props.label = _('backend help') info = _('Info on available backend:\npostgres, mysql...') info2 = _('Sqledit manual page') self.l.widgets['lb=backend_help'].set_tooltip_text(info) self.l.widgets['lb=sqledit'].set_tooltip_text(info2) self.l.widgets['lb=sqledit'].set_uri(self.URL_SQLKIT) self.l.widgets['l=errors'].props.wrap = True info_auto_try = _("""Try continuosly to connect. Nice and usefull but may cause temporary blocks if you enter an inexistent hostname """) self.l.widgets['c=auto'].set_tooltip_text(info_auto_try) self.l.widgets['c=auto'].set_active(True) self.l.connect( ('lb=backend_help', 'clicked', self.clicked_cb), ('lb=sqledit', 'clicked', self.clicked_cb), ) self.l.widgets['e=url'].connect('changed', self.changed_cb) self.l.widgets['e=url'].connect('activate', self.activate_cb) self.set_error_msg('') self.add_demo() tip = _('If you enter a wrong hostname\nthe application may hang some seconds ' + \ 'till the natural network timeout. \nUncheck the flag on the right to disable this feature') self.l.widgets['e=url'].set_tooltip_text(tip) def changed_cb(self, entry): global opts url_text = entry.get_text() try: URL = url.make_url(url_text) except Exception, e: self.wdict[gtk.RESPONSE_OK].props.sensitive = False return self.set_error_msg("" ) if URL.drivername and URL.database and (URL.host or ( URL.drivername == 'sqlite' and os.path.exists(URL.database))): opts.url = url_text ## a checkbox determines if we try continuosly auto = self.l.widgets['c=auto'].get_active() if auto: self.activate_cb(entry) else: self.wdict[gtk.RESPONSE_OK].props.sensitive = False opts.url = None def activate_cb(self, entry): url_text = entry.get_text() self.set_error_msg(_("Attempting to connect to %s") % url_text) try: open_db(exc=True) self.set_error_msg(_("Connected to %s") % url_text, 'sea green') self.wdict[gtk.RESPONSE_OK].props.sensitive = True except Exception, e: self.set_error_msg(str(e), 'red') self.wdict[gtk.RESPONSE_OK].props.sensitive = False return def clicked_cb(self, widget): import webbrowser webbrowser.open(widget.get_uri()) def run(self): global opts response = self.dialog.run() if response == gtk.RESPONSE_CANCEL: sys.exit() elif response == RESPONSE_DEMO: start_demo() self.dialog.destroy() return response else: opts.url = self.l.widgets['e=url'].get_text() if open_db(): self.dialog.destroy() else: pass return response def add_demo(self): demo_message = _('A complete demo of all the features of the sqlkit package') demo_missing = _('The demo was not found, if you know where it is,\n' + \ 'run it manually: python demo.py') if find_demo(): self.wdict[RESPONSE_DEMO].props.sensitive = True self.wdict[RESPONSE_DEMO].set_tooltip_text(demo_message) else: self.wdict[RESPONSE_DEMO].set_tooltip_text(demo_missing) def set_error_msg(self, text, color='black'): from gobject import markup_escape_text markup = '%s' % (color, markup_escape_text(text)) self.l.widgets['l=errors'].set_markup(markup) def find_demo(): """ find the demo dir looking in fixed positions or in module sqlkit.demo """ found_demo = None for demo_dir in DEMO_DIRS: demo_program = os.path.join(demo_dir, 'demo.py') if os.path.exists(demo_program): found_demo = demo_dir try: from sqlkit.demo import sql demo_dir = os.path.dirname(sql.__file__) found_demo = demo_dir except ImportError, e: pass return found_demo def start_demo(): """ Start the demo application """ demo_dir = find_demo() demo_program = os.path.join(demo_dir, 'demo.py') if os.path.exists(demo_program): os.chdir(demo_dir) sys.path.insert(0, os.getcwd()) execfile('demo.py', {}) def open_db(exc=False): global db, models db = None if getattr(opts,'nick_dir', None): os.chdir(opts.nick_dir) sys.path.append(opts.nick_dir) if opts.run and not opts.browser: os.getcwd() execfile(opts.run, {'opts': opts}) sys.exit() else: if opts.models: import models try: if opts.create_tables: if not models.Base.metadata.bind: models.Base.metadata.bind = opts.url models.Base.metadata.create_all() except Exception, e: print e pass try: db = models.db except AttributeError: pass if opts.hooks: import hooks if opts.layout: import layout try: db = DbProxy(bind=opts.url) db.metadata.bind.connect() return True except Exception, e: raise e if exc: raise e else: if e.message == 'No module named MySQLdb': print "You need to install mysql driver (python-mysqldb under debian)" sys.exit(1) return False def program(): # Don't move this import before parsing opt gtk_debug, it won't work! from sqlkit.misc import table_browser global opts, tb if args: if re.search('://', args[0]): opts.url = args[0] else: opts.nick = args[0] if len(args) >1: opts.table = args[1] if opts.create_templates: conf.create_templates(opts) sys.exit(0) if opts.debug: dbg.debug(True) if opts.nick: try: opts = conf.read_conf(opts.nick, opts) except conf.NoSectionError: print "No nick named '%s'" % opts.nick sys.exit(1) if not args and not opts: dialog = ConnectionDialog() dialog.set_error_msg(os.environ['LANG']) response = dialog.run() else: response = None open_db() fields.BLANK_OK = True if not response == RESPONSE_DEMO: session = db.get_session() try: if opts.table and not opts.browser: tables = re.split('[ ,]+', opts.table) for t in tables: if len(tables) > 1: single = False else: single = True tb = table_browser.open_sqlwidget(t, single, opts, db) elif opts.configure: tb = table_browser.sqlkit_model(dbproxy=db, single=True) else: title = session.bind.url.database tb = table_browser.TableBrowser(db, title=title, opts=opts, x='quit') except OperationalError, e: if opts.debug: raise e else: print e.orig sys.exit(1) ###### Program def main(): global opts, args opts, args = optionparse.parse(__doc__) if opts.version: print sqlkit.__version__ sys.exit(0) if opts.gtk_debug: from sqlkit import debug as dbg dbg.trace_class(ok='SqlTable|SqlWidget|SqlMask|Completion') but = 'set_fkey_descr|(lookup|set|get)_value|[el]ne_cb|match_func' + \ '|cell_default_cb|cell_bool_cb|_lookup_value|is_fkey' dbg.trace_function(exclude=but) dbg.debug(True, gtk=True) import sqlkit program() if opts.ipython: try: gtk.set_interactive(True) except AttributeError: print "This version of gtk does'n not have 'set_interactive', sorry." sys.exit(1) try: import IPython from IPython.Shell import IPShellEmbed ipshell = IPShellEmbed([]) ipshell() except ImportError, e: print "Interactive demo needs ipython. Quitting." else: try: gtk.main() except KeyboardInterrupt: pass if __name__ == '__main__': main() sqlkit-0.9.5/sqlkit/layout/0000755000175000017500000000000011714210425015213 5ustar sandrosandrosqlkit-0.9.5/sqlkit/layout/__init__.py0000644000175000017500000000006111714210425017321 0ustar sandrosandrofrom layout import Layout, get_label, map_labels sqlkit-0.9.5/sqlkit/layout/interval_entry.py0000644000175000017500000000643411714210425020641 0ustar sandrosandro""" Very very minimal widget to edit intervals in 4 entries. Mainly used to make test pass. I gues anybody that needs to work with intervals should work out a bettere widget and possibly contribute it... """ import re from datetime import timedelta import gobject import gtk from sqlkit import debug as dbg class IntervalEdit(gtk.Alignment): __gtype_name__ = 'IntervalEdit' __gproperties__ = { 'interval' : (gobject.TYPE_PYOBJECT, # type 'Interval', # nick name 'The interval currently selected', # description gobject.PARAM_READWRITE), # flags } def __init__(self): gtk.Alignment.__init__(self) self.hbox = gtk.HBox() self.days = gtk.Entry() self.hours = gtk.Entry() self.minutes = gtk.Entry() self.seconds = gtk.Entry() try: ## only from gtk 2.12 self.days.set_tooltip_text('Days') self.hours.set_tooltip_text('Hours') self.minutes.set_tooltip_text('Minutes') self.seconds.set_tooltip_text('Seconds') except: pass self._align = gtk.Alignment(0,0,0,0) self.add(self.hbox) for name in ('days', 'hours',): # 'minutes', 'seconds'): widget = getattr(self, name) widget.set_property('width_chars', 8) widget.set_alignment(1) self.hbox.add(widget) self.show_all() self.connect('realize', self.set_packing) def set_packing(self, other): parent = self.get_parent() props = [prop.name for prop in parent.list_child_properties()] if 'y-options' in props: parent.child_set_property(self, 'y-options', 0) def split_interval(self, interval): if interval is None: return [0] * 4 days = interval.days hours, spare = divmod(interval.seconds, 3600) minutes, seconds = divmod(spare, 60) return days, hours, minutes, seconds def do_set_property(self, property, value): if property.name == 'interval': days, hours, minutes, seconds = self.split_interval(value) self.days.set_text(str(days) or '') self.hours.set_text("%.2d:%.2d:%.2d" % (hours, minutes, seconds)) #self.minutes.set_text(str(minutes)) #self.seconds.set_text(str(seconds)) def do_get_property(self, property): if property.name == 'interval': days = self.days.get_text() or 0 hours = self.hours.get_text() or '0:0:0' sep = "[:.]" H = "(?P\d+)" M = "(?:[:.](?P\d+))?" S = "(?:[:.](?P\d+))?" try: m = re.match(H+M+S, hours) hours, minutes, seconds = m.groups() except: ## FIXME: I should propagate an error hours, minutes, seconds = [0,0,0] value = timedelta(days=int(days), hours=int(hours or 0), minutes=int(minutes or 0), seconds=int(seconds or 0)) return value sqlkit-0.9.5/sqlkit/layout/layout.py0000644000175000017500000010367711714210425017120 0ustar sandrosandro# Copyright (C) 2005 Sandro Dentella. All rights reserved. # e-mail: sandro@e-den.it # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # FOUNDATION66, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """Layout Creation using a GUI description language This module provides a class named Layout, that uses a description language to generate xml code for glade to generate Gtk GUI Each widget is defined by a very concise string similar to e=txt or b=var """ import re import os import tempfile import gtk import gobject from sqlkit import debug as dbg from sqlkit import _ import widgets from sqlkitplugin import * # A simple way to import all custom widgets, only used by Builder() class UnbalancedLayoutCurlyBaces(Exception): pass class ConnectBeforeShowNotImplemented(Exception): pass class Layout(object): """Generate xml for a specific layout Methods: xml -- generate the xml for glade dim -- returns a tuple w/ the dimentions of the Table that contains the layout A container is generated to contain the widgets. Default is GtkTable (T), but other can be choosen by arguments or by specification """ notebook_tabs = [] _W = widgets._W container_regexp = _W['container_regexp'] shown = False def __init__(self, lay, level=0, parent_container="W", opts='T', elements=None, container_id='', addto=None, title=None, geom=(-1,-1), position=None, anchestor=None, label_map=None, ): """create a Layout description to be used to generate GUI lay: the variable that hold all the information on the layout level: nested level used internally by the recursion mechanism parent_container: a letter indicating the outermost container opts: options for the outer container. Same syntax as after a '{' container_id: ??? addto: the outermost container will be added to the widget 'addto' title: title of the window geom: a tuple indicating the desired width and height for the window """ #print 'layout init: %s level: %s' % (id(elements), level) self.anchestor = anchestor or self if label_map: self.label_map = map_labels(buf=label_map, private=True) else: self.label_map = {} if elements is None: elements = {} if level == 0: self.connect_dict = {} del self.notebook_tabs[0:] self.addto = addto self.elements = elements self.rows = 0 # maximum N. for iteration self.cols = 0 # idem self.level = level # nested level self.cells_list = [] self.cells = {} # each element is an LWidget or LContainer self.lay = lay self.parent_container = parent_container self.container_id = container_id self._parse_opts(opts) row = 0 col = -1 elements_list = self._parse_layout(lay.strip('\n ')) for el in elements_list: ### 'a capo' both w/ newline *and* @ if el in ('\n','@'): row = row + 1 self._set_dim(row,col) col = -1 else: ## add it to notebook tabs if el.startswith('%'): self.notebook_tabs += [re.sub('[%]','',el)] continue ### col span if el in '-': self._colspan(row,col) col = col + 1 continue col = col + 1 ### row span if el in '^': self._rowspan() continue nopts, nested, container_id = self._is_nested(el) if nested: new = Layout(nested, self.level + 1, self.container_flag, opts=nopts, elements=elements, container_id=container_id, anchestor=self.anchestor, label_map=label_map, ) self.cells[(row,col)] = new.container() new.container().position((row,col)) else: self.cells[(row,col)] = self._create_lwidget( el, (row, col), container=self.container_flag, elements=elements) #print "Layout(pos) %s: %s,%s" % (el, row, col) #self.cells[(row,col)].position((row, col)) self.cells_list += [self.cells[(row,col)]] #print "Fatta %s,%s %s" % (row, col, self.cells[(row,col)]) # for v in dir(self.cells[(row,col)]): # print "%s %s" % (el, v) self._set_dim(row,col) # not always usefull, never harmful #print "level: %d opts: %s type %s" % (self.level, opts, type(opts)) if opts and 's' in opts: self._add_status_bar(elements) #print "Cells_list: %s" % self.cells_list self.lcontainer = self._create_container(row, col, elements) self.elements = elements #self._add_container_to_elements() if self.parent_container == "W" and not addto: self._create_window(geom, title,position) def container(self): return self.lcontainer def _create_container(self, row, col,elements): # create the containet looking the widget dictionary _W #return Table('',self.parent_container, (row,col),'') key = self._create_container_id() if '|' in self.opts: ## well a | is a request to put a line around it, we do that ## with a FrameWithlabel 'F' (Frame+Align+Label) parent_container = 'A' if 'X' in self.opts or '>' in self.opts: parent_container = 'X' else: parent_container = self.parent_container cont = self._W[self.container_flag](key, (row, col), parent_container, children_list=self.cells_list, elements=elements, layout=self.anchestor) # in case | I add the Frame and I put now created 'cont' as child # Frame F will create 'A' (alignment) and move cont as its child if '|' in self.opts: key = "F." + self.container_id frame = self._W['F'](key, (row, col), self.parent_container, children_list=[cont], elements=elements, layout=self.anchestor) cont = frame # in case X I add the Expander and I put now created 'cont' as child if 'X' in self.opts or '>' in self.opts: key = "X." + self.container_id expander = self._W['X'](key, (row, col), self.parent_container, children_list=[cont], elements=elements, layout=self.anchestor) ## if >> expander will be expanded if re.search('>>',self.opts): expander.properties['expanded'] = True cont = expander ## notebook labels, if any if re.match("N.", key): if self.notebook_tabs: m = re.search('(.*)_(TOP|LEFT|RIGHT|BOTTOM)$', self.notebook_tabs[0]) if m: pos_note = m.group(2) self.notebook_tabs[0] = m.group(1) else: pos_note = 'top' self.notebook(key, self.notebook_tabs, pos_note.lower()) return cont def _create_container_id(self): # let's use an element of dict 'elements' as counter, so it is unique # for all the nested calls to layout if self.container_id: container_id = "%s%s" % (self.container_flag, self.container_id ) else: idx = '%s_progr' % self.container_flag #print "_create_cont_id: %s" % (self.container_flag) self.elements.setdefault(idx,-1) self.elements[idx] += 1 container_id = "%s.%s" % (self.container_flag, self.elements[idx] ) self.container_id = re.sub("^..",'',container_id) return container_id def _create_window(self, geom=None, title='Top Window',position=None): W = widgets.Window('Window:%s.%s' % (geom[0], geom[1]), (0,0),'W', children_list=[self.lcontainer], elements=self.elements, layout=self.anchestor) self.lcontainer = W if title: W.properties['title'] = title if position in ('NONE', 'CENTER', 'MOUSE', 'CENTER_ALWAYS', 'CENTER_ON_PARENT'): position = 'GTK_WIN_POS_'+position W.properties['window_position']=position #print "CREATE_WINDOW: %s" % self.lcontainer def _dbg(self, txt): if self.dbg == 1: print txt def _create_lwidget(self, el, pos, container,elements): ## i extract the type. Default type is 'le' #print "Ora creo " + el key = re.split("^(?:(.*)=)?(.*)",el)[1] or 'le' return self._W[key](el, pos, container, elements=elements, layout=self.anchestor) def _is_nested(self,el): ### is it a final token or a nested layout? p = re.compile(r""" { # start a new nested layout (?P[-=>|a-zA-Z0-9]*) # it may have some flags HV-=| (?P\S*) # possible identifier for the container (?P.*) # the nested layout } # the end... """, re.VERBOSE | re.DOTALL) m = p.match(el) ### nested: we call Layout again if m: #print "NESTED: cont_flag: %s opts: %s, nested: %s, id: %s" % \ # m.group('cont_flag', 'opts', 'nested', 'id') return m.group('opts', 'nested', 'id') else: #print "NOT NESTED %s" % el return None, None, None def _colspan(self,row,col): ## let's go backword to search for a defined cell for c in range(col,-1,-1): if (row,c) in self.cells: self.cells[row,c].colspan() return def _rowspan(self,row,col): ## let's go backword to search for a defined cell for r in range(row,-1,-1): if (r,col) in self.cells: self.cells[r,col].rowspan() return def _parse_layout(self, lay): lvl = 0 comment = 0 where = 'out' ret = [] nested = '' i = 0 for c in lay: i += 1 if c in '#': comment = 1 if c in '\n': comment = 0 if comment == 1: continue # entering a nested if c in '{': lvl +=1 # lowering the nested level if c in '}': lvl -=1 if lvl >= 1: nested += c if lvl == 0: if c in (' ','\t','@','\n'): if len(nested) > 0: ret += [nested] nested = '' if c in ('\n','@'): ret += [c] else: nested += c if len(nested) > 0: ret += [nested] if lvl != 0: raise UnbalancedLayoutCurlyBaces(lay) ## transform any default widget (those w/o modifier '[a-Z]+=') into ## L=name e=name. This happens if '-' is in the options #if True: if '-' in self.opts: new_ret = [] for el in ret: if re.match('([a-zA-Z0-9]+)=|[%-^\n@{]', el): new_ret += [el] else: pattern = re.compile(r""" (?P[^/:]*) # value (?:/(?P[^:]+))? # variable (?::(?P[-\d]*))? # width .? (?P[-\d]+)? # height """, re.VERBOSE) m = pattern.search(el) if m: new_ret += [ 'L=%s:%s' % (m.group('val') or '', m.group('w') or '')] new_ret += [ 'ae=%s:%s' % (m.group('val') or '', m.group('h') or '')] else: print "ERRORE: %s scartato" % el ret = new_ret return ret def _dbg_parse_layout(self): return self._parse_layout(self.lay) def _dbg_show_objs(self): for key,val in self.elements.iteritems(): print "%-10.10s ==> %s" % (key, val) def _add_status_bar(self, elements): self.rows += 1 sb = self._create_lwidget( 'sb=StBar', (self.rows,0), self.container_flag, elements) self.cells[self.rows,0] = sb self.cells_list += [sb] self.cells[self.rows,0].pack_properties['right_attach'] = self.cols+1 #print self.cells[self.rows,0].pack_properties # def _parse_flags(self, cont_flag=None): # # now is just which container to build, missing "-=|" # #container_regexp = '[THVBbNP]' # if not cont_flag: # self.container_flag = 'T' # default container is GtkTable 'T' # else: # s = re.search('(?P%s)' % self.container_regexp, cont_flag) # if s: # self.container_flag = s.group('cont') # else: # raise UnknownContainer ### FARE!!!!! def _parse_opts(self, opts=None): # now is just which container to build, missing "-=|" #container_regexp = '[THVBbNP]' # X (gtkExpander) is treated as a modifier rather than a real container # X can be added to a normal container flag. If we don't hide it from # container_regexp, it would hide the other container s = re.search('(?P%s)' % self.container_regexp.replace('X',''), opts) if s: self.container_flag = s.group('cont') else: self.container_flag = 'T' #print "_parse_opts: %s" % self.container_flag self.opts = opts.replace(self.container_flag,'') def _set_dim(self,row,col): self.rows, self.cols = max(self.rows, row), max(self.cols, col) def xml(self, filename=None, tmpfile=None, logfile=None): """Produces the xml for gtk.Builder() to create the GUI, possibly writes it to file""" start = ' '+\ '\n\n' end = "\n" # not needed if nested layout if self.level > 0: start = end = '' txt = self._clean_items(self.lcontainer.xml()) if tmpfile: tmpfile.write(txt) # write it to file? if filename: f = open(filename,'w') f.write("%s\n%s\n%s" % (start, txt, end)) f.close() else: return "%s\n%s\n%s" % (start, txt, end) def create(self, file=None, tmpfile=None): """pack the widgets using gtk function (no glade), start from lcontainer""" w = self.lcontainer.create(visible=False) w.show_all() return w def _clean_items(self, txt): pat = re.compile('^(\s+)([^\s<])',re.MULTILINE|re.DOTALL) return pat.sub(r'\2', txt) def dbg_xml(self): return self.lcontainer.dbg_xml() def dim(self): return "DIM %s,%s" % (self.rows+1, self.cols+1) def show(self,action=None, x=None): if self.shown: self.widgets['Window'].show() else: self.builder = gtk.Builder() xml = self.xml() try: self.builder.add_from_string(xml) except: # gtk 2.12 of Builder.ad_from_string() need the length self.builder.add_from_string(xml, len(xml)) self.widgets = {} for key, val in self.elements.iteritems(): ## if int, that's just a counter if not isinstance(val, int): self.widgets[key] = self.builder.get_object(key) if x: if 'Window' in self.widgets: W = self.widgets['Window'] if x == 'q': W.connect("delete_event", gtk.main_quit) else: W.connect("delete_event", x) if action == 'gtk.main': gtk.main() ## parent if self.addto: container_widget = self.widgets[self.lcontainer.el_def] self.addto.add(container_widget) self.shown = True return self.widgets def gshow(self,action=None, x=None): if self.shown: self.widgets['Window'].show() else: self.create() self.widgets = {} for key, val in self.elements.iteritems(): ## if int, that's just a counter if not isinstance(val, int): self.widgets[key] = val.gtkWdg if x: if 'Window' in self.widgets: W = self.widgets['Window'] if x == 'q': W.connect("delete_event", gtk.main_quit) else: W.connect("delete_event", x) if action == 'gtk.main': gtk.main() ## parent if self.addto: container_widget = self.widgets[self.lcontainer.el_def] self.addto.add(container_widget) self.shown = True return self.widgets def fshow(self,action=None, x=None, filename=None): if not filename: tmpdir = tempfile.mkdtemp() filename = os.path.join(tmpdir,'layout.glade') print filename self.xml(file=filename) glade = gtk.glade.XML(filename) glade.signal_autoconnect(self) self.widgets = {} for key, val in self.elements.iteritems(): if not isinstance(val, int): self.widgets[key] = glade.get_widget(key) if x: if 'Window' in self.widgets: W = self.widgets['Window'] if x == 'q': W.connect("delete_event", gtk.main_quit) else: W.connect("delete_event", x) if action == 'gtk.main': gtk.main() ## clean os.unlink(filename) os.rmdir(tmpdir) return self.widgets def tip(self, el_def, text): if self.shown: if isinstance(self.widgets[el_def], gtk.ToolItem): self.widgets[el_def].set_tooltip_text(text) else: self.widgets[el_def].set_tooltip_text(text) else: self.elements[el_def].properties['tooltip_text'] = text def menu(self, el_def, *args): """the dictionary must be a list of tuples (key, signal, handler) A separator can be as simple as '-' both in key and handler """ items = {} for d in args: d = list(d) el_def = el_def.replace('m=','M=') menu = self.widgets[el_def] #print "menu: ", el_def, menu, self.widgets key = d[0] if key == '-': items[key] = gtk.SeparatorMenuItem() #print "Separator" d.extend(['activate']) d.extend([None]) # try: # signal = d[2] # except IndexError: # signal = 'activate' # try: # i = d[3] # except IndexError: # i = None if not key in items: if key.startswith('gtk-'): items[key] = gtk.ImageMenuItem(key) else: items[key] = gtk.MenuItem(key) items[key].connect(*d[1:]) menu.append(items[key]) menu.show_all() def connect(self, *args): """the dictionary must be a list of tuples (key, signal, handler, arg1, arg2...)""" if not self.shown: raise ConnectBeforeShowNotImplemented( "\n You must run layout.connect after layout.show()") # syntactic check if not isinstance(args[0], tuple): self.connect_with_id(args) else: for d in args: self.connect_with_id(d) def connect_with_id(self, args): el_def = args[0] id = self.widgets[el_def].connect(*args[1:]) if el_def in self.connect_dict: self.connect_dict[el_def] += [(id, args[1], args[2])] else: self.connect_dict[el_def] = [(id, args[1], args[2])] def connect_set(self, *args): # syntactic check if not isinstance(args[0], tuple): raise WrongTypeConnect( "Layout.connect requires a list of tuples but found: '%s'" % args[0]) for d in args: self.disconnect(d[0], signal=d[1]) self.connect(*args) def disconnect(self, el_def, signal=None, id=None, handler=None): if id: self.widgets[el_def].disconnect(id) del self.connect_dict[el_def][id] if signal: i = 0 for info in self.connect_dict[el_def]: if info[1] == signal: self.widgets[el_def].disconnect(info[0]) del self.connect_dict[el_def][i] i += 1 def get_connect(self, el_def): pass def _dbg_widgets(self): for key, val in self.widgets.iteritems(): print "%-15.15s ==> %s" % (key, val) def sb(self,txt, seconds=None): """ Push info on Status Bar seconds: how many seconds it must stay displayed before being cancelled """ sb = self.widgets['sb=StBar'] # we need to prevent any attempt to write on a statusbar after the toplevel # windows is destroyed. Under Windows you get crashes randomly. if not isinstance(sb.get_toplevel(), gtk.Window): return idx = sb.get_context_id(txt) msg_id = sb.push(idx, txt) ## delete the message def del_text(idx, msg_id): if not isinstance(sb.get_toplevel(), gtk.Window): return try: sb.remove_message(idx, msg_id) except AttributeError: # deprecated in PyGTK 2.2.16 sb.remove(idx, msg_id) return False if seconds: gobject.timeout_add(seconds * 1000, del_text, idx, msg_id) def notebook(self, el_key, notebook_labels=None, tab_pos='top'): """Adds notebook entry for notebooks, allows to correct position of tabs, returns a dictionary w/ label entries""" notebook = self.elements[el_key] labels_dict = {} keys = [label.replace(' ','').lower() for label in notebook_labels] for index, label in enumerate(notebook_labels): key = keys[index] labels_dict[key] = widgets.NotebookLabel(key, (0, 0), 'N', self.elements, layout=self.anchestor) labels_dict[key].properties['label'] = _(label) children = notebook.lw['children_list'] new_children = [] index = 0 #print children for child in children: #print "notebook", el_key, index #print child.el_def key = keys[index] new_children += [child , labels_dict[key]] index = index + 1 notebook.lw['children_list'] = new_children ## change notebook tab position # make top the default allowed_positions = ["left", "right", "top", "bottom"] if tab_pos.lower() not in allowed_positions: tab_pos = "top" tab_pos = "GTK_POS_%s" % tab_pos.upper() notebook.properties['tab_pos'] = tab_pos return labels_dict def frame(self, el_key, notebook_labels=[], tab_pos='top'): """Adds frame labels allows to correct position of tabs, returns a dictionary w/ label entries""" ### PIGRIZIA!!! non ho neanche cambiato i nomi delle variabili! ### questa funziona va ricongiunta con notebook con un nome unico notebook = self.elements[el_key] labels_dict = {} keys = [label.replace(' ','').lower() for label in notebook_labels] # make top the default ## PRIMA DIFFERENZA allowed_positions = ["left", "right"] if tab_pos.lower() not in allowed_positions: tab_pos = "top" tab_pos = "GTK_JUSTIFY_%s" % tab_pos.upper() for index, label in enumerate(notebook_labels): key = keys[index] ### seconda differenza labels_dict[key] = widgets.FrameLabel(key, (0, 0), 'N', self.elements) ### bold face for frame labels labels_dict[key].properties['label'] = "<b>%s</b>" % \ label labels_dict[key].properties['justify'] = tab_pos labels_dict[key].properties['use_markup'] = True children = notebook.lw['children_list'] new_children = [] index = 0 for child in children: key = keys[index] new_children += [child , labels_dict[key]] index = index + 1 notebook.lw['children_list'] = new_children ## change notebook tab position return labels_dict def prop(self, *args): """ this is just a shorter way to set a property""" if isinstance (args[0], tuple): for prop in args: self.prop(*prop) return else: key, name, value = args[0], args[1], args[2] try: ## after self.show(),gtk widgets are there already self.widgets[key].set_property(name, value) except AttributeError, e: self.elements[key].properties[name] = value def pack(self, *args): """ this is just a shorter way to set a pack property""" if isinstance (args[0], tuple): for prop in args: self.pack(*prop) return else: key, name, value = args[0], args[1], args[2] self.elements[key].pack_properties[name] = value if 'widgets' in dir(self): raise NotImplementedError("pack must be used *before* layout.show()") def dialog(self, *args, **kw): """ create a dialog modal that will be destroyed with parent """ toplevel = self.widgets.values()[0].get_toplevel() return dialog(toplevel, *args, **kw) def message(self, type=None, text=None, show=True): """Pop a message dialog and return the dialog itself :param type: info, warn, warning, question, error. Default 'info'. :param text: Write this text :param show: (boolean), It true (default) shaw_all() """ if type is None: type = 'info' types = { 'info' : gtk.MESSAGE_INFO, 'warn' : gtk.MESSAGE_WARNING, 'warning' : gtk.MESSAGE_WARNING, 'question' : gtk.MESSAGE_QUESTION, 'error' : gtk.MESSAGE_ERROR, } window = self.widgets.values()[0].get_toplevel() opts = { 'parent' : window, 'flags' : gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, 'type' : types[type], 'buttons' : gtk.BUTTONS_OK, 'message_format' : text, } dialog = gtk.MessageDialog(**opts) if show: dialog.show_all() return dialog def map_labels(filename=None, buf=None, sep='\t', private=False): """ Return a dictionary with keys the element keys or just the names and values a tuple (label, tip) If private=False, this will be used in global widget.info """ # if it's a dict, just check that the value is a tuple # if not consider it a label and set tip to None if isinstance(buf, dict): for key, value in buf.iteritems(): if isinstance(value, basestring): buf[key] = (value, None) return buf if not filename and not buf: return {} info = {} if filename: f = open(filename) buf = f.read() f.close() for line in buf.strip('\n').split("\n"): # tab separated list of name, label, tooltip line = line.split(sep) name = line[0] try: label = line[1] except: label = name try: tip = line[2] except: tip = name info[name] = (label, tip) if private: return info else: widgets.info.update(info) def get_label(name, layout=None): """ return the label associated with 'name' if layout is passed, look first if a private layout_map exists Slightely different from widget.get_label_and_tip in that we don't look for a specific key (eg.: l=name) since we don't have it """ try: if layout: label, tip = layout.label_map.get(name, None) or widgets.info.get(name, None) else: label, tip = widgets.info.get(name, None) return label or name except TypeError, e: return name def get_tip(name, layout=None): """ return the help_text associated with 'name' if layout is passed, look first if a private layout_map exists """ try: if layout: label, tip = self.layout.label_map.get(name, None) or widgets.info.get(name, None) else: label, tip = widgets.info.get(name, None) return tip except TypeError, e: return None def dialog(toplevel = None, layout=None, title='', butts=None, type=None, text=None, show=True, mode='', wdict=False): """ A simple way to create a dialog and possibly to setup a layout :param toplevel: a toplevel to be bound to (optional) :param layout: the layout of the internal widgets :param title: a title :param butts: a possible tuple of (images/response) :param type: the type of dialog: ok, yes-no, yes-no-cancel, ok-cancel, ok-apply-cancel :param text: a simple text to be displayed :param mode: the mode (defaults to gtk.DIALOG_MODAL if no toplevel, gtk.MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, is toplevel is not None) use None to prevent settng the mode. :param wdict: if True a dict with the reposnse_id as key and the widet as value is added to the reurned values returns a (gtk.dialog, Layout object) """ if butts is None and type is None: type = 'ok' if type == 'yes-no': butts = (gtk.STOCK_YES, gtk.RESPONSE_YES, gtk.STOCK_NO, gtk.RESPONSE_NO) if type == 'yes-no-cancel': butts = (gtk.STOCK_YES, gtk.RESPONSE_YES, gtk.STOCK_NO, gtk.RESPONSE_NO, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) elif type == 'ok-cancel': butts = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) elif type == 'ok-apply-cancel': butts = (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_APPLY, gtk.RESPONSE_APPLY, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) elif type == 'ok': butts = (gtk.STOCK_OK, gtk.RESPONSE_OK) if mode == '': if toplevel: mode = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT else: mode = gtk.DIALOG_MODAL dialog = gtk.Dialog(title, toplevel, mode, ) bdict = {} if butts: for i in range(0, len(butts), 2): icon = butts[i] response = butts[i+1] bdict[response] = dialog.add_button(icon, response) lay = Layout(layout, addto=dialog.vbox) if text: lay.prop(('l=msg', 'label', text)) lay.show() if show: dialog.show_all() if wdict: return (dialog, lay, bdict) else: return (dialog, lay) sqlkit-0.9.5/sqlkit/layout/greedy_treeview.py0000644000175000017500000001111411714210425020754 0ustar sandrosandro""" A TreeView which gives as size_request the sum of the width requests of its columns """ import gtk import gobject class GreedyTreeView(gtk.TreeView): __gtype_name__ = 'GreedyTreeView' def __init__(self, *args): gtk.TreeView.__init__(self, *args) def do_size_request(self, requisition): treeview = self gtk.TreeView.do_size_request(self, requisition) # print "width would be", requisition.width model = treeview.get_model() try: rows_number = len(model) except: return columns = treeview.get_columns() titles = [column.get_title() for column in columns] widths = [] width_known = True for column_index in range(len(titles)): column = columns[column_index] cell_renderers = column.get_cell_renderers() renderers_widths = [] # We have 3 possibilities: # 1) the renderer has a fixed width (the member "fixed_width" is set in # table.py): we just take this as its width # 2) the renderer has a variable size and its criterion for populating # is set in table.py: then, it also has a member 'cells_data_func', # which contain informations about the criterion, and we use it to # populate and ask get_size() # 3) the renderer wasn't set in table.py: we just take a constant size. for renderer in cell_renderers: if hasattr(renderer, 'fixed_width') and renderer.fixed_width: renderers_widths.append(renderer.get_size(treeview, None)[2]) elif hasattr(column, 'cells_data_func'): # We'll have to check with all values that populate the column. renderer_func = None for cell_func in column.cells_data_func: if cell_func[0] == renderer: renderer_func = cell_func[1] renderer_func_data = cell_func[2:] break if renderer_func: # This will be the minimum (for non populated columns). max_width = 30 for row_index in range(rows_number): iter = model.get_iter(row_index) renderer_func(column, renderer, model, iter, *renderer_func_data) if gobject.type_is_a(renderer, gtk.CellRendererText): ellipsize = renderer.get_property('ellipsize') renderer.set_property('ellipsize', False) width = renderer.get_size(treeview, None)[2] if gobject.type_is_a(renderer, gtk.CellRendererText): # Put things back in order. renderer.set_property('ellipsize', ellipsize) max_width = max(max_width, width) renderers_widths.append(max_width) else: # The renderer was not found! width_known = False break else: # Don't know how to populate the renderer! width_known = False break top_widget = column.get_property('widget') if top_widget: top_width = top_widget.get_allocation().width else: top_width = 0 expected_width = sum(renderers_widths) expected_width += len(cell_renderers)*treeview.style_get_property('horizontal-separator') widths.append(max(expected_width, top_width) + 4) # FIXME: clarify this 4 # print "column", column.get_title() # print "renderers", cell_renderers # if hasattr(cell_renderers[0], 'lenght'): # print "lenght", cell_renderers[0].lenght # print "widths", renderers_widths # print "widths", widths if width_known: total_desired_width = sum(widths) else: total_desired_width = treeview.size_request()[0] # If we happened to mismatch because of missing introspection, let's # trust in gtk... requisition.width = max(total_desired_width, requisition.width) # print "instead, it could be", total_desired_width, "... it is", requisition.width sqlkit-0.9.5/sqlkit/layout/fk_entry.py0000644000175000017500000000471511714210425017415 0ustar sandrosandro""" An entry with an arrow. Similar to comboBoxEdit but with possibility to connect to click on the arrow """ import gobject import gtk from sqlkit import _ if gtk.gtk_version >= (2, 16): class FkEdit(gtk.Entry): __gtype_name__ = 'FkEntry' __gsignals__ = { 'completion-requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } def __init__(self): gtk.Entry.__init__(self) self.props.secondary_icon_stock = 'gtk-find' self.props.secondary_icon_tooltip_text = _( "Find allowed values (Control-Enter/Shift-Enter)") #self.props.secondary_icon_stock = None self.connect('icon-press', self.icon_press_cb) def icon_press_cb(self, entry, icon_pos, event): self.emit('completion-requested', event) else: class FkEdit(gtk.HBox): __gtype_name__ = 'FkEntry' __gsignals__ = { 'completion-requested' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } def __init__(self): gtk.HBox.__init__(self) self.entry = gtk.Entry() # the date button self.button = gtk.Button() self._align_entry = gtk.Alignment(1,0,1,0) self._align_arrow = gtk.Alignment(0,0,0,0) self._align_entry.add(self.entry) self._align_arrow.add(self.button) self.add(self._align_entry) self.add(self._align_arrow) # the down arrow arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_OUT) self.button.add(arrow) arrow.show() # finally show the button self._align_entry.show_all() self._align_arrow.show_all() self.button.connect('button-press-event', self.button_press_cb) self.connect('realize', self.set_packing) def button_press_cb(self, button, event): self.emit('completion-requested', event) def modify_base(self, state, color): self.entry.modify_base(state, color) def set_packing(self, other): parent = self.get_parent() props = [prop.name for prop in parent.list_child_properties()] if 'y-options' in props: parent.child_set_property(self, 'y-options', 0) sqlkit-0.9.5/sqlkit/layout/dateedit.py0000644000175000017500000006064111714210425017357 0ustar sandrosandro# Python GTK+ date and time entry widget. Version 0.2 # Copyright (C) 2005 Fabian Sturm, Sandro Dentella 2009 # # ported from the libgnomeui/gnome-dateedit.c # with changes where it makes sense for python (e.g. constructor, # return types etc.) # # This widget is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this widget; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA """ .. _date_edit: DateEdit ======== .. autoclass:: DateEdit :members: __init__, get_date, set_date .. autoclass:: DateTimeEdit :members: __init__, get_datetime, set_datetime """ import sys import os import re import time import locale import datetime import pygtk pygtk.require('2.0') import gobject import gtk from gtk import gdk from babel import dates from sqlkit import _ class WrongDateFormat(Exception): pass # gnome_date_edit_new: # @the_date: date and time to be displayed on the widget # @show_time: whether time should be displayed # @use_24_format: whether 24-hour format is desired for the time display. # # Description: Creates a new #GnomeDateEdit widget which can be used # to provide an easy to use way for entering dates and times. # If @the_date is 0 then current time is used. # # Returns: a new #GnomeDateEdit widget. class DateEdit(gtk.HBox): """ basic gtk widget to edit data """ __gtype_name__ = 'DateEdit' __gproperties__ = { 'date' : (gobject.TYPE_PYOBJECT, # type 'Date', # nick name 'The date currently selected', # description gobject.PARAM_READWRITE), # flags 'initial-date' : (gobject.TYPE_PYOBJECT, 'Initial Date', 'The initial date', gobject.PARAM_READABLE), 'width-chars' : (gobject.TYPE_PYOBJECT, 'Width of the entry in chars', 'The width of the entry', gobject.PARAM_READWRITE), } __gsignals__ = { 'date-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__(self): gtk.HBox.__init__(self) # preset values self._initial_date = self._current_date = None self.invalid_date = False #datetime.datetime.today() try: self.locale_setup() except locale.Error: pass self.build_layout() hid = self._date_entry.connect('changed', self.date_entry_changed_cb) self._ids = {'date_entry_changed' : (self._date_entry, hid)} self._date_button.connect('clicked', self.date_button_clicked_cb) self._calendar.connect('day-selected', self.day_selected_cb) self._calendar.connect('day-selected-double-click', self.day_selected_double_click_cb) self._cal_popup.connect('delete_event', self.delete_popup_cb) self._cal_popup.connect('key_press_event', self.key_press_popup_cb) self.date_format = 'short' self.date_parse = None self._id_timeout_add = None def locale_setup(self): if os.name == 'nt' or sys.platform == 'darwin': # Todo: fix this for windows self._use_24h_format = False else: # locale stuff, ugly hack, due to ugly locale functions saved_locale = locale.getlocale() if saved_locale[0] == None: # no locale set, so set it myself locale.setlocale(locale.LC_ALL, '') # the time format if locale.nl_langinfo(locale.T_FMT_AMPM) == '' : self._use_24h_format = True else: self._use_24h_format = False # restore original locale locale.setlocale(locale.LC_ALL, saved_locale) ##### layout def build_layout(self): self.set_spacing(6) date_box = gtk.HBox() self.add(date_box) date_box.show() # the date entry self._date_entry = gtk.Entry() self._entry = self._date_entry self._date_entry.set_max_length(15) self._date_entry.set_width_chars(10) self._align_entry = gtk.Alignment(0,0) #self.pack_start(self._date_entry, True, True, 0) date_box.add(self._align_entry) date_box.child_set_property(self._align_entry, 'expand', False) self._align_entry.add(self._date_entry) self._align_entry.show() self._date_entry.show() ## default style self._default_base = self._date_entry.get_style().base[gtk.STATE_NORMAL] # the date button self._date_button = gtk.Button() #pixmap = gtk.gdk.pixbuf_new_from_file('cal32.png') image = gtk.Image() image.set_from_stock('sk-calendar', gtk.ICON_SIZE_MENU) self._date_button.set_image(image) self._align_arrow = gtk.Alignment(0,0,0,0) self._align_arrow.show() self._align_arrow.add(self._date_button) #self.pack_start(self._date_button, False, False, 0) date_box.add(self._align_arrow) # # the down arrow # arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_OUT) # self._date_button.add(arrow) # arrow.show() # finally show the button self._date_button.show() # the calendar popup self._cal_popup = gtk.Window(gtk.WINDOW_POPUP) self._cal_popup.set_title('popup for calendar') self._cal_popup.set_events(self._cal_popup.get_events() | gdk.KEY_PRESS_MASK) self._cal_popup.set_resizable(False) # Todo: Needed? self._cal_popup.set_tooltip_text(_( 'Press Esc to close or double click a date to select it')) frame = gtk.Frame() frame.set_shadow_type(gtk.SHADOW_OUT) self._cal_popup.add(frame) frame.show() # the calendar self._calendar = gtk.Calendar() self._calendar.display_options(gtk.CALENDAR_SHOW_DAY_NAMES # some space around the widgets, and two boxes | gtk.CALENDAR_SHOW_HEADING) frame.add(self._calendar) self._calendar.show() def flash_bg(self, entry, color, seconds): """ flash background color for seconds to warn for wrong date format """ colormap = entry.get_colormap() base_color = colormap.alloc_color(color) entry.modify_base(gtk.STATE_NORMAL, base_color) gobject.timeout_add(1000 * seconds, entry.modify_base, gtk.STATE_NORMAL, self._default_base) def modify_base(self, state, color): self._date_entry.modify_base(state, color) self._default_base = color def popup_grab_on_window(self, window, activate_time): grab = gdk.pointer_grab(window, True, gdk.BUTTON_PRESS_MASK | gdk.BUTTON_RELEASE_MASK | gdk.POINTER_MOTION_MASK, None, None, activate_time) if grab == 0: if gdk.keyboard_grab (window, True, activate_time) == 0: return True else: gdk.pointer_ungrab(activate_time) return False return False def position_popup(self): req = self._cal_popup.size_request() (x,y) = gdk.Window.get_origin(self._date_button.window) x += self._date_button.allocation.x y += self._date_button.allocation.y bwidth = self._date_button.allocation.width bheight = self._date_button.allocation.height x += bwidth - req[0] y += bheight if x < 0: x = 0 if y < 0: y = 0 self._cal_popup.move(x,y) def hide_popup(self): self._cal_popup.hide() self._cal_popup.grab_remove() ##### callback def date_entry_changed_cb(self, entry): # check entry for valid date if self._id_timeout_add: gobject.source_remove(self._id_timeout_add) # let's delay the check so we can complete our input self._id_timeout_add = gobject.timeout_add(700, self.verify_date, entry) def verify_date(self, entry): """Verify the text anc heck if it parses as date, complain if not """ date_string = self._date_entry.get_text() if not date_string: self.set_date(None) return False try: if callable(self.date_parse): the_date = self.date_parse(date_string) else: the_date = dates.parse_date(date_string) except Exception, e: self.wrong_date_format(e, date_string) return False try: self.set_date(the_date, from_focus_out=True) except Exception, e: print e return False entry.modify_base(gtk.STATE_NORMAL, self._default_base) return False def date_button_clicked_cb(self, widget): # Temporarily grab pointer and keyboard on a window we know exists if not self.popup_grab_on_window(widget.window, gtk.get_current_event_time()): return # set calendar date if self.get_date(): self._calendar.select_month(self._current_date.month - 1, self._current_date.year) self._calendar.select_day(self._current_date.day) # position and show popup window self.position_popup() self._cal_popup.show() self._calendar.grab_focus() # Now transfer our grabs to the popup window, this should always succed self.popup_grab_on_window(self._cal_popup.window, gtk.get_current_event_time()) def day_selected_cb(self, widget): year, month, day = self._calendar.get_date() month += 1 the_date = datetime.date(year, month, day) self.set_date(the_date) def day_selected_double_click_cb(self, widget, data=None): self.hide_popup() def key_press_popup_cb(self, widget, event): """ Implement Esc to get rid of calendar """ if event.keyval == gtk.keysyms.Escape: self.hide_popup() return True return False def delete_popup_cb(self, widget, data=None): # Todo: is this ever called?? self.hide_popup(); lll return TRUE; ####### date & gobject def wrong_date_format(self, error, date_string): self.invalid_date = True colormap = self._date_entry.get_colormap() base_color = colormap.alloc_color('orangered') self._date_entry.modify_base(gtk.STATE_NORMAL, base_color) #self.flash_bg(self._date_entry, 'orangered', 2) def get_date(self): date_string = self._date_entry.get_text() if self.invalid_date: return date_string ## i'd like to raise the error but python wouldn't catch it... #raise WrongDateFormat(_('Wrong format for date %s' % date_string)) if not date_string: self._current_date = None return None return self._current_date def set_date(self, new_date, from_focus_out=False): """ new_date is a datetime.date object. Can be set by: - do_set_property - focus_out_event - popup click """ date_changed = not new_date == self._current_date self.invalid_date = False # set the time and date string entry, hid = self._ids['date_entry_changed'] entry.handler_block(hid) if new_date: if callable(self.date_format): try: self._date_entry.set_text(self.date_format(new_date)) except: self._date_entry.set_text( dates.format_date(new_date, format='short')) else: self._date_entry.set_text( dates.format_date(new_date, format=self.date_format)) else: self._date_entry.set_text('') entry.handler_unblock(hid) if date_changed: self._current_date = new_date self.emit('date-changed') return True return False def do_get_property(self, property): if property.name == 'date': return self.get_date() elif property.name == 'initial-date': return self._initial_date elif property.name == 'width-chars': return self._date_entry.props.width_chars else: raise AttributeError, 'unknown property %s' % property.name # set_properties def do_set_property(self, property, value): if property.name == 'date': self.set_date(value) elif property.name == 'width-chars': self._date_entry.props.width_chars = value else: raise AttributeError, 'unknown property %s' % property.name class DateTimeEdit(DateEdit): __gtype_name__ = 'DateTimeEdit' __gproperties__ = { 'datetime' : (gobject.TYPE_PYOBJECT, # type 'Date', # nick name 'The date currently selected', # description gobject.PARAM_READWRITE), # flags 'initial_datetime' : (gobject.TYPE_PYOBJECT, 'Initial Date', 'The initial date', gobject.PARAM_READABLE), 'lower-hour' : (gobject.TYPE_INT, 'Lower Hour', 'Lower hour in the time popup selector', 0, 24, 7, gobject.PARAM_READWRITE), 'upper-hour' : (gobject.TYPE_INT, 'Upper Hour', 'Upper hour in the time popup selector', 0, 24, 19, gobject.PARAM_READWRITE), 'show-time' : (gobject.TYPE_BOOLEAN, 'Show Time', 'Show the time widget', True, gobject.PARAM_READWRITE), # TODO: use-24 is not working, only 24 hours is working 'use-24h-format' : (gobject.TYPE_BOOLEAN, 'Use 24h Format', 'Display time in 24h format', True, gobject.PARAM_READWRITE) } __gsignals__ = { 'datetime-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), 'time-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__(self): # preset values self._initial_datetime = self._current_datetime = None self._lower_hour = 7; self._upper_hour = 19; self._show_time = True self._use_24h_format = True DateEdit.__init__(self) self._time_combobox.connect('changed', self.time_combobox_changed_cb) self._time_combobox.child.connect('focus-out-event', self.time_entry_focus_out_event_cb) self.connect('realize', self.fill_time_combobox) def build_layout(self): DateEdit.build_layout(self) time_box = gtk.HBox() self.add(time_box) time_box.show() # the time entry combobox self._time_combobox = gtk.ComboBoxEntry() self._time_combobox.child.set_max_length(12) self._time_combobox.child.set_width_chars(8) time_box.add(self._time_combobox) self._time_combobox.set_no_show_all(True) self._time_combobox.show() # set the initial widget properties self.set_show_time(self._show_time) self.set_use_24h_format(self._use_24h_format) def fill_time_combobox(self, widget, data=None): if self._lower_hour > self._upper_hour: return # create the new model store = gtk.TreeStore(str) for i in range(self._lower_hour, self._upper_hour + 1): the_time = datetime.time(i, 0) if self._use_24h_format: label = the_time.strftime('%H:%M') else: label = the_time.strftime('%I:%M %p') iter = store.append(None, [label]) # create sub menu for j in range(15,60,15): the_time = datetime.time(i,j) if self._use_24h_format: label = the_time.strftime('%H:%M') else: label = the_time.strftime('%I:%M %p') store.append(iter, [label]) # finally replace current model with this new one self._time_combobox.set_model(store) if self._time_combobox.get_text_column() == -1: self._time_combobox.set_text_column(0) def time_combobox_changed_cb(self, widget, data=None): # if the changed was due to a time selection in the menu, # handle as if focus-out-event if self._time_combobox.get_active_iter() != None: self.time_entry_focus_out_event_cb(widget, data) def time_entry_focus_out_event_cb(self, widget, data=None): # check entry for valid time, reset if not time_text = self._time_combobox.child.get_text() try: if self._use_24h_format: new_time = time.strptime(time_text, '%H:%M') else: new_time = time.strptime(time_text, '%I:%M %p') the_time = datetime.time(new_time.tm_hour,new_time.tm_min) the_date = self.get_date() the_datetime = datetime.datetime.combine(self.get_date(), the_time) except Exception, e: self.wrong_time_format(e, time_text) self.set_time(the_time) ############################################ # properties and their convenience functions def wrong_time_format(self, error, date_string): self.invalid_time = True self.flash_bg(self._time_combobox.child, 'orangered', 2) def set_use_24h_format(self, use_24h_format): dt = self.get_time() self._use_24h_format = use_24h_format self.fill_time_combobox(self) self._current_time = dt # get_properties def set_lower_hour(self, value): if value < 0 or value > 24 or value > self._upper_hour: return self._lower_hour = value self.fill_time_combobox(None) def set_upper_hour(self, value): if value < 0 or value > 24 or value < self._lower_hour: return self._upper_hour = value self.fill_time_combobox(None) def set_show_time(self, show_time): if show_time == True: self._show_time = True self._time_combobox.show() else: self._show_time = False self._time_combobox.hide() def get_time(self): """ return only the time """ time_string = self._time_combobox.child.get_text() if not time_string: self._current_time = None return None else: try: value = datetime.time(*[int(i) for i in re.split('[.:]', time_string)]) return value except Exception, e: self.wrong_time_format(e, time_string) return time_string def get_datetime(self): ### I should propagate errors via an Exception but I'm not able write now. See: # http://www.mail-archive.com/pygtk@daa.com.au/msg17444.html new_date = self.get_date() if isinstance(new_date, basestring): return new_date new_time = self.get_time() if isinstance(new_date, basestring): return new_time if not new_date: return None return datetime.datetime.combine(self.get_date(), self.get_time() or datetime.time()) def set_datetime(self, new_datetime): if new_datetime: date_changed = self.set_date(new_datetime.date()) time_changed = self.set_time(new_datetime.time()) else: date_changed = self.set_date(None) time_changed = self.set_time(None) if date_changed or time_changed: self.invalid_time = False self.invalid_date = False self.emit('datetime-changed') def set_time(self, new_time, focus=False): time_changed = not new_time == self._current_time if new_time == None: self._time_combobox.child.set_text('') else: # set the time and date string if self._use_24h_format == True: self._time_combobox.child.set_text(new_time.strftime('%H:%M')) else: self._time_combobox.child.set_text(new_time.strftime('%I:%M %p')) # emit signal on time change if time_changed == True: self._current_time = new_time self.invalid_time = False self.emit('time-changed') return True return False # get_properties def do_get_property(self, property): if property.name == 'datetime': return self.get_datetime() elif property.name == 'initial-datetime': return self._initial_datetime elif property.name == 'lower-hour': return self._lower_hour elif property.name == 'upper-hour': return self._upper_hour elif property.name == 'show-time': return self._show_time elif property.name == 'use-24h-format': return self._use_24h_format else: raise AttributeError, 'unknown property %s' % property.name # set_properties def do_set_property(self, property, value): if property.name == 'datetime': self.set_datetime(value) elif property.name == 'lower-hour': self.set_lower_hour(value) elif property.name == 'upper-hour': self.set_upper_hour(value) elif property.name == 'show-time': self.set_show_time(value) elif property.name == 'use-24h-format': self.set_use_24h_format(value) else: raise AttributeError, 'unknown property %s' % property.name class EntryCal(gtk.Entry): """ basic gtk widget to edit data that uses primary/secondary icon stock """ __gtype_name__ = 'DateEdit2' __gproperties__ = { 'date' : (gobject.TYPE_PYOBJECT, # type 'Date', # nick name 'The date currently selected', # description gobject.PARAM_READWRITE), # flags 'initial-date' : (gobject.TYPE_PYOBJECT, 'Initial Date', 'The initial date', gobject.PARAM_READABLE), 'width-chars' : (gobject.TYPE_PYOBJECT, 'Width of the entry in chars', 'The width of the entry', gobject.PARAM_READWRITE), } __gsignals__ = { 'date-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()), } def __init__(self): gtk.Entry.__init__(self) self.props.primary_icon_stock = 'gtk-quit' self.props.secondary_icon_stock = 'sk-calendar' self.props.secondary_icon_tooltip_text = "Calendar" if __name__ == '__main__': import misc # adds the sk-calendar stock-icon w = gtk.Window() d = EntryCal() # d = DateTimeEdit() w.add(d) w.show_all() gtk.set_interactive(True) sqlkit-0.9.5/sqlkit/layout/cal15.png0000644000175000017500000000137111714210425016630 0ustar sandrosandroPNG  IHDRsO/sRGBbKGD pHYs  tIME 37yIDAT(uOoe3z'NmVm(mPWȑC|@ҖVJpFN/Jsy/ֿՀfخ Q׳W[*fHzhIgeAI h}۠( "s%HFnʒzEYit.WWCCD&b .ԣݺ5edmӷ4o^9UU]ʲD~F=BDH ^]rp5/Ǩ^`+O3Fiv& xZ-R}Dӈx"BExOF$IX,H "z]lbn[TA]uQ1ai@\o`EUqeHex.tel/mail cell email tel_uff email2 fax { piva:11 cf:16 } - note_tel:.30 - - - {>.secondo_riferimento nome_partner cognome_partner tel_partner fax_partner cell_partner email_partner tel_uff_partner email2_partner } - - - } - - - {|>>.statistiche { {| fornitore partner concorrente cliente preventivo contatto } { mandato_da_id } {T.2m m2m=provenienza:3 m2m=motivo:3 } } {| vip influenzatore tu invito_cartaceo auguri anni esclusione_posta - - - - motivo_esclusione - - - - categoria_id - - } {= L=note:< @ TXS==note } - } - - - o2m=indirizzi:4 - - - } { %contatti m2o=contatti } # { %commissioni # } { %preventivi } { %jobs m2m=jobs } } - """ NEW_LAYOUT = """ | cognome ro=created m2m=progettisti nome architetto_id ^ N titolo_id tel:150 incompleto |>.tel/mail cell email - tel_uff email2 - fax piva:11 cf:16 note_tel:.30 - - - >.secondo_riferimento nome_partner cognome_partner tel_partner fax_partner cell_partner email_partner tel_uff_partner email2_partner |>>.statistiche ** | fornitore partner concorrente | cliente preventivo contatto mandato_da_id - m2m=provenienza:3 m2m=motivo:3 ** | vip influenzatore tu | invito_cartaceo auguri | anni esclusione_posta - - - - | motivo_esclusione - - - | categoria_id = L=note:< TXS==note o2m=indirizzi:4 - - - m2o=contatti # m2m=jobs """" sqlkit-0.9.5/sqlkit/layout/misc.py0000644000175000017500000000731011714210425016521 0ustar sandrosandroimport os import sys import gtk from sqlkit import _ ### utility: stock icons SK_ICONS = ( ('cal15.png', 'sk-calendar'), ('keys.png', 'sk-pkeys'), ('table-load.png', 'sk-table-load'), ) def add_stock_icons(*icon_list): factory = gtk.IconFactory() for icon_file, stock_id in icon_list: if os.path.exists(icon_file): filename = icon_file else: filename = os.path.join(os.path.dirname(__file__), icon_file) if not os.path.exists(filename): filename = os.path.join(os.path.dirname(sys.executable), icon_file) pixbuf = gtk.gdk.pixbuf_new_from_file(filename) iconset = gtk.IconSet(pixbuf) factory.add(stock_id, iconset) factory.add_default() add_stock_icons(*SK_ICONS) class StockMenuItem(gtk.ImageMenuItem): def __init__(self, label, stock, size=gtk.ICON_SIZE_MENU): gtk.ImageMenuItem.__init__(self, label) image = gtk.Image() image.set_from_stock(stock, size) self.set_image(image) self.show_all() class EasyTreeView(object): def __init__(self, cols, tv=None, model=None, sort=True, resize=True, show=True, but='', title='EasyTreeView'): if tv: self.tv = tv else: self.l = self._get_layout(but, title=title) self.l.prop('Window', 'visible', show) self.w = self.l.show() self.tv = self.w['TV=a'] self.l.connect( ('tb=gtk-quit', 'clicked', lambda l: self.w['Window'].destroy()), ) self.columns = [] self.column_names = [] for col in cols: if isinstance(col, str): self.column_names += [col] self.columns += [str] if isinstance(col, tuple): # the second position is for type self.column_names += [col[0]] self.columns += [col[1]] if not model: self.model = gtk.TreeStore(*self.columns) else: self.model = model self.tv.set_enable_search(True) self.tv.set_model(self.model) self.tvc = {} for i, col in enumerate(self.column_names): cell = gtk.CellRendererText if self.columns[i] == bool: cell = gtk.CellRendererToggle self.tvc[col] = gtk.TreeViewColumn(_(col), cell(), active=i) else: self.tvc[col] = gtk.TreeViewColumn(_(col), cell(), text=i) if sort == True: self.tvc[col].set_sort_column_id(i) self.tvc[col].set_resizable(resize) self.tv.append_column(self.tvc[col]) def show(self): self.w['Window'].show_all() def hide(self): self.w['Window'].hide() def _get_layout(self, but, title=None): from sqlkit import layout lay = """ {O tb=gtk-quit tb=gtk-clear %s} TVS=a """ % (but) #dbg.write() return layout.Layout(lay, opts="s", title=title) class InputStream(object): """ Simple Wrapper for File-like objects. [c]StringIO doesn't provide a readline function for use with generate_tokens. Using a iterator-like interface doesn't succeed, because the readline function isn't used in such a context. (see /tokenize.py) """ def __init__(self, data): self.__data = [ '%s\n' % x for x in data.splitlines() ] self.__lcount = 0 def readline(self): try: line = self.__data[self.__lcount] self.__lcount += 1 except IndexError: line = '' self.__lcount = 0 return line sqlkit-0.9.5/sqlkit/layout/sqlkitplugin.py0000644000175000017500000000076211714210425020320 0ustar sandrosandro#!/usr/bin/python # -*- coding: utf-8 -*- import sys import os from image_widget import ImageWidget from dateedit import DateEdit, DateTimeEdit from greedy_treeview import GreedyTreeView from fk_entry import FkEdit from interval_entry import IntervalEdit from misc import add_stock_icons SK_ICONS = ( ('cal15.png', 'sk-calendar'), ('keys.png', 'sk-pkeys'), ('table-load.png', 'sk-table-load'), ) add_stock_icons(*SK_ICONS) #path = os.path.split(os.path.dirname(__file__))[0] sqlkit-0.9.5/sqlkit/layout/sqlkit-catalog.xml0000644000175000017500000000243411714210425020657 0ustar sandrosandro glade_python_init sqlkit-0.9.5/sqlkit/layout/widgets.py0000644000175000017500000012050611714210425017237 0ustar sandrosandro# Copyright (C) 2005 Sandro Dentella. All rights reserved. # e-mail: sandro@e-den.it # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. import re import os import gtk import gobject from sqlkit import _ from sqlkit import debug as dbg from sqlkit.layout.greedy_treeview import GreedyTreeView class IdentifierAlreadyPresent: pass info = {} class LWidget(object): #__metaclass__ = LogTheMethods #logMatch = "position*" gtkClass = None transpose = {'w' : 'width_request', 'h' : 'height_request' } class_properties = {'visible' : True} class_pack_properties = {'y-options' : '0'} def __init__(self, el_def, pos, container, elements, layout): ## update info for this class # print "Lwidget: %s %s" % (el_def, self.gtkClass) # if self.gtkClass == 'GtkAlignment': # dbg.show_caller(sfilter='elements') self.el_def = el_def assert layout is not None self.layout = layout self.lw = {} self.container_flag = container self.properties = {} self.pack_properties = {} # By default, widgets do not expand vertically. Those who do can be # recognized (and set) by this attribute being True self.v_expand = False # used to specify order in properties self.ordered_properties = [] if self.gtkClass: self.properties.update(LWidget.class_properties) self.pack_properties.update(LWidget.class_pack_properties) self.properties.update(self.class_properties) self._parse_cell_def(el_def) self.lw.update( { ## lw : layout widget 'container' : container, 'el_def' : el_def, 'gtkClass' : self.gtkClass, 'children_list' : [], } ) self.position(pos) self.pos = pos self.elements = elements self._labels_and_tips_from_info() #dbg.write('class_specific', type(self)) self._class_specific() # if re.match('GtkEventBox', self.gtkClass): # dbg.write('ok') self._set_dimentions() #dbg.debug("elements[%s] = %s (%s)" % (self.lw['el_key'], # self,elements.keys()) ) elements[self.lw['el_key']] = self def position(self, (row, col)): ## ask the class of the container which are the pack_properties ## apart from Table. Table has position changing, the others pack ## widgets as they arrive. I tried with classmethod for Table w/ no luck if self.container_flag in ('T'): self.pack_properties.update( { 'left_attach' : col, 'top_attach' : row, 'right_attach' : col+1, 'bottom_attach' : row+1, }) else: self.pack_properties.update( _W[self.container_flag].class_pack_properties) def _debug_pos(self): p = self.pack_properties if self.container_flag == 'T': return "pos: %s : %s" % ((p['top_attach'],p['left_attach']), (p['bottom_attach'],p['right_attach'])) else: return '' def _parse_cell_def(self, el_def): pattern = re.compile(r""" (?:(?P\w+)=)? # ignore if passed the full string (?P[^/:<>]*) # value (?:/(?P[^:]+))? # variable (?:(?::(?P[\d]*))? # width \.? (?P-?[\d]+)?)? # height (?P[-<>])? # align, a < or a > at the end of the string """, re.VERBOSE) match = pattern.search(el_def) self.class_flag = match.group("flag") self.val = match.group("val") self.var = match.group("var") self.w = match.group("w") self.h = match.group("h") self.align = match.group("align") #dbg.write("el: %s w=%s h=%s align=%s-" % (el_def,self.w,self.h, self.align)) #print "_parse_cell_def: el: %s var=%s val=%s h=%s align=%s" % ( # el_def,self.var,self.val, self.h, self.align) #print match.groups() if not self.var: ## radiobutton use self.val as 'group' if not re.search("GtkRadioButton",self.gtkClass): self.var = self.val ## let's set a reasonable key. Strip any width info # not sure If I should put all Containers cont_pat = "Gtk(Frame|Table|HBox|VBox|Notebook)" if not re.search(cont_pat,self.gtkClass): self.lw['el_key'] = re.sub('(:.*)|[-<>]$','',el_def) else: self.lw['el_key'] = re.sub('[-<>|=]$','',el_def) ### gtk-stock? # if re.match('gtk-', self.val) and not self.gtkClass == 'GtkImage': if gtk.icon_factory_lookup_default(self.val) and not self.gtkClass == 'GtkImage': if re.match('GtkToolButton', self.gtkClass): self.properties['stock_id'] = re.sub('\..+','',self.val) self.properties['label'] = None elif re.match('GtkButton', self.gtkClass): self.properties['label'] = _(re.sub('\..+','',self.val)) #dbg.write(self.properties['label']) self.properties['use-stock'] = True else: self.properties['use-stock'] = True def _labels_and_tips_from_info(self): """ set possible label and tip """ if not self.class_flag: return label, tip = self.get_label_and_tip() if label: self.val = label if tip: self.properties['tooltip_text'] = tip def get_label_and_tip(self): """ keys can be mapped to labels in 3 ways: 1. via a label_map local to the layout (shared with all nested layout) 2. via a global info dictionary 3. via gettext 1. is attempted first, failing that 2. is attempted. The resulting string is anyhow passed via gettext. The aim is to allow to use per-table (meaning database table) translation from a name to the relative label. Names can normally be similar between tables. """ name = self.val key = "%s=%s" % (self.class_flag, name) details = self.layout.label_map.get(key, None) or info.get(key, None) if not details: details = self.layout.label_map.get(name, None) or info.get(name, None) if details: return details else: return None, None def _set_dimentions(self): """Give a meaning to dimentions for this object""" ## width if self.w and 'w' in self.transpose: name = self.transpose['w'] self.properties[name] = self.w #self.pack_properties['x_options'] = 'shrink' ## 'height' if self.h and 'h' in self.transpose: name = self.transpose['h'] self.properties[name] = self.h def rowspan(self): if self.lw['container'] in ('T'): self.pack_properties['bottom_attach'] += 1 # else: # dbg.warning("Span available only for Table qui: %s" % \ # self.lw['container']) def colspan(self): if self.lw['container'] in ('T'): self.pack_properties['right_attach'] += 1 # else: # dbg.write("Span available only for Table qui: %s" % self.lw['container']) def _class_specific(self): pass ### Real pack def xml(self, ContGtkClass=None): return "%s\n%s" % (self._widget_xml(), self._packing_xml(self.pack_properties,ContGtkClass)) def create(self): """ Create the wdget """ #dbg.write(self.gtkClass) #print dbg.dshow(self.properties) self.gtkWdg = gobject.new(gobject.type_from_name(self.gtkClass)) # **self.properties for p, val in self.properties.iteritems(): try: self.gtkWdg.set_property(p, val) except: pass #dbg.write("Fail prop %s for %s" % (p, self.gtkClass)) return self.gtkWdg def _packing_xml(self, pack_properties, ContGtkClass): # no packing if adding with 'addto'. In that case ContGtkClass # is not defined if not ContGtkClass: return "" allowed_pack_properties = () for i in gtk.container_class_list_child_properties(ContGtkClass): allowed_pack_properties += (i.name,) xml = [""] for key, value in pack_properties.iteritems(): ## tabs label are child of notebook but of a type that ## is not returned by container_class... function above if (key == 'type' and ContGtkClass == 'GtkNotebook') \ or key.replace('_','-') in allowed_pack_properties: xml.append(' %s' % (key, value)) xml.append("") return "\n".join(xml) def _widget_xml(self): # If a children is v_expandable, the parent becomes too. if 'children_list' in self.lw and self.lw['children_list']: if True in [child.v_expand for child in self.lw['children_list']]: self.make_v_expandable() xml = ['' % self.lw] inner_xml = [] # write properties with order before extra_text = "" for key in self.ordered_properties: value = self.properties[key] if key in self.transpose: pass #print key, self.transpose[key] if key in ('text', 'label'): extra_text = ' translatable="yes"' if value == '': value = None else: extra_text = "" if value: inner_xml.append('%s' %\ (key, extra_text, value)) # now write everything else extra_text = "" for item, value in self.properties.iteritems(): if item not in self.ordered_properties: if item in self.transpose: item = self.transpose[item] if item in ('text', 'label'): extra_text = ' translatable="yes"' else: extra_text = "" if value is not None: inner_xml.append('%s' %\ (item, extra_text, value)) children = self._children_xml() if children: inner_xml.append(children) xml.append(self._offset_txt("\n".join(inner_xml), 2)) xml.append("") return "\n".join(xml) def _children_xml(self): # this is significant only for LContainer return None def _offset_txt(self, txt, amount): ## offset text to get a prettier xml p = re.compile("^", re.MULTILINE) return p.sub(' ' * amount, txt) def make_v_expandable(self): """Teach a widget it must expand (vertically); it can be because it has a viewing benefit (e.g: a Treeview) or because a child of it has. """ if self.v_expand: return self.v_expand = True # FIXME: (how?!) this expands horizontally if parent is a HBox self.pack_properties.update({'expand' : True}) self.pack_properties.update({'y-options' : 'expand | fill'}) def __str__(self): return "<%s: %s >" % (self.__class__.__name__, self.el_def) def __repr__(self): return "<%s: %s >" % (self.__class__.__name__, self.el_def) class LContainer(LWidget): # the difference with LWidget is it has children # and the el_def is not written in the layout but created on the fly # (Layout._create_container_label). We use it as label class_pack_properties = {'y-options' : 'fill|expand'} def __init__(self, el_def, pos, container, children_list=None, elements=None, layout=None): LWidget.__init__(self, el_def, pos, container, elements=elements, layout=layout) ## the children self.lw['children_list'] = children_list #self.properties['label'] = el_def def _children_xml(self): txt = '' try: for obj in self.lw['children_list']: child = self._offset_txt(obj.xml(self.gtkClass), 4) txt += "\n\n%s\n \n" % ( child, obj.lw['el_key'], obj.lw['container']) except: pass return txt def create(self, visible=None): """ Create the wdget """ if visible is not None: self.properties['visible'] = visible #dbg.write(self.gtkClass) self.gtkWdg = gobject.new(gobject.type_from_name(self.gtkClass), **self.properties) #dbg.write(gtkWdg) self.add_children(self.gtkWdg) return self.gtkWdg def add_children(self, gtkWdg): if not self.lw['children_list']: return for obj in self.lw['children_list']: #dbg.write("gtkWdg", gtkWdg, obj.gtkClass) child = obj.create() pack_props = self.clean_pack_properties(obj.pack_properties, gtkWdg) if self.gtkClass not in ('GtkTable'): #dbg.write('%s.add(%s, %s)' %(gtkWdg, child, pack_props )) gtkWdg.add(child) for p, val in pack_props.iteritems(): #dbg.write("%s.set_property(%s, %s)" % (gtkWdg, p, val)) setattr(gtkWdg, p, val) if self.gtkClass == 'GtkTable': P = pack_props #dbg.write("pack_props", P, obj.pack_properties) gtkWdg.attach(child, P['left_attach'], P['right_attach'], P['top_attach'], P['bottom_attach'], ) return def clean_pack_properties(self, pack_props, gtkWdg): return pack_props # dbg.write(gtkWdg, gtkWdg.name) # dbg.write(gtk.container_class_list_child_properties(gtkWdg)) # props = [p.name for p in gtk.container_class_list_child_properties(gtkWdg)] # dbg.write(props) # ko = [] # for p in pack_props: # if p not in props: # ko += [p] # for p in ko: # del pack_props[p] # dbg.write(pack_props) # return pack_props class AlignmentEntry(LContainer): gtkClass = 'GtkAlignment' def __init__(self, el_def, pos, container, children_list=None, elements={}, layout=None): el_def = el_def.replace('ae=','A=') super(LContainer, self).__init__(el_def, pos, container, elements=elements, layout=layout) ## prepare the names... entry_str = "e=%s" % (el_def.replace('A=','')) wdg = Entry(entry_str,(0,0), 'A', elements=elements, layout=layout) self.lw['children_list'] = [ wdg ] if self.align == '-': self.properties['xscale'] = 1 else: self.properties['xscale'] = 0 if self.align == '>': self.properties['xalign'] = 1 else: self.properties['xalign'] = 0 def _class_specific(self): self.pack_properties.update({ 'x_options' : 'expand|fill', }) def _get_w(self): return -1 def _set_w(self, value): ## when setting the with of an ae object we just want to set the width of the entry return w = property(_get_w, _set_w ) class Entry(LWidget): gtkClass = 'GtkEntry' transpose = { 'w' : 'width_chars', } # def _class_specific(self): # if self.w: # self.properties['max-length'] = self.w class DateEdit(LWidget): gtkClass = 'DateEdit' ## this widget is provided by dateedit module class ImageWidget(LWidget): gtkClass = 'ImageWidget' ## this widget is provided by image module class_properties = { 'image' : None, } class DateTimeEdit(LWidget): gtkClass = 'DateTimeEdit' ## this widget is provided by dateedit module class_properties = { 'time' : None, 'lower-hour' : 10, 'upper-hour' : 23, 'show-time' : None, 'use-24h-format' : True } class Calendar(LWidget): # TODO: add a constructor to pass dates in a friendly way gtkClass = 'GtkCalendar' def _class_specific(self): self.pack_properties.update({'y_options': ""}) #class_properties = { #'visible' : True, #'day' : 7, #'month' : 3, #'year' : 1963, #} class TreeView(LWidget): gtkClass = 'GreedyTreeView' transpose = { 'w' : 'width_request', 'h' : 'height_request', } class_properties = { 'width_request' : '-1', 'height_request' : '-1', } def _class_specific(self): self.make_v_expandable() class TextView(LWidget): gtkClass = 'GtkTextView' def _class_specific(self): self.make_v_expandable() class Statusbar(LWidget): gtkClass = 'GtkStatusbar' def _class_specific(self): if self.container_flag == "T": self.pack_properties.update({'y_options': ''}) elif self.container_flag == "V": self.pack_properties.update({'pack_type': 'GTK_PACK_END'}) class Label(LWidget): gtkClass = 'GtkLabel' transpose = { 'w' : 'width_chars', } def _class_specific(self): self.properties.update({ 'xalign' : '1', 'label' : _(self.val).replace('_',' ') + ':', 'xpad' : 10, 'use-markup' : True, }) self.pack_properties.update({ 'y_options' : 'fill', 'x_options' : 'fill', }) if self.align: #dbg.debug("SELF ALIGN for ", self.lw['el_key']) if self.align in '<': #dbg.debug("""properties['xalign'] = 0""") self.properties['xalign'] = 0 else: self.properties['xalign'] = 1 class LabelFill(LWidget): gtkClass = 'GtkLabel' def _class_specific(self): self.properties.update({ 'xalign' : '0', 'label' : _(self.val), 'xpad' : 10, }) self.pack_properties.update({ 'y_options' : 'expand|shrink|fill', }) class NotebookLabel(Label): """This is for the tab of a notebook""" gtkClass = 'GtkLabel' # def _class_specific(self): # ## must overwrite any other info # self.pack_properties = { # 'type' : 'tab', # } class FrameLabel(Label): """This is for the tab of a notebook""" gtkClass = 'GtkLabel' class_properties = { 'xpad' : 5, } def _class_specific(self): ## must overwrite any other info self.pack_properties = { 'type' : 'label_item', } class Button(LWidget): gtkClass = 'GtkButton' # transpose = { # 'w' : 'width_chars', # } class_pack_properties = { 'expand' : False, 'fill' : True, } def _class_specific(self): self.properties.update({ 'xalign' : '0', 'label' : _(re.sub('\..+','',self.val)) }) self.pack_properties.update({'y_options': ""}) class ToolButton(LWidget): gtkClass = 'GtkToolButton' def _class_specific(self): #if re.match("gtk-",self.val): if gtk.icon_factory_lookup_default(self.val): del self.properties['label'] #self.properties['stock_id'] = self.val class RadioButton(Button): gtkClass = 'GtkRadioButton' def _class_specific(self): if self.var: ## this puts this radioButton in the same group as the radiobutton ## written after the '/' self.properties['group'] = 'r=' + self.var class ToggleButton(Button): gtkClass = 'GtkToggleButton' class CheckButton(Button): gtkClass = 'GtkCheckButton' def _class_specific(self): self.properties['label'] = '' class LinkButton(LWidget): gtkClass = 'GtkLinkButton' # transpose = { # 'w' : 'width_chars', # } class_pack_properties = { 'expand' : False, 'fill' : True, } def _class_specific(self): self.properties.update({ 'xalign' : '0', 'label' : _(re.sub('\..+','',self.val)) }) self.pack_properties.update({'y_options': ""}) class ProgressBar(LWidget): gtkClass = 'GtkProgressBar' class ComboBox(Entry): gtkClass = 'GtkComboBox' transpose = { 'w' : 'width_request', } def _class_specific(self): self.pack_properties.update({ 'x_options' : 'fill', }) class AlignmentComboBox(LContainer): gtkClass = 'GtkAlignment' def __init__(self, el_def, pos, container, children_list=None, elements={}, layout=None): el_def = el_def.replace('aC=','A=') super(LContainer, self).__init__(el_def, pos, container, elements=elements, layout=layout) ## prepare the names... entry_str = "C=%s" % (el_def.replace('A=','')) #print "ENTRY_STR", entry_str ## and create the children #print "EventLabel elements: ", elements.keys() wdg = ComboBox(entry_str,(0,0), 'A', elements=elements, layout=layout) self.lw['children_list'] = [ wdg ] if self.align == '-': self.properties['xscale'] = 1 else: self.properties['xscale'] = 0 if self.align == '>': self.properties['xalign'] = 1 else: self.properties['xalign'] = 0 def _class_specific(self): self.pack_properties.update({ 'x_options' : 'expand|fill', }) def _get_w(self): return -1 def _set_w(self, value): ## when setting the with of an ae object we just want to set the width of the entry return w = property(_get_w, _set_w ) class ComboBoxEntry(Entry): gtkClass = 'GtkComboBoxEntry' class SpinButton(LContainer): gtkClass = 'GtkSpinButton' class_properties = { "can_focus" : True, "climb_rate" : 1, "digits" : 0, "numeric" : True, "update_policy" : 'GTK_UPDATE_ALWAYS', "snap_to_ticks" : False, "wrap" : False, # "adjustment" : '0 0 1000 1 10 0', } class ToolSpinButton(LContainer): gtkClass = 'GtkToolItem' def __init__(self, el_def, pos, container, elements, layout=None): el_def = el_def.replace('ts=', 'ti=') #super(LContainer, self).__init__(el_def, pos, container=container, elements=elements, layout=layout) super(ToolSpinButton, self).__init__(el_def, pos, container=container, elements=elements, layout=layout) ## if spinbutton in toolbar, we need to encapsulate in a ToolItem spin_def = el_def.replace('ti=','s=') spin = SpinButton("%s" % (spin_def), (0,0), 'ti', elements=elements, layout=layout) self.lw['children_list'] = [ spin ] #self.el_def = "ti=%s" % (self.var) class Image(LWidget): gtkClass = 'GtkImage' def _class_specific(self): ## self.var is probably the name of a image file ## let's check, look for it and set it with a full path #if self.var.startswith('gtk-'): if gtk.icon_factory_lookup_default(self.var): self.properties['icon_name'] = self.var self.properties['icon_size'] = self.w self.w = None self.properties.pop('width_request', None) else: cdir = os.getcwd() filename = os.path.join(cdir,self.var) if os.path.exists(filename) and os.path.isfile(filename): self.properties['pixbuf'] = filename class Window(LContainer): gtkClass = 'GtkWindow' ## secondo me questo non dovrebbe essere necessario class_pack_properties = { } class_properties = { 'visible' : True, 'title' : 'window1', 'type' : 'GTK_WINDOW_TOPLEVEL', 'window_position' : 'GTK_WIN_POS_NONE', 'modal' : False, 'resizable' : True, 'destroy_with_parent' : False, 'decorated' : True, 'skip_taskbar_hint' : False, 'skip_pager_hint' : False, 'type_hint' : 'GDK_WINDOW_TYPE_HINT_NORMAL', 'gravity' : 'GDK_GRAVITY_NORTH_WEST', } def __init__(self, *args, **kw): LContainer.__init__(self, *args, **kw) def _packing_xml(self, packing_info, ContGtkClass): return '' class Table(LContainer): gtkClass = 'GtkTable' class_pack_properties = { 'homogeneous' : False, # 'expand' : True, 'y_options' : 'shrink', # 'y_options' : 'expand|fill', 'x_options' : 'shrink', } #def _class_specific(self): #self.pack_properties.update({"y_options": ""}) class VBox(LContainer): gtkClass = 'GtkVBox' class_pack_properties = { 'padding' : '0', 'expand' : False, 'fill' : False, } class_properties = { 'homogeneous' : False, 'spacing' : 0, } class HBox(VBox): gtkClass = 'GtkHBox' class Layout(LContainer): gtkClass = 'GtkLayout' class Fixed(LContainer): gtkClass = 'GtkFixed' class ScrolledWindow(LContainer): gtkClass = 'GtkScrolledWindow' def _class_specific(self): self.properties.update({ 'hscrollbar_policy' : 'GTK_POLICY_AUTOMATIC', 'vscrollbar_policy' : 'GTK_POLICY_AUTOMATIC', 'shadow_type' : 'GTK_SHADOW_IN', }) self.pack_properties.update({ 'expand' : True, 'fill' : True, #'y_options' : 'GTK_EXPAND|GTK_FILL', #'y_options' : 'GTK_EXPAND|GTK_FILL', }) class Notebook(LContainer): gtkClass = 'GtkNotebook' def _class_specific(self): self.make_v_expandable() def _children_xml(self): txt = '' try: for obj in self.lw['children_list']: ## incorrect: it would fail f the only sun of a tab is a Label!!! child_type = 'type="tab"' if obj.gtkClass == 'GtkLabel' else '' child = self._offset_txt(obj.xml(self.gtkClass), 4) txt += "\n\n%s\n \n" % ( child_type, child, obj.lw['el_key'], obj.lw['container']) except: pass return txt class VPaned(LContainer): gtkClass = 'GtkVPaned' class_pack_properties = { 'resize' : False, } def __init__(self, el_def, pos, container, children_list=None, elements=None, layout=None): msg = "%s cannot have more that 2 children (now: %s)" % (self.gtkClass, children_list) assert len(children_list) <= 2, msg LWidget.__init__(self, el_def, pos, container, elements=elements, layout=layout) ## the children self.lw['children_list'] = children_list def _class_specific(self): self.make_v_expandable() class HPaned(VPaned): gtkClass = 'GtkHPaned' #class_pack_properties = VPaned.class_pack_properties class DrawingArea(LWidget): gtkClass = 'GtkDrawingArea' class HandleBox(LContainer): gtkClass = 'GtkHandleBox' class Expander(VPaned): gtkClass = 'GtkExpander' class_properties = { 'expanded' : False, 'border_width' : 3, } def _class_specific(self): label = self.el_def.replace('X.','').replace('_',' ') self.properties['label'] = label class ViewPort(LContainer): gtkClass = 'GtkViewport' class Frame(LContainer): # a frame conta gtkClass = 'GtkFrame' class_properties = { 'label_xalign' : 0, 'border_width' : 6, 'label_yalign' : 0.5, 'shadow_type' : 'GTK_SHADOW_ETCHED_IN', } class FrameWithLabel(LContainer): # a frame conta gtkClass = 'GtkFrame' class_properties = { 'label_xalign' : 0, 'label_yalign' : 0.5, 'border_width' : 6, 'shadow_type' : 'GTK_SHADOW_ETCHED_IN', } def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): super(LContainer, self).__init__(el_def, pos, container, elements, layout=layout) ## prepare the names... al_str = "A=%s" % (self.val.replace('F','') ) ## and create the children. We pass the child over to Alignment alignment = Alignment(al_str,(0,0), 'F' , children_list=children_list, elements=elements, layout=layout) # fl_def = "fl=%s" % self.val.replace('F','') # frame_label = FrameLabel(fl_def, (0,0), 'F', elements=elements, layout=layout) # self.lw['children_list'] = [ alignment, frame_label ] self.lw['children_list'] = [ alignment ] ## correct the parent_container of the child alignment.lw['container_flag'] = 'A' class Alignment(LContainer): gtkClass = 'GtkAlignment' class_properties = { 'xalign' : 0.5, 'yalign' : 0, 'xscale' : 1, 'yscale' : 1, 'top_padding' : 4, 'bottom_padding' : 4, 'left_padding' : 8, 'right_padding' : 4, } class EventBox(LContainer): gtkClass = 'GtkEventBox' class_properties = { 'visible_window' : True, 'above_child' : False, } def _class_specific(self): ## not executed frm EventLabel pass class EventLabel(LContainer): gtkClass = 'GtkEventBox' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): #dbg.write("EventLabel ", el_def) el_def = el_def.replace('L=','E=') super(LContainer, self).__init__(el_def, pos, container, elements=elements, layout=layout) ## prepare the names... lbl_str = "l=%s:.%s" % (el_def.replace('E=',''), self.w or '') ## and create the children #print "EventLabel elements: ", elements.keys() label = Label(lbl_str,(0,0), 'E', elements=elements, layout=layout) self.lw['children_list'] = [ label ] def _class_specific(self): self.pack_properties.update({ 'x_options' : 'fill', }) class ComboLE(LContainer): # it'a HBox/VBox with a label and an entry gtkClass = 'GtkVBox' def __init__(self, el_def, pos, container, children_list=[], elements=None, layout=None): super(ComboLE, self).__init__(el_def, pos, container, elements=elements, layout=layout) ## prepare the names... lbl_str = "E=%s:%s%s" % (self.val, self.w or '', self.align or '') entry_str = "e=%s:%s%s" % (self.var, self.h or '', self.align or '') #dbg.debug(entry_str, lbl_str, self.w, self.h) ## and create the children label = EventLabel(lbl_str,(0,0), 'V', elements=elements, layout=layout) entry = Entry(entry_str,(0,1), 'V', elements=elements, layout=layout) self.lw['children_list'] = [ label, entry ] ### w and h are there only to propagate to label & entry self.w = None self.h = None try: del self.properties['height_request'] except: pass def _class_specific(self): self.pack_properties.update({"y_options": ""}) class ScrolledTreeView(LContainer): gtkClass = 'GtkScrolledWindow' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): el_def = el_def.replace('TVS=','S=') super(LContainer, self).__init__(el_def, pos, container, elements, layout=layout) ## prepare the names... tree = "TV=%s:%s.%s" % (self.val, self.h or '', self.w or '') ## and create the children new = TreeView(tree,(0,0), 'S', elements, layout=layout) self.lw['children_list'] = [ new ] def _class_specific(self): self.pack_properties.update({ 'expand' : True, 'fill' : True, }) class ScrolledLayout(ScrolledWindow): gtkClass = 'GtkScrolledWindow' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): el_def = el_def.replace('LS=', 'S=') super(LContainer, self).__init__(el_def, pos, container=container, elements=elements, layout=layout) ## prepare the names... tree = "Lay=%s:%s.%s" % (self.val, self.h or '', self.w or '') ## and create the children new = Layout(tree,(0,0), 'S', elements=elements, layout=layout) self.lw['children_list'] = [ new ] def _class_specific(self): self.pack_properties.update({ 'expand' : True, 'fill' : True, }) class ScrolledTextView(ScrolledWindow): gtkClass = 'GtkScrolledWindow' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): el_def = el_def.replace('TXS=','S=') #super(LContainer, self).__init__(el_def, pos, container, elements) ScrolledWindow.__init__(self, el_def, pos, container=container, elements=elements, layout=layout) ## prepare the names... tree = "TX=%s:%s.%s" % (el_def.replace('S=',''), self.h or '', self.w or '') ## and create the children new = TextView(tree,(0,0), 'S', elements=elements, layout=layout) self.lw['children_list'] = [ new ] class ScrolledDrawingArea(ScrolledWindow): gtkClass = 'GtkScrolledWindow' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): el_def = el_def.replace('daS=','S=') #super(LContainer, self).__init__(el_def, pos, container, elements) ScrolledWindow.__init__(self, el_def, pos, container=container, elements=elements, layout=layout) ## prepare the names... wdg = "da=%s:%s.%s" % (self.val, self.h or '', self.w or '') #dbg.write('wdg', wdg) ## and create the children new = DrawingArea(wdg,(0,0), 'S', elements=elements, layout=layout) self.lw['children_list'] = [ new ] class MenuBar(LContainer): gtkClass = 'GtkMenuBar' class_pack_properties = { 'y_options' : 'fill', 'x_options' : 'fill', } def _class_specific(self): if self.container_flag == "T": #self.pack_properties.update({'y_options': ''}) self.pack_properties.update( {'y_options': 'fill', 'x_options' : 'fill', } ) class Menu(LContainer): gtkClass = 'GtkMenu' # I don't mean to fill it up with MenuItems. I'll class ToolItem(LContainer): gtkClass = 'GtkToolItem' class MenuItem(LContainer): gtkClass = 'GtkMenuItem' parent = 'm' def __init__(self, el_def, pos, container, children_list=[], elements={}, layout=None): super(MenuItem, self).__init__(el_def, pos, container, [], elements, layout=layout) #print "MENU: " + el_def ## prepare the names... menu = "M=%s" % (self.val) ## and create the children new = Menu(menu,(0,0), self.parent, children_list=[], elements=elements, layout=layout) #print "new menu ", new, menu, self.parent self.lw['children_list'] = [ new ] def _class_specific(self): self.properties['label'] = _(self.val) #self.properties['use_underline'] = True # specify that we want write label before use_underline #self.ordered_properties = ["label", "use_underline"] def _children_xml(self): txt = '' try: for obj in self.lw['children_list']: ## incorrect: it would fail f the only sun of a tab is a Label!!! child_type = 'type="submenu"' if obj.gtkClass == 'GtkMenu' else '' child = self._offset_txt(obj.xml(self.gtkClass), 4) txt += "\n\n%s\n \n" % ( child_type, child, obj.lw['el_key'], obj.lw['container']) except: pass return txt # for f in self.lw['children_list']: # dbg.debug("%s %s" % (self, self.lw['children_list'])) class MenuToolButton(MenuItem): gtkClass = 'GtkMenuToolButton' parent = 'tm' # transpose = {'var' : 'label', # } # # I don't mean to fill it up with MenuItems. I'll class Toolbar(LContainer): gtkClass = 'GtkToolbar' # fixme; transpose does nos seem to work... transpose = {'width' : 'width_request', 'height' : 'height_request' } def _class_specific(self): if self.container_flag == "T": self.pack_properties.update({'y_options': ''}) class ColorSelection(LWidget): gtkClass = 'GtkColorSelection' class ColorButton(LWidget): gtkClass = 'GtkColorButton' ##### Widgets list _W = { 'l' : Label, 'lf' : LabelFill, 'ro' : LabelFill, 'L' : EventLabel, 'nl' : NotebookLabel, 'fl' : FrameLabel, 'e' : Entry, 'ae' : AlignmentEntry, 'aC' : AlignmentComboBox, 'd' : DateEdit, 'dt' : DateTimeEdit, 'da' : DrawingArea, 'i' : Image, 'img' : ImageWidget, 'b' : Button, 'lb' : LinkButton, 'r' : RadioButton, 't' : ToggleButton, 'ti' : ToolItem, 'tb' : ToolButton, 'c' : CheckButton, 's' : SpinButton, 'ts' : ToolSpinButton, 'prg' : ProgressBar, 'C' : ComboBox, 'Ce' : ComboBoxEntry, 'TV' : GreedyTreeView, 'TX' : TextView, 'daS' : ScrolledDrawingArea, 'TVS' : ScrolledTreeView, 'TXS' : ScrolledTextView, 'cal' : Calendar, 'sb' : Statusbar, 'Col' : ColorSelection, 'ColB': ColorButton, # containers 'T': Table, 'W': Window, 'E': EventBox, 'X': Expander, 'V': VBox, 'H': HBox, 'le': ComboLE, 'S' : ScrolledWindow, 'N' : Notebook, 'h' : HPaned, 'v' : VPaned, 'p' : ViewPort, 'f' : Frame, 'Fix' : Fixed, 'F' : FrameWithLabel, 'A' : Alignment, 'Lay' : Layout, 'LS' : ScrolledLayout, 'B': MenuBar, 'M': Menu, 'm': MenuItem, 'tm': MenuToolButton, 'O': Toolbar, 'Z': HandleBox, 'container_regexp' : '[EXTHVNPSvhpOBmMFfAZ]', } def custom_widget(glade, function_name, widget_name, str1=None, str2=None, int1=None , int2=None): cw = MyCustomWidget() return getattr(cw, function_name)(str1, str2) def register(Class, mod, custom=None, force=False): """ register a class to be used within widgets 'mod' is the string used in layout """ global _W exec "global " + Class.__name__ exec "%s = Class" % (Class.__name__) if mod in _W and not force: msg = "Alias %s is already present. Use force=True to force it" % mod raise IdentifiedAlreadyPresent(msg) _W[mod] = Class if custom: MyCustomWidget = custom def register_alias(Class, alias, force=False): """ register an alias for Class """ global _W if alias in _W and not force: msg = "Alias %s is already present. Use force=True to force it" % alias raise IdentifierAlreadyPresent(msg) _W[alias] = Class sqlkit-0.9.5/sqlkit/layout/image_widget.py0000644000175000017500000003347211714210425020223 0ustar sandrosandro""" .. _image_widget: Image Viewer ============ .. autoclass:: ImageWidget :members: Properties ---------- imageViewer has the following properties: :image: the image path currently rendered :scale: possible values AUTOREDUCTION, SCALE. If True the image should autoscale. If value is AUTOREDUCTION the image is never enlarged over its natural size :width: the with of the gtk.Image widget :height: the height og the gtk.Image widget :scale_factor: the current scale factor between render image and thumbnail Signals ------- ImageViewer has the following signals: :image-selected: The image has been selected for upload. Callback: .. function:: on_image_selected(widget, filename, new_filename): :param widget: the widget that issued the signal :param fielname: the filename that has been selected :param new_filename: a preferred filename as suggested by the user if any :image-displayed: The image has been displayed .. function:: on_image_displayed(widget, pixbuf_full, pixbuf): :param widget: the widget that issued the signal :param pixbuf_full: the pixbuf rendered from the file :param pixbuf: the scaled pixbuf :image-deleted: the image has been deleted .. function:: on_image_deleted(widget, filename): :param widget: the widget that issued the signal :param fielname: the filename that has been deleted """ import re import os import gobject import gtk import weakref from sqlkit import _ IMAGE_MENU = ''' ''' IMAGE_FORMATS = ["." + x['name'] for x in gtk.gdk.pixbuf_get_formats()] + ['.jpg'] class ImageWidget(gtk.VBox): """ Image Widget suitable for basic image viewing. Inherits from VBox """ __gtype_name__ = 'ImageWidget' __gproperties__ = { 'image-path' : (gobject.TYPE_PYOBJECT, # type 'Image', # nick name 'The Image currently rendered', # description gobject.PARAM_READWRITE), # flags 'scale' : (gobject.TYPE_STRING, # type 'Autoscale', # nick name 'True if the image should autoscale', # description 'AUTOREDUCTION', # default value gobject.PARAM_READWRITE), # flags 'width' : (gobject.TYPE_INT, # type 'width', # nick name 'The width of the image area', # description 1, 10000, 250, gobject.PARAM_READWRITE), # flags 'height' : (gobject.TYPE_INT, # type 'height', # nick name 'The height of the image area', # description 1, 10000, 250, gobject.PARAM_READWRITE), # flags 'scale-factor' : (gobject.TYPE_FLOAT, # type 'scale-factor', # nick name 'The scale factor for the pixbuf ', # description 0, 1000, 1, gobject.PARAM_READABLE), # flags 'show-name' : (gobject.TYPE_BOOLEAN, # type 'show-name', # nick name 'The scale factor for the pixbuf ', # description False, gobject.PARAM_READWRITE), # flags 'strip-dir' : (gobject.TYPE_STRING, # type 'strip-dir', # nick name 'A dir name to strip from image name when writing in status bar ', '', gobject.PARAM_READWRITE), # flags } __gsignals__ = { 'image-selected' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_STRING), ), 'image-displayed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gobject.TYPE_PYOBJECT , gobject.TYPE_PYOBJECT ), ), 'image-deleted' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING,)) } def __init__(self): gtk.VBox.__init__(self) event = gtk.EventBox() self.status_bar = gtk.Statusbar() self._image = gtk.Image() self._image.set_tooltip_text(_("Right click on the image area\n to upload an image")) self.add(event) event.add(self._image) self.props.scale = 'AUTOREDUCTION' self._image.set_size_request(self.props.width, self.props.height) event.connect('button-press-event', self.button_press_event_cb, self._image) self.prepare_actions() self.show_all() self.add(self.status_bar) def set_image(self, image_path): """ set rendered image :param image_path: the path of the image """ self.sb(image_path) if not image_path: pixbuf = None pixbuf_full = None else: ext = os.path.splitext(image_path)[1] if not ext.lower() in IMAGE_FORMATS: self._image.set_property('stock', gtk.STOCK_FILE) self.set_data('scale-factor', 1) self.set_data('image-path', image_path) self.emit('image-displayed', image_path, None, None) return else: if not os.path.exists(image_path): self._image.set_property('stock', gtk.STOCK_MISSING_IMAGE) self.set_data('image-path', image_path) return pixbuf_full = gtk.gdk.pixbuf_new_from_file(image_path) if self.props.scale in ('AUTOREDUCTION', 'AUTOSCALE'): pixbuf, scale = self.scale_pixbuf(pixbuf_full, w=self.props.width, h=self.props.height) else: self.set_data('scale-factor', 1) self.set_data('image-path', image_path) self._image.set_from_pixbuf(pixbuf) self.emit('image-displayed', image_path, pixbuf_full, pixbuf) def scale_pixbuf(self, pixbuf, w=None, h=None): """ scale pixbuf image with the same ratio so that it fits into self.w/self.h :param pixbuf: a gtk.gdk.Pixbuf object :param w: the desired width :param h: the desired height """ pw, ph = pixbuf.get_width(), pixbuf.get_height() w, h = w or self.get_property('width'), h or self.get_property('height') ratio = min(w/float(pw), h/float(ph)) if ratio >= 1 and self.props.scale == 'AUTOREDUCTION': self.set_data('scale-factor', 1) return pixbuf, 1 self.set_data('scale-factor', ratio) return pixbuf.scale_simple(int(pw*ratio), int(ph*ratio), gtk.gdk.INTERP_BILINEAR), ratio def set_pixbuf(self, pixbuf): """ Set the image via the pixbuf :param pixbuf: the gtk.gdk.Pixbuf """ self._image.set_from_pixbuf(pixbuf) def set_stock(self, stock_id, size=gtk.ICON_SIZE_DIALOG): """ Set the image via the stock-id :param stock-id: a stock-id :param size: the desired image (default: ICON_SIZE_DIALOG) """ self._image.set_from_stock(stock_id, size) def do_set_property(self, property, value): if property.name == 'width': self.set_data('width', value) self._image.set_size_request(value, self.props.height) elif property.name == 'height': self.set_data('height', value) self._image.set_size_request(self.props.width, value) elif property.name == 'scale': self.set_data('scale', value) elif property.name == 'image-path': self.set_image(value) self.set_data('image-path', value) elif property.name == 'show-name': self.status_bar.pros.visibile = value elif property.name == 'strip-dir': self.status_bar.set_data('strip-dir', value) else: raise AttributeError, 'unknown property %s' % property.name def do_get_property(self, property): if property.name == 'width': return self.get_data('width') or 250 elif property.name == 'height': return self.get_data('height') or 250 elif property.name == 'scale': return self.get_data('scale') elif property.name == 'image-path': return self.get_data('image-path') elif property.name == 'scale-factor': return self.get_data('scale-factor') elif property.name == 'show-name': return self.status_bar.props.visible elif property.name == 'strip-dir': return self.status_bar.get_data('strip-dir') or '' else: raise AttributeError, 'unknown property %s' % property.name def button_press_event_cb(self, eventbox, event, image): if event.button == 3: self.popup_menu(event) def prepare_actions(self): self.ui_manager = gtk.UIManager() self.actiongroup = gtk.ActionGroup('Image') self.actiongroup.add_actions([ ('UploadImage', None, _('Upload image'), None, _('Upload or modify an image'), self.upload_image), ('DeleteImage', None, _('Delete image'), None, _('Delete the image'), self.delete_image), ('DetachImage', None, _('Image viewer'), None, _('Open separate image viewer'), self.show_image), ('SaveImage', None, _('Save image as'), None, _('Save image as'), self.save_image), ('Zoom-in', gtk.STOCK_ZOOM_IN, None, None, None, self.zoom_in), ('Zoom-out', gtk.STOCK_ZOOM_OUT, None, None, None, self.zoom_out), ('ShowName', None, _("Show/Hide image name"), None, None, self.toggle_status_bar), ],) self.ui_manager.insert_action_group(self.actiongroup, 10) self.ui_manager.add_ui_from_string(IMAGE_MENU) def show_image(self, action=None): """ Open another window that will show the same image with at a different zooming. Setup callback to keep the new window updated by the first one. """ w = gtk.Window() image2 = ImageWidget() w.add(image2) image2.props.width = 700 image2.props.height = 700 w.show_all() #self.imgref = weakref.ref(image2) #self.winref= weakref.ref(w) def update_image(widget, image_path, pixbuf_full, pixbuf): image2.set_pixbuf(pixbuf_full) image2.sb(image_path) self.connect('image-displayed', update_image) image2.props.strip_dir = self.props.strip_dir image2.set_image(self.props.image_path) def popup_menu(self, event): menu = self.ui_manager.get_widget('/ImagePopup') menu.popup(None, None, None, event.button, event.time) def upload_image(self, action): from sqlkit.widgets.common import dialogs dialog = dialogs.OpenDialog(title=_('upload image'), default_filename='') filename = dialog.filename if not filename: return self.set_image(unicode(filename)) self.emit('image-selected', filename, unicode(dialog.new_filename or u'')) def save_image(self, action): import shutil from sqlkit.widgets.common import dialogs orig_filename = self.props.image_path dialog = dialogs.SaveDialog(default_filename=os.path.basename(orig_filename)) if dialog.filename: shutil.copyfile(orig_filename, dialog.filename) def delete_image(self, action): self.emit('image-deleted', self.props.image_path) self.set_image(None) def zoom_in(self, action): w, h = self.props.width, self.props.height self.props.width = int(w * 1.2) self.props.height = int(h * 1.2) self.set_image(self.props.image_path) def zoom_out(self, action): w, h = self.props.width, self.props.height self.props.width = int(w / 1.2) self.props.height = int(h / 1.2) self.set_image(self.props.image_path) def toggle_status_bar(self, action): self.status_bar.props.visible = not self.status_bar.props.visible def sb(self,txt): """ Push info on Status Bar """ if not isinstance(self.status_bar.get_toplevel(), gtk.Window): return if not txt: txt = '' txt = re.sub("^%s" % self.props.strip_dir, '', txt) idx = self.status_bar.get_context_id(txt) msg_id = self.status_bar.push(idx, txt) # finally register our new Type if gobject.pygtk_version < (2, 8): gobject.type_register(ImageWidget) if __name__ == '__main__': w = gtk.Window() i = ImageWidget() w.add(i) w.show_all() # i.set_image('cal15.png') i.set_image('/tmp/la-ragazza-sul-ponte2.jpg') def dbg(widget, filename): print filename i.connect('image-selected', dbg) gtk.main() sqlkit-0.9.5/sqlkit/layout/keys.png0000644000175000017500000000215011714210425016672 0ustar sandrosandroPNG  IHDRw=bKGDC pHYsHHFk>IDATHœIle>, h)j F[ĄDOތbƥ)b!p05 20)2_ϋ i57'}6ӳ~~d2=.j{mJaQdFu$M"%ZKK]x Q[eIqЧ\̲F,$_󺻻g-x9?}YXnr4 vqu]"(b6M("&رgm>V 8r<~6wqgNNgd3\:H<֭ |pQhjjV@-KCBoo^I&*ԔyT~@]2QsDA/:d2)5kYUQ @Ƕ% s2e۝\xmbQqZ@ au6 6y\E 0LIoYtzfd.wiFAM,KCҲWvjnڪ(IsufffD x.qSJ/<-X,=1ys[r܎+ xs*#.`ֿ?*uҁ_IENDB`sqlkit-0.9.5/sqlkit/layout/table-load.png0000644000175000017500000000203711714210425017727 0ustar sandrosandroPNG  IHDR szzsRGBbKGD pHYs  tIME 59U*;IDATX]H[g|#(i3:\aa kc+n6v12j/FYa"7aM?;+kkX`uju)g~Dijy?vRI\}974qWUޱEJ_e<5'T`[[ٹM/ᑱ5:{-[˷R_ )ub"+ ou%x|v!{u-G.P%疦 ͥ*.'{oc#_.Y\x$jmu۪dl`/1蜮JqrIk]7X<厃"k4kj*$qg`TW!W Kn*wZb:G[$$$059_6ZdYznK"hI9Jh}HE2:c[s# (mFeCot-q, (Nل$a0I"2x9i>6U2G`MoT!KqdÓl"= pv(req): return elif pv(sqlalchemy.__version__) < pv('0.5.4'): return fatal_exit() else: warning() except ImportError, e: ## don't quit just becouse they don't have pkg_resources return True def str2list(list_or_string): """ input parsing common func """ if isinstance(list_or_string, basestring): return re.split('[ ,]*', list_or_string.strip()) return list_or_string or [] def get_viewer(ext): """ return the path of the viewer for extension :param ext: extension """ import platform import subprocess as sp progs = { 'pdf' : ('xdg-open', 'gnome-open', 'acroread', 'xpdf', 'gv', ), 'odt' : ('xdg-open', 'gnome-open', 'oowriter', ), } # another workig way to invoke the correct application is to use # webbrowser.open_new() but under linux it may switch you to another desktop. if platform.system() == 'Windows' or os.name == "mac": # this is good for local file but for remote files *in PrintTool* only, it shows # an amaizing delay of 15 seconds that you don't have via webbrowser module # I really don't understand why, a simple test in a file doesn't show this delay! return 'start' else: for cmd in progs[ext]: path = sp.Popen('which ' + cmd, shell=True, stdout=sp.PIPE).communicate()[0] if path: return path.strip() def open_file(filename, viewer=None): """ Open file filename with the default application (ext: .pdf or openoffice family) :param filename: the name of the file to be opened """ if os.name in ('posix', 'mac'): if filename.endswith('pdf'): p = subprocess.call((viewer or get_viewer('pdf'), filename)) else: p = subprocess.call((viewer or get_viewer('odt'), filename)) else: # I made some test with call(('start', filename)) but in several # occasion I coudn't get the start command to find the # file. os.startfile() doesn't suffer the same problem. os.startfile(filename) if __name__ == '__main__' : c = Container() c.add_element('uno', 1) print c.keys() print 'uno' in c c['due'] = 2 c['tre'] = 3 c['quattro'] = 4 print c.due for x in c: if x>2: break print x for x in c: print x print str2list('uno,due, tre') print str2list(['uno','due', 'tre']) sqlkit-0.9.5/sqlkit/misc/__init__.py0000644000175000017500000000000011714210425016730 0ustar sandrosandrosqlkit-0.9.5/sqlkit/misc/babel_support.py0000644000175000017500000000110611714210425020042 0ustar sandrosandro""" Babel support. If babel is not present print an explanation. If default locale is 'C', set locale to 'en' """ try: import babel except ImportError, e: print "\nSqlkit now depends on babel to provide localization. Quitting\n" raise e if babel.default_locale('LC_NUMERIC') in ('c', 'C', '', None): import os os.environ['LANG'] = 'en_US' os.environ['LC_NUMERIC'] = 'en_US' reload(babel) if babel.default_locale('LC_TIME') in ('c', 'C', '', None): import os os.environ['LC_TIME'] = 'en_US' reload(babel) from babel import numbers, dates sqlkit-0.9.5/sqlkit/misc/optionparse.py0000644000175000017500000000670411714210425017555 0ustar sandrosandro# coding: utf-8 """\ :Author: M. Simionato :Date: April 2004 :Title: A much simplified interface to optparse. added description handling Sandro Dentella by sandro@e-den.it 2008 You should use optionparse in your scripts as follows. First, write a module level docstring containing something like this (this is just an example): '''usage: %prog files [options] -d, --delete: delete all files -e, --erase = ERASE: erase the given file''' Then write a main program of this kind: # sketch of a script to delete files if __name__=='__main__': import optionparse option,args=optionparse.parse(__doc__) if not args and not option: optionparse.exit() elif option.delete: print "Delete all files" elif option.erase: print "Delete the given file" Notice that ``optionparse`` parses the docstring by looking at the characters ",", ":", "=", "\\n", so be careful in using them. If the docstring is not correctly formatted you will get a SyntaxError or worse, the script will not work as expected. """ import optparse, re, sys USAGE = re.compile(r'\s*(?P.*)\s*\s*usage: (?P.*?)(\n[ \t]*\n|$)', re.DOTALL|re.UNICODE|re.LOCALE) def nonzero(self): # will become the nonzero method of optparse.Values "True if options were given" for v in self.__dict__.itervalues(): if v is not None: return True return False optparse.Values.__nonzero__ = nonzero # dynamically fix optparse.Values class ParsingError(Exception): pass optionstring="" def exit(msg=""): raise SystemExit(msg or optionstring.replace("%prog",sys.argv[0])) def parse(docstring, arglist=None): global optionstring optionstring = docstring match = USAGE.search(optionstring) if not match: raise ParsingError("Cannot find the option string") # optlines = match.group('usage').splitlines() optlines = re.split('\n\s*(?=[-,])', match.group('usage')) try: descr = match.group('descr') p = optparse.OptionParser(optlines[0], description=descr, formatter=IndentedHelpFormatterWithNL() ) for line in optlines[1:]: opt, help=line.split(':')[:2] short,long=opt.split(',')[:2] if '=' in opt: action='store' long=long.split('=')[0] else: action='store_true' p.add_option(str(short.strip()),str(long.strip()), action = action, help = re.sub('\s+', ' ', help.strip())) except (IndexError,ValueError): raise ParsingError("Cannot parse the option string correctly") return p.parse_args(arglist) class IndentedHelpFormatterWithNL(optparse.IndentedHelpFormatter): """ This is part of a class that I read in message: # originally from Tim Chase via comp.lang.python. http://groups.google.com/group/comp.lang.python/browse_frm/thread/e72deee779d9989b/ """ def format_description(self, description): import textwrap if not description: return "" desc_width = self.width - self.current_indent indent = " "*self.current_indent bits = description.split('\n') formatted_bits = [textwrap.fill(bit, desc_width, initial_indent=indent, subsequent_indent=indent) for bit in bits] result = "\n".join(formatted_bits) + "\n" return result sqlkit-0.9.5/sqlkit/misc/localtimezone.py0000644000175000017500000000160511714210425020052 0ustar sandrosandroimport time as _time import datetime ZERO = datetime.timedelta(0) STDOFFSET = datetime.timedelta(seconds = -_time.timezone) if _time.daylight: DSTOFFSET = datetime.timedelta(seconds = -_time.altzone) else: DSTOFFSET = STDOFFSET DSTDIFF = DSTOFFSET - STDOFFSET class LocalTimezone(datetime.tzinfo): def utcoffset(self, dt): if self._isdst(dt): return DSTOFFSET else: return STDOFFSET def dst(self, dt): if self._isdst(dt): return DSTDIFF else: return ZERO def tzname(self, dt): return _time.tzname[self._isdst(dt)] def _isdst(self, dt): tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, -1) stamp = _time.mktime(tt) tt = _time.localtime(stamp) return tt.tm_isdst > 0 Local = LocalTimezone() sqlkit-0.9.5/sqlkit/misc/datetools.py0000644000175000017500000001663311714210425017212 0ustar sandrosandro""" Simple relative date algebra ============================= a function that implements simple relative date algebra so that we can use it in bookmarks and queries. Differently from what other packages do (as the very usefull relativedelta that is used in this module) datetools tries to use the term month as a period of length that depends on the 'current' month. End of february + 1month will be end of march, not 28th of march! Allowed chars are:: [-+ diwWmMyY @>] letters refer to a moment of time (start/end of month, year, week) or period according to use: the forst token is a moment of time, the further ones are periods. :d: today :i: yesterday ('ieri' in italian, 'y' was already used by year) :w: beginning of week :W: end of week :m: beginning of month :M: end of month :y: beginning of year :Y: end of year Math signs ``+`` and ``-`` work as you would expect they add and subtract period of time. When used this way the following letter refers to a period:: m+14d is the 15th day of the month (beginning of month + 14 days) .. versionadded:: 0.8.6.1 If the first token is the end of the month, we try to stick to the end as much as possible, till another period is used, so that order is significant, note what follows that is extracted from the doctests, assuming the current day is June 2th:: >>> dt.string2date('M-1m-2d') datetime.date(2007, 5, 29) >>> dt.string2date('M-2d-1m') datetime.date(2007, 5, 28) You can also use a short form (compatible with previous syntax):: m-1 == m-1m You can use also > in which case the string is considered 2 dates, each built as already stated:: m-1 > M+2 means a 3 months period, starting from beginnig of last month to end of next month Periods ------------------- @ is a compact way to set a period:: @m == m > M @m-1 == m-1 > M-1 .. versionadded:: 0.8.6.1 @ accepts also a quantity:: @2m-1 = m-1 > M-1 +1m that means a period of 2 months starting from beginning of last month. Other examples -------------- :m-1: beginnning of last month :M+1: end of next month """ import re from dateutil.relativedelta import relativedelta from datetime import date, timedelta from babel import dates from sqlkit import _ class WrongRelativeDateFormat(Exception): pass DATE_PATTERN = re.compile(""" (?P@)? (?P[+-][0-9]*)? (?P[imMdDyYwW])? (?P[+-][0-9]*)? $""", re.VERBOSE) CHARS = re.compile('[-+dimMwWyY>]') TEST = False TODAY = date.today() def today(): ## in tests wee need to force computation on a different day if TEST == True: return TODAY else: return date.today() def string2dates(string): """ return a tuple of date values (start, end) that translates 'string' end may be None if 'string' does not imply a period. Firstly a babel.dated.parse_date is attempted, if it fails math as described in the doctring of the module is applied Since a string definition can split into two dates, a tuple is return anyhow, a tipical way to call it is:: start, end = string2dates('whatever') if you are sure one date is in the definition, you can use string2date instead """ try: # first try a normal parsing via babel return dates.parse_date(string, locale=dates.default_locale()), None except: pass # m = re.search(CHARS, string) # if not m: # return string, None if '@' in string: m = re.match('@(\d?)(([ymdw]).*)', string) n, base, period = m.groups() string = "%s > %s" % (base.lower(), base.upper()) if n: string += " +%s%s" % (int(n)-1, period) data = string.strip().split('>') if len(data) == 1: return (string2date(data[0]), None) else: if not data[1]: raise WrongRelativeDateFormat(_('Incomplete date format')) return ( string2date(data[0]), string2date(data[1] or None)) def string2date(string): """ return a date represented by a string applying math syntax as described in the docstring """ try: # first try a normal parsing via babel return dates.parse_date(string, locale=dates.default_locale()) except: pass ## add spaces to math simbols, not in starting position, only if not ## preceded by spaces PAT_ADD_SPACES = '(?<=[^ ])([-+])' data = re.split(' +', re.sub(PAT_ADD_SPACES, r' \1', string).strip()) ## end_month = 'M' in data[0] try: computed, last_period = transform(data[0]) for token in data[1:]: computed, last_period = transform(token, start=computed, last_period=last_period, further=True) ## I want to stick to end of month as much as possible: if we're in june, 2nd # M-1m must return end of may: 31 NOT 30!!! if end_month: if last_period == 'm': computed = get_end_of_month(computed) elif last_period in 'dw': end_month = False except Exception, e: raise WrongRelativeDateFormat(str(e)) return computed def transform(token, start=None, last_period=None, further=False): """transform token into a date Token is the very simple information: m +1m 2d if last_period is not none, the period can be missing and 'last_period' is used. This allows to write: m-1 meaning 'beginning of last month' """ if not start: start = today() m = re.match(DATE_PATTERN, token) if not m: raise WrongRelativeDateFormat( "Wrong Relative date format: %s" % token) else: period = m.group('period') if not period and last_period: period = last_period last_period = period.lower() if m.group('math'): math = m.group('math') else: math = m.group('math2') if math == '-': math = -1 try: math = int(math) except TypeError: math = None if period == 'd': computed = start if math is not None: computed = computed + relativedelta(days=+math) if period == 'i': computed = start - timedelta(days=1) if period in ('w', 'W'): if not further: computed = start - timedelta(days=start.isoweekday()-1) else: computed = start if math is not None: computed = computed + relativedelta(weeks=+math) if period == 'W': computed = computed + relativedelta(weeks=+1, days=-1) if period in ('m', 'M'): if not further: computed = start.replace(day=1) else: computed = start if math is not None: computed = computed + relativedelta(months=+math) if period == 'M': computed = computed + relativedelta(months=+1, days=-1) if period in ('y', 'Y'): j = int(start.strftime('%j')) if not further: computed = start - timedelta(days=j-1) else: computed = start if math is not None: computed = computed + relativedelta(years=+math) if period == 'Y': computed = computed + relativedelta(years=+1, days=-1) return (computed, last_period) def get_end_of_month(date): date = date.replace(day=1) return date + relativedelta(months=+1, days=-1) �����������������������������������������������������������������������������������������������������sqlkit-0.9.5/sqlkit/misc/table_browser.py�����������������������������������������������������������0000644�0001750�0001750�00000043221�11714210425�020037� 0����������������������������������������������������������������������������������������������������ustar �sandro��������������������������sandro�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������# Copyright (C) 2008-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import re import gtk import sqlalchemy as sa import sqlkit from sqlkit import debug as dbg, _, fields from sqlkit.widgets import SqlTable, SqlMask from sqlkit import layout from sqlkit.db import utils GENERAL_UI = ''' ''' class TableBrowser(object): NAME_COLUMN = 0 COUNT_COLUMN = 1 TYPE_COLUMN = 2 DEFAULT_COLUMN = 3 PKEY_COLUMN = 4 FKEY_COLUMN = 5 NULL_COLUMN = 6 INDEX_COLUMN = 7 COUNT_VISIBILITY_COLUMN = 8 def __init__(self, dbproxy, title=None, opts=None, x=None): lay = """ {V.menu } {- b=filter_tables e=filter} prg=count TVS=tree """ self.tables = {} self.x = x if opts: self.opts = opts else: class Opts: pass opts = Opts() opts.load = True opts.dev = False opts.all_tables = None self.opts = opts self.dbproxy = dbproxy self.l = layout.Layout(lay, title=title, opts='-') self.l.pack( ## fill is just to avoid default that is expand ('T.0', 'y-options', 'fill'), ('b=filter_tables', 'x-options', 'fill'), ) self.model = gtk.TreeStore(*[str] + [int] + [str] * 6 + [bool] ) self.w = self.l.show() self.w['prg=count'].hide() self.tv = self.w['TV=tree'] cols = ['Table', 'N.rows', 'Type', 'Default', 'Prim. Key', 'Foreign Key', 'Nullable', 'Indexes'] # cols = [_('Table'), _('N. rows'), _('Default'), _('Prim. Key'), _('Foreign Key'), # _('Nullable'), _('Indexes')] self.stv = layout.misc.EasyTreeView(cols, model=self.model, tv=self.tv) count_column = self.stv.tvc['N.rows'] count_cell = count_column.get_cell_renderers()[0] count_cell.set_property('xalign', 1.0) count_column.add_attribute(count_cell, 'visible', self.COUNT_VISIBILITY_COLUMN) self.tv.connect('button-press-event', self.popup_col_menu) self.l.connect( ('b=filter_tables', 'clicked', self.fill_model), ('e=filter', 'activate', self.fill_model), ('Window', 'delete-event', self.quit_cb), ) self.fill_model() self.lay_obj = self.l self.widgets = self.w self.set_actions() self.prepare_uimanager() self.add_accelerators() self.ui_manager.get_action('/Main/Modify/Blank').set_active(not fields.BLANK_OK) from sqlkit.widgets.table.utils import set_height_request set_height_request(self.tv, rows=min(30, len(self.model))) self.ui_manager.get_action('/Main/Modify/Reload').set_active(bool(opts.load)) def set_actions(self): self.actiongroup = gtk.ActionGroup('Main') self.actiongroup.add_actions([ ('Database', None, _('Database')), ('Modify', None, _('Modify')), ('Tools', None, _('Tool')), ('Help', None, _('Help')), ('Quit', gtk.STOCK_QUIT, None, 'q', None, self.quit_cb), ('About', gtk.STOCK_ABOUT, None, None, None, self.about_dialog_cb), ('Count', None, _('Count records'), None, _('Count records in all tables'), self.count_records), ]) tooltip = _("""Configure the fields: \nlabels, tooltip, completion, search field""") self.actiongroup.add_actions([ ('EditFields', None, _('Edit Sqlkit Fields'), 'e', tooltip, sqlkit_model), ], self.dbproxy) self.actiongroup.add_toggle_actions([ ('Dev', 'sk-pkeys', _('Primary Key'), None, _('Show/Hide primary key if the key is numeric'), self.toggle_dev_cb), ('Reload', 'sk-table-load', _('Load data'), None, _('Load the data as well'), self.toggle_reload_cb), ('Blank', 'gtk-bold', _('Blank'), None, _('Cast blank into NULL'), self.toggle_blank_cb), ]) def toggle_reload_cb(self, item): pass def toggle_dev_cb(self, item): pass def toggle_blank_cb(self, item): fields.BLANK_OK = not self.ui_manager.get_action('/Main/Modify/Blank').get_active() def prepare_uimanager(self): self.ui_manager = gtk.UIManager() self.accel_group = self.ui_manager.get_accel_group() self.ui_manager.add_ui_from_string(GENERAL_UI) self.w['Window'].add_accel_group(self.accel_group) self.ui_manager.insert_action_group(self.actiongroup, 0) header = self.w['V.menu'] menu = self.ui_manager.get_widget('/Main') tools = self.ui_manager.get_widget('/TbMain') header.add(menu) header.add(tools) def count_records(self, item): """ Count the records in all tables """ progress = self.widgets['prg=count'] progress.show() n_tables = len(self.model) sess = self.dbproxy.get_session() meta = self.dbproxy.metadata i = 0 for row in self.model: if not self.model.get(row.iter, self.COUNT_COLUMN)[0]: table_name = self.model.get(row.iter, 0)[0] i += 1 progress.props.text = table_name progress.props.fraction = (1.0/n_tables * i) while gtk.events_pending(): gtk.main_iteration() reflected_table = meta.tables.get(table_name, None) if reflected_table is None: reflected_table = sa.Table(table_name, meta, autoload=True) count = sess.query(reflected_table).count() self.model.set(row.iter, self.COUNT_COLUMN, count, self.COUNT_VISIBILITY_COLUMN, True ) progress.props.fraction = 1.0 progress.hide() def add_accelerators(self): """Add accelerators: now Ctrl-l""" # key accelerators self.accel_group.connect_group(ord('l'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_LOCKED, self.focus_filter) self.accel_group.connect_group(ord('f'), gtk.gdk.CONTROL_MASK, gtk.ACCEL_LOCKED, self.focus_filter) def focus_filter(self, *args): self.widgets['e=filter'].grab_focus() def quit_cb(self, widget, event=None): if self.x == 'quit': gtk.main_quit() else: self.widgets['Window'].destroy() def reload(self, *args): dbg.write('reloading sqlkit') #print reload(sqlkit) #reload(sqlkit.sqlwidget) #print reload(sqlkit.table.table) #reload(sqlkit.mask.mask) def popup_col_menu(self, wdg, ev ): try: self.w['M=popup'].destroy() except: pass iter = self.model.get_iter( wdg.get_path_at_pos(int(ev.x),int(ev.y))[0]) if self.model.iter_parent(iter): return table = self.model.get_value(iter,0) if self.model.iter_has_child(iter) and \ self.tv.row_expanded(self.model.get_path(iter)): return False #table = self.model.get_value(self.model.get_iter(path), 0) menu = self.w['M=popup'] = gtk.Menu() item = gtk.MenuItem(label=_("Mask") ) item.connect('activate', self.activate_cb, ('mask', table )) menu.append(item) item = gtk.MenuItem(label=_("Table") ) item.connect('activate', self.activate_cb, ('table', table )) menu.append(item) if self.model.iter_children(iter) and \ self.tv.row_expanded(self.model.get_path(iter)): item = gtk.MenuItem(label=_("Collapse row") ) item.connect('activate', self.get_table_info_in_model, table, iter ) menu.append(item) else: item = gtk.MenuItem(label=_("Table reflection") ) item.connect('activate', self.get_table_info_in_model, table, iter ) menu.append(item) menu.show_all() menu.popup(None, None, None, ev.button, ev.time) return False def activate_cb(self, wdg, data): mode, table = data dev = self.ui_manager.get_action('/Main/Modify/Dev').get_active() options ={ 'dev' : dev, 'single' : False, 'nick' : "%s_%s" % (table, mode), 'dbproxy' : self.dbproxy, } if mode == 'mask': SqlWid = SqlMask else: SqlWid = SqlTable try: self.t = SqlWid(table, **options) if self.ui_manager.get_action('/Main/Modify/Reload').get_active(): self.t.reload() except sqlkit.exc.MissingDescriptorField: pass except sqlkit.exc.MissingPrimaryKey, e: dialog, l = self.l.dialog(type='ok', layout="l=msg", text=e) dialog.show_all() response = dialog.run() dialog.destroy() def skip_table(self, table): """ this is just for skipping, must return True""" if self.dbproxy.metadata.bind.name == 'postgres' and table.startswith('pg_'): return True if not re.search(TABLE_MATCH, table): return True def fill_model(self, *args): """ called by button filter_table and filter entry """ global TABLE_MATCH self.model.clear() TABLE_MATCH = self.w['e=filter'].get_text() or ".*" tables = self.dbproxy.metadata.bind.table_names() tables.sort() for table in tables: if self.skip_table(table): continue iter = self.model.append(None) self.model.set(iter, self.NAME_COLUMN, table) if self.opts.all_tables: self.get_table_info_in_model(None, (table, iter)) def get_table_info_in_model(self, junk, table_name, iter): """ fill the model relative to info on table """ if self.model.iter_has_child(iter): if self.tv.row_expanded(self.model.get_path(iter)): self.tv.collapse_row(self.model.get_path(iter)) else: self.tv.expand_row(self.model.get_path(iter), True) return False tbl = sa.Table(table_name, self.dbproxy.metadata, autoload=True) index_dict = {} self.tables[table_name] = tbl pks = [] for pk in tbl.primary_key: pks += [pk.name] ## Indexes for i, index in enumerate(tbl.indexes): for j, col in enumerate(index.columns): # show order of column in index if not col.name in index_dict: index_dict[col.name] = [] index_dict[col.name] += [index.unique and 'UNIQUE_%s.%s' % (i, j) or 'IDX_%s.%s' % (i,j)] ## pkey and count visibility self.model.set(iter, self.PKEY_COLUMN, ",".join(pks), self.COUNT_VISIBILITY_COLUMN, False, ) ## loop on columns for c in tbl.c: iter2 = self.model.append(iter) self.model.set(iter2, self.NAME_COLUMN, c.name ) self.model.set(iter2, self.TYPE_COLUMN, c.type.compile( dialect=tbl.metadata.bind.engine.dialect)) if c.primary_key: self.model.set(iter2, self.PKEY_COLUMN, 'X' ) if c.foreign_keys: self.model.set(iter2, self.FKEY_COLUMN, ",".join([x.target_fullname for x in c.foreign_keys]) ) if not c.nullable: self.model.set(iter2, self.NULL_COLUMN, "NOT NULL") if c.name in index_dict: self.model.set(iter2, self.INDEX_COLUMN, " ".join(index_dict[c.name])) path = self.model.get_path(iter) self.tv.expand_row(path, True) def about_dialog_cb(self, menuItem): """ The about dialog """ dialog = create_about_dialog() dialog.run() dialog.destroy() def sqlkit_model(item=None, dbproxy=None, single=False): lay = """ name search_field format o2m=attributes - """ global tbl if dbproxy: db = dbproxy try: from sqlkit.db.sqlkit_model import get_classes, Base SqlkitTable, SqlkitFields = get_classes(db.metadata.bind) Base.metadata.bind = db.metadata.bind if not db.metadata.bind.has_table('_sqlkit_table'): response = dialog_create_continue() if response == gtk.RESPONSE_CANCEL: sys.exit(0) else: Base.metadata.create_all() tbl = SqlMask(SqlkitTable, dbproxy=db, single=single, hooks=ConfigHook(), layout=lay) tbl.widgets['l=name'].set_tooltip_text(_('The name of the table we are customizing')) format_tip = _("The best representation of a record \nas a combination of fields, " +\ "e.g.: %(title)s %(year)s") tbl.widgets['l=format'].set_tooltip_text(format_tip) search_tip = _('The field that will be searched for when completion is used') tbl.widgets['l=search_field'].set_tooltip_text(search_tip) except Exception, e: print e raise def dialog_create_continue(): dialog = gtk.Dialog("Creating sqlkit tables", None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) label = gtk.Label("No sqlkit tables are present: I'll proceed creating them") dialog.vbox.add(label) dialog.show_all() response = dialog.run() dialog.destroy() return response def create_about_dialog(): """ Create a default AboutDialog """ def func(dialog, link, user_data): import webbrowser webbrowser.open(link) about_dialog = gtk.AboutDialog() gtk.about_dialog_set_url_hook(func, None) about_dialog.set_authors(["Sandro Dentella "]) about_dialog.set_license("GPL v.3") about_dialog.set_version(sqlkit.__version__) about_dialog.set_name("sqlkit") about_dialog.set_website("http://sqlkit.argolinux.org") about_dialog.set_website_label("http://sqlkit.argolinux.org") return about_dialog def open_sqlwidget(table, single, opts, db): options ={'dev' : opts.dev, 'single' : single, 'limit' : opts.limit and int(opts.limit) or 200, 'dbproxy' : db, 'sql' : opts.sql, 'field_list' : opts.field_list, 'order_by' : opts.order_by, } if opts.mask: try: t = SqlMask(table, **options) except sqlkit.exc.MissingDescriptorField: pass else: try: t = SqlTable(table, **options) except sqlkit.exc.MissingDescriptorField: pass if opts.load: t.reload() return t class ConfigHook(object): def on_init(self, m): self.m = m self.db = m.dbproxy m.completions.name.set_values(self.db.metadata.bind.table_names()) m.completions.name.autostart = 1 m.completions.search_field.set_values(self.get_names) m.completions.search_field.autostart = 1 m.related.attributes.completions.name.set_values(self.get_all_names) m.related.attributes.completions.name.autostart = 1 def get_names(self, values = None): table = self.m.get_value('name') if table: cols = utils.get_fields(table, metadata=self.db.metadata, name=False) return [c.name for c in cols if isinstance(c.type, (sa.String, sa.Text))] else: return [] def get_all_names(self, values = None): table = self.m.get_value('name') if table: return utils.get_fields(table, metadata=self.db.metadata, name=True) else: return [] sqlkit-0.9.5/sqlkit/misc/oootemplate.py0000644000175000017500000010775611714210425017553 0ustar sandrosandro# Copyright (C) 2009-2010-2011, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ .. _oootemplate: ======================= OpenOffice.org template ======================= This module makes it possible to use an oasis ``.odt`` document as a template to produce documents via the use of an openoffice server (i.e. an istance of openoffice that listens for connections by clients). It uses the uno module that comes with openoffice. In the following image you can see how the template looks before rendering and how the ``pdf`` produced looks. The red circle stresses the marker that makes the template produce many lines for each input line: one for each object in context. .. image:: ../img/ootemplate-colors.png This rendering can be obtained by code very similar to this :ref:`example` The template ============ An openoffice writer document (``.odt`` extension) can be used as template. No particular templating skills are required by the template designer, just ability to create an openoffice document and a list of variable's names that must be used. Clearly this list must be provided by the programmer to the designer. scenario -------- oootemplate will substitute variables values where needed. :: Dear Mr. $user.name $user.last_name, Today $date we received an order for $n books: +-----------------------------+----------------+----------+----------+ |Title |Author |N. pieces |$currency | +-----------------------------+----------------+----------+----------+ | ++$title |$author |$qty |$price | +-----------------------------+----------------+----------+----------+ This is an example of a template rendered as possible with ascii art. There are 2 different substitutions that can be done: a. simple substitutions ($user, $date, $currency, above - yellow in the image) b. multiline substitutions (the book entries above, starting with ++ - green in the image) the first refers to substitution of a single value that is already present in the document, the second refers to the insertion of several rows according to a list of fields that are probably in a table. The variables defined in the table should be repeted in loop for each book provided in the ``context``, that implies an increment of the number of rows of the table. The expected output resembles what follows:: Dear Mr. Alessandro Dentella, today June, 2 2008, we received an order of 2 books: +-----------------------------+----------------+----------+----------+ |Title |Author |Copies |Euro | +-----------------------------+----------------+----------+----------+ | Q |Luther Blisset |1 |10 | +-----------------------------+----------------+----------+----------+ | Il sistema periodico |Primo Levi |2 |8 | +-----------------------------+----------------+----------+----------+ As for any template system a special syntax is needed to allow people to create loops. We are constrained to what can be done by a simple user of openoffice (no programming at all) so we choose to use a MULTI_LINE_MARKER in the default form of a '++' (red circle in the image) . To make things more complicated it's clear that the person that created the template (the .odt document), may have used a table just for formatting reason so that substitution of type a. can be in an openoffice table or not. implementation --------------- Substitution of type ``a.`` above are done using search and replace functionality of openoffice API, while substitution of type ``b.`` are implemented as a loop on table's rows and cell in 2 different ways: one that preserves the style of each word of the cells and one that doesn't. The former uses getTrasferable() method on each cell and is slower. You can switch from one to the other setting Template's attrribute :attr:`Template.preserve_styles` The pattern used to detect what is a table can be customized. While the default is '$' as in the shell or in python template system (and perl and php...) since you may have the '$' symbol in your document you may want to customize it. See Template's method ``set_pattern``. context ------- The context is the object that contains the mapping variable/value to be used. It needs an argument -a dict- that holds the values. oootemplate allows you to have more that one tables in your document. That means you can implement easily things as:: Dear Mr. $user.name $user.last_name, you can order these books at 20% discount +-----------------------------+----------------+----------+----------+ |Title |Author |N. pieces |$currency | +-----------------------------+----------------+----------+----------+ | ++$title |$author |$qty |$price | +-----------------------------+----------------+----------+----------+ or these books at 50% discount +-----------------------------+-----------------+----------+----------+ |Title |Author |N. pieces |$currency | +-----------------------------+-----------------+----------+----------+ | ++$title |$author |$qty |$price | +-----------------------------+----------+------+----------+----------+ |Title |Author | Extra|N. pieces |$currency | +-----------------------------+----------+------+----------+----------+ | ++$title |$author | $x |$qty |$price | +-----------------------------+----------+------+----------+----------+ In this example we have 3 lines that start with ++, that means that will be conseidered prototipes for new lines. For each of these 3 rows there need to be a list of objects (books in the example) in the context. Since Openoffice-org tables have names, we wil use that name as a key in the context for the value. That's enought for the first table (20% discount) not for the second where we have 2 lists in a table. To cope with this case, we can put as value a dict with entry an integer that indicates the position starting from 1 (see example below) name mapping ~~~~~~~~~~~~ Occasionally the name of the variable will be too long to fit in the space that you want to allocate. You can translate a shorter name to the real name in the dict context.translate (see example in the demo), blue circle in the image. This way you can hide the complexity of some variable name. Note that you can translate both ``$rs.manager.address`` in ``$addr`` or ``$director.last_name`` in ``$d.last_name``. output ------ All output accepted by openoffice filters, you're probably using ``.odt`` or ``.pdf`` .. _example: example ------- A tipical sample code is:: import ooootemplate as oo tmpl = oo.Template('/tmp/mytemplate.odt', server='127.0.0.1', port=2002) context = oo.Context({ 'user' : user, 'date' : date(2008, 6, 2), 'currency' : 'Euro', 'Table1' : (book1, book2, ...) # lazy assignement (simple tuple) 'Table2' : ( (book21, book22, ...), # correct assignement (list of tuples) (book31, book32, ...), ) }) tmpl.render(context) tmpl.save_as('/tmp/new_document.pdf') API ======= Context ------- .. autoclass:: Context :members: __init__ Template -------- .. autoclass:: Template :members: __init__, render, save_as, set_pattern, VARIABLE_PATTERN, VARIABLE_PATTERN_OO, MULTI_LINE_MARKER, document, oo_context, search, preserve_styles Table ------- .. autoclass:: Table :members: __init__ .. autoclass:: TableWithStyles :members: __init__ """ import os import re import sys import datetime import subprocess import warnings import socket import uno from com.sun.star.beans import PropertyValue NoConnectionException = uno.getClass("com.sun.star.connection.NoConnectException") OutOfBoundsException = uno.getClass("com.sun.star.lang.IndexOutOfBoundsException") IllegalArgumentException = uno.getClass("com.sun.star.lang.IllegalArgumentException") class WrongTemplateName(Exception): pass #class OOExecuteException(Exception): pass # VARIABLE_PATTERN = re.compile("(?P__(?P[^ ]+)__)") # VARIABLE_PATTERN_OO = "__[^ ]+__" # MULTI_LINE_MARKER = '\+\+' # a re.match pattern i.e. starts from beginning of string def start_oo(server="127.0.0.1", port=8100, headless=False): """ Starts OpenOffice.org with a listening socket. :param server: a server name :param port: the port to connect to :param headless: if False, disables the headless mode. """ # there's a class implementing all this here: # http://www.linuxjournal.com/content/starting-stopping-and-connecting-openoffice-python # binary name to invoke program = ['ooffice'] # basic options needed opts = ['-accept="socket,host=%(server)s,port=%(port)s;urp;StarOffice.ServiceManager"' % locals()] opts += ['-norestore', '-nologo', '-nocrashreport'] # -invisible if headless: opts += ['-headless'] cmd = " ".join(program + opts) p = subprocess.Popen(cmd, shell=True) def connect(server='127.0.0.1', port=8100, headless=False): """ connect to server, returning the oo_context :param server: a server name :param port: the port to connect to :param headless: if False, disables the headless mode. """ import time # Import the OpenOffice Component Context. comp_context = uno.getComponentContext() # Now access the UnoUrlResolver service. This will allow you to connect # to OpenOffice.org program. resolver = comp_context.ServiceManager.createInstanceWithContext( "com.sun.star.bridge.UnoUrlResolver", comp_context) cmd = "uno:socket,host=%(server)s,port=%(port)s;urp;StarOffice.ComponentContext" % { 'server':server,'port':port} if not test_srv_connection(server, port, timeout=3) and \ server in ('127.0.0.1', 'localhost'): warnings.warn("Couldn't connect to server: %s:%s - trying to launch it" % ( server, port), RuntimeWarning, stacklevel=1) start_oo(server, port, headless) #Wait 3 seconds for openoffice to load. time.sleep(3) try: # Now load the context and you are now connected. You can access OpenOffice # via its API mechanism. oo_context = resolver.resolve(cmd) except NoConnectionException, e: # Launching openoffice if server not in ('127.0.0.1', 'localhost'): warnings.warn("couldn't connect to server: %s:%s" % (server, port), RuntimeWarning, stacklevel=1) raise return oo_context class Context(object): """ A context used to render a template. It contains both the data and the way to substitute variable with the data. """ translate = None """A translation dict. Whenever a variable pattern is found in a document, it will be searched for in this dictionary to see if it should be translated before sarching in the context. The goal is to allow short variable names in template for narrow cells even if the real attribute name in the program is longer. e.g.:: context.translate['a'] = 'client.user.address' context.translate['mq'] = 'minimum_quantity_in_store' would allow to write ``$u.city`` instead of ``$client.user.addres.city`` and ``$mq`` instead of ``$minimum_quantity_in_store`` """ def __init__(self, content, lazy=True): """ :param content: a dict with keys the variable names :param lazy: boolean. If True (default) invokes _implement_lazy_tables to allow the list of objects for a table rendered to be directly set as value of the table_name entry (rather than a list of lists). See :ref:`example` ``Table1`` is lazy ``Table2`` is not. The goal is to prevent common errors rather than promoting lazy writing. When you only have a single list of objects you may easily forget that you may have more than one. The assumption is that you don't normally have lists as values of context (other that for tables). While probably true, should you need lists as contetxt values, you can just set lazy=False. """ self.content= content if lazy: self._implement_lazy_tables() self.translate = {} def __getitem__(self, key): return self.content[key] def __setitem__(self, key, value): self.content[key] = value def __contains__(self, key): return key in self.content def _implement_lazy_tables(self): """ implement a lazy context where lists are directly values of contents[Table_name] rather then conten[Table_name] = ((...),) """ for key, value in self.content.iteritems(): if isinstance(value, (list, tuple)) and len(value): if not isinstance(value[0], (list, tuple)): self.content[key] = (value, ) def sub(self, match=None, key=None): """ Substitution used in normal find & replace on the whole document. The match must have a group named 'var_name' :param match: the match resulting from re.match. if match is None the key is taken from m.group('var_name') :param key: just usefull for debugging purpose. """ if not key: assert match is not None key = match.group('var_name') key = self.translate.get(key, key) try: value = self.content[key] value = unicode(self.value2string(value, key)) return value except KeyError, e: return self._deep_sub(None, *key.split('.')) def sub_cell(self, match, record_index, table_name, list_num): """ Substitution used for cell values. Specialized version of sub that knows how to retrieve data from the object of the cell. :param match: a match that have groups named 'var_name' and 'match' :param record_index: the index of the record in the table's list :param table_name: the openoffice name of the table :param list_num: the odered number of the list in the table (starts from 1). """ assert table_name in self.content, "%s not in context" % self.table_name key = match.group('var_name') key = self.translate.get(key, key) match = match.group('match').replace('$', r'\$') try: ## the object where we get the values are in a list obj = self.content[table_name][list_num][record_index] value = unicode(self.value2string(getattr(obj, key), key)) return unicode(match.sub(value)) except (KeyError, AttributeError), e: return self._deep_sub(obj, *key.split('.')) def _deep_sub(self, obj, key, *tokens ): """ search a value in nested structure (eg.: user.name, rs.address.city) """ key = self.translate.get(key, key) test_split = key.split('.') if len(test_split) > 1: return self._deep_sub(obj, *(test_split + list(tokens))) try: if obj: value = getattr(obj, key) else: value = self.content[key] except (KeyError, AttributeError), e: self.missing_keys[key] = None return "Missing Key " + key for tk in tokens: try: value = getattr(value, tk) except AttributeError, e: self.missing_keys[tk] = "%s.%s (%s)" % (key, tk, value) value = "No attr %s in %s" % (tk, value) return unicode(self.value2string(value, key)) def _get_records_len(self, table_name, list_num): """ return the number of records that must be rendered for this table """ try: return len(self.content[table_name][list_num]) except KeyError: print "No TableName %s in context" % table_name return 0 def value2string(self, value, key): """ :param value: the value found in context :param key: the key used to retrieve the value. Note that it is ony partially usefull as it can be a key of the context or an attribute name of an object containted in the context or in a row customize the value from the context. You are supposed to customize this method that currently only trasforms a date in a locale compliant form (if locale is set) """ ## Don't write 'None' for None... if value is None: return '' if isinstance(value, datetime.date): return value.strftime('%x') return value def reset_missing(self): """ reset list of key missing in the context """ self.missing_keys = {} def update(self, d): """ Add dict ``d`` to content of this context :param d: the dict I want to add to context """ self.content.update(d) def __str__(self): return "%s" % self.content class Template(): """ The class template that connects to a server (or starts a local one), read a document, parses the document to find tables """ DEFAULT_VARIABLE_PATTERN = '(?P\$(?P[^ ,\n-\)\t]+))' DEFAULT_VARIABLE_PATTERN_OO = '$[^ ,\n\-)\t]+' DEFAULT_MULTI_LINE_MARKER = '(?P\+\+)' # a re.match pattern i.e. starts from beginning of string VARIABLE_PATTERN = None """the pattern for python variable detection. it's a regular expression read :attr:`set_pattern` for details""" VARIABLE_PATTERN_OO = None """the pattern for openoffice variable detection. read :attr:`set_pattern` for details """ MULTI_LINE_MARKER = None """the pattern used to tel when a multiline line starts. Defaults to ++""" oo_context = None """the connection with the server. This can be reused between templates""" document = None """the openoffice document from the server""" search = None """the openoffice SearchDescriptor""" preserve_styles = False """Preserve style of each word in the cell when cloning rows. This will slow down the process so it's disabled by default""" def __init__(self, filename, server='127.0.0.1', port=8100, headless=False, oo_context=None, preserve_styles=False): """ only the ``filename`` is needed if the server is local. If we already have a template on the same server we can reuse the ``oo_context`` :params filename: the template filename in the server's filesystem :param server: a server name :param port: the port to connect to :param headless: if False, disables the headless mode. :param oo_context: the openoffice context (not to be confused with ``oootemplate.Context``). The `oo_context`` plays the role of the connection to the server :param preserve_styles: use :class:`TemplateWithStyles` to enforce preservation of styles in each word of the cell when duplicating rows """ self.oo_context = oo_context or connect(server, port, headless) self.document = self.open_doc(filename, self.oo_context) self.cursor = self.document.Text.createTextCursor() self.preserve_styles = preserve_styles self.tables = self.get_tables() self.search = self.document.createSearchDescriptor() self.search.SearchRegularExpression = True self.search.SearchCaseSensitive = True self.search.SearchWords = True self.VARIABLE_PATTERN = re.compile(self.DEFAULT_VARIABLE_PATTERN) """the pattern for python variable detection. It's a regular expression, read ``set_pattern`` for details""" self.VARIABLE_PATTERN_OO = self.DEFAULT_VARIABLE_PATTERN_OO """the pattern for openoffice variable detection. read ``set_pattern`` for details """ self.MULTI_LINE_MARKER = self.DEFAULT_MULTI_LINE_MARKER """the pattern used to tel when a multiline line starts. Defaults to ++""" def set_pattern(self, pattern, oo_pattern): """ Set the pattern to detect what is a variable to be substituted :param pattern: the pattern with syntax suitable for module ``re`` module. It must define at least 2 groups: ``var_name`` and ``match`` pointing respectively to the name of the variable and the whole match. Default is ``(?P\$(?P[^ ]+))`` :param oo_pattern: the pattern with syntax suitable for openoffice regexp search. It can only use the openoffice syntax. Default is ``$[^ ]+`` """ self.VARIABLE_PATTERN = re.compile(pattern) self.VARIABLE_PATTERN_OO = oo_pattern def open_doc(self, filename, oo_context): """ open the template file and return the document Se url sbagliato :: SystemError: pyuno runtime is not initialized, (the pyuno.bootstrap needs to be called before using any uno classes) WARNING: Failure executing file: """ url = self.filename_to_url(filename) desktop = oo_context.ServiceManager.createInstanceWithContext( "com.sun.star.frame.Desktop", oo_context) prop_ro = PropertyValue('ReadOnly', 0, True, 0) prop_h = PropertyValue('Hidden', 0, True, 0) try: document = desktop.loadComponentFromURL(url, "_blank", 0, (prop_ro, prop_h, )) except IllegalArgumentException, e: msg = "Original error: '%s'\nTemplate may have a wrong name: %s" % (str(e), filename) raise WrongTemplateName(msg) return document def filename_to_url(self, filename): # This will not always work if the server is remote: it depends on the OSs # eg.: under windows would become file://C:/... that has no meaning in a *nix server #url = uno.systemPathToFileUrl(filename) # if not re.search('://', filename): url = u'file://%s' % filename else: url = unicode(filename) return url def get_tables(self): """ returns a table_dict {table_name:table_obj} containing all tables found """ qty = self.document.TextTables.Count table_dict = {} for n in range(qty): oo_table = self.document.TextTables.getByIndex(n) table_name = oo_table.getName() if self.preserve_styles: table = TableWithStyles(oo_table, self) else: table = Table(oo_table, self) table_dict[table_name] = table return table_dict #### Render def render(self, context): """ substitute all the variables with values from context :param context: the Contex instance to be used """ context.reset_missing() self.context = context for table_name, table in self.tables.iteritems(): table.render(context) self.find_and_replace(context) return context.missing_keys def find_and_replace(self, context, search_obj=None): """ This function searches and replaces. Create search, call function findFirst, and finally replace what we found. """ pattern = self.VARIABLE_PATTERN (search_obj or self.search).SearchString = self.VARIABLE_PATTERN_OO found = self.document.findFirst( self.search ) while found: m = pattern.match(found.String) new_text = pattern.sub(context.sub(m), found.String) found.String = new_text found = self.document.findNext( found.End, self.search) def save_as(self, filename, local=None): """ save the template using save_as capability of openoffice. :param filename: filename in the `server`'s filesystem. The extension is used to detect the file type as in regular openoffice use. """ document = self.document path, file_ = os.path.split(filename) name, extension = os.path.splitext(file_) if extension == '.odt': url = self.filename_to_url(filename) document.storeToURL(url,()) elif extension == '.pdf': url = self.filename_to_url(filename) prop = PropertyValue( "FilterName" , 0, "writer_pdf_Export" , 0 ) document.storeToURL(url, (prop,)) else: filename = filename+'.odt' print 'Unsupported Extension "%s", saving as "%s"' % (extension, filename) url = self.filename_to_url(filename) document.storeAsURL(url,()) # if local: # self.save_local(filename, local) # def save_local(remote_path, local_path): # """ # Retrieve the remote # """ def close(self): """ close the related document """ self.document.close(True) #### class Table(): """ table object detail on the API exposed by uno: http://api.openoffice.org/docs/common/ref/com/sun/star/table/module-ix.html """ def __init__(self, oo_table, template): """ :param oo_table: the openoffice table object :param template: the ``oootemplate.template`` object in which this table is """ self.table = oo_table self.name = oo_table.Name self.rows = self.table.getRows() self.template = template def _get_rows_num(self): return self.table.Rows.Count rows_num = property(_get_rows_num) def __str__(self): return "Table Object" def add_rows(self, index, count): """ Insert `count` rows just before position `index` :param index: index `before` which rows will be added :param count: number of rows to add """ self.table.Rows.insertByIndex(index, count) def del_rows(self, index, count=1): """ Delete count rows just before position index """ self.table.Rows.removeByIndex(index, count) def render(self, context): """ render the table with the context :param context: the Context object that holds the data """ list_num = 0 offset = 0 # increments after rows are added for r in range(self.rows_num): row_data = self.get_row(r + offset, data_array=True) for c, text in enumerate(row_data): if re.match(self.template.MULTI_LINE_MARKER, text): ## get rid of the multiline marker self.set_text(c, r+offset, re.sub(self.template.MULTI_LINE_MARKER, '', text)) row_data = self.get_row(r+offset, data_array=True) offset += self._render_rows(r+offset, row_data, context, list_num) list_num += 1 def _render_rows(self, r, row_data, context, list_num): """ render the row adding as many rows as needed for the records present in contex for this table and using the prototipe in row_data """ n_records = context._get_records_len(self.name, list_num) pattern = self.template.VARIABLE_PATTERN self.add_rows(r, n_records) j = 0 for j in range(r, r + n_records): for k, value in enumerate(row_data): ## a cell can have more variables | $d.first_name $d.last_name | $year | def sub(m): return context.sub_cell(m, record_index=j-r, table_name=self.name, list_num=list_num) text = pattern.sub(sub, value) self.set_text(k, j, text) self.del_rows(j+1) # this row deletes the row with ++$obj... return n_records -1 # we added n_records and deleted 1 def get_row(self, r, data_array=True): """ return the row at position r :param r: index of the row to return :param data_array: return a tuple of the data instead. If ``False`` an Openoffice ``CellRange`` object is returned """ assert r < self.rows_num, "Max row index for table %s is %s (requested %s)"% ( self.name, self.rows_num-1, r) ## I wasn't able to find a way to get the maximum number of columns ## table.Rows.Count returns the number of columns of the first row. for c in xrange(30): try: row = self.table.getCellRangeByPosition(0, r, c, r) except OutOfBoundsException: break pass if data_array: return row.DataArray[0] else: return row def get_text(self, c, r): return self.table.getCellByPosition(c,r).getString() def set_text(self, c, r, value): self.table.getCellByPosition(c,r).setString(value) def __repr__(self): return '<%s %s>' % (self.__class__.__name__, self.name) class TableWithStyles(Table): """ A Table that clones rows preserving style info even if several different styles are used within the same cell. This process is slower so it's not active by default: set Template's arg ``preserve_styles`` to True to enable it (see ex. N. 5). """ def render(self, context): """ render the table with the context :param context: the Context object that holds the data """ list_num = 0 offset = 0 # increments after rows are added for r in range(self.rows_num): row_data = self.get_row(r + offset, data_array=True) for c, text in enumerate(row_data): if re.match(self.template.MULTI_LINE_MARKER, text): ## get rid of the multiline marker #self.set_text(c, r+offset, # re.sub(self.template.MULTI_LINE_MARKER, '', text)) self.replace_marker(c, r+offset) row_data = self.get_row(r+offset, data_array=True) offset += self._render_rows(r+offset, row_data, context, list_num) list_num += 1 def _render_rows(self, r, row_data, context, list_num): """ render the row adding as many rows as needed for the records present in contex for this table and using the prototipe in row_data """ n_records = context._get_records_len(self.name, list_num) pattern = self.template.VARIABLE_PATTERN self.clone_rows(r, n_records -1) j = 0 for j in range(r, r + n_records): for k, value in enumerate(row_data): try: self.sub_text_in_cell(k, j, context, record_index=j-r, list_num=list_num) except OutOfBoundsException: pass return n_records -1 # we added n_records -1 def sub_text_in_cell(self, c, r, context, record_index, list_num): # substitution that preserves the style of the caractes cell = self.table.getCellByPosition(c,r) cursor = cell.createTextCursor() ## a cell can have more variables | $d.first_name $d.last_name | $year | while True: m = re.search(self.template.VARIABLE_PATTERN,cell.String) if not m: break cursor.gotoStart(False) cursor.goRight(m.start('match'), False) cursor.goRight(m.end('match') - m.start('match'), True) new_text = context.sub_cell(m, record_index=record_index, table_name=self.name, list_num=list_num) cell.insertString(cursor, new_text, True) def replace_marker(self, c, r): cell = self.table.getCellByPosition(c,r) m = re.search(self.template.MULTI_LINE_MARKER, cell.String) if not m: return cursor = cell.createTextCursor() cursor.gotoStart(False) cursor.goRight(m.start('match'), False) cursor.goRight(m.end('match') - m.start('match'), True) cell.insertString(cursor, '', True) def clone_rows(self, index, count): """ Insert `count` rows just before position `index` :param index: index 0-based of the row that must be cloned :param count: number of rows to add """ self.table.Rows.insertByIndex(index, count) ## Let us fill with the same content controller = self.template.document.getCurrentController() view_cursor = controller.getViewCursor() for c in xrange(300): try: src_cell = self.table.getCellByPosition(c, index + count) except OutOfBoundsException: break view_cursor.gotoRange(src_cell.Text, False) transferable = controller.getTransferable() for j in range(index , index + count ): try: dst_cell = self.table.getCellByPosition(c, j) except OutOfBoundsException: break view_cursor.gotoRange(dst_cell.Text, False) controller.insertTransferable(transferable) def clone_rows_dispatcher(self, index, count): """ Insert `count` rows just before position `index` :param index: index `before` which rows will be added :param count: number of rows to add """ # this would probably be a little faster than the one based on getTransferable() # but I can't understand how to copy the Row. It seems that the EntireRow doesn't # select any text, whe run from within uno in headless mode self.table.Rows.insertByIndex(index, count) dispatcher = self.template.oo_context.ServiceManager.createInstance( 'com.sun.star.frame.DispatchHelper') controller = self.template.document.getCurrentController() frame = controller.getFrame() ## Let us fill with the same content src_cell = self.table.getCellByPosition(0, index + count) view_cursor = controller.getViewCursor() view_cursor.gotoRange(src_cell.Text, False) dispatcher.executeDispatch(frame, ".uno:EntireRow", "", 0, tuple()) dispatcher.executeDispatch(frame, ".uno:Copy", "", 0, tuple()) for j in range(count): dst_cell = self.table.getCellByPosition(0, index + j) view_cursor.gotoRange(dst_cell.Text, False) dispatcher.executeDispatch(frame, ".uno:Paste", "", 0, tuple()) # clone_rows = clone_rows_dispatcher # Cursor.CharWeight = com.sun.star.awt.FontWeight.BOLD def __repr__(self): return '' % (self.__class__.__name__, self.name) def test_srv_connection(host, port, timeout=3): """ test TCP connectivity :param host: the host on which the server must be tested :param posrt: the port to test :param timeout: timeout for the socket (default 3) """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) try: sock.connect((host, int(port))) sock.close() return True except socket.error, err: return False sqlkit-0.9.5/sqlkit/misc/conf.py0000644000175000017500000001662111714210425016136 0ustar sandrosandro""" Nick extended configuration ============================ Sqledit configuration may be extended beyond :file:`.sqledit/nicks` file. You can add configurations for a nick in a subdirectory of :file:`.sqledit` named as the nick itself. Hooks, layout, or a program can be placed directly hereq. As an example if you have a nick 'film' defined in ``.sqledit/nicks``:: [film] url = 'postgres://localhost/film you can add customizations w/o creating a real program. Currently you can add: :layout.py: the layout that SqlMask should use when creating the GUI. You can create an empty template for this with ``sqledit --create-template layout nick``. That's the place where you would register your layout. This is autoloaded if no ``program.py`` is present :hooks.py: the :ref:`hooks` that will be used as default. You can create an empty template for this with ``sqledit --create-template hooks nick``. That's the place where you would register yout hooks. This is autoloaded if no ``program.py`` is present :models.py: this file is for definition of the classes. If you define classes here you can set :ref:`relationships` between them so that a mask can display parent/child records as director/movies. Many examples in the demo. This is autoloaded if no ``program.py`` is present You can create an empty template for this with ``sqledit --create-template models nick`` :program.py: a program that will be executed instead of normal sqledit. There is no particular advantage running this instead of creating a real standalone program but sometimes it just grows step by step (you start with a simple nick, than you add a layout, maybe a hook and you need more customization). You will call the program as ``sqledit film`` and sqledit will: * cd to ~/.sqledit/film * execfile('program.py', {'opts' : opts}) * no other file will be autoloaded in this case where ``opts`` is a class that holds the options you set in the nick definition (among which ``opts.url``). You can create an empty template for this with ``sqledit --create-template hooks nick``. A second reason is that many times a program that uses sqlkit is a *very* short script and I found myself spending more time deciding a proper name and place, this already makes these decisions for you! There is no need to start ``gtk.main()`` since this is no longer the main program, sqledit will run it before this moment """ import os from ConfigParser import ConfigParser, NoSectionError import sqlalchemy class MissingNickDefinition(Exception): pass class Opts(object): pass ## Default program.py TEMPLATE_PROGRAM = """\ import gtk from sqlkit.widgets import SqlMask from sqlkit.db import proxy import models import hooks URL = 'postgresql://simone:pr3s33d@ubu64/scuole' models.Base.metadata.bind = URL ## opts will be added to globals when ececuting this file db = proxy.DbProxy(bind=opts.URL, metadata=models.Base.metadata) # no need to run gtk.main()!!! """ ## Default layout.py TEMPLATE_LAYOUT = """\ from sqlkit.db import utils #LAYOUT = """ """ # utils.register_layout('table_name', LAYOUT) """ ## Default hooks.py TEMPLATE_HOOKS = """\ from sqlkit.db import utils class MyHook(object): def on_init(self, widget): pass # utils.register_hook('table_name', MyHook) """ TEMPLATE_MODELS = """\ from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Table, Column, ForeignKey from sqlalchemy.types import * from sqlalchemy import orm, sql from sqlkit.db import utils, proxy Base = declarative_base() class MyModel(Base): __tablename__ = 'demo_model' # utils.register_class(MyModel) """ def read_conf(nick, opts=None): """ read sqledit configurations for 'nick' and return an opts object with values in configuration possibly overwritten by values in opts. opts can be an optionparse.Values object or a dict. :param nick: the nickname for which we want the configuration :param opts: the optparse.Values object or a dict """ from copy import deepcopy if opts is None: opts = Opts() dbconf = os.path.join(os.path.expanduser('~'), '.sqledit', 'nicks') lopts = deepcopy(opts) def loop_on_opts(c, opts, nick=nick): """ implements "copy" option in .sqledit/nicks """ for key, val in c.items(nick): if key == 'copy': loop_on_opts(c, opts, nick=val) for key, val in c.items(nick): if key == 'copy': continue if getattr(lopts, key, None) is None: setattr(opts, key, val) if os.path.exists(dbconf): c = ConfigParser() c.read(dbconf) loop_on_opts(c, opts, nick) read_conf_in_dir(nick, opts) if sqlalchemy.__version__ < '0.6': if hasattr(opts, 'url5'): opts.url = opts.url5 # option 'mask' in the nicks should not prevail over command line opts! # in this case it would prevail due to different key name # and parsing order if getattr(lopts, 'sqltable', None): opts.mask = False return opts else: raise MissingNickDefinition def get_nick_dir(nick): dir_nick = os.path.join(os.path.expanduser('~'), '.sqledit', nick) return dir_nick def read_conf_in_dir(nick, opts): """ Read the conf dir and fill in opts: :run: the executable to be... execfile'ed :hooks: the file containig the hooks """ nick_dir = os.path.join(os.path.expanduser('~'), '.sqledit', nick) opts.run = opts.nick_dir = opts.hooks = opts.models = opts.layout = None if os.path.exists(nick_dir): opts.nick_dir = nick_dir files = os.listdir(nick_dir) if 'program.py' in files: opts.run = 'program.py' if 'hooks.py' in files: opts.hooks = 'hooks.py' if 'models.py' in files: opts.models = 'models.py' if 'layout.py' in files: opts.layout = 'layout.py' def create_templates(opts): """ Create templates for these """ dir_nick = os.path.join(os.path.expanduser('~'), '.sqledit', opts.nick) if not os.path.exists(dir_nick): os.mkdir(dir_nick) layout = opts.create_templates in ('layout', 'all') hooks = opts.create_templates in ('hooks', 'all') program = opts.create_templates in ('program', 'all') models = opts.create_templates in ('models', 'all') if layout: filename = os.path.join(dir_nick, 'layout.py') if os.path.exists(filename): print "File %s already exists, skipping creation" % filename open(filename, 'w').write(TEMPLATE_LAYOUT) if models: filename = os.path.join(dir_nick, 'models.py') if os.path.exists(filename): print "File %s already exists, skipping creation" % filename open(filename, 'w').write(TEMPLATE_MODELS) if hooks: filename = os.path.join(dir_nick, 'hooks.py') if os.path.exists(filename): print "File %s already exists, skipping creation" % filename open(filename, 'w').write(TEMPLATE_HOOKS) if program: filename = os.path.join(dir_nick, 'program.py') if os.path.exists(filename): print "File %s already exists, skipping creation" % filename open(filename, 'w').write(TEMPLATE_PROGRAM) sqlkit-0.9.5/sqlkit/misc/words.py0000644000175000017500000000121611714210425016341 0ustar sandrosandro""" A collection of words I want translated This is just a fake module. Too lazy to learn babel extractors... """ # TIP: filter page of the filter panel _('filter') # TIP: output page of the filter panel _('output') # TIP: menu entry _('File') # TIP: menu entry _('Modify') # TIP: menu entry _('Help') # TIP: menu entry _('Go') # TIP: opts in filetr panel _('opts') # TIP: opts in filetr panel _('Tools') # TIP: serach mode: _('regexp') # TIP: search mode _('start') # TIP: count records in sqledit _('N.Rows') # index column in sqledit _('Indexes') _('Nullable') _('Prim. Keys') _('Default') _('Foreign Key') _('filter_tables') # _('') # _('') # _('') sqlkit-0.9.5/sqlkit/misc/printing.py0000644000175000017500000005433611714210425017050 0ustar sandrosandro# Copyright (C) 2009-2010, Sandro Dentella # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """ Sqlkit printing -------------------- Each sqlwidget has a ``printing`` attribute that manages printer entries that is an instance of ``PrintTool``. A standard way should be as easy as just setting a directory where templates should be found and adding menu entries via ``add_menu_entry``. The default template dir is a subdir of the current directory named ``templates``. Templates present in this directory -if named as the nick of the sqlwidget- will be automatically added, but you will normally want to customize the context or adding variable to it or adding formatting functions (e.g. to transform numbers into monetary format), that can be easily done connecting to :ref:`context-ready signal `. The default way of filling the context wraps each object into an :class:`ObjectProxy`` that uses :ref:`fields` to present each value in a human readable format, that means also that it tries hard to follow foreign keys and substitute values retrieved by :ref:`description`. Tables: iteration on records ---------------------------- Oootemplate offers a powerfull way to iterate over lines via the ``++`` syntax. It requires you to define a table's name matching an entry in the context. For SqlTables the default name for such table is Table1 and all records in the table end up there. For SqlMask the default behaviour is to add an entry in the context for each related attribute (eg: *movies* for the *director*'s mask). Each such entry holds all the related records as per sqlelchemy class definition. In the director/movie example the default context would be:: context = { 'obj' : , 'movies' : [(, )] } It's up to you to prepare the openoffice file to be used as template with a table named ``movies``: you do that interactively editing oowriter's table attributes. If you prefere to have to have a different table's name or you need to customize the content of the record, you simply connet to 'context-ready' signal and prepare the context directly. ObjProxy -------- What stated above is only partially true... each object is wrapped in another object that allows you to write ``direct_id`` but really returns what you would really want: a human representation of the id exactly as returned by ``field.lookup_value(id)``. API --- .. autoclass:: PrintTool :members: __init__, add_menu_entry, prepare_context, template_dir, server, port, output_dir, remote_output_dir, pdf_viewer, odt_viewer, add_to_table, obj_proxy_class .. autoclass:: ObjProxy :members: """ import os import re import gtk import urllib import tempfile import subprocess import gobject from sqlkit import _ from sqlkit.widgets.common import dialogs from sqlkit.misc.utils import get_viewer MENU_ENTRY = """ """ class PrintAbortException(Exception): pass class DownloadException(Exception): pass class PrintTool(gobject.GObject): """ A print obj that is able to create a default context to handle to oootemplate. """ template_dir = None """The directory where templates are searched for. Default is cwd/templates. It's a path significant for the openoffice server """ local_template_dir = None """The directory where templates are searched for locally in case OpenOffice server is remote . The only need for this variable is to make it possible to :meth:`automagically <__init__>` add templates that show up in the menu entry. The templates that get used are clearly on the machine where OpenOffice is runnning, it does not need to be visible to the client. If defined, will be used just to add meny entry automatically. """ server = '127.0.0.1' """the openoffice server to connect to for printing""" port = 8100 """the port to use to connect to the server""" output_dir = None """the output dir when server is remote as viewed from client (may be an URL)""" remote_output_dir = None "the output dir when server is remote as viewed from server (a local file for the server)" pdf_viewer = get_viewer('pdf') """the preferred viewer for pdf files""" odt_viewer = get_viewer('odt') """the preferred viewer for openoffice templates""" server_dir_separator = '/' """The server dir separator / (default) or \\: determines how the remote path is built for templates and output files. Only used if server is remote (not 127.0.0.1 nor localhost)""" proxies = {} """the proxies passed to urllib.FancyURLopener (see urllib module documentation)""" obj_proxy_class = None """The :class:`ObjProxy` to use to wrap objects. Can be changed any time. You can customize it so that __getattr__ can return fancy personalization of the attributes""" preserve_styles = False """Open the template with the ``preserve_styles`` option. It's mode that allows to have a mix of styles in any singl cell. """ retrieve = True """Determine if the file must be retrieved so as to be opened locally or should be be opened remotedly (default True)""" __gsignals__ = { 'context-ready' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_BOOLEAN, # context, template_name, sqlwidget, (gobject.TYPE_PYOBJECT , gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT ), ), } def __init__(self, master): """ Initializes the printing tool setting a default template dir and checks for availability of module 'uno' from openoffice. If uno is missing the printing capabilities are made invisible. If templates are found with the name ``nick.odt`` where the nick is the nick of the sqlwidget, entries are automatically added. This way you can generate templates and use them w/o even the need for a single line of code (unless you need to customize the context, of course) :param master: the table or mask instance of which this is a printing tool """ gobject.GObject.__init__(self) self.master = master self.template_dir = self.template_dir or os.path.abspath(os.path.join(os.curdir, 'templates')) try: import uno self.disabled = False except ImportError: self.master.sb(_('Disabling printing for lack of "uno" module'), seconds=20) self.disabled = True self.hide() self.obj_proxy_class = ObjProxy self.look_for_automatic_templates() def hide(self): "Hide the print meny entries" self.master.actiongroup_print.set_visible(False) def show(self): "Show the print menu entries" self.master.actiongroup_print.set_visible(True) self.master.actiongroup_print.set_sensitive(True) def add_menu_entry(self, description, template_name, server=None, port=None, mode='pdf', output_name=None, accel=None, tip=None, action_name=None): """ Add menu entry with 'description' using 'template' Both are fake printers in the sense that are 'printer to file' :param description: the description that will appear in the menu entry :param template_name: the template name to be used :param mode: the mode of the printer: pdf or odt :param output_name: the output name. Can be a callable that will receive ``template_name`` as parameter. If ``self.output_dir`` is defined ``output_name`` will be joined with ``self.ouput_dir`` to retrieve it from the client :param accel: accelerator to be used with this :param tip: tip for the entry :param action_name: action name (defaults to templ\_ + template_name) """ assert template_name is not None, "Template name must be defined" output_name = output_name or "%s.%s" % ((os.path.splitext(os.path.split(template_name)[1])[0]), mode) if not self.disabled: self.show() dir_name, template_name, template_path = self.split_template_name(template_name) if not action_name: action_name = 'templ_%s_%s' % (template_name, mode) path = '/Main/File/Print-ph/Print/%s' % action_name action = self.master.ui_manager.get_action(path) if not action: self.master.actiongroup_print.add_actions([ (action_name, None, description, accel, tip, self.use_template,), ], (template_path, mode, server, port, output_name)) self.master.ui_manager.add_ui_from_string(MENU_ENTRY % action_name) def split_template_name(self, template_name): """ return a tuple (dir, template_name, template_path) if templatename does not contain a path, self.template_dir is used if that leads to an existent filename, otherwise it'd be set no None :param template_name: a template name possibly with a complete path """ template_path = template_name dirname, template_name = os.path.split(template_name) if not dirname: dirname = self.template_dir template_path = os.path.join(dirname, template_name) if not os.path.exists(template_path): dirname = None return dirname, template_name, template_path def use_template(self, menuItem=None, template_path=None, mode='pdf', server=None, port=None, output_path=None, preview=False, template_name=None): """ create a context from master and call template :param menuItem: the menu entry that was clicked. Not needed. :param tempate_path: the template_path to be used on the server. :param mode: currently ``pdf`` or ``odt``. Default: pdf. :param server: the name or ip of the openoffice server. Default: self.server :param port: the port on which the server runs. Default: self.port :param output_path: the output name. It can be a callable that will receive 2 args: * this PrintTool instance * the template_path :param preview: (Boolean) if ``True``, the local_output_path is a temporary one and the user is not prompted for a position :param template_name: template_name. If given the template_path is """ import oootemplate as oo if template_name: template_path = self.split_template_name(template_name)[2] server = server or self.server port = port or self.port ## Context context = oo.Context({}) if self.master.is_mask(): context['obj'] = self.obj_proxy_class(self.master.current, self.master) else: self.add_to_table(context, 'Table1', self.master.records) for rel in self.master.related.keys(): self.add_to_table(context, rel, self.master.related[rel].records, master=self.master.related[rel]) self.emit('context-ready', context, os.path.split(template_path)[1], self.master) ## Template & output templ = oo.Template(unicode(template_path), server=server, port=port, preserve_styles=self.preserve_styles) missing_keys = templ.render(self.prepare_context(context)) if missing_keys: self.debug_missing(missing_keys, templ, context) ## Output # when the server is remote, the output file is created remotely (remote_output_path) # downloaded and saved (local_output_path) via http, and opened locally if callable(output_path): output_path = output_path(self, template_path) if preview: f, local_output_path = tempfile.mkstemp(suffix='.pdf') if not os.name == 'mac': os.close(f) # under windows if we don't close the file id AdobeReader won't # open the file claiming it's used by others remote_output_path = self.get_remote_output_path( output_path or 'preview-%s.pdf' % template_name, server) else: local_output_path = self.get_local_output_path(template_path, mode, output_path) remote_output_path = self.get_remote_output_path(output_path, server) if not local_output_path: return if server in ('localhost', '127.0.0.1'): if local_output_path == template_path: self.master.message(_("Template name (%s) cannot be same as output_path (%s)") % ( template_path, output_path), type='ok') return remote_output_path = local_output_path templ.save_as(remote_output_path) templ.close() ## Show Result self.show_result(remote_output_path, local_output_path, server) def get_remote_output_path(self, output_path, server): """ return the name the remote server must use to save the file """ if self.remote_output_dir and not server in ('localhost', '127.0.0.1'): return "%s%s%s" % (self.remote_output_dir, self.server_dir_separator, output_path) return output_path def show_result(self, remote_output_path, local_output_path, server): """ Open the resulting file with the best possible viewer/editor """ # check that the file exists both if remote... if not server in ('localhost', '127.0.0.1') and \ self.output_dir and re.search('http://', self.output_dir): remote_path = "%s/%s" % (self.output_dir, os.path.split(remote_output_path)[1]) opener = Opener(self.proxies) try: f = opener.open(remote_path) except DownloadException: self.master.dialog(text="Missing file %s" % remote_path, type="ok") return if self.retrieve: opener.retrieve(remote_path, local_output_path) # or local elif not os.path.exists(local_output_path): self.master.dialog(text="Missing file '%s'" % local_output_path, type="ok") return self.open_local_file(local_output_path) def debug_missing(self, missing_keys, template, context): """ Communicate that some keys are missing :param missing_keys: the dict of missing keys :param template: the oootemplate.Template :param context: the oootemplate.Context """ msg = _("The following variable are missing in the context") # default table name has not been changed and is not present in the .odt file # chances are that a localized .odt file names table in a localized way... if 'Table1' in context and not 'Table1' in template.tables: msg += _("\nA possible error is that I don't see 'Table1' in your template\n" \ + "but I see %s" % " ".join(template.tables)) for key, where in missing_keys.iteritems(): if where: msg += "\n'%s' (%s)" % (key, where or '') else: msg += "\n'%s'" % key self.master.dialog(text=msg, type='ok') def prepare_context(self, context): """ This function is meant to be overridden by a customized one :param context: the automatically prepared ``oootemplate.Context``. It contains 2 keys: * 'obj': the current object for mask and normally *None* for tables (unless an object was edited, in which case it will point to that object) * 'Table1' : the list of record currently loaded in this sqlwidget You can add any keys but remember to use the correct syntax for tables (a dict with lists as , 2009. # msgid "" msgstr "" "Project-Id-Version: 1.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2009-07-02 12:34+0200\n" "PO-Revision-Date: 2011-03-13 00:08+0100\n" "Last-Translator: Juan Fernando Jaramillo Botero " "\n" "Language-Team: Español \n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.4\n" #: bin/sqledit.py:87 msgid "Connection setup" msgstr "Configuración de la conexión" #: bin/sqledit.py:90 msgid "" "\n" " You can indicate the database you want to connect to\n" " using an URI in the form as:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " or using password and username:\n" " mysql://sandro:pass@host/dbname\n" " " msgstr "" "\n" " Puede indicar la base de datos a la que quiera conectarse\n" " usando una URI de la forma:\n" " postgres://localhost/nombrebd\n" " sqlite:///nombrebd\n" " o usando clave y usuario:\n" " mysql://sandro:clave@host/nombrebd\n" " " #: bin/sqledit.py:117 msgid "Run Demo" msgstr "Ejecutar Demo" #: bin/sqledit.py:140 msgid "" "Info on available backend:\n" "postgres, mysql..." msgstr "" "Información sobre plataformas disponibles:\n" "postgres, mysql..." #: bin/sqledit.py:141 msgid "Sqledit manual page" msgstr "Manual de Sqledit" #: bin/sqledit.py:146 msgid "" "Try continuosly to connect.\n" " Nice and usefull but may cause temporary blocks\n" " if you write an inexistent hostname\n" " " msgstr "" "Trate continuamente conectarse.\n" " Bueno y útil pero puede causar bloqueos temporales\n" " si usted escribe un nombre de host inexistentes\n" " " #: bin/sqledit.py:160 msgid "" "If you write a wrong hostname\n" "the application may hang some seconds till the natural network timeout. \n" "Uncheck the flag on the right ti disable this feature" msgstr "" "Si escribe un nombre de host errado\n" "la aplicación puede colgarse algunos segundos hasta el timeout natural de" " la red. \n" "Des selecciones la bandera a la derecha eso inhabilitará esta propiedad" #: bin/sqledit.py:187 #, python-format msgid "Attempting to connect to %s" msgstr "Intentando conectar a %s" #: bin/sqledit.py:190 #, python-format msgid "Connected to %s" msgstr "Conectado a %s" #: bin/sqledit.py:221 msgid "A complete demo of all the features of the sqlkit package" msgstr "Una completa demostración de todas las cualidades del paquete sqlkit" #: bin/sqledit.py:222 msgid "" "The demo was not found, if you know where it is,\n" "run it manually: python demo.py" msgstr "" "La demostración no fue encontrada, si conoce su localización,\n" "ejecute esta manualmente: python demo.py" #: sqlkit/exc.py:48 #, fuzzy, python-format msgid "Field '%s' cannot be NULL" msgstr "el campo %s no puede ser NULL" #: sqlkit/exc.py:60 #, fuzzy, python-format msgid "Field '%s' cannot have value %s" msgstr "el campo %s no puede tener el valor %s" #: sqlkit/fields.py:979 #, python-format msgid "value is not Decimal nor string: %s" msgstr "el valor no es Decimal ni cadena: %s" #: sqlkit/fields.py:1055 #, python-format msgid "value is not date nor string: %s" msgstr "el valor no es fecha ni cadena: %s" #: sqlkit/fields.py:1174 sqlkit/widgets/mask/miniwidgets.py:430 #, fuzzy, python-format msgid "Wrong date format: %s" msgstr "Formato de hora errado: %s" #: sqlkit/fields.py:1184 msgid "Wrong time format" msgstr "Formato di tempo errato" #: sqlkit/fields.py:1336 msgid "Value cannot be NULL" msgstr "" #: sqlkit/fields.py:1340 #, fuzzy, python-format msgid "Value '%s' is not accepted" msgstr "El valor está disponible" #: sqlkit/db/proxy.py:219 #, fuzzy, python-format msgid "Table %s doesn't have a primary key, editing is not possible" msgstr "La tabla %s no tiene una llave primaria, su edición no es posible" #: sqlkit/layout/dateedit.py:190 msgid "Press Esc to close or double click a date to select it" msgstr "" #: sqlkit/layout/fk_entry.py:25 msgid "Find allowed values (Control-Enter/Shift-Enter)" msgstr "" #: sqlkit/layout/image_widget.py:142 msgid "" "Right click on the image area\n" " to upload an image" msgstr "" #: sqlkit/layout/image_widget.py:273 sqlkit/widgets/table/table.py:1395 msgid "Upload image" msgstr "" #: sqlkit/layout/image_widget.py:273 msgid "Upload or modify an image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete the image" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Image viewer" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Open separate image viewer" msgstr "" #: sqlkit/layout/image_widget.py:276 msgid "Save image as" msgstr "" #: sqlkit/layout/image_widget.py:279 msgid "Show/Hide image name" msgstr "" #: sqlkit/layout/image_widget.py:317 msgid "upload image" msgstr "" #: sqlkit/misc/datetools.py:152 #, fuzzy msgid "Incomplete date format" msgstr "Formato de fecha errado %s" #: sqlkit/misc/table_browser.py:135 msgid "Database" msgstr "Base de Datos" #. TIP: menu entry #: sqlkit/misc/table_browser.py:136 sqlkit/misc/words.py:13 #: sqlkit/widgets/common/sqlwidget.py:795 msgid "Modify" msgstr "Modificar" #: sqlkit/misc/table_browser.py:137 msgid "Tool" msgstr "Herramienta" #. TIP: menu entry #: sqlkit/misc/table_browser.py:138 sqlkit/misc/words.py:15 #: sqlkit/widgets/common/sqlwidget.py:798 msgid "Help" msgstr "Ayuda" #: sqlkit/misc/table_browser.py:141 msgid "Count records" msgstr "Contar registros" #: sqlkit/misc/table_browser.py:141 msgid "Count records in all tables" msgstr "Contar registros en todas las tablas" #: sqlkit/misc/table_browser.py:144 msgid "" "Configure the fields: \n" "labels, tooltip, completion, search field" msgstr "" "Configure los campos: \n" "marcadores, tooltip, finalización, buscar campo" #: sqlkit/misc/table_browser.py:147 msgid "Edit Sqlkit Fields" msgstr "Edite campos de Sqlkit" #: sqlkit/misc/table_browser.py:150 msgid "Primary Key" msgstr "Llave primaria" #: sqlkit/misc/table_browser.py:151 msgid "Show/Hide primary key if the key is numeric" msgstr "Mostrar/Ocultar llave primaria si la llave es numerica" #: sqlkit/misc/table_browser.py:152 msgid "Load data" msgstr "Cargar datos" #: sqlkit/misc/table_browser.py:153 msgid "Load the data as well" msgstr "Cargar los datos también" #: sqlkit/misc/table_browser.py:154 msgid "Blank" msgstr "En blanco" #: sqlkit/misc/table_browser.py:154 msgid "Cast blank into NULL" msgstr "Cambiar blancos a NULL" #: sqlkit/misc/table_browser.py:262 msgid "Mask" msgstr "Mascara" #: sqlkit/misc/table_browser.py:266 msgid "Table" msgstr "Tabla" #: sqlkit/misc/table_browser.py:272 msgid "Collapse row" msgstr "" #: sqlkit/misc/table_browser.py:276 msgid "Table reflection" msgstr "Reflexión de la Tabla" #: sqlkit/misc/table_browser.py:423 msgid "The name of the table we are customizing" msgstr "" #: sqlkit/misc/table_browser.py:424 #, python-format msgid "" "The best representation of a record \n" "as a combination of fields, e.g.: %(title)s %(year)s" msgstr "" #: sqlkit/misc/table_browser.py:427 msgid "The field that will be searched for when completion is used" msgstr "" #. TIP: filter page of the filter panel #: sqlkit/misc/words.py:7 msgid "filter" msgstr "filtro" #. TIP: output page of the filter panel #: sqlkit/misc/words.py:9 sqlkit/widgets/common/sqlfilter.py:259 #: sqlkit/widgets/common/sqlfilter.py:306 msgid "output" msgstr "salida" #. TIP: menu entry #: sqlkit/misc/words.py:11 msgid "File" msgstr "Archivo" #. TIP: menu entry #: sqlkit/misc/words.py:17 sqlkit/widgets/common/sqlwidget.py:796 msgid "Go" msgstr "Ir" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:19 msgid "opts" msgstr "opts" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:21 sqlkit/widgets/common/sqlwidget.py:797 msgid "Tools" msgstr "Herramientas" #. TIP: serach mode: #: sqlkit/misc/words.py:23 msgid "regexp" msgstr "regexp" #. TIP: search mode #: sqlkit/misc/words.py:25 msgid "start" msgstr "inicio" #. TIP: count records in sqledit #: sqlkit/misc/words.py:27 msgid "N.Rows" msgstr "N. Filas" #: sqlkit/misc/words.py:29 msgid "Indexes" msgstr "Indices" #: sqlkit/misc/words.py:30 msgid "Nullable" msgstr "Nullable" #: sqlkit/misc/words.py:31 msgid "Prim. Keys" msgstr "Llave Prim." #: sqlkit/misc/words.py:32 msgid "Default" msgstr "Defecto" #: sqlkit/misc/words.py:33 msgid "Foreign Key" msgstr "Llave Foránea" #: sqlkit/misc/words.py:34 msgid "filter_tables" msgstr "filter_tables" #. TIP: status bar #: sqlkit/widgets/common/completion.py:285 #: sqlkit/widgets/common/completion.py:1124 #, python-format msgid "search mode: %s" msgstr "modo busca: %s" #: sqlkit/widgets/common/completion.py:643 msgid "Show all info on this field, db type & Co." msgstr "Muestre toda la información de estos campos, db type & Co." #. TIP: menu enty in menu on right click on down arro in fkey completion widget #: sqlkit/widgets/common/completion.py:649 msgid "Show possible values: regexp - Ctrl-Enter" msgstr "Muestre posibles valores: regexp - Ctrl-Enter" #. TIP: yellow tip to menu entry in down arrow in completion widget #: sqlkit/widgets/common/completion.py:653 msgid "Show all values in the db that match your string" msgstr "Muestre todos los valores en la bd que concuerda con su cadena" #: sqlkit/widgets/common/completion.py:658 msgid "Show possible values, starting - Shift-Enter" msgstr "Muestre posibles valores, iniciando - Shift-Enter" #: sqlkit/widgets/common/completion.py:661 msgid "Show all values in the database starting with your string" msgstr "Muestre todos los valores en la base de datos comenzando con su cadena" #: sqlkit/widgets/common/completion.py:667 msgid "Edit the referenced table in 'table' mode" msgstr "Edite la tabla en referencia en el modo 'tabla'" #: sqlkit/widgets/common/completion.py:669 msgid "Edit the table from which admitted values are taken in 'table' mode" msgstr "Edite la tabla para la cual ha admitido valores tomados en el modo 'tabla'" #: sqlkit/widgets/common/completion.py:674 msgid "Edit the referenced table in 'mask' mode" msgstr "Edite la tabla en referencia en el modo 'mascara'" #: sqlkit/widgets/common/completion.py:676 msgid "Edit the table from which admitted values are taken in 'mask' mode" msgstr "" "Edite la tabla para la cual ha admitido valores tomados en el modo " "'mascara'" #: sqlkit/widgets/common/completion.py:783 msgid "Good match" msgstr "Buena coincidencia" #: sqlkit/widgets/common/completion.py:792 #: sqlkit/widgets/common/completion.py:798 msgid "No exact match, trying regexp completion" msgstr "No hay coincidencia exacta, tratando finalización regexp" #: sqlkit/widgets/common/dialogs.py:26 sqlkit/widgets/common/dialogs.py:94 #: sqlkit/widgets/table/table.py:1703 #, fuzzy msgid "All files" msgstr "" #: sqlkit/widgets/common/dialogs.py:100 msgid "Images" msgstr "" #: sqlkit/widgets/common/dialogs.py:106 msgid "Documents" msgstr "" #. TIP: possible new name for an uploaded file #: sqlkit/widgets/common/dialogs.py:123 msgid "Preferred new filename:" msgstr "" #: sqlkit/widgets/common/dialogs.py:124 msgid "If not empty, the file will be uploaded with this new name" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:107 #: sqlkit/widgets/common/sqlfilter.py:110 msgid "Match as regexp" msgstr "Coincidencia como regexp" #: sqlkit/widgets/common/sqlfilter.py:108 msgid "Match as LIKE, \"%\" automatically added" msgstr "Coincide como LIKE, \"%\" adicionado automáticamente" #: sqlkit/widgets/common/sqlfilter.py:109 msgid "Match as LIKE case insensitive, \"%\" automatically added" msgstr "Coincidencia a LIKE insensible al caso, \"%\" adicionado automáticamente " #: sqlkit/widgets/common/sqlfilter.py:111 msgid "Match as regexp, case insensitive" msgstr "Coincidencia como regexp, insensible al caso" #: sqlkit/widgets/common/sqlfilter.py:112 msgid "Negation of match as regexp" msgstr "Negación de coincidencia como regexp" #: sqlkit/widgets/common/sqlfilter.py:113 msgid "Negation of match case insensitive" msgstr "Negación de coincidencia insensible al caso" #: sqlkit/widgets/common/sqlfilter.py:114 msgid "Equal" msgstr "Igual" #: sqlkit/widgets/common/sqlfilter.py:115 msgid "Not equal" msgstr "No igual" #: sqlkit/widgets/common/sqlfilter.py:116 msgid "Greater then (after than)" msgstr "Mayor que (después de)" #: sqlkit/widgets/common/sqlfilter.py:117 msgid "Greater or equal" msgstr "Mayor o Igual" #: sqlkit/widgets/common/sqlfilter.py:118 msgid "Less than (before then)" msgstr "Menor que (antes de)" #: sqlkit/widgets/common/sqlfilter.py:119 msgid "Less than or equal" msgstr "Menos que o igual" #: sqlkit/widgets/common/sqlfilter.py:120 msgid "LIKE: a \"%\" means any char - case sensitive" msgstr "LIKE: un \"%\" significa cualquier carácter - sensible al caso" #: sqlkit/widgets/common/sqlfilter.py:121 msgid "Negation of LIKE" msgstr "Negación de LIKE" #: sqlkit/widgets/common/sqlfilter.py:122 msgid "As LIKE but case insensitive" msgstr "Como LIKE pero insensible al caso" #: sqlkit/widgets/common/sqlfilter.py:123 msgid "Negation of ILIKE" msgstr "Negación de ILIKE" #: sqlkit/widgets/common/sqlfilter.py:124 msgid "The boolean is True" msgstr "El booleano es True" #: sqlkit/widgets/common/sqlfilter.py:125 msgid "The boolean is False" msgstr "El booleano es False" #: sqlkit/widgets/common/sqlfilter.py:126 #, fuzzy msgid "The boolean is not True" msgstr "El valor no está disponible" #: sqlkit/widgets/common/sqlfilter.py:127 msgid "The boolean is not False" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:128 #, fuzzy msgid "The value is not set" msgstr "El valor está disponible" #: sqlkit/widgets/common/sqlfilter.py:129 msgid "The value is set" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:130 msgid "ID equality (don't follow foreign table)" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:221 msgid "Filter Panel" msgstr "Panel de Filtro" #: sqlkit/widgets/common/sqlfilter.py:230 msgid "Add filters for your query" msgstr "Adicione filtros para su consulta" #: sqlkit/widgets/common/sqlfilter.py:232 msgid "Result page for your query" msgstr "Página resultado para su consulta" #: sqlkit/widgets/common/sqlfilter.py:269 #, fuzzy msgid "Filter actions" msgstr "Panel de Filtros" #: sqlkit/widgets/common/sqlfilter.py:270 msgid "Reload from db" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:271 #, fuzzy msgid "Close the panel" msgstr "Panel de Filtros" #: sqlkit/widgets/common/sqlfilter.py:274 #, fuzzy msgid "Go to filter panel" msgstr "Panel de Filtros" #: sqlkit/widgets/common/sqlfilter.py:276 msgid "Go to output panel" msgstr "" #. TIP: status bar message #: sqlkit/widgets/common/sqlfilter.py:479 sqlkit/widgets/table/table.py:762 #: sqlkit/widgets/table/table.py:1039 #, python-format msgid "Total N. of records: %s" msgstr "Total N. de registros: %s" #: sqlkit/widgets/common/sqlfilter.py:801 #, python-format msgid "value '%s' cannot be used for field '%s'" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:868 #, python-format msgid "" "value '%s' does not seem a valid date and cannot be transformed into a " "date" msgstr "" "valor '%s' no parece una fecha valida y no puede ser transformada en una " "fecha" #. TIP: appears in the menu in the filter panel to add a second entry of the #. same field #: sqlkit/widgets/common/sqlfilter.py:1042 #, python-format msgid "Add a new filter on this field '%s'" msgstr "Adicione un nuevo filtro al campo '%s'" #: sqlkit/widgets/common/sqlfilter.py:1048 msgid "Use this filter" msgstr "Use este filtro" #: sqlkit/widgets/common/sqlfilter.py:1092 msgid "Click here to select an operator" msgstr "Presione aquí para seleccionar un operador" #: sqlkit/widgets/common/sqlfilter.py:1164 msgid "incomplete date format" msgstr "formato data incompleto" #: sqlkit/widgets/common/sqlwidget.py:358 #, fuzzy, python-format msgid "" "Sorry, problems connecting to remote db. Original error was: \n" "\n" "%s" msgstr "" "Error mientras escribe la base de datos: \n" "̉¿esta 'salvando como nuevo' un registro conocido?\n" "el error original fue: \n" "%s" #: sqlkit/widgets/common/sqlwidget.py:625 msgid "Hiding field is only supported for Tables" msgstr "Ocultar campos solo es soportado para Tablas" #: sqlkit/widgets/common/sqlwidget.py:792 msgid "Show all the differences that should be saved to database" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:802 msgid "Pending Differences" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:806 #, fuzzy msgid "Save current record" msgstr "Contar registros" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Filter panel" msgstr "Panel de Filtros" #: sqlkit/widgets/common/sqlwidget.py:810 #, fuzzy msgid "Add filter panel" msgstr "Panel de Filtros" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload" msgstr "Recargar" #: sqlkit/widgets/common/sqlwidget.py:811 #, fuzzy msgid "Reload from the database" msgstr "Cargar los datos también" #: sqlkit/widgets/common/sqlwidget.py:815 #, fuzzy msgid "Go to next record" msgstr "Contar registros" #: sqlkit/widgets/common/sqlwidget.py:816 msgid "Go to previous record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:823 msgid "Inspect widgets" msgstr "Inspeccionar widgets" #: sqlkit/widgets/common/sqlwidget.py:990 msgid "Saved" msgstr "Salvado" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1020 #, fuzzy, python-format msgid "" "Error while writing to the database. \n" "Original error was: \n" "%s" msgstr "" "Error mientras escribe la base de datos: \n" "̉¿esta 'salvando como nuevo' un registro conocido?\n" "el error original fue: \n" "%s" #. TIP: Error while saving into a table w/o permission #. TIP: reloading data from the database #: sqlkit/widgets/common/sqlwidget.py:1026 #: sqlkit/widgets/common/sqlwidget.py:1563 #, python-format msgid "" "A programming error was received from the backend:\n" "%s" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1035 #, python-format msgid "%s - Dirty objects: %s - New objects: %s" msgstr "%s - Objetos sucios: %s - Nuevos objetos: %s" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1041 #, python-format msgid "" "Error while writing to the database: \n" "are you 'saving as new' an already known record?\n" "original error was: \n" "%s" msgstr "" "Error mientras escribe la base de datos: \n" "̉¿esta 'salvando como nuevo' un registro conocido?\n" "el error original fue: \n" "%s" #: sqlkit/widgets/common/sqlwidget.py:1186 msgid "Discarding new obj" msgstr "Eliminar nuevo obj" #: sqlkit/widgets/common/sqlwidget.py:1223 #, python-format msgid "Char %s is not accepted in numeric context" msgstr "Carácter %s no es aceptado en el contexto numérico" #: sqlkit/widgets/common/sqlwidget.py:1281 msgid "record validation" msgstr "validación de registro" #: sqlkit/widgets/common/sqlwidget.py:1503 msgid "Unsaved differences" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1741 msgid "Sorry, export is implemented only for table view" msgstr "Disculpe, exportar está implementado solo para tablas vista" #: sqlkit/widgets/common/sqlwidget.py:2097 msgid "Property" msgstr "Propiedad" #: sqlkit/widgets/common/sqlwidget.py:2098 msgid "Value" msgstr "Valor" #: sqlkit/widgets/common/sqlwidget.py:2184 msgid "Save unsaved data?" msgstr "¿Salva dato no salvado?" #: sqlkit/widgets/common/sqlwidget.py:2187 msgid "Unsaved data" msgstr "Dato no salvado" #: sqlkit/widgets/common/sqlwidget.py:2209 msgid "State" msgstr "Estado" #: sqlkit/widgets/common/sqlwidget.py:2210 #: sqlkit/widgets/common/sqlwidget.py:2330 msgid "Field name" msgstr "Nombre del campo" #: sqlkit/widgets/common/sqlwidget.py:2211 msgid "Original value" msgstr "Valor original" #: sqlkit/widgets/common/sqlwidget.py:2212 msgid "Present value" msgstr "Valor presente" #: sqlkit/widgets/common/sqlwidget.py:2242 msgid "Modified" msgstr "Modificado" #: sqlkit/widgets/common/sqlwidget.py:2269 sqlkit/widgets/mask/mask.py:597 msgid "Deleted" msgstr "Borrado" #: sqlkit/widgets/common/sqlwidget.py:2279 msgid "New" msgstr "Nuevo" #: sqlkit/widgets/common/sqlwidget.py:2293 msgid "empty string: ''" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2295 msgid "NULL value" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2301 msgid "" "Errors are present in the record. \n" "Correct them now, to continue \n" "or delete the record" msgstr "" "Errores presentes en el registro. \n" "Corrija ahora, para continuar \n" "o borre el registro" #: sqlkit/widgets/common/sqlwidget.py:2302 msgid "Validation errors" msgstr "Validación de errores" #: sqlkit/widgets/common/sqlwidget.py:2331 msgid "Error" msgstr "Error" #: sqlkit/widgets/common/sqlwidget.py:2362 msgid "" "You can continue or go back editing. \n" "Read the following warnings to decide" msgstr "" "Puede continuar o regresar a editar. \n" "Lea la siguiente advertencia para decidir" #: sqlkit/widgets/common/sqlwidget.py:2363 msgid "Validation Warnings" msgstr "Validación de advertencias" #: sqlkit/widgets/mask/mask.py:258 #, fuzzy msgid "Add new record" msgstr "Ya está en el nuevo registro" #: sqlkit/widgets/mask/mask.py:262 msgid "Discard changes" msgstr "" #. TIP Modify menu entry in the mask to reread a single record from database #: sqlkit/widgets/mask/mask.py:264 #, fuzzy msgid "Refresh this record" msgstr "" "¿Borra este registro?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:265 #, fuzzy msgid "Reread this record from db" msgstr "" "¿Borra este registro?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:268 #, fuzzy msgid "Delete this record" msgstr "" "¿Borra este registro?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:342 sqlkit/widgets/table/table.py:908 msgid "Already at new record" msgstr "Ya está en el nuevo registro" #: sqlkit/widgets/mask/mask.py:362 #, python-format msgid "New record %s" msgstr "Nuevo registro %s" #. TIP message issued when a refresh is done on a deleted record #: sqlkit/widgets/mask/mask.py:405 msgid "The record is no longer present in the database" msgstr "" #: sqlkit/widgets/mask/mask.py:473 sqlkit/widgets/table/table.py:838 msgid "Nothing to save" msgstr "Nada para salvar" #: sqlkit/widgets/mask/mask.py:523 #, python-format msgid "Primary key (%s) didn't change. Refusing to save as new" msgstr "Llave primaria (%s) no cambia. Se niega salvarla como nueva" #: sqlkit/widgets/mask/mask.py:537 msgid "Do you want to copy all data to a new record?" msgstr "" #: sqlkit/widgets/mask/mask.py:585 sqlkit/widgets/table/table.py:982 #, python-format msgid "" "Delete this record?\n" "(%s)" msgstr "" "¿Borra este registro?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:654 msgid "No record present" msgstr "No hay registro presente" #: sqlkit/widgets/mask/mask.py:677 msgid "Already last record" msgstr "Último Registro" #: sqlkit/widgets/mask/mask.py:679 msgid "Already first record" msgstr "Primer registro" #: sqlkit/widgets/mask/miniwidgets.py:695 #, python-format msgid "A file with name '%s' already exists. Overwrite?" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:696 msgid "Upload name conflict" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:888 #, python-format msgid "'%s' may have an invalid value: try completion on that" msgstr "'%s' puede tener un valor inválido: trate de completarlo" #: sqlkit/widgets/table/columns.py:208 msgid "Editing canceled. Restoring original value" msgstr "Edición cancelada. Restaurando el valor original" #: sqlkit/widgets/table/columns.py:848 #, python-format msgid "Add a filter on '%s'" msgstr "Adicionar un filtro a '%s' " #: sqlkit/widgets/table/columns.py:858 msgid "" "Sort on this column reloading from the database, you can used 's' to sort" " locally" msgstr "" #: sqlkit/widgets/table/columns.py:861 msgid "Sort on this column locally (w/o touching the database)" msgstr "" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:871 msgid "Hide this column" msgstr "Ocultar esta columna" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:878 msgid "Create total" msgstr "Crear total" #. TIP: column menu total #: sqlkit/widgets/table/columns.py:888 #, python-format msgid "Subtotal on %s" msgstr "Subtotal en %s" #. TIP: column menu opt #. TIP: modify menu entry #: sqlkit/widgets/table/columns.py:910 sqlkit/widgets/table/columns.py:1293 #: sqlkit/widgets/table/table.py:287 msgid "Show field" msgstr "Mostrar campo" #: sqlkit/widgets/table/columns.py:911 sqlkit/widgets/table/columns.py:1294 #: sqlkit/widgets/table/table.py:288 msgid "Hide field" msgstr "Ocultar campo" #: sqlkit/widgets/table/columns.py:938 msgid "day" msgstr "día" #: sqlkit/widgets/table/columns.py:938 msgid "week" msgstr "semana" #: sqlkit/widgets/table/columns.py:938 msgid "month" msgstr "mes" #: sqlkit/widgets/table/columns.py:939 msgid "quarter" msgstr "trimestre" #: sqlkit/widgets/table/columns.py:939 msgid "year" msgstr "año" #: sqlkit/widgets/table/columns.py:942 #, python-format msgid "Subtotals by %s" msgstr "Subtotal por %s" #: sqlkit/widgets/table/columns.py:1267 msgid "Right click on this table to show the column again" msgstr "" #: sqlkit/widgets/table/columns.py:1287 sqlkit/widgets/table/table.py:301 msgid "Export" msgstr "Exportar" #: sqlkit/widgets/table/columns.py:1287 msgid "Export these data into csv format" msgstr "" #: sqlkit/widgets/table/columns.py:1292 msgid "Adapt width of columns to data" msgstr "" #: sqlkit/widgets/table/columns.py:1321 #, python-format msgid "View '%s' in a Mask" msgstr "Ver '%s' en una Máscara" #: sqlkit/widgets/table/table.py:274 msgid "Duplicate" msgstr "" #: sqlkit/widgets/table/table.py:275 msgid "Create a new row as a duplicate of this one" msgstr "" #: sqlkit/widgets/table/table.py:277 msgid "New child row" msgstr "" #: sqlkit/widgets/table/table.py:278 msgid "Create e new row as child of this one" msgstr "" #: sqlkit/widgets/table/table.py:292 msgid "View this record in a Mask" msgstr "Ver este registro en una Máscara" #: sqlkit/widgets/table/table.py:293 msgid "View this ForeignKey in a Mask" msgstr "Ver esta llave foránea en una Máscara" #: sqlkit/widgets/table/table.py:294 msgid "Upload Image" msgstr "" #: sqlkit/widgets/table/table.py:295 msgid "Upload File" msgstr "" #: sqlkit/widgets/table/table.py:296 msgid "Delete File/Image" msgstr "" #: sqlkit/widgets/table/table.py:297 msgid "Show File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save file locally" msgstr "" #: sqlkit/widgets/table/table.py:528 sqlkit/widgets/table/table.py:542 #: sqlkit/widgets/table/table.py:554 msgid "Insertion of new records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:558 msgid "Update of records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:570 msgid "Deletion of records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:583 msgid "browsing of new records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:731 msgid "no current obj: maybe no record has yet been edited" msgstr "no hay obj actual: tal vez ningún registro ha sido editado aún" #. TIP: when saving m2m, we delay till leader record will be saved #: sqlkit/widgets/table/table.py:869 msgid "delaying saving when main record will be saved" msgstr "retardando salvar mientras el registro principal sea salvado" #: sqlkit/widgets/table/table.py:879 msgid "Record has NOT been saved" msgstr "Registro NO ha sido salvado" #: sqlkit/widgets/table/table.py:978 msgid "No record selected" msgstr "" #: sqlkit/widgets/table/table.py:1137 msgid "Value is not valid, trying completion" msgstr "El valor no es valido, trate de completar" #. TIP: check in input field length %s %s -> field_name length #: sqlkit/widgets/table/table.py:1170 #, python-format msgid "Input exceeded max %s length (%s)" msgstr "Entrada excede el máximo %s tamaño (%s)" #: sqlkit/widgets/table/table.py:1284 msgid "Unsaved data prevent opening a Mask to show the (unsaved) record" msgstr "" #: sqlkit/widgets/table/table.py:1407 #, python-format msgid "" "A file with name '%s' already exists.\n" "Overwrite?" msgstr "" #: sqlkit/widgets/table/table.py:1408 msgid "Upload name duplication" msgstr "" #: sqlkit/widgets/table/table.py:1629 msgid "Export in csv file" msgstr "Exportar a archivo csv" #: sqlkit/widgets/table/table.py:1702 #, fuzzy msgid "Csv files" msgstr "" #: sqlkit/widgets/table/tablewidgets.py:34 #, python-format msgid "No current obj for field '%s' in %s" msgstr "No hay objeto actual para el campo '%s' en %s" sqlkit-0.9.5/sqlkit/locale/es/LC_MESSAGES/sqlkit.mo0000644000175000017500000004003011714210425021172 0ustar sandrosandro  (689o005 A`#u,2*G r @ +,%X ~ &$-7 e oy()BC"*fnV cj!}   /  1<Mg lw)(: -!+K+[ 7&6!Fhqx "  # (0Yk ~ 6 < G7S12DM] o}*99 0E v,*+07KQY%P;(N/w d @q  $ !!&!>!X!h!|!!!%!!!""K/"*{"".""" ""#3#P#U#\#d#v#}## #(#K##$ 9$Z$_$d$'&,'9K'E'0'0'5-(c((&(!(())!&)H) a)k)4)+) )G)8*G*f*$w*+*%* * *++ "+/+@+\+&d+++-+ + ++1,/9,Li,J,1-3-9-y?-U-..!/.Q.b.j.z./...... //// 5/C/,X/(/:// 000>0)1+/1[1=p111 111H23M22,2 2 22 223%3,;3h3 n3|3-39334"4+4<4E4`4o44464 44;4 85B5^5g5"v51525 5 66 .6<6U6]6;m696F6>*7 i71w7-7767<#87`8Q88899"9(9Y?9999h9;D:(:/:: : :;;;@; <$< D< P< ]<j<<<<<<<<)=9='R=!z==O=*>/><4>q>> >>>@>>> ? ?"?)?8? ??(L?Nu?$?"? @@ You can indicate the database you want to connect to using an URI in the form as: postgres://localhost/dbname sqlite:///dbname or using password and username: mysql://sandro:pass@host/dbname %s - Dirty objects: %s - New objects: %s'%s' may have an invalid value: try completion on thatA complete demo of all the features of the sqlkit packageA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataAdd a filter on '%s'Add a new filter on this field '%s'Add filters for your queryAlready at new recordAlready first recordAlready last recordAs LIKE but case insensitiveAttempting to connect to %sBlankCast blank into NULLChar %s is not accepted in numeric contextClick here to select an operatorCollapse rowConfigure the fields: labels, tooltip, completion, search fieldConnected to %sConnection setupCount recordsCount records in all tablesCreate a new row as a duplicate of this oneCreate e new row as child of this oneCreate totalDatabaseDefaultDelete File/ImageDelete imageDelete the imageDelete this record? (%s)DeletedDeletion of records is disabled. SorryDiscard changesDiscarding new objDo you want to copy all data to a new record?DocumentsDuplicateEdit Sqlkit FieldsEdit the referenced table in 'mask' modeEdit the referenced table in 'table' modeEdit the table from which admitted values are taken in 'mask' modeEdit the table from which admitted values are taken in 'table' modeEditing canceled. Restoring original valueEqualErrorError while writing to the database: are you 'saving as new' an already known record? original error was: %sErrors are present in the record. Correct them now, to continue or delete the recordExportExport in csv fileExport these data into csv formatField nameFileFilter PanelFilter panelFind allowed values (Control-Enter/Shift-Enter)Foreign KeyGoGo to output panelGo to previous recordGood matchGreater or equalGreater then (after than)HelpHide fieldHide this columnHiding field is only supported for TablesID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameIf you write a wrong hostname the application may hang some seconds till the natural network timeout. Uncheck the flag on the right ti disable this featureImage viewerImagesIndexesInfo on available backend: postgres, mysql...Input exceeded max %s length (%s)Insertion of new records is disabled. SorryInspect widgetsLIKE: a "%" means any char - case sensitiveLess than (before then)Less than or equalLoad dataLoad the data as wellMaskMatch as LIKE case insensitive, "%" automatically addedMatch as LIKE, "%" automatically addedMatch as regexpMatch as regexp, case insensitiveModifiedModifyN.RowsNULL valueNegation of ILIKENegation of LIKENegation of match as regexpNegation of match case insensitiveNewNew child rowNew record %sNo current obj for field '%s' in %sNo exact match, trying regexp completionNo record presentNo record selectedNot equalNothing to saveNullableOpen separate image viewerOriginal valuePending DifferencesPreferred new filename:Present valuePress Esc to close or double click a date to select itPrim. KeysPrimary KeyPrimary key (%s) didn't change. Refusing to save as newPropertyRecord has NOT been savedReloadReload from dbResult page for your queryRight click on the image area to upload an imageRight click on this table to show the column againRun DemoSave File/ImageSave file locallySave image asSave unsaved data?SavedShow File/ImageShow all info on this field, db type & Co.Show all the differences that should be saved to databaseShow all values in the database starting with your stringShow all values in the db that match your stringShow fieldShow possible values, starting - Shift-EnterShow possible values: regexp - Ctrl-EnterShow/Hide image nameShow/Hide primary key if the key is numericSorry, export is implemented only for table viewSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallySqledit manual pageStateSubtotal on %sSubtotals by %sTableTable reflectionThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sThe boolean is FalseThe boolean is TrueThe boolean is not FalseThe demo was not found, if you know where it is, run it manually: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseThe value is setToolToolsTotal N. of records: %sTry continuosly to connect. Nice and usefull but may cause temporary blocks if you write an inexistent hostname Unsaved dataUnsaved data prevent opening a Mask to show the (unsaved) recordUnsaved differencesUpdate of records is disabled. SorryUpload FileUpload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUse this filterValidation WarningsValidation errorsValueValue cannot be NULLValue is not valid, trying completionView '%s' in a MaskView this ForeignKey in a MaskView this record in a MaskWrong time formatYou can continue or go back editing. Read the following warnings to decidebrowsing of new records is disabled. Sorrydaydelaying saving when main record will be savedempty string: ''filterfilter_tablesincomplete date formatmonthno current obj: maybe no record has yet been editedoptsoutputquarterrecord validationregexpsearch mode: %sstartupload imagevalue '%s' cannot be used for field '%s'value '%s' does not seem a valid date and cannot be transformed into a datevalue is not Decimal nor string: %svalue is not date nor string: %sweekyearProject-Id-Version: 1.0 Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2009-07-02 12:34+0200 PO-Revision-Date: 2011-03-13 00:10+0100 Last-Translator: Juan Fernando Jaramillo Botero Language-Team: Español Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.4 Puede indicar la base de datos a la que quiera conectarse usando una URI de la forma: postgres://localhost/nombrebd sqlite:///nombrebd o usando clave y usuario: mysql://sandro:clave@host/nombrebd %s - Objetos sucios: %s - Nuevos objetos: %s'%s' puede tener un valor inválido: trate de completarloUna completa demostración de todas las cualidades del paquete sqlkitA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataAdicionar un filtro a '%s' Adicione un nuevo filtro al campo '%s'Adicione filtros para su consultaYa está en el nuevo registroPrimer registroÚltimo RegistroComo LIKE pero insensible al casoIntentando conectar a %sEn blancoCambiar blancos a NULLCarácter %s no es aceptado en el contexto numéricoPresione aquí para seleccionar un operadorCollapse rowConfigure los campos: marcadores, tooltip, finalización, buscar campoConectado a %sConfiguración de la conexiónContar registrosContar registros en todas las tablasCreate a new row as a duplicate of this oneCreate e new row as child of this oneCrear totalBase de DatosDefectoDelete File/ImageDelete imageDelete the image¿Borra este registro? (%s)BorradoDeletion of records is disabled. SorryDiscard changesEliminar nuevo objDo you want to copy all data to a new record?DocumentsDuplicateEdite campos de SqlkitEdite la tabla en referencia en el modo 'mascara'Edite la tabla en referencia en el modo 'tabla'Edite la tabla para la cual ha admitido valores tomados en el modo 'mascara'Edite la tabla para la cual ha admitido valores tomados en el modo 'tabla'Edición cancelada. Restaurando el valor originalIgualErrorError mientras escribe la base de datos: ̉¿esta 'salvando como nuevo' un registro conocido? el error original fue: %sErrores presentes en el registro. Corrija ahora, para continuar o borre el registroExportarExportar a archivo csvExport these data into csv formatNombre del campoArchivoPanel de FiltroPanel de FiltrosFind allowed values (Control-Enter/Shift-Enter)Llave ForáneaIrGo to output panelGo to previous recordBuena coincidenciaMayor o IgualMayor que (después de)AyudaOcultar campoOcultar esta columnaOcultar campos solo es soportado para TablasID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameSi escribe un nombre de host errado la aplicación puede colgarse algunos segundos hasta el timeout natural de la red. Des selecciones la bandera a la derecha eso inhabilitará esta propiedadImage viewerImagesIndicesInformación sobre plataformas disponibles: postgres, mysql...Entrada excede el máximo %s tamaño (%s)Insertion of new records is disabled. SorryInspeccionar widgetsLIKE: un "%" significa cualquier carácter - sensible al casoMenor que (antes de)Menos que o igualCargar datosCargar los datos tambiénMascaraCoincidencia a LIKE insensible al caso, "%" adicionado automáticamente Coincide como LIKE, "%" adicionado automáticamenteCoincidencia como regexpCoincidencia como regexp, insensible al casoModificadoModificarN. FilasNULL valueNegación de ILIKENegación de LIKENegación de coincidencia como regexpNegación de coincidencia insensible al casoNuevoNew child rowNuevo registro %sNo hay objeto actual para el campo '%s' en %sNo hay coincidencia exacta, tratando finalización regexpNo hay registro presenteNo record selectedNo igualNada para salvarNullableOpen separate image viewerValor originalPending DifferencesPreferred new filename:Valor presentePress Esc to close or double click a date to select itLlave Prim.Llave primariaLlave primaria (%s) no cambia. Se niega salvarla como nuevaPropiedadRegistro NO ha sido salvadoRecargarReload from dbPágina resultado para su consultaRight click on the image area to upload an imageRight click on this table to show the column againEjecutar DemoSave File/ImageSave file locallySave image as¿Salva dato no salvado?SalvadoShow File/ImageMuestre toda la información de estos campos, db type & Co.Show all the differences that should be saved to databaseMuestre todos los valores en la base de datos comenzando con su cadenaMuestre todos los valores en la bd que concuerda con su cadenaMostrar campoMuestre posibles valores, iniciando - Shift-EnterMuestre posibles valores: regexp - Ctrl-EnterShow/Hide image nameMostrar/Ocultar llave primaria si la llave es numericaDisculpe, exportar está implementado solo para tablas vistaSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallyManual de SqleditEstadoSubtotal en %sSubtotal por %sTablaReflexión de la TablaThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sEl booleano es FalseEl booleano es TrueThe boolean is not FalseLa demostración no fue encontrada, si conoce su localización, ejecute esta manualmente: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseThe value is setHerramientaHerramientasTotal N. de registros: %sTrate continuamente conectarse. Bueno y útil pero puede causar bloqueos temporales si usted escribe un nombre de host inexistentes Dato no salvadoUnsaved data prevent opening a Mask to show the (unsaved) recordUnsaved differencesUpdate of records is disabled. SorryUpload FileUpload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUse este filtroValidación de advertenciasValidación de erroresValorValue cannot be NULLEl valor no es valido, trate de completarVer '%s' en una MáscaraVer esta llave foránea en una MáscaraVer este registro en una MáscaraFormato di tempo erratoPuede continuar o regresar a editar. Lea la siguiente advertencia para decidirbrowsing of new records is disabled. Sorrydíaretardando salvar mientras el registro principal sea salvadoempty string: ''filtrofilter_tablesformato data incompletomesno hay obj actual: tal vez ningún registro ha sido editado aúnoptssalidatrimestrevalidación de registroregexpmodo busca: %sinicioupload imagevalue '%s' cannot be used for field '%s'valor '%s' no parece una fecha valida y no puede ser transformada en una fechael valor no es Decimal ni cadena: %sel valor no es fecha ni cadena: %ssemanaañosqlkit-0.9.5/sqlkit/locale/pl/0000755000175000017500000000000011714210425015550 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/pl/LC_MESSAGES/0000755000175000017500000000000011714210425017335 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/pl/LC_MESSAGES/sqlkit.po0000644000175000017500000007175511714210425021223 0ustar sandrosandro# Polish translations for PROJECT. # Copyright (C) 2010 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , 2010. # msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2010-05-20 14:11+0200\n" "PO-Revision-Date: 2011-03-13 00:08+0100\n" "Last-Translator: Sebastian Majka \n" "Language-Team: pl \n" "Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && " "(n%100<10 || n%100>=20) ? 1 : 2)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.4\n" #: bin/sqledit.py:87 msgid "Connection setup" msgstr "Parametry połączenia" #: bin/sqledit.py:90 msgid "" "\n" " You can indicate the database you want to connect to\n" " using an URI in the form as:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " or using password and username:\n" " mysql://sandro:pass@host/dbname\n" " " msgstr "" "\n" " Możesz wskazać bazę danych, z którą chcesz nawiązać połączenie\n" " używając następujących form adresu:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " lub podając nazwę użytkownika i hasło:\n" " mysql://sandro:pass@host/dbname\n" " " #: bin/sqledit.py:117 msgid "Run Demo" msgstr "Włącz demonstrację" #: bin/sqledit.py:140 msgid "" "Info on available backend:\n" "postgres, mysql..." msgstr "" "Informacja o dostępności silników bazodanowych:\n" "postgres, mysql" #: bin/sqledit.py:141 msgid "Sqledit manual page" msgstr "Przewodnik po Sqledit" #: bin/sqledit.py:146 msgid "" "Try continuosly to connect.\n" " Nice and usefull but may cause temporary blocks\n" " if you write an inexistent hostname\n" " " msgstr "" "Nie przestawaj nawiązywać połączenia\n" " Jest to użyteczne, lecz może powodować chwilowe przestoje,\n" " jeśli określisz nieistniejący adres serwera.\n" #: bin/sqledit.py:160 msgid "" "If you write a wrong hostname\n" "the application may hang some seconds till the natural network timeout. \n" "Uncheck the flag on the right ti disable this feature" msgstr "" "Jeśli użyjesz nieprawidłowego adresu\n" "aplikacja może zawiesić się na krótki czas poszukiwań w sieci (timeout)." " \n" "Odznacz flagę po prawej by zablokować tę funkcję" #: bin/sqledit.py:187 #, python-format msgid "Attempting to connect to %s" msgstr "Trwa próba połączenia do %s" #: bin/sqledit.py:190 #, python-format msgid "Connected to %s" msgstr "Połączono z %s" #: bin/sqledit.py:221 msgid "A complete demo of all the features of the sqlkit package" msgstr "Pełna demonstracja możliwości pakietu sqlkit" #: bin/sqledit.py:222 msgid "" "The demo was not found, if you know where it is,\n" "run it manually: python demo.py" msgstr "" "Nie znaleziono demonstracji, jeśli wiesz gdzie jest,\n" "uruchom ją ręcznie: python demo.py" #: sqlkit/exc.py:48 #, fuzzy, python-format msgid "Field '%s' cannot be NULL" msgstr "pole %s nie może być puste (NULL)" #: sqlkit/exc.py:60 #, fuzzy, python-format msgid "Field '%s' cannot have value %s" msgstr "pole %s nie może mieć wartości %s" #: sqlkit/fields.py:979 #, python-format msgid "value is not Decimal nor string: %s" msgstr "wartość nie jest liczbowa ani napisem: %s" #: sqlkit/fields.py:1055 #, python-format msgid "value is not date nor string: %s" msgstr "wartość nie jest datą ani napisem: %s" #: sqlkit/fields.py:1174 sqlkit/widgets/mask/miniwidgets.py:430 #, python-format msgid "Wrong date format: %s" msgstr "Nieprawidłowy format daty: %s" #: sqlkit/fields.py:1184 msgid "Wrong time format" msgstr "Niepoprawny format czasu" #: sqlkit/fields.py:1336 #, fuzzy msgid "Value cannot be NULL" msgstr "wartość '%s' nie może być użyta dla pola '%s'" #: sqlkit/fields.py:1340 #, fuzzy, python-format msgid "Value '%s' is not accepted" msgstr "Wartość nie została ustawiona" #: sqlkit/db/proxy.py:219 #, python-format msgid "Table %s doesn't have a primary key, editing is not possible" msgstr "Tabela %s nie ma klucza głównego, edytowanie nie jest możliwe" #: sqlkit/layout/dateedit.py:190 msgid "Press Esc to close or double click a date to select it" msgstr "" #: sqlkit/layout/fk_entry.py:25 msgid "Find allowed values (Control-Enter/Shift-Enter)" msgstr "" #: sqlkit/layout/image_widget.py:142 #, fuzzy msgid "" "Right click on the image area\n" " to upload an image" msgstr "Użyj prawego klawisza by ponownie pokazać kolumnę " #: sqlkit/layout/image_widget.py:273 sqlkit/widgets/table/table.py:1395 msgid "Upload image" msgstr "" #: sqlkit/layout/image_widget.py:273 msgid "Upload or modify an image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete the image" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Image viewer" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Open separate image viewer" msgstr "" #: sqlkit/layout/image_widget.py:276 msgid "Save image as" msgstr "" #: sqlkit/layout/image_widget.py:279 msgid "Show/Hide image name" msgstr "" #: sqlkit/layout/image_widget.py:317 msgid "upload image" msgstr "" #: sqlkit/misc/datetools.py:152 msgid "Incomplete date format" msgstr "Niepełny format daty" #: sqlkit/misc/table_browser.py:135 msgid "Database" msgstr "Baza danych" #. TIP: menu entry #: sqlkit/misc/table_browser.py:136 sqlkit/misc/words.py:13 #: sqlkit/widgets/common/sqlwidget.py:795 msgid "Modify" msgstr "Modyfikuj" #: sqlkit/misc/table_browser.py:137 msgid "Tool" msgstr "Narzędzia" #. TIP: menu entry #: sqlkit/misc/table_browser.py:138 sqlkit/misc/words.py:15 #: sqlkit/widgets/common/sqlwidget.py:798 msgid "Help" msgstr "Pomoc" #: sqlkit/misc/table_browser.py:141 msgid "Count records" msgstr "Policz rekordy" #: sqlkit/misc/table_browser.py:141 msgid "Count records in all tables" msgstr "Policz rekordy we wszystkich tabelach" #: sqlkit/misc/table_browser.py:144 #, fuzzy msgid "" "Configure the fields: \n" "labels, tooltip, completion, search field" msgstr "" "Konfiguruj pola: \n" "etykiety, podpowiedź, uzupełnianie, obszar poszukiwań" #: sqlkit/misc/table_browser.py:147 msgid "Edit Sqlkit Fields" msgstr "Edytuj pola Sqlkit" #: sqlkit/misc/table_browser.py:150 msgid "Primary Key" msgstr "Klucz główny" #: sqlkit/misc/table_browser.py:151 msgid "Show/Hide primary key if the key is numeric" msgstr "Pokaż/Ukryj główny klucz jeśli jest numeryczny" #: sqlkit/misc/table_browser.py:152 msgid "Load data" msgstr "Ładuj dane" #: sqlkit/misc/table_browser.py:153 #, fuzzy msgid "Load the data as well" msgstr "Wczytaj również dane" #: sqlkit/misc/table_browser.py:154 msgid "Blank" msgstr "Luki" #: sqlkit/misc/table_browser.py:154 msgid "Cast blank into NULL" msgstr "Traktuj luki jako NULL" #: sqlkit/misc/table_browser.py:262 msgid "Mask" msgstr "tryb Mask" #: sqlkit/misc/table_browser.py:266 msgid "Table" msgstr "tryb Table" #: sqlkit/misc/table_browser.py:272 msgid "Collapse row" msgstr "" #: sqlkit/misc/table_browser.py:276 msgid "Table reflection" msgstr "tryb Table reflection" #: sqlkit/misc/table_browser.py:423 msgid "The name of the table we are customizing" msgstr "" #: sqlkit/misc/table_browser.py:424 #, python-format msgid "" "The best representation of a record \n" "as a combination of fields, e.g.: %(title)s %(year)s" msgstr "" #: sqlkit/misc/table_browser.py:427 msgid "The field that will be searched for when completion is used" msgstr "" #. TIP: filter page of the filter panel #: sqlkit/misc/words.py:7 msgid "filter" msgstr "filtr" #. TIP: output page of the filter panel #: sqlkit/misc/words.py:9 sqlkit/widgets/common/sqlfilter.py:259 #: sqlkit/widgets/common/sqlfilter.py:306 msgid "output" msgstr "plik wynikowy" #. TIP: menu entry #: sqlkit/misc/words.py:11 msgid "File" msgstr "Plik" #. TIP: menu entry #: sqlkit/misc/words.py:17 sqlkit/widgets/common/sqlwidget.py:796 msgid "Go" msgstr "Idź" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:19 msgid "opts" msgstr "opcje" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:21 sqlkit/widgets/common/sqlwidget.py:797 msgid "Tools" msgstr "Narzędzia" #. TIP: serach mode: #: sqlkit/misc/words.py:23 msgid "regexp" msgstr "wyrażenie regularne" #. TIP: search mode #: sqlkit/misc/words.py:25 msgid "start" msgstr "start" #. TIP: count records in sqledit #: sqlkit/misc/words.py:27 msgid "N.Rows" msgstr "Liczba rekordów" #: sqlkit/misc/words.py:29 msgid "Indexes" msgstr "Indeksy" #: sqlkit/misc/words.py:30 msgid "Nullable" msgstr "" #: sqlkit/misc/words.py:31 msgid "Prim. Keys" msgstr "Klucze główne" #: sqlkit/misc/words.py:32 msgid "Default" msgstr "Domyślne" #: sqlkit/misc/words.py:33 msgid "Foreign Key" msgstr "Klucz obcy" #: sqlkit/misc/words.py:34 msgid "filter_tables" msgstr "filtruj _tables" #. TIP: status bar #: sqlkit/widgets/common/completion.py:285 #: sqlkit/widgets/common/completion.py:1124 #, python-format msgid "search mode: %s" msgstr "tryb szukania: %s" #: sqlkit/widgets/common/completion.py:643 msgid "Show all info on this field, db type & Co." msgstr "Pokaż wszystkie informacje o polu, typie bazy i podobnych" #. TIP: menu enty in menu on right click on down arro in fkey completion widget #: sqlkit/widgets/common/completion.py:649 msgid "Show possible values: regexp - Ctrl-Enter" msgstr "Pokaż możliwe wartości: wyrażenie regularne - Ctrl-Enter" #. TIP: yellow tip to menu entry in down arrow in completion widget #: sqlkit/widgets/common/completion.py:653 msgid "Show all values in the db that match your string" msgstr "Pokaż wszystkie pola, które zawierają twój napis" #: sqlkit/widgets/common/completion.py:658 msgid "Show possible values, starting - Shift-Enter" msgstr "Pokaż możliwe wartości, rozpoczynanie - Shift-Enter" #: sqlkit/widgets/common/completion.py:661 msgid "Show all values in the database starting with your string" msgstr "Pokaż wszystkie wartości w bazie danych z prefiksem" #: sqlkit/widgets/common/completion.py:667 msgid "Edit the referenced table in 'table' mode" msgstr "Edytuj poleconą tabelę w trybie Table" #: sqlkit/widgets/common/completion.py:669 #, fuzzy msgid "Edit the table from which admitted values are taken in 'table' mode" msgstr "Edytuj tabelę w trybie Table" #: sqlkit/widgets/common/completion.py:674 msgid "Edit the referenced table in 'mask' mode" msgstr "Edytuj poleconą tabelę w trybie Mask" #: sqlkit/widgets/common/completion.py:676 #, fuzzy msgid "Edit the table from which admitted values are taken in 'mask' mode" msgstr "Edytuj tabelę w trybie Mode" #: sqlkit/widgets/common/completion.py:783 msgid "Good match" msgstr "Dobre dopasowanie" #: sqlkit/widgets/common/completion.py:792 #: sqlkit/widgets/common/completion.py:798 msgid "No exact match, trying regexp completion" msgstr "Niedokładne dopasowanie, spróbuj wyrażenia regularnego" #: sqlkit/widgets/common/dialogs.py:26 sqlkit/widgets/common/dialogs.py:94 #: sqlkit/widgets/table/table.py:1703 msgid "All files" msgstr "Wszystkie pliki" #: sqlkit/widgets/common/dialogs.py:100 msgid "Images" msgstr "" #: sqlkit/widgets/common/dialogs.py:106 msgid "Documents" msgstr "" #. TIP: possible new name for an uploaded file #: sqlkit/widgets/common/dialogs.py:123 msgid "Preferred new filename:" msgstr "" #: sqlkit/widgets/common/dialogs.py:124 msgid "If not empty, the file will be uploaded with this new name" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:107 #: sqlkit/widgets/common/sqlfilter.py:110 msgid "Match as regexp" msgstr "wyrażenie regularne" #: sqlkit/widgets/common/sqlfilter.py:108 msgid "Match as LIKE, \"%\" automatically added" msgstr "LIKE, \"%\" dodane automatycznie" #: sqlkit/widgets/common/sqlfilter.py:109 msgid "Match as LIKE case insensitive, \"%\" automatically added" msgstr "LIKE, \"%\" dodane automatycznie (niewrażliwe na wielkość liter)" #: sqlkit/widgets/common/sqlfilter.py:111 msgid "Match as regexp, case insensitive" msgstr "wyrażenie regularne (niewrażliwe na wielkość liter)" #: sqlkit/widgets/common/sqlfilter.py:112 msgid "Negation of match as regexp" msgstr "negacja wyrażenia regularnego" #: sqlkit/widgets/common/sqlfilter.py:113 #, fuzzy msgid "Negation of match case insensitive" msgstr "wrażliwe na wielkość liter" #: sqlkit/widgets/common/sqlfilter.py:114 msgid "Equal" msgstr "równe" #: sqlkit/widgets/common/sqlfilter.py:115 msgid "Not equal" msgstr "nie-równe" #: sqlkit/widgets/common/sqlfilter.py:116 msgid "Greater then (after than)" msgstr "większe niż" #: sqlkit/widgets/common/sqlfilter.py:117 msgid "Greater or equal" msgstr "większe lub równe" #: sqlkit/widgets/common/sqlfilter.py:118 msgid "Less than (before then)" msgstr "mniejsze niż " #: sqlkit/widgets/common/sqlfilter.py:119 msgid "Less than or equal" msgstr "mniejsze lub równe" #: sqlkit/widgets/common/sqlfilter.py:120 msgid "LIKE: a \"%\" means any char - case sensitive" msgstr "LIKE: \"%\" oznacza jakikolwiek znak- wrażliwe na wielkość liter" #: sqlkit/widgets/common/sqlfilter.py:121 msgid "Negation of LIKE" msgstr "zaprzeczenie LIKE" #: sqlkit/widgets/common/sqlfilter.py:122 msgid "As LIKE but case insensitive" msgstr "Jak LIKE lecz niewrażliwy na wielkość liter" #: sqlkit/widgets/common/sqlfilter.py:123 msgid "Negation of ILIKE" msgstr "negacja ILIKE" #: sqlkit/widgets/common/sqlfilter.py:124 msgid "The boolean is True" msgstr "Wartość logiczna: Prawda" #: sqlkit/widgets/common/sqlfilter.py:125 msgid "The boolean is False" msgstr "Wartość logiczna: Fałsz" #: sqlkit/widgets/common/sqlfilter.py:126 msgid "The boolean is not True" msgstr "Wartość logiczna nie jest Prawdą" #: sqlkit/widgets/common/sqlfilter.py:127 msgid "The boolean is not False" msgstr "Wartość logiczna nie jest Fałszem" #: sqlkit/widgets/common/sqlfilter.py:128 msgid "The value is not set" msgstr "Wartość nie została ustawiona" #: sqlkit/widgets/common/sqlfilter.py:129 msgid "The value is set" msgstr "Wartość została ustawiona" #: sqlkit/widgets/common/sqlfilter.py:130 msgid "ID equality (don't follow foreign table)" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:221 msgid "Filter Panel" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlfilter.py:230 msgid "Add filters for your query" msgstr "Dodaj filtr do zapytania" #: sqlkit/widgets/common/sqlfilter.py:232 msgid "Result page for your query" msgstr "Strona wynikowa dla twojego zapytania" #: sqlkit/widgets/common/sqlfilter.py:269 #, fuzzy msgid "Filter actions" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlfilter.py:270 msgid "Reload from db" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:271 #, fuzzy msgid "Close the panel" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlfilter.py:274 #, fuzzy msgid "Go to filter panel" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlfilter.py:276 msgid "Go to output panel" msgstr "" #. TIP: status bar message #: sqlkit/widgets/common/sqlfilter.py:479 sqlkit/widgets/table/table.py:762 #: sqlkit/widgets/table/table.py:1039 #, python-format msgid "Total N. of records: %s" msgstr "Łączna liczba rekordów: %s" #: sqlkit/widgets/common/sqlfilter.py:801 #, python-format msgid "value '%s' cannot be used for field '%s'" msgstr "wartość '%s' nie może być użyta dla pola '%s'" #: sqlkit/widgets/common/sqlfilter.py:868 #, python-format msgid "" "value '%s' does not seem a valid date and cannot be transformed into a " "date" msgstr "" "wartość '%s' nie wygląda na poprawną datę i nie może być w nią " "przekształcona" #. TIP: appears in the menu in the filter panel to add a second entry of the #. same field #: sqlkit/widgets/common/sqlfilter.py:1042 #, python-format msgid "Add a new filter on this field '%s'" msgstr "Dodaj nowy filtr na pole '%s'" #: sqlkit/widgets/common/sqlfilter.py:1048 msgid "Use this filter" msgstr "Użyj tego filtra" #: sqlkit/widgets/common/sqlfilter.py:1092 msgid "Click here to select an operator" msgstr "Kliknij by wybrać operatora" #: sqlkit/widgets/common/sqlfilter.py:1164 msgid "incomplete date format" msgstr "niekompletny format daty" #: sqlkit/widgets/common/sqlwidget.py:358 #, fuzzy, python-format msgid "" "Sorry, problems connecting to remote db. Original error was: \n" "\n" "%s" msgstr "" "Błąd podczas zapisu do bazy danych: \n" "czy 'zapisujesz jako nowy' istniejący już rekord?\n" "błąd w oryginale: \n" "%s" #: sqlkit/widgets/common/sqlwidget.py:625 msgid "Hiding field is only supported for Tables" msgstr "Ukrywanie pól dostępne jedynie dla trybu Tables" #: sqlkit/widgets/common/sqlwidget.py:792 msgid "Show all the differences that should be saved to database" msgstr "Pokaż wszystkie różnice, które powinny być zapisane do bazy" #: sqlkit/widgets/common/sqlwidget.py:802 msgid "Pending Differences" msgstr "Zmiany w toku" #: sqlkit/widgets/common/sqlwidget.py:806 #, fuzzy msgid "Save current record" msgstr "Policz rekordy" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Filter panel" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlwidget.py:810 #, fuzzy msgid "Add filter panel" msgstr "Panel filtrowania" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload" msgstr "Przeładuj" #: sqlkit/widgets/common/sqlwidget.py:811 #, fuzzy msgid "Reload from the database" msgstr "Wczytaj również dane" #: sqlkit/widgets/common/sqlwidget.py:815 #, fuzzy msgid "Go to next record" msgstr "Policz rekordy" #: sqlkit/widgets/common/sqlwidget.py:816 msgid "Go to previous record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:823 msgid "Inspect widgets" msgstr "Inspektor obiektów" #: sqlkit/widgets/common/sqlwidget.py:990 msgid "Saved" msgstr "Zachowano" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1020 #, fuzzy, python-format msgid "" "Error while writing to the database. \n" "Original error was: \n" "%s" msgstr "" "Błąd podczas zapisu do bazy danych: \n" "czy 'zapisujesz jako nowy' istniejący już rekord?\n" "błąd w oryginale: \n" "%s" #. TIP: Error while saving into a table w/o permission #. TIP: reloading data from the database #: sqlkit/widgets/common/sqlwidget.py:1026 #: sqlkit/widgets/common/sqlwidget.py:1563 #, python-format msgid "" "A programming error was received from the backend:\n" "%s" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1035 #, python-format msgid "%s - Dirty objects: %s - New objects: %s" msgstr "%s - Zużywane obiekty: %s - Nowe obiekty: %s" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1041 #, python-format msgid "" "Error while writing to the database: \n" "are you 'saving as new' an already known record?\n" "original error was: \n" "%s" msgstr "" "Błąd podczas zapisu do bazy danych: \n" "czy 'zapisujesz jako nowy' istniejący już rekord?\n" "błąd w oryginale: \n" "%s" #: sqlkit/widgets/common/sqlwidget.py:1186 msgid "Discarding new obj" msgstr "Zawetuj nowy objekt" #: sqlkit/widgets/common/sqlwidget.py:1223 #, python-format msgid "Char %s is not accepted in numeric context" msgstr "Znak %s nie jest akceptowalny w kontekście numerycznym" #: sqlkit/widgets/common/sqlwidget.py:1281 msgid "record validation" msgstr "potwierdzanie rekordu" #: sqlkit/widgets/common/sqlwidget.py:1503 msgid "Unsaved differences" msgstr "Niezachowane różnice" #: sqlkit/widgets/common/sqlwidget.py:1741 msgid "Sorry, export is implemented only for table view" msgstr "Niestety, eksport jest zaimplementowany jedynie dla trybu Table" #: sqlkit/widgets/common/sqlwidget.py:2097 msgid "Property" msgstr "Własności" #: sqlkit/widgets/common/sqlwidget.py:2098 msgid "Value" msgstr "Wartość" #: sqlkit/widgets/common/sqlwidget.py:2184 msgid "Save unsaved data?" msgstr "Zapisać niezachowane dane?" #: sqlkit/widgets/common/sqlwidget.py:2187 msgid "Unsaved data" msgstr "Niezachowane dane" #: sqlkit/widgets/common/sqlwidget.py:2209 msgid "State" msgstr "Stan" #: sqlkit/widgets/common/sqlwidget.py:2210 #: sqlkit/widgets/common/sqlwidget.py:2330 msgid "Field name" msgstr "Nazwa pola" #: sqlkit/widgets/common/sqlwidget.py:2211 msgid "Original value" msgstr "Oryginalna wartość" #: sqlkit/widgets/common/sqlwidget.py:2212 msgid "Present value" msgstr "Obecna wartość" #: sqlkit/widgets/common/sqlwidget.py:2242 msgid "Modified" msgstr "Zmodyfikowane" #: sqlkit/widgets/common/sqlwidget.py:2269 sqlkit/widgets/mask/mask.py:597 msgid "Deleted" msgstr "Usunięte" #: sqlkit/widgets/common/sqlwidget.py:2279 msgid "New" msgstr "Nowe" #: sqlkit/widgets/common/sqlwidget.py:2293 msgid "empty string: ''" msgstr "pusty napis: ''" #: sqlkit/widgets/common/sqlwidget.py:2295 msgid "NULL value" msgstr "wartość pusta" #: sqlkit/widgets/common/sqlwidget.py:2301 msgid "" "Errors are present in the record. \n" "Correct them now, to continue \n" "or delete the record" msgstr "" "W rekordzie występują błędy. \n" "Popraw je, by kontynuować \n" "lub usuń rekord" #: sqlkit/widgets/common/sqlwidget.py:2302 msgid "Validation errors" msgstr "Błędy poprawności składniowej" #: sqlkit/widgets/common/sqlwidget.py:2331 msgid "Error" msgstr "Błąd" #: sqlkit/widgets/common/sqlwidget.py:2362 msgid "" "You can continue or go back editing. \n" "Read the following warnings to decide" msgstr "" "Możesz kontynuować lub wrócić do edycji. \n" "Przeczytaj następujące ostrzeżenia by zdecydować" #: sqlkit/widgets/common/sqlwidget.py:2363 msgid "Validation Warnings" msgstr "Ostrzeżenia kontrolera poprawności" #: sqlkit/widgets/mask/mask.py:258 #, fuzzy msgid "Add new record" msgstr "Aktualnie w nowym rekordzie" #: sqlkit/widgets/mask/mask.py:262 msgid "Discard changes" msgstr "" #. TIP Modify menu entry in the mask to reread a single record from database #: sqlkit/widgets/mask/mask.py:264 #, fuzzy msgid "Refresh this record" msgstr "" "Usunąć ten rekord?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:265 #, fuzzy msgid "Reread this record from db" msgstr "Aktualnie pierwszy rekord" #: sqlkit/widgets/mask/mask.py:268 #, fuzzy msgid "Delete this record" msgstr "" "Usunąć ten rekord?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:342 sqlkit/widgets/table/table.py:908 msgid "Already at new record" msgstr "Aktualnie w nowym rekordzie" #: sqlkit/widgets/mask/mask.py:362 #, python-format msgid "New record %s" msgstr "Nowy rekord %s" #. TIP message issued when a refresh is done on a deleted record #: sqlkit/widgets/mask/mask.py:405 msgid "The record is no longer present in the database" msgstr "" #: sqlkit/widgets/mask/mask.py:473 sqlkit/widgets/table/table.py:838 msgid "Nothing to save" msgstr "Nie ma nic do zachowania" #: sqlkit/widgets/mask/mask.py:523 #, python-format msgid "Primary key (%s) didn't change. Refusing to save as new" msgstr "Klucz główny (%s) nie zmienił się. Odmowa dla zapisu nowego" #: sqlkit/widgets/mask/mask.py:537 msgid "Do you want to copy all data to a new record?" msgstr "" #: sqlkit/widgets/mask/mask.py:585 sqlkit/widgets/table/table.py:982 #, python-format msgid "" "Delete this record?\n" "(%s)" msgstr "" "Usunąć ten rekord?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:654 msgid "No record present" msgstr "Brak rekordów do pokazania" #: sqlkit/widgets/mask/mask.py:677 msgid "Already last record" msgstr "Aktualnie ostatni rekord" #: sqlkit/widgets/mask/mask.py:679 msgid "Already first record" msgstr "Aktualnie pierwszy rekord" #: sqlkit/widgets/mask/miniwidgets.py:695 #, python-format msgid "A file with name '%s' already exists. Overwrite?" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:696 msgid "Upload name conflict" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:888 #, python-format msgid "'%s' may have an invalid value: try completion on that" msgstr "'%s' może mieć niepoprawną wartość: spróbuj uzupełnić" #: sqlkit/widgets/table/columns.py:208 msgid "Editing canceled. Restoring original value" msgstr "Edytowanie wstrzymane. Przywracanie oryginalnej wartości" #: sqlkit/widgets/table/columns.py:848 #, python-format msgid "Add a filter on '%s'" msgstr "Dodaj filtr na '%s'" #: sqlkit/widgets/table/columns.py:858 msgid "" "Sort on this column reloading from the database, you can used 's' to sort" " locally" msgstr "" #: sqlkit/widgets/table/columns.py:861 msgid "Sort on this column locally (w/o touching the database)" msgstr "" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:871 msgid "Hide this column" msgstr "Ukryj tę kolumnę" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:878 msgid "Create total" msgstr "Stworzono łącznie" #. TIP: column menu total #: sqlkit/widgets/table/columns.py:888 #, python-format msgid "Subtotal on %s" msgstr "Częściowo %s" #. TIP: column menu opt #. TIP: modify menu entry #: sqlkit/widgets/table/columns.py:910 sqlkit/widgets/table/columns.py:1293 #: sqlkit/widgets/table/table.py:287 msgid "Show field" msgstr "Pokaż pole" #: sqlkit/widgets/table/columns.py:911 sqlkit/widgets/table/columns.py:1294 #: sqlkit/widgets/table/table.py:288 msgid "Hide field" msgstr "Ukryj pole" #: sqlkit/widgets/table/columns.py:938 msgid "day" msgstr "dzień" #: sqlkit/widgets/table/columns.py:938 msgid "week" msgstr "tydzień" #: sqlkit/widgets/table/columns.py:938 msgid "month" msgstr "miesiąc" #: sqlkit/widgets/table/columns.py:939 msgid "quarter" msgstr "kwartał" #: sqlkit/widgets/table/columns.py:939 msgid "year" msgstr "rok" #: sqlkit/widgets/table/columns.py:942 #, python-format msgid "Subtotals by %s" msgstr "Częściowo przez %s" #: sqlkit/widgets/table/columns.py:1267 msgid "Right click on this table to show the column again" msgstr "Użyj prawego klawisza by ponownie pokazać kolumnę " #: sqlkit/widgets/table/columns.py:1287 sqlkit/widgets/table/table.py:301 msgid "Export" msgstr "Eksport" #: sqlkit/widgets/table/columns.py:1287 msgid "Export these data into csv format" msgstr "" #: sqlkit/widgets/table/columns.py:1292 msgid "Adapt width of columns to data" msgstr "" #: sqlkit/widgets/table/columns.py:1321 #, python-format msgid "View '%s' in a Mask" msgstr "Zobacz '%s' w trybie Mask" #: sqlkit/widgets/table/table.py:274 msgid "Duplicate" msgstr "" #: sqlkit/widgets/table/table.py:275 msgid "Create a new row as a duplicate of this one" msgstr "" #: sqlkit/widgets/table/table.py:277 msgid "New child row" msgstr "" #: sqlkit/widgets/table/table.py:278 msgid "Create e new row as child of this one" msgstr "" #: sqlkit/widgets/table/table.py:292 msgid "View this record in a Mask" msgstr "Zobacz rekord w trybie Mask" #: sqlkit/widgets/table/table.py:293 msgid "View this ForeignKey in a Mask" msgstr "Zobacz klucz obcy w trybie Mask" #: sqlkit/widgets/table/table.py:294 msgid "Upload Image" msgstr "" #: sqlkit/widgets/table/table.py:295 #, fuzzy msgid "Upload File" msgstr "Wszystkie pliki" #: sqlkit/widgets/table/table.py:296 msgid "Delete File/Image" msgstr "" #: sqlkit/widgets/table/table.py:297 msgid "Show File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save file locally" msgstr "" #: sqlkit/widgets/table/table.py:528 sqlkit/widgets/table/table.py:542 #: sqlkit/widgets/table/table.py:554 msgid "Insertion of new records is disabled. Sorry" msgstr "Niestety, dodawanie nowych rekordów jest wyłączone." #: sqlkit/widgets/table/table.py:558 msgid "Update of records is disabled. Sorry" msgstr "Niestety, aktualizacja rekordów jest wyłączona." #: sqlkit/widgets/table/table.py:570 msgid "Deletion of records is disabled. Sorry" msgstr "Niestety, usuwanie rekordów jest wyłączone." #: sqlkit/widgets/table/table.py:583 msgid "browsing of new records is disabled. Sorry" msgstr "Niestety, przeglądanie rekordów jest wyłączone." #: sqlkit/widgets/table/table.py:731 msgid "no current obj: maybe no record has yet been edited" msgstr "nie ma obiektu do zaznaczenia: może żaden nie był jeszcze opisany" #. TIP: when saving m2m, we delay till leader record will be saved #: sqlkit/widgets/table/table.py:869 msgid "delaying saving when main record will be saved" msgstr "opóźnianie zapisu jeśli główny rekord został zachowany" #: sqlkit/widgets/table/table.py:879 msgid "Record has NOT been saved" msgstr "Rekord NIE został zachowany" #: sqlkit/widgets/table/table.py:978 msgid "No record selected" msgstr "Nie zaznaczono rekordu" #: sqlkit/widgets/table/table.py:1137 msgid "Value is not valid, trying completion" msgstr "Wartość nie jest poprawna, próba uzupełnienia" #. TIP: check in input field length %s %s -> field_name length #: sqlkit/widgets/table/table.py:1170 #, python-format msgid "Input exceeded max %s length (%s)" msgstr "Maksymalna długość %s została przekroczona (%s)" #: sqlkit/widgets/table/table.py:1284 #, fuzzy msgid "Unsaved data prevent opening a Mask to show the (unsaved) record" msgstr "Niezachowane dane zapobiegają otworzeniu trybu Mask " #: sqlkit/widgets/table/table.py:1407 #, python-format msgid "" "A file with name '%s' already exists.\n" "Overwrite?" msgstr "" #: sqlkit/widgets/table/table.py:1408 msgid "Upload name duplication" msgstr "" #: sqlkit/widgets/table/table.py:1629 msgid "Export in csv file" msgstr "Eksportuj do pliku csv" #: sqlkit/widgets/table/table.py:1702 msgid "Csv files" msgstr "Pliki csv" #: sqlkit/widgets/table/tablewidgets.py:34 #, python-format msgid "No current obj for field '%s' in %s" msgstr "Nie ma obiektu dla pola '%s' in %s" sqlkit-0.9.5/sqlkit/locale/pl/LC_MESSAGES/sqlkit.mo0000644000175000017500000003734211714210425021212 0ustar sandrosandro ( 69O005!@#Uy *1 \ } +% ' 4>GO an&-  ",(?)h*nV8!   / %14G ]hy )(:B  -!@+b+ 7&<c!s   #(:cu  6 F Q7]2%5 GUhn*~990 N,Y*+07#Q[<)Y:P;?({/ $    3 M ] q  %     !K%!*q!!.!!! !! "3"F"K"R"Z"l"s"" "("K"# # /#P#U#Z#$%-?&=m&/&0&0 '5='s'''''' (#(.<(k(((7(( ())0)%?)+e)%)) ) ) )) ) ** 5*.?*n*~*-* * **&*'+96+p+w+r~+N+@,H,!_, ,,,,/, ,,,--0- D-R- X-c-1v-(-:- . ....B.3-/6a//A/// 0 0A'0i0070 0 000 11.1M1 R1`1"o19111 1 2#2,2G2 \2j226222?2 )353 R3]3%l353333 44 *444:D4@45444 +5675<n5525?5736Qk66666 6@7H7Y^777$7#8Z78;8(8/8 '9H9 e9 p9{99A:S:2j: : :::::$;!5; W;1a;;;;;<b!<3<<<<< =="=;=DD== ====== =2=V)>+>(>>> You can indicate the database you want to connect to using an URI in the form as: postgres://localhost/dbname sqlite:///dbname or using password and username: mysql://sandro:pass@host/dbname %s - Dirty objects: %s - New objects: %s'%s' may have an invalid value: try completion on thatA complete demo of all the features of the sqlkit packageA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataAdd a filter on '%s'Add a new filter on this field '%s'Add filters for your queryAll filesAlready at new recordAlready first recordAlready last recordAs LIKE but case insensitiveAttempting to connect to %sBlankCast blank into NULLChar %s is not accepted in numeric contextClick here to select an operatorCollapse rowConnected to %sConnection setupCount recordsCount records in all tablesCreate a new row as a duplicate of this oneCreate e new row as child of this oneCreate totalCsv filesDatabaseDefaultDelete File/ImageDelete imageDelete the imageDelete this record? (%s)DeletedDeletion of records is disabled. SorryDiscard changesDiscarding new objDo you want to copy all data to a new record?DocumentsDuplicateEdit Sqlkit FieldsEdit the referenced table in 'mask' modeEdit the referenced table in 'table' modeEditing canceled. Restoring original valueEqualErrorError while writing to the database: are you 'saving as new' an already known record? original error was: %sErrors are present in the record. Correct them now, to continue or delete the recordExportExport in csv fileExport these data into csv formatField nameFileFilter PanelFilter panelFind allowed values (Control-Enter/Shift-Enter)Foreign KeyGoGo to output panelGo to previous recordGood matchGreater or equalGreater then (after than)HelpHide fieldHide this columnHiding field is only supported for TablesID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameIf you write a wrong hostname the application may hang some seconds till the natural network timeout. Uncheck the flag on the right ti disable this featureImage viewerImagesIncomplete date formatIndexesInfo on available backend: postgres, mysql...Input exceeded max %s length (%s)Insertion of new records is disabled. SorryInspect widgetsLIKE: a "%" means any char - case sensitiveLess than (before then)Less than or equalLoad dataMaskMatch as LIKE case insensitive, "%" automatically addedMatch as LIKE, "%" automatically addedMatch as regexpMatch as regexp, case insensitiveModifiedModifyN.RowsNULL valueNegation of ILIKENegation of LIKENegation of match as regexpNewNew child rowNew record %sNo current obj for field '%s' in %sNo exact match, trying regexp completionNo record presentNo record selectedNot equalNothing to saveNullableOpen separate image viewerOriginal valuePending DifferencesPreferred new filename:Present valuePress Esc to close or double click a date to select itPrim. KeysPrimary KeyPrimary key (%s) didn't change. Refusing to save as newPropertyRecord has NOT been savedReloadReload from dbResult page for your queryRight click on this table to show the column againRun DemoSave File/ImageSave file locallySave image asSave unsaved data?SavedShow File/ImageShow all info on this field, db type & Co.Show all the differences that should be saved to databaseShow all values in the database starting with your stringShow all values in the db that match your stringShow fieldShow possible values, starting - Shift-EnterShow possible values: regexp - Ctrl-EnterShow/Hide image nameShow/Hide primary key if the key is numericSorry, export is implemented only for table viewSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallySqledit manual pageStateSubtotal on %sSubtotals by %sTableTable %s doesn't have a primary key, editing is not possibleTable reflectionThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sThe boolean is FalseThe boolean is TrueThe boolean is not FalseThe boolean is not TrueThe demo was not found, if you know where it is, run it manually: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseThe value is not setThe value is setToolToolsTotal N. of records: %sTry continuosly to connect. Nice and usefull but may cause temporary blocks if you write an inexistent hostname Unsaved dataUnsaved differencesUpdate of records is disabled. SorryUpload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUse this filterValidation WarningsValidation errorsValueValue is not valid, trying completionView '%s' in a MaskView this ForeignKey in a MaskView this record in a MaskWrong date format: %sWrong time formatYou can continue or go back editing. Read the following warnings to decidebrowsing of new records is disabled. Sorrydaydelaying saving when main record will be savedempty string: ''filterfilter_tablesincomplete date formatmonthno current obj: maybe no record has yet been editedoptsoutputquarterrecord validationregexpsearch mode: %sstartupload imagevalue '%s' cannot be used for field '%s'value '%s' does not seem a valid date and cannot be transformed into a datevalue is not Decimal nor string: %svalue is not date nor string: %sweekyearProject-Id-Version: PROJECT VERSION Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2010-05-20 14:11+0200 PO-Revision-Date: 2011-03-13 00:10+0100 Last-Translator: Sebastian Majka Language-Team: pl Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.4 Możesz wskazać bazę danych, z którą chcesz nawiązać połączenie używając następujących form adresu: postgres://localhost/dbname sqlite:///dbname lub podając nazwę użytkownika i hasło: mysql://sandro:pass@host/dbname %s - Zużywane obiekty: %s - Nowe obiekty: %s'%s' może mieć niepoprawną wartość: spróbuj uzupełnićPełna demonstracja możliwości pakietu sqlkitA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataDodaj filtr na '%s'Dodaj nowy filtr na pole '%s'Dodaj filtr do zapytaniaWszystkie plikiAktualnie w nowym rekordzieAktualnie pierwszy rekordAktualnie ostatni rekordJak LIKE lecz niewrażliwy na wielkość literTrwa próba połączenia do %sLukiTraktuj luki jako NULLZnak %s nie jest akceptowalny w kontekście numerycznymKliknij by wybrać operatoraCollapse rowPołączono z %sParametry połączeniaPolicz rekordyPolicz rekordy we wszystkich tabelachCreate a new row as a duplicate of this oneCreate e new row as child of this oneStworzono łączniePliki csvBaza danychDomyślneDelete File/ImageDelete imageDelete the imageUsunąć ten rekord? (%s)UsunięteNiestety, usuwanie rekordów jest wyłączone.Discard changesZawetuj nowy objektDo you want to copy all data to a new record?DocumentsDuplicateEdytuj pola SqlkitEdytuj poleconą tabelę w trybie MaskEdytuj poleconą tabelę w trybie TableEdytowanie wstrzymane. Przywracanie oryginalnej wartościrówneBłądBłąd podczas zapisu do bazy danych: czy 'zapisujesz jako nowy' istniejący już rekord? błąd w oryginale: %sW rekordzie występują błędy. Popraw je, by kontynuować lub usuń rekordEksportEksportuj do pliku csvExport these data into csv formatNazwa polaPlikPanel filtrowaniaPanel filtrowaniaFind allowed values (Control-Enter/Shift-Enter)Klucz obcyIdźGo to output panelGo to previous recordDobre dopasowaniewiększe lub równewiększe niżPomocUkryj poleUkryj tę kolumnęUkrywanie pól dostępne jedynie dla trybu TablesID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameJeśli użyjesz nieprawidłowego adresu aplikacja może zawiesić się na krótki czas poszukiwań w sieci (timeout). Odznacz flagę po prawej by zablokować tę funkcjęImage viewerImagesNiepełny format datyIndeksyInformacja o dostępności silników bazodanowych: postgres, mysqlMaksymalna długość %s została przekroczona (%s)Niestety, dodawanie nowych rekordów jest wyłączone.Inspektor obiektówLIKE: "%" oznacza jakikolwiek znak- wrażliwe na wielkość litermniejsze niż mniejsze lub równeŁaduj danetryb MaskLIKE, "%" dodane automatycznie (niewrażliwe na wielkość liter)LIKE, "%" dodane automatyczniewyrażenie regularnewyrażenie regularne (niewrażliwe na wielkość liter)ZmodyfikowaneModyfikujLiczba rekordówwartość pustanegacja ILIKEzaprzeczenie LIKEnegacja wyrażenia regularnegoNoweNew child rowNowy rekord %sNie ma obiektu dla pola '%s' in %sNiedokładne dopasowanie, spróbuj wyrażenia regularnegoBrak rekordów do pokazaniaNie zaznaczono rekordunie-równeNie ma nic do zachowaniaNullableOpen separate image viewerOryginalna wartośćZmiany w tokuPreferred new filename:Obecna wartośćPress Esc to close or double click a date to select itKlucze główneKlucz głównyKlucz główny (%s) nie zmienił się. Odmowa dla zapisu nowegoWłasnościRekord NIE został zachowanyPrzeładujReload from dbStrona wynikowa dla twojego zapytaniaUżyj prawego klawisza by ponownie pokazać kolumnę Włącz demonstracjęSave File/ImageSave file locallySave image asZapisać niezachowane dane?ZachowanoShow File/ImagePokaż wszystkie informacje o polu, typie bazy i podobnychPokaż wszystkie różnice, które powinny być zapisane do bazyPokaż wszystkie wartości w bazie danych z prefiksemPokaż wszystkie pola, które zawierają twój napisPokaż polePokaż możliwe wartości, rozpoczynanie - Shift-EnterPokaż możliwe wartości: wyrażenie regularne - Ctrl-EnterShow/Hide image namePokaż/Ukryj główny klucz jeśli jest numerycznyNiestety, eksport jest zaimplementowany jedynie dla trybu TableSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallyPrzewodnik po SqleditStanCzęściowo %sCzęściowo przez %stryb TableTabela %s nie ma klucza głównego, edytowanie nie jest możliwetryb Table reflectionThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sWartość logiczna: FałszWartość logiczna: PrawdaWartość logiczna nie jest FałszemWartość logiczna nie jest PrawdąNie znaleziono demonstracji, jeśli wiesz gdzie jest, uruchom ją ręcznie: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseWartość nie została ustawionaWartość została ustawionaNarzędziaNarzędziaŁączna liczba rekordów: %sNie przestawaj nawiązywać połączenia Jest to użyteczne, lecz może powodować chwilowe przestoje, jeśli określisz nieistniejący adres serwera. Niezachowane daneNiezachowane różniceNiestety, aktualizacja rekordów jest wyłączona.Upload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUżyj tego filtraOstrzeżenia kontrolera poprawnościBłędy poprawności składniowejWartośćWartość nie jest poprawna, próba uzupełnieniaZobacz '%s' w trybie MaskZobacz klucz obcy w trybie MaskZobacz rekord w trybie MaskNieprawidłowy format daty: %sNiepoprawny format czasuMożesz kontynuować lub wrócić do edycji. Przeczytaj następujące ostrzeżenia by zdecydowaćNiestety, przeglądanie rekordów jest wyłączone.dzieńopóźnianie zapisu jeśli główny rekord został zachowanypusty napis: ''filtrfiltruj _tablesniekompletny format datymiesiącnie ma obiektu do zaznaczenia: może żaden nie był jeszcze opisanyopcjeplik wynikowykwartałpotwierdzanie rekorduwyrażenie regularnetryb szukania: %sstartupload imagewartość '%s' nie może być użyta dla pola '%s'wartość '%s' nie wygląda na poprawną datę i nie może być w nią przekształconawartość nie jest liczbowa ani napisem: %swartość nie jest datą ani napisem: %stydzieńroksqlkit-0.9.5/sqlkit/locale/it/0000755000175000017500000000000011714210425015551 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/it/LC_MESSAGES/0000755000175000017500000000000011714210425017336 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/it/LC_MESSAGES/sqlkit.po0000644000175000017500000007462111714210425021217 0ustar sandrosandro# Italian translations for sqlkit. # Copyright (C) 2009 ORGANIZATION # This file is distributed under the same license as the sqlkit project. # FIRST AUTHOR , 2009. # Sandro Dentella , 2009, 2010, 2011. msgid "" msgstr "" "Project-Id-Version: sqlkit 0.8.5-pre\n" "Report-Msgid-Bugs-To: sandro@e-den.it\n" "POT-Creation-Date: 2009-01-07 19:38+0100\n" "PO-Revision-Date: 2011-03-13 00:08+0100\n" "Last-Translator: Sandro \n" "Language-Team: sqlkit\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.4\n" #: bin/sqledit.py:87 msgid "Connection setup" msgstr "Setup della connessione" #: bin/sqledit.py:90 msgid "" "\n" " You can indicate the database you want to connect to\n" " using an URI in the form as:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " or using password and username:\n" " mysql://sandro:pass@host/dbname\n" " " msgstr "" "Puoi indicare il database a cui connetterti\n" "usando un URI in una delle forme seguenti:\n" "postgres://localhost/dbname\n" "sqlite:///dbname\n" "o usando password e username:\n" "mysql://sandro:pass@host/dbname" #: bin/sqledit.py:117 msgid "Run Demo" msgstr "Avvia Demo" #: bin/sqledit.py:140 msgid "" "Info on available backend:\n" "postgres, mysql..." msgstr "" "Informazioni sui backend disponibili:\n" "postgres, mysql,..." #: bin/sqledit.py:141 msgid "Sqledit manual page" msgstr "Pagina di manuale di Sqledit" #: bin/sqledit.py:146 msgid "" "Try continuosly to connect.\n" " Nice and usefull but may cause temporary blocks\n" " if you write an inexistent hostname\n" " " msgstr "" "Prova a connettersi continuamente:\n" "utile ma può causare blocchi temporanei\n" "se scrivi un nome host inesistente" #: bin/sqledit.py:160 msgid "" "If you write a wrong hostname\n" "the application may hang some seconds till the natural network timeout. \n" "Uncheck the flag on the right ti disable this feature" msgstr "" "Se scrivi un nome host errato\n" "l'applicazione potrebbe bloccarsi \n" "alcuni secondi fino al timeout di rete\n" "Smarca la casella a fianco per disabilitare questo comportamento" #: bin/sqledit.py:187 #, python-format msgid "Attempting to connect to %s" msgstr "Sto connettendomi a %s" #: bin/sqledit.py:190 #, python-format msgid "Connected to %s" msgstr "Connesso a %s" #: bin/sqledit.py:221 msgid "A complete demo of all the features of the sqlkit package" msgstr "" "Una demo completa di tutte le caratteristiche\n" "del pacchetto sqlkit" #: bin/sqledit.py:222 msgid "" "The demo was not found, if you know where it is,\n" "run it manually: python demo.py" msgstr "" "La demo non è stata trovata\n" "se sai dove è provala manualmente:\n" "python demo.py" #: sqlkit/exc.py:48 #, python-format msgid "Field '%s' cannot be NULL" msgstr "il campo '%s' non può esser NULLO" #: sqlkit/exc.py:60 #, python-format msgid "Field '%s' cannot have value %s" msgstr "il campo '%s' non può avere valore %s" #: sqlkit/fields.py:979 #, python-format msgid "value is not Decimal nor string: %s" msgstr "il valore fornito non è né un Decimal né una stringa: %s" #: sqlkit/fields.py:1055 #, python-format msgid "value is not date nor string: %s" msgstr "il valore fornito non è né una data né una stringa: %s" #: sqlkit/fields.py:1174 sqlkit/widgets/mask/miniwidgets.py:430 #, python-format msgid "Wrong date format: %s" msgstr "Formato data errato: %s" #: sqlkit/fields.py:1184 msgid "Wrong time format" msgstr "Formato di tempo errato" #: sqlkit/fields.py:1336 msgid "Value cannot be NULL" msgstr "Il valore non può essere NULLO" #: sqlkit/fields.py:1340 #, python-format msgid "Value '%s' is not accepted" msgstr "Il valore '%s' non è permesso" #: sqlkit/db/proxy.py:219 #, python-format msgid "Table %s doesn't have a primary key, editing is not possible" msgstr "La tabella %s non ha una chiave primaria. Non è possibile editarla." #: sqlkit/layout/dateedit.py:190 msgid "Press Esc to close or double click a date to select it" msgstr "Premi 'Esc' per chiudere o fai doppio click su una data per selezionarla" #: sqlkit/layout/fk_entry.py:25 msgid "Find allowed values (Control-Enter/Shift-Enter)" msgstr "Trova i valori permessi (Control-Invio/Maiusc-Invio)" #: sqlkit/layout/image_widget.py:142 msgid "" "Right click on the image area\n" " to upload an image" msgstr "Click destro sulla area dell'immagine per caricare una immagine" #: sqlkit/layout/image_widget.py:273 sqlkit/widgets/table/table.py:1395 msgid "Upload image" msgstr "Carica Immagine" #: sqlkit/layout/image_widget.py:273 msgid "Upload or modify an image" msgstr "Carica o modifica immagine" #: sqlkit/layout/image_widget.py:274 msgid "Delete image" msgstr "Elimina immagine" #: sqlkit/layout/image_widget.py:274 msgid "Delete the image" msgstr "Elimina l'immagine" #: sqlkit/layout/image_widget.py:275 msgid "Image viewer" msgstr "Visualizzatore immagine" #: sqlkit/layout/image_widget.py:275 msgid "Open separate image viewer" msgstr "Apri un visualizzatore separato" #: sqlkit/layout/image_widget.py:276 msgid "Save image as" msgstr "Mostra/Nascondi il percorso dell'immagine" #: sqlkit/layout/image_widget.py:279 msgid "Show/Hide image name" msgstr "Mostra/Nascondi il nome dell'immagine" #: sqlkit/layout/image_widget.py:317 msgid "upload image" msgstr "Carica Immagine" #: sqlkit/misc/datetools.py:152 msgid "Incomplete date format" msgstr "Formato di data incompleto: %s" #: sqlkit/misc/table_browser.py:135 msgid "Database" msgstr "Database" #. TIP: menu entry #: sqlkit/misc/table_browser.py:136 sqlkit/misc/words.py:13 #: sqlkit/widgets/common/sqlwidget.py:795 msgid "Modify" msgstr "Modifica" #: sqlkit/misc/table_browser.py:137 msgid "Tool" msgstr "Strumenti" #. TIP: menu entry #: sqlkit/misc/table_browser.py:138 sqlkit/misc/words.py:15 #: sqlkit/widgets/common/sqlwidget.py:798 msgid "Help" msgstr "Aiuto" #: sqlkit/misc/table_browser.py:141 msgid "Count records" msgstr "Conta i record" #: sqlkit/misc/table_browser.py:141 msgid "Count records in all tables" msgstr "Conta i record in tutte le tabelle" #: sqlkit/misc/table_browser.py:144 msgid "" "Configure the fields: \n" "labels, tooltip, completion, search field" msgstr "" "Configura i campi:\n" "etichette, suggerimenti, completamento, campi di ricerca" #: sqlkit/misc/table_browser.py:147 msgid "Edit Sqlkit Fields" msgstr "Edita i campi di sqlkit" #: sqlkit/misc/table_browser.py:150 msgid "Primary Key" msgstr "Chiave Prim." #: sqlkit/misc/table_browser.py:151 msgid "Show/Hide primary key if the key is numeric" msgstr "Mostra/nascondi la chiave primaria se numerica" #: sqlkit/misc/table_browser.py:152 msgid "Load data" msgstr "Carica dati" #: sqlkit/misc/table_browser.py:153 msgid "Load the data as well" msgstr "Carica anche i dati (quando apre una tabella)" #: sqlkit/misc/table_browser.py:154 msgid "Blank" msgstr "Valori Nulli" #: sqlkit/misc/table_browser.py:154 msgid "Cast blank into NULL" msgstr "Trasforma stringhe vuote in NULL" #: sqlkit/misc/table_browser.py:262 msgid "Mask" msgstr "Maschera" #: sqlkit/misc/table_browser.py:266 msgid "Table" msgstr "Tabella" #: sqlkit/misc/table_browser.py:272 msgid "Collapse row" msgstr "Nascondi le informazioni " #: sqlkit/misc/table_browser.py:276 msgid "Table reflection" msgstr "Introspezione della tabella" #: sqlkit/misc/table_browser.py:423 msgid "The name of the table we are customizing" msgstr "Nome della tabella da personalizzare" #: sqlkit/misc/table_browser.py:424 #, python-format msgid "" "The best representation of a record \n" "as a combination of fields, e.g.: %(title)s %(year)s" msgstr "" "La migliore rappresentazione di un record\n" "come combinazione di campi, ad esempio: %(title)s %(year)s" #: sqlkit/misc/table_browser.py:427 msgid "The field that will be searched for when completion is used" msgstr "" "Il campo che sarà utilizzato nella ricerca quando viene usato il " "completamento. Es.: description " #. TIP: filter page of the filter panel #: sqlkit/misc/words.py:7 msgid "filter" msgstr "Filtri" #. TIP: output page of the filter panel #: sqlkit/misc/words.py:9 sqlkit/widgets/common/sqlfilter.py:259 #: sqlkit/widgets/common/sqlfilter.py:306 msgid "output" msgstr "Risultati" #. TIP: menu entry #: sqlkit/misc/words.py:11 msgid "File" msgstr "File" #. TIP: menu entry #: sqlkit/misc/words.py:17 sqlkit/widgets/common/sqlwidget.py:796 msgid "Go" msgstr "Vai" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:19 msgid "opts" msgstr "Opzioni" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:21 sqlkit/widgets/common/sqlwidget.py:797 msgid "Tools" msgstr "Strumenti" #. TIP: serach mode: #: sqlkit/misc/words.py:23 msgid "regexp" msgstr "espressione regolare" #. TIP: search mode #: sqlkit/misc/words.py:25 msgid "start" msgstr "inizio parola" #. TIP: count records in sqledit #: sqlkit/misc/words.py:27 msgid "N.Rows" msgstr "Numero Records" #: sqlkit/misc/words.py:29 msgid "Indexes" msgstr "Indici" #: sqlkit/misc/words.py:30 msgid "Nullable" msgstr "Nullo?" #: sqlkit/misc/words.py:31 msgid "Prim. Keys" msgstr "Chiave primaria" #: sqlkit/misc/words.py:32 msgid "Default" msgstr "Default" #: sqlkit/misc/words.py:33 msgid "Foreign Key" msgstr "Foreig Key" #: sqlkit/misc/words.py:34 msgid "filter_tables" msgstr "Filtra tabelle" #. TIP: status bar #: sqlkit/widgets/common/completion.py:285 #: sqlkit/widgets/common/completion.py:1124 #, python-format msgid "search mode: %s" msgstr "modo di ricerca: %s" #: sqlkit/widgets/common/completion.py:643 msgid "Show all info on this field, db type & Co." msgstr "Mostra tutte le informazioni su questo campo (tipo del database & Co.)" #. TIP: menu enty in menu on right click on down arro in fkey completion widget #: sqlkit/widgets/common/completion.py:649 msgid "Show possible values: regexp - Ctrl-Enter" msgstr "Mostra possibili valori con espressione regolare: Ctrl-Invio" #. TIP: yellow tip to menu entry in down arrow in completion widget #: sqlkit/widgets/common/completion.py:653 msgid "Show all values in the db that match your string" msgstr "" "Mostra tutti valori del database che contengono la tua stringa " "(espressione regolare)" #: sqlkit/widgets/common/completion.py:658 msgid "Show possible values, starting - Shift-Enter" msgstr "Mostra possibili valori con match sulle prima parole Maiusc-Invio" #: sqlkit/widgets/common/completion.py:661 msgid "Show all values in the database starting with your string" msgstr "Mostra tutti i valori nel database che cominciano con la tua string" #: sqlkit/widgets/common/completion.py:667 msgid "Edit the referenced table in 'table' mode" msgstr "Edita la tabella referenziata in modalità 'tabella'" #: sqlkit/widgets/common/completion.py:669 msgid "Edit the table from which admitted values are taken in 'table' mode" msgstr "" "Edita la tabella nella quale sono definiti i valori ammessi in modalità " "'tabella'" #: sqlkit/widgets/common/completion.py:674 msgid "Edit the referenced table in 'mask' mode" msgstr "Edita la tabella referenziata in modalità maschera" #: sqlkit/widgets/common/completion.py:676 msgid "Edit the table from which admitted values are taken in 'mask' mode" msgstr "Edita la tabella dove sono definiti i valori ammessi come maschera" #: sqlkit/widgets/common/completion.py:783 msgid "Good match" msgstr "Riferimento trovato" #: sqlkit/widgets/common/completion.py:792 #: sqlkit/widgets/common/completion.py:798 msgid "No exact match, trying regexp completion" msgstr "Non c'è un riferimento corretto, cerco un completamento" #: sqlkit/widgets/common/dialogs.py:26 sqlkit/widgets/common/dialogs.py:94 #: sqlkit/widgets/table/table.py:1703 msgid "All files" msgstr "Tutti i file" #: sqlkit/widgets/common/dialogs.py:100 msgid "Images" msgstr "Immagini" #: sqlkit/widgets/common/dialogs.py:106 msgid "Documents" msgstr "Documenti" #. TIP: possible new name for an uploaded file #: sqlkit/widgets/common/dialogs.py:123 msgid "Preferred new filename:" msgstr "Nome file preferito" #: sqlkit/widgets/common/dialogs.py:124 msgid "If not empty, the file will be uploaded with this new name" msgstr "Se valorizzato, il file sarà caricato con questo nome" #: sqlkit/widgets/common/sqlfilter.py:107 #: sqlkit/widgets/common/sqlfilter.py:110 msgid "Match as regexp" msgstr "Corrispondenza su espressione regolare" #: sqlkit/widgets/common/sqlfilter.py:108 msgid "Match as LIKE, \"%\" automatically added" msgstr "Come LIKE, '%' viene aggiunto automaticamente" #: sqlkit/widgets/common/sqlfilter.py:109 msgid "Match as LIKE case insensitive, \"%\" automatically added" msgstr "Come LIKE, ignora differenze maiuscole" #: sqlkit/widgets/common/sqlfilter.py:111 msgid "Match as regexp, case insensitive" msgstr "Corrispondenza su espressione regolare, ignora differenza maiuscole " #: sqlkit/widgets/common/sqlfilter.py:112 msgid "Negation of match as regexp" msgstr "Negazione di espressione regolare" #: sqlkit/widgets/common/sqlfilter.py:113 msgid "Negation of match case insensitive" msgstr "Negazione espressione regolare, ignora maiuscole" #: sqlkit/widgets/common/sqlfilter.py:114 msgid "Equal" msgstr "Uguale" #: sqlkit/widgets/common/sqlfilter.py:115 msgid "Not equal" msgstr "Non uguale" #: sqlkit/widgets/common/sqlfilter.py:116 msgid "Greater then (after than)" msgstr "Più grande di (o dopo di)" #: sqlkit/widgets/common/sqlfilter.py:117 msgid "Greater or equal" msgstr "Maggiore o uguale" #: sqlkit/widgets/common/sqlfilter.py:118 msgid "Less than (before then)" msgstr "Minore di (o prima di)" #: sqlkit/widgets/common/sqlfilter.py:119 msgid "Less than or equal" msgstr "Minore o uguale" #: sqlkit/widgets/common/sqlfilter.py:120 msgid "LIKE: a \"%\" means any char - case sensitive" msgstr "LIKE: un '%' significa qualunque carattere" #: sqlkit/widgets/common/sqlfilter.py:121 msgid "Negation of LIKE" msgstr "Negazione di LIKE" #: sqlkit/widgets/common/sqlfilter.py:122 msgid "As LIKE but case insensitive" msgstr "Come LIKE ma ignora diferenza maiuscole minuscole" #: sqlkit/widgets/common/sqlfilter.py:123 msgid "Negation of ILIKE" msgstr "Negazione di ILIKE" #: sqlkit/widgets/common/sqlfilter.py:124 msgid "The boolean is True" msgstr "È vero" #: sqlkit/widgets/common/sqlfilter.py:125 msgid "The boolean is False" msgstr "È Falso" #: sqlkit/widgets/common/sqlfilter.py:126 msgid "The boolean is not True" msgstr "Il valore booleano non è 'vero'" #: sqlkit/widgets/common/sqlfilter.py:127 msgid "The boolean is not False" msgstr "Il valore boolenao non è 'False'" #: sqlkit/widgets/common/sqlfilter.py:128 msgid "The value is not set" msgstr "Il valore non è impostato" #: sqlkit/widgets/common/sqlfilter.py:129 msgid "The value is set" msgstr "Il valore è impostato" #: sqlkit/widgets/common/sqlfilter.py:130 msgid "ID equality (don't follow foreign table)" msgstr "Uguaglianza esatta dell'ID (non seguire la tabella referenziata)" #: sqlkit/widgets/common/sqlfilter.py:221 msgid "Filter Panel" msgstr "Pannello dei filtri" #: sqlkit/widgets/common/sqlfilter.py:230 msgid "Add filters for your query" msgstr "Aggiungi filtri per la tua ricerca" #: sqlkit/widgets/common/sqlfilter.py:232 msgid "Result page for your query" msgstr "Risultati della ricerca" #: sqlkit/widgets/common/sqlfilter.py:269 msgid "Filter actions" msgstr "Azioni sui filtri" #: sqlkit/widgets/common/sqlfilter.py:270 msgid "Reload from db" msgstr "Ricarica dal database" #: sqlkit/widgets/common/sqlfilter.py:271 msgid "Close the panel" msgstr "Pannello dei filtri" #: sqlkit/widgets/common/sqlfilter.py:274 msgid "Go to filter panel" msgstr "Vai al pannello dei filtri" #: sqlkit/widgets/common/sqlfilter.py:276 msgid "Go to output panel" msgstr "Vai alla scheda dei risultati" #. TIP: status bar message #: sqlkit/widgets/common/sqlfilter.py:479 sqlkit/widgets/table/table.py:762 #: sqlkit/widgets/table/table.py:1039 #, python-format msgid "Total N. of records: %s" msgstr "N. totale di record: %s" #: sqlkit/widgets/common/sqlfilter.py:801 #, python-format msgid "value '%s' cannot be used for field '%s'" msgstr "il valore '%s' non può essere usato per il campo '%s'" #: sqlkit/widgets/common/sqlfilter.py:868 #, python-format msgid "" "value '%s' does not seem a valid date and cannot be transformed into a " "date" msgstr "Il valore '%s' non è una data e non può essere trasformato in una data." #. TIP: appears in the menu in the filter panel to add a second entry of the #. same field #: sqlkit/widgets/common/sqlfilter.py:1042 #, python-format msgid "Add a new filter on this field '%s'" msgstr "Aggiungi un filtro per il campo '%s'" #: sqlkit/widgets/common/sqlfilter.py:1048 msgid "Use this filter" msgstr "Abilita filtro" #: sqlkit/widgets/common/sqlfilter.py:1092 msgid "Click here to select an operator" msgstr "Clicca per selezionare un operatore" #: sqlkit/widgets/common/sqlfilter.py:1164 msgid "incomplete date format" msgstr "Formato data incompleto" #: sqlkit/widgets/common/sqlwidget.py:358 #, python-format msgid "" "Sorry, problems connecting to remote db. Original error was: \n" "\n" "%s" msgstr "" "Spiacente, problemi nella connessione al db. \n" "\n" "L' errore originale era: %s" #: sqlkit/widgets/common/sqlwidget.py:625 msgid "Hiding field is only supported for Tables" msgstr "E' possibile nascondere le colonne solo in modo Tabella" #: sqlkit/widgets/common/sqlwidget.py:792 msgid "Show all the differences that should be saved to database" msgstr "Mostra tutte le modifiche che dovrebbero essere salvate nel data" #: sqlkit/widgets/common/sqlwidget.py:802 msgid "Pending Differences" msgstr "Modifiche non salvate" #: sqlkit/widgets/common/sqlwidget.py:806 msgid "Save current record" msgstr "Salva il record corrente" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Filter panel" msgstr "Pannello dei filtri" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Add filter panel" msgstr "Aggiungi il pannello dei filtri" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload" msgstr "Ricarica" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload from the database" msgstr "Carica dal database" #: sqlkit/widgets/common/sqlwidget.py:815 msgid "Go to next record" msgstr "Vai al prossimo record" #: sqlkit/widgets/common/sqlwidget.py:816 msgid "Go to previous record" msgstr "Vai al record precedente" #: sqlkit/widgets/common/sqlwidget.py:823 msgid "Inspect widgets" msgstr "Ispeziona la gerarchia degli oggetti GTK (debug)" #: sqlkit/widgets/common/sqlwidget.py:990 msgid "Saved" msgstr "Salvato" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1020 #, python-format msgid "" "Error while writing to the database. \n" "Original error was: \n" "%s" msgstr "" "Errore durante la scrittura nel database: \n" "\n" "L' errore originale era: %s" #. TIP: Error while saving into a table w/o permission #. TIP: reloading data from the database #: sqlkit/widgets/common/sqlwidget.py:1026 #: sqlkit/widgets/common/sqlwidget.py:1563 #, python-format msgid "" "A programming error was received from the backend:\n" "%s" msgstr "" "Un errore di programmazione è stato ricevuto dal backend:\n" "%s" #: sqlkit/widgets/common/sqlwidget.py:1035 #, python-format msgid "%s - Dirty objects: %s - New objects: %s" msgstr "%s Record non salvati: %s -- Record nuovi: %s" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1041 #, python-format msgid "" "Error while writing to the database: \n" "are you 'saving as new' an already known record?\n" "original error was: \n" "%s" msgstr "" "Errore durante la scrittura nel database: \n" "stai 'salvando come nuovo' un record già noto (stessa primary key)\n" "L' errore originale era: %s" #: sqlkit/widgets/common/sqlwidget.py:1186 msgid "Discarding new obj" msgstr "Elimino il nuovo oggetto" #: sqlkit/widgets/common/sqlwidget.py:1223 #, python-format msgid "Char %s is not accepted in numeric context" msgstr "Il carattere %s non è utilizzabile in un campo numerico" #: sqlkit/widgets/common/sqlwidget.py:1281 msgid "record validation" msgstr "validazione di record" #: sqlkit/widgets/common/sqlwidget.py:1503 msgid "Unsaved differences" msgstr "Modifiche non salvate" #: sqlkit/widgets/common/sqlwidget.py:1741 msgid "Sorry, export is implemented only for table view" msgstr "Spiacente: è possibile esportare dati solo in modo Tabella" #: sqlkit/widgets/common/sqlwidget.py:2097 msgid "Property" msgstr "Proprietà" #: sqlkit/widgets/common/sqlwidget.py:2098 msgid "Value" msgstr "Valore" #: sqlkit/widgets/common/sqlwidget.py:2184 msgid "Save unsaved data?" msgstr "Salvo i dati non salvati?" #: sqlkit/widgets/common/sqlwidget.py:2187 msgid "Unsaved data" msgstr "Dati non salvati" #: sqlkit/widgets/common/sqlwidget.py:2209 msgid "State" msgstr "Stato" #: sqlkit/widgets/common/sqlwidget.py:2210 #: sqlkit/widgets/common/sqlwidget.py:2330 msgid "Field name" msgstr "Nome del campo" #: sqlkit/widgets/common/sqlwidget.py:2211 msgid "Original value" msgstr "Valore originale" #: sqlkit/widgets/common/sqlwidget.py:2212 msgid "Present value" msgstr "Valore attuale" #: sqlkit/widgets/common/sqlwidget.py:2242 msgid "Modified" msgstr "Modifica" #: sqlkit/widgets/common/sqlwidget.py:2269 sqlkit/widgets/mask/mask.py:597 msgid "Deleted" msgstr "Cancellato" #: sqlkit/widgets/common/sqlwidget.py:2279 msgid "New" msgstr "Nuovi" #: sqlkit/widgets/common/sqlwidget.py:2293 msgid "empty string: ''" msgstr "stringa vuota: ''" #: sqlkit/widgets/common/sqlwidget.py:2295 msgid "NULL value" msgstr "Valore NULLO" #: sqlkit/widgets/common/sqlwidget.py:2301 msgid "" "Errors are present in the record. \n" "Correct them now, to continue \n" "or delete the record" msgstr "" "In questo record sono presenti errori. \n" "Correggili ora per continuare \n" "o cancella il record" #: sqlkit/widgets/common/sqlwidget.py:2302 msgid "Validation errors" msgstr "Errori nella validazione" #: sqlkit/widgets/common/sqlwidget.py:2331 msgid "Error" msgstr "Errore" #: sqlkit/widgets/common/sqlwidget.py:2362 msgid "" "You can continue or go back editing. \n" "Read the following warnings to decide" msgstr "" "Puoi continuare o modificare il campo. \n" "Leggi quanto segue per decidere " #: sqlkit/widgets/common/sqlwidget.py:2363 msgid "Validation Warnings" msgstr "Ammonimenti di validazione" #: sqlkit/widgets/mask/mask.py:258 msgid "Add new record" msgstr "Aggiungi un record nuovo" #: sqlkit/widgets/mask/mask.py:262 msgid "Discard changes" msgstr "Annulla le modifiche" #. TIP Modify menu entry in the mask to reread a single record from database #: sqlkit/widgets/mask/mask.py:264 msgid "Refresh this record" msgstr "Rileggo questo record?" #: sqlkit/widgets/mask/mask.py:265 msgid "Reread this record from db" msgstr "Rileggo questo record?" #: sqlkit/widgets/mask/mask.py:268 msgid "Delete this record" msgstr "Cancello questo record?" #: sqlkit/widgets/mask/mask.py:342 sqlkit/widgets/table/table.py:908 msgid "Already at new record" msgstr "Già record nuovo" #: sqlkit/widgets/mask/mask.py:362 #, python-format msgid "New record %s" msgstr "Record nuovo %s" #. TIP message issued when a refresh is done on a deleted record #: sqlkit/widgets/mask/mask.py:405 msgid "The record is no longer present in the database" msgstr "Il record non è più presente nel database." #: sqlkit/widgets/mask/mask.py:473 sqlkit/widgets/table/table.py:838 msgid "Nothing to save" msgstr "Niente da salvare" #: sqlkit/widgets/mask/mask.py:523 #, python-format msgid "Primary key (%s) didn't change. Refusing to save as new" msgstr "" "La chiave primaria (%s) non è stata cambiata. Mi rifiuto di \"salvare " "come nuovo\"" #: sqlkit/widgets/mask/mask.py:537 msgid "Do you want to copy all data to a new record?" msgstr "Vuoi copiare tutti i dati in un nuovo record?" #: sqlkit/widgets/mask/mask.py:585 sqlkit/widgets/table/table.py:982 #, python-format msgid "" "Delete this record?\n" "(%s)" msgstr "" "Cancello questo record\n" "(%s)?" #: sqlkit/widgets/mask/mask.py:654 msgid "No record present" msgstr "Nessun record presente" #: sqlkit/widgets/mask/mask.py:677 msgid "Already last record" msgstr "Già a un record nuovo" #: sqlkit/widgets/mask/mask.py:679 msgid "Already first record" msgstr "Già al primo record" #: sqlkit/widgets/mask/miniwidgets.py:695 #, python-format msgid "A file with name '%s' already exists. Overwrite?" msgstr "Esiste già un record con nome '%s': sovrascrivo?" #: sqlkit/widgets/mask/miniwidgets.py:696 msgid "Upload name conflict" msgstr "Conflitto di nome" #: sqlkit/widgets/mask/miniwidgets.py:888 #, python-format msgid "'%s' may have an invalid value: try completion on that" msgstr "'%s' potrebbe avere un valore non valido. Tenta il completamento" #: sqlkit/widgets/table/columns.py:208 msgid "Editing canceled. Restoring original value" msgstr "Modifica cancellata. Ripristino il valore originale." #: sqlkit/widgets/table/columns.py:848 #, python-format msgid "Add a filter on '%s'" msgstr "Aggiungi un filtro per '%s'" #: sqlkit/widgets/table/columns.py:858 msgid "" "Sort on this column reloading from the database, you can used 's' to sort" " locally" msgstr "" "Ordina su questa colonna ricaricando dal database, puoi usare 's' per " "ordinare localmente" #: sqlkit/widgets/table/columns.py:861 msgid "Sort on this column locally (w/o touching the database)" msgstr "Ordina su questa colonna localmente (senza usare il database)" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:871 msgid "Hide this column" msgstr "Nascondi questa colonna" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:878 msgid "Create total" msgstr "Crea il totale" #. TIP: column menu total #: sqlkit/widgets/table/columns.py:888 #, python-format msgid "Subtotal on %s" msgstr "Subtotale su %s" #. TIP: column menu opt #. TIP: modify menu entry #: sqlkit/widgets/table/columns.py:910 sqlkit/widgets/table/columns.py:1293 #: sqlkit/widgets/table/table.py:287 msgid "Show field" msgstr "Mostra il campo" #: sqlkit/widgets/table/columns.py:911 sqlkit/widgets/table/columns.py:1294 #: sqlkit/widgets/table/table.py:288 msgid "Hide field" msgstr "Nascondi il campo" #: sqlkit/widgets/table/columns.py:938 msgid "day" msgstr "giorno" #: sqlkit/widgets/table/columns.py:938 msgid "week" msgstr "settimana" #: sqlkit/widgets/table/columns.py:938 msgid "month" msgstr "mese" #: sqlkit/widgets/table/columns.py:939 msgid "quarter" msgstr "trimestre" #: sqlkit/widgets/table/columns.py:939 msgid "year" msgstr "anno" #: sqlkit/widgets/table/columns.py:942 #, python-format msgid "Subtotals by %s" msgstr "Subtotale per %s" #: sqlkit/widgets/table/columns.py:1267 msgid "Right click on this table to show the column again" msgstr "Clock destro sulla tabella per mostrare nuovamente la colonna" #: sqlkit/widgets/table/columns.py:1287 sqlkit/widgets/table/table.py:301 msgid "Export" msgstr "Esporta" #: sqlkit/widgets/table/columns.py:1287 msgid "Export these data into csv format" msgstr "Esporta questi dati in formato csv" #: sqlkit/widgets/table/columns.py:1292 msgid "Adapt width of columns to data" msgstr "Adatta l larghezza delle colonne ai dati" #: sqlkit/widgets/table/columns.py:1321 #, python-format msgid "View '%s' in a Mask" msgstr "Mostra '%s' in una maschera" #: sqlkit/widgets/table/table.py:274 msgid "Duplicate" msgstr "Duplica" #: sqlkit/widgets/table/table.py:275 msgid "Create a new row as a duplicate of this one" msgstr "Crea una nuova riga come duplicato di questa" #: sqlkit/widgets/table/table.py:277 msgid "New child row" msgstr "Nuova riga figlia" #: sqlkit/widgets/table/table.py:278 msgid "Create e new row as child of this one" msgstr "Crea una riga come figlia di questa" #: sqlkit/widgets/table/table.py:292 msgid "View this record in a Mask" msgstr "Mostra questo record in una maschera" #: sqlkit/widgets/table/table.py:293 msgid "View this ForeignKey in a Mask" msgstr "Mostra questa ForeigKey in una maschera" #: sqlkit/widgets/table/table.py:294 msgid "Upload Image" msgstr "Carica Immagine" #: sqlkit/widgets/table/table.py:295 msgid "Upload File" msgstr "Carica un file" #: sqlkit/widgets/table/table.py:296 msgid "Delete File/Image" msgstr "Elimina immagine/file" #: sqlkit/widgets/table/table.py:297 msgid "Show File/Image" msgstr "Mostra il file/immagine" #: sqlkit/widgets/table/table.py:298 msgid "Save File/Image" msgstr "Salva immagine/file" #: sqlkit/widgets/table/table.py:298 msgid "Save file locally" msgstr "Salva il file localmente" #: sqlkit/widgets/table/table.py:528 sqlkit/widgets/table/table.py:542 #: sqlkit/widgets/table/table.py:554 msgid "Insertion of new records is disabled. Sorry" msgstr "L'inserimento di nuovi record è disabilitato" #: sqlkit/widgets/table/table.py:558 msgid "Update of records is disabled. Sorry" msgstr "Spiacente, l'aggiornamento di record è disabilitato. " #: sqlkit/widgets/table/table.py:570 msgid "Deletion of records is disabled. Sorry" msgstr "La cancellazione di record è disabilitata." #: sqlkit/widgets/table/table.py:583 msgid "browsing of new records is disabled. Sorry" msgstr "Non è permesso sfogliare i dati." #: sqlkit/widgets/table/table.py:731 msgid "no current obj: maybe no record has yet been edited" msgstr "non esiste un oggetto in uso: forse nessun record è stato modificato" #. TIP: when saving m2m, we delay till leader record will be saved #: sqlkit/widgets/table/table.py:869 msgid "delaying saving when main record will be saved" msgstr "Ritardo il salvataggio quando verrà salvato il record principale" #: sqlkit/widgets/table/table.py:879 msgid "Record has NOT been saved" msgstr "Il record NON è stato salvato" #: sqlkit/widgets/table/table.py:978 msgid "No record selected" msgstr "Nessun record selezionato" #: sqlkit/widgets/table/table.py:1137 msgid "Value is not valid, trying completion" msgstr "Il valore non è valido: provo il completamento" #. TIP: check in input field length %s %s -> field_name length #: sqlkit/widgets/table/table.py:1170 #, python-format msgid "Input exceeded max %s length (%s)" msgstr "L'input eccede la lunghezza massima per il campo %s (%s)" #: sqlkit/widgets/table/table.py:1284 msgid "Unsaved data prevent opening a Mask to show the (unsaved) record" msgstr "Dati non salvati bloccano l'apertura della maschera per mostrare il record" #: sqlkit/widgets/table/table.py:1407 #, python-format msgid "" "A file with name '%s' already exists.\n" "Overwrite?" msgstr "Esiste già un record con nome '%s': sovrascrivo?" #: sqlkit/widgets/table/table.py:1408 msgid "Upload name duplication" msgstr "Conflitto di nome" #: sqlkit/widgets/table/table.py:1629 msgid "Export in csv file" msgstr "Esporta in un file .csv" #: sqlkit/widgets/table/table.py:1702 msgid "Csv files" msgstr "file csv" #: sqlkit/widgets/table/tablewidgets.py:34 #, python-format msgid "No current obj for field '%s' in %s" msgstr "Nessun oggetto corrente per il campo '%s' in %s" sqlkit-0.9.5/sqlkit/locale/it/LC_MESSAGES/sqlkit.mo0000644000175000017500000004414511714210425021212 0ustar sandrosandroT(6900J5{# 5 DNdy* - =@J +% ( 5?HP bo&- , 6@(S)|BC*-X^=dnVho!   / GSVi{  )(%:N &3:Q-Y!++) <F\7a&! &7"Sv z #( "+FUi 6  78LSb{12/C Ucv|*990+ \,g*+0 A1 7s Q  !!&!6!<";"("/"$#9#J#O#U#m# #@$D$$X$ }$ $ $$$$$$% %&%A%%V%|%%%%%K%*>&i&.m&&& &&&3&'''''9'@'P' V'(c'K'#' '("(z'()-y*@*B*1++1]+=+(++$,7,"W,z, ,,,,1,- '- 4-8U-#---K- ,.:.R."a.,.#....../%/8/P/ m/+x///-/ 0 003*04^0B0R04)1^1e1Gl11[?222"2"2&3(373<3P3b34v3 33333 4#474I4d4j4|474@46 5D5566-69468n6-606+727I7 Y7-e77&7-7&7D8]8f8o8 ~888!8089 99/+98[999 9999 ::0:D:HS:: :Q: ;;5;L;U;k;;;?;=; ,<7<K<d<)}<<<<F<@(=Ci=U=>A><U>%>.>;>J#?=n?Y?@#@)@9@J@DR@@d@A!A!)A KAOlAbA$B,DBqBB B BBnB>CJOCC6CCCDD(D:DUDdDDDDD/DE'*E$REwEEHE!EFAF[FmFtFFFEFF F FGG-G AGOG6_GIG;G9H VH`H You can indicate the database you want to connect to using an URI in the form as: postgres://localhost/dbname sqlite:///dbname or using password and username: mysql://sandro:pass@host/dbname %s - Dirty objects: %s - New objects: %s'%s' may have an invalid value: try completion on thatA complete demo of all the features of the sqlkit packageA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataAdd a filter on '%s'Add a new filter on this field '%s'Add filter panelAdd filters for your queryAdd new recordAll filesAlready at new recordAlready first recordAlready last recordAs LIKE but case insensitiveAttempting to connect to %sBlankCast blank into NULLChar %s is not accepted in numeric contextClick here to select an operatorClose the panelCollapse rowConfigure the fields: labels, tooltip, completion, search fieldConnected to %sConnection setupCount recordsCount records in all tablesCreate a new row as a duplicate of this oneCreate e new row as child of this oneCreate totalCsv filesDatabaseDefaultDelete File/ImageDelete imageDelete the imageDelete this recordDelete this record? (%s)DeletedDeletion of records is disabled. SorryDiscard changesDiscarding new objDo you want to copy all data to a new record?DocumentsDuplicateEdit Sqlkit FieldsEdit the referenced table in 'mask' modeEdit the referenced table in 'table' modeEdit the table from which admitted values are taken in 'mask' modeEdit the table from which admitted values are taken in 'table' modeEditing canceled. Restoring original valueEqualErrorError while writing to the database. Original error was: %sError while writing to the database: are you 'saving as new' an already known record? original error was: %sErrors are present in the record. Correct them now, to continue or delete the recordExportExport in csv fileExport these data into csv formatField '%s' cannot be NULLField '%s' cannot have value %sField nameFileFilter PanelFilter actionsFilter panelFind allowed values (Control-Enter/Shift-Enter)Foreign KeyGoGo to filter panelGo to next recordGo to output panelGo to previous recordGood matchGreater or equalGreater then (after than)HelpHide fieldHide this columnHiding field is only supported for TablesID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameIf you write a wrong hostname the application may hang some seconds till the natural network timeout. Uncheck the flag on the right ti disable this featureImage viewerImagesIncomplete date formatIndexesInfo on available backend: postgres, mysql...Input exceeded max %s length (%s)Insertion of new records is disabled. SorryInspect widgetsLIKE: a "%" means any char - case sensitiveLess than (before then)Less than or equalLoad dataLoad the data as wellMaskMatch as LIKE case insensitive, "%" automatically addedMatch as LIKE, "%" automatically addedMatch as regexpMatch as regexp, case insensitiveModifiedModifyN.RowsNULL valueNegation of ILIKENegation of LIKENegation of match as regexpNegation of match case insensitiveNewNew child rowNew record %sNo current obj for field '%s' in %sNo exact match, trying regexp completionNo record presentNo record selectedNot equalNothing to saveNullableOpen separate image viewerOriginal valuePending DifferencesPreferred new filename:Present valuePress Esc to close or double click a date to select itPrim. KeysPrimary KeyPrimary key (%s) didn't change. Refusing to save as newPropertyRecord has NOT been savedRefresh this recordReloadReload from dbReload from the databaseReread this record from dbResult page for your queryRight click on the image area to upload an imageRight click on this table to show the column againRun DemoSave File/ImageSave current recordSave file locallySave image asSave unsaved data?SavedShow File/ImageShow all info on this field, db type & Co.Show all the differences that should be saved to databaseShow all values in the database starting with your stringShow all values in the db that match your stringShow fieldShow possible values, starting - Shift-EnterShow possible values: regexp - Ctrl-EnterShow/Hide image nameShow/Hide primary key if the key is numericSorry, export is implemented only for table viewSorry, problems connecting to remote db. Original error was: %sSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallySqledit manual pageStateSubtotal on %sSubtotals by %sTableTable %s doesn't have a primary key, editing is not possibleTable reflectionThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sThe boolean is FalseThe boolean is TrueThe boolean is not FalseThe boolean is not TrueThe demo was not found, if you know where it is, run it manually: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseThe value is not setThe value is setToolToolsTotal N. of records: %sTry continuosly to connect. Nice and usefull but may cause temporary blocks if you write an inexistent hostname Unsaved dataUnsaved data prevent opening a Mask to show the (unsaved) recordUnsaved differencesUpdate of records is disabled. SorryUpload FileUpload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUse this filterValidation WarningsValidation errorsValueValue '%s' is not acceptedValue cannot be NULLValue is not valid, trying completionView '%s' in a MaskView this ForeignKey in a MaskView this record in a MaskWrong date format: %sWrong time formatYou can continue or go back editing. Read the following warnings to decidebrowsing of new records is disabled. Sorrydaydelaying saving when main record will be savedempty string: ''filterfilter_tablesincomplete date formatmonthno current obj: maybe no record has yet been editedoptsoutputquarterrecord validationregexpsearch mode: %sstartupload imagevalue '%s' cannot be used for field '%s'value '%s' does not seem a valid date and cannot be transformed into a datevalue is not Decimal nor string: %svalue is not date nor string: %sweekyearProject-Id-Version: sqlkit 0.8.5-pre Report-Msgid-Bugs-To: sandro@e-den.it POT-Creation-Date: 2009-01-07 19:38+0100 PO-Revision-Date: 2011-03-13 00:10+0100 Last-Translator: Sandro Language-Team: sqlkit Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.4 Puoi indicare il database a cui connetterti usando un URI in una delle forme seguenti: postgres://localhost/dbname sqlite:///dbname o usando password e username: mysql://sandro:pass@host/dbname%s Record non salvati: %s -- Record nuovi: %s'%s' potrebbe avere un valore non valido. Tenta il completamentoUna demo completa di tutte le caratteristiche del pacchetto sqlkitEsiste già un record con nome '%s': sovrascrivo?Esiste già un record con nome '%s': sovrascrivo?Un errore di programmazione è stato ricevuto dal backend: %sAdatta l larghezza delle colonne ai datiAggiungi un filtro per '%s'Aggiungi un filtro per il campo '%s'Aggiungi il pannello dei filtriAggiungi filtri per la tua ricercaAggiungi un record nuovoTutti i fileGià record nuovoGià al primo recordGià a un record nuovoCome LIKE ma ignora diferenza maiuscole minuscoleSto connettendomi a %sValori NulliTrasforma stringhe vuote in NULLIl carattere %s non è utilizzabile in un campo numericoClicca per selezionare un operatorePannello dei filtriNascondi le informazioni Configura i campi: etichette, suggerimenti, completamento, campi di ricercaConnesso a %sSetup della connessioneConta i recordConta i record in tutte le tabelleCrea una nuova riga come duplicato di questaCrea una riga come figlia di questaCrea il totalefile csvDatabaseDefaultElimina immagine/fileElimina immagineElimina l'immagineCancello questo record?Cancello questo record (%s)?CancellatoLa cancellazione di record è disabilitata.Annulla le modificheElimino il nuovo oggettoVuoi copiare tutti i dati in un nuovo record?DocumentiDuplicaEdita i campi di sqlkitEdita la tabella referenziata in modalità mascheraEdita la tabella referenziata in modalità 'tabella'Edita la tabella dove sono definiti i valori ammessi come mascheraEdita la tabella nella quale sono definiti i valori ammessi in modalità 'tabella'Modifica cancellata. Ripristino il valore originale.UgualeErroreErrore durante la scrittura nel database: L' errore originale era: %sErrore durante la scrittura nel database: stai 'salvando come nuovo' un record già noto (stessa primary key) L' errore originale era: %sIn questo record sono presenti errori. Correggili ora per continuare o cancella il recordEsportaEsporta in un file .csvEsporta questi dati in formato csvil campo '%s' non può esser NULLOil campo '%s' non può avere valore %sNome del campoFilePannello dei filtriAzioni sui filtriPannello dei filtriTrova i valori permessi (Control-Invio/Maiusc-Invio)Foreig KeyVaiVai al pannello dei filtriVai al prossimo recordVai alla scheda dei risultatiVai al record precedenteRiferimento trovatoMaggiore o ugualePiù grande di (o dopo di)AiutoNascondi il campoNascondi questa colonnaE' possibile nascondere le colonne solo in modo TabellaUguaglianza esatta dell'ID (non seguire la tabella referenziata)Se valorizzato, il file sarà caricato con questo nomeSe scrivi un nome host errato l'applicazione potrebbe bloccarsi alcuni secondi fino al timeout di rete Smarca la casella a fianco per disabilitare questo comportamentoVisualizzatore immagineImmaginiFormato di data incompleto: %sIndiciInformazioni sui backend disponibili: postgres, mysql,...L'input eccede la lunghezza massima per il campo %s (%s)L'inserimento di nuovi record è disabilitatoIspeziona la gerarchia degli oggetti GTK (debug)LIKE: un '%' significa qualunque carattereMinore di (o prima di)Minore o ugualeCarica datiCarica anche i dati (quando apre una tabella)MascheraCome LIKE, ignora differenze maiuscoleCome LIKE, '%' viene aggiunto automaticamenteCorrispondenza su espressione regolareCorrispondenza su espressione regolare, ignora differenza maiuscole ModificaModificaNumero RecordsValore NULLONegazione di ILIKENegazione di LIKENegazione di espressione regolareNegazione espressione regolare, ignora maiuscoleNuoviNuova riga figliaRecord nuovo %sNessun oggetto corrente per il campo '%s' in %sNon c'è un riferimento corretto, cerco un completamentoNessun record presenteNessun record selezionatoNon ugualeNiente da salvareNullo?Apri un visualizzatore separatoValore originaleModifiche non salvateNome file preferitoValore attualePremi 'Esc' per chiudere o fai doppio click su una data per selezionarlaChiave primariaChiave Prim.La chiave primaria (%s) non è stata cambiata. Mi rifiuto di "salvare come nuovo"ProprietàIl record NON è stato salvatoRileggo questo record?RicaricaRicarica dal databaseCarica dal databaseRileggo questo record?Risultati della ricercaClick destro sulla area dell'immagine per caricare una immagineClock destro sulla tabella per mostrare nuovamente la colonnaAvvia DemoSalva immagine/fileSalva il record correnteSalva il file localmenteMostra/Nascondi il percorso dell'immagineSalvo i dati non salvati?SalvatoMostra il file/immagineMostra tutte le informazioni su questo campo (tipo del database & Co.)Mostra tutte le modifiche che dovrebbero essere salvate nel dataMostra tutti i valori nel database che cominciano con la tua stringMostra tutti valori del database che contengono la tua stringa (espressione regolare)Mostra il campoMostra possibili valori con match sulle prima parole Maiusc-InvioMostra possibili valori con espressione regolare: Ctrl-InvioMostra/Nascondi il nome dell'immagineMostra/nascondi la chiave primaria se numericaSpiacente: è possibile esportare dati solo in modo TabellaSpiacente, problemi nella connessione al db. L' errore originale era: %sOrdina su questa colonna localmente (senza usare il database)Ordina su questa colonna ricaricando dal database, puoi usare 's' per ordinare localmentePagina di manuale di SqleditStatoSubtotale su %sSubtotale per %sTabellaLa tabella %s non ha una chiave primaria. Non è possibile editarla.Introspezione della tabellaLa migliore rappresentazione di un record come combinazione di campi, ad esempio: %(title)s %(year)sÈ FalsoÈ veroIl valore boolenao non è 'False'Il valore booleano non è 'vero'La demo non è stata trovata se sai dove è provala manualmente: python demo.pyIl campo che sarà utilizzato nella ricerca quando viene usato il completamento. Es.: description Nome della tabella da personalizzareIl record non è più presente nel database.Il valore non è impostatoIl valore è impostatoStrumentiStrumentiN. totale di record: %sProva a connettersi continuamente: utile ma può causare blocchi temporanei se scrivi un nome host inesistenteDati non salvatiDati non salvati bloccano l'apertura della maschera per mostrare il recordModifiche non salvateSpiacente, l'aggiornamento di record è disabilitato. Carica un fileCarica ImmagineCarica ImmagineConflitto di nomeConflitto di nomeCarica o modifica immagineAbilita filtroAmmonimenti di validazioneErrori nella validazioneValoreIl valore '%s' non è permessoIl valore non può essere NULLOIl valore non è valido: provo il completamentoMostra '%s' in una mascheraMostra questa ForeigKey in una mascheraMostra questo record in una mascheraFormato data errato: %sFormato di tempo erratoPuoi continuare o modificare il campo. Leggi quanto segue per decidere Non è permesso sfogliare i dati.giornoRitardo il salvataggio quando verrà salvato il record principalestringa vuota: ''FiltriFiltra tabelleFormato data incompletomesenon esiste un oggetto in uso: forse nessun record è stato modificatoOpzioniRisultatitrimestrevalidazione di recordespressione regolaremodo di ricerca: %sinizio parolaCarica Immagineil valore '%s' non può essere usato per il campo '%s'Il valore '%s' non è una data e non può essere trasformato in una data.il valore fornito non è né un Decimal né una stringa: %sil valore fornito non è né una data né una stringa: %ssettimanaannosqlkit-0.9.5/sqlkit/locale/de/0000755000175000017500000000000011714210425015525 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/de/LC_MESSAGES/0000755000175000017500000000000011714210425017312 5ustar sandrosandrosqlkit-0.9.5/sqlkit/locale/de/LC_MESSAGES/sqlkit.po0000644000175000017500000007533311714210425021174 0ustar sandrosandro# German translations for PROJECT. # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , 2011. # Sandro , 2011. msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2011-03-03 12:33+0100\n" "PO-Revision-Date: 2011-03-13 00:10+0200\n" "Last-Translator: Sandro \n" "Language-Team: sqlkit\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Virtaal 0.5.2\n" "Generated-By: Babel 0.9.4\n" #: bin/sqledit.py:87 msgid "Connection setup" msgstr "Verbindungsaufbau" #: bin/sqledit.py:90 msgid "" "\n" " You can indicate the database you want to connect to\n" " using an URI in the form as:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " or using password and username:\n" " mysql://sandro:pass@host/dbname\n" " " msgstr "" "\n" " Sie können die Datenbank für die Verbindung angeben\n" " mit einer URI in der Form von:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " oder mit Passwort und Benutzername:\n" " mysql://sandro:pass@host/dbname\n" " " #: bin/sqledit.py:117 msgid "Run Demo" msgstr "Demo ausführen" #: bin/sqledit.py:140 msgid "" "Info on available backend:\n" "postgres, mysql..." msgstr "" "Information zu verfügbaren \"Backends\":\n" "postgres, mysql..." #: bin/sqledit.py:141 msgid "Sqledit manual page" msgstr "sqledit Handbuch" #: bin/sqledit.py:146 msgid "" "Try continuosly to connect.\n" " Nice and usefull but may cause temporary blocks\n" " if you write an inexistent hostname\n" " " msgstr "" "Versuch ständig eine Verbindung herzustellen.\n" " Nützlich, kann aber eine vorübergehende Blockierung verursachen\n" " wenn Sie einen nicht existierenden host Namen eingeben.\n" #: bin/sqledit.py:160 msgid "" "If you write a wrong hostname\n" "the application may hang some seconds till the natural network timeout. \n" "Uncheck the flag on the right ti disable this feature" msgstr "" "Wenn Sie einen falschen host Namen eingeben\n" "kann es sein das die Applikation für einige Sekunden blockiert (hängt vom" " Network timeout ab).\n" "Diese Funktion können Sie rechts deaktivieren." #: bin/sqledit.py:187 #, python-format msgid "Attempting to connect to %s" msgstr "Versuche Verbindung mit %s aufzubauen" #: bin/sqledit.py:190 #, python-format msgid "Connected to %s" msgstr "'Verbunden zu %s" #: bin/sqledit.py:221 msgid "A complete demo of all the features of the sqlkit package" msgstr "Eine komplette Demostration aller Funktionen des sqlkit Packages." #: bin/sqledit.py:222 msgid "" "The demo was not found, if you know where it is,\n" "run it manually: python demo.py" msgstr "" "'Die Demonstration wurde nicht gefunden, falls Sie wissen wo Sie ist\n" "können Sie manuel ausführen: python demo.py" #: sqlkit/exc.py:48 #, python-format msgid "Field '%s' cannot be NULL" msgstr "Feld '%s' kann nicht NULL sein" #: sqlkit/exc.py:60 #, python-format msgid "Field '%s' cannot have value %s" msgstr "Feld '%s' kann nicht diesen Wert '%s' haben" #: sqlkit/fields.py:979 #, python-format msgid "value is not Decimal nor string: %s" msgstr "Wert ist nicht Decimal oder eine Zeichenfolge: %s" #: sqlkit/fields.py:1055 #, python-format msgid "value is not date nor string: %s" msgstr "Wert ist nicht ein Datum oder eine Zeichenfolge: %s" #: sqlkit/fields.py:1174 sqlkit/widgets/mask/miniwidgets.py:430 #, python-format msgid "Wrong date format: %s" msgstr "Falsches Datumsformat: %s" #: sqlkit/fields.py:1184 msgid "Wrong time format" msgstr "Falsches Zeitformat" #: sqlkit/fields.py:1336 msgid "Value cannot be NULL" msgstr "Wert kann nicht NULL sein" #: sqlkit/fields.py:1340 #, python-format msgid "Value '%s' is not accepted" msgstr "Wert '%s' ist nicht akzeptiert" #: sqlkit/db/proxy.py:219 #, python-format msgid "Table %s doesn't have a primary key, editing is not possible" msgstr "Tabelle '%s' hat keinen Primärschlüssel, Bearbeiten ist nicht möglich" #: sqlkit/layout/dateedit.py:190 msgid "Press Esc to close or double click a date to select it" msgstr "" "Klicken Sie Esc um zu schliessen oder Doppelklick ein Datum um es zu " "selektieren" #: sqlkit/layout/fk_entry.py:25 msgid "Find allowed values (Control-Enter/Shift-Enter)" msgstr "Finde erlaubte Werte (Control-Enter/Shift-Enter)" #: sqlkit/layout/image_widget.py:142 msgid "" "Right click on the image area\n" " to upload an image" msgstr "" "Rechts Klicken auf dem Bild\n" "um das Bild hoch zu laden" #: sqlkit/layout/image_widget.py:273 sqlkit/widgets/table/table.py:1395 msgid "Upload image" msgstr "Bild hochladen" #: sqlkit/layout/image_widget.py:273 msgid "Upload or modify an image" msgstr "Bild hochladen oder ändern" #: sqlkit/layout/image_widget.py:274 msgid "Delete image" msgstr "Bild löschen" #: sqlkit/layout/image_widget.py:274 msgid "Delete the image" msgstr "Das Bild löschen" #: sqlkit/layout/image_widget.py:275 msgid "Image viewer" msgstr "Bildbetrachter" #: sqlkit/layout/image_widget.py:275 msgid "Open separate image viewer" msgstr "Einen separaten Bildbetrachter öffnen" #: sqlkit/layout/image_widget.py:276 msgid "Save image as" msgstr "Bild speichern unter" #: sqlkit/layout/image_widget.py:279 msgid "Show/Hide image name" msgstr "Anzeigen/Verbergen Bildname" #: sqlkit/layout/image_widget.py:317 msgid "upload image" msgstr "Bild hochladen" #: sqlkit/misc/datetools.py:152 msgid "Incomplete date format" msgstr "Unvollständiges Datumsformat" #: sqlkit/misc/table_browser.py:135 msgid "Database" msgstr "Datenbank" #. TIP: menu entry #: sqlkit/misc/table_browser.py:136 sqlkit/misc/words.py:13 #: sqlkit/widgets/common/sqlwidget.py:795 msgid "Modify" msgstr "Verändern" #: sqlkit/misc/table_browser.py:137 msgid "Tool" msgstr "Werkzeug" #. TIP: menu entry #: sqlkit/misc/table_browser.py:138 sqlkit/misc/words.py:15 #: sqlkit/widgets/common/sqlwidget.py:798 msgid "Help" msgstr "Hilfe" #: sqlkit/misc/table_browser.py:141 msgid "Count records" msgstr "Anzahl Datensätze" #: sqlkit/misc/table_browser.py:141 msgid "Count records in all tables" msgstr "Anzahl Datensätze in allen Tabellen" #: sqlkit/misc/table_browser.py:144 msgid "" "Configure the fields: \n" "labels, tooltip, completion, search field" msgstr "" "Die Felder konfigurieren:\n" "Kennzeichen, Tooltip, Vervollständigung, Suchfeld" #: sqlkit/misc/table_browser.py:147 msgid "Edit Sqlkit Fields" msgstr "Bearbeiten sqlkit Felder" #: sqlkit/misc/table_browser.py:150 msgid "Primary Key" msgstr "Primärschlüssel" #: sqlkit/misc/table_browser.py:151 msgid "Show/Hide primary key if the key is numeric" msgstr "Anzeigen/Verbergen Primärschlüssel wenn der Schlüssel numerisch ist" #: sqlkit/misc/table_browser.py:152 msgid "Load data" msgstr "Laden der Daten" #: sqlkit/misc/table_browser.py:153 msgid "Load the data as well" msgstr "Laden auch der Daten" #: sqlkit/misc/table_browser.py:154 msgid "Blank" msgstr "Leer" #: sqlkit/misc/table_browser.py:154 msgid "Cast blank into NULL" msgstr "Cast \"leer\" zu NULL" #: sqlkit/misc/table_browser.py:262 msgid "Mask" msgstr "Maske" #: sqlkit/misc/table_browser.py:266 msgid "Table" msgstr "Tabelle" #: sqlkit/misc/table_browser.py:272 msgid "Collapse row" msgstr "Zeile zusammenklappen" #: sqlkit/misc/table_browser.py:276 msgid "Table reflection" msgstr "Tabelle Reflexion" #: sqlkit/misc/table_browser.py:423 msgid "The name of the table we are customizing" msgstr "Der Name der Tabelle die wir anpassen" #: sqlkit/misc/table_browser.py:424 #, python-format msgid "" "The best representation of a record \n" "as a combination of fields, e.g.: %(title)s %(year)s" msgstr "" "Die beste Darstellung eines Datensatzes\n" "als eine Kombination von Feldern, z.B. %(title)s %(year)s" #: sqlkit/misc/table_browser.py:427 msgid "The field that will be searched for when completion is used" msgstr "Das Feld das gesucht wird wenn Vervollständigung benutzt wird" #. TIP: filter page of the filter panel #: sqlkit/misc/words.py:7 msgid "filter" msgstr "Filter" #. TIP: output page of the filter panel #: sqlkit/misc/words.py:9 sqlkit/widgets/common/sqlfilter.py:259 #: sqlkit/widgets/common/sqlfilter.py:306 msgid "output" msgstr "Ausgabe" #. TIP: menu entry #: sqlkit/misc/words.py:11 msgid "File" msgstr "Datei" #. TIP: menu entry #: sqlkit/misc/words.py:17 sqlkit/widgets/common/sqlwidget.py:796 msgid "Go" msgstr "Go" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:19 msgid "opts" msgstr "opts" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:21 sqlkit/widgets/common/sqlwidget.py:797 msgid "Tools" msgstr "Werkzeuge" #. TIP: serach mode: #: sqlkit/misc/words.py:23 msgid "regexp" msgstr "'regexp'" #. TIP: search mode #: sqlkit/misc/words.py:25 msgid "start" msgstr "Beginn" #. TIP: count records in sqledit #: sqlkit/misc/words.py:27 msgid "N.Rows" msgstr "N. Zeilen" #: sqlkit/misc/words.py:29 msgid "Indexes" msgstr "Indizes" #: sqlkit/misc/words.py:30 msgid "Nullable" msgstr "Nullable" #: sqlkit/misc/words.py:31 msgid "Prim. Keys" msgstr "Primärschlüssel" #: sqlkit/misc/words.py:32 msgid "Default" msgstr "Vorgabe" #: sqlkit/misc/words.py:33 msgid "Foreign Key" msgstr "Fremdschlüssel" #: sqlkit/misc/words.py:34 msgid "filter_tables" msgstr "Filter_Tabellen" #. TIP: status bar #: sqlkit/widgets/common/completion.py:285 #: sqlkit/widgets/common/completion.py:1124 #, python-format msgid "search mode: %s" msgstr "Such modus: %s" #: sqlkit/widgets/common/completion.py:643 msgid "Show all info on this field, db type & Co." msgstr "Alle Informationen zu diesem Feld anzeigen, db, type & Co." #. TIP: menu enty in menu on right click on down arro in fkey completion widget #: sqlkit/widgets/common/completion.py:649 msgid "Show possible values: regexp - Ctrl-Enter" msgstr "Zeige mögliche Werte an: 'regexp' - Ctrl-Enter" #. TIP: yellow tip to menu entry in down arrow in completion widget #: sqlkit/widgets/common/completion.py:653 msgid "Show all values in the db that match your string" msgstr "Zeige alle Werte in der Datenbank an, die der Zeichenfolge entsprechen" #: sqlkit/widgets/common/completion.py:658 msgid "Show possible values, starting - Shift-Enter" msgstr "Zeige mögliche Werte an, anfangen - Shift-Enter" #: sqlkit/widgets/common/completion.py:661 msgid "Show all values in the database starting with your string" msgstr "Zeige alle Werte in der Datenbank die mit Ihrer Zeichenfolge anfangen" #: sqlkit/widgets/common/completion.py:667 msgid "Edit the referenced table in 'table' mode" msgstr "Ändere die referenz Tabelle im \"Tabellen\" Modus" #: sqlkit/widgets/common/completion.py:669 msgid "Edit the table from which admitted values are taken in 'table' mode" msgstr "" "Ändere die Tabelle von welcher gültige Werte entnommen werden im " "'Tabellen' Modus" #: sqlkit/widgets/common/completion.py:674 msgid "Edit the referenced table in 'mask' mode" msgstr "Änder die referenz Tabelle im 'Maske\" Modus" #: sqlkit/widgets/common/completion.py:676 msgid "Edit the table from which admitted values are taken in 'mask' mode" msgstr "" "Ändere die Tabelle von welcher gültige Werte entnommen werden im 'Masken'" " Modus" #: sqlkit/widgets/common/completion.py:783 msgid "Good match" msgstr "Guter 'match'" #: sqlkit/widgets/common/completion.py:792 #: sqlkit/widgets/common/completion.py:798 msgid "No exact match, trying regexp completion" msgstr "Kein genauer 'match', versuche 'regexp' Vervollständigung" #: sqlkit/widgets/common/dialogs.py:26 sqlkit/widgets/common/dialogs.py:94 #: sqlkit/widgets/table/table.py:1703 msgid "All files" msgstr "Alle Dateien" #: sqlkit/widgets/common/dialogs.py:100 msgid "Images" msgstr "Bilder" #: sqlkit/widgets/common/dialogs.py:106 msgid "Documents" msgstr "Dokumente" #. TIP: possible new name for an uploaded file #: sqlkit/widgets/common/dialogs.py:123 msgid "Preferred new filename:" msgstr "Bevorzugter neuer Dateiname:" #: sqlkit/widgets/common/dialogs.py:124 msgid "If not empty, the file will be uploaded with this new name" msgstr "Falls nicht leer wird die Datei mit diesem neuen Namen hochgeladen" #: sqlkit/widgets/common/sqlfilter.py:107 #: sqlkit/widgets/common/sqlfilter.py:110 msgid "Match as regexp" msgstr "'match' als 'regexp'" #: sqlkit/widgets/common/sqlfilter.py:108 msgid "Match as LIKE, \"%\" automatically added" msgstr "'match' als 'LIKE', \"%\" wird automatisch hinzugefügt" #: sqlkit/widgets/common/sqlfilter.py:109 msgid "Match as LIKE case insensitive, \"%\" automatically added" msgstr "'match' als 'LIKE' case insensitive, \"%\" wird automatisch hinzugefügt" #: sqlkit/widgets/common/sqlfilter.py:111 msgid "Match as regexp, case insensitive" msgstr "'match' als 'regexp' - case insensitive" #: sqlkit/widgets/common/sqlfilter.py:112 msgid "Negation of match as regexp" msgstr "Die Negation von 'match' als 'regexp'" #: sqlkit/widgets/common/sqlfilter.py:113 msgid "Negation of match case insensitive" msgstr "Die Negation von 'match' - case insensitive" #: sqlkit/widgets/common/sqlfilter.py:114 msgid "Equal" msgstr "Gleichwertig" #: sqlkit/widgets/common/sqlfilter.py:115 msgid "Not equal" msgstr "Nicht gleichwertig" #: sqlkit/widgets/common/sqlfilter.py:116 msgid "Greater then (after than)" msgstr "Grösser als (nach als)" #: sqlkit/widgets/common/sqlfilter.py:117 msgid "Greater or equal" msgstr "Grösser oder gleichwertig" #: sqlkit/widgets/common/sqlfilter.py:118 msgid "Less than (before then)" msgstr "Kleiner als (bevor dann)" #: sqlkit/widgets/common/sqlfilter.py:119 msgid "Less than or equal" msgstr "Kleiner als oder gleichwertig" #: sqlkit/widgets/common/sqlfilter.py:120 msgid "LIKE: a \"%\" means any char - case sensitive" msgstr "LIKE: das \"%\" bedeutet ein beliebiges Zeichen - case sensitive " #: sqlkit/widgets/common/sqlfilter.py:121 msgid "Negation of LIKE" msgstr "Die Negation von LIKE" #: sqlkit/widgets/common/sqlfilter.py:122 msgid "As LIKE but case insensitive" msgstr "Wie LIKE aber case insensitive" #: sqlkit/widgets/common/sqlfilter.py:123 msgid "Negation of ILIKE" msgstr "Die Negation von ILIKE" #: sqlkit/widgets/common/sqlfilter.py:124 msgid "The boolean is True" msgstr "Der Boolean ist 'True'" #: sqlkit/widgets/common/sqlfilter.py:125 msgid "The boolean is False" msgstr "Der Boolean ist 'False'" #: sqlkit/widgets/common/sqlfilter.py:126 msgid "The boolean is not True" msgstr "Der Boolean ist nicht 'True'" #: sqlkit/widgets/common/sqlfilter.py:127 msgid "The boolean is not False" msgstr "Der Boolean ist nicht 'False'" #: sqlkit/widgets/common/sqlfilter.py:128 msgid "The value is not set" msgstr "Der Wert wurde nicht gesetzt" #: sqlkit/widgets/common/sqlfilter.py:129 msgid "The value is set" msgstr "Der Wert wurde gesetzt" #: sqlkit/widgets/common/sqlfilter.py:130 msgid "ID equality (don't follow foreign table)" msgstr "ID Gleichheit (der Fremdtabelle nicht nachgehen)" #: sqlkit/widgets/common/sqlfilter.py:221 msgid "Filter Panel" msgstr "Filter Panel" #: sqlkit/widgets/common/sqlfilter.py:230 msgid "Add filters for your query" msgstr "Add Filter für Ihre Anfrage" #: sqlkit/widgets/common/sqlfilter.py:232 msgid "Result page for your query" msgstr "Resultat Seite für Ihre Anfrage" #: sqlkit/widgets/common/sqlfilter.py:269 msgid "Filter actions" msgstr "Filter Aktionen" #: sqlkit/widgets/common/sqlfilter.py:270 msgid "Reload from db" msgstr "Neu Laden von der Datenbank" #: sqlkit/widgets/common/sqlfilter.py:271 msgid "Close the panel" msgstr "Das Panel schliessen" #: sqlkit/widgets/common/sqlfilter.py:274 msgid "Go to filter panel" msgstr "Zum Filter Panel gehen" #: sqlkit/widgets/common/sqlfilter.py:276 msgid "Go to output panel" msgstr "Zum Ausgabe Panel gehen" #. TIP: status bar message #: sqlkit/widgets/common/sqlfilter.py:479 sqlkit/widgets/table/table.py:762 #: sqlkit/widgets/table/table.py:1039 #, python-format msgid "Total N. of records: %s" msgstr "Anzahl von Datensätzen: %s" #: sqlkit/widgets/common/sqlfilter.py:801 #, python-format msgid "value '%s' cannot be used for field '%s'" msgstr "Wert '%s' kann für diese Feld '%s' nicht benutz werden" #: sqlkit/widgets/common/sqlfilter.py:868 #, python-format msgid "" "value '%s' does not seem a valid date and cannot be transformed into a " "date" msgstr "" "Wert '%s' schein kein gültiges Datum zu sein und kann nicht in ein Datum " "transformiert werden" #. TIP: appears in the menu in the filter panel to add a second entry of the #. same field #: sqlkit/widgets/common/sqlfilter.py:1042 #, python-format msgid "Add a new filter on this field '%s'" msgstr "Einen neun Filter für dieses Feld '%s' hinzufügen" #: sqlkit/widgets/common/sqlfilter.py:1048 msgid "Use this filter" msgstr "Diesen Filter benutzen" #: sqlkit/widgets/common/sqlfilter.py:1092 msgid "Click here to select an operator" msgstr "Hier klicken um " #: sqlkit/widgets/common/sqlfilter.py:1164 msgid "incomplete date format" msgstr "Unvollständiges Datumsformat" #: sqlkit/widgets/common/sqlwidget.py:358 #, python-format msgid "" "Sorry, problems connecting to remote db. Original error was: \n" "\n" "%s" msgstr "" "Sorry, Problem mit remote Datenbank Verdingung. Orginaller Fehler war:\n" "\n" "%s" #: sqlkit/widgets/common/sqlwidget.py:625 msgid "Hiding field is only supported for Tables" msgstr "Verbergen eines Feldes ist nur für Tabellen unterstützt" #: sqlkit/widgets/common/sqlwidget.py:792 msgid "Show all the differences that should be saved to database" msgstr "Alle Unterschiede anzeigen die der Datenbank gespeichert werden sollten " #: sqlkit/widgets/common/sqlwidget.py:802 msgid "Pending Differences" msgstr "Anstehende Unterschiede" #: sqlkit/widgets/common/sqlwidget.py:806 msgid "Save current record" msgstr "Speichern des aktuellen Datensatzes" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Filter panel" msgstr "Filter Panel" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Add filter panel" msgstr "Filter Panel hinzufügen" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload" msgstr "Neu Laden" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload from the database" msgstr "Neu von der Datenbank laden" #: sqlkit/widgets/common/sqlwidget.py:815 msgid "Go to next record" msgstr "Gehe zum nächsten Datensatz" #: sqlkit/widgets/common/sqlwidget.py:816 msgid "Go to previous record" msgstr "Gehe zum vorhergehenden Datensatz" #: sqlkit/widgets/common/sqlwidget.py:823 msgid "Inspect widgets" msgstr "Überprüfe 'widgets'" #: sqlkit/widgets/common/sqlwidget.py:990 msgid "Saved" msgstr "Gesichert" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1020 #, python-format msgid "" "Error while writing to the database. \n" "Original error was: \n" "%s" msgstr "" "Fehler beim Schreiben in die Datenbank.\n" "Orignaller Fehler war:\n" "%s" #. TIP: Error while saving into a table w/o permission #. TIP: reloading data from the database #: sqlkit/widgets/common/sqlwidget.py:1026 #: sqlkit/widgets/common/sqlwidget.py:1563 #, python-format msgid "" "A programming error was received from the backend:\n" "%s" msgstr "" "Ein Programmier Fehler wurde vom Backend erhalten:\n" "%s" #: sqlkit/widgets/common/sqlwidget.py:1035 #, python-format msgid "%s - Dirty objects: %s - New objects: %s" msgstr "%s - Schmutzige Objekte: %s - Neue Objekte: %s" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1041 #, python-format msgid "" "Error while writing to the database: \n" "are you 'saving as new' an already known record?\n" "original error was: \n" "%s" msgstr "" "Fehler beim schreiben in die Datenbank:\n" "Speichern Sie \"als Neu\" einen Datensatz der schon existiert?\n" "Original Fehler war:\n" "%s" #: sqlkit/widgets/common/sqlwidget.py:1186 msgid "Discarding new obj" msgstr "Verwerfe neues Objekt" #: sqlkit/widgets/common/sqlwidget.py:1223 #, python-format msgid "Char %s is not accepted in numeric context" msgstr "Char %s ist im numerischen Kontext nicht akzeptiert" #: sqlkit/widgets/common/sqlwidget.py:1281 msgid "record validation" msgstr "Datensatz Validierung" #: sqlkit/widgets/common/sqlwidget.py:1503 msgid "Unsaved differences" msgstr "Ungesicherte Differenzen" #: sqlkit/widgets/common/sqlwidget.py:1741 msgid "Sorry, export is implemented only for table view" msgstr "Sorry, Export ist nur für Tabellenansicht implementiert" #: sqlkit/widgets/common/sqlwidget.py:2097 msgid "Property" msgstr "Property" #: sqlkit/widgets/common/sqlwidget.py:2098 msgid "Value" msgstr "Wert" #: sqlkit/widgets/common/sqlwidget.py:2184 msgid "Save unsaved data?" msgstr "Sichere ungesicherte Daten" #: sqlkit/widgets/common/sqlwidget.py:2187 msgid "Unsaved data" msgstr "Ungesicherte Daten" #: sqlkit/widgets/common/sqlwidget.py:2209 msgid "State" msgstr "Zustand" #: sqlkit/widgets/common/sqlwidget.py:2210 #: sqlkit/widgets/common/sqlwidget.py:2330 msgid "Field name" msgstr "Feldname" #: sqlkit/widgets/common/sqlwidget.py:2211 msgid "Original value" msgstr "Orginal Wert" #: sqlkit/widgets/common/sqlwidget.py:2212 msgid "Present value" msgstr "Gegenwertiger Wert" #: sqlkit/widgets/common/sqlwidget.py:2242 msgid "Modified" msgstr "Geändert" #: sqlkit/widgets/common/sqlwidget.py:2269 sqlkit/widgets/mask/mask.py:597 msgid "Deleted" msgstr "Gelöscht" #: sqlkit/widgets/common/sqlwidget.py:2279 msgid "New" msgstr "Neu" #: sqlkit/widgets/common/sqlwidget.py:2293 msgid "empty string: ''" msgstr "Leere Zeichenfolge: ''" #: sqlkit/widgets/common/sqlwidget.py:2295 msgid "NULL value" msgstr "NULL Wert" #: sqlkit/widgets/common/sqlwidget.py:2301 msgid "" "Errors are present in the record. \n" "Correct them now, to continue \n" "or delete the record" msgstr "" "Der Datensatz enthält Fehler.\n" "Entweder jetzt korrigieren, um weiter zu machen\n" "oder den Datensatz löschen" #: sqlkit/widgets/common/sqlwidget.py:2302 msgid "Validation errors" msgstr "Validierungsfehlern" #: sqlkit/widgets/common/sqlwidget.py:2331 msgid "Error" msgstr "Fehler" #: sqlkit/widgets/common/sqlwidget.py:2362 msgid "" "You can continue or go back editing. \n" "Read the following warnings to decide" msgstr "" "Sie können weiter machen oder gehen zurück zur Bearbeitung.\n" "Lesen Sie den folgenden Warnhinweis um zu entscheiden " #: sqlkit/widgets/common/sqlwidget.py:2363 msgid "Validation Warnings" msgstr "Validierungswarnung" #: sqlkit/widgets/mask/mask.py:258 msgid "Add new record" msgstr "Neuen Datensatz hinzufügen" #: sqlkit/widgets/mask/mask.py:262 msgid "Discard changes" msgstr "Änderungen verwerfen" #. TIP Modify menu entry in the mask to reread a single record from database #: sqlkit/widgets/mask/mask.py:264 msgid "Refresh this record" msgstr "Datensatz aktualisieren " #: sqlkit/widgets/mask/mask.py:265 msgid "Reread this record from db" msgstr "Datensatz neu von der Datenbank lesen" #: sqlkit/widgets/mask/mask.py:268 msgid "Delete this record" msgstr "Datensatz löschen" #: sqlkit/widgets/mask/mask.py:342 sqlkit/widgets/table/table.py:908 msgid "Already at new record" msgstr "Schon im \"Neuer Datensatz\" modus" #: sqlkit/widgets/mask/mask.py:362 #, python-format msgid "New record %s" msgstr "Neuen Datensatz %s" #. TIP message issued when a refresh is done on a deleted record #: sqlkit/widgets/mask/mask.py:405 msgid "The record is no longer present in the database" msgstr "Dieser Datensatz ist nicht mehr in der Datenbank" #: sqlkit/widgets/mask/mask.py:473 sqlkit/widgets/table/table.py:838 msgid "Nothing to save" msgstr "Nichts zu sichern" #: sqlkit/widgets/mask/mask.py:523 #, python-format msgid "Primary key (%s) didn't change. Refusing to save as new" msgstr "" "Primärschlüssel (%s) ist nicht geändert, kann nicht als Neu gespeichert " "werden" #: sqlkit/widgets/mask/mask.py:537 msgid "Do you want to copy all data to a new record?" msgstr "Wollen Sie alle Daten in einen neuen Datensatz kopieren?" #: sqlkit/widgets/mask/mask.py:585 sqlkit/widgets/table/table.py:982 #, python-format msgid "" "Delete this record?\n" "(%s)" msgstr "" "Diesen Datensatz löschen?\n" "(%s)" #: sqlkit/widgets/mask/mask.py:654 msgid "No record present" msgstr "Kein Datensatz vorhanden" #: sqlkit/widgets/mask/mask.py:677 msgid "Already last record" msgstr "Schon beim letzen Datensatz" #: sqlkit/widgets/mask/mask.py:679 msgid "Already first record" msgstr "Schon beim ersten Datensatz" #: sqlkit/widgets/mask/miniwidgets.py:695 #, python-format msgid "A file with name '%s' already exists. Overwrite?" msgstr "Eine Datei mit Namen \"%s\" existiert schon. Überschreiben?" #: sqlkit/widgets/mask/miniwidgets.py:696 msgid "Upload name conflict" msgstr "Namens konflikt beim hochladen" #: sqlkit/widgets/mask/miniwidgets.py:888 #, python-format msgid "'%s' may have an invalid value: try completion on that" msgstr "'%s' kann einen ungültigen Wert haben: versuche Vervollständigung" #: sqlkit/widgets/table/columns.py:208 msgid "Editing canceled. Restoring original value" msgstr "Bearbeiten annuliert. Wiederherstellung des Orginal Wertes" #: sqlkit/widgets/table/columns.py:848 #, python-format msgid "Add a filter on '%s'" msgstr "Einen Filter für '%s' hinzufügen" #: sqlkit/widgets/table/columns.py:858 msgid "" "Sort on this column reloading from the database, you can used 's' to sort" " locally" msgstr "" "Sortieren dieser Spalte und neu laden von der Datenbank, Sie können 's' " "benutzen um Lokal zu sortieren" #: sqlkit/widgets/table/columns.py:861 msgid "Sort on this column locally (w/o touching the database)" msgstr "Sortierung der Spalte lokal (ohne die Datenbank zu berühren)" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:871 msgid "Hide this column" msgstr "Diese Spalte verbergen" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:878 msgid "Create total" msgstr "Summe anlegen" #. TIP: column menu total #: sqlkit/widgets/table/columns.py:888 #, python-format msgid "Subtotal on %s" msgstr "Zwischensumme auf %s" #. TIP: column menu opt #. TIP: modify menu entry #: sqlkit/widgets/table/columns.py:910 sqlkit/widgets/table/columns.py:1293 #: sqlkit/widgets/table/table.py:287 msgid "Show field" msgstr "Feld anzeigen" #: sqlkit/widgets/table/columns.py:911 sqlkit/widgets/table/columns.py:1294 #: sqlkit/widgets/table/table.py:288 msgid "Hide field" msgstr "Feld verbergen" #: sqlkit/widgets/table/columns.py:938 msgid "day" msgstr "Tag" #: sqlkit/widgets/table/columns.py:938 msgid "week" msgstr "Woche" #: sqlkit/widgets/table/columns.py:938 msgid "month" msgstr "Monat" #: sqlkit/widgets/table/columns.py:939 msgid "quarter" msgstr "Quartal" #: sqlkit/widgets/table/columns.py:939 msgid "year" msgstr "Jahr" #: sqlkit/widgets/table/columns.py:942 #, python-format msgid "Subtotals by %s" msgstr "Zwischensumme auf %s" #: sqlkit/widgets/table/columns.py:1267 msgid "Right click on this table to show the column again" msgstr "Rechts auf dies Tabelle klicken um the Spalte wieder anzuzeigen" #: sqlkit/widgets/table/columns.py:1287 sqlkit/widgets/table/table.py:301 msgid "Export" msgstr "Exportieren" #: sqlkit/widgets/table/columns.py:1287 msgid "Export these data into csv format" msgstr "Exportiere diese Daten in das 'CSV' Format" #: sqlkit/widgets/table/columns.py:1292 msgid "Adapt width of columns to data" msgstr "Breite diese Spalte den Daten anpassen" #: sqlkit/widgets/table/columns.py:1321 #, python-format msgid "View '%s' in a Mask" msgstr "'%s' in eine Maske anzeigen" #: sqlkit/widgets/table/table.py:274 msgid "Duplicate" msgstr "Duplikat" #: sqlkit/widgets/table/table.py:275 msgid "Create a new row as a duplicate of this one" msgstr "Eine neu" #: sqlkit/widgets/table/table.py:277 msgid "New child row" msgstr "Neue Unterzeile" #: sqlkit/widgets/table/table.py:278 msgid "Create e new row as child of this one" msgstr "Eine neue Zeile anlegen als Unterzeile von dieser Zeile" #: sqlkit/widgets/table/table.py:292 msgid "View this record in a Mask" msgstr "Diese Datensatz in eine Maske anzeigen" #: sqlkit/widgets/table/table.py:293 msgid "View this ForeignKey in a Mask" msgstr "Diesen Fremdschlüssel in eine Maske anzeigen" #: sqlkit/widgets/table/table.py:294 msgid "Upload Image" msgstr "Bild hochladen" #: sqlkit/widgets/table/table.py:295 msgid "Upload File" msgstr "Datei hochladen" #: sqlkit/widgets/table/table.py:296 msgid "Delete File/Image" msgstr "Datei/Bild löschen" #: sqlkit/widgets/table/table.py:297 msgid "Show File/Image" msgstr "Datei/Bild anzeigen" #: sqlkit/widgets/table/table.py:298 msgid "Save File/Image" msgstr "Datei/Bild sichern" #: sqlkit/widgets/table/table.py:298 msgid "Save file locally" msgstr "Datei lokal sichern" #: sqlkit/widgets/table/table.py:528 sqlkit/widgets/table/table.py:542 #: sqlkit/widgets/table/table.py:554 msgid "Insertion of new records is disabled. Sorry" msgstr "Neue Datensätze anlegen ist deaktiviert. Sorry." #: sqlkit/widgets/table/table.py:558 msgid "Update of records is disabled. Sorry" msgstr "Aktualisierung der Datensätze ist deaktiviert. Sorry" #: sqlkit/widgets/table/table.py:570 msgid "Deletion of records is disabled. Sorry" msgstr "Löschen von Datensätzen ist deaktiviert. Sorry" #: sqlkit/widgets/table/table.py:583 msgid "browsing of new records is disabled. Sorry" msgstr "'Browsing' neuer Datensätze ist deaktiviert. Sorry" #: sqlkit/widgets/table/table.py:731 msgid "no current obj: maybe no record has yet been edited" msgstr "Kein aktuelles Objekt, vielleicht wurde noch kein Datensatz bearbeitet." #. TIP: when saving m2m, we delay till leader record will be saved #: sqlkit/widgets/table/table.py:869 msgid "delaying saving when main record will be saved" msgstr "Verzögerung der Speicherung bis Hauptdatensatz gespeichert wird" #: sqlkit/widgets/table/table.py:879 msgid "Record has NOT been saved" msgstr "Datensatz wurde nicht gespeichert" #: sqlkit/widgets/table/table.py:978 msgid "No record selected" msgstr "Kein Datensatz ist ausgewählt" #: sqlkit/widgets/table/table.py:1137 msgid "Value is not valid, trying completion" msgstr "Wert ist nicht gültig, versuche Vervollständigung" #. TIP: check in input field length %s %s -> field_name length #: sqlkit/widgets/table/table.py:1170 #, python-format msgid "Input exceeded max %s length (%s)" msgstr "Eingabe hat maximale %s Länge überschritten (%s)" #: sqlkit/widgets/table/table.py:1284 msgid "Unsaved data prevent opening a Mask to show the (unsaved) record" msgstr "" "Nicht gespeicherte Daten, verhindert das öffnen der Maske um den " "ungesicherten Datensatz anzuzeigen" #: sqlkit/widgets/table/table.py:1407 #, python-format msgid "" "A file with name '%s' already exists.\n" "Overwrite?" msgstr "" "Eine Datei mit Namen '%s' existiert schon.\n" "Überschreiben?" #: sqlkit/widgets/table/table.py:1408 msgid "Upload name duplication" msgstr "Hochladen - Name duplikated" #: sqlkit/widgets/table/table.py:1629 msgid "Export in csv file" msgstr "Export in eine 'CSV' Datei" #: sqlkit/widgets/table/table.py:1702 msgid "Csv files" msgstr "'CSV' Dateien" #: sqlkit/widgets/table/tablewidgets.py:34 #, python-format msgid "No current obj for field '%s' in %s" msgstr "Kein aktuelles Objekt for Feld '%s' in %s" sqlkit-0.9.5/sqlkit/locale/de/LC_MESSAGES/sqlkit.mo0000644000175000017500000004454711714210425021174 0ustar sandrosandroT(6900J5{# 5 DNdy* - =@J +% ( 5?HP bo&- , 6@(S)|BC*-X^=dnVho!   / GSVi{  )(%:N &3:Q-Y!++) <F\7a&! &7"Sv z #( "+FUi 6  78LSb{12/C Ucv|*990+ \,g*+0 A1 7s Q  !!&!6!<";"("/"$#9#J#O#U#m# #@$D$$X$ }$ $ $$$$$$% %&%A%%V%|%%%%%K%*>&i&.m&&& &&&3&'''''9'@'P' V'(c'K'#' '("(w'().*C*A +:M+:+5+&+" ,3C,w,,, , ,,-/-%N-t-y-3----L-J.[.m.$..7. . . / // (/6/H/[/ {/0///8/ 0%0.0,G00t0Q0S0;K1 11A1|1jZ2 22*23+63b3k3 q3~3 31333334!,4 N4\4w44449404B&5i5&656<6Z6:b626067?7W7p7777F75868'K8 s8 }8 8 888%8+899/9)B9:l99999:& : 4:A:Y:v:P:::Q:P;!Y;{; ;;;%; ;5<?S<<<#<<<= =(=:<=Hw=E=F> M>0[>/>>F>8?KX?=?g?J@[@c@x@@H@@a@RAjAAArA>/B%nB0BBBB C C(CCdCZD5sDDDDDDE.EEEYEmErEE3EE-E&)FPFjFt~F3F'G@+GlGGGGGGGH HHH1H:HIHPH7_H^H1H3(I\IbI You can indicate the database you want to connect to using an URI in the form as: postgres://localhost/dbname sqlite:///dbname or using password and username: mysql://sandro:pass@host/dbname %s - Dirty objects: %s - New objects: %s'%s' may have an invalid value: try completion on thatA complete demo of all the features of the sqlkit packageA file with name '%s' already exists. Overwrite?A file with name '%s' already exists. Overwrite?A programming error was received from the backend: %sAdapt width of columns to dataAdd a filter on '%s'Add a new filter on this field '%s'Add filter panelAdd filters for your queryAdd new recordAll filesAlready at new recordAlready first recordAlready last recordAs LIKE but case insensitiveAttempting to connect to %sBlankCast blank into NULLChar %s is not accepted in numeric contextClick here to select an operatorClose the panelCollapse rowConfigure the fields: labels, tooltip, completion, search fieldConnected to %sConnection setupCount recordsCount records in all tablesCreate a new row as a duplicate of this oneCreate e new row as child of this oneCreate totalCsv filesDatabaseDefaultDelete File/ImageDelete imageDelete the imageDelete this recordDelete this record? (%s)DeletedDeletion of records is disabled. SorryDiscard changesDiscarding new objDo you want to copy all data to a new record?DocumentsDuplicateEdit Sqlkit FieldsEdit the referenced table in 'mask' modeEdit the referenced table in 'table' modeEdit the table from which admitted values are taken in 'mask' modeEdit the table from which admitted values are taken in 'table' modeEditing canceled. Restoring original valueEqualErrorError while writing to the database. Original error was: %sError while writing to the database: are you 'saving as new' an already known record? original error was: %sErrors are present in the record. Correct them now, to continue or delete the recordExportExport in csv fileExport these data into csv formatField '%s' cannot be NULLField '%s' cannot have value %sField nameFileFilter PanelFilter actionsFilter panelFind allowed values (Control-Enter/Shift-Enter)Foreign KeyGoGo to filter panelGo to next recordGo to output panelGo to previous recordGood matchGreater or equalGreater then (after than)HelpHide fieldHide this columnHiding field is only supported for TablesID equality (don't follow foreign table)If not empty, the file will be uploaded with this new nameIf you write a wrong hostname the application may hang some seconds till the natural network timeout. Uncheck the flag on the right ti disable this featureImage viewerImagesIncomplete date formatIndexesInfo on available backend: postgres, mysql...Input exceeded max %s length (%s)Insertion of new records is disabled. SorryInspect widgetsLIKE: a "%" means any char - case sensitiveLess than (before then)Less than or equalLoad dataLoad the data as wellMaskMatch as LIKE case insensitive, "%" automatically addedMatch as LIKE, "%" automatically addedMatch as regexpMatch as regexp, case insensitiveModifiedModifyN.RowsNULL valueNegation of ILIKENegation of LIKENegation of match as regexpNegation of match case insensitiveNewNew child rowNew record %sNo current obj for field '%s' in %sNo exact match, trying regexp completionNo record presentNo record selectedNot equalNothing to saveNullableOpen separate image viewerOriginal valuePending DifferencesPreferred new filename:Present valuePress Esc to close or double click a date to select itPrim. KeysPrimary KeyPrimary key (%s) didn't change. Refusing to save as newPropertyRecord has NOT been savedRefresh this recordReloadReload from dbReload from the databaseReread this record from dbResult page for your queryRight click on the image area to upload an imageRight click on this table to show the column againRun DemoSave File/ImageSave current recordSave file locallySave image asSave unsaved data?SavedShow File/ImageShow all info on this field, db type & Co.Show all the differences that should be saved to databaseShow all values in the database starting with your stringShow all values in the db that match your stringShow fieldShow possible values, starting - Shift-EnterShow possible values: regexp - Ctrl-EnterShow/Hide image nameShow/Hide primary key if the key is numericSorry, export is implemented only for table viewSorry, problems connecting to remote db. Original error was: %sSort on this column locally (w/o touching the database)Sort on this column reloading from the database, you can used 's' to sort locallySqledit manual pageStateSubtotal on %sSubtotals by %sTableTable %s doesn't have a primary key, editing is not possibleTable reflectionThe best representation of a record as a combination of fields, e.g.: %(title)s %(year)sThe boolean is FalseThe boolean is TrueThe boolean is not FalseThe boolean is not TrueThe demo was not found, if you know where it is, run it manually: python demo.pyThe field that will be searched for when completion is usedThe name of the table we are customizingThe record is no longer present in the databaseThe value is not setThe value is setToolToolsTotal N. of records: %sTry continuosly to connect. Nice and usefull but may cause temporary blocks if you write an inexistent hostname Unsaved dataUnsaved data prevent opening a Mask to show the (unsaved) recordUnsaved differencesUpdate of records is disabled. SorryUpload FileUpload ImageUpload imageUpload name conflictUpload name duplicationUpload or modify an imageUse this filterValidation WarningsValidation errorsValueValue '%s' is not acceptedValue cannot be NULLValue is not valid, trying completionView '%s' in a MaskView this ForeignKey in a MaskView this record in a MaskWrong date format: %sWrong time formatYou can continue or go back editing. Read the following warnings to decidebrowsing of new records is disabled. Sorrydaydelaying saving when main record will be savedempty string: ''filterfilter_tablesincomplete date formatmonthno current obj: maybe no record has yet been editedoptsoutputquarterrecord validationregexpsearch mode: %sstartupload imagevalue '%s' cannot be used for field '%s'value '%s' does not seem a valid date and cannot be transformed into a datevalue is not Decimal nor string: %svalue is not date nor string: %sweekyearProject-Id-Version: PROJECT VERSION Report-Msgid-Bugs-To: EMAIL@ADDRESS POT-Creation-Date: 2011-03-03 12:33+0100 PO-Revision-Date: 2011-03-13 00:10+0100 Last-Translator: Sandro Language-Team: sqlkit Plural-Forms: nplurals=2; plural=(n != 1) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Generated-By: Babel 0.9.4 Sie können die Datenbank für die Verbindung angeben mit einer URI in der Form von: postgres://localhost/dbname sqlite:///dbname oder mit Passwort und Benutzername: mysql://sandro:pass@host/dbname %s - Schmutzige Objekte: %s - Neue Objekte: %s'%s' kann einen ungültigen Wert haben: versuche VervollständigungEine komplette Demostration aller Funktionen des sqlkit Packages.Eine Datei mit Namen '%s' existiert schon. Überschreiben?Eine Datei mit Namen "%s" existiert schon. Überschreiben?Ein Programmier Fehler wurde vom Backend erhalten: %sBreite diese Spalte den Daten anpassenEinen Filter für '%s' hinzufügenEinen neun Filter für dieses Feld '%s' hinzufügenFilter Panel hinzufügenAdd Filter für Ihre AnfrageNeuen Datensatz hinzufügenAlle DateienSchon im "Neuer Datensatz" modusSchon beim ersten DatensatzSchon beim letzen DatensatzWie LIKE aber case insensitiveVersuche Verbindung mit %s aufzubauenLeerCast "leer" zu NULLChar %s ist im numerischen Kontext nicht akzeptiertHier klicken um Das Panel schliessenZeile zusammenklappenDie Felder konfigurieren: Kennzeichen, Tooltip, Vervollständigung, Suchfeld'Verbunden zu %sVerbindungsaufbauAnzahl DatensätzeAnzahl Datensätze in allen TabellenEine neuEine neue Zeile anlegen als Unterzeile von dieser ZeileSumme anlegen'CSV' DateienDatenbankVorgabeDatei/Bild löschenBild löschenDas Bild löschenDatensatz löschenDiesen Datensatz löschen? (%s)GelöschtLöschen von Datensätzen ist deaktiviert. SorryÄnderungen verwerfenVerwerfe neues ObjektWollen Sie alle Daten in einen neuen Datensatz kopieren?DokumenteDuplikatBearbeiten sqlkit FelderÄnder die referenz Tabelle im 'Maske" ModusÄndere die referenz Tabelle im "Tabellen" ModusÄndere die Tabelle von welcher gültige Werte entnommen werden im 'Masken' ModusÄndere die Tabelle von welcher gültige Werte entnommen werden im 'Tabellen' ModusBearbeiten annuliert. Wiederherstellung des Orginal WertesGleichwertigFehlerFehler beim Schreiben in die Datenbank. Orignaller Fehler war: %sFehler beim schreiben in die Datenbank: Speichern Sie "als Neu" einen Datensatz der schon existiert? Original Fehler war: %sDer Datensatz enthält Fehler. Entweder jetzt korrigieren, um weiter zu machen oder den Datensatz löschenExportierenExport in eine 'CSV' DateiExportiere diese Daten in das 'CSV' FormatFeld '%s' kann nicht NULL seinFeld '%s' kann nicht diesen Wert '%s' habenFeldnameDateiFilter PanelFilter AktionenFilter PanelFinde erlaubte Werte (Control-Enter/Shift-Enter)FremdschlüsselGoZum Filter Panel gehenGehe zum nächsten DatensatzZum Ausgabe Panel gehenGehe zum vorhergehenden DatensatzGuter 'match'Grösser oder gleichwertigGrösser als (nach als)HilfeFeld verbergenDiese Spalte verbergenVerbergen eines Feldes ist nur für Tabellen unterstütztID Gleichheit (der Fremdtabelle nicht nachgehen)Falls nicht leer wird die Datei mit diesem neuen Namen hochgeladenWenn Sie einen falschen host Namen eingeben kann es sein das die Applikation für einige Sekunden blockiert (hängt vom Network timeout ab). Diese Funktion können Sie rechts deaktivieren.BildbetrachterBilderUnvollständiges DatumsformatIndizesInformation zu verfügbaren "Backends": postgres, mysql...Eingabe hat maximale %s Länge überschritten (%s)Neue Datensätze anlegen ist deaktiviert. Sorry.Überprüfe 'widgets'LIKE: das "%" bedeutet ein beliebiges Zeichen - case sensitive Kleiner als (bevor dann)Kleiner als oder gleichwertigLaden der DatenLaden auch der DatenMaske'match' als 'LIKE' case insensitive, "%" wird automatisch hinzugefügt'match' als 'LIKE', "%" wird automatisch hinzugefügt'match' als 'regexp''match' als 'regexp' - case insensitiveGeändertVerändernN. ZeilenNULL WertDie Negation von ILIKEDie Negation von LIKEDie Negation von 'match' als 'regexp'Die Negation von 'match' - case insensitiveNeuNeue UnterzeileNeuen Datensatz %sKein aktuelles Objekt for Feld '%s' in %sKein genauer 'match', versuche 'regexp' VervollständigungKein Datensatz vorhandenKein Datensatz ist ausgewähltNicht gleichwertigNichts zu sichernNullableEinen separaten Bildbetrachter öffnenOrginal WertAnstehende UnterschiedeBevorzugter neuer Dateiname:Gegenwertiger WertKlicken Sie Esc um zu schliessen oder Doppelklick ein Datum um es zu selektierenPrimärschlüsselPrimärschlüsselPrimärschlüssel (%s) ist nicht geändert, kann nicht als Neu gespeichert werdenPropertyDatensatz wurde nicht gespeichertDatensatz aktualisieren Neu LadenNeu Laden von der DatenbankNeu von der Datenbank ladenDatensatz neu von der Datenbank lesenResultat Seite für Ihre AnfrageRechts Klicken auf dem Bild um das Bild hoch zu ladenRechts auf dies Tabelle klicken um the Spalte wieder anzuzeigenDemo ausführenDatei/Bild sichernSpeichern des aktuellen DatensatzesDatei lokal sichernBild speichern unterSichere ungesicherte DatenGesichertDatei/Bild anzeigenAlle Informationen zu diesem Feld anzeigen, db, type & Co.Alle Unterschiede anzeigen die der Datenbank gespeichert werden sollten Zeige alle Werte in der Datenbank die mit Ihrer Zeichenfolge anfangenZeige alle Werte in der Datenbank an, die der Zeichenfolge entsprechenFeld anzeigenZeige mögliche Werte an, anfangen - Shift-EnterZeige mögliche Werte an: 'regexp' - Ctrl-EnterAnzeigen/Verbergen BildnameAnzeigen/Verbergen Primärschlüssel wenn der Schlüssel numerisch istSorry, Export ist nur für Tabellenansicht implementiertSorry, Problem mit remote Datenbank Verdingung. Orginaller Fehler war: %sSortierung der Spalte lokal (ohne die Datenbank zu berühren)Sortieren dieser Spalte und neu laden von der Datenbank, Sie können 's' benutzen um Lokal zu sortierensqledit HandbuchZustandZwischensumme auf %sZwischensumme auf %sTabelleTabelle '%s' hat keinen Primärschlüssel, Bearbeiten ist nicht möglichTabelle ReflexionDie beste Darstellung eines Datensatzes als eine Kombination von Feldern, z.B. %(title)s %(year)sDer Boolean ist 'False'Der Boolean ist 'True'Der Boolean ist nicht 'False'Der Boolean ist nicht 'True''Die Demonstration wurde nicht gefunden, falls Sie wissen wo Sie ist können Sie manuel ausführen: python demo.pyDas Feld das gesucht wird wenn Vervollständigung benutzt wirdDer Name der Tabelle die wir anpassenDieser Datensatz ist nicht mehr in der DatenbankDer Wert wurde nicht gesetztDer Wert wurde gesetztWerkzeugWerkzeugeAnzahl von Datensätzen: %sVersuch ständig eine Verbindung herzustellen. Nützlich, kann aber eine vorübergehende Blockierung verursachen wenn Sie einen nicht existierenden host Namen eingeben. Ungesicherte DatenNicht gespeicherte Daten, verhindert das öffnen der Maske um den ungesicherten Datensatz anzuzeigenUngesicherte DifferenzenAktualisierung der Datensätze ist deaktiviert. SorryDatei hochladenBild hochladenBild hochladenNamens konflikt beim hochladenHochladen - Name duplikatedBild hochladen oder ändernDiesen Filter benutzenValidierungswarnungValidierungsfehlernWertWert '%s' ist nicht akzeptiertWert kann nicht NULL seinWert ist nicht gültig, versuche Vervollständigung'%s' in eine Maske anzeigenDiesen Fremdschlüssel in eine Maske anzeigenDiese Datensatz in eine Maske anzeigenFalsches Datumsformat: %sFalsches ZeitformatSie können weiter machen oder gehen zurück zur Bearbeitung. Lesen Sie den folgenden Warnhinweis um zu entscheiden 'Browsing' neuer Datensätze ist deaktiviert. SorryTagVerzögerung der Speicherung bis Hauptdatensatz gespeichert wirdLeere Zeichenfolge: ''FilterFilter_TabellenUnvollständiges DatumsformatMonatKein aktuelles Objekt, vielleicht wurde noch kein Datensatz bearbeitet.optsAusgabeQuartalDatensatz Validierung'regexp'Such modus: %sBeginnBild hochladenWert '%s' kann für diese Feld '%s' nicht benutz werdenWert '%s' schein kein gültiges Datum zu sein und kann nicht in ein Datum transformiert werdenWert ist nicht Decimal oder eine Zeichenfolge: %sWert ist nicht ein Datum oder eine Zeichenfolge: %sWocheJahrsqlkit-0.9.5/sqlkit/locale/sqlkit.pot0000644000175000017500000005526011714210425017200 0ustar sandrosandro# Translations template for PROJECT. # Copyright (C) 2011 ORGANIZATION # This file is distributed under the same license as the PROJECT project. # FIRST AUTHOR , 2011. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" "POT-Creation-Date: 2011-03-03 12:33+0100\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=utf-8\n" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 0.9.4\n" #: bin/sqledit.py:87 msgid "Connection setup" msgstr "" #: bin/sqledit.py:90 msgid "" "\n" " You can indicate the database you want to connect to\n" " using an URI in the form as:\n" " postgres://localhost/dbname\n" " sqlite:///dbname\n" " or using password and username:\n" " mysql://sandro:pass@host/dbname\n" " " msgstr "" #: bin/sqledit.py:117 msgid "Run Demo" msgstr "" #: bin/sqledit.py:140 msgid "" "Info on available backend:\n" "postgres, mysql..." msgstr "" #: bin/sqledit.py:141 msgid "Sqledit manual page" msgstr "" #: bin/sqledit.py:146 msgid "" "Try continuosly to connect.\n" " Nice and usefull but may cause temporary blocks\n" " if you write an inexistent hostname\n" " " msgstr "" #: bin/sqledit.py:160 msgid "" "If you write a wrong hostname\n" "the application may hang some seconds till the natural network timeout. \n" "Uncheck the flag on the right ti disable this feature" msgstr "" #: bin/sqledit.py:187 #, python-format msgid "Attempting to connect to %s" msgstr "" #: bin/sqledit.py:190 #, python-format msgid "Connected to %s" msgstr "" #: bin/sqledit.py:221 msgid "A complete demo of all the features of the sqlkit package" msgstr "" #: bin/sqledit.py:222 msgid "" "The demo was not found, if you know where it is,\n" "run it manually: python demo.py" msgstr "" #: sqlkit/exc.py:48 #, python-format msgid "Field '%s' cannot be NULL" msgstr "" #: sqlkit/exc.py:60 #, python-format msgid "Field '%s' cannot have value %s" msgstr "" #: sqlkit/fields.py:979 #, python-format msgid "value is not Decimal nor string: %s" msgstr "" #: sqlkit/fields.py:1055 #, python-format msgid "value is not date nor string: %s" msgstr "" #: sqlkit/fields.py:1174 sqlkit/widgets/mask/miniwidgets.py:430 #, python-format msgid "Wrong date format: %s" msgstr "" #: sqlkit/fields.py:1184 msgid "Wrong time format" msgstr "" #: sqlkit/fields.py:1336 msgid "Value cannot be NULL" msgstr "" #: sqlkit/fields.py:1340 #, python-format msgid "Value '%s' is not accepted" msgstr "" #: sqlkit/db/proxy.py:219 #, python-format msgid "Table %s doesn't have a primary key, editing is not possible" msgstr "" #: sqlkit/layout/dateedit.py:190 msgid "Press Esc to close or double click a date to select it" msgstr "" #: sqlkit/layout/fk_entry.py:25 msgid "Find allowed values (Control-Enter/Shift-Enter)" msgstr "" #: sqlkit/layout/image_widget.py:142 msgid "" "Right click on the image area\n" " to upload an image" msgstr "" #: sqlkit/layout/image_widget.py:273 sqlkit/widgets/table/table.py:1395 msgid "Upload image" msgstr "" #: sqlkit/layout/image_widget.py:273 msgid "Upload or modify an image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete image" msgstr "" #: sqlkit/layout/image_widget.py:274 msgid "Delete the image" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Image viewer" msgstr "" #: sqlkit/layout/image_widget.py:275 msgid "Open separate image viewer" msgstr "" #: sqlkit/layout/image_widget.py:276 msgid "Save image as" msgstr "" #: sqlkit/layout/image_widget.py:279 msgid "Show/Hide image name" msgstr "" #: sqlkit/layout/image_widget.py:317 msgid "upload image" msgstr "" #: sqlkit/misc/datetools.py:152 msgid "Incomplete date format" msgstr "" #: sqlkit/misc/table_browser.py:135 msgid "Database" msgstr "" #. TIP: menu entry #: sqlkit/misc/table_browser.py:136 sqlkit/misc/words.py:13 #: sqlkit/widgets/common/sqlwidget.py:795 msgid "Modify" msgstr "" #: sqlkit/misc/table_browser.py:137 msgid "Tool" msgstr "" #. TIP: menu entry #: sqlkit/misc/table_browser.py:138 sqlkit/misc/words.py:15 #: sqlkit/widgets/common/sqlwidget.py:798 msgid "Help" msgstr "" #: sqlkit/misc/table_browser.py:141 msgid "Count records" msgstr "" #: sqlkit/misc/table_browser.py:141 msgid "Count records in all tables" msgstr "" #: sqlkit/misc/table_browser.py:144 msgid "" "Configure the fields: \n" "labels, tooltip, completion, search field" msgstr "" #: sqlkit/misc/table_browser.py:147 msgid "Edit Sqlkit Fields" msgstr "" #: sqlkit/misc/table_browser.py:150 msgid "Primary Key" msgstr "" #: sqlkit/misc/table_browser.py:151 msgid "Show/Hide primary key if the key is numeric" msgstr "" #: sqlkit/misc/table_browser.py:152 msgid "Load data" msgstr "" #: sqlkit/misc/table_browser.py:153 msgid "Load the data as well" msgstr "" #: sqlkit/misc/table_browser.py:154 msgid "Blank" msgstr "" #: sqlkit/misc/table_browser.py:154 msgid "Cast blank into NULL" msgstr "" #: sqlkit/misc/table_browser.py:262 msgid "Mask" msgstr "" #: sqlkit/misc/table_browser.py:266 msgid "Table" msgstr "" #: sqlkit/misc/table_browser.py:272 msgid "Collapse row" msgstr "" #: sqlkit/misc/table_browser.py:276 msgid "Table reflection" msgstr "" #: sqlkit/misc/table_browser.py:423 msgid "The name of the table we are customizing" msgstr "" #: sqlkit/misc/table_browser.py:424 #, python-format msgid "" "The best representation of a record \n" "as a combination of fields, e.g.: %(title)s %(year)s" msgstr "" #: sqlkit/misc/table_browser.py:427 msgid "The field that will be searched for when completion is used" msgstr "" #. TIP: filter page of the filter panel #: sqlkit/misc/words.py:7 msgid "filter" msgstr "" #. TIP: output page of the filter panel #: sqlkit/misc/words.py:9 sqlkit/widgets/common/sqlfilter.py:259 #: sqlkit/widgets/common/sqlfilter.py:306 msgid "output" msgstr "" #. TIP: menu entry #: sqlkit/misc/words.py:11 msgid "File" msgstr "" #. TIP: menu entry #: sqlkit/misc/words.py:17 sqlkit/widgets/common/sqlwidget.py:796 msgid "Go" msgstr "" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:19 msgid "opts" msgstr "" #. TIP: opts in filetr panel #: sqlkit/misc/words.py:21 sqlkit/widgets/common/sqlwidget.py:797 msgid "Tools" msgstr "" #. TIP: serach mode: #: sqlkit/misc/words.py:23 msgid "regexp" msgstr "" #. TIP: search mode #: sqlkit/misc/words.py:25 msgid "start" msgstr "" #. TIP: count records in sqledit #: sqlkit/misc/words.py:27 msgid "N.Rows" msgstr "" #: sqlkit/misc/words.py:29 msgid "Indexes" msgstr "" #: sqlkit/misc/words.py:30 msgid "Nullable" msgstr "" #: sqlkit/misc/words.py:31 msgid "Prim. Keys" msgstr "" #: sqlkit/misc/words.py:32 msgid "Default" msgstr "" #: sqlkit/misc/words.py:33 msgid "Foreign Key" msgstr "" #: sqlkit/misc/words.py:34 msgid "filter_tables" msgstr "" #. TIP: status bar #: sqlkit/widgets/common/completion.py:285 #: sqlkit/widgets/common/completion.py:1124 #, python-format msgid "search mode: %s" msgstr "" #: sqlkit/widgets/common/completion.py:643 msgid "Show all info on this field, db type & Co." msgstr "" #. TIP: menu enty in menu on right click on down arro in fkey completion widget #: sqlkit/widgets/common/completion.py:649 msgid "Show possible values: regexp - Ctrl-Enter" msgstr "" #. TIP: yellow tip to menu entry in down arrow in completion widget #: sqlkit/widgets/common/completion.py:653 msgid "Show all values in the db that match your string" msgstr "" #: sqlkit/widgets/common/completion.py:658 msgid "Show possible values, starting - Shift-Enter" msgstr "" #: sqlkit/widgets/common/completion.py:661 msgid "Show all values in the database starting with your string" msgstr "" #: sqlkit/widgets/common/completion.py:667 msgid "Edit the referenced table in 'table' mode" msgstr "" #: sqlkit/widgets/common/completion.py:669 msgid "Edit the table from which admitted values are taken in 'table' mode" msgstr "" #: sqlkit/widgets/common/completion.py:674 msgid "Edit the referenced table in 'mask' mode" msgstr "" #: sqlkit/widgets/common/completion.py:676 msgid "Edit the table from which admitted values are taken in 'mask' mode" msgstr "" #: sqlkit/widgets/common/completion.py:783 msgid "Good match" msgstr "" #: sqlkit/widgets/common/completion.py:792 #: sqlkit/widgets/common/completion.py:798 msgid "No exact match, trying regexp completion" msgstr "" #: sqlkit/widgets/common/dialogs.py:26 sqlkit/widgets/common/dialogs.py:94 #: sqlkit/widgets/table/table.py:1703 msgid "All files" msgstr "" #: sqlkit/widgets/common/dialogs.py:100 msgid "Images" msgstr "" #: sqlkit/widgets/common/dialogs.py:106 msgid "Documents" msgstr "" #. TIP: possible new name for an uploaded file #: sqlkit/widgets/common/dialogs.py:123 msgid "Preferred new filename:" msgstr "" #: sqlkit/widgets/common/dialogs.py:124 msgid "If not empty, the file will be uploaded with this new name" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:107 #: sqlkit/widgets/common/sqlfilter.py:110 msgid "Match as regexp" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:108 msgid "Match as LIKE, \"%\" automatically added" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:109 msgid "Match as LIKE case insensitive, \"%\" automatically added" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:111 msgid "Match as regexp, case insensitive" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:112 msgid "Negation of match as regexp" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:113 msgid "Negation of match case insensitive" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:114 msgid "Equal" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:115 msgid "Not equal" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:116 msgid "Greater then (after than)" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:117 msgid "Greater or equal" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:118 msgid "Less than (before then)" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:119 msgid "Less than or equal" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:120 msgid "LIKE: a \"%\" means any char - case sensitive" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:121 msgid "Negation of LIKE" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:122 msgid "As LIKE but case insensitive" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:123 msgid "Negation of ILIKE" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:124 msgid "The boolean is True" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:125 msgid "The boolean is False" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:126 msgid "The boolean is not True" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:127 msgid "The boolean is not False" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:128 msgid "The value is not set" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:129 msgid "The value is set" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:130 msgid "ID equality (don't follow foreign table)" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:221 msgid "Filter Panel" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:230 msgid "Add filters for your query" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:232 msgid "Result page for your query" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:269 msgid "Filter actions" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:270 msgid "Reload from db" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:271 msgid "Close the panel" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:274 msgid "Go to filter panel" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:276 msgid "Go to output panel" msgstr "" #. TIP: status bar message #: sqlkit/widgets/common/sqlfilter.py:479 sqlkit/widgets/table/table.py:762 #: sqlkit/widgets/table/table.py:1039 #, python-format msgid "Total N. of records: %s" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:801 #, python-format msgid "value '%s' cannot be used for field '%s'" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:868 #, python-format msgid "" "value '%s' does not seem a valid date and cannot be transformed into a " "date" msgstr "" #. TIP: appears in the menu in the filter panel to add a second entry of the #. same field #: sqlkit/widgets/common/sqlfilter.py:1042 #, python-format msgid "Add a new filter on this field '%s'" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:1048 msgid "Use this filter" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:1092 msgid "Click here to select an operator" msgstr "" #: sqlkit/widgets/common/sqlfilter.py:1164 msgid "incomplete date format" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:358 #, python-format msgid "" "Sorry, problems connecting to remote db. Original error was: \n" "\n" "%s" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:625 msgid "Hiding field is only supported for Tables" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:792 msgid "Show all the differences that should be saved to database" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:802 msgid "Pending Differences" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:806 msgid "Save current record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Filter panel" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:810 msgid "Add filter panel" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:811 msgid "Reload from the database" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:815 msgid "Go to next record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:816 msgid "Go to previous record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:823 msgid "Inspect widgets" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:990 msgid "Saved" msgstr "" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1020 #, python-format msgid "" "Error while writing to the database. \n" "Original error was: \n" "%s" msgstr "" #. TIP: Error while saving into a table w/o permission #. TIP: reloading data from the database #: sqlkit/widgets/common/sqlwidget.py:1026 #: sqlkit/widgets/common/sqlwidget.py:1563 #, python-format msgid "" "A programming error was received from the backend:\n" "%s" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1035 #, python-format msgid "%s - Dirty objects: %s - New objects: %s" msgstr "" #. TIP: message in the status bar when a commit error is handled #: sqlkit/widgets/common/sqlwidget.py:1041 #, python-format msgid "" "Error while writing to the database: \n" "are you 'saving as new' an already known record?\n" "original error was: \n" "%s" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1186 msgid "Discarding new obj" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1223 #, python-format msgid "Char %s is not accepted in numeric context" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1281 msgid "record validation" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1503 msgid "Unsaved differences" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:1741 msgid "Sorry, export is implemented only for table view" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2097 msgid "Property" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2098 msgid "Value" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2184 msgid "Save unsaved data?" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2187 msgid "Unsaved data" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2209 msgid "State" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2210 #: sqlkit/widgets/common/sqlwidget.py:2330 msgid "Field name" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2211 msgid "Original value" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2212 msgid "Present value" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2242 msgid "Modified" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2269 sqlkit/widgets/mask/mask.py:597 msgid "Deleted" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2279 msgid "New" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2293 msgid "empty string: ''" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2295 msgid "NULL value" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2301 msgid "" "Errors are present in the record. \n" "Correct them now, to continue \n" "or delete the record" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2302 msgid "Validation errors" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2331 msgid "Error" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2362 msgid "" "You can continue or go back editing. \n" "Read the following warnings to decide" msgstr "" #: sqlkit/widgets/common/sqlwidget.py:2363 msgid "Validation Warnings" msgstr "" #: sqlkit/widgets/mask/mask.py:258 msgid "Add new record" msgstr "" #: sqlkit/widgets/mask/mask.py:262 msgid "Discard changes" msgstr "" #. TIP Modify menu entry in the mask to reread a single record from database #: sqlkit/widgets/mask/mask.py:264 msgid "Refresh this record" msgstr "" #: sqlkit/widgets/mask/mask.py:265 msgid "Reread this record from db" msgstr "" #: sqlkit/widgets/mask/mask.py:268 msgid "Delete this record" msgstr "" #: sqlkit/widgets/mask/mask.py:342 sqlkit/widgets/table/table.py:908 msgid "Already at new record" msgstr "" #: sqlkit/widgets/mask/mask.py:362 #, python-format msgid "New record %s" msgstr "" #. TIP message issued when a refresh is done on a deleted record #: sqlkit/widgets/mask/mask.py:405 msgid "The record is no longer present in the database" msgstr "" #: sqlkit/widgets/mask/mask.py:473 sqlkit/widgets/table/table.py:838 msgid "Nothing to save" msgstr "" #: sqlkit/widgets/mask/mask.py:523 #, python-format msgid "Primary key (%s) didn't change. Refusing to save as new" msgstr "" #: sqlkit/widgets/mask/mask.py:537 msgid "Do you want to copy all data to a new record?" msgstr "" #: sqlkit/widgets/mask/mask.py:585 sqlkit/widgets/table/table.py:982 #, python-format msgid "" "Delete this record?\n" "(%s)" msgstr "" #: sqlkit/widgets/mask/mask.py:654 msgid "No record present" msgstr "" #: sqlkit/widgets/mask/mask.py:677 msgid "Already last record" msgstr "" #: sqlkit/widgets/mask/mask.py:679 msgid "Already first record" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:695 #, python-format msgid "A file with name '%s' already exists. Overwrite?" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:696 msgid "Upload name conflict" msgstr "" #: sqlkit/widgets/mask/miniwidgets.py:888 #, python-format msgid "'%s' may have an invalid value: try completion on that" msgstr "" #: sqlkit/widgets/table/columns.py:208 msgid "Editing canceled. Restoring original value" msgstr "" #: sqlkit/widgets/table/columns.py:848 #, python-format msgid "Add a filter on '%s'" msgstr "" #: sqlkit/widgets/table/columns.py:858 msgid "" "Sort on this column reloading from the database, you can used 's' to sort" " locally" msgstr "" #: sqlkit/widgets/table/columns.py:861 msgid "Sort on this column locally (w/o touching the database)" msgstr "" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:871 msgid "Hide this column" msgstr "" #. TIP: column menu opt #: sqlkit/widgets/table/columns.py:878 msgid "Create total" msgstr "" #. TIP: column menu total #: sqlkit/widgets/table/columns.py:888 #, python-format msgid "Subtotal on %s" msgstr "" #. TIP: column menu opt #. TIP: modify menu entry #: sqlkit/widgets/table/columns.py:910 sqlkit/widgets/table/columns.py:1293 #: sqlkit/widgets/table/table.py:287 msgid "Show field" msgstr "" #: sqlkit/widgets/table/columns.py:911 sqlkit/widgets/table/columns.py:1294 #: sqlkit/widgets/table/table.py:288 msgid "Hide field" msgstr "" #: sqlkit/widgets/table/columns.py:938 msgid "day" msgstr "" #: sqlkit/widgets/table/columns.py:938 msgid "week" msgstr "" #: sqlkit/widgets/table/columns.py:938 msgid "month" msgstr "" #: sqlkit/widgets/table/columns.py:939 msgid "quarter" msgstr "" #: sqlkit/widgets/table/columns.py:939 msgid "year" msgstr "" #: sqlkit/widgets/table/columns.py:942 #, python-format msgid "Subtotals by %s" msgstr "" #: sqlkit/widgets/table/columns.py:1267 msgid "Right click on this table to show the column again" msgstr "" #: sqlkit/widgets/table/columns.py:1287 sqlkit/widgets/table/table.py:301 msgid "Export" msgstr "" #: sqlkit/widgets/table/columns.py:1287 msgid "Export these data into csv format" msgstr "" #: sqlkit/widgets/table/columns.py:1292 msgid "Adapt width of columns to data" msgstr "" #: sqlkit/widgets/table/columns.py:1321 #, python-format msgid "View '%s' in a Mask" msgstr "" #: sqlkit/widgets/table/table.py:274 msgid "Duplicate" msgstr "" #: sqlkit/widgets/table/table.py:275 msgid "Create a new row as a duplicate of this one" msgstr "" #: sqlkit/widgets/table/table.py:277 msgid "New child row" msgstr "" #: sqlkit/widgets/table/table.py:278 msgid "Create e new row as child of this one" msgstr "" #: sqlkit/widgets/table/table.py:292 msgid "View this record in a Mask" msgstr "" #: sqlkit/widgets/table/table.py:293 msgid "View this ForeignKey in a Mask" msgstr "" #: sqlkit/widgets/table/table.py:294 msgid "Upload Image" msgstr "" #: sqlkit/widgets/table/table.py:295 msgid "Upload File" msgstr "" #: sqlkit/widgets/table/table.py:296 msgid "Delete File/Image" msgstr "" #: sqlkit/widgets/table/table.py:297 msgid "Show File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save File/Image" msgstr "" #: sqlkit/widgets/table/table.py:298 msgid "Save file locally" msgstr "" #: sqlkit/widgets/table/table.py:528 sqlkit/widgets/table/table.py:542 #: sqlkit/widgets/table/table.py:554 msgid "Insertion of new records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:558 msgid "Update of records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:570 msgid "Deletion of records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:583 msgid "browsing of new records is disabled. Sorry" msgstr "" #: sqlkit/widgets/table/table.py:731 msgid "no current obj: maybe no record has yet been edited" msgstr "" #. TIP: when saving m2m, we delay till leader record will be saved #: sqlkit/widgets/table/table.py:869 msgid "delaying saving when main record will be saved" msgstr "" #: sqlkit/widgets/table/table.py:879 msgid "Record has NOT been saved" msgstr "" #: sqlkit/widgets/table/table.py:978 msgid "No record selected" msgstr "" #: sqlkit/widgets/table/table.py:1137 msgid "Value is not valid, trying completion" msgstr "" #. TIP: check in input field length %s %s -> field_name length #: sqlkit/widgets/table/table.py:1170 #, python-format msgid "Input exceeded max %s length (%s)" msgstr "" #: sqlkit/widgets/table/table.py:1284 msgid "Unsaved data prevent opening a Mask to show the (unsaved) record" msgstr "" #: sqlkit/widgets/table/table.py:1407 #, python-format msgid "" "A file with name '%s' already exists.\n" "Overwrite?" msgstr "" #: sqlkit/widgets/table/table.py:1408 msgid "Upload name duplication" msgstr "" #: sqlkit/widgets/table/table.py:1629 msgid "Export in csv file" msgstr "" #: sqlkit/widgets/table/table.py:1702 msgid "Csv files" msgstr "" #: sqlkit/widgets/table/tablewidgets.py:34 #, python-format msgid "No current obj for field '%s' in %s" msgstr "" sqlkit-0.9.5/AUTHORS0000644000175000017500000000125611714210425013443 0ustar sandrosandro Sqlkit has been written by sandro dentella In the late '90 I developed -as my first job with GUI- tksql a library in tcl/tk that is still being used in a cpuple of production environment. Sqlkit is meant as a replacement, better under all possible points of view: better language, nicer grafical toolkit, based on a serious ORM. Contributors ============= The first version of SqlTable was started by Michele Comitini The module optionparse was written by Michele Simionato The module dateedit was written by Fabian Sturm The multiline renderer was set to pygtk list by Osmo Salomaa The way tables width are worked out has been developed by Pietro Battiston sqlkit-0.9.5/sqledit.completion0000644000175000017500000000143611714210425016133 0ustar sandrosandro# -*- mode: sh -*- _sqledit() { local cur prev ENGINES COMPREPLY=() cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} ENGINES='sqlite:// postgres:// mysql:// mssql:// oracle:// sybase:// maxdb:// firebird://' # an option and URL or a nick if [ $COMP_CWORD -eq 1 ] || [[ "$cur" == -* ]] ; then COMPREPLY=( $( compgen -W '$ENGINES --url -u --nick -n --table -t --mask -m \ --sqltable -T --all-tables -A -c --configure -v --version' -- $cur ) $( [ -e ~/.sqledit/nicks ] && perl -ne '{if ($_ =~ m/\[(.*)\]/) {print "$1\n"};} ' ~/.sqledit/nicks| grep "^$cur" )) # an URL elif [ "$prev" == --url ] || [ "$prev" == -u ] ; then COMPREPLY=( $( compgen -W "$ENGINES" -- $cur )) fi return 0 } complete -F _sqledit sqledit sqlkit-0.9.5/COPYING0000644000175000017500000010451311714210425013426 0ustar sandrosandro GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . sqlkit-0.9.5/Announce0000644000175000017500000000445011714210425014063 0ustar sandrosandro ANNOUNCE: sqlkit 0.9.5 Fab, 7 - 2012 I'm happy to announce release 0.9.5 of sqlkit package for Python. http://sqlkit.argolinux.org/ This release ------------ This release adds support sqlalchemy 0.7+ beside som minor other changes and fixes The python package ------------------ SQLkit PyGtk package provides Mask and Table widgets to edit database data. It's meant as a base for database desktop applications. The application --------------- It also provides 'sqledit' a PyGTK application based on sqlkit that can be used from command line to browse and edit data. The package has 2 very rich demo suites for sql widgets (the main one in sqlkit/demo/sql/demo.py) and for layout creation Translations ------------ If you like sqlkit and want to help translating, you may find the project on: https://launchpad.net/sqlkit Main features of sqlkit: ------------------------ * editor of databases in 2 modes: table & mask * based on sqlalchemy: can cope with many different databases * very powerful filtering capabilities: - each field can be used to filter records - filter may span relationship - date filtering possible also on relative basis (good for saved queries) * completion on all text field and foreign keys * very easy way to draw a layout for mask views * completely effortless editing of relationships * very easy way to set defaults * possibility to display totals of numeric fields * any possible sql constraint can be attached to a Mask or a Table. It can be expressed as a normal sqlalchemy query or with django-like syntax * sqledit: the application to edit db Sqlkit is based on: ------------------- * python (>= 2.5) * PyGtk * Sqlalchemy (>= 0.5.4) * python-dateutil * babel (localization) * you db driver of choice Download & more: --------------- * Download: http://sqlkit.argolinux.org/misc/download.html easy_install sqlkit * Source: hg clone http://hg.argolinux.org/py/sqlkit * Google Group: http://groups.google.it/group/sqlkit/ * Translation: https://launchpad.net/sqlkit * Tutorial: http://sqlkit.argolinux.org/misc/tutorials.html * Changelog: http://sqlkit.argolinux.org/download/Changelog * License: GNU GPLv3 sqlkit-0.9.5/INSTALL0000644000175000017500000000171411714210425013423 0ustar sandrosandroDependencies ============ Sqlkit depends on: * Python * Pygtk * sqlalchemy (>=0.5) * dateutils * setuptools * the correct driver for your database of choice Installation ============ Please read how to install in the web pacge of the installation tutorial where tips for the different operative systems are collected: http://sqlkit.argolinux.org/misc/tutorial.html as any python package, provided you have pygtk already installed, you can install by just running: python setup.py install it will install the library and a script called 'sqledit'. You can make a tour of the featueres from the 'demo' button of sqledit or going to demo/sql subdir of the source package and running ./demo.py Completion ========== If you use sqledit from command line under linux you may find usefull the sqledit.competion file that you should install under /etc/bash_completion.d It lets you complete the arguments reading the nicks defined in .sqledit/nicks sqlkit-0.9.5/MANIFEST.in0000644000175000017500000000061611714210425014130 0ustar sandrosandroinclude sqlkit/locale/*pot include sqlkit/locale/*/*/sqlkit.* include sqlkit/layout/*.png include demo/* include demo/*/*.py include demo/*/*/* include doc/* include doc/*/* include doc/*/*/* include AUTHORS include Announce include changelog include COPYING include distribute-setup.py include INSTALL include README.localization include sqledit.completion include sqlkit.desktop include test/*/* sqlkit-0.9.5/TODO0000644000175000017500000000251411714210425013061 0ustar sandrosandroIdeas list ========== * Improve some widgets: + multiline editing/viewing under SqlTable (important) + date/datetime/interval (minor) * New layout formalism starting from studying the ReST very clean directives. Possibly adding possibility to use glade along with the actual (and preferred) layout system. * Refactoring with the aim of simplifying. Need to add new tests where possible. Some parts are really old and need cleanup (5-10%), others have grown too big (the two main MegaWidgets). * Analizing other similar projects to see what they can teach us. Projects to keep into consideration: dabo (wx based, site at http://dabodev.com/), camelot (qt based, site at http://www.python-camelot.com/) * Now interactive filters only allow AND connectors. It's easy to allow OR instead but I'd like to understand how we can add more flexible intermixing of features. It may be worthwhile to see how Filemaker cope with that or Access. * Add a way to store queryies created dinamically by the user * Add a plugin to work with Quickly * Study wich are the bottleneck that slow it down with remote db server on a slow line. * Add a way to inform user of all items deemed to deletetion when a record is deleted, i.e.: all records that would be deleted by the database or by sqlalchemy itself due to delete-orphan option. sqlkit-0.9.5/pyinstaller-sqledit-win.spec0000755000175000017500000000247211714210425020057 0ustar sandrosandro# -*- mode: python -*- # spec file for pyinstaller. Read README.pyinstaller for more info import os import sys ROOT = os.path.dirname(sys.argv[1]) + '/' #sqlkit = Tree(ROOT + 'sqlkit', prefix='sqlkit') icon = Tree(ROOT + 'sqlkit/layout', excludes='*.py') locale = Tree(ROOT + 'sqlkit/locale', prefix='sqlkit/locale') demo = Tree(ROOT + 'demo', prefix='demo', excludes= ROOT + 'demo/layout') a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), ROOT + '/bin/sqledit'], pathex=[os.path.abspath(os.path.dirname(sys.argv[0]))] ) pyz = PYZ(a.pure ) exe = EXE(pyz, a.scripts, exclude_binaries=1, # name=os.path.join('build/pyi.linux2/sqledit', 'sqledit'), name=os.path.join('build/pyi.win32/sqledit', 'sqledit.exe'), debug=False, strip=True, upx=True, console=0 ) coll = COLLECT( exe, a.binaries, a.zipfiles, a.datas, demo, icon, locale, #sqlkit, strip=False, upx=True, name=os.path.join('pyinstaller', 'sqledit') ) sqlkit-0.9.5/pyinstaller/0000755000175000017500000000000011714210425014735 5ustar sandrosandrosqlkit-0.9.5/pyinstaller/make-installer0000755000175000017500000000047111714210425017575 0ustar sandrosandro#!/bin/bash # cmd may be: pyinstaller_path/Build.py # we need to start the command from the pyinstaller directory CMD=$(which $1) SPEC_FILE="$(pwd)/$2" VER=$3 PYINSTALLER_DIR=$(dirname $CMD) cd $PYINSTALLER_DIR $CMD $SPEC_FILE cd - cd pyinstaller rm -f sqledit-* ln -s sqledit sqledit-$VER cp README sqledit sqlkit-0.9.5/pyinstaller/README0000644000175000017500000000070111714210425015613 0ustar sandrosandroWhat is it ========== This is a pyinstller build of sqledit. It means it provides a single command -sqledit- that uses libraries that are present in this directory. You can run it from here just to test sqledit and run the demo or install it and use it from there. Install ======= Yoy can save the directory in /usr/local/share/sqledit and create a link that points to that position: ln -s /usr/local/share/sqledit/sqledit /usr/bin/sqledit sqlkit-0.9.5/test/0000755000175000017500000000000011714210425013346 5ustar sandrosandrosqlkit-0.9.5/test/doctest/0000755000175000017500000000000011714210425015013 5ustar sandrosandrosqlkit-0.9.5/test/doctest/django2sa.txt0000644000175000017500000001323011714210425017423 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> import sys >>> os.environ['LANG'] = 'en_US.utf8' >>> sys.path.insert(0, '.') >>> from address_model import User, Address, Provider, Country, session >>> from sqlkit.db import django_syntax as djs >>> from sqlalchemy import and_, or_ >>> >>> query = session.query(User) >>> def compare(q1, q2): ... res = str(q1) == str(q2) ... if not res: ... print "%s\n\n%s" % (q1, q2) ... else: ... return res >>> def test2(*args, **kw): ... sql, join = djs.django2sqlalchemy(User.__mapper__, **kw) ... print sql, join #### components: path, str_op, col, op, value >>> djs.django2components(User.__table__, {'first_name' : 'ed'}) ([], '=', 'eq', 'ed', Column('first_name', String(length=50...), table=), None) >>> djs.django2components(User.__table__, {'first_name__ilike' : 'ed'}) ([], 'ilike', 'ilike', 'ed', Column('first_name', String(length=50...), table=), None) >>> djs.django2components(User.__table__, {'first_name__notilike' : 'ed'}) ([], 'not ilike', 'notilike', 'ed', Column('first_name', String(length=50...), table=), None) >>> djs.django2components(User.__table__, {'id__in' : [3,4]}) ([], 'IN', 'in', [3, 4], Column('id', Integer(), table=...), None) >>> djs.django2components(User.__table__, {'id__notin' : [3,4]}) ([], 'NOT IN', 'notin', [3, 4], Column('id', Integer(), table=...), None) >>> djs.django2components(User.__mapper__, {'first_name' : 'ed'}) ([], '=', 'eq', 'ed', Column('first_name', String(length=50...), table=), None) >>> djs.django2components(User.__mapper__, {'first_name__icontains' : 'ed'}) ([], 'icontains', 'icontains', 'ed', Column('first_name', String(length=50...), table=), None) >>> djs.django2components(User.__mapper__, {'addresses__email__icontains' : 'e-den.it'}) (['addresses'], 'icontains', 'icontains', 'e-den.it', Column('email', String(length=50...), table=
    ), False) >>> djs.django2components(User.__mapper__, {'addresses__email__in' : ['abc', 'def']}) (['addresses'], 'IN', 'in', ['abc', 'def'], Column('email', String(length=50...), table=
    ), False) >>> djs.django2components(User.__mapper__, {'addresses__date_created__gte' : 'm'}) (['addresses'], '>=', 'gte', 'm', Column('date_created', Date(), table=
    ), False) >>> djs.django2components(User.__mapper__, {'addresses__provider__provider' : 'e-den.it'}) (['addresses', 'provider'], '=', 'eq', 'e-den.it', Column('provider', String(length=50...), table=), False) >>> djs.django2components(User.__mapper__, {'addresses__provider__provider__icontains' : 'e-den.it'}) (['addresses', 'provider'], 'icontains', 'icontains', 'e-den.it', Column('provider', String(length=50...), table=), False) >>> djs.django2components(User.__mapper__, {'addresses__provider__country__country' : 'IT'}) (['addresses', 'provider', 'country'], '=', 'eq', 'IT', Column('country', String(length=50...), table=), False) ## Foreign keys >>> djs.django2components(Address.__mapper__, {'user_id__first_name' : 'ed'})[:-1] (['user_id'], '=', 'eq', 'ed', Column('first_name', String(length=50...), table=)) >>> djs.django2components(Address.__mapper__, {'user_id__first_name' : 'ed'})[-1][0] == User.__table__ True >>> binary_expr = Address.__table__.c['user_id'] == User.__table__.c['id'] >>> print djs.django2components(Address.__mapper__, {'user_id__first_name' : 'ed'})[-1][1] address.user_id = ...user....id ## shift one step ahead in the mapper chain >>> djs.django2components(Address.__mapper__, {'date_created__gte' : 'm'}) ([], '>=', 'gte', 'm', Column('date_created', Date(), table=
    ), None) ## query + class >>> q = djs.django2query(query, User, first_name='ed') >>> compare(q, query.filter(User.first_name == 'ed')) True ## query + mapper >>> q = djs.django2query(query, User.__mapper__, first_name='ed') >>> compare(q, query.filter(User.first_name == 'ed')) True >>> q = djs.django2query(query, User.__mapper__, first_name='ed', ... addresses__email='me@google.com') >>> compare(q, query.filter(and_(User.first_name.op('=')('ed'), ... Address.email.op('=')('me@google.com'))).join(Address)) True # TODO query + mapper + fk: it doesn't follow the relation (yet) ## Error is commented as sqla 0.5 and 0.6 raise different errors # >>> q = djs.django2query(query, Address.__mapper__, user_id__first_name='ed') # Traceback (most recent call last): # ... # KeyError: 'user_id' >>> q = djs.django2query(query, User.__mapper__, first_name='ed', ... addresses__provider__provider__icontains='google', OR=True) >>> compare(q, query.filter(or_(User.first_name.op('=')('ed'), ... Provider.provider.ilike('%google.com%'))).join('addresses','provider')) True >>> q = djs.django2query(query, User.__mapper__, first_name='ed', ... addresses__provider__country__country__icontains='ITA') >>> compare(q, query.filter(and_(User.first_name.op('=')('ed'), ... Country.country.ilike('%ITA%'))).join('addresses','provider','country')) True ## django2sqlalchemy w/ mapper >>> clause_list, path = djs.django2sqlalchemy(User.__mapper__, first_name='ed') >>> compare(clause_list, and_(User.first_name == 'ed')) True ## django2sqlalchemy w/ table >>> clause_list, path = djs.django2sqlalchemy(User.__table__, first_name='ed') >>> compare(clause_list, and_(User.first_name == 'ed')) True ## dont't ask the moon: a table does not know anything about relationships >>> clause_list, path = djs.django2sqlalchemy(User.__table__, first_name__addresses__provider='google') Traceback (most recent call last): ... NotImplementedError: Can't cross relationship if you don't provide a mapper sqlkit-0.9.5/test/doctest/fields.txt0000644000175000017500000001372511714210425017032 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> os.environ['LANG'] = 'en_US.utf8' >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies import Movie, Director, AllTypes, db >>> from sqlkit.widgets import SqlMask >>> from sqlkit.fields import FieldChooser >>> lay = "title year director_id m2m=genres date_release score image" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.gui_fields = {} >>> field_chooser = FieldChooser(sm.mapper_info, sm.widgets) >>> field_name, key = 'title', 'title' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'year', 'year' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'director_id', 'fk=director_id' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'genres', 'm2m=genres' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key, test=True) >>> field_name, key = 'date_release', 'date_release' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'score', 'score' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) ### clean_value >>> from decimal import Decimal >>> from datetime import date, datetime, time, timedelta # fkey >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> f = sm.gui_fields['director_id'] >>> f.lookup_value(14) u'Fellini' >>> f.get_human_value(14) u'Fellini' # image. In the definition of Movie model image has a base_dir=./images >>> f= sm.gui_fields['image'] >>> sql_images = os.path.abspath('../../demo/sql') >>> f.clean_value("%s/%s" % (sql_images, "images/test.jpg")) 'test.jpg' >>> f.clean_value("images/test.jpg") 'images/test.jpg' >>> f.clean_value("./images/test.jpg") 'images/test.jpg' >>> f.clean_value("/images/test.jpg") 'test.jpg' >>> f.clean_value("/my_dir/test.jpg") 'test.jpg' >>> f.clean_value("/tmp/test.jpg") 'test.jpg' ## get_save_path is relative to the current working directory >>> f.get_save_path('/tmp/test.jpg') == "%s/%s" % (os.getcwd(), 'images/test.jpg') True # enum >>> f = sm.gui_fields['score'] >>> f.clean_value(3) 3 >>> f.clean_value(4) Traceback (most recent call last): ... ValidationError: Value '4' is not accepted # integer >>> sm = SqlMask(AllTypes, dbproxy=db, naked=True, show=False) >>> f = sm.gui_fields['integer'] >>> print f >>> f.clean_value(1) 1 >>> f.clean_value('1'), type(f.clean_value('1')) (1L, ) >>> isinstance(f.format_value(f.clean_value('1')), basestring) True # float >>> f = sm.gui_fields['float'] >>> print f >>> f.clean_value('1.2'), type(f.clean_value('1')) (1.2, ) >>> f.format_value(1.2) u'1.2' >>> isinstance(f.format_value(f.clean_value('1.2')), basestring) True # numeric >>> f = sm.gui_fields['numeric'] >>> f.clean_value(Decimal('1.2')) == Decimal("1.2") True >>> f.clean_value('4881.69') == Decimal("4881.69") True >> type(f.clean_value('4881.69')) == Decimal True >>> f.clean_value(4881.69) == Decimal("4881.69") True >>> type(f.clean_value(4881.69)) == Decimal True >>> f.clean_value(12) == Decimal("12") True >>> type(f.clean_value(12)) == Decimal True >>> isinstance(f.format_value(f.clean_value('1.2')), basestring) True # date >>> f = sm.gui_fields['date'] >>> f.clean_value(date(2008, 6, 2)) datetime.date(2008, 6, 2) >>> f.clean_value('6/2/2008') datetime.date(2008, 6, 2) >>> isinstance(f.format_value(f.clean_value('2/6/2008')), basestring) True # datetime >>> f = sm.gui_fields['datetime'] >>> f.clean_value(datetime(2008, 6, 2, 15, 30)) datetime.datetime(2008, 6, 2, 15, 30) >>> f.clean_value('2/6/2008 15:30') ## missing datetime parsing datetime.datetime(2008, 2, 6, 15, 30) >>> isinstance(f.format_value(f.clean_value('2/6/2008 15:30')), basestring) True # time >>> f = sm.gui_fields['time'] >>> f.clean_value(time(15, 30)) datetime.time(15, 30) >>> f.clean_value('15:30:00') datetime.time(15, 30) >>> f.clean_value('15:30') datetime.time(15, 30) >>> isinstance(f.format_value(f.clean_value('15:30')), basestring) True ##################################### ## # Change locale: it_IT.utf8 ## ###################################### >>> from babel import numbers, dates >>> os.environ['LANG'] = 'it_IT.utf8' # integer >>> sm = SqlMask(AllTypes, dbproxy=db, naked=True, show=False) >>> f = sm.gui_fields['integer'] >>> f.clean_value(1) 1 >>> f.clean_value('1'), type(f.clean_value('1')) (1L, ) # float >>> f = sm.gui_fields['float'] >>> print f >>> f.clean_value(1) 1.0 >>> f.clean_value('1'), type(f.clean_value('1')) (1.0, ) # numeric >>> f = sm.gui_fields['numeric'] >>> f.clean_value(Decimal('1.2')) == Decimal("1.2") True >>> f.clean_value('4881,69') == Decimal("4881.69") True >>> type(f.clean_value('4881,69')) == Decimal True # these test fail for unknown reason. I don't have time ti investigate # as real code works # >>> f.clean_value(4881.69), type(f.clean_value(4881.69)) # (Decimal("4881.69"), ) >>> f.clean_value(12) == Decimal("12") True >>> type(f.clean_value(12)) == Decimal True # date >>> f = sm.gui_fields['date'] >>> f.locale 'it_IT' >>> f.clean_value(date(2008, 6, 2)) datetime.date(2008, 6, 2) >>> f.clean_value('2/6/2008') datetime.date(2008, 6, 2) # datetime >>> f = sm.gui_fields['datetime'] >>> f.clean_value(datetime(2008, 6, 2, 15, 30)) datetime.datetime(2008, 6, 2, 15, 30) >>> f.clean_value('2/6/2008 15:30') datetime.datetime(2008, 6, 2, 15, 30) # time >>> f = sm.gui_fields['time'] >>> f.clean_value(time(15, 30)) datetime.time(15, 30) >>> f.clean_value('15:30:00') datetime.time(15, 30) sqlkit-0.9.5/test/doctest/address_model.py0000644000175000017500000000263411714210425020177 0ustar sandrosandrofrom sqlalchemy.ext.declarative import declarative_base from sqlalchemy import Table, Column, Integer, String, Date, MetaData, ForeignKey, create_engine, engine from sqlalchemy.orm import relation, scoped_session, sessionmaker Base = declarative_base() meta = Base.metadata meta.bind = create_engine('sqlite://') Session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=meta.bind)) session = Session() class User(Base): __tablename__ = 'user' id = Column('id', Integer, primary_key=True) first_name = Column('first_name', String(50)) addresses = relation("Address", backref="user") class Address(Base): __tablename__ = 'address' id = Column('id', Integer, primary_key=True) email = Column('email', String(50)) date_created = Column('date_created', Date) user_id = Column('user_id', Integer, ForeignKey('user.id')) provider_id = Column('provider_id', Integer, ForeignKey('provider.id')) class Provider(Base): __tablename__ = 'provider' id = Column('id', Integer, primary_key=True) provider = Column('provider', String(50)) country_id = Column('country_id', Integer, ForeignKey('country.id')) addresses = relation("Address", backref="provider") class Country(Base): __tablename__ = 'country' id = Column('id', Integer, primary_key=True) country = Column('country', String(50)) addresses = relation("Provider", backref="country") sqlkit-0.9.5/test/doctest/runtests.py0000755000175000017500000000211211714210425017253 0ustar sandrosandro#!/usr/bin/python import os import sys import doctest import sqlite3 from contextlib import closing import tempfile import babel pdir = os.path.dirname(os.getcwd()) ppdir = os.path.dirname(pdir) sys.path.insert(0,pdir) sys.path.insert(0,ppdir) DB_FILE = os.path.join(tempfile.gettempdir(), 'db-%s.sqlite' % os.environ.get('USERNAME')) DB_SCHEMA = '../../demo/sql/model/schema.sql' def init_db(): if os.path.exists(DB_FILE): os.remove(DB_FILE) with closing(sqlite3.connect(DB_FILE)) as db: with open(DB_SCHEMA, 'r') as schema: db.cursor().executescript(schema.read()) db.commit() init_db() os.environ['LANG'] = 'C' os.environ['LANG'] = 'en_US.utf8' doctest.ELLIPSIS = 10 files = ( 'completions.txt', 'dates.txt', 'defaults.txt', 'django2sa.txt', 'fields.txt', 'filters.txt', 'mapper_inspect.txt', 'mask.txt', ) if sys.argv[1:]: files = sys.argv[1:] for filename in files: print filename os.environ['LANG'] = 'en_US.utf8' doctest.testfile(filename, optionflags=doctest.ELLIPSIS) sqlkit-0.9.5/test/doctest/defaults.txt0000644000175000017500000000436011714210425017366 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> import sys >>> import gtk >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> os.environ['LANG'] = 'en_US.utf8' >>> from sqlkit.db import defaults >>> from movies import Movie, Director, db >>> from sqlkit.widgets import SqlMask, SqlTable >>> defaults.tables {} ## A local default >>> def_local_director=defaults.Defaults('director', metadata=db.metadata, local=True) >>> def_local_director.tdict is defaults.tables False >>> def_local_director.set_default(last_name='Olmi') >>> defaults.tables {} >>> def_local_director.tdict {'last_name': 'Olmi'} >>> def_local_director.get('last_name') 'Olmi' >>> defaults.get_default('director', 'last_name') # A global default >>> def_global_director=defaults.Defaults('director', metadata=db.metadata, local=False) >>> def_global_director.tdict is defaults.tables['director'] True >>> def_global_director.set_default(last_name='Lautrec') >>> def_global_director.get('last_name') 'Lautrec' >>> defaults.get_default('director', 'last_name') 'Lautrec' ## it doesn't hide local default >>> def_local_director.get('last_name') 'Olmi' >>> sm = SqlMask(Movie, dbproxy=db, naked=True, show=False) >>> while gtk.events_pending(): ... silent = gtk.main_iteration() # default automatically generated are local >>> sm.defaults.local True >>> sm.defaults.set_default(title='a title please...', date_release=sm.defaults.today) >>> sm.gui_fields.title.get_default() 'a title please...' >>> sm.defaults.get('director_id') >>> sm.defaults.fk('director_id', 'last_name', 'Donnersmak') >>> sm.gui_fields.director_id.get_human_value(sm.defaults.get('director_id')) u'Donnersmak' ## and don't change global ones >>> defaults.get_default('movie', 'title') >>> sm = SqlMask(Director, dbproxy=db, naked=True, show=False) ## no default, get the global one >>> sm.gui_fields.last_name.get_default() 'Lautrec' >>> sm.defaults.set_default(last_name='Fellini') >>> sm.gui_fields.last_name.get_default() 'Fellini' # global defaults is unchanged >>> def_global_director.get('last_name') 'Lautrec' >>> def_local_director.get('last_name') 'Olmi' >>> sm = SqlMask(Movie, dbproxy=db, naked=True, show=False) >>> sm.defaults.tdict {} >>> defaults.tables = {} >>> defaults.tables {} sqlkit-0.9.5/test/doctest/babel.txt.ko0000644000175000017500000000121311714210425017226 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> os.environ['LANG'] = 'en_US.utf8' >>> import babel >>> from babel import numbers, support >>> babel.default_locale() 'en_US' >>> numbers.default_locale() 'en_US' >>> numbers.format_number(1.2) u'1.2' ### it_IT.uft8 >>> os.environ['LANG'] = 'it_IT.utf8' >>> babel.default_locale() 'it_IT' >>> numbers.default_locale() 'it_IT' # This is not the desired behaviour!!!! >>> numbers.format_number(1.2) u'1.2' # This is not eather. So we need to specify the locale all the times. >>> silent = reload(babel) >>> silent = reload(numbers) >>> numbers.format_number(1.2) u'1.2' >>> support.format_number(1.2) u'1.2' sqlkit-0.9.5/test/doctest/mask.txt0000644000175000017500000001367211714210425016520 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> import gtk #>>> os.environ['LANG'] = 'en_US.utf8' >>> os.environ['LANG'] = 'en_US.utf8' >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies import Movie, Director, AllTypes, db >>> from sqlkit.widgets import SqlMask, SqlTable ## entry with dimenstions: uses entry + alignement >>> lay = "title" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.lay_obj.lay 'L=title ae=title:30-' >>> sm.laygen.fields_in_layout {'title': 'e=title'} ## TEXT: widget is a text TextWidget >>> lay = "text" >>> sm = SqlMask(AllTypes, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=text TXS=text >>> sm.laygen.fields_in_layout {'text': 'TX=text'} ## TEXT: casting the text widget to entry >>> lay = "e=text" >>> sm = SqlMask(AllTypes, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=text e=text >>> sm.laygen.fields_in_layout {'text': 'e=text'} ## TEXT: casting the entry widget to text >>> lay = "TXS=description" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.lay_obj.lay 'L=description TXS=description:30-' >>> sm.laygen.fields_in_layout {'description': 'TX=description'} ## TEXT no label >>> lay = "TXS==description" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.lay_obj.lay 'TXS=description' >>> sm.laygen.fields_in_layout {'description': 'TX=description'} >>> lay = "date_release" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=date_release d=date_release >>> sm.laygen.fields_in_layout {'date_release': 'd=date_release'} >>> lay = "year" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=year ae=year:8 >>> sm.laygen.fields_in_layout {'year': 'e=year'} >>> lay = "time" >>> sm = SqlMask(AllTypes, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=time ae=time >>> sm.laygen.fields_in_layout {'time': 'e=time'} >>> lay = "director_id" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=director_id fk=director_id >>> sm.laygen.fields_in_layout {'director_id': 'fk=director_id'} >>> lay = "{>>.open title}" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() {>>.open L=title ae=title:30-} >>> sm.laygen.fields_in_layout {'title': 'e=title'} >>> lay = "{>.closed title}" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() {>.closed L=title ae=title:30-} >>> lay = "m2m=genres" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() m2m=genres >>> sm.laygen.loaders {'genres': {}} >>> sm.laygen.fields_in_layout {'genres': 'm2m=genres'} >>> lay = "m2m=genres::name" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() m2m=genres::name >>> sm.laygen.loaders {'genres': {'field_list': 'name'}} >>> sm.laygen.fields_in_layout {'genres': 'm2m=genres'} >>> lay = "m2m=genres:2" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() m2m=genres:2 >>> sm.laygen.loaders {'genres': {'rows': 2}} >>> sm.laygen.fields_in_layout {'genres': 'm2m=genres'} >>> lay = "m2m=genres:2:" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() m2m=genres:2: >>> sm.laygen.loaders {'genres': {'rows': 2}} >>> sm.laygen.fields_in_layout {'genres': 'm2m=genres'} # >>> lay = "m2m=genres.film:20" # >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) # >>> print sm.lay_obj.lay.strip() # m2m=genres.film:20 # >>> sm.laygen.loaders # {'genres': {'rows': 20,}} >>> lay = "b=gtk-save-as" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() b=gtk-save-as >>> sm.laygen.fields_in_layout {} >>> lay = """ ... title year:5< ... director_id - - ... m2m=genres - m2m=actors - ... """ >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() L=title ae=title:30- L=year ae=year:5< L=director_id fk=director_id - - m2m=genres - m2m=actors - >>> lay = """ ... {|.uno ... {N { %title ... title year:5< ... } ... { %file ... m2m=genres m2m=actors - ... } ... } ... } ... """ >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> print sm.lay_obj.lay.strip() {|.uno {N { %title L=title ae=title:30- L=year ae=year:5< } { %file m2m=genres m2m=actors - } } } # :30 is wrong for dates and numbers >>> sm = SqlMask(Movie, dbproxy=db, naked=True, show=True) # sm.current is added with gobject.idle_add... >>> while gtk.events_pending(): ... a=gtk.main_iteration() >>> sm.current >>> sm.get_value('title') >>> sm.widgets['fk=director_id'].set_text('f') >>> sm.completions.director_id.show_possible_completion(sm.widgets['fk=director_id'], 'start', pop=False) Faenza Fellini >>> sm.completions.director_id.show_possible_completion(sm.widgets['fk=director_id'], 'regexp', pop=False) Faenza Fellini Truffaut # check for the need of the generation of an object to satisfy delete-orphan in backref # namely: relation director has a backref 'movies' that has cascade.delete_orphan set # that would prevent setting director_id if the references object is not present in the session >>> for p in sm.gui_fields.director_id._props_for_delete_orphan: str(p) 'Movie.director' >>> sm = SqlMask(AllTypes, dbproxy=db, naked=True, show=False) >>> n = sm.reload() >>> t = SqlTable(AllTypes, dbproxy=db, naked=True, show=False) sqlkit-0.9.5/test/doctest/mapper_inspect.txt0000644000175000017500000000174111714210425020570 0ustar sandrosandro# -*- mode: python -*- >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies import Movie, Director, AllTypes, db >>> from sqlkit.db.minspect import InspectMapper >>> import decimal >>> import datetime >>> mapper = AllTypes.__mapper__ >>> ip = InspectMapper(mapper) >>> def check(field_name): ... return ip.get_py_type(type(mapper.c[field_name].type), field_name) >>> check('id') >>> check('varchar10') >>> check('varchar200') >>> check('text') >>> check('uni') >>> check('uni_text') >>> check('date') >>> check('datetime') >>> check('interval') >>> check('time') >>> check('integer') >>> check('float') >>> check('numeric') >>> check('bool') >>> check('bool_null') sqlkit-0.9.5/test/doctest/README0000644000175000017500000000030111714210425015665 0ustar sandrosandroTo test all doctests you can: ./runtests.py Alternatives: nose ---- nosetests --pdb-failure --with-doctest --doctest-extension=txt py.test ------- py.test --pdb --doctest-glob=*txt sqlkit-0.9.5/test/doctest/dates.txt0000644000175000017500000000613411714210425016660 0ustar sandrosandro# -*- mode: python -*- >>> from sqlkit.misc import datetools as dt >>> from datetime import * >>> dt.TEST = True >>> dt.TODAY = date(2007, 5, 15) >>> dt.string2date('d') datetime.date(2007, 5, 15) >>> dt.string2date('m') datetime.date(2007, 5, 1) >>> dt.string2date('M') datetime.date(2007, 5, 31) >>> dt.TODAY = date(2007, 6, 2) ## day >>> dt.string2date('d') datetime.date(2007, 6, 2) >>> dt.string2date('-1d') datetime.date(2007, 6, 1) >>> dt.string2date('i') datetime.date(2007, 6, 1) >>> dt.string2date('d-5') datetime.date(2007, 5, 28) >>> dt.string2date('d+9') datetime.date(2007, 6, 11) ## week (european stile...) >>> dt.string2date('w') datetime.date(2007, 5, 28) >>> dt.string2date('W') datetime.date(2007, 6, 3) >>> dt.string2date('w-1') datetime.date(2007, 5, 21) >>> dt.string2date('W-1') datetime.date(2007, 5, 27) >>> dt.string2date('w+1') datetime.date(2007, 6, 4) >>> dt.string2date('W+1') datetime.date(2007, 6, 10) ## month >>> dt.string2date('m') datetime.date(2007, 6, 1) >>> dt.string2date('M') datetime.date(2007, 6, 30) >>> dt.string2date('m-1') datetime.date(2007, 5, 1) >>> dt.string2date('M-1') datetime.date(2007, 5, 31) >>> dt.string2date('M-1m') datetime.date(2007, 5, 31) >>> dt.string2date('M-2d-1m') datetime.date(2007, 5, 28) >>> dt.string2date('M-1m-2d') datetime.date(2007, 5, 29) >>> dt.string2date('m+1') datetime.date(2007, 7, 1) >>> dt.string2date('M+1') datetime.date(2007, 7, 31) ## year >>> dt.string2date('y') datetime.date(2007, 1, 1) >>> dt.string2date('Y') datetime.date(2007, 12, 31) >>> dt.string2date('y-1') datetime.date(2006, 1, 1) >>> dt.string2date('Y-1') datetime.date(2006, 12, 31) >>> dt.string2date('y-1') datetime.date(2006, 1, 1) >>> dt.string2date('Y-1') datetime.date(2006, 12, 31) >>> dt.string2date('y+1') datetime.date(2008, 1, 1) >>> dt.string2date('Y+1') datetime.date(2008, 12, 31) # mixing. >>> dt.string2date('y +2m') datetime.date(2007, 3, 1) >>> dt.string2date('y+2m') datetime.date(2007, 3, 1) # >>> dt.string2date('Y -2m') datetime.date(2007, 10, 31) >>> dt.string2date('Y -1m') datetime.date(2007, 11, 30) >>> dt.string2date('Y -m') datetime.date(2007, 11, 30) # periods >>> dt.string2dates('y > Y') (datetime.date(2007, 1, 1), datetime.date(2007, 12, 31)) # >>> dt.string2dates('y+3m > Y-3m') (datetime.date(2007, 4, 1), datetime.date(2007, 9, 30)) >>> dt.string2dates('@m') (datetime.date(2007, 6, 1), datetime.date(2007, 6, 30)) >>> dt.string2dates('@2m') (datetime.date(2007, 6, 1), datetime.date(2007, 7, 31)) >>> dt.string2dates('@2m-1') (datetime.date(2007, 5, 1), datetime.date(2007, 6, 30)) >>> dt.string2dates('@w') (datetime.date(2007, 5, 28), datetime.date(2007, 6, 3)) >>> dt.string2dates('@w+2') (datetime.date(2007, 6, 11), datetime.date(2007, 6, 17)) >>> dt.string2dates('@m-1') (datetime.date(2007, 5, 1), datetime.date(2007, 5, 31)) >>> dt.string2dates('@y') (datetime.date(2007, 1, 1), datetime.date(2007, 12, 31)) >>> dt.string2dates('@y-1') (datetime.date(2006, 1, 1), datetime.date(2006, 12, 31)) >>> dt.string2dates('@y +1') (datetime.date(2008, 1, 1), datetime.date(2008, 12, 31)) # Not implemented #>>> dt.string2dates('@m') sqlkit-0.9.5/test/doctest/filters.txt0000644000175000017500000000326511714210425017232 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> import gtk >>> os.environ['LANG'] = 'en_US.utf8' >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies import Movie, Director, AllTypes, db >>> from sqlkit.widgets import SqlMask, SqlTable >>> from sqlkit.db import defaults >>> defaults.tables {} >>> lay = "title" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.lay_obj.lay 'L=title ae=title:30-' >>> sm.laygen.fields_in_layout {'title': 'e=title'} ## completions >>> sm = SqlMask(Movie, dbproxy=db, naked=True, show=True) >>> sm.get_value('title') >>> while gtk.events_pending(): ... a=gtk.main_iteration() >>> sm.widgets['fk=director_id'].set_text('f') ## the following will fail with gtk < 2.16 (where a differenfk-entry is defined >>> sm.completions.director_id.show_possible_completion(sm.widgets['fk=director_id'], 'start', pop=False) Faenza Fellini >>> sm.completions.director_id.show_possible_completion(sm.widgets['fk=director_id'], 'regexp', pop=False) Faenza Fellini Truffaut >>> sm = SqlMask(AllTypes, dbproxy=db, naked=True, show=False) >>> n = sm.reload() >>> t = SqlTable(AllTypes, dbproxy=db, naked=True, show=False) ## Filters >>> sm = SqlMask(AllTypes, dbproxy=db, naked=True, show=False) >>> sm.add_filter(integer__gte=1) >>> sm.add_filter(float__gte=1.1) >>> sm.add_filter(numeric__gte=1.1) >>> sm.add_filter(date__gte='y-1') >>> sm.add_filter(date__gte='6/2/08') >>> sm.add_filter(datetime__null=True) >>> sm.add_filter(time__null=False) >>> sm.add_filter(bool=True) >>> sm.add_filter(bool=False) >>> sm.add_filter(varchar10__icontains='a') >>> sm.add_filter(varchar10__ilike='a') >>> n = sm.reload() sqlkit-0.9.5/test/doctest/completions.txt0000644000175000017500000001607011714210425020114 0ustar sandrosandro# -*- mode: python -*- >>> import os >>> import gobject >>> import gtk >>> os.environ['LANG'] = 'en_US.utf8' >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies import Movie, Director, AllTypes, Nation, db >>> from sqlkit.widgets import SqlMask, SqlTable >>> lay = "title year director_id m2m=genres m2m=actors " >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) ## 01 - title simple >>> print sm.completions.title.debug_completion_sql(value='il') SELECT DISTINCT movie.title FROM movie WHERE movie.title LIKE 'il%' ORDER BY movie.title ## 02 - title mode regexp >>> print sm.completions.title.debug_completion_sql(value='il', mode='regexp') SELECT DISTINCT movie.title FROM movie WHERE movie.title LIKE '%il%' ORDER BY movie.title ## 03 - filter title >>> sm.completions.title.filter(title__icontains='a') >>> print sm.completions.title.debug_completion_sql(value='il') SELECT DISTINCT movie.title FROM movie WHERE lower(movie.title) LIKE lower('%a%') AND (movie.title LIKE 'il%') ORDER BY movie.title ## 04 - numbers do not have completions >>> print sm.completions.year.debug_completion_sql(value=None) Traceback (most recent call last): ... AttributeError: 'Container' object has no attribute 'year' ## 05 - completion simple on ForeignKey >>> print sm.completions.director_id.debug_completion_sql(value=None) SELECT DISTINCT director.id, director.last_name FROM director WHERE director.last_name LIKE '%' ORDER BY director.last_name ## 06 - filter on completion on ForeignKey >>> sm.completions.director_id.filter(last_name__ilike='fe%') >>> print sm.completions.director_id.debug_completion_sql(value='xx') SELECT DISTINCT director.id, director.last_name FROM director WHERE lower(director.last_name) LIKE lower('fe%') AND (director.last_name LIKE 'xx%') ORDER BY director.last_name ## 07 - completion on m2m enum mode - no real filter >>> print sm.related.genres.completions.name.debug_completion_sql(value='xxx', mode='enum') SELECT DISTINCT genre.name FROM genre ORDER BY genre.name ## 08 - filter on completion m2m enum mode - >>> sm.related.genres.completions.name.filter(name__ilike='dr') >>> print sm.related.genres.completions.name.debug_completion_sql(value='xxx', mode='enum') SELECT DISTINCT genre.name FROM genre WHERE lower(genre.name) LIKE lower('dr') ORDER BY genre.name ## 09 - Completion on fields of an m2m - field: first_name >>> print sm.related.actors.completions.first_name.debug_completion_sql(value='a') SELECT DISTINCT actor.id, actor.first_name, actor.last_name FROM actor WHERE actor.first_name LIKE 'a%' ORDER BY actor.first_name ## 10 - Completion on fields of an m2m - field: last_name >>> print sm.related.actors.completions.last_name.debug_completion_sql(value='a') SELECT DISTINCT actor.id, actor.first_name, actor.last_name FROM actor WHERE actor.last_name LIKE 'a%' ORDER BY actor.last_name ## 11 - completion on a foreign_key of an m2m >>> print sm.related.actors.completions.nation_cod.debug_completion_sql(value='Italy') SELECT DISTINCT actor.id, actor.first_name, actor.last_name, nation_1.nation FROM actor JOIN nation AS nation_1 ON actor.nation_cod = nation_1.cod WHERE nation_1.nation LIKE 'Italy%' ORDER BY actor.last_name ## 12 - filter on a completion of an m2m >>> sm.related.actors.completions.first_name.filter(first_name__ilike='f%') >>> print sm.related.actors.completions.nation_cod.debug_completion_sql(value='') SELECT DISTINCT actor.id, actor.first_name, actor.last_name, nation_1.nation FROM actor JOIN nation AS nation_1 ON actor.nation_cod = nation_1.cod WHERE lower(actor.first_name) LIKE lower('f%') AND (nation_1.nation LIKE '%') ORDER BY actor.last_name ## 13 - completion on a foreign_key of an m2m - same as 11 but different output since filter ## was set in 12. The flter was set on another field but non editable m2m implies ## the same filter on all fields >>> print sm.related.actors.completions.nation_cod.debug_completion_sql(value='Italy') SELECT DISTINCT actor.id, actor.first_name, actor.last_name, nation_1.nation FROM actor JOIN nation AS nation_1 ON actor.nation_cod = nation_1.cod WHERE lower(actor.first_name) LIKE lower('f%') AND (nation_1.nation LIKE 'Italy%') ORDER BY actor.last_name ## 14 - filter on a completion of an m2m with relation on table >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.related.actors.completions.nation_cod.filter(nation__cod='Italy') >>> print sm.related.actors.completions.nation_cod.debug_completion_sql() SELECT DISTINCT actor.id, actor.first_name, actor.last_name, nation_1.nation FROM actor JOIN nation AS nation_1 ON actor.nation_cod = nation_1.cod JOIN nation ON nation.cod = actor.nation_cod WHERE (nation.cod = 'Italy') AND (nation_1.nation LIKE '%') ORDER BY actor.last_name >>> from sqlkit.db import utils >>> utils.register_class(Movie) >>> utils.register_class(Nation) ## 15 - filter on a completion of an m2m with relation on table - afetr registering >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> sm.related.actors.completions.nation_cod.filter(nation__cod='Italy') >>> print sm.related.actors.completions.nation_cod.debug_completion_sql() SELECT DISTINCT actor.id, actor.first_name, actor.last_name, nation_1.nation FROM actor JOIN nation AS nation_1 ON actor.nation_cod = nation_1.cod JOIN nation ON nation.cod = actor.nation_cod WHERE (nation.cod = 'Italy') AND (nation_1.nation LIKE '%') ORDER BY actor.last_name ## Join ## 20 - completion on a field in a joined table - first table >>> sm = SqlTable("actor nation", dbproxy=db, naked=True, show=False) >>> print sm.completions.first_name.debug_completion_sql() SELECT DISTINCT actor.first_name FROM actor WHERE actor.first_name LIKE '%' ORDER BY actor.first_name ## 21 - completion on a field in a joined table - second table >>> print sm.completions.nation.debug_completion_sql() SELECT DISTINCT nation.nation FROM nation WHERE nation.nation LIKE '%' ORDER BY nation.nation ## 22 - filter on a completion on a field in a joined table - first table >>> sm.completions.first_name.filter(last_name__like='aa') >>> print sm.completions.first_name.debug_completion_sql() SELECT DISTINCT actor.first_name FROM actor WHERE (actor.last_name like 'aa') AND (actor.first_name LIKE '%') ORDER BY actor.first_name ## 23 - filter on a completion on a field in a joined table - second table >>> sm.completions.nation.filter(nation__like='aa') >>> print sm.completions.nation.debug_completion_sql() SELECT DISTINCT nation.nation FROM nation WHERE (nation.nation like 'aa') AND (nation.nation LIKE '%') ORDER BY nation.nation ## Dynamic filter >>> lay = "first_name nation" >>> sm = SqlMask(Director, dbproxy=db, layout=lay, naked=True) >>> idx = gobject.idle_add(sm.set_value, 'nation', 'IT') >>> while gtk.events_pending(): ... hide = gtk.main_iteration_do() >>> sm.completions.first_name.filter(nation='$nation') >>> print sm.completions.first_name.debug_completion_sql() SELECT DISTINCT director.first_name FROM director WHERE (director.first_name LIKE '%') AND (director.nation = 'IT') ORDER BY director.first_name sqlkit-0.9.5/test/doctest/fields_autoload.txt0000644000175000017500000000261711714210425020720 0ustar sandrosandro# -*- mode: python -*- >>> import sys >>> sys.path.insert(0, '../../demo/sql/model') >>> sys.path.insert(0, '../../') >>> from movies_autoload import Movie, Director, db >>> from sqlkit import SqlMask >>> from sqlkit.widgets.common.fields import FieldChooser >>> lay = "title year director_id m2m=genres date_release" >>> sm = SqlMask(Movie, dbproxy=db, layout=lay, naked=True, show=False) >>> field_chooser = FieldChooser(sm, sm.widgets) >>> field_name, key = 'title', 'title' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'year', 'year' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'director_id', 'director_id' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'director_id', 'fk=director_id' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) >>> field_name, key = 'genres', 'm2m=genres' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key, test=True) >>> field_name, key = 'date_release', 'date_release' >>> print field_chooser.get_field(field_name, sm.mapper_info.fields[field_name], key) sqlkit-0.9.5/sqlkit.desktop0000644000175000017500000000020711714210425015270 0ustar sandrosandro[Desktop Entry] Version=1.0 Comment=sqledit Name=Sqledit Exec=/usr/bin/sqledit Icon= Terminal=false Type=Application Categories=Office sqlkit-0.9.5/README.localization0000644000175000017500000000127411714210425015742 0ustar sandrosandroLocalization ============ You can read about sqlkit and localization in the docs. If you feel like contributing, I'd really appreciate you send me a translation file that you can obtain following the following steps. 1. copy locale/sqlkit.pot in the folder (to be created) for your language following the standard structure. Eg. for italy: locale/it/LC_MESSAGES/sqlkit.po 2. edit it with your preferred tool (emacs, virtaal, poEdit) and send me (sandro@e-den.it) the translation 3. compile the .po file into a .mo file with: python setup.py compile_catalog it you are updating you translation, substitute step 1. with: 1b. python setup.py update_catalog sqlkit-0.9.5/NOTE.txt0000644000175000017500000000640311714210425013700 0ustar sandrosandro =========== l'obiettivo =========== 1. Quando si apre una tabella deve aprirsi con già le colonne dimensionate correttamente per il testo che deve mostrare -anche se è al momento vuota-, prendendo le informazioni dal database via il campo fields[field_name]['length'] 2. La dimensione della tabella deve essere sufficiente per mostrare tutte le colonne ma non superiore alla dimensione dello schermo. 2b. per le colonne condimensione > di (circa) 60 caratteri, la colonna deve non superare i 60 caratteri. 3. Deve essere possibile ridimensionare le colonne *ai dati presenti* in un *dato momento*. Questa è una operazioen differente dalla precedente. Quetsa funzione potrebbe ad esempio essere invocata da una icona Zoom-to-fit 4. per quanto possibile le funzioni deve essere esportabile alle table non SqlTable, ovvero quelle che non hanno un database sotto e quindi che non hanno coscienza a priori della larghezza del campo (questo può essere vero solo per il punto 3.) columns ======= Usando la proprietà 'expand' solo per le colonne corrispondenti a VarChar con length > x (es. 20 o 30), ed il resize = True per tutti si ottiene già una maggiore usabilità. La versione nel repo (dev) implementa questo. Resta un fastidioso problema di interazione quando si voglia ridimensionare. cell-renderer ============= Proviamo usando dei cell renderers che passino dele informazioni sulla larghezza da utilizzare via le funzioni on_get_size. date ---- L'esempio da cui sono partito è il cell renderer delle date, ho provato a truccare la larghezza così:: width = width * XSCALE + (xpad * 2) height = height + (ypad * 2) ed effettivamente se XSCALE cambia anche la larghezza della colonna cambia (nella fattispece la colonna ha expand = False). .. note:: se si cambia interattivamente XSCALE (es. con ipython) la largezza della colonna non cambia subito ma quando uso:: t.treeview.column_autosize() se però ridimensiono a mano, non le nuove dimensioni vengono mantenute (cosa che ci va bene) varchar ------- procedi a fare un CellRenderer per i varchar che erediti da CellRendererText e sovrascriva solo on_get_size, calcolandosi la larghezza da passare. Il cell Renderer deve avere un campo aggiuntivo length: class CellRendererVarchar(gtk.CellRendererText): def __init__(self, length, *args, **kwargs) self.length = length return gtk.CellRendererText.__gobject_init__(self, *args, **kwargs) def on_get_size(...) Se questa cosa dimensiona correttamente la colonna (e non vedo perché non dovrebbe), il gioco è fatto: il resto è qualche algoritmo per decidere che larghezza massima da dare alla finestra (ma già hai fatto qualcosa) e veder come farsi passare le informazioni dall cell renderer per dimenzionare il tutto. =============== Dubbi =============== metodi virtuali =============== io credo che si possa usare on_get_size invece che do_get_size, cosa che è permessa a GenericCellRenderer, ma non sono sicuro. Quello che non capisco è quando si usa il metodo virtuale do_ e quando si usa l'altro. esempi ======== io ho un esempio di finestra che sballa i calcoli ma è in un database differente. Nei prossimi giorni provo a metterlo sul db movies. sqlkit-0.9.5/distribute_setup.py0000644000175000017500000003661511714210425016352 0ustar sandrosandro#!python """Bootstrap distribute installation If you want to use setuptools in your package's setup.py, just include this file in the same directory with it, and add this to the top of your setup.py:: from distribute_setup import use_setuptools use_setuptools() If you want to require a specific version of setuptools, set a download mirror, or use an alternate download directory, you can do so by supplying the appropriate options to ``use_setuptools()``. This file can also be run as a script to install or upgrade setuptools. """ import os import sys import time import fnmatch import tempfile import tarfile from distutils import log try: from site import USER_SITE except ImportError: USER_SITE = None try: import subprocess def _python_cmd(*args): args = (sys.executable,) + args return subprocess.call(args) == 0 except ImportError: # will be used for python 2.3 def _python_cmd(*args): args = (sys.executable,) + args # quoting arguments if windows if sys.platform == 'win32': def quote(arg): if ' ' in arg: return '"%s"' % arg return arg args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 DEFAULT_VERSION = "0.6.14" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" SETUPTOOLS_PKG_INFO = """\ Metadata-Version: 1.0 Name: setuptools Version: %s Summary: xxxx Home-page: xxx Author: xxx Author-email: xxx License: xxx Description: xxx """ % SETUPTOOLS_FAKED_VERSION def _install(tarball): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # installing log.warn('Installing Distribute') if not _python_cmd('setup.py', 'install'): log.warn('Something went wrong during the installation.') log.warn('See the error message above.') finally: os.chdir(old_wd) def _build_egg(egg, tarball, to_dir): # extracting the tarball tmpdir = tempfile.mkdtemp() log.warn('Extracting in %s', tmpdir) old_wd = os.getcwd() try: os.chdir(tmpdir) tar = tarfile.open(tarball) _extractall(tar) tar.close() # going in the directory subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0]) os.chdir(subdir) log.warn('Now working in %s', subdir) # building an egg log.warn('Building a Distribute egg in %s', to_dir) _python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir) finally: os.chdir(old_wd) # returning the result log.warn(egg) if not os.path.exists(egg): raise IOError('Could not build the egg.') def _do_download(version, download_base, to_dir, download_delay): egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg' % (version, sys.version_info[0], sys.version_info[1])) if not os.path.exists(egg): tarball = download_setuptools(version, download_base, to_dir, download_delay) _build_egg(egg, tarball, to_dir) sys.path.insert(0, egg) import setuptools setuptools.bootstrap_install_from = egg def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, download_delay=15, no_fake=True): # making sure we use the absolute path to_dir = os.path.abspath(to_dir) was_imported = 'pkg_resources' in sys.modules or \ 'setuptools' in sys.modules try: try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): if not no_fake: _fake_setuptools() raise ImportError except ImportError: return _do_download(version, download_base, to_dir, download_delay) try: pkg_resources.require("distribute>="+version) return except pkg_resources.VersionConflict: e = sys.exc_info()[1] if was_imported: sys.stderr.write( "The required version of distribute (>=%s) is not available,\n" "and can't be installed while this script is running. Please\n" "install a more recent version first, using\n" "'easy_install -U distribute'." "\n\n(Currently using %r)\n" % (version, e.args[0])) sys.exit(2) else: del pkg_resources, sys.modules['pkg_resources'] # reload ok return _do_download(version, download_base, to_dir, download_delay) except pkg_resources.DistributionNotFound: return _do_download(version, download_base, to_dir, download_delay) finally: if not no_fake: _create_fake_setuptools_pkg_info(to_dir) def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir, delay=15): """Download distribute from a specified location and return its filename `version` should be a valid distribute version number that is available as an egg for download under the `download_base` URL (which should end with a '/'). `to_dir` is the directory where the egg will be downloaded. `delay` is the number of seconds to pause before an actual download attempt. """ # making sure we use the absolute path to_dir = os.path.abspath(to_dir) try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen tgz_name = "distribute-%s.tar.gz" % version url = download_base + tgz_name saveto = os.path.join(to_dir, tgz_name) src = dst = None if not os.path.exists(saveto): # Avoid repeated downloads try: log.warn("Downloading %s", url) src = urlopen(url) # Read/write all in one block, so we don't create a corrupt file # if the download is interrupted. data = src.read() dst = open(saveto, "wb") dst.write(data) finally: if src: src.close() if dst: dst.close() return os.path.realpath(saveto) def _no_sandbox(function): def __no_sandbox(*args, **kw): try: from setuptools.sandbox import DirectorySandbox if not hasattr(DirectorySandbox, '_old'): def violation(*args): pass DirectorySandbox._old = DirectorySandbox._violation DirectorySandbox._violation = violation patched = True else: patched = False except ImportError: patched = False try: return function(*args, **kw) finally: if patched: DirectorySandbox._violation = DirectorySandbox._old del DirectorySandbox._old return __no_sandbox def _patch_file(path, content): """Will backup the file then patch it""" existing_content = open(path).read() if existing_content == content: # already patched log.warn('Already patched.') return False log.warn('Patching...') _rename_path(path) f = open(path, 'w') try: f.write(content) finally: f.close() return True _patch_file = _no_sandbox(_patch_file) def _same_content(path, content): return open(path).read() == content def _rename_path(path): new_name = path + '.OLD.%s' % time.time() log.warn('Renaming %s into %s', path, new_name) os.rename(path, new_name) return new_name def _remove_flat_installation(placeholder): if not os.path.isdir(placeholder): log.warn('Unkown installation at %s', placeholder) return False found = False for file in os.listdir(placeholder): if fnmatch.fnmatch(file, 'setuptools*.egg-info'): found = True break if not found: log.warn('Could not locate setuptools*.egg-info') return log.warn('Removing elements out of the way...') pkg_info = os.path.join(placeholder, file) if os.path.isdir(pkg_info): patched = _patch_egg_dir(pkg_info) else: patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO) if not patched: log.warn('%s already patched.', pkg_info) return False # now let's move the files out of the way for element in ('setuptools', 'pkg_resources.py', 'site.py'): element = os.path.join(placeholder, element) if os.path.exists(element): _rename_path(element) else: log.warn('Could not find the %s element of the ' 'Setuptools distribution', element) return True _remove_flat_installation = _no_sandbox(_remove_flat_installation) def _after_install(dist): log.warn('After install bootstrap.') placeholder = dist.get_command_obj('install').install_purelib _create_fake_setuptools_pkg_info(placeholder) def _create_fake_setuptools_pkg_info(placeholder): if not placeholder or not os.path.exists(placeholder): log.warn('Could not find the install location') return pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1]) setuptools_file = 'setuptools-%s-py%s.egg-info' % \ (SETUPTOOLS_FAKED_VERSION, pyver) pkg_info = os.path.join(placeholder, setuptools_file) if os.path.exists(pkg_info): log.warn('%s already exists', pkg_info) return log.warn('Creating %s', pkg_info) f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() pth_file = os.path.join(placeholder, 'setuptools.pth') log.warn('Creating %s', pth_file) f = open(pth_file, 'w') try: f.write(os.path.join(os.curdir, setuptools_file)) finally: f.close() _create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info) def _patch_egg_dir(path): # let's check if it's already patched pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') if os.path.exists(pkg_info): if _same_content(pkg_info, SETUPTOOLS_PKG_INFO): log.warn('%s already patched.', pkg_info) return False _rename_path(path) os.mkdir(path) os.mkdir(os.path.join(path, 'EGG-INFO')) pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO') f = open(pkg_info, 'w') try: f.write(SETUPTOOLS_PKG_INFO) finally: f.close() return True _patch_egg_dir = _no_sandbox(_patch_egg_dir) def _before_install(): log.warn('Before install bootstrap.') _fake_setuptools() def _under_prefix(location): if 'install' not in sys.argv: return True args = sys.argv[sys.argv.index('install')+1:] for index, arg in enumerate(args): for option in ('--root', '--prefix'): if arg.startswith('%s=' % option): top_dir = arg.split('root=')[-1] return location.startswith(top_dir) elif arg == option: if len(args) > index: top_dir = args[index+1] return location.startswith(top_dir) if arg == '--user' and USER_SITE is not None: return location.startswith(USER_SITE) return True def _fake_setuptools(): log.warn('Scanning installed packages') try: import pkg_resources except ImportError: # we're cool log.warn('Setuptools or Distribute does not seem to be installed.') return ws = pkg_resources.working_set try: setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools', replacement=False)) except TypeError: # old distribute API setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools')) if setuptools_dist is None: log.warn('No setuptools distribution found') return # detecting if it was already faked setuptools_location = setuptools_dist.location log.warn('Setuptools installation detected at %s', setuptools_location) # if --root or --preix was provided, and if # setuptools is not located in them, we don't patch it if not _under_prefix(setuptools_location): log.warn('Not patching, --root or --prefix is installing Distribute' ' in another location') return # let's see if its an egg if not setuptools_location.endswith('.egg'): log.warn('Non-egg installation') res = _remove_flat_installation(setuptools_location) if not res: return else: log.warn('Egg installation') pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO') if (os.path.exists(pkg_info) and _same_content(pkg_info, SETUPTOOLS_PKG_INFO)): log.warn('Already patched.') return log.warn('Patching...') # let's create a fake egg replacing setuptools one res = _patch_egg_dir(setuptools_location) if not res: return log.warn('Patched done.') _relaunch() def _relaunch(): log.warn('Relaunching...') # we have to relaunch the process # pip marker to avoid a relaunch bug if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']: sys.argv[0] = 'setup.py' args = [sys.executable] + sys.argv sys.exit(subprocess.call(args)) def _extractall(self, path=".", members=None): """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned by getmembers(). """ import copy import operator from tarfile import ExtractError directories = [] if members is None: members = self for tarinfo in members: if tarinfo.isdir(): # Extract directories with a safe mode. directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 448 # decimal for oct 0700 self.extract(tarinfo, path) # Reverse sort directories. if sys.version_info < (2, 4): def sorter(dir1, dir2): return cmp(dir1.name, dir2.name) directories.sort(sorter) directories.reverse() else: directories.sort(key=operator.attrgetter('name'), reverse=True) # Set correct owner, mtime and filemode on directories. for tarinfo in directories: dirpath = os.path.join(path, tarinfo.name) try: self.chown(tarinfo, dirpath) self.utime(tarinfo, dirpath) self.chmod(tarinfo, dirpath) except ExtractError: e = sys.exc_info()[1] if self.errorlevel > 1: raise else: self._dbg(1, "tarfile: %s" % e) def main(argv, version=DEFAULT_VERSION): """Install or upgrade setuptools and EasyInstall""" tarball = download_setuptools() _install(tarball) if __name__ == '__main__': main(sys.argv[1:]) sqlkit-0.9.5/README.pyinstaller0000644000175000017500000000264311714210425015621 0ustar sandrosandro=========== Pyinstaller =========== Main goals in building this installer are: * make it easy to test the software and run the demo * make it possible to use the command sqledit with an asy installation process If you just want to use the installer you should simply install it and run it. If you want to prepare a different installer to add some feature (eg: more database drivers), you should follow the following steps. Configuration is inside file pyinstaller-sqledit.spec. Thanks to Giovanni Bajo -author of pyinstaller- for assisting me in writing the specs and fixing pyinstaller when needed. install pyinstaller =================== install pyinstaller (http://www.pyinstaller.org/) from the last devel (at least rev 700):: svn co http://svn.pyinstaller.org/trunk pyinstaller and follow instruction in the manual [1] Getting Started * Installing PyInstaller * Building the runtime executables just under linux: sudo apt-get install build-essential python-dev cd source/linux python Make.py; make cd ../../ * Configuring your PyInstaller setup Create the installer ==================== run:: ./pynstaller/make-installer path_to_Build.py pyinstaller-sqledit.spec This will create a directory build and a directory pyinstaller. Inside the latter one you'll find directory sqledit [1] http://www.pyinstaller.org/static/docs/Manual_v1.1.html sqlkit-0.9.5/README0000644000175000017500000000066611714210425013257 0ustar sandrosandroDemo ------ there are two demo: one for the two main widgets (SqlMask and SqlTable) and one for the layout module. Each demo should be started from the respective directory and does not require to be installed: cd demo/sql/ python demo.py I hope you'll appreciate it. I'd be happy to have some feedback. Support ------- There's a googlegroup dedicated to this package: http://groups.google.it/group/sqlkit sandro *:-)