home *** CD-ROM | disk | FTP | other *** search
/ LG Super CD / LG Super CD.iso / bitpim / bitpim-0.62-setup.exe / {app} / bitpim.exe / wxPython / lib / maskededit.pyo (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2003-11-06  |  172.6 KB  |  5,039 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyo (Python 2.3)
  3.  
  4. from wxPython.wx import *
  5. import string
  6. import re
  7. import copy
  8. import difflib
  9. import types
  10. from wxPython.tools.dbg import Logger
  11. dbg = Logger()
  12. dbg(enable = 0)
  13. WXK_CTRL_A = ord('A') + 1 - ord('A')
  14. WXK_CTRL_C = ord('C') + 1 - ord('A')
  15. WXK_CTRL_S = ord('S') + 1 - ord('A')
  16. WXK_CTRL_V = ord('V') + 1 - ord('A')
  17. WXK_CTRL_X = ord('X') + 1 - ord('A')
  18. WXK_CTRL_Z = ord('Z') + 1 - ord('A')
  19. nav = (WXK_BACK, WXK_LEFT, WXK_RIGHT, WXK_UP, WXK_DOWN, WXK_TAB, WXK_HOME, WXK_END, WXK_RETURN, WXK_PRIOR, WXK_NEXT)
  20. control = (WXK_BACK, WXK_DELETE, WXK_CTRL_A, WXK_CTRL_C, WXK_CTRL_S, WXK_CTRL_V, WXK_CTRL_X, WXK_CTRL_Z)
  21. maskchars = ('#', 'A', 'a', 'X', 'C', 'N', '&')
  22. months = '(01|02|03|04|05|06|07|08|09|10|11|12)'
  23. charmonths = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec|JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)'
  24. charmonths_dict = {
  25.     'jan': 1,
  26.     'feb': 2,
  27.     'mar': 3,
  28.     'apr': 4,
  29.     'may': 5,
  30.     'jun': 6,
  31.     'jul': 7,
  32.     'aug': 8,
  33.     'sep': 9,
  34.     'oct': 10,
  35.     'nov': 11,
  36.     'dec': 12 }
  37. days = '(01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)'
  38. hours = '(0\\d| \\d|1[012])'
  39. milhours = '(00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23)'
  40. minutes = '(00|01|02|03|04|05|06|07|08|09|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59)'
  41. seconds = minutes
  42. am_pm_exclude = 'BCDEFGHIJKLMNOQRSTUVWXYZ\x8a\x8c\x8e\x9f\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd8\xd9\xda\xdb\xdc\xdd\xde'
  43. states = 'AL,AK,AZ,AR,CA,CO,CT,DE,DC,FL,GA,GU,HI,ID,IL,IN,IA,KS,KY,LA,MA,ME,MD,MI,MN,MS,MO,MT,NE,NV,NH,NJ,NM,NY,NC,ND,OH,OK,OR,PA,PR,RI,SC,SD,TN,TX,UT,VA,VT,VI,WA,WV,WI,WY'.split(',')
  44. state_names = [
  45.     'Alabama',
  46.     'Alaska',
  47.     'Arizona',
  48.     'Arkansas',
  49.     'California',
  50.     'Colorado',
  51.     'Connecticut',
  52.     'Delaware',
  53.     'District of Columbia',
  54.     'Florida',
  55.     'Georgia',
  56.     'Hawaii',
  57.     'Idaho',
  58.     'Illinois',
  59.     'Indiana',
  60.     'Iowa',
  61.     'Kansas',
  62.     'Kentucky',
  63.     'Louisiana',
  64.     'Maine',
  65.     'Maryland',
  66.     'Massachusetts',
  67.     'Michigan',
  68.     'Minnesota',
  69.     'Mississippi',
  70.     'Missouri',
  71.     'Montana',
  72.     'Nebraska',
  73.     'Nevada',
  74.     'New Hampshire',
  75.     'New Jersey',
  76.     'New Mexico',
  77.     'New York',
  78.     'North Carolina',
  79.     'North Dakokta',
  80.     'Ohio',
  81.     'Oklahoma',
  82.     'Oregon',
  83.     'Pennsylvania',
  84.     'Puerto Rico',
  85.     'Rhode Island',
  86.     'South Carolina',
  87.     'South Dakota',
  88.     'Tennessee',
  89.     'Texas',
  90.     'Utah',
  91.     'Vermont',
  92.     'Virginia',
  93.     'Washington',
  94.     'West Virginia',
  95.     'Wisconsin',
  96.     'Wyoming']
  97. masktags = {
  98.     'USPHONEFULLEXT': {
  99.         'mask': '(###) ###-#### x:###',
  100.         'formatcodes': 'F^->',
  101.         'validRegex': '^\\(\\d{3}\\) \\d{3}-\\d{4}',
  102.         'description': 'Phone Number w/opt. ext' },
  103.     'USPHONETIGHTEXT': {
  104.         'mask': '###-###-#### x:###',
  105.         'formatcodes': 'F^->',
  106.         'validRegex': '^\\d{3}-\\d{3}-\\d{4}',
  107.         'description': 'Phone Number\n (w/hyphens and opt. ext)' },
  108.     'USPHONEFULL': {
  109.         'mask': '(###) ###-####',
  110.         'formatcodes': 'F^->',
  111.         'validRegex': '^\\(\\d{3}\\) \\d{3}-\\d{4}',
  112.         'description': 'Phone Number only' },
  113.     'USPHONETIGHT': {
  114.         'mask': '###-###-####',
  115.         'formatcodes': 'F^->',
  116.         'validRegex': '^\\d{3}-\\d{3}-\\d{4}',
  117.         'description': 'Phone Number\n(w/hyphens)' },
  118.     'USSTATE': {
  119.         'mask': 'AA',
  120.         'formatcodes': 'F!V',
  121.         'validRegex': '([ACDFGHIKLMNOPRSTUVW] |%s)' % string.join(states, '|'),
  122.         'choices': states,
  123.         'choiceRequired': True,
  124.         'description': 'US State Code' },
  125.     'USSTATENAME': {
  126.         'mask': 'ACCCCCCCCCCCCCCCCCCC',
  127.         'formatcodes': 'F_',
  128.         'validRegex': '([ACDFGHIKLMNOPRSTUVW] |%s)' % string.join(state_names, '|'),
  129.         'choices': state_names,
  130.         'choiceRequired': True,
  131.         'description': 'US State Name' },
  132.     'USDATETIMEMMDDYYYY/HHMMSS': {
  133.         'mask': '##/##/#### ##:##:## AM',
  134.         'excludeChars': am_pm_exclude,
  135.         'formatcodes': 'DF!',
  136.         'validRegex': '^' + months + '/' + days + '/' + '\\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  137.         'description': 'US Date + Time' },
  138.     'USDATETIMEMMDDYYYY-HHMMSS': {
  139.         'mask': '##-##-#### ##:##:## AM',
  140.         'excludeChars': am_pm_exclude,
  141.         'formatcodes': 'DF!',
  142.         'validRegex': '^' + months + '-' + days + '-' + '\\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  143.         'description': 'US Date + Time\n(w/hypens)' },
  144.     'USDATEMILTIMEMMDDYYYY/HHMMSS': {
  145.         'mask': '##/##/#### ##:##:##',
  146.         'formatcodes': 'DF',
  147.         'validRegex': '^' + months + '/' + days + '/' + '\\d{4} ' + milhours + ':' + minutes + ':' + seconds,
  148.         'description': 'US Date + Military Time' },
  149.     'USDATEMILTIMEMMDDYYYY-HHMMSS': {
  150.         'mask': '##-##-#### ##:##:##',
  151.         'formatcodes': 'DF',
  152.         'validRegex': '^' + months + '-' + days + '-' + '\\d{4} ' + milhours + ':' + minutes + ':' + seconds,
  153.         'description': 'US Date + Military Time\n(w/hypens)' },
  154.     'USDATETIMEMMDDYYYY/HHMM': {
  155.         'mask': '##/##/#### ##:## AM',
  156.         'excludeChars': am_pm_exclude,
  157.         'formatcodes': 'DF!',
  158.         'validRegex': '^' + months + '/' + days + '/' + '\\d{4} ' + hours + ':' + minutes + ' (A|P)M',
  159.         'description': 'US Date + Time\n(without seconds)' },
  160.     'USDATEMILTIMEMMDDYYYY/HHMM': {
  161.         'mask': '##/##/#### ##:##',
  162.         'formatcodes': 'DF',
  163.         'validRegex': '^' + months + '/' + days + '/' + '\\d{4} ' + milhours + ':' + minutes,
  164.         'description': 'US Date + Military Time\n(without seconds)' },
  165.     'USDATETIMEMMDDYYYY-HHMM': {
  166.         'mask': '##-##-#### ##:## AM',
  167.         'excludeChars': am_pm_exclude,
  168.         'formatcodes': 'DF!',
  169.         'validRegex': '^' + months + '-' + days + '-' + '\\d{4} ' + hours + ':' + minutes + ' (A|P)M',
  170.         'description': 'US Date + Time\n(w/hypens and w/o secs)' },
  171.     'USDATEMILTIMEMMDDYYYY-HHMM': {
  172.         'mask': '##-##-#### ##:##',
  173.         'formatcodes': 'DF',
  174.         'validRegex': '^' + months + '-' + days + '-' + '\\d{4} ' + milhours + ':' + minutes,
  175.         'description': 'US Date + Military Time\n(w/hyphens and w/o seconds)' },
  176.     'USDATEMMDDYYYY/': {
  177.         'mask': '##/##/####',
  178.         'formatcodes': 'DF',
  179.         'validRegex': '^' + months + '/' + days + '/' + '\\d{4}',
  180.         'description': 'US Date\n(MMDDYYYY)' },
  181.     'USDATEMMDDYY/': {
  182.         'mask': '##/##/##',
  183.         'formatcodes': 'DF',
  184.         'validRegex': '^' + months + '/' + days + '/\\d\\d',
  185.         'description': 'US Date\n(MMDDYY)' },
  186.     'USDATEMMDDYYYY-': {
  187.         'mask': '##-##-####',
  188.         'formatcodes': 'DF',
  189.         'validRegex': '^' + months + '-' + days + '-' + '\\d{4}',
  190.         'description': 'MM-DD-YYYY' },
  191.     'EUDATEYYYYMMDD/': {
  192.         'mask': '####/##/##',
  193.         'formatcodes': 'DF',
  194.         'validRegex': '^' + '\\d{4}' + '/' + months + '/' + days,
  195.         'description': 'YYYY/MM/DD' },
  196.     'EUDATEYYYYMMDD.': {
  197.         'mask': '####.##.##',
  198.         'formatcodes': 'DF',
  199.         'validRegex': '^' + '\\d{4}' + '.' + months + '.' + days,
  200.         'description': 'YYYY.MM.DD' },
  201.     'EUDATEDDMMYYYY/': {
  202.         'mask': '##/##/####',
  203.         'formatcodes': 'DF',
  204.         'validRegex': '^' + days + '/' + months + '/' + '\\d{4}',
  205.         'description': 'DD/MM/YYYY' },
  206.     'EUDATEDDMMYYYY.': {
  207.         'mask': '##.##.####',
  208.         'formatcodes': 'DF',
  209.         'validRegex': '^' + days + '.' + months + '.' + '\\d{4}',
  210.         'description': 'DD.MM.YYYY' },
  211.     'EUDATEDDMMMYYYY.': {
  212.         'mask': '##.CCC.####',
  213.         'formatcodes': 'DF',
  214.         'validRegex': '^' + days + '.' + charmonths + '.' + '\\d{4}',
  215.         'description': 'DD.Month.YYYY' },
  216.     'EUDATEDDMMMYYYY/': {
  217.         'mask': '##/CCC/####',
  218.         'formatcodes': 'DF',
  219.         'validRegex': '^' + days + '/' + charmonths + '/' + '\\d{4}',
  220.         'description': 'DD/Month/YYYY' },
  221.     'EUDATETIMEYYYYMMDD/HHMMSS': {
  222.         'mask': '####/##/## ##:##:## AM',
  223.         'excludeChars': am_pm_exclude,
  224.         'formatcodes': 'DF!',
  225.         'validRegex': '^' + '\\d{4}' + '/' + months + '/' + days + ' ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  226.         'description': 'YYYY/MM/DD HH:MM:SS' },
  227.     'EUDATETIMEYYYYMMDD.HHMMSS': {
  228.         'mask': '####.##.## ##:##:## AM',
  229.         'excludeChars': am_pm_exclude,
  230.         'formatcodes': 'DF!',
  231.         'validRegex': '^' + '\\d{4}' + '.' + months + '.' + days + ' ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  232.         'description': 'YYYY.MM.DD HH:MM:SS' },
  233.     'EUDATETIMEDDMMYYYY/HHMMSS': {
  234.         'mask': '##/##/#### ##:##:## AM',
  235.         'excludeChars': am_pm_exclude,
  236.         'formatcodes': 'DF!',
  237.         'validRegex': '^' + days + '/' + months + '/' + '\\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  238.         'description': 'DD/MM/YYYY HH:MM:SS' },
  239.     'EUDATETIMEDDMMYYYY.HHMMSS': {
  240.         'mask': '##.##.#### ##:##:## AM',
  241.         'excludeChars': am_pm_exclude,
  242.         'formatcodes': 'DF!',
  243.         'validRegex': '^' + days + '.' + months + '.' + '\\d{4} ' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  244.         'description': 'DD.MM.YYYY HH:MM:SS' },
  245.     'EUDATETIMEYYYYMMDD/HHMM': {
  246.         'mask': '####/##/## ##:## AM',
  247.         'excludeChars': am_pm_exclude,
  248.         'formatcodes': 'DF!',
  249.         'validRegex': '^' + '\\d{4}' + '/' + months + '/' + days + ' ' + hours + ':' + minutes + ' (A|P)M',
  250.         'description': 'YYYY/MM/DD HH:MM' },
  251.     'EUDATETIMEYYYYMMDD.HHMM': {
  252.         'mask': '####.##.## ##:## AM',
  253.         'excludeChars': am_pm_exclude,
  254.         'formatcodes': 'DF!',
  255.         'validRegex': '^' + '\\d{4}' + '.' + months + '.' + days + ' ' + hours + ':' + minutes + ' (A|P)M',
  256.         'description': 'YYYY.MM.DD HH:MM' },
  257.     'EUDATETIMEDDMMYYYY/HHMM': {
  258.         'mask': '##/##/#### ##:## AM',
  259.         'excludeChars': am_pm_exclude,
  260.         'formatcodes': 'DF!',
  261.         'validRegex': '^' + days + '/' + months + '/' + '\\d{4} ' + hours + ':' + minutes + ' (A|P)M',
  262.         'description': 'DD/MM/YYYY HH:MM' },
  263.     'EUDATETIMEDDMMYYYY.HHMM': {
  264.         'mask': '##.##.#### ##:## AM',
  265.         'excludeChars': am_pm_exclude,
  266.         'formatcodes': 'DF!',
  267.         'validRegex': '^' + days + '.' + months + '.' + '\\d{4} ' + hours + ':' + minutes + ' (A|P)M',
  268.         'description': 'DD.MM.YYYY HH:MM' },
  269.     'EUDATEMILTIMEYYYYMMDD/HHMMSS': {
  270.         'mask': '####/##/## ##:##:##',
  271.         'formatcodes': 'DF',
  272.         'validRegex': '^' + '\\d{4}' + '/' + months + '/' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
  273.         'description': 'YYYY/MM/DD Mil. Time' },
  274.     'EUDATEMILTIMEYYYYMMDD.HHMMSS': {
  275.         'mask': '####.##.## ##:##:##',
  276.         'formatcodes': 'DF',
  277.         'validRegex': '^' + '\\d{4}' + '.' + months + '.' + days + ' ' + milhours + ':' + minutes + ':' + seconds,
  278.         'description': 'YYYY.MM.DD Mil. Time' },
  279.     'EUDATEMILTIMEDDMMYYYY/HHMMSS': {
  280.         'mask': '##/##/#### ##:##:##',
  281.         'formatcodes': 'DF',
  282.         'validRegex': '^' + days + '/' + months + '/' + '\\d{4} ' + milhours + ':' + minutes + ':' + seconds,
  283.         'description': 'DD/MM/YYYY Mil. Time' },
  284.     'EUDATEMILTIMEDDMMYYYY.HHMMSS': {
  285.         'mask': '##.##.#### ##:##:##',
  286.         'formatcodes': 'DF',
  287.         'validRegex': '^' + days + '.' + months + '.' + '\\d{4} ' + milhours + ':' + minutes + ':' + seconds,
  288.         'description': 'DD.MM.YYYY Mil. Time' },
  289.     'EUDATEMILTIMEYYYYMMDD/HHMM': {
  290.         'mask': '####/##/## ##:##',
  291.         'formatcodes': 'DF',
  292.         'validRegex': '^' + '\\d{4}' + '/' + months + '/' + days + ' ' + milhours + ':' + minutes,
  293.         'description': 'YYYY/MM/DD Mil. Time\n(w/o seconds)' },
  294.     'EUDATEMILTIMEYYYYMMDD.HHMM': {
  295.         'mask': '####.##.## ##:##',
  296.         'formatcodes': 'DF',
  297.         'validRegex': '^' + '\\d{4}' + '.' + months + '.' + days + ' ' + milhours + ':' + minutes,
  298.         'description': 'YYYY.MM.DD Mil. Time\n(w/o seconds)' },
  299.     'EUDATEMILTIMEDDMMYYYY/HHMM': {
  300.         'mask': '##/##/#### ##:##',
  301.         'formatcodes': 'DF',
  302.         'validRegex': '^' + days + '/' + months + '/' + '\\d{4} ' + milhours + ':' + minutes,
  303.         'description': 'DD/MM/YYYY Mil. Time\n(w/o seconds)' },
  304.     'EUDATEMILTIMEDDMMYYYY.HHMM': {
  305.         'mask': '##.##.#### ##:##',
  306.         'formatcodes': 'DF',
  307.         'validRegex': '^' + days + '.' + months + '.' + '\\d{4} ' + milhours + ':' + minutes,
  308.         'description': 'DD.MM.YYYY Mil. Time\n(w/o seconds)' },
  309.     'TIMEHHMMSS': {
  310.         'mask': '##:##:## AM',
  311.         'excludeChars': am_pm_exclude,
  312.         'formatcodes': 'TF!',
  313.         'validRegex': '^' + hours + ':' + minutes + ':' + seconds + ' (A|P)M',
  314.         'description': 'HH:MM:SS (A|P)M\n(see wxTimeCtrl)' },
  315.     'TIMEHHMM': {
  316.         'mask': '##:## AM',
  317.         'excludeChars': am_pm_exclude,
  318.         'formatcodes': 'TF!',
  319.         'validRegex': '^' + hours + ':' + minutes + ' (A|P)M',
  320.         'description': 'HH:MM (A|P)M\n(see wxTimeCtrl)' },
  321.     'MILTIMEHHMMSS': {
  322.         'mask': '##:##:##',
  323.         'formatcodes': 'TF',
  324.         'validRegex': '^' + milhours + ':' + minutes + ':' + seconds,
  325.         'description': 'Military HH:MM:SS\n(see wxTimeCtrl)' },
  326.     'MILTIMEHHMM': {
  327.         'mask': '##:##',
  328.         'formatcodes': 'TF',
  329.         'validRegex': '^' + milhours + ':' + minutes,
  330.         'description': 'Military HH:MM\n(see wxTimeCtrl)' },
  331.     'USSOCIALSEC': {
  332.         'mask': '###-##-####',
  333.         'formatcodes': 'F',
  334.         'validRegex': '\\d{3}-\\d{2}-\\d{4}',
  335.         'description': 'Social Sec#' },
  336.     'CREDITCARD': {
  337.         'mask': '####-####-####-####',
  338.         'formatcodes': 'F',
  339.         'validRegex': '\\d{4}-\\d{4}-\\d{4}-\\d{4}',
  340.         'description': 'Credit Card' },
  341.     'EXPDATEMMYY': {
  342.         'mask': '##/##',
  343.         'formatcodes': 'F',
  344.         'validRegex': '^' + months + '/\\d\\d',
  345.         'description': 'Expiration MM/YY' },
  346.     'USZIP': {
  347.         'mask': '#####',
  348.         'formatcodes': 'F',
  349.         'validRegex': '^\\d{5}',
  350.         'description': 'US 5-digit zip code' },
  351.     'USZIPPLUS4': {
  352.         'mask': '#####-####',
  353.         'formatcodes': 'F',
  354.         'validRegex': '\\d{5}-(\\s{4}|\\d{4})',
  355.         'description': 'US zip+4 code' },
  356.     'PERCENT': {
  357.         'mask': '0.##',
  358.         'formatcodes': 'F',
  359.         'validRegex': '^0.\\d\\d',
  360.         'description': 'Percentage' },
  361.     'AGE': {
  362.         'mask': '###',
  363.         'formatcodes': 'F',
  364.         'validRegex': '^[1-9]{1}  |[1-9][0-9] |1[0|1|2][0-9]',
  365.         'description': 'Age' },
  366.     'EMAIL': {
  367.         'mask': 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
  368.         'excludeChars': ' \\/*&%$#!+=\'"',
  369.         'formatcodes': 'F>',
  370.         'validRegex': '^\\w+([\\-\\.]\\w+)*@((([a-zA-Z0-9]+(\\-[a-zA-Z0-9]+)*\\.)+)[a-zA-Z]{2,4}|\\[(\\d|\\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))(\\.(\\d|\\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))){3}\\]) *$',
  371.         'description': 'Email address' },
  372.     'IPADDR': {
  373.         'mask': '###.###.###.###',
  374.         'formatcodes': 'F_Sr',
  375.         'validRegex': '(  \\d| \\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))(\\.(  \\d| \\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))){3}',
  376.         'description': 'IP Address\n(see wxIpAddrCtrl)' } }
  377. autoformats = []
  378. for key, value in masktags.items():
  379.     autoformats.append((key, value['description']))
  380.  
  381. autoformats.sort()
  382.  
  383. class Field:
  384.     valid_params = {
  385.         'index': None,
  386.         'mask': '',
  387.         'extent': (),
  388.         'formatcodes': '',
  389.         'fillChar': ' ',
  390.         'groupChar': ',',
  391.         'decimalChar': '.',
  392.         'shiftDecimalChar': '>',
  393.         'useParensForNegatives': False,
  394.         'defaultValue': '',
  395.         'excludeChars': '',
  396.         'includeChars': '',
  397.         'validRegex': '',
  398.         'validRange': (),
  399.         'choices': [],
  400.         'choiceRequired': False,
  401.         'compareNoCase': False,
  402.         'autoSelect': False,
  403.         'validFunc': None,
  404.         'validRequired': False,
  405.         'emptyInvalid': False,
  406.         'description': '' }
  407.     propagating_params = ('fillChar', 'groupChar', 'decimalChar', 'useParensForNegatives', 'compareNoCase', 'emptyInvalid', 'validRequired')
  408.     
  409.     def __init__(self, **kwargs):
  410.         for key in kwargs.keys():
  411.             if key not in Field.valid_params.keys():
  412.                 raise TypeError('invalid parameter "%s"' % key)
  413.                 continue
  414.         
  415.         for key, value in Field.valid_params.items():
  416.             setattr(self, '_' + key, copy.copy(value))
  417.             if not kwargs.has_key(key):
  418.                 kwargs[key] = copy.copy(value)
  419.                 continue
  420.         
  421.         self._autoCompleteIndex = -1
  422.         self._SetParameters(**kwargs)
  423.         self._ValidateParameters(**kwargs)
  424.  
  425.     
  426.     def _SetParameters(self, **kwargs):
  427.         dbg(suspend = 1)
  428.         dbg('maskededit.Field::_SetParameters', indent = 1)
  429.         for key in kwargs.keys():
  430.             if key not in Field.valid_params.keys():
  431.                 dbg(indent = 0, suspend = 0)
  432.                 raise AttributeError('invalid keyword argument "%s"' % key)
  433.                 continue
  434.         
  435.         if self._index is not None:
  436.             dbg('field index:', self._index)
  437.         
  438.         dbg('parameters:', indent = 1)
  439.         for key, value in kwargs.items():
  440.             dbg('%s:' % key, value)
  441.         
  442.         dbg(indent = 0)
  443.         old_fillChar = self._fillChar
  444.         for key in Field.valid_params.keys():
  445.             if kwargs.has_key(key):
  446.                 setattr(self, '_' + key, kwargs[key])
  447.                 continue
  448.         
  449.         if kwargs.has_key('formatcodes'):
  450.             self._forceupper = '!' in self._formatcodes
  451.             self._forcelower = '^' in self._formatcodes
  452.             self._groupdigits = ',' in self._formatcodes
  453.             self._okSpaces = '_' in self._formatcodes
  454.             self._padZero = '0' in self._formatcodes
  455.             self._autofit = 'F' in self._formatcodes
  456.             self._insertRight = 'r' in self._formatcodes
  457.             self._allowInsert = '>' in self._formatcodes
  458.             if not 'R' in self._formatcodes:
  459.                 pass
  460.             self._alignRight = 'r' in self._formatcodes
  461.             self._moveOnFieldFull = not ('<' in self._formatcodes)
  462.             self._selectOnFieldEntry = 'S' in self._formatcodes
  463.             if kwargs.has_key('groupChar'):
  464.                 self._groupChar = kwargs['groupChar']
  465.             
  466.             if kwargs.has_key('decimalChar'):
  467.                 self._decimalChar = kwargs['decimalChar']
  468.             
  469.             if kwargs.has_key('shiftDecimalChar'):
  470.                 self._shiftDecimalChar = kwargs['shiftDecimalChar']
  471.             
  472.         
  473.         if kwargs.has_key('formatcodes') or kwargs.has_key('validRegex'):
  474.             if 'V' in self._formatcodes:
  475.                 pass
  476.             self._regexMask = self._validRegex
  477.         
  478.         if kwargs.has_key('fillChar'):
  479.             self._old_fillChar = old_fillChar
  480.         
  481.         if kwargs.has_key('mask') or kwargs.has_key('validRegex'):
  482.             self._isInt = isInteger(self._mask)
  483.             dbg('isInt?', self._isInt, 'self._mask:"%s"' % self._mask)
  484.         
  485.         dbg(indent = 0, suspend = 0)
  486.  
  487.     
  488.     def _ValidateParameters(self, **kwargs):
  489.         dbg(suspend = 1)
  490.         dbg('maskededit.Field::_ValidateParameters', indent = 1)
  491.         if self._index is not None:
  492.             dbg('field index:', self._index)
  493.         
  494.         if self._groupdigits and self._groupChar == self._decimalChar:
  495.             dbg(indent = 0, suspend = 0)
  496.             raise AttributeError("groupChar '%s' cannot be the same as decimalChar '%s'" % (self._groupChar, self._decimalChar))
  497.         
  498.         if kwargs.has_key('validRegex'):
  499.             if self._validRegex:
  500.                 
  501.                 try:
  502.                     if self._compareNoCase:
  503.                         self._filter = re.compile(self._validRegex, re.IGNORECASE)
  504.                     else:
  505.                         self._filter = re.compile(self._validRegex)
  506.                 dbg(indent = 0, suspend = 0)
  507.                 raise TypeError('%s: validRegex "%s" not a legal regular expression' % (str(self._index), self._validRegex))
  508.  
  509.             else:
  510.                 self._filter = None
  511.         
  512.         if kwargs.has_key('validRange'):
  513.             self._hasRange = False
  514.             self._rangeHigh = 0
  515.             self._rangeLow = 0
  516.             if self._validRange:
  517.                 if type(self._validRange) != types.TupleType and len(self._validRange) != 2 or self._validRange[0] > self._validRange[1]:
  518.                     dbg(indent = 0, suspend = 0)
  519.                     raise TypeError('%s: validRange %s parameter must be tuple of form (a,b) where a <= b' % (str(self._index), repr(self._validRange)))
  520.                 
  521.                 self._hasRange = True
  522.                 self._rangeLow = self._validRange[0]
  523.                 self._rangeHigh = self._validRange[1]
  524.             
  525.         
  526.         if kwargs.has_key('choices') and len(self._choices) and len(self._choices[0]) != len(self._mask):
  527.             self._hasList = False
  528.             if self._choices and type(self._choices) not in (types.TupleType, types.ListType):
  529.                 dbg(indent = 0, suspend = 0)
  530.                 raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
  531.             elif len(self._choices) > 0:
  532.                 for choice in self._choices:
  533.                     if type(choice) not in (types.StringType, types.UnicodeType):
  534.                         dbg(indent = 0, suspend = 0)
  535.                         raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
  536.                         continue
  537.                 
  538.                 length = len(self._mask)
  539.                 dbg('len(%s)' % self._mask, length, 'len(self._choices):', len(self._choices), 'length:', length, 'self._alignRight?', self._alignRight)
  540.                 if hasattr(self, '_template'):
  541.                     for choice in self._choices:
  542.                         if self.IsEmpty(choice) and not (self._validRequired):
  543.                             continue
  544.                         
  545.                         if not self.IsValid(choice):
  546.                             dbg(indent = 0, suspend = 0)
  547.                             raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self._index), choice))
  548.                             continue
  549.                     
  550.                 
  551.                 self._hasList = True
  552.             
  553.         
  554.         if kwargs.has_key('autoSelect') and kwargs['autoSelect']:
  555.             if not (self._hasList):
  556.                 dbg('no list to auto complete; ignoring "autoSelect=True"')
  557.                 self._autoSelect = False
  558.             
  559.         
  560.         self._valid = True
  561.         dbg(indent = 0, suspend = 0)
  562.  
  563.     
  564.     def _GetParameter(self, paramname):
  565.         if Field.valid_params.has_key(paramname):
  566.             return getattr(self, '_' + paramname)
  567.         else:
  568.             TypeError('Field._GetParameter: invalid parameter "%s"' % key)
  569.  
  570.     
  571.     def IsEmpty(self, slice):
  572.         dbg('Field::IsEmpty("%s")' % slice, indent = 1)
  573.         if not hasattr(self, '_template'):
  574.             dbg(indent = 0)
  575.             raise AttributeError('_template')
  576.         
  577.         dbg('self._template: "%s"' % self._template)
  578.         dbg('self._defaultValue: "%s"' % str(self._defaultValue))
  579.         if slice == self._template and not (self._defaultValue):
  580.             dbg(indent = 0)
  581.             return True
  582.         elif slice == self._template:
  583.             empty = True
  584.             for pos in range(len(self._template)):
  585.                 if slice[pos] not in (' ', self._fillChar):
  586.                     empty = False
  587.                     break
  588.                     continue
  589.             
  590.             dbg('IsEmpty? %(empty)d (do all mask chars == fillChar?)' % locals(), indent = 0)
  591.             return empty
  592.         else:
  593.             dbg("IsEmpty? 0 (slice doesn't match template)", indent = 0)
  594.             return False
  595.  
  596.     
  597.     def IsValid(self, slice):
  598.         dbg(suspend = 1)
  599.         dbg('Field[%s]::IsValid("%s")' % (str(self._index), slice), indent = 1)
  600.         valid = True
  601.         if self.IsEmpty(slice):
  602.             dbg(indent = 0, suspend = 0)
  603.             if self._emptyInvalid:
  604.                 return False
  605.             else:
  606.                 return True
  607.         elif self._hasList and self._choiceRequired:
  608.             dbg('(member of list required)')
  609.             if self._fillChar != ' ':
  610.                 slice = slice.replace(self._fillChar, ' ')
  611.                 dbg('updated slice:"%s"' % slice)
  612.             
  613.             compareStr = slice.strip()
  614.             if self._compareNoCase:
  615.                 compareStr = compareStr.lower()
  616.             
  617.             valid = compareStr in self._compareChoices
  618.         elif self._hasRange and not self.IsEmpty(slice):
  619.             dbg('validating against range')
  620.             
  621.             try:
  622.                 valid = None if float(slice) <= float(slice) else float(slice) <= self._rangeHigh
  623.             valid = False
  624.  
  625.         elif self._validRegex and self._filter:
  626.             dbg('validating against regex')
  627.             valid = re.match(self._filter, slice) is not None
  628.         
  629.         if valid and self._validFunc:
  630.             dbg('validating against supplied function')
  631.             valid = self._validFunc(slice)
  632.         
  633.         dbg('valid?', valid, indent = 0, suspend = 0)
  634.         return valid
  635.  
  636.     
  637.     def _AdjustField(self, slice):
  638.         dbg('Field::_AdjustField("%s")' % slice, indent = 1)
  639.         length = len(self._mask)
  640.         if self._isInt:
  641.             if self._useParensForNegatives:
  642.                 signpos = slice.find('(')
  643.                 right_signpos = slice.find(')')
  644.                 intStr = slice.replace('(', '').replace(')', '')
  645.             else:
  646.                 signpos = slice.find('-')
  647.                 intStr = slice.replace('-', '')
  648.                 right_signpos = -1
  649.             intStr = intStr.replace(' ', '')
  650.             intStr = string.replace(intStr, self._fillChar, '')
  651.             intStr = string.replace(intStr, '-', '')
  652.             intStr = string.replace(intStr, self._groupChar, '')
  653.             (start, end) = self._extent
  654.             field_len = end - start
  655.             if not (self._padZero) and len(intStr) != field_len and intStr.strip():
  656.                 intStr = str(long(intStr))
  657.             
  658.             if self._groupdigits:
  659.                 new = ''
  660.                 cnt = 1
  661.                 for i in range(len(intStr) - 1, -1, -1):
  662.                     new = intStr[i] + new
  663.                     if cnt % 3 == 0:
  664.                         new = self._groupChar + new
  665.                     
  666.                     cnt += 1
  667.                 
  668.                 if new and new[0] == self._groupChar:
  669.                     new = new[1:]
  670.                 
  671.                 if len(new) <= length:
  672.                     intStr = new
  673.                 
  674.             
  675.             dbg('padzero?', self._padZero)
  676.             dbg('len(intStr):', len(intStr), 'field length:', length)
  677.             if self._padZero and len(intStr) < length:
  678.                 intStr = '0' * (length - len(intStr)) + intStr
  679.                 if signpos != -1:
  680.                     if self._useParensForNegatives:
  681.                         intStr = '(' + intStr[1:]
  682.                         if right_signpos != -1:
  683.                             intStr += ')'
  684.                         
  685.                     else:
  686.                         intStr = '-' + intStr[1:]
  687.                 
  688.             elif signpos != -1 and slice[0:signpos].strip() == '':
  689.                 if self._useParensForNegatives:
  690.                     intStr = '(' + intStr
  691.                     if right_signpos != -1:
  692.                         intStr += ')'
  693.                     
  694.                 else:
  695.                     intStr = '-' + intStr
  696.             elif right_signpos != -1:
  697.                 intStr += ')'
  698.             
  699.             slice = intStr
  700.         
  701.         slice = slice.strip()
  702.         if self._alignRight:
  703.             slice = slice.rjust(length)
  704.         else:
  705.             slice = slice.ljust(length)
  706.         if self._fillChar != ' ':
  707.             slice = slice.replace(' ', self._fillChar)
  708.         
  709.         dbg('adjusted slice: "%s"' % slice, indent = 0)
  710.         return slice
  711.  
  712.  
  713.  
  714. class wxMaskedEditMixin:
  715.     valid_ctrl_params = {
  716.         'mask': 'XXXXXXXXXXXXX',
  717.         'autoformat': '',
  718.         'fields': { },
  719.         'datestyle': 'MDY',
  720.         'autoCompleteKeycodes': [],
  721.         'useFixedWidthFont': True,
  722.         'retainFieldValidation': False,
  723.         'emptyBackgroundColour': 'White',
  724.         'validBackgroundColour': 'White',
  725.         'invalidBackgroundColour': 'Yellow',
  726.         'foregroundColour': 'Black',
  727.         'signedForegroundColour': 'Red',
  728.         'demo': False }
  729.     
  730.     def __init__(self, name = 'wxMaskedEdit', **kwargs):
  731.         self.name = name
  732.         if not hasattr(self, 'controlInitialized'):
  733.             self.controlInitialized = False
  734.         
  735.         self.modified = False
  736.         self._previous_mask = None
  737.         for key in kwargs.keys():
  738.             if key.replace('Color', 'Colour') not in wxMaskedEditMixin.valid_ctrl_params.keys() + Field.valid_params.keys():
  739.                 raise TypeError('%s: invalid parameter "%s"' % (name, key))
  740.                 continue
  741.         
  742.         self._keyhandlers = {
  743.             WXK_BACK: self._OnErase,
  744.             WXK_LEFT: self._OnArrow,
  745.             WXK_RIGHT: self._OnArrow,
  746.             WXK_UP: self._OnAutoCompleteField,
  747.             WXK_DOWN: self._OnAutoCompleteField,
  748.             WXK_TAB: self._OnChangeField,
  749.             WXK_HOME: self._OnHome,
  750.             WXK_END: self._OnEnd,
  751.             WXK_RETURN: self._OnReturn,
  752.             WXK_PRIOR: self._OnAutoCompleteField,
  753.             WXK_NEXT: self._OnAutoCompleteField,
  754.             WXK_DELETE: self._OnErase,
  755.             WXK_CTRL_A: self._OnCtrl_A,
  756.             WXK_CTRL_C: self._OnCtrl_C,
  757.             WXK_CTRL_S: self._OnCtrl_S,
  758.             WXK_CTRL_V: self._OnCtrl_V,
  759.             WXK_CTRL_X: self._OnCtrl_X,
  760.             WXK_CTRL_Z: self._OnCtrl_Z }
  761.         self._nav = list(nav)
  762.         self._control = list(control)
  763.         self.maskchardict = {
  764.             '#': string.digits,
  765.             'A': string.uppercase,
  766.             'a': string.lowercase,
  767.             'X': string.letters + string.punctuation + string.digits,
  768.             'C': string.letters,
  769.             'N': string.letters + string.digits,
  770.             '&': string.punctuation }
  771.         self._ignoreChange = False
  772.         self._curValue = None
  773.         self._prevValue = None
  774.         self._valid = True
  775.         for key, value in wxMaskedEditMixin.valid_ctrl_params.items():
  776.             setattr(self, '_' + key, copy.copy(value))
  777.             if not kwargs.has_key(key):
  778.                 kwargs[key] = copy.copy(value)
  779.                 continue
  780.         
  781.         self._ctrl_constraints = self._fields[-1] = Field(index = -1)
  782.         self.SetCtrlParameters(**kwargs)
  783.  
  784.     
  785.     def SetCtrlParameters(self, **kwargs):
  786.         dbg(suspend = 1)
  787.         dbg('wxMaskedEditMixin::SetCtrlParameters', indent = 1)
  788.         constraint_kwargs = { }
  789.         ctrl_kwargs = { }
  790.         for key, value in kwargs.items():
  791.             key = key.replace('Color', 'Colour')
  792.             if key not in wxMaskedEditMixin.valid_ctrl_params.keys() + Field.valid_params.keys():
  793.                 dbg(indent = 0, suspend = 0)
  794.                 raise TypeError('Invalid keyword argument "%s" for control "%s"' % (key, self.name))
  795.                 continue
  796.             if key in Field.valid_params.keys():
  797.                 constraint_kwargs[key] = value
  798.                 continue
  799.             ctrl_kwargs[key] = value
  800.         
  801.         mask = None
  802.         reset_args = { }
  803.         if ctrl_kwargs.has_key('autoformat'):
  804.             autoformat = ctrl_kwargs['autoformat']
  805.         else:
  806.             autoformat = None
  807.         if autoformat != self._autoformat and autoformat in masktags.keys():
  808.             dbg('autoformat:', autoformat)
  809.             self._autoformat = autoformat
  810.             mask = masktags[self._autoformat]['mask']
  811.             for param, value in masktags[self._autoformat].items():
  812.                 if param == 'mask':
  813.                     continue
  814.                 
  815.                 constraint_kwargs[param] = value
  816.             
  817.         elif autoformat and not (autoformat in masktags.keys()):
  818.             raise AttributeError('invalid value for autoformat parameter: %s' % repr(autoformat))
  819.         else:
  820.             dbg('autoformat not selected')
  821.             if kwargs.has_key('mask'):
  822.                 mask = kwargs['mask']
  823.                 dbg('mask:', mask)
  824.             
  825.         if mask is None:
  826.             dbg('preserving previous mask')
  827.             mask = self._previous_mask
  828.         else:
  829.             dbg('mask (re)set')
  830.             reset_args['reset_mask'] = mask
  831.             constraint_kwargs['mask'] = mask
  832.             self._fields = {
  833.                 -1: self._ctrl_constraints }
  834.         if ctrl_kwargs.has_key('fields'):
  835.             fields = ctrl_kwargs['fields']
  836.             if type(fields) in (types.ListType, types.TupleType):
  837.                 for i in range(len(fields)):
  838.                     field = fields[i]
  839.                     if not isinstance(field, Field):
  840.                         dbg(indent = 0, suspend = 0)
  841.                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
  842.                     
  843.                     self._fields[i] = field
  844.                 
  845.             elif type(fields) == types.DictionaryType:
  846.                 for index, field in fields.items():
  847.                     if not isinstance(field, Field):
  848.                         dbg(indent = 0, suspend = 0)
  849.                         raise AttributeError('invalid type for field parameter: %s' % repr(field))
  850.                     
  851.                     self._fields[index] = field
  852.                 
  853.             else:
  854.                 dbg(indent = 0, suspend = 0)
  855.                 raise AttributeError('fields parameter must be a list or dictionary; not %s' % repr(fields))
  856.         
  857.         for key in wxMaskedEditMixin.valid_ctrl_params.keys():
  858.             if key in ('mask', 'fields'):
  859.                 continue
  860.             
  861.             if ctrl_kwargs.has_key(key):
  862.                 setattr(self, '_' + key, ctrl_kwargs[key])
  863.                 continue
  864.         
  865.         for key in ('emptyBackgroundColour', 'invalidBackgroundColour', 'validBackgroundColour', 'foregroundColour', 'signedForegroundColour'):
  866.             if ctrl_kwargs.has_key(key):
  867.                 if type(ctrl_kwargs[key]) in (types.StringType, types.UnicodeType):
  868.                     c = wxNamedColor(ctrl_kwargs[key])
  869.                     if c.Get() == (-1, -1, -1):
  870.                         raise TypeError('%s not a legal color specification for %s' % (repr(ctrl_kwargs[key]), key))
  871.                     else:
  872.                         setattr(self, '_' + key, c)
  873.                         c._name = ctrl_kwargs[key]
  874.                 elif type(ctrl_kwargs[key]) != type(wxBLACK):
  875.                     raise TypeError('%s not a legal color specification for %s' % (repr(ctrl_kwargs[key]), key))
  876.                 
  877.             type(ctrl_kwargs[key]) in (types.StringType, types.UnicodeType)
  878.         
  879.         dbg('self._retainFieldValidation:', self._retainFieldValidation)
  880.         if not (self._retainFieldValidation):
  881.             for arg in Field.propagating_params:
  882.                 if kwargs.has_key(arg):
  883.                     pass
  884.                 reset_args[arg] = kwargs[arg] != getattr(self._ctrl_constraints, '_' + arg)
  885.             
  886.         
  887.         self._ctrl_constraints._SetParameters(**constraint_kwargs)
  888.         self._configure(mask, **reset_args)
  889.         self._ctrl_constraints._ValidateParameters(**constraint_kwargs)
  890.         self._validateChoices()
  891.         self._autofit = self._ctrl_constraints._autofit
  892.         self._isNeg = False
  893.         if 'D' in self._ctrl_constraints._formatcodes:
  894.             pass
  895.         self._isDate = isDateType(mask)
  896.         if 'T' in self._ctrl_constraints._formatcodes:
  897.             pass
  898.         self._isTime = isTimeType(mask)
  899.         if self._isDate:
  900.             if self._mask.find('CCC') != -1:
  901.                 self._dateExtent = 11
  902.             else:
  903.                 self._dateExtent = 10
  904.             if len(self._mask) > 8:
  905.                 pass
  906.             self._4digityear = self._mask[9] == '#'
  907.         
  908.         if self._isDate and self._autoformat:
  909.             if self._autoformat.find('MDDY') != -1:
  910.                 self._datestyle = 'MDY'
  911.             elif self._autoformat.find('YMMD') != -1:
  912.                 self._datestyle = 'YMD'
  913.             elif self._autoformat.find('YMMMD') != -1:
  914.                 self._datestyle = 'YMD'
  915.             elif self._autoformat.find('DMMY') != -1:
  916.                 self._datestyle = 'DMY'
  917.             elif self._autoformat.find('DMMMY') != -1:
  918.                 self._datestyle = 'DMY'
  919.             
  920.         
  921.         if self.controlInitialized:
  922.             if kwargs.has_key('useFixedWidthFont'):
  923.                 self._setFont()
  924.             
  925.             if reset_args.has_key('reset_mask'):
  926.                 dbg('reset mask')
  927.                 curvalue = self._GetValue()
  928.                 if curvalue.strip():
  929.                     
  930.                     try:
  931.                         dbg('attempting to _SetInitialValue(%s)' % self._GetValue())
  932.                         self._SetInitialValue(self._GetValue())
  933.                     except Exception:
  934.                         e = None
  935.                         dbg('exception caught:', e)
  936.                         dbg("current value doesn't work; attempting to reset to template")
  937.                         self._SetInitialValue()
  938.                     except:
  939.                         None<EXCEPTION MATCH>Exception
  940.                     
  941.  
  942.                 None<EXCEPTION MATCH>Exception
  943.                 dbg('attempting to _SetInitialValue() with template')
  944.                 self._SetInitialValue()
  945.             elif kwargs.has_key('useParensForNegatives'):
  946.                 newvalue = self._getSignedValue()[0]
  947.                 if newvalue is not None:
  948.                     if len(newvalue) < len(self._mask):
  949.                         newvalue += ' '
  950.                     elif len(newvalue) > len(self._mask):
  951.                         if newvalue[-1] in (' ', ')'):
  952.                             newvalue = newvalue[:-1]
  953.                         
  954.                     
  955.                     dbg('reconfiguring value for parens:"%s"' % newvalue)
  956.                     self._SetValue(newvalue)
  957.                     if self._prevValue != newvalue:
  958.                         self._prevValue = newvalue
  959.                     
  960.                 
  961.             
  962.             if self._autofit:
  963.                 dbg('setting client size to:', self._CalcSize())
  964.                 self.SetClientSize(self._CalcSize())
  965.             
  966.             self._applyFormatting()
  967.         
  968.         dbg(indent = 0, suspend = 0)
  969.  
  970.     
  971.     def SetMaskParameters(self, **kwargs):
  972.         return self.SetCtrlParameters(**kwargs)
  973.  
  974.     
  975.     def GetCtrlParameter(self, paramname):
  976.         if wxMaskedEditMixin.valid_ctrl_params.has_key(paramname.replace('Color', 'Colour')):
  977.             return getattr(self, '_' + paramname.replace('Color', 'Colour'))
  978.         elif Field.valid_params.has_key(paramname):
  979.             return self._ctrl_constraints._GetParameter(paramname)
  980.         else:
  981.             TypeError('"%s".GetCtrlParameter: invalid parameter "%s"' % (self.name, paramname))
  982.  
  983.     
  984.     def GetMaskParameter(self, paramname):
  985.         return self.GetCtrlParameter(paramname)
  986.  
  987.     for param in valid_ctrl_params.keys() + Field.valid_params.keys():
  988.         propname = param[0].upper() + param[1:]
  989.         exec 'def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param)
  990.         exec 'def Get%s(self): return self.GetCtrlParameter("%s")' % (propname, param)
  991.         if param.find('Colour') != -1:
  992.             propname.replace('Colour', 'Color')
  993.             exec 'def Set%s(self, value): self.SetCtrlParameters(%s=value)' % (propname, param)
  994.             exec 'def Get%s(self): return self.GetCtrlParameter("%s")' % (propname, param)
  995.             continue
  996.     
  997.     
  998.     def SetFieldParameters(self, field_index, **kwargs):
  999.         if field_index not in self._field_indices:
  1000.             raise IndexError('%s is not a valid field for control "%s".' % (str(field_index), self.name))
  1001.         
  1002.         self._fields[field_index]._SetParameters(**kwargs)
  1003.         self._configure(self._previous_mask)
  1004.         self._fields[field_index]._ValidateParameters(**kwargs)
  1005.         if self.controlInitialized:
  1006.             if kwargs.has_key('fillChar') or kwargs.has_key('defaultValue'):
  1007.                 self._SetInitialValue()
  1008.                 if self._autofit:
  1009.                     self.SetClientSize(self._CalcSize())
  1010.                 
  1011.             
  1012.             self._applyFormatting()
  1013.         
  1014.  
  1015.     
  1016.     def GetFieldParameter(self, field_index, paramname):
  1017.         if field_index not in self._field_indices:
  1018.             raise IndexError('%s is not a valid field for control "%s".' % (str(field_index), self.name))
  1019.         elif Field.valid_params.has_key(paramname):
  1020.             return self._fields[field_index]._GetParameter(paramname)
  1021.         else:
  1022.             TypeError('"%s".GetFieldParameter: invalid parameter "%s"' % (self.name, paramname))
  1023.  
  1024.     
  1025.     def _SetKeycodeHandler(self, keycode, func):
  1026.         self._keyhandlers[keycode] = func
  1027.  
  1028.     
  1029.     def _SetKeyHandler(self, char, func):
  1030.         self._SetKeycodeHandler(ord(char), func)
  1031.  
  1032.     
  1033.     def _AddNavKeycode(self, keycode, handler = None):
  1034.         self._nav.append(keycode)
  1035.         if handler:
  1036.             self._keyhandlers[keycode] = handler
  1037.         
  1038.  
  1039.     
  1040.     def _AddNavKey(self, char, handler = None):
  1041.         self._AddNavKeycode(ord(char), handler)
  1042.  
  1043.     
  1044.     def _GetNavKeycodes(self):
  1045.         return self._nav
  1046.  
  1047.     
  1048.     def _SetNavKeycodes(self, keycode_func_tuples):
  1049.         self._nav = []
  1050.         for keycode, func in keycode_func_tuples:
  1051.             self._nav.append(keycode)
  1052.             if func:
  1053.                 self._keyhandlers[keycode] = func
  1054.                 continue
  1055.         
  1056.  
  1057.     
  1058.     def _processMask(self, mask):
  1059.         dbg('_processMask: mask', mask, indent = 1)
  1060.         rex = re.compile('([' + string.join(maskchars, '') + '])\\{(\\d+)\\}')
  1061.         s = mask
  1062.         match = rex.search(s)
  1063.         while match:
  1064.             maskchr = s[match.start(1):match.end(1)]
  1065.             repcount = int(s[match.start(2):match.end(2)])
  1066.             replacement = string.join(maskchr * repcount, '')
  1067.             s = s[:match.start(1)] + replacement + s[match.end(2) + 1:]
  1068.             match = rex.search(s)
  1069.         self._decimalChar = self._ctrl_constraints._decimalChar
  1070.         self._shiftDecimalChar = self._ctrl_constraints._shiftDecimalChar
  1071.         if isFloatingPoint(s):
  1072.             pass
  1073.         self._isFloat = not (self._ctrl_constraints._validRegex)
  1074.         if isInteger(s):
  1075.             pass
  1076.         self._isInt = not (self._ctrl_constraints._validRegex)
  1077.         if not '-' in self._ctrl_constraints._formatcodes and self._isFloat:
  1078.             pass
  1079.         self._signOk = self._isInt
  1080.         self._useParens = self._ctrl_constraints._useParensForNegatives
  1081.         self._isNeg = False
  1082.         if self._signOk and s[0] != ' ':
  1083.             s = ' ' + s
  1084.             if self._ctrl_constraints._defaultValue and self._ctrl_constraints._defaultValue[0] != ' ':
  1085.                 self._ctrl_constraints._defaultValue = ' ' + self._ctrl_constraints._defaultValue
  1086.             
  1087.             self._signpos = 0
  1088.             if self._useParens:
  1089.                 s += ' '
  1090.                 self._ctrl_constraints._defaultValue += ' '
  1091.             
  1092.         
  1093.         ismasked = { }
  1094.         i = 0
  1095.         while i < len(s):
  1096.             if s[i] == '\\':
  1097.                 ismasked[i] = False
  1098.                 if i + 1 < len(s):
  1099.                     s = s[:i] + s[i + 1:]
  1100.                     if i + 2 < len(s) and s[i + 1] == '\\':
  1101.                         s = s[:i] + s[i + 1:]
  1102.                     
  1103.                 
  1104.             else:
  1105.                 ismasked[i] = s[i] in maskchars
  1106.             i += 1
  1107.         dbg('new mask: "%s"' % s, indent = 0)
  1108.         return (s, ismasked)
  1109.  
  1110.     
  1111.     def _calcFieldExtents(self):
  1112.         self._lookupField = { }
  1113.         if self._mask:
  1114.             self.maskdict = { }
  1115.             for charnum in range(len(self._mask)):
  1116.                 self.maskdict[charnum] = self._mask[charnum:charnum + 1]
  1117.             
  1118.             if self._signOk:
  1119.                 start = 1
  1120.             else:
  1121.                 start = 0
  1122.             if self._isFloat:
  1123.                 if not self._fields.has_key(0):
  1124.                     self._fields[0] = Field()
  1125.                 
  1126.                 if not self._fields.has_key(1):
  1127.                     self._fields[1] = Field()
  1128.                 
  1129.                 self._decimalpos = string.find(self._mask, '.')
  1130.                 dbg('decimal pos =', self._decimalpos)
  1131.                 formatcodes = self._fields[0]._GetParameter('formatcodes')
  1132.                 if 'R' not in formatcodes:
  1133.                     formatcodes += 'R'
  1134.                 
  1135.                 self._fields[0]._SetParameters(index = 0, extent = (start, self._decimalpos), mask = self._mask[start:self._decimalpos], formatcodes = formatcodes)
  1136.                 end = len(self._mask)
  1137.                 if self._signOk and self._useParens:
  1138.                     end -= 1
  1139.                 
  1140.                 self._fields[1]._SetParameters(index = 1, extent = (self._decimalpos + 1, end), mask = self._mask[self._decimalpos + 1:end])
  1141.                 for i in range(self._decimalpos + 1):
  1142.                     self._lookupField[i] = 0
  1143.                 
  1144.                 for i in range(self._decimalpos + 1, len(self._mask) + 1):
  1145.                     self._lookupField[i] = 1
  1146.                 
  1147.             elif self._isInt:
  1148.                 if not self._fields.has_key(0):
  1149.                     self._fields[0] = Field(index = 0)
  1150.                 
  1151.                 end = len(self._mask)
  1152.                 if self._signOk and self._useParens:
  1153.                     end -= 1
  1154.                 
  1155.                 self._fields[0]._SetParameters(index = 0, extent = (start, end), mask = self._mask[start:end])
  1156.                 for i in range(len(self._mask) + 1):
  1157.                     self._lookupField[i] = 0
  1158.                 
  1159.             else:
  1160.                 field_index = 0
  1161.                 pos = 0
  1162.                 i = self._findNextEntry(pos, adjustInsert = False)
  1163.                 if i < len(self._mask):
  1164.                     for j in range(pos, i + 1):
  1165.                         self._lookupField[j] = field_index
  1166.                     
  1167.                     pos = i
  1168.                 
  1169.                 while i <= len(self._mask):
  1170.                     if self._isMaskChar(i):
  1171.                         edit_start = i
  1172.                         while i < len(self._mask) and self._isMaskChar(i):
  1173.                             self._lookupField[i] = field_index
  1174.                             i += 1
  1175.                         edit_end = i
  1176.                         self._lookupField[i] = field_index
  1177.                         if not self._fields.has_key(field_index):
  1178.                             kwargs = Field.valid_params.copy()
  1179.                             kwargs['index'] = field_index
  1180.                             kwargs['extent'] = (edit_start, edit_end)
  1181.                             kwargs['mask'] = self._mask[edit_start:edit_end]
  1182.                             self._fields[field_index] = Field(**kwargs)
  1183.                         else:
  1184.                             self._fields[field_index]._SetParameters(index = field_index, extent = (edit_start, edit_end), mask = self._mask[edit_start:edit_end])
  1185.                     
  1186.                     pos = i
  1187.                     i = self._findNextEntry(pos, adjustInsert = False)
  1188.                     if i > pos:
  1189.                         for j in range(pos, i + 1):
  1190.                             self._lookupField[j] = field_index
  1191.                         
  1192.                     
  1193.                     if i >= len(self._mask):
  1194.                         break
  1195.                         continue
  1196.                     field_index += 1
  1197.         
  1198.         indices = self._fields.keys()
  1199.         indices.sort()
  1200.         self._field_indices = indices[1:]
  1201.         for index in self._fields.keys():
  1202.             if index not in [
  1203.                 -1] + self._lookupField.values():
  1204.                 raise IndexError('field %d is not a valid field for mask "%s"' % (index, self._mask))
  1205.                 continue
  1206.         
  1207.  
  1208.     
  1209.     def _calcTemplate(self, reset_fillchar, reset_default):
  1210.         default_set = False
  1211.         if self._ctrl_constraints._defaultValue:
  1212.             default_set = True
  1213.         else:
  1214.             for field in self._fields.values():
  1215.                 if field._defaultValue and not reset_default:
  1216.                     default_set = True
  1217.                     continue
  1218.             
  1219.         dbg('default set?', default_set)
  1220.         if self.controlInitialized:
  1221.             curvalue = list(self._GetValue())
  1222.         else:
  1223.             curvalue = None
  1224.         if hasattr(self, '_fillChar'):
  1225.             old_fillchars = self._fillChar
  1226.         else:
  1227.             old_fillchars = None
  1228.         if hasattr(self, '_template'):
  1229.             old_template = self._template
  1230.         else:
  1231.             old_template = None
  1232.         self._template = ''
  1233.         self._fillChar = { }
  1234.         reset_value = False
  1235.         for field in self._fields.values():
  1236.             field._template = ''
  1237.         
  1238.         for pos in range(len(self._mask)):
  1239.             field = self._FindField(pos)
  1240.             (start, end) = field._extent
  1241.             if pos == 0 and self._signOk:
  1242.                 self._template = ' '
  1243.                 continue
  1244.             if self._isFloat and pos == self._decimalpos:
  1245.                 self._template += self._decimalChar
  1246.                 continue
  1247.             self
  1248.             if self._isMaskChar(pos):
  1249.                 if field._fillChar != self._ctrl_constraints._fillChar and not reset_fillchar:
  1250.                     fillChar = field._fillChar
  1251.                 else:
  1252.                     fillChar = self._ctrl_constraints._fillChar
  1253.                 self._fillChar[pos] = fillChar
  1254.                 if self.controlInitialized and old_fillchars and old_fillchars.has_key(pos) and curvalue:
  1255.                     if curvalue[pos] == old_fillchars[pos] and old_fillchars[pos] != fillChar:
  1256.                         reset_value = True
  1257.                         curvalue[pos] = fillChar
  1258.                     
  1259.                 
  1260.                 if not (field._defaultValue) and not (self._ctrl_constraints._defaultValue):
  1261.                     self._template += fillChar
  1262.                     field._template += fillChar
  1263.                 elif field._defaultValue and not reset_default:
  1264.                     if len(field._defaultValue) > pos - start:
  1265.                         self._template += field._defaultValue[pos - start]
  1266.                         field._template += field._defaultValue[pos - start]
  1267.                     else:
  1268.                         self._template += fillChar
  1269.                         field._template += fillChar
  1270.                 elif len(self._ctrl_constraints._defaultValue) > pos:
  1271.                     self._template += self._ctrl_constraints._defaultValue[pos]
  1272.                     field._template += self._ctrl_constraints._defaultValue[pos]
  1273.                 else:
  1274.                     self._template += fillChar
  1275.                     field._template += fillChar
  1276.             not reset_default
  1277.             self._template += self._mask[pos]
  1278.         
  1279.         self._fields[-1]._template = self._template
  1280.         if curvalue:
  1281.             newvalue = string.join(curvalue, '')
  1282.         else:
  1283.             newvalue = None
  1284.         if default_set:
  1285.             self._defaultValue = self._template
  1286.             dbg('self._defaultValue:', self._defaultValue)
  1287.             if not self.IsEmpty(self._defaultValue) and not self.IsValid(self._defaultValue):
  1288.                 raise ValueError('Default value of "%s" is not a valid value for control "%s"' % (self._defaultValue, self.name))
  1289.             
  1290.             if newvalue == old_template:
  1291.                 newvalue = self._template
  1292.                 reset_value = true
  1293.             
  1294.         else:
  1295.             self._defaultValue = None
  1296.         if reset_value:
  1297.             dbg('resetting value to: "%s"' % newvalue)
  1298.             pos = self._GetInsertionPoint()
  1299.             (sel_start, sel_to) = self._GetSelection()
  1300.             self._SetValue(newvalue)
  1301.             self._SetInsertionPoint(pos)
  1302.             self._SetSelection(sel_start, sel_to)
  1303.         
  1304.  
  1305.     
  1306.     def _propagateConstraints(self, **reset_args):
  1307.         parent_codes = self._ctrl_constraints._formatcodes
  1308.         parent_includes = self._ctrl_constraints._includeChars
  1309.         parent_excludes = self._ctrl_constraints._excludeChars
  1310.         for i in self._field_indices:
  1311.             field = self._fields[i]
  1312.             inherit_args = { }
  1313.             if len(self._field_indices) == 1:
  1314.                 inherit_args['formatcodes'] = parent_codes
  1315.                 inherit_args['includeChars'] = parent_includes
  1316.                 inherit_args['excludeChars'] = parent_excludes
  1317.             else:
  1318.                 field_codes = current_codes = field._GetParameter('formatcodes')
  1319.                 for c in parent_codes:
  1320.                     if c not in field_codes:
  1321.                         field_codes += c
  1322.                         continue
  1323.                 
  1324.                 if field_codes != current_codes:
  1325.                     inherit_args['formatcodes'] = field_codes
  1326.                 
  1327.                 include_chars = current_includes = field._GetParameter('includeChars')
  1328.                 for c in parent_includes:
  1329.                     if not (c in include_chars):
  1330.                         include_chars += c
  1331.                         continue
  1332.                 
  1333.                 if include_chars != current_includes:
  1334.                     inherit_args['includeChars'] = include_chars
  1335.                 
  1336.                 exclude_chars = current_excludes = field._GetParameter('excludeChars')
  1337.                 for c in parent_excludes:
  1338.                     if not (c in exclude_chars):
  1339.                         exclude_chars += c
  1340.                         continue
  1341.                 
  1342.                 if exclude_chars != current_excludes:
  1343.                     inherit_args['excludeChars'] = exclude_chars
  1344.                 
  1345.             if reset_args.has_key('defaultValue') and reset_args['defaultValue']:
  1346.                 inherit_args['defaultValue'] = ''
  1347.             
  1348.             for param in Field.propagating_params:
  1349.                 if reset_args.has_key(param):
  1350.                     inherit_args[param] = self.GetCtrlParameter(param)
  1351.                     continue
  1352.             
  1353.             if inherit_args:
  1354.                 field._SetParameters(**inherit_args)
  1355.                 field._ValidateParameters(**inherit_args)
  1356.                 continue
  1357.         
  1358.  
  1359.     
  1360.     def _validateChoices(self):
  1361.         for field in self._fields.values():
  1362.             if field._choices:
  1363.                 index = field._index
  1364.                 if len(self._field_indices) == 1 and index == 0 and field._choices == self._ctrl_constraints._choices:
  1365.                     dbg('skipping (duplicate) choice validation of field 0')
  1366.                     continue
  1367.                 
  1368.                 (start, end) = field._extent
  1369.                 field_length = end - start
  1370.                 for choice in field._choices:
  1371.                     (valid_paste, ignore, replace_to) = self._validatePaste(choice, start, end)
  1372.                     if not valid_paste:
  1373.                         raise ValueError('"%s" could not be entered into field %d of control "%s"' % (choice, index, self.name))
  1374.                         continue
  1375.                     if replace_to > end:
  1376.                         raise ValueError('"%s" will not fit into field %d of control "%s"'(choice, index, self.name))
  1377.                         continue
  1378.                 
  1379.         
  1380.  
  1381.     
  1382.     def _configure(self, mask, **reset_args):
  1383.         dbg(suspend = 1)
  1384.         dbg('wxMaskedEditMixin::_configure("%s")' % mask, indent = 1)
  1385.         (self._mask, self.ismasked) = self._processMask(mask)
  1386.         self._masklength = len(self._mask)
  1387.         dbg('mask: "%s"' % self._mask, 'previous mask: "%s"' % self._previous_mask)
  1388.         self._previous_mask = mask
  1389.         self._ctrl_constraints._SetParameters(mask = self._mask, extent = (0, self._masklength))
  1390.         self._calcFieldExtents()
  1391.         if reset_args.has_key('fillChar'):
  1392.             pass
  1393.         reset_fillchar = reset_args['fillChar']
  1394.         if reset_args.has_key('defaultValue'):
  1395.             pass
  1396.         reset_default = reset_args['defaultValue']
  1397.         self._calcTemplate(reset_fillchar, reset_default)
  1398.         self._propagateConstraints(**reset_args)
  1399.         if self._isFloat and self._fields[0]._groupChar == self._decimalChar:
  1400.             raise AttributeError('groupChar (%s) and decimalChar (%s) must be distinct.' % (self._fields[0]._groupChar, self._decimalChar))
  1401.         
  1402.         if self._signOk:
  1403.             self._signpos = 0
  1404.             signkeys = [
  1405.                 '-',
  1406.                 '+',
  1407.                 ' ']
  1408.             if self._useParens:
  1409.                 signkeys += [
  1410.                     '(',
  1411.                     ')']
  1412.             
  1413.             for key in signkeys:
  1414.                 keycode = ord(key)
  1415.                 if not self._keyhandlers.has_key(keycode):
  1416.                     self._SetKeyHandler(key, self._OnChangeSign)
  1417.                     continue
  1418.             
  1419.         
  1420.         if self._isFloat or self._isInt:
  1421.             if self.controlInitialized:
  1422.                 value = self._GetValue()
  1423.                 if len(value) < len(self._ctrl_constraints._mask):
  1424.                     newvalue = value
  1425.                     if self._useParens and len(newvalue) < len(self._ctrl_constraints._mask) and newvalue.find('(') == -1:
  1426.                         newvalue += ' '
  1427.                     
  1428.                     if self._signOk and len(newvalue) < len(self._ctrl_constraints._mask) and newvalue.find(')') == -1:
  1429.                         newvalue = ' ' + newvalue
  1430.                     
  1431.                     if len(newvalue) < len(self._ctrl_constraints._mask):
  1432.                         if self._ctrl_constraints._alignRight:
  1433.                             newvalue = newvalue.rjust(len(self._ctrl_constraints._mask))
  1434.                         else:
  1435.                             newvalue = newvalue.ljust(len(self._ctrl_constraints._mask))
  1436.                     
  1437.                     dbg('old value: "%s"' % value)
  1438.                     dbg('new value: "%s"' % newvalue)
  1439.                     
  1440.                     try:
  1441.                         self._SetValue(newvalue)
  1442.                     except Exception:
  1443.                         e = None
  1444.                         dbg('exception raised:', e, 'resetting to initial value')
  1445.                         self._SetInitialValue()
  1446.                     except:
  1447.                         None<EXCEPTION MATCH>Exception
  1448.                     
  1449.  
  1450.                 None<EXCEPTION MATCH>Exception
  1451.                 if len(value) > len(self._ctrl_constraints._mask):
  1452.                     newvalue = value
  1453.                     if not (self._useParens) and newvalue[-1] == ' ':
  1454.                         newvalue = newvalue[:-1]
  1455.                     
  1456.                     if not (self._signOk) and len(newvalue) > len(self._ctrl_constraints._mask):
  1457.                         newvalue = newvalue[1:]
  1458.                     
  1459.                     if not (self._signOk):
  1460.                         (newvalue, signpos, right_signpos) = self._getSignedValue(newvalue)
  1461.                     
  1462.                     dbg('old value: "%s"' % value)
  1463.                     dbg('new value: "%s"' % newvalue)
  1464.                     
  1465.                     try:
  1466.                         self._SetValue(newvalue)
  1467.                     except Exception:
  1468.                         e = None
  1469.                         dbg('exception raised:', e, 'resetting to initial value')
  1470.                         self._SetInitialValue()
  1471.                     except:
  1472.                         None<EXCEPTION MATCH>Exception
  1473.                     
  1474.  
  1475.                 None<EXCEPTION MATCH>Exception
  1476.                 if not (self._signOk) and '(' in value or '-' in value:
  1477.                     (newvalue, signpos, right_signpos) = self._getSignedValue(value)
  1478.                     dbg('old value: "%s"' % value)
  1479.                     dbg('new value: "%s"' % newvalue)
  1480.                     
  1481.                     try:
  1482.                         self._SetValue(newvalue)
  1483.                     except e:
  1484.                         dbg('exception raised:', e, 'resetting to initial value')
  1485.                         self._SetInitialValue()
  1486.                     except:
  1487.                         None<EXCEPTION MATCH>e
  1488.                     
  1489.  
  1490.                 None<EXCEPTION MATCH>e
  1491.             
  1492.             if not self._keyhandlers.has_key(WXK_DOWN):
  1493.                 self._SetKeycodeHandler(WXK_DOWN, self._OnChangeField)
  1494.             
  1495.             if not self._keyhandlers.has_key(WXK_UP):
  1496.                 self._SetKeycodeHandler(WXK_UP, self._OnUpNumeric)
  1497.             
  1498.             if not self._keyhandlers.has_key(ord(self._decimalChar)):
  1499.                 self._SetKeyHandler(self._decimalChar, self._OnDecimalPoint)
  1500.             
  1501.             if not self._keyhandlers.has_key(ord(self._shiftDecimalChar)):
  1502.                 self._SetKeyHandler(self._shiftDecimalChar, self._OnChangeField)
  1503.             
  1504.             if not self._keyhandlers.has_key(ord(self._fields[0]._groupChar)):
  1505.                 self._SetKeyHandler(self._fields[0]._groupChar, self._OnGroupChar)
  1506.             
  1507.         
  1508.         dbg(indent = 0, suspend = 0)
  1509.  
  1510.     
  1511.     def _SetInitialValue(self, value = ''):
  1512.         dbg('wxMaskedEditMixin::_SetInitialValue("%s")' % value, indent = 1)
  1513.         if not value:
  1514.             self._prevValue = self._curValue = self._template
  1515.             
  1516.             try:
  1517.                 self._SetValue(self._curValue)
  1518.             except Exception:
  1519.                 e = None
  1520.                 dbg('exception thrown:', e, indent = 0)
  1521.                 raise 
  1522.             except:
  1523.                 None<EXCEPTION MATCH>Exception
  1524.             
  1525.  
  1526.         None<EXCEPTION MATCH>Exception
  1527.         self._prevValue = self._curValue = value
  1528.         
  1529.         try:
  1530.             self.SetValue(value)
  1531.         except Exception:
  1532.             e = None
  1533.             dbg('exception thrown:', e, indent = 0)
  1534.             raise 
  1535.  
  1536.         self._applyFormatting()
  1537.         dbg(indent = 0)
  1538.  
  1539.     
  1540.     def _calcSize(self, size = None):
  1541.         if not size is None:
  1542.             pass
  1543.         cont = size == wxDefaultSize
  1544.         if cont and self._autofit:
  1545.             sizing_text = 'M' * self._masklength
  1546.             if wxPlatform != '__WXMSW__':
  1547.                 sizing_text += 'M'
  1548.             
  1549.             if wxPlatform == '__WXMAC__':
  1550.                 sizing_text += 'M'
  1551.             
  1552.             (w, h) = self.GetTextExtent(sizing_text)
  1553.             size = (w + 4, self.GetClientSize().height)
  1554.         
  1555.         return size
  1556.  
  1557.     
  1558.     def _setFont(self):
  1559.         if not (self._useFixedWidthFont):
  1560.             self._font = wxSystemSettings_GetFont(wxSYS_DEFAULT_GUI_FONT)
  1561.         else:
  1562.             font = self.GetFont()
  1563.             self._font = wxFont(font.GetPointSize(), wxTELETYPE, font.GetStyle(), font.GetWeight(), font.GetUnderlined())
  1564.         self.SetFont(self._font)
  1565.  
  1566.     
  1567.     def _OnTextChange(self, event):
  1568.         newvalue = self._GetValue()
  1569.         dbg('wxMaskedEditMixin::_OnTextChange: value: "%s"' % newvalue, indent = 1)
  1570.         bValid = False
  1571.         if self._ignoreChange:
  1572.             dbg(indent = 0)
  1573.             return bValid
  1574.         
  1575.         if newvalue == self._curValue:
  1576.             dbg('ignoring bogus text change event', indent = 0)
  1577.         else:
  1578.             dbg('curvalue: "%s", newvalue: "%s"' % (self._curValue, newvalue))
  1579.             if self._Change():
  1580.                 if self._signOk and self._isNeg and newvalue.find('-') == -1 and newvalue.find('(') == -1:
  1581.                     dbg('clearing self._isNeg')
  1582.                     self._isNeg = False
  1583.                     (text, self._signpos, self._right_signpos) = self._getSignedValue()
  1584.                 
  1585.                 self._CheckValid()
  1586.             
  1587.             dbg('calling event.Skip()')
  1588.             event.Skip()
  1589.             bValid = True
  1590.         self._prevValue = self._curValue
  1591.         self._curValue = newvalue
  1592.         dbg(indent = 0)
  1593.         return bValid
  1594.  
  1595.     
  1596.     def _OnKeyDown(self, event):
  1597.         key = event.GetKeyCode()
  1598.         if key in self._nav and event.ControlDown():
  1599.             dbg('wxMaskedEditMixin::OnKeyDown: calling _OnChar')
  1600.             self._OnChar(event)
  1601.             return None
  1602.         
  1603.         event.Skip()
  1604.  
  1605.     
  1606.     def _OnChar(self, event):
  1607.         dbg('wxMaskedEditMixin::_OnChar', indent = 1)
  1608.         key = event.GetKeyCode()
  1609.         orig_pos = self._GetInsertionPoint()
  1610.         orig_value = self._GetValue()
  1611.         dbg('keycode = ', key)
  1612.         dbg('current pos = ', orig_pos)
  1613.         dbg('current selection = ', self._GetSelection())
  1614.         if not self._Keypress(key):
  1615.             dbg(indent = 0)
  1616.             return None
  1617.         
  1618.         if not (self._mask) or not self._IsEditable():
  1619.             event.Skip()
  1620.             dbg(indent = 0)
  1621.             return None
  1622.         
  1623.         if key in self._nav + self._control:
  1624.             if self._keyhandlers.has_key(key):
  1625.                 keep_processing = self._keyhandlers[key](event)
  1626.                 if self._GetValue() != orig_value:
  1627.                     self.modified = True
  1628.                 
  1629.                 if not keep_processing:
  1630.                     dbg(indent = 0)
  1631.                     return None
  1632.                 
  1633.                 self._applyFormatting()
  1634.                 dbg(indent = 0)
  1635.                 return None
  1636.             
  1637.         
  1638.         pos = self._adjustPos(orig_pos, key)
  1639.         (sel_start, sel_to) = self._GetSelection()
  1640.         dbg('pos, sel_start, sel_to:', pos, sel_start, sel_to)
  1641.         keep_processing = True
  1642.         if pos > len(self.maskdict):
  1643.             dbg('field length exceeded:', pos)
  1644.             keep_processing = False
  1645.         
  1646.         if keep_processing:
  1647.             if self._isMaskChar(pos):
  1648.                 okchars = self._getAllowedChars(pos)
  1649.             else:
  1650.                 dbg('Not a valid position: pos = ', pos, 'chars=', maskchars)
  1651.                 okchars = ''
  1652.         
  1653.         key = self._adjustKey(pos, key)
  1654.         if self._keyhandlers.has_key(key):
  1655.             dbg('using supplied key handler:', self._keyhandlers[key])
  1656.             keep_processing = self._keyhandlers[key](event)
  1657.             if self._GetValue() != orig_value:
  1658.                 self.modified = True
  1659.             
  1660.             if not keep_processing:
  1661.                 dbg(indent = 0)
  1662.                 return None
  1663.             
  1664.         
  1665.         if key < WXK_SPACE or key > 255:
  1666.             dbg('key < WXK_SPACE or key > 255')
  1667.             event.Skip()
  1668.             keep_processing = False
  1669.         else:
  1670.             field = self._FindField(pos)
  1671.             dbg("key ='%s'" % chr(key))
  1672.             if chr(key) == ' ':
  1673.                 dbg('okSpaces?', field._okSpaces)
  1674.             
  1675.             if chr(key) in field._excludeChars + self._ctrl_constraints._excludeChars:
  1676.                 keep_processing = False
  1677.             
  1678.             if keep_processing and self._isCharAllowed(chr(key), pos, checkRegex = True):
  1679.                 dbg('key allowed by mask')
  1680.                 oldstr = self._GetValue()
  1681.                 (newstr, newpos, new_select_to, match_field, match_index) = self._insertKey(chr(key), pos, sel_start, sel_to, self._GetValue(), allowAutoSelect = True)
  1682.                 dbg("str with '%s' inserted:" % chr(key), '"%s"' % newstr)
  1683.                 if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
  1684.                     dbg('not valid; checking to see if adjusted string is:')
  1685.                     keep_processing = False
  1686.                     if self._isFloat and newstr != self._template:
  1687.                         newstr = self._adjustFloat(newstr)
  1688.                         dbg('adjusted str:', newstr)
  1689.                         if self.IsValid(newstr):
  1690.                             dbg('it is!')
  1691.                             keep_processing = True
  1692.                             wxCallAfter(self._SetInsertionPoint, self._decimalpos)
  1693.                         
  1694.                     
  1695.                     if not keep_processing:
  1696.                         dbg('key disallowed by validation')
  1697.                         if not wxValidator_IsSilent() and orig_pos == pos:
  1698.                             wxBell()
  1699.                         
  1700.                     
  1701.                 
  1702.                 if keep_processing:
  1703.                     unadjusted = newstr
  1704.                     if self._isDate and newstr != self._template:
  1705.                         newstr = self._adjustDate(newstr)
  1706.                     
  1707.                     dbg('adjusted newstr:', newstr)
  1708.                     if newstr != orig_value:
  1709.                         self.modified = True
  1710.                     
  1711.                     wxCallAfter(self._SetValue, newstr)
  1712.                     if not self.IsDefault() and self._isDate and self._4digityear:
  1713.                         year2dig = self._dateExtent - 2
  1714.                         if pos == year2dig and unadjusted[year2dig] != newstr[year2dig]:
  1715.                             newpos = pos + 2
  1716.                         
  1717.                     
  1718.                     wxCallAfter(self._SetInsertionPoint, newpos)
  1719.                     if match_field is not None:
  1720.                         dbg('matched field')
  1721.                         self._OnAutoSelect(match_field, match_index)
  1722.                     
  1723.                     if new_select_to != newpos:
  1724.                         dbg('queuing selection: (%d, %d)' % (newpos, new_select_to))
  1725.                         wxCallAfter(self._SetSelection, newpos, new_select_to)
  1726.                     else:
  1727.                         newfield = self._FindField(newpos)
  1728.                         if newfield != field and newfield._selectOnFieldEntry:
  1729.                             dbg('queuing selection: (%d, %d)' % (newfield._extent[0], newfield._extent[1]))
  1730.                             wxCallAfter(self._SetSelection, newfield._extent[0], newfield._extent[1])
  1731.                         
  1732.                     keep_processing = false
  1733.                 
  1734.             elif keep_processing:
  1735.                 dbg('char not allowed')
  1736.                 keep_processing = False
  1737.                 if not wxValidator_IsSilent() and orig_pos == pos:
  1738.                     wxBell()
  1739.                 
  1740.             
  1741.         self._applyFormatting()
  1742.         if keep_processing and key not in self._nav:
  1743.             pos = self._GetInsertionPoint()
  1744.             next_entry = self._findNextEntry(pos)
  1745.             if pos != next_entry:
  1746.                 dbg('moving from %(pos)d to next valid entry: %(next_entry)d' % locals())
  1747.                 wxCallAfter(self._SetInsertionPoint, next_entry)
  1748.             
  1749.             if self._isTemplateChar(pos):
  1750.                 self._AdjustField(pos)
  1751.             
  1752.         
  1753.         dbg(indent = 0)
  1754.  
  1755.     
  1756.     def _FindFieldExtent(self, pos = None, getslice = False, value = None):
  1757.         dbg('wxMaskedEditMixin::_FindFieldExtent(pos=%s, getslice=%s)' % (str(pos), str(getslice)), indent = 1)
  1758.         field = self._FindField(pos)
  1759.         if not field:
  1760.             if getslice:
  1761.                 return (None, None, '')
  1762.             else:
  1763.                 return (None, None)
  1764.         
  1765.         (edit_start, edit_end) = field._extent
  1766.         if getslice:
  1767.             if value is None:
  1768.                 value = self._GetValue()
  1769.             
  1770.             slice = value[edit_start:edit_end]
  1771.             dbg('edit_start:', edit_start, 'edit_end:', edit_end, 'slice: "%s"' % slice)
  1772.             dbg(indent = 0)
  1773.             return (edit_start, edit_end, slice)
  1774.         else:
  1775.             dbg('edit_start:', edit_start, 'edit_end:', edit_end)
  1776.             dbg(indent = 0)
  1777.             return (edit_start, edit_end)
  1778.  
  1779.     
  1780.     def _FindField(self, pos = None):
  1781.         if pos is None:
  1782.             pos = self._GetInsertionPoint()
  1783.         elif pos < 0 or pos > self._masklength:
  1784.             raise IndexError('position %s out of range of control' % str(pos))
  1785.         
  1786.         if len(self._fields) == 0:
  1787.             dbg(indent = 0)
  1788.             return None
  1789.         
  1790.         return self._fields[self._lookupField[pos]]
  1791.  
  1792.     
  1793.     def ClearValue(self):
  1794.         dbg('wxMaskedEditMixin::ClearValue - value reset to default value (template)')
  1795.         self._SetValue(self._template)
  1796.         self._SetInsertionPoint(0)
  1797.         self.Refresh()
  1798.  
  1799.     
  1800.     def _baseCtrlEventHandler(self, event):
  1801.         event.Skip()
  1802.         return False
  1803.  
  1804.     
  1805.     def _OnUpNumeric(self, event):
  1806.         dbg('wxMaskedEditMixin::_OnUpNumeric', indent = 1)
  1807.         event.m_shiftDown = 1
  1808.         dbg('event.ShiftDown()?', event.ShiftDown())
  1809.         self._OnChangeField(event)
  1810.         dbg(indent = 0)
  1811.  
  1812.     
  1813.     def _OnArrow(self, event):
  1814.         dbg('wxMaskedEditMixin::_OnArrow', indent = 1)
  1815.         pos = self._GetInsertionPoint()
  1816.         keycode = event.GetKeyCode()
  1817.         (sel_start, sel_to) = self._GetSelection()
  1818.         entry_end = self._goEnd(getPosOnly = True)
  1819.         if keycode in (WXK_RIGHT, WXK_DOWN):
  1820.             if not self._isTemplateChar(pos) and pos + 1 > entry_end and self._isTemplateChar(pos) and pos >= entry_end:
  1821.                 dbg("can't advance", indent = 0)
  1822.                 return False
  1823.             elif self._isTemplateChar(pos):
  1824.                 self._AdjustField(pos)
  1825.             
  1826.         elif keycode in (WXK_LEFT, WXK_UP) and sel_start == sel_to and pos > 0 and self._isTemplateChar(pos - 1):
  1827.             dbg('adjusting field')
  1828.             self._AdjustField(pos)
  1829.         
  1830.         if event.ShiftDown() and keycode in (WXK_UP, WXK_DOWN):
  1831.             event.m_shiftDown = False
  1832.             keep_processing = self._OnChangeField(event)
  1833.         elif self._FindField(pos)._selectOnFieldEntry:
  1834.             if keycode in (WXK_UP, WXK_LEFT) and sel_start != 0 and self._isTemplateChar(sel_start - 1) and sel_start != self._masklength and not (self._signOk) and not (self._useParens):
  1835.                 event.m_shiftDown = True
  1836.                 event.m_ControlDown = True
  1837.                 keep_processing = self._OnChangeField(event)
  1838.             elif keycode in (WXK_DOWN, WXK_RIGHT) and sel_to != self._masklength and self._isTemplateChar(sel_to):
  1839.                 event.m_shiftDown = False
  1840.                 keep_processing = self._OnChangeField(event)
  1841.             else:
  1842.                 dbg('using base ctrl event processing')
  1843.                 event.Skip()
  1844.         elif sel_to == self._fields[0]._extent[0] and keycode == WXK_LEFT and sel_to == self._masklength and keycode == WXK_RIGHT:
  1845.             if not wxValidator_IsSilent():
  1846.                 wxBell()
  1847.             
  1848.         else:
  1849.             dbg('using base event processing')
  1850.             event.Skip()
  1851.         keep_processing = False
  1852.         dbg(indent = 0)
  1853.         return keep_processing
  1854.  
  1855.     
  1856.     def _OnCtrl_S(self, event):
  1857.         dbg('wxMaskedEditMixin::_OnCtrl_S')
  1858.         if self._demo:
  1859.             print 'wxMaskedEditMixin.GetValue()       = "%s"\nwxMaskedEditMixin.GetPlainValue() = "%s"' % (self.GetValue(), self.GetPlainValue())
  1860.             print 'Valid? => ' + str(self.IsValid())
  1861.             print 'Current field, start, end, value =', str(self._FindFieldExtent(getslice = True))
  1862.         
  1863.         return False
  1864.  
  1865.     
  1866.     def _OnCtrl_X(self, event = None):
  1867.         dbg('wxMaskedEditMixin::_OnCtrl_X', indent = 1)
  1868.         self.Cut()
  1869.         dbg(indent = 0)
  1870.         return False
  1871.  
  1872.     
  1873.     def _OnCtrl_C(self, event = None):
  1874.         self.Copy()
  1875.         return False
  1876.  
  1877.     
  1878.     def _OnCtrl_V(self, event = None):
  1879.         dbg('wxMaskedEditMixin::_OnCtrl_V', indent = 1)
  1880.         self.Paste()
  1881.         dbg(indent = 0)
  1882.         return False
  1883.  
  1884.     
  1885.     def _OnCtrl_Z(self, event = None):
  1886.         dbg('wxMaskedEditMixin::_OnCtrl_Z', indent = 1)
  1887.         self.Undo()
  1888.         dbg(indent = 0)
  1889.         return False
  1890.  
  1891.     
  1892.     def _OnCtrl_A(self, event = None):
  1893.         end = self._goEnd(getPosOnly = True)
  1894.         if not event or event.ShiftDown():
  1895.             wxCallAfter(self._SetInsertionPoint, 0)
  1896.             wxCallAfter(self._SetSelection, 0, self._masklength)
  1897.         else:
  1898.             wxCallAfter(self._SetInsertionPoint, 0)
  1899.             wxCallAfter(self._SetSelection, 0, end)
  1900.         return False
  1901.  
  1902.     
  1903.     def _OnErase(self, event = None):
  1904.         dbg('wxMaskedEditMixin::_OnErase', indent = 1)
  1905.         (sel_start, sel_to) = self._GetSelection()
  1906.         if event is None:
  1907.             key = WXK_DELETE
  1908.         else:
  1909.             key = event.GetKeyCode()
  1910.         field = self._FindField(sel_to)
  1911.         (start, end) = field._extent
  1912.         value = self._GetValue()
  1913.         oldstart = sel_start
  1914.         if not sel_to == 0 and key == WXK_BACK:
  1915.             if not self._signOk and sel_to == 1 and value[0] == ' ' and key == WXK_BACK:
  1916.                 if sel_to == self._masklength and sel_start == sel_to and key == WXK_DELETE and not (field._insertRight) and self._signOk and self._useParens and sel_start == sel_to and sel_to == self._masklength - 1 and value[sel_to] == ' ' and key == WXK_DELETE and not (field._insertRight):
  1917.                     if not wxValidator_IsSilent():
  1918.                         wxBell()
  1919.                     
  1920.                     dbg(indent = 0)
  1921.                     return False
  1922.                 
  1923.         if field._insertRight and value[start:end] != self._template[start:end] and sel_start >= start:
  1924.             if sel_to == sel_start and sel_to == end and key in (WXK_BACK, WXK_DELETE) and key == WXK_BACK and sel_to == end and sel_to < end and field._allowInsert:
  1925.                 dbg('delete left')
  1926.                 while key == WXK_BACK and sel_start == sel_to and sel_start < end and value[start:sel_start] == self._template[start:sel_start]:
  1927.                     sel_start += 1
  1928.                     sel_to = sel_start
  1929.                 dbg('sel_start, start:', sel_start, start)
  1930.                 if sel_start == sel_to:
  1931.                     keep = sel_start - 1
  1932.                 else:
  1933.                     keep = sel_start
  1934.                 newfield = value[start:keep] + value[sel_to:end]
  1935.                 move_sign_into_field = False
  1936.                 if not (field._padZero) and self._signOk and self._isNeg and value[0] in ('-', '('):
  1937.                     signchar = value[0]
  1938.                     newfield = signchar + newfield
  1939.                     move_sign_into_field = True
  1940.                 
  1941.                 dbg('cut newfield: "%s"' % newfield)
  1942.                 left = ''
  1943.                 for i in range(start, end - len(newfield)):
  1944.                     if field._padZero:
  1945.                         left += '0'
  1946.                         continue
  1947.                     if self._signOk and self._isNeg and i == 1:
  1948.                         if self._useParens and newfield.find('(') == -1 and not (self._useParens) and newfield.find('-') == -1:
  1949.                             left += ' '
  1950.                             continue
  1951.                     left += self._template[i]
  1952.                 
  1953.                 newfield = left + newfield
  1954.                 dbg('filled newfield: "%s"' % newfield)
  1955.                 newstr = value[:start] + newfield + value[end:]
  1956.                 if move_sign_into_field:
  1957.                     newstr = ' ' + newstr[1:]
  1958.                 
  1959.                 pos = sel_to
  1960.             elif self._signOk and sel_start == 0:
  1961.                 newstr = value = ' ' + value[1:]
  1962.                 sel_start += 1
  1963.             
  1964.         if field._allowInsert and sel_start >= start:
  1965.             select_len = sel_to - sel_start
  1966.             if key == WXK_BACK:
  1967.                 if select_len == 0:
  1968.                     newpos = sel_start - 1
  1969.                 else:
  1970.                     newpos = sel_start
  1971.                 erase_to = sel_to
  1972.             else:
  1973.                 newpos = sel_start
  1974.                 if sel_to == sel_start:
  1975.                     erase_to = sel_to + 1
  1976.                 else:
  1977.                     erase_to = sel_to
  1978.             if self._isTemplateChar(newpos) and select_len == 0:
  1979.                 if self._signOk:
  1980.                     if value[newpos] in ('(', '-'):
  1981.                         newpos += 1
  1982.                         newstr = ' ' + value[newpos:]
  1983.                     elif value[newpos] == ')':
  1984.                         newstr = value[:newpos] + ' '
  1985.                     else:
  1986.                         newstr = value
  1987.                 else:
  1988.                     newstr = value
  1989.             elif erase_to > end:
  1990.                 erase_to = end
  1991.             
  1992.             erase_len = erase_to - newpos
  1993.             left = value[start:newpos]
  1994.             dbg("retained ='%s'" % value[erase_to:end], 'sel_to:', sel_to, "fill: '%s'" % self._template[end - erase_len:end])
  1995.             right = value[erase_to:end] + self._template[end - erase_len:end]
  1996.             pos_adjust = 0
  1997.             if field._alignRight:
  1998.                 rstripped = right.rstrip()
  1999.                 if rstripped != right:
  2000.                     pos_adjust = len(right) - len(rstripped)
  2001.                 
  2002.                 right = rstripped
  2003.             
  2004.             if not (field._insertRight) and value[-1] == ')' and end == self._masklength - 1:
  2005.                 right = right[:-1] + ')'
  2006.                 value = value[:-1] + ' '
  2007.             
  2008.             newfield = left + right
  2009.             if pos_adjust:
  2010.                 newfield = newfield.rjust(end - start)
  2011.                 newpos += pos_adjust
  2012.             
  2013.             dbg("left='%s', right ='%s', newfield='%s'" % (left, right, newfield))
  2014.             newstr = value[:start] + newfield + value[end:]
  2015.             pos = newpos
  2016.         elif sel_start == sel_to:
  2017.             dbg('current sel_start, sel_to:', sel_start, sel_to)
  2018.             if key == WXK_BACK:
  2019.                 (sel_start, sel_to) = (sel_to - 1, sel_to - 1)
  2020.                 dbg('new sel_start, sel_to:', sel_start, sel_to)
  2021.             
  2022.             if field._padZero and not value[start:sel_to].replace('0', '').replace(' ', '').replace(field._fillChar, ''):
  2023.                 newchar = '0'
  2024.             else:
  2025.                 newchar = self._template[sel_to]
  2026.             dbg('value = "%s"' % value, 'value[%d] = "%s"' % (sel_start, value[sel_start]))
  2027.             if self._isTemplateChar(sel_to):
  2028.                 if sel_to == 0 and self._signOk and value[sel_to] == '-':
  2029.                     newstr = ' ' + value[1:]
  2030.                     sel_to += 1
  2031.                 elif self._signOk and self._useParens and value[sel_to] == ')' or value[sel_to] == '(':
  2032.                     newstr = value[:self._signpos] + ' ' + value[self._signpos + 1:-1] + ' '
  2033.                 else:
  2034.                     newstr = value
  2035.                 newpos = sel_to
  2036.             elif field._insertRight and sel_start == sel_to:
  2037.                 sel_to += 1
  2038.             
  2039.             (newstr, ignore) = self._insertKey(newchar, sel_start, sel_start, sel_to, value)
  2040.         else:
  2041.             newstr = self._eraseSelection(value, sel_start, sel_to)
  2042.         pos = sel_start
  2043.         if self._signOk and self._useParens:
  2044.             left_signpos = newstr.find('(')
  2045.             right_signpos = newstr.find(')')
  2046.             if left_signpos == -1 and right_signpos != -1:
  2047.                 newstr = newstr[:right_signpos] + ' ' + newstr[right_signpos + 1:]
  2048.             elif left_signpos != -1 and right_signpos == -1:
  2049.                 newstr = newstr[:left_signpos] + ' ' + newstr[left_signpos + 1:]
  2050.             
  2051.         
  2052.         dbg("oldstr:'%s'" % value, 'oldpos:', oldstart)
  2053.         dbg("newstr:'%s'" % newstr, 'pos:', pos)
  2054.         dbg('field._validRequired?', field._validRequired)
  2055.         dbg('field.IsValid("%s")?' % newstr[start:end], field.IsValid(newstr[start:end]))
  2056.         if field._validRequired and not field.IsValid(newstr[start:end]):
  2057.             if not wxValidator_IsSilent():
  2058.                 wxBell()
  2059.             
  2060.             dbg(indent = 0)
  2061.             return False
  2062.         
  2063.         if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
  2064.             if not wxValidator_IsSilent():
  2065.                 wxBell()
  2066.             
  2067.             dbg(indent = 0)
  2068.             return False
  2069.         
  2070.         dbg('setting value (later) to', newstr)
  2071.         wxCallAfter(self._SetValue, newstr)
  2072.         dbg('setting insertion point (later) to', pos)
  2073.         wxCallAfter(self._SetInsertionPoint, pos)
  2074.         dbg(indent = 0)
  2075.         return False
  2076.  
  2077.     
  2078.     def _OnEnd(self, event):
  2079.         dbg('wxMaskedEditMixin::_OnEnd', indent = 1)
  2080.         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  2081.         if not event.ControlDown():
  2082.             end = self._masklength
  2083.             if self._signOk and self._useParens:
  2084.                 end = end - 1
  2085.             
  2086.         else:
  2087.             end_of_input = self._goEnd(getPosOnly = True)
  2088.             (sel_start, sel_to) = self._GetSelection()
  2089.             if sel_to < pos:
  2090.                 sel_to = pos
  2091.             
  2092.             field = self._FindField(sel_to)
  2093.             field_end = self._FindField(end_of_input)
  2094.             if field != field_end or sel_to >= end_of_input:
  2095.                 (edit_start, edit_end) = field._extent
  2096.                 if sel_to == edit_end and field._index < self._field_indices[-1]:
  2097.                     (edit_start, edit_end) = self._FindFieldExtent(self._findNextEntry(edit_end))
  2098.                     end = edit_end
  2099.                     dbg('end moved to', end)
  2100.                 elif sel_to == edit_end and field._index == self._field_indices[-1]:
  2101.                     end = self._masklength
  2102.                     dbg('end moved to', end)
  2103.                 else:
  2104.                     end = edit_end
  2105.                     dbg('end moved to ', end)
  2106.             else:
  2107.                 end = end_of_input
  2108.         if event.ShiftDown():
  2109.             if not event.ControlDown():
  2110.                 dbg('shift-end; select to end of control')
  2111.             else:
  2112.                 dbg('shift-ctrl-end; select to end of non-whitespace')
  2113.             wxCallAfter(self._SetInsertionPoint, pos)
  2114.             wxCallAfter(self._SetSelection, pos, end)
  2115.         elif not event.ControlDown():
  2116.             dbg('go to end of control:')
  2117.         
  2118.         wxCallAfter(self._SetInsertionPoint, end)
  2119.         wxCallAfter(self._SetSelection, end, end)
  2120.         dbg(indent = 0)
  2121.         return False
  2122.  
  2123.     
  2124.     def _OnReturn(self, event):
  2125.         dbg('wxMaskedEditMixin::OnReturn')
  2126.         event.m_keyCode = WXK_TAB
  2127.         event.Skip()
  2128.  
  2129.     
  2130.     def _OnHome(self, event):
  2131.         dbg('wxMaskedEditMixin::_OnHome', indent = 1)
  2132.         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  2133.         (sel_start, sel_to) = self._GetSelection()
  2134.         if event.ShiftDown() and not event.ControlDown():
  2135.             dbg('shift-home; select to start of control')
  2136.             start = 0
  2137.             end = sel_start
  2138.         elif not event.ControlDown():
  2139.             dbg('home; move to start of control')
  2140.             start = 0
  2141.             end = 0
  2142.         elif event.ControlDown() and not event.ShiftDown() and event.ShiftDown() and sel_start > 0:
  2143.             if len(self._field_indices) > 1:
  2144.                 field = self._FindField(sel_start)
  2145.                 (start, ignore) = field._extent
  2146.                 if sel_start == start and field._index != self._field_indices[0]:
  2147.                     (start, ignore) = self._FindFieldExtent(sel_start - 1)
  2148.                 elif sel_start == start:
  2149.                     start = 0
  2150.                 
  2151.                 end_of_field = True
  2152.             else:
  2153.                 start = 0
  2154.             if not event.ShiftDown():
  2155.                 dbg('ctrl-home; move to beginning of field')
  2156.                 end = start
  2157.             else:
  2158.                 dbg('shift-ctrl-home; select to beginning of field')
  2159.                 end = sel_to
  2160.         else:
  2161.             start = sel_start
  2162.             if len(self._field_indices) > 1:
  2163.                 field = self._FindField(sel_to)
  2164.                 if sel_to > start and field._index != self._field_indices[0]:
  2165.                     (ignore, end) = self._FindFieldExtent(field._extent[0] - 1)
  2166.                 else:
  2167.                     end = start
  2168.                 end_of_field = True
  2169.             else:
  2170.                 end = start
  2171.                 end_of_field = False
  2172.             dbg('shift-ctrl-home; unselect to beginning of field')
  2173.         dbg('queuing new sel_start, sel_to:', (start, end))
  2174.         wxCallAfter(self._SetInsertionPoint, start)
  2175.         wxCallAfter(self._SetSelection, start, end)
  2176.         dbg(indent = 0)
  2177.         return False
  2178.  
  2179.     
  2180.     def _OnChangeField(self, event):
  2181.         dbg('wxMaskedEditMixin::_OnChangeField', indent = 1)
  2182.         pos = self._GetInsertionPoint()
  2183.         dbg('current pos:', pos)
  2184.         (sel_start, sel_to) = self._GetSelection()
  2185.         if self._masklength < 0:
  2186.             self._AdjustField(pos)
  2187.             if event.GetKeyCode() == WXK_TAB:
  2188.                 dbg('tab to next ctrl')
  2189.                 event.Skip()
  2190.             
  2191.             dbg(indent = 0)
  2192.             return False
  2193.         
  2194.         if event.ShiftDown():
  2195.             field = self._FindField(pos)
  2196.             index = field._index
  2197.             field_start = field._extent[0]
  2198.             if pos < field_start:
  2199.                 dbg('cursor before 1st field; cannot change to a previous field')
  2200.                 if not wxValidator_IsSilent():
  2201.                     wxBell()
  2202.                 
  2203.                 return false
  2204.             
  2205.             if event.ControlDown():
  2206.                 dbg('queuing select to beginning of field:', field_start, pos)
  2207.                 wxCallAfter(self._SetInsertionPoint, field_start)
  2208.                 wxCallAfter(self._SetSelection, field_start, pos)
  2209.                 dbg(indent = 0)
  2210.                 return False
  2211.             elif index == 0:
  2212.                 self._AdjustField(pos)
  2213.                 if event.GetKeyCode() == WXK_TAB:
  2214.                     dbg('tab to previous ctrl')
  2215.                     event.Skip()
  2216.                 else:
  2217.                     dbg('position at beginning')
  2218.                     wxCallAfter(self._SetInsertionPoint, field_start)
  2219.                 dbg(indent = 0)
  2220.                 return False
  2221.             else:
  2222.                 begin_prev = self._FindField(field_start - 1)._extent[0]
  2223.                 self._AdjustField(pos)
  2224.                 dbg('repositioning to', begin_prev)
  2225.                 wxCallAfter(self._SetInsertionPoint, begin_prev)
  2226.                 if self._FindField(begin_prev)._selectOnFieldEntry:
  2227.                     (edit_start, edit_end) = self._FindFieldExtent(begin_prev)
  2228.                     dbg('queuing selection to (%d, %d)' % (edit_start, edit_end))
  2229.                     wxCallAfter(self._SetInsertionPoint, edit_start)
  2230.                     wxCallAfter(self._SetSelection, edit_start, edit_end)
  2231.                 
  2232.                 dbg(indent = 0)
  2233.                 return False
  2234.         else:
  2235.             field = self._FindField(sel_to)
  2236.             (field_start, field_end) = field._extent
  2237.             if event.ControlDown():
  2238.                 dbg('queuing select to end of field:', pos, field_end)
  2239.                 wxCallAfter(self._SetInsertionPoint, pos)
  2240.                 wxCallAfter(self._SetSelection, pos, field_end)
  2241.                 dbg(indent = 0)
  2242.                 return False
  2243.             elif pos < field_start:
  2244.                 dbg('cursor before 1st field; go to start of field')
  2245.                 wxCallAfter(self._SetInsertionPoint, field_start)
  2246.                 if field._selectOnFieldEntry:
  2247.                     wxCallAfter(self._SetSelection, field_start, field_end)
  2248.                 else:
  2249.                     wxCallAfter(self._SetSelection, field_start, field_start)
  2250.                 return False
  2251.             
  2252.             dbg('end of current field:', field_end)
  2253.             dbg('go to next field')
  2254.             if field_end == self._fields[self._field_indices[-1]]._extent[1]:
  2255.                 self._AdjustField(pos)
  2256.                 if event.GetKeyCode() == WXK_TAB:
  2257.                     dbg('tab to next ctrl')
  2258.                     event.Skip()
  2259.                 else:
  2260.                     dbg('position at end')
  2261.                     wxCallAfter(self._SetInsertionPoint, field_end)
  2262.                 dbg(indent = 0)
  2263.                 return False
  2264.             else:
  2265.                 next_pos = self._findNextEntry(field_end)
  2266.                 if next_pos == field_end:
  2267.                     dbg('already in last field')
  2268.                     self._AdjustField(pos)
  2269.                     if event.GetKeyCode() == WXK_TAB:
  2270.                         dbg('tab to next ctrl')
  2271.                         event.Skip()
  2272.                     
  2273.                     dbg(indent = 0)
  2274.                     return False
  2275.                 else:
  2276.                     self._AdjustField(pos)
  2277.                     field = self._FindField(next_pos)
  2278.                     (edit_start, edit_end) = field._extent
  2279.                     if field._selectOnFieldEntry:
  2280.                         dbg('move to ', next_pos)
  2281.                         wxCallAfter(self._SetInsertionPoint, next_pos)
  2282.                         (edit_start, edit_end) = self._FindFieldExtent(next_pos)
  2283.                         dbg('queuing select', edit_start, edit_end)
  2284.                         wxCallAfter(self._SetSelection, edit_start, edit_end)
  2285.                     elif field._insertRight:
  2286.                         next_pos = field._extent[1]
  2287.                     
  2288.                     dbg('move to ', next_pos)
  2289.                     wxCallAfter(self._SetInsertionPoint, next_pos)
  2290.                     dbg(indent = 0)
  2291.                     return False
  2292.  
  2293.     
  2294.     def _OnDecimalPoint(self, event):
  2295.         dbg('wxMaskedEditMixin::_OnDecimalPoint', indent = 1)
  2296.         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  2297.         if self._isFloat:
  2298.             dbg('key == Decimal tab; decimal pos:', self._decimalpos)
  2299.             value = self._GetValue()
  2300.             if pos < self._decimalpos:
  2301.                 clipped_text = value[0:pos] + self._decimalChar + value[self._decimalpos + 1:]
  2302.                 dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
  2303.                 newstr = self._adjustFloat(clipped_text)
  2304.             else:
  2305.                 newstr = self._adjustFloat(value)
  2306.             wxCallAfter(self._SetValue, newstr)
  2307.             fraction = self._fields[1]
  2308.             (start, end) = fraction._extent
  2309.             wxCallAfter(self._SetInsertionPoint, start)
  2310.             if fraction._selectOnFieldEntry:
  2311.                 dbg('queuing selection after decimal point to:', (start, end))
  2312.                 wxCallAfter(self._SetSelection, start, end)
  2313.             
  2314.             keep_processing = False
  2315.         
  2316.         if self._isInt:
  2317.             dbg('key == Integer decimal event')
  2318.             value = self._GetValue()
  2319.             clipped_text = value[0:pos]
  2320.             dbg('value: "%s"' % self._GetValue(), "clipped_text:'%s'" % clipped_text)
  2321.             newstr = self._adjustInt(clipped_text)
  2322.             dbg('newstr: "%s"' % newstr)
  2323.             wxCallAfter(self._SetValue, newstr)
  2324.             newpos = len(newstr.rstrip())
  2325.             if newstr.find(')') != -1:
  2326.                 newpos -= 1
  2327.             
  2328.             wxCallAfter(self._SetInsertionPoint, newpos)
  2329.             keep_processing = False
  2330.         
  2331.         dbg(indent = 0)
  2332.  
  2333.     
  2334.     def _OnChangeSign(self, event):
  2335.         dbg('wxMaskedEditMixin::_OnChangeSign', indent = 1)
  2336.         key = event.GetKeyCode()
  2337.         pos = self._adjustPos(self._GetInsertionPoint(), key)
  2338.         value = self._eraseSelection()
  2339.         integer = self._fields[0]
  2340.         (start, end) = integer._extent
  2341.         if chr(key) in ('-', '+', '(', ')') and chr(key) == ' ' and pos == self._signpos:
  2342.             cursign = self._isNeg
  2343.             dbg('cursign:', cursign)
  2344.             if chr(key) in ('-', '(', ')'):
  2345.                 self._isNeg = not (self._isNeg)
  2346.             else:
  2347.                 self._isNeg = False
  2348.             dbg('isNeg?', self._isNeg)
  2349.             (text, self._signpos, self._right_signpos) = self._getSignedValue(candidate = value)
  2350.             dbg('text:"%s"' % text, 'signpos:', self._signpos, 'right_signpos:', self._right_signpos)
  2351.             if text is None:
  2352.                 text = value
  2353.             
  2354.             if self._isNeg and self._signpos is not None and self._signpos != -1:
  2355.                 if self._useParens and self._right_signpos is not None:
  2356.                     text = text[:self._signpos] + '(' + text[self._signpos + 1:self._right_signpos] + ')' + text[self._right_signpos + 1:]
  2357.                 else:
  2358.                     text = text[:self._signpos] + '-' + text[self._signpos + 1:]
  2359.             elif self._useParens:
  2360.                 text = text[:self._signpos] + ' ' + text[self._signpos + 1:self._right_signpos] + ' ' + text[self._right_signpos + 1:]
  2361.             else:
  2362.                 text = text[:self._signpos] + ' ' + text[self._signpos + 1:]
  2363.             dbg('clearing self._isNeg')
  2364.             self._isNeg = False
  2365.             wxCallAfter(self._SetValue, text)
  2366.             wxCallAfter(self._applyFormatting)
  2367.             dbg('pos:', pos, 'signpos:', self._signpos)
  2368.             if pos == self._signpos or integer.IsEmpty(text[start:end]):
  2369.                 wxCallAfter(self._SetInsertionPoint, self._signpos + 1)
  2370.             else:
  2371.                 wxCallAfter(self._SetInsertionPoint, pos)
  2372.             keep_processing = False
  2373.         else:
  2374.             keep_processing = True
  2375.         dbg(indent = 0)
  2376.         return keep_processing
  2377.  
  2378.     
  2379.     def _OnGroupChar(self, event):
  2380.         dbg('wxMaskedEditMixin::_OnGroupChar', indent = 1)
  2381.         keep_processing = True
  2382.         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  2383.         (sel_start, sel_to) = self._GetSelection()
  2384.         groupchar = self._fields[0]._groupChar
  2385.         if not self._isCharAllowed(groupchar, pos, checkRegex = True):
  2386.             keep_processing = False
  2387.             if not wxValidator_IsSilent():
  2388.                 wxBell()
  2389.             
  2390.         
  2391.         if keep_processing:
  2392.             (newstr, newpos) = self._insertKey(groupchar, pos, sel_start, sel_to, self._GetValue())
  2393.             dbg("str with '%s' inserted:" % groupchar, '"%s"' % newstr)
  2394.             if self._ctrl_constraints._validRequired and not self.IsValid(newstr):
  2395.                 keep_processing = False
  2396.                 if not wxValidator_IsSilent():
  2397.                     wxBell()
  2398.                 
  2399.             
  2400.         
  2401.         if keep_processing:
  2402.             wxCallAfter(self._SetValue, newstr)
  2403.             wxCallAfter(self._SetInsertionPoint, newpos)
  2404.         
  2405.         keep_processing = False
  2406.         dbg(indent = 0)
  2407.         return keep_processing
  2408.  
  2409.     
  2410.     def _findNextEntry(self, pos, adjustInsert = True):
  2411.         if self._isTemplateChar(pos):
  2412.             adjustInsert = adjustInsert
  2413.         else:
  2414.             adjustInsert = False
  2415.         while self._isTemplateChar(pos) and pos < self._masklength:
  2416.             pos += 1
  2417.         if adjustInsert and pos < self._masklength:
  2418.             field = self._FindField(pos)
  2419.             (start, end) = field._extent
  2420.             slice = self._GetValue()[start:end]
  2421.             if field._insertRight and field.IsEmpty(slice):
  2422.                 pos = end
  2423.             
  2424.         
  2425.         return pos
  2426.  
  2427.     
  2428.     def _findNextTemplateChar(self, pos):
  2429.         while not self._isTemplateChar(pos) and pos < self._masklength:
  2430.             pos += 1
  2431.         return pos
  2432.  
  2433.     
  2434.     def _OnAutoCompleteField(self, event):
  2435.         dbg('wxMaskedEditMixin::_OnAutoCompleteField', indent = 1)
  2436.         pos = self._GetInsertionPoint()
  2437.         field = self._FindField(pos)
  2438.         (edit_start, edit_end, slice) = self._FindFieldExtent(pos, getslice = True)
  2439.         match_index = None
  2440.         keycode = event.GetKeyCode()
  2441.         if field._fillChar != ' ':
  2442.             text = slice.replace(field._fillChar, '')
  2443.         else:
  2444.             text = slice
  2445.         text = text.strip()
  2446.         keep_processing = True
  2447.         dbg('field._hasList?', field._hasList)
  2448.         if field._hasList:
  2449.             dbg('choices:', field._choices)
  2450.             dbg('compareChoices:', field._compareChoices)
  2451.             (choices, choice_required) = (field._compareChoices, field._choiceRequired)
  2452.             if keycode in (WXK_PRIOR, WXK_UP):
  2453.                 direction = -1
  2454.             else:
  2455.                 direction = 1
  2456.             (match_index, partial_match) = self._autoComplete(direction, choices, text, compareNoCase = field._compareNoCase, current_index = field._autoCompleteIndex)
  2457.             if match_index is None and keycode in self._autoCompleteKeycodes + [
  2458.                 WXK_PRIOR,
  2459.                 WXK_NEXT] and keycode in [
  2460.                 WXK_UP,
  2461.                 WXK_DOWN] and event.ShiftDown():
  2462.                 match_index = 0
  2463.             
  2464.             if not match_index is not None and keycode in self._autoCompleteKeycodes + [
  2465.                 WXK_PRIOR,
  2466.                 WXK_NEXT]:
  2467.                 if keycode in [
  2468.                     WXK_UP,
  2469.                     WXK_DOWN] and event.ShiftDown() and keycode == WXK_DOWN and partial_match:
  2470.                     dbg('match found')
  2471.                     value = self._GetValue()
  2472.                     newvalue = value[:edit_start] + field._choices[match_index] + value[edit_end:]
  2473.                     dbg('setting value to "%s"' % newvalue)
  2474.                     self._SetValue(newvalue)
  2475.                     self._SetInsertionPoint(min(edit_end, len(newvalue.rstrip())))
  2476.                     self._OnAutoSelect(field, match_index)
  2477.                     self._CheckValid()
  2478.                 
  2479.             
  2480.         if keycode in (WXK_UP, WXK_DOWN, WXK_LEFT, WXK_RIGHT):
  2481.             if event.ShiftDown():
  2482.                 if keycode in (WXK_DOWN, WXK_RIGHT):
  2483.                     event.m_shiftDown = False
  2484.                 
  2485.                 keep_processing = self._OnChangeField(event)
  2486.             else:
  2487.                 keep_processing = self._OnArrow(event)
  2488.         
  2489.         dbg('keep processing?', keep_processing, indent = 0)
  2490.         return keep_processing
  2491.  
  2492.     
  2493.     def _OnAutoSelect(self, field, match_index = None):
  2494.         dbg('wxMaskedEditMixin::OnAutoSelect', field._index)
  2495.         if match_index is not None:
  2496.             field._autoCompleteIndex = match_index
  2497.         
  2498.  
  2499.     
  2500.     def _autoComplete(self, direction, choices, value, compareNoCase, current_index):
  2501.         dbg('autoComplete(direction=', direction, 'choices=', choices, 'value=', value, 'compareNoCase?', compareNoCase, 'current_index:', current_index, indent = 1)
  2502.         if value is None:
  2503.             dbg('nothing to match against', indent = 0)
  2504.             return (None, False)
  2505.         
  2506.         partial_match = False
  2507.         if compareNoCase:
  2508.             value = value.lower()
  2509.         
  2510.         last_index = len(choices) - 1
  2511.         if value in choices:
  2512.             dbg('"%s" in', choices)
  2513.             if current_index is not None and choices[current_index] == value:
  2514.                 index = current_index
  2515.             else:
  2516.                 index = choices.index(value)
  2517.             dbg('matched "%s" (%d)' % (choices[index], index))
  2518.             if direction == -1:
  2519.                 dbg('going to previous')
  2520.                 if index == 0:
  2521.                     index = len(choices) - 1
  2522.                 else:
  2523.                     index -= 1
  2524.             elif index == len(choices) - 1:
  2525.                 index = 0
  2526.             else:
  2527.                 index += 1
  2528.             dbg('change value to "%s" (%d)' % (choices[index], index))
  2529.             match = index
  2530.         else:
  2531.             partial_match = True
  2532.             value = value.strip()
  2533.             dbg('no match; try to auto-complete:')
  2534.             match = None
  2535.             dbg('searching for "%s"' % value)
  2536.             if current_index is None:
  2537.                 indices = range(len(choices))
  2538.                 if direction == -1:
  2539.                     indices.reverse()
  2540.                 
  2541.             elif direction == 1:
  2542.                 indices = range(current_index + 1, len(choices)) + range(current_index + 1)
  2543.                 dbg('range(current_index+1 (%d), len(choices) (%d)) + range(%d):' % (current_index + 1, len(choices), current_index + 1), indices)
  2544.             else:
  2545.                 indices = range(current_index - 1, -1, -1) + range(len(choices) - 1, current_index - 1, -1)
  2546.                 dbg('range(current_index-1 (%d), -1) + range(len(choices)-1 (%d)), current_index-1 (%d):' % (current_index - 1, len(choices) - 1, current_index - 1), indices)
  2547.             for index in indices:
  2548.                 choice = choices[index]
  2549.                 if choice.find(value, 0) == 0:
  2550.                     dbg('match found:', choice)
  2551.                     match = index
  2552.                     break
  2553.                     continue
  2554.                 dbg('choice: "%s" - no match' % choice)
  2555.             
  2556.             if match is not None:
  2557.                 dbg('matched', match)
  2558.             else:
  2559.                 dbg('no match found')
  2560.         dbg(indent = 0)
  2561.         return (match, partial_match)
  2562.  
  2563.     
  2564.     def _AdjustField(self, pos):
  2565.         newvalue = value = self._GetValue()
  2566.         field = self._FindField(pos)
  2567.         (start, end, slice) = self._FindFieldExtent(getslice = True)
  2568.         newfield = field._AdjustField(slice)
  2569.         newvalue = value[:start] + newfield + value[end:]
  2570.         if self._isFloat and newvalue != self._template:
  2571.             newvalue = self._adjustFloat(newvalue)
  2572.         
  2573.         if self._ctrl_constraints._isInt and value != self._template:
  2574.             newvalue = self._adjustInt(value)
  2575.         
  2576.         if self._isDate and value != self._template:
  2577.             newvalue = self._adjustDate(value, fixcentury = True)
  2578.             if self._4digityear:
  2579.                 year2dig = self._dateExtent - 2
  2580.                 if pos == year2dig and value[year2dig] != newvalue[year2dig]:
  2581.                     pos = pos + 2
  2582.                 
  2583.             
  2584.         
  2585.         if newvalue != value:
  2586.             self._SetValue(newvalue)
  2587.             self._SetInsertionPoint(pos)
  2588.         
  2589.  
  2590.     
  2591.     def _adjustKey(self, pos, key):
  2592.         field = self._FindField(pos)
  2593.         if field._forceupper and key in range(97, 123):
  2594.             key = ord(chr(key).upper())
  2595.         
  2596.         if field._forcelower and key in range(97, 123):
  2597.             key = ord(chr(key).lower())
  2598.         
  2599.         return key
  2600.  
  2601.     
  2602.     def _adjustPos(self, pos, key):
  2603.         dbg('_adjustPos', pos, key, indent = 1)
  2604.         (sel_start, sel_to) = self._GetSelection()
  2605.         if self._signOk:
  2606.             if pos == self._signpos and key in (ord('-'), ord('+'), ord(' ')) and self._useParens and pos == self._masklength - 1:
  2607.                 dbg('adjusted pos:', pos, indent = 0)
  2608.                 return pos
  2609.             
  2610.         if key not in self._nav:
  2611.             field = self._FindField(pos)
  2612.             dbg('field._insertRight?', field._insertRight)
  2613.             if field._insertRight:
  2614.                 (start, end) = field._extent
  2615.                 slice = self._GetValue()[start:end].strip()
  2616.                 field_len = end - start
  2617.                 if pos == end:
  2618.                     if len(slice) == field_len and field._moveOnFieldFull:
  2619.                         pos = self._findNextEntry(pos)
  2620.                         self._SetInsertionPoint(pos)
  2621.                         if pos < sel_to:
  2622.                             self._SetSelection(pos, sel_to)
  2623.                         else:
  2624.                             self._SetSelection(pos, pos)
  2625.                     
  2626.                 elif sel_to == sel_start and self._isTemplateChar(pos) and pos != end:
  2627.                     pos = end
  2628.                 elif self._signOk and sel_start == 0:
  2629.                     pos = self._fields[0]._extent[0]
  2630.                     self._SetInsertionPoint(pos)
  2631.                     self._SetSelection(pos, sel_to)
  2632.                 
  2633.             elif self._isTemplateChar(pos):
  2634.                 if not (field._moveOnFieldFull) and not (self._signOk) and self._signOk and field._index == 0 and pos > 0:
  2635.                     pass
  2636.                 else:
  2637.                     pos = self._findNextEntry(pos)
  2638.                     self._SetInsertionPoint(pos)
  2639.                     if pos < sel_to:
  2640.                         self._SetSelection(pos, sel_to)
  2641.                     
  2642.             
  2643.         
  2644.         dbg('adjusted pos:', pos, indent = 0)
  2645.         return pos
  2646.  
  2647.     
  2648.     def _adjustFloat(self, candidate = None):
  2649.         dbg('wxMaskedEditMixin::_adjustFloat, candidate = "%s"' % candidate, indent = 1)
  2650.         (lenInt, lenFraction) = [ len(s) for s in self._mask.split('.') ]
  2651.         dbg('value = "%(value)s"' % locals(), 'len(value):', len(value))
  2652.         (intStr, fracStr) = value.split(self._decimalChar)
  2653.         intStr = self._fields[0]._AdjustField(intStr)
  2654.         dbg('adjusted intStr: "%s"' % intStr)
  2655.         lenInt = len(intStr)
  2656.         fracStr = fracStr + '0' * (lenFraction - len(fracStr))
  2657.         dbg('intStr "%(intStr)s"' % locals())
  2658.         dbg('lenInt:', lenInt)
  2659.         intStr = string.rjust(intStr[-lenInt:], lenInt)
  2660.         dbg('right-justifed intStr = "%(intStr)s"' % locals())
  2661.         newvalue = intStr + self._decimalChar + fracStr
  2662.         if self._signOk:
  2663.             if len(newvalue) < self._masklength:
  2664.                 newvalue = ' ' + newvalue
  2665.             
  2666.             signedvalue = self._getSignedValue(newvalue)[0]
  2667.             if signedvalue is not None:
  2668.                 newvalue = signedvalue
  2669.             
  2670.         
  2671.         newdecpos = newvalue.find(self._decimalChar)
  2672.         if newdecpos < self._decimalpos:
  2673.             padlen = self._decimalpos - newdecpos
  2674.             newvalue = string.join([
  2675.                 ' ' * padlen] + [
  2676.                 newvalue], '')
  2677.         
  2678.         if self._signOk and self._useParens:
  2679.             if newvalue.find('(') != -1:
  2680.                 newvalue = newvalue[:-1] + ')'
  2681.             else:
  2682.                 newvalue = newvalue[:-1] + ' '
  2683.         
  2684.         dbg('newvalue = "%s"' % newvalue)
  2685.         if candidate is None:
  2686.             wxCallAfter(self._SetValue, newvalue)
  2687.         
  2688.         dbg(indent = 0)
  2689.         return newvalue
  2690.  
  2691.     
  2692.     def _adjustInt(self, candidate = None):
  2693.         dbg('wxMaskedEditMixin::_adjustInt', candidate)
  2694.         lenInt = self._masklength
  2695.         if candidate is None:
  2696.             value = self._GetValue()
  2697.         else:
  2698.             value = candidate
  2699.         intStr = self._fields[0]._AdjustField(value)
  2700.         intStr = intStr.strip()
  2701.         dbg('adjusted field: "%s"' % intStr)
  2702.         if self._isNeg and intStr.find('-') == -1 and intStr.find('(') == -1:
  2703.             if self._useParens:
  2704.                 intStr = '(' + intStr + ')'
  2705.             else:
  2706.                 intStr = '-' + intStr
  2707.         elif self._isNeg and intStr.find('-') != -1 and self._useParens:
  2708.             intStr = intStr.replace('-', '(')
  2709.         
  2710.         if self._signOk:
  2711.             if self._useParens and intStr.find('(') == -1 and not (self._useParens) and intStr.find('-') == -1:
  2712.                 intStr = ' ' + intStr
  2713.                 if self._useParens:
  2714.                     intStr += ' '
  2715.                 
  2716.             elif self._signOk and self._useParens and intStr.find('(') != -1 and intStr.find(')') == -1:
  2717.                 intStr += ')'
  2718.             
  2719.         if self._fields[0]._alignRight:
  2720.             intStr = intStr.rjust(lenInt)
  2721.         else:
  2722.             intStr = intStr.ljust(lenInt)
  2723.         if candidate is None:
  2724.             wxCallAfter(self._SetValue, intStr)
  2725.         
  2726.         return intStr
  2727.  
  2728.     
  2729.     def _adjustDate(self, candidate = None, fixcentury = False, force4digit_year = False):
  2730.         dbg('wxMaskedEditMixin::_adjustDate', indent = 1)
  2731.         if candidate is None:
  2732.             text = self._GetValue()
  2733.         else:
  2734.             text = candidate
  2735.         dbg('text=', text)
  2736.         if self._datestyle == 'YMD':
  2737.             year_field = 0
  2738.         else:
  2739.             year_field = 2
  2740.         dbg('getYear: "%s"' % getYear(text, self._datestyle))
  2741.         year = string.replace(getYear(text, self._datestyle), self._fields[year_field]._fillChar, '')
  2742.         month = getMonth(text, self._datestyle)
  2743.         day = getDay(text, self._datestyle)
  2744.         dbg('self._datestyle:', self._datestyle, 'year:', year, 'Month', month, 'day:', day)
  2745.         yearVal = None
  2746.         yearstart = self._dateExtent - 4
  2747.         if not len(year) < 4 and fixcentury and force4digit_year:
  2748.             if self._GetInsertionPoint() > yearstart + 1 and text[yearstart + 2] == ' ' and self._GetInsertionPoint() > yearstart + 2 and text[yearstart + 3] == ' ':
  2749.                 
  2750.                 try:
  2751.                     yearVal = int(year)
  2752.                 dbg('bad year=', year)
  2753.                 year = text[yearstart:self._dateExtent]
  2754.  
  2755.             
  2756.         if len(year) < 4 and yearVal:
  2757.             if len(year) == 2:
  2758.                 now = wxDateTime_Now()
  2759.                 century = (now.GetYear() / 100) * 100
  2760.                 twodig_year = now.GetYear() - century
  2761.                 if abs(yearVal - twodig_year) > 50:
  2762.                     yearVal = (century - 100) + yearVal
  2763.                 else:
  2764.                     yearVal = century + yearVal
  2765.                 year = str(yearVal)
  2766.             else:
  2767.                 year = '%04d' % yearVal
  2768.             if self._4digityear or force4digit_year:
  2769.                 text = makeDate(year, month, day, self._datestyle, text) + text[self._dateExtent:]
  2770.             
  2771.         
  2772.         dbg('newdate: "%s"' % text, indent = 0)
  2773.         return text
  2774.  
  2775.     
  2776.     def _goEnd(self, getPosOnly = False):
  2777.         dbg('wxMaskedEditMixin::_goEnd; getPosOnly:', getPosOnly, indent = 1)
  2778.         text = self._GetValue()
  2779.         i = 0
  2780.         if len(text.rstrip()):
  2781.             for i in range(min(self._masklength - 1, len(text.rstrip())), -1, -1):
  2782.                 if self._isMaskChar(i):
  2783.                     char = text[i]
  2784.                     if char != ' ':
  2785.                         i += 1
  2786.                         break
  2787.                     
  2788.                 char != ' '
  2789.             
  2790.         
  2791.         if i == 0:
  2792.             pos = self._goHome(getPosOnly = True)
  2793.         else:
  2794.             pos = min(i, self._masklength)
  2795.         field = self._FindField(pos)
  2796.         (start, end) = field._extent
  2797.         if field._insertRight and pos < end:
  2798.             pos = end
  2799.         
  2800.         dbg('next pos:', pos)
  2801.         dbg(indent = 0)
  2802.         if getPosOnly:
  2803.             return pos
  2804.         else:
  2805.             self._SetInsertionPoint(pos)
  2806.  
  2807.     
  2808.     def _goHome(self, getPosOnly = False):
  2809.         dbg('wxMaskedEditMixin::_goHome; getPosOnly:', getPosOnly, indent = 1)
  2810.         text = self._GetValue()
  2811.         for i in range(self._masklength):
  2812.             if self._isMaskChar(i):
  2813.                 break
  2814.                 continue
  2815.         
  2816.         pos = max(i, 0)
  2817.         dbg(indent = 0)
  2818.         if getPosOnly:
  2819.             return pos
  2820.         else:
  2821.             self._SetInsertionPoint(max(i, 0))
  2822.  
  2823.     
  2824.     def _getAllowedChars(self, pos):
  2825.         maskChar = self.maskdict[pos]
  2826.         okchars = self.maskchardict[maskChar]
  2827.         field = self._FindField(pos)
  2828.         if okchars and field._okSpaces:
  2829.             okchars += ' '
  2830.         
  2831.         if okchars and field._includeChars:
  2832.             okchars += field._includeChars
  2833.         
  2834.         return okchars
  2835.  
  2836.     
  2837.     def _isMaskChar(self, pos):
  2838.         if pos < self._masklength:
  2839.             return self.ismasked[pos]
  2840.         else:
  2841.             return False
  2842.  
  2843.     
  2844.     def _isTemplateChar(self, Pos):
  2845.         if Pos < self._masklength:
  2846.             return not self._isMaskChar(Pos)
  2847.         else:
  2848.             return False
  2849.  
  2850.     
  2851.     def _isCharAllowed(self, char, pos, checkRegex = False, allowAutoSelect = True, ignoreInsertRight = False):
  2852.         dbg('_isCharAllowed', char, pos, checkRegex, indent = 1)
  2853.         field = self._FindField(pos)
  2854.         right_insert = False
  2855.         if self.controlInitialized:
  2856.             (sel_start, sel_to) = self._GetSelection()
  2857.         else:
  2858.             (sel_start, sel_to) = (pos, pos)
  2859.         if (field._insertRight or self._ctrl_constraints._insertRight) and not ignoreInsertRight:
  2860.             (start, end) = field._extent
  2861.             field_len = end - start
  2862.             if self.controlInitialized:
  2863.                 value = self._GetValue()
  2864.                 fstr = value[start:end].strip()
  2865.                 if field._padZero:
  2866.                     while fstr and fstr[0] == '0':
  2867.                         fstr = fstr[1:]
  2868.                 
  2869.                 input_len = len(fstr)
  2870.                 if self._signOk and '-' in fstr or '(' in fstr:
  2871.                     input_len -= 1
  2872.                 
  2873.             else:
  2874.                 value = self._template
  2875.                 input_len = 0
  2876.             if (sel_start, sel_to) == field._extent and pos == end and input_len < field_len:
  2877.                 pos = end - 1
  2878.                 dbg('pos = end - 1 = ', pos, 'right_insert? 1')
  2879.                 right_insert = True
  2880.             elif field._allowInsert and sel_start == sel_to:
  2881.                 if sel_to == end and sel_to < self._masklength and value[sel_start] != field._fillChar and input_len < field_len:
  2882.                     pos = sel_to - 1
  2883.                     dbg('pos = sel_to - 1 = ', pos, 'right_insert? 1')
  2884.                     right_insert = True
  2885.                 else:
  2886.                     dbg('pos stays ', pos, 'right_insert? 0')
  2887.             
  2888.         if self._isTemplateChar(pos):
  2889.             dbg('%d is a template character; returning false' % pos, indent = 0)
  2890.             return False
  2891.         
  2892.         if self._isMaskChar(pos):
  2893.             okChars = self._getAllowedChars(pos)
  2894.             if self._fields[0]._groupdigits and self._isInt and self._isFloat and pos < self._decimalpos:
  2895.                 okChars += self._fields[0]._groupChar
  2896.             
  2897.             if self._signOk:
  2898.                 if self._isInt and self._isFloat and pos < self._decimalpos:
  2899.                     okChars += '-'
  2900.                     if self._useParens:
  2901.                         okChars += '('
  2902.                     
  2903.                 elif self._useParens and self._isInt and self._isFloat and pos > self._decimalpos:
  2904.                     okChars += ')'
  2905.                 
  2906.             
  2907.             approved = char in okChars
  2908.             if approved and checkRegex:
  2909.                 dbg("checking appropriate regex's")
  2910.                 value = self._eraseSelection(self._GetValue())
  2911.                 if right_insert:
  2912.                     at = pos + 1
  2913.                 else:
  2914.                     at = pos
  2915.                 if allowAutoSelect:
  2916.                     (newvalue, ignore, ignore, ignore, ignore) = self._insertKey(char, at, sel_start, sel_to, value, allowAutoSelect = True)
  2917.                 else:
  2918.                     (newvalue, ignore) = self._insertKey(char, at, sel_start, sel_to, value)
  2919.                 dbg('newvalue: "%s"' % newvalue)
  2920.                 fields = [
  2921.                     self._FindField(pos)] + [
  2922.                     self._ctrl_constraints]
  2923.                 for field in fields:
  2924.                     if field._regexMask and field._filter:
  2925.                         dbg('checking vs. regex')
  2926.                         (start, end) = field._extent
  2927.                         slice = newvalue[start:end]
  2928.                         approved = re.match(field._filter, slice) is not None
  2929.                         dbg('approved?', approved)
  2930.                     
  2931.                     if not approved:
  2932.                         break
  2933.                         continue
  2934.                 
  2935.             
  2936.             dbg(indent = 0)
  2937.             return approved
  2938.         else:
  2939.             dbg('%d is a !???! character; returning false', indent = 0)
  2940.             return False
  2941.  
  2942.     
  2943.     def _applyFormatting(self):
  2944.         dbg(suspend = 1)
  2945.         dbg('wxMaskedEditMixin::_applyFormatting', indent = 1)
  2946.         if self._signOk:
  2947.             (text, signpos, right_signpos) = self._getSignedValue()
  2948.             dbg('text: "%s", signpos:' % text, signpos)
  2949.             if not text or text[signpos] not in ('-', '('):
  2950.                 self._isNeg = False
  2951.                 dbg('no valid sign found; new sign:', self._isNeg)
  2952.                 if text and signpos != self._signpos:
  2953.                     self._signpos = signpos
  2954.                 
  2955.             elif text and self._valid and not (self._isNeg) and text[signpos] in ('-', '('):
  2956.                 dbg('setting _isNeg to True')
  2957.                 self._isNeg = True
  2958.             
  2959.             dbg('self._isNeg:', self._isNeg)
  2960.         
  2961.         if self._signOk and self._isNeg:
  2962.             fc = self._signedForegroundColour
  2963.         else:
  2964.             fc = self._foregroundColour
  2965.         if hasattr(fc, '_name'):
  2966.             c = fc._name
  2967.         else:
  2968.             c = fc
  2969.         dbg('setting foreground to', c)
  2970.         self.SetForegroundColour(fc)
  2971.         if self._valid:
  2972.             dbg('valid')
  2973.             if self.IsEmpty():
  2974.                 bc = self._emptyBackgroundColour
  2975.             else:
  2976.                 bc = self._validBackgroundColour
  2977.         else:
  2978.             dbg('invalid')
  2979.             bc = self._invalidBackgroundColour
  2980.         if hasattr(bc, '_name'):
  2981.             c = bc._name
  2982.         else:
  2983.             c = bc
  2984.         dbg('setting background to', c)
  2985.         self.SetBackgroundColour(bc)
  2986.         self._Refresh()
  2987.         dbg(indent = 0, suspend = 0)
  2988.  
  2989.     
  2990.     def _getAbsValue(self, candidate = None):
  2991.         dbg('wxMaskedEditMixin::_getAbsValue; candidate="%s"' % candidate, indent = 1)
  2992.         if candidate is None:
  2993.             text = self._GetValue()
  2994.         else:
  2995.             text = candidate
  2996.         right_signpos = text.find(')')
  2997.         if self._isInt:
  2998.             if self._ctrl_constraints._alignRight and self._fields[0]._fillChar == ' ':
  2999.                 signpos = text.find('-')
  3000.                 if signpos == -1:
  3001.                     dbg('no - found; searching for (')
  3002.                     signpos = text.find('(')
  3003.                 elif signpos != -1:
  3004.                     dbg('- found at', signpos)
  3005.                 
  3006.                 if signpos == -1:
  3007.                     dbg('signpos still -1')
  3008.                     dbg('len(%s) (%d) < len(%s) (%d)?' % (text, len(text), self._mask, self._masklength), len(text) < self._masklength)
  3009.                     if len(text) < self._masklength:
  3010.                         text = ' ' + text
  3011.                     
  3012.                     if len(text) < self._masklength:
  3013.                         text += ' '
  3014.                     
  3015.                     if len(text) > self._masklength and text[-1] in (')', ' '):
  3016.                         text = text[:-1]
  3017.                     else:
  3018.                         dbg('len(%s) (%d), len(%s) (%d)' % (text, len(text), self._mask, self._masklength))
  3019.                         dbg('len(%s) - (len(%s) + 1):' % (text, text.lstrip()), len(text) - (len(text.lstrip()) + 1))
  3020.                         signpos = len(text) - (len(text.lstrip()) + 1)
  3021.                         if self._useParens and not text.strip():
  3022.                             signpos -= 1
  3023.                         
  3024.                 
  3025.                 dbg('signpos:', signpos)
  3026.                 if signpos >= 0:
  3027.                     text = text[:signpos] + ' ' + text[signpos + 1:]
  3028.                 
  3029.             elif self._signOk:
  3030.                 signpos = 0
  3031.                 text = self._template[0] + text[1:]
  3032.             else:
  3033.                 signpos = -1
  3034.             if right_signpos != -1:
  3035.                 if self._signOk:
  3036.                     text = text[:right_signpos] + ' ' + text[right_signpos + 1:]
  3037.                 elif len(text) > self._masklength:
  3038.                     text = text[:right_signpos] + text[right_signpos + 1:]
  3039.                     right_signpos = -1
  3040.                 
  3041.             elif self._useParens and self._signOk:
  3042.                 right_signpos = self._masklength - 1
  3043.                 if not (self._ctrl_constraints._alignRight):
  3044.                     dbg('not right-aligned')
  3045.                     if len(text.strip()) == 0:
  3046.                         right_signpos = signpos + 1
  3047.                     elif len(text.strip()) < self._masklength:
  3048.                         right_signpos = len(text.rstrip())
  3049.                     
  3050.                 
  3051.                 dbg('right_signpos:', right_signpos)
  3052.             
  3053.             groupchar = self._fields[0]._groupChar
  3054.             
  3055.             try:
  3056.                 value = long(text.replace(groupchar, '').replace('(', '-').replace(')', '').replace(' ', ''))
  3057.             dbg('invalid number', indent = 0)
  3058.             return (None, signpos, right_signpos)
  3059.  
  3060.         else:
  3061.             
  3062.             try:
  3063.                 groupchar = self._fields[0]._groupChar
  3064.                 value = float(text.replace(groupchar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')', '').replace(' ', ''))
  3065.                 dbg('value:', value)
  3066.             except:
  3067.                 value = None
  3068.  
  3069.             if value < 0 and value is not None:
  3070.                 signpos = text.find('-')
  3071.                 if signpos == -1:
  3072.                     signpos = text.find('(')
  3073.                 
  3074.                 text = text[:signpos] + self._template[signpos] + text[signpos + 1:]
  3075.             else:
  3076.                 dbg('decimal pos:', self._decimalpos)
  3077.                 dbg('text: "%s"' % text)
  3078.                 if self._signOk:
  3079.                     signpos = self._decimalpos - (len(text[:self._decimalpos].lstrip()) + 1)
  3080.                     if text[signpos + 1] in ('-', '('):
  3081.                         signpos += 1
  3082.                     
  3083.                 else:
  3084.                     signpos = -1
  3085.                 dbg('signpos:', signpos)
  3086.             if self._useParens:
  3087.                 if self._signOk:
  3088.                     right_signpos = self._masklength - 1
  3089.                     text = text[:right_signpos] + ' '
  3090.                     if text[signpos] == '(':
  3091.                         text = text[:signpos] + ' ' + text[signpos + 1:]
  3092.                     
  3093.                 else:
  3094.                     right_signpos = text.find(')')
  3095.                     if right_signpos != -1:
  3096.                         text = text[:-1]
  3097.                         right_signpos = -1
  3098.                     
  3099.             
  3100.             if value is None:
  3101.                 dbg('invalid number')
  3102.                 text = None
  3103.             
  3104.         dbg('abstext = "%s"' % text, 'signpos:', signpos, 'right_signpos:', right_signpos)
  3105.         dbg(indent = 0)
  3106.         return (text, signpos, right_signpos)
  3107.  
  3108.     
  3109.     def _getSignedValue(self, candidate = None):
  3110.         dbg('wxMaskedEditMixin::_getSignedValue; candidate="%s"' % candidate, indent = 1)
  3111.         if candidate is None:
  3112.             text = self._GetValue()
  3113.         else:
  3114.             text = candidate
  3115.         (abstext, signpos, right_signpos) = self._getAbsValue(text)
  3116.         if self._signOk:
  3117.             if abstext is None:
  3118.                 dbg(indent = 0)
  3119.                 return (abstext, signpos, right_signpos)
  3120.             
  3121.             if self._isNeg or text[signpos] in ('-', '('):
  3122.                 if self._useParens:
  3123.                     sign = '('
  3124.                 else:
  3125.                     sign = '-'
  3126.             else:
  3127.                 sign = ' '
  3128.             if abstext[signpos] not in string.digits:
  3129.                 text = abstext[:signpos] + sign + abstext[signpos + 1:]
  3130.             else:
  3131.                 text = sign + abstext
  3132.             if self._useParens and text.find('(') != -1:
  3133.                 text = text[:right_signpos] + ')' + text[right_signpos + 1:]
  3134.             
  3135.         else:
  3136.             text = abstext
  3137.         dbg('signedtext = "%s"' % text, 'signpos:', signpos, 'right_signpos', right_signpos)
  3138.         dbg(indent = 0)
  3139.         return (text, signpos, right_signpos)
  3140.  
  3141.     
  3142.     def GetPlainValue(self, candidate = None):
  3143.         dbg('wxMaskedEditMixin::GetPlainValue; candidate="%s"' % candidate, indent = 1)
  3144.         if candidate is None:
  3145.             text = self._GetValue()
  3146.         else:
  3147.             text = candidate
  3148.         if self.IsEmpty():
  3149.             dbg('returned ""', indent = 0)
  3150.             return ''
  3151.         else:
  3152.             plain = ''
  3153.             for idx in range(min(len(self._template), len(text))):
  3154.                 if self._mask[idx] in maskchars:
  3155.                     plain += text[idx]
  3156.                     continue
  3157.             
  3158.             if self._isFloat or self._isInt:
  3159.                 dbg('plain so far: "%s"' % plain)
  3160.                 plain = plain.replace('(', '-').replace(')', ' ')
  3161.                 dbg('plain after sign regularization: "%s"' % plain)
  3162.                 if self._signOk and self._isNeg and plain.count('-') == 0:
  3163.                     plain = '-' + plain.strip()
  3164.                 
  3165.                 if self._fields[0]._alignRight:
  3166.                     lpad = plain.count(',')
  3167.                     plain = ' ' * lpad + plain.replace(',', '')
  3168.                 else:
  3169.                     plain = plain.replace(',', '')
  3170.                 dbg('plain after pad and group:"%s"' % plain)
  3171.             
  3172.             dbg('returned "%s"' % plain.rstrip(), indent = 0)
  3173.             return plain.rstrip()
  3174.  
  3175.     
  3176.     def IsEmpty(self, value = None):
  3177.         if value is None:
  3178.             value = self._GetValue()
  3179.         
  3180.         if value == self._template and not (self._defaultValue):
  3181.             return True
  3182.         elif value == self._template:
  3183.             empty = True
  3184.             for pos in range(len(self._template)):
  3185.                 if self._isMaskChar(pos) and value[pos] not in (' ', self._fillChar[pos]):
  3186.                     empty = False
  3187.                     continue
  3188.             
  3189.             return empty
  3190.         else:
  3191.             return False
  3192.  
  3193.     
  3194.     def IsDefault(self, value = None):
  3195.         if value is None:
  3196.             value = self._GetValue()
  3197.         
  3198.         return value == self._template
  3199.  
  3200.     
  3201.     def IsValid(self, value = None):
  3202.         if value is None:
  3203.             value = self._GetValue()
  3204.         
  3205.         ret = self._CheckValid(value)
  3206.         return ret
  3207.  
  3208.     
  3209.     def _eraseSelection(self, value = None, sel_start = None, sel_to = None):
  3210.         dbg('wxMaskedEditMixin::_eraseSelection', indent = 1)
  3211.         if value is None:
  3212.             value = self._GetValue()
  3213.         
  3214.         if sel_start is None or sel_to is None:
  3215.             (sel_start, sel_to) = self._GetSelection()
  3216.         
  3217.         dbg('value: "%s"' % value)
  3218.         dbg('current sel_start, sel_to:', sel_start, sel_to)
  3219.         newvalue = list(value)
  3220.         for i in range(sel_start, sel_to):
  3221.             if self._signOk and newvalue[i] in ('-', '(', ')'):
  3222.                 dbg('found sign (%s) at' % newvalue[i], i)
  3223.                 if newvalue[i] == '(':
  3224.                     right_signpos = value.find(')')
  3225.                     if right_signpos != -1:
  3226.                         newvalue[right_signpos] = ' '
  3227.                     
  3228.                 elif newvalue[i] == ')':
  3229.                     left_signpos = value.find('(')
  3230.                     if left_signpos != -1:
  3231.                         newvalue[left_signpos] = ' '
  3232.                     
  3233.                 
  3234.                 newvalue[i] = ' '
  3235.                 continue
  3236.             if self._isMaskChar(i):
  3237.                 field = self._FindField(i)
  3238.                 if field._padZero:
  3239.                     newvalue[i] = '0'
  3240.                 else:
  3241.                     newvalue[i] = self._template[i]
  3242.             field._padZero
  3243.         
  3244.         value = string.join(newvalue, '')
  3245.         dbg('new value: "%s"' % value)
  3246.         dbg(indent = 0)
  3247.         return value
  3248.  
  3249.     
  3250.     def _insertKey(self, char, pos, sel_start, sel_to, value, allowAutoSelect = False):
  3251.         dbg('wxMaskedEditMixin::_insertKey', "'" + char + "'", pos, sel_start, sel_to, '"%s"' % value, indent = 1)
  3252.         text = self._eraseSelection(value)
  3253.         field = self._FindField(pos)
  3254.         (start, end) = field._extent
  3255.         newtext = ''
  3256.         newpos = pos
  3257.         if pos != sel_start and sel_start == sel_to:
  3258.             sel_start = sel_to = pos
  3259.         
  3260.         dbg('field._insertRight?', field._insertRight)
  3261.         if field._insertRight and (sel_start, sel_to) == field._extent and sel_start == sel_to and sel_start == end and field._allowInsert and sel_start < end and text[sel_start] != field._fillChar:
  3262.             dbg('insertRight')
  3263.             fstr = text[start:end]
  3264.             erasable_chars = [
  3265.                 field._fillChar,
  3266.                 ' ']
  3267.             if field._padZero:
  3268.                 erasable_chars.append('0')
  3269.             
  3270.             erased = ''
  3271.             if fstr[0] in erasable_chars and self._signOk and field._index == 0 and fstr[0] in ('-', '('):
  3272.                 erased = fstr[0]
  3273.                 field_sel_start = sel_start - start
  3274.                 field_sel_to = sel_to - start
  3275.                 dbg('left fstr:  "%s"' % fstr[1:field_sel_start])
  3276.                 dbg('right fstr: "%s"' % fstr[field_sel_to:end])
  3277.                 fstr = fstr[1:field_sel_start] + char + fstr[field_sel_to:end]
  3278.             
  3279.             if field._alignRight and sel_start != sel_to:
  3280.                 field_len = end - start
  3281.                 pos = sel_to
  3282.                 dbg('setting pos to:', pos)
  3283.                 if field._padZero:
  3284.                     fstr = '0' * (field_len - len(fstr)) + fstr
  3285.                 else:
  3286.                     fstr = fstr.rjust(field_len)
  3287.             
  3288.             dbg('field str: "%s"' % fstr)
  3289.             newtext = text[:start] + fstr + text[end:]
  3290.             if erased in ('-', '(') and self._signOk:
  3291.                 newtext = erased + newtext[1:]
  3292.             
  3293.             dbg('newtext: "%s"' % newtext)
  3294.             if self._signOk and field._index == 0:
  3295.                 start -= 1
  3296.             
  3297.             if field._moveOnFieldFull and pos == end and len(fstr.lstrip()) == end - start:
  3298.                 newpos = self._findNextEntry(end)
  3299.             else:
  3300.                 newpos = pos
  3301.         
  3302.         if not newtext:
  3303.             dbg('not newtext')
  3304.             if newpos != pos:
  3305.                 dbg('newpos:', newpos)
  3306.             
  3307.             if self._signOk and self._useParens:
  3308.                 old_right_signpos = text.find(')')
  3309.             
  3310.             if field._allowInsert and not (field._insertRight) and sel_to <= end and sel_start >= start:
  3311.                 field_len = end - start
  3312.                 before = text[start:sel_start]
  3313.                 after = text[sel_to:end].strip()
  3314.                 new_len = len(before) + len(after) + 1
  3315.                 if new_len < field_len:
  3316.                     retained = after + self._template[end - field_len - new_len:end]
  3317.                 elif new_len > end - start:
  3318.                     retained = after[1:]
  3319.                 else:
  3320.                     retained = after
  3321.                 left = text[0:start] + before
  3322.                 right = retained + text[end:]
  3323.             else:
  3324.                 left = text[0:pos]
  3325.                 right = text[pos + 1:]
  3326.             newtext = left + char + right
  3327.             if self._signOk and self._useParens:
  3328.                 left_signpos = newtext.find('(')
  3329.                 if left_signpos == -1:
  3330.                     right_signpos = newtext.find(')')
  3331.                     if right_signpos != -1:
  3332.                         newtext = newtext[:right_signpos] + ' ' + newtext[right_signpos + 1:]
  3333.                     
  3334.                 elif old_right_signpos != -1:
  3335.                     right_signpos = newtext.find(')')
  3336.                     if right_signpos == -1:
  3337.                         if newtext[pos] == ' ':
  3338.                             newtext = newtext[:left_signpos] + ' ' + newtext[left_signpos + 1:]
  3339.                         elif self._ctrl_constraints._alignRight or self._isFloat:
  3340.                             newtext = newtext[:-1] + ')'
  3341.                         else:
  3342.                             rstripped_text = newtext.rstrip()
  3343.                             right_signpos = len(rstripped_text)
  3344.                             dbg('old_right_signpos:', old_right_signpos, 'right signpos now:', right_signpos)
  3345.                             newtext = newtext[:right_signpos] + ')' + newtext[right_signpos + 1:]
  3346.                     
  3347.                 
  3348.             
  3349.             if field._insertRight and field._moveOnFieldFull and len(newtext[start:end].strip()) == end - start:
  3350.                 newpos = self._findNextEntry(end)
  3351.                 dbg('newpos = nextentry =', newpos)
  3352.             else:
  3353.                 dbg('pos:', pos, 'newpos:', pos + 1)
  3354.                 newpos = pos + 1
  3355.         
  3356.         if allowAutoSelect:
  3357.             new_select_to = newpos
  3358.             match_field = None
  3359.             match_index = None
  3360.             if field._autoSelect:
  3361.                 (match_index, partial_match) = self._autoComplete(1, field._compareChoices, newtext[start:end], compareNoCase = field._compareNoCase, current_index = field._autoCompleteIndex - 1)
  3362.                 if match_index is not None and partial_match:
  3363.                     matched_str = newtext[start:end]
  3364.                     newtext = newtext[:start] + field._choices[match_index] + newtext[end:]
  3365.                     new_select_to = end
  3366.                     match_field = field
  3367.                     if field._insertRight:
  3368.                         newpos = end - len(field._choices[match_index].strip()) - len(matched_str.strip())
  3369.                     
  3370.                 
  3371.             elif self._ctrl_constraints._autoSelect:
  3372.                 (match_index, partial_match) = self._autoComplete(1, self._ctrl_constraints._compareChoices, newtext, self._ctrl_constraints._compareNoCase, current_index = self._ctrl_constraints._autoCompleteIndex - 1)
  3373.                 if match_index is not None and partial_match:
  3374.                     matched_str = newtext
  3375.                     newtext = self._ctrl_constraints._choices[match_index]
  3376.                     new_select_to = self._ctrl_constraints._extent[1]
  3377.                     match_field = self._ctrl_constraints
  3378.                     if self._ctrl_constraints._insertRight:
  3379.                         newpos = self._masklength - len(self._ctrl_constraints._choices[match_index].strip()) - len(matched_str.strip())
  3380.                     
  3381.                 
  3382.             
  3383.             dbg('newtext: "%s"' % newtext, 'newpos:', newpos, 'new_select_to:', new_select_to)
  3384.             dbg(indent = 0)
  3385.             return (newtext, newpos, new_select_to, match_field, match_index)
  3386.         else:
  3387.             dbg('newtext: "%s"' % newtext, 'newpos:', newpos)
  3388.             dbg(indent = 0)
  3389.             return (newtext, newpos)
  3390.  
  3391.     
  3392.     def _OnFocus(self, event):
  3393.         dbg('wxMaskedEditMixin::_OnFocus')
  3394.         wxCallAfter(self._fixSelection)
  3395.         event.Skip()
  3396.         self.Refresh()
  3397.  
  3398.     
  3399.     def _CheckValid(self, candidate = None):
  3400.         dbg(suspend = 1)
  3401.         dbg('wxMaskedEditMixin::_CheckValid: candidate="%s"' % candidate, indent = 1)
  3402.         oldValid = self._valid
  3403.         if candidate is None:
  3404.             value = self._GetValue()
  3405.         else:
  3406.             value = candidate
  3407.         dbg('value: "%s"' % value)
  3408.         oldvalue = value
  3409.         valid = True
  3410.         if not self.IsDefault(value) and self._isDate:
  3411.             valid = self._validateDate(value)
  3412.             dbg('valid date?', valid)
  3413.         elif not self.IsDefault(value) and self._isTime:
  3414.             valid = self._validateTime(value)
  3415.             dbg('valid time?', valid)
  3416.         elif not self.IsDefault(value) and self._isInt or self._isFloat:
  3417.             valid = self._validateNumeric(value)
  3418.             dbg('valid Number?', valid)
  3419.         
  3420.         if valid:
  3421.             valid = self._validateGeneric(value)
  3422.             dbg('valid value?', valid)
  3423.         
  3424.         dbg('valid?', valid)
  3425.         if not candidate:
  3426.             self._valid = valid
  3427.             self._applyFormatting()
  3428.             if self._valid != oldValid:
  3429.                 dbg('validity changed: oldValid =', oldValid, 'newvalid =', self._valid)
  3430.                 dbg('oldvalue: "%s"' % oldvalue, 'newvalue: "%s"' % self._GetValue())
  3431.             
  3432.         
  3433.         dbg(indent = 0, suspend = 0)
  3434.         return valid
  3435.  
  3436.     
  3437.     def _validateGeneric(self, candidate = None):
  3438.         if candidate is None:
  3439.             text = self._GetValue()
  3440.         else:
  3441.             text = candidate
  3442.         valid = True
  3443.         for i in [
  3444.             -1] + self._field_indices:
  3445.             field = self._fields[i]
  3446.             (start, end) = field._extent
  3447.             slice = text[start:end]
  3448.             valid = field.IsValid(slice)
  3449.             if not valid:
  3450.                 break
  3451.                 continue
  3452.         
  3453.         return valid
  3454.  
  3455.     
  3456.     def _validateNumeric(self, candidate = None):
  3457.         if candidate is None:
  3458.             value = self._GetValue()
  3459.         else:
  3460.             value = candidate
  3461.         
  3462.         try:
  3463.             groupchar = self._fields[0]._groupChar
  3464.             if self._isFloat:
  3465.                 number = float(value.replace(groupchar, '').replace(self._decimalChar, '.').replace('(', '-').replace(')', ''))
  3466.             else:
  3467.                 number = long(value.replace(groupchar, '').replace('(', '-').replace(')', ''))
  3468.                 if value.strip():
  3469.                     if self._fields[0]._alignRight:
  3470.                         require_digit_at = self._fields[0]._extent[1] - 1
  3471.                     else:
  3472.                         require_digit_at = self._fields[0]._extent[0]
  3473.                     dbg('require_digit_at:', require_digit_at)
  3474.                     dbg("value[rda]: '%s'" % value[require_digit_at])
  3475.                     if value[require_digit_at] not in list(string.digits):
  3476.                         valid = False
  3477.                         return valid
  3478.                     
  3479.                 
  3480.             dbg('number:', number)
  3481.             if self._ctrl_constraints._hasRange:
  3482.                 valid = None if number <= number else number <= self._ctrl_constraints._rangeHigh
  3483.             else:
  3484.                 valid = True
  3485.             groupcharpos = value.rfind(groupchar)
  3486.             if groupcharpos != -1:
  3487.                 dbg('groupchar found at', groupcharpos)
  3488.                 if self._isFloat and groupcharpos > self._decimalpos:
  3489.                     dbg('groupchar in fraction; illegal')
  3490.                     valid = False
  3491.                 elif self._isFloat:
  3492.                     integer = value[:self._decimalpos].strip()
  3493.                 else:
  3494.                     integer = value.strip()
  3495.                 dbg("integer:'%s'" % integer)
  3496.                 if integer[0] in ('-', '('):
  3497.                     integer = integer[1:]
  3498.                 
  3499.                 if integer[-1] == ')':
  3500.                     integer = integer[:-1]
  3501.                 
  3502.                 parts = integer.split(groupchar)
  3503.                 dbg('parts:', parts)
  3504.                 for i in range(len(parts)):
  3505.                     if i == 0 and abs(int(parts[0])) > 999:
  3506.                         dbg('group 0 too long; illegal')
  3507.                         valid = False
  3508.                         break
  3509.                         continue
  3510.                     if i > 0 and len(parts[i]) != 3 or ' ' in parts[i]:
  3511.                         dbg('group %i (%s) not right size; illegal' % (i, parts[i]))
  3512.                         valid = False
  3513.                         break
  3514.                         continue
  3515.                 
  3516.         except ValueError:
  3517.             dbg('value not a valid number')
  3518.             valid = False
  3519.  
  3520.         return valid
  3521.  
  3522.     
  3523.     def _validateDate(self, candidate = None):
  3524.         dbg('wxMaskedEditMixin::_validateDate', indent = 1)
  3525.         if candidate is None:
  3526.             value = self._GetValue()
  3527.         else:
  3528.             value = candidate
  3529.         dbg('value = "%s"' % value)
  3530.         text = self._adjustDate(value, force4digit_year = True)
  3531.         dbg('text =', text)
  3532.         valid = True
  3533.         
  3534.         try:
  3535.             datestr = text[0:self._dateExtent]
  3536.             for i in range(3):
  3537.                 field = self._fields[i]
  3538.                 (start, end) = field._extent
  3539.                 fstr = datestr[start:end]
  3540.                 fstr.replace(field._fillChar, ' ')
  3541.                 datestr = datestr[:start] + fstr + datestr[end:]
  3542.             
  3543.             (year, month, day) = getDateParts(datestr, self._datestyle)
  3544.             year = int(year)
  3545.             dbg('self._dateExtent:', self._dateExtent)
  3546.             if self._dateExtent == 11:
  3547.                 month = charmonths_dict[month.lower()]
  3548.             else:
  3549.                 month = int(month)
  3550.             day = int(day)
  3551.             dbg('year, month, day:', year, month, day)
  3552.         except ValueError:
  3553.             dbg('cannot convert string to integer parts')
  3554.             valid = False
  3555.         except KeyError:
  3556.             dbg('cannot convert string to integer month')
  3557.             valid = False
  3558.  
  3559.         if valid:
  3560.             if month > 12:
  3561.                 valid = False
  3562.             else:
  3563.                 month -= 1
  3564.                 
  3565.                 try:
  3566.                     dbg('trying to create date from values day=%d, month=%d, year=%d' % (day, month, year))
  3567.                     dateHandler = wxDateTimeFromDMY(day, month, year)
  3568.                     dbg('succeeded')
  3569.                     dateOk = True
  3570.                 except:
  3571.                     dbg('cannot convert string to valid date')
  3572.                     dateOk = False
  3573.  
  3574.                 if not dateOk:
  3575.                     valid = False
  3576.                 
  3577.             if valid:
  3578.                 timeStr = text[self._dateExtent + 1:].strip()
  3579.                 if timeStr:
  3580.                     dbg('timeStr: "%s"' % timeStr)
  3581.                     
  3582.                     try:
  3583.                         checkTime = dateHandler.ParseTime(timeStr)
  3584.                         valid = checkTime == len(timeStr)
  3585.                     except:
  3586.                         valid = False
  3587.  
  3588.                     if not valid:
  3589.                         dbg('cannot convert string to valid time')
  3590.                     
  3591.                 
  3592.             
  3593.         
  3594.         if valid:
  3595.             dbg('valid date')
  3596.         
  3597.         dbg(indent = 0)
  3598.         return valid
  3599.  
  3600.     
  3601.     def _validateTime(self, candidate = None):
  3602.         dbg('wxMaskedEditMixin::_validateTime', indent = 1)
  3603.         if candidate is None:
  3604.             value = self._GetValue().strip()
  3605.         else:
  3606.             value = candidate.strip()
  3607.         dbg('value = "%s"' % value)
  3608.         valid = True
  3609.         dateHandler = wxDateTime_Today()
  3610.         
  3611.         try:
  3612.             checkTime = dateHandler.ParseTime(value)
  3613.             dbg('checkTime:', checkTime, 'len(value)', len(value))
  3614.             valid = checkTime == len(value)
  3615.         except:
  3616.             valid = False
  3617.  
  3618.         if not valid:
  3619.             dbg('cannot convert string to valid time')
  3620.         
  3621.         if valid:
  3622.             dbg('valid time')
  3623.         
  3624.         dbg(indent = 0)
  3625.         return valid
  3626.  
  3627.     
  3628.     def _OnKillFocus(self, event):
  3629.         dbg('wxMaskedEditMixin::_OnKillFocus', 'isDate=', self._isDate, indent = 1)
  3630.         if self._mask and self._IsEditable():
  3631.             self._AdjustField(self._GetInsertionPoint())
  3632.             self._CheckValid()
  3633.         
  3634.         self._LostFocus()
  3635.         event.Skip()
  3636.         dbg(indent = 0)
  3637.  
  3638.     
  3639.     def _fixSelection(self):
  3640.         dbg('wxMaskedEditMixin::_fixSelection', indent = 1)
  3641.         if not (self._mask) or not self._IsEditable():
  3642.             dbg(indent = 0)
  3643.             return None
  3644.         
  3645.         (sel_start, sel_to) = self._GetSelection()
  3646.         dbg('sel_start, sel_to:', sel_start, sel_to, 'self.IsEmpty()?', self.IsEmpty())
  3647.         if sel_start == 0 and sel_to >= len(self._mask) and not (self._ctrl_constraints._autoSelect) and self.IsEmpty() or self.IsDefault():
  3648.             dbg('entire text selected; resetting selection to start of control')
  3649.             self._goHome()
  3650.             field = self._FindField(self._GetInsertionPoint())
  3651.             (edit_start, edit_end) = field._extent
  3652.             if field._selectOnFieldEntry:
  3653.                 self._SetInsertionPoint(edit_start)
  3654.                 self._SetSelection(edit_start, edit_end)
  3655.             elif field._insertRight:
  3656.                 self._SetInsertionPoint(edit_end)
  3657.                 self._SetSelection(edit_end, edit_end)
  3658.             
  3659.         elif self._isFloat or self._isInt:
  3660.             (text, signpos, right_signpos) = self._getAbsValue()
  3661.             if text is None or text == self._template:
  3662.                 integer = self._fields[0]
  3663.                 (edit_start, edit_end) = integer._extent
  3664.                 if integer._selectOnFieldEntry:
  3665.                     dbg('select on field entry:')
  3666.                     self._SetInsertionPoint(edit_start)
  3667.                     self._SetSelection(edit_start, edit_end)
  3668.                 elif integer._insertRight:
  3669.                     dbg('moving insertion point to end')
  3670.                     self._SetInsertionPoint(edit_end)
  3671.                     self._SetSelection(edit_end, edit_end)
  3672.                 else:
  3673.                     dbg('numeric ctrl is empty; start at beginning after sign')
  3674.                     self._SetInsertionPoint(signpos + 1)
  3675.                     self._SetSelection(signpos + 1, signpos + 1)
  3676.             
  3677.         elif sel_start > self._goEnd(getPosOnly = True):
  3678.             dbg('cursor beyond the end of the user input; go to end of it')
  3679.             self._goEnd()
  3680.         else:
  3681.             dbg('sel_start, sel_to:', sel_start, sel_to, 'self._masklength:', self._masklength)
  3682.         dbg(indent = 0)
  3683.  
  3684.     
  3685.     def _Keypress(self, key):
  3686.         return True
  3687.  
  3688.     
  3689.     def _LostFocus(self):
  3690.         pass
  3691.  
  3692.     
  3693.     def _OnDoubleClick(self, event):
  3694.         pos = self._GetInsertionPoint()
  3695.         field = self._FindField(pos)
  3696.         (start, end) = field._extent
  3697.         self._SetInsertionPoint(start)
  3698.         self._SetSelection(start, end)
  3699.  
  3700.     
  3701.     def _Change(self):
  3702.         return True
  3703.  
  3704.     
  3705.     def _Cut(self):
  3706.         dbg('wxMaskedEditMixin::_Cut', indent = 1)
  3707.         value = self._GetValue()
  3708.         dbg('current value: "%s"' % value)
  3709.         (sel_start, sel_to) = self._GetSelection()
  3710.         dbg('selected text: "%s"' % value[sel_start:sel_to].strip())
  3711.         do = wxTextDataObject()
  3712.         do.SetText(value[sel_start:sel_to].strip())
  3713.         wxTheClipboard.Open()
  3714.         wxTheClipboard.SetData(do)
  3715.         wxTheClipboard.Close()
  3716.         if sel_to - sel_start != 0:
  3717.             self._OnErase()
  3718.         
  3719.         dbg(indent = 0)
  3720.  
  3721.     
  3722.     def _getClipboardContents(self):
  3723.         do = wxTextDataObject()
  3724.         wxTheClipboard.Open()
  3725.         success = wxTheClipboard.GetData(do)
  3726.         wxTheClipboard.Close()
  3727.         if not success:
  3728.             return None
  3729.         else:
  3730.             return do.GetText().strip()
  3731.  
  3732.     
  3733.     def _validatePaste(self, paste_text, sel_start, sel_to, raise_on_invalid = False):
  3734.         dbg(suspend = 1)
  3735.         dbg('wxMaskedEditMixin::_validatePaste("%(paste_text)s", %(sel_start)d, %(sel_to)d), raise_on_invalid? %(raise_on_invalid)d' % locals(), indent = 1)
  3736.         select_length = sel_to - sel_start
  3737.         maxlength = select_length
  3738.         dbg('sel_to - sel_start:', maxlength)
  3739.         if maxlength == 0:
  3740.             maxlength = self._masklength - sel_start
  3741.             item = 'control'
  3742.         else:
  3743.             item = 'selection'
  3744.         dbg('maxlength:', maxlength)
  3745.         length_considered = len(paste_text)
  3746.         if length_considered > maxlength:
  3747.             dbg('paste text will not fit into the %s:' % item, indent = 0)
  3748.             if raise_on_invalid:
  3749.                 dbg(indent = 0, suspend = 0)
  3750.                 if item == 'control':
  3751.                     raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
  3752.                 else:
  3753.                     raise ValueError('"%s" will not fit into the selection' % paste_text)
  3754.             else:
  3755.                 dbg(indent = 0, suspend = 0)
  3756.                 return (False, None, None)
  3757.         
  3758.         text = self._template
  3759.         dbg('length_considered:', length_considered)
  3760.         valid_paste = True
  3761.         replacement_text = ''
  3762.         replace_to = sel_start
  3763.         i = 0
  3764.         while valid_paste and i < length_considered and replace_to < self._masklength:
  3765.             if paste_text[i:] == self._template[replace_to:length_considered]:
  3766.                 dbg('remainder paste_text[%d:] (%s) matches template[%d:%d]' % (i, paste_text[i:], replace_to, length_considered))
  3767.                 replacement_text += paste_text[i:]
  3768.                 replace_to = i = length_considered
  3769.                 continue
  3770.             
  3771.             char = paste_text[i]
  3772.             field = self._FindField(replace_to)
  3773.             if not (field._compareNoCase):
  3774.                 if field._forceupper:
  3775.                     char = char.upper()
  3776.                 elif field._forcelower:
  3777.                     char = char.lower()
  3778.                 
  3779.             
  3780.             dbg('char:', "'" + char + "'", 'i =', i, 'replace_to =', replace_to)
  3781.             dbg('self._isTemplateChar(%d)?' % replace_to, self._isTemplateChar(replace_to))
  3782.             if not self._isTemplateChar(replace_to) and self._isCharAllowed(char, replace_to, allowAutoSelect = False, ignoreInsertRight = True):
  3783.                 replacement_text += char
  3784.                 dbg("not template(%(replace_to)d) and charAllowed('%(char)s',%(replace_to)d)" % locals())
  3785.                 dbg('replacement_text:', '"' + replacement_text + '"')
  3786.                 i += 1
  3787.                 replace_to += 1
  3788.                 continue
  3789.             if char == self._template[replace_to] and self._signOk:
  3790.                 if i == 0 and char == '-' and self._useParens and char == '(' and i == self._masklength - 1 and self._useParens and char == ')':
  3791.                     replacement_text += char
  3792.                     dbg("'%(char)s' == template(%(replace_to)d)" % locals())
  3793.                     dbg('replacement_text:', '"' + replacement_text + '"')
  3794.                     i += 1
  3795.                     replace_to += 1
  3796.                     continue
  3797.             next_entry = self._findNextEntry(replace_to, adjustInsert = False)
  3798.             if next_entry == replace_to:
  3799.                 valid_paste = False
  3800.                 continue
  3801.             replacement_text += self._template[replace_to:next_entry]
  3802.             dbg('skipping template; next_entry =', next_entry)
  3803.             dbg('replacement_text:', '"' + replacement_text + '"')
  3804.             replace_to = next_entry
  3805.         if not valid_paste and raise_on_invalid:
  3806.             dbg('raising exception', indent = 0, suspend = 0)
  3807.             raise ValueError('"%s" cannot be inserted into the control "%s"' % (paste_text, self.name))
  3808.         elif i < len(paste_text):
  3809.             valid_paste = False
  3810.             if raise_on_invalid:
  3811.                 dbg('raising exception', indent = 0, suspend = 0)
  3812.                 raise ValueError('"%s" will not fit into the control "%s"' % (paste_text, self.name))
  3813.             
  3814.         
  3815.         dbg('valid_paste?', valid_paste)
  3816.         if valid_paste:
  3817.             dbg('replacement_text: "%s"' % replacement_text, 'replace to:', replace_to)
  3818.         
  3819.         dbg(indent = 0, suspend = 0)
  3820.         return (valid_paste, replacement_text, replace_to)
  3821.  
  3822.     
  3823.     def _Paste(self, value = None, raise_on_invalid = False, just_return_value = False):
  3824.         dbg('wxMaskedEditMixin::_Paste (value = "%s")' % value, indent = 1)
  3825.         if value is None:
  3826.             paste_text = self._getClipboardContents()
  3827.         else:
  3828.             paste_text = value
  3829.         if paste_text is not None:
  3830.             dbg('paste text: "%s"' % paste_text)
  3831.             (sel_start, sel_to) = self._GetSelection()
  3832.             dbg('selection:', (sel_start, sel_to))
  3833.             field = self._FindField(sel_start)
  3834.             (edit_start, edit_end) = field._extent
  3835.             new_pos = None
  3836.             if field._allowInsert and sel_to <= edit_end and sel_start + len(paste_text) < edit_end:
  3837.                 new_pos = sel_start + len(paste_text)
  3838.                 paste_text = paste_text + self._GetValue()[sel_to:edit_end].rstrip()
  3839.                 dbg('paste within insertable field; adjusted paste_text: "%s"' % paste_text, 'end:', edit_end)
  3840.                 sel_to = sel_start + len(paste_text)
  3841.             
  3842.             if len(paste_text) > sel_to - sel_start and field._insertRight and sel_start > edit_start and sel_to >= edit_end and not self._GetValue()[edit_start:sel_start].strip():
  3843.                 empty_space = sel_start - edit_start
  3844.                 amount_needed = len(paste_text) - sel_to - sel_start
  3845.                 if amount_needed <= empty_space:
  3846.                     sel_start -= amount_needed
  3847.                     dbg('expanded selection to:', (sel_start, sel_to))
  3848.                 
  3849.             
  3850.             if self._signOk:
  3851.                 (signedvalue, signpos, right_signpos) = self._getSignedValue()
  3852.                 paste_signpos = paste_text.find('-')
  3853.                 if paste_signpos == -1:
  3854.                     paste_signpos = paste_text.find('(')
  3855.                 
  3856.                 if paste_signpos != -1 and sel_start <= signpos and field._insertRight and sel_start - len(paste_text) <= signpos:
  3857.                     signed = True
  3858.                 else:
  3859.                     signed = False
  3860.                 paste_text = paste_text.replace('-', ' ').replace('(', ' ').replace(')', '')
  3861.                 dbg('unsigned paste text: "%s"' % paste_text)
  3862.             else:
  3863.                 signed = False
  3864.             if field._insertRight and sel_start == edit_end and sel_start == sel_to:
  3865.                 sel_start -= len(paste_text)
  3866.                 if sel_start < 0:
  3867.                     sel_start = 0
  3868.                 
  3869.                 dbg('adjusted selection:', (sel_start, sel_to))
  3870.             
  3871.             
  3872.             try:
  3873.                 (valid_paste, replacement_text, replace_to) = self._validatePaste(paste_text, sel_start, sel_to, raise_on_invalid)
  3874.             except:
  3875.                 dbg('exception thrown', indent = 0)
  3876.                 raise 
  3877.  
  3878.             if not valid_paste:
  3879.                 dbg('paste text not legal for the selection or portion of the control following the cursor;')
  3880.                 if not wxValidator_IsSilent():
  3881.                     wxBell()
  3882.                 
  3883.                 dbg(indent = 0)
  3884.                 return False
  3885.             
  3886.             text = self._eraseSelection()
  3887.             new_text = text[:sel_start] + replacement_text + text[replace_to:]
  3888.             if new_text:
  3889.                 new_text = string.ljust(new_text, self._masklength)
  3890.             
  3891.             if signed:
  3892.                 (new_text, signpos, right_signpos) = self._getSignedValue(candidate = new_text)
  3893.                 if new_text:
  3894.                     if self._useParens:
  3895.                         new_text = new_text[:signpos] + '(' + new_text[signpos + 1:right_signpos] + ')' + new_text[right_signpos + 1:]
  3896.                     else:
  3897.                         new_text = new_text[:signpos] + '-' + new_text[signpos + 1:]
  3898.                     if not (self._isNeg):
  3899.                         self._isNeg = 1
  3900.                     
  3901.                 
  3902.             
  3903.             dbg('new_text:', '"' + new_text + '"')
  3904.             if not just_return_value:
  3905.                 if new_text == '':
  3906.                     self.ClearValue()
  3907.                 else:
  3908.                     wxCallAfter(self._SetValue, new_text)
  3909.                     if new_pos is None:
  3910.                         new_pos = sel_start + len(replacement_text)
  3911.                     
  3912.                     wxCallAfter(self._SetInsertionPoint, new_pos)
  3913.             else:
  3914.                 dbg(indent = 0)
  3915.                 return new_text
  3916.         elif just_return_value:
  3917.             dbg(indent = 0)
  3918.             return self._GetValue()
  3919.         
  3920.         dbg(indent = 0)
  3921.  
  3922.     
  3923.     def _Undo(self):
  3924.         dbg('wxMaskedEditMixin::_Undo', indent = 1)
  3925.         value = self._GetValue()
  3926.         prev = self._prevValue
  3927.         dbg('current value:  "%s"' % value)
  3928.         dbg('previous value: "%s"' % prev)
  3929.         if prev is None:
  3930.             dbg('no previous value', indent = 0)
  3931.             return None
  3932.         elif value != prev:
  3933.             i = 0
  3934.             length = len(value)
  3935.             while value[:i] == prev[:i]:
  3936.                 i += 1
  3937.             sel_start = i - 1
  3938.             if self._signOk:
  3939.                 (text, signpos, right_signpos) = self._getSignedValue(candidate = prev)
  3940.                 if self._useParens:
  3941.                     if prev[signpos] == '(' and prev[right_signpos] == ')':
  3942.                         self._isNeg = True
  3943.                     else:
  3944.                         self._isNeg = False
  3945.                     value = value.replace(')', ' ')
  3946.                     prev = prev.replace(')', ' ')
  3947.                 elif prev[signpos] == '-':
  3948.                     self._isNeg = True
  3949.                 else:
  3950.                     self._isNeg = False
  3951.             
  3952.             sm = difflib.SequenceMatcher(None, a = value, b = prev)
  3953.             (i, j, k) = sm.find_longest_match(sel_start, length, sel_start, length)
  3954.             dbg('i,j,k = ', (i, j, k), 'value[i:i+k] = "%s"' % value[i:i + k], 'prev[j:j+k] = "%s"' % prev[j:j + k])
  3955.             if k == 0:
  3956.                 sel_to = length
  3957.             else:
  3958.                 code_5tuples = sm.get_opcodes()
  3959.                 for op, i1, i2, j1, j2 in code_5tuples:
  3960.                     dbg('%7s value[%d:%d] (%s) prev[%d:%d] (%s)' % (op, i1, i2, value[i1:i2], j1, j2, prev[j1:j2]))
  3961.                 
  3962.                 diff_found = False
  3963.                 for next_op in range(len(code_5tuples) - 1, -1, -1):
  3964.                     (op, i1, i2, j1, j2) = code_5tuples[next_op]
  3965.                     dbg('value[i1:i2]: "%s"' % value[i1:i2], 'template[i1:i2] "%s"' % self._template[i1:i2])
  3966.                     if op == 'insert' and prev[j1:j2] != self._template[j1:j2]:
  3967.                         dbg('insert found: selection =>', (j1, j2))
  3968.                         sel_start = j1
  3969.                         sel_to = j2
  3970.                         diff_found = True
  3971.                         break
  3972.                         continue
  3973.                     if op == 'delete' and value[i1:i2] != self._template[i1:i2]:
  3974.                         field = self._FindField(i2)
  3975.                         (edit_start, edit_end) = field._extent
  3976.                         if field._insertRight and i2 == edit_end:
  3977.                             sel_start = i2
  3978.                             sel_to = i2
  3979.                         else:
  3980.                             sel_start = i1
  3981.                             sel_to = j1
  3982.                         dbg('delete found: selection =>', (sel_start, sel_to))
  3983.                         diff_found = True
  3984.                         break
  3985.                         continue
  3986.                     if op == 'replace':
  3987.                         dbg('replace found: selection =>', (j1, j2))
  3988.                         sel_start = j1
  3989.                         sel_to = j2
  3990.                         diff_found = True
  3991.                         break
  3992.                         continue
  3993.                 
  3994.                 if diff_found:
  3995.                     for next_op in range(len(code_5tuples)):
  3996.                         (op, i1, i2, j1, j2) = code_5tuples[next_op]
  3997.                         field = self._FindField(i1)
  3998.                         if op == 'equal':
  3999.                             continue
  4000.                             continue
  4001.                         if op == 'replace':
  4002.                             dbg('setting sel_start to', i1)
  4003.                             sel_start = i1
  4004.                             break
  4005.                             continue
  4006.                         if op == 'insert' and not value[i1:i2]:
  4007.                             dbg('forward %s found' % op)
  4008.                             if prev[j1:j2].strip():
  4009.                                 dbg('item to insert non-empty; setting sel_start to', j1)
  4010.                                 sel_start = j1
  4011.                                 break
  4012.                             elif not (field._insertRight):
  4013.                                 dbg('setting sel_start to inserted space:', j1)
  4014.                                 sel_start = j1
  4015.                                 break
  4016.                             
  4017.                         prev[j1:j2].strip()
  4018.                         if op == 'delete' and field._insertRight and not value[i1:i2].lstrip():
  4019.                             continue
  4020.                             continue
  4021.                     
  4022.                 
  4023.                 if not diff_found:
  4024.                     dbg('no insert,delete or replace found (!)')
  4025.                     if i == j and j != sel_start:
  4026.                         sel_to = sel_start + (j - sel_start)
  4027.                     else:
  4028.                         sel_to = j
  4029.                 
  4030.             if (sel_start, sel_to) != self._prevSelection:
  4031.                 dbg('calculated selection', (sel_start, sel_to), "doesn't match previous", self._prevSelection)
  4032.                 (prev_sel_start, prev_sel_to) = self._prevSelection
  4033.                 field = self._FindField(sel_start)
  4034.                 if self._signOk and self._prevValue[sel_start] in ('-', '(', ')') or self._curValue[sel_start] in ('-', '(', ')'):
  4035.                     (sel_start, sel_to) = self._prevSelection
  4036.                 elif field._groupdigits and self._curValue[sel_start:sel_to] == field._groupChar or self._prevValue[sel_start:sel_to] == field._groupChar:
  4037.                     (sel_start, sel_to) = self._prevSelection
  4038.                 else:
  4039.                     calc_select_len = sel_to - sel_start
  4040.                     prev_select_len = prev_sel_to - prev_sel_start
  4041.                     dbg('sel_start == prev_sel_start', sel_start == prev_sel_start)
  4042.                     dbg('sel_to > prev_sel_to', sel_to > prev_sel_to)
  4043.                     if prev_select_len >= calc_select_len:
  4044.                         (sel_start, sel_to) = self._prevSelection
  4045.                     elif sel_to > prev_sel_to and prev_sel_to < len(self._template) and sel_to == len(self._template):
  4046.                         (i, j, k) = sm.find_longest_match(prev_sel_to, length, prev_sel_to, length)
  4047.                         dbg('i,j,k = ', (i, j, k), 'value[i:i+k] = "%s"' % value[i:i + k], 'prev[j:j+k] = "%s"' % prev[j:j + k])
  4048.                         if k > 0:
  4049.                             sel_to = j
  4050.                         
  4051.                     elif prev_sel_start == prev_sel_to:
  4052.                         calc_select_len = sel_to - sel_start
  4053.                         field = self._FindField(prev_sel_start)
  4054.                         if field._insertRight:
  4055.                             test_sel_start = prev_sel_start
  4056.                             test_sel_to = prev_sel_start + calc_select_len
  4057.                         else:
  4058.                             test_sel_start = prev_sel_start - calc_select_len
  4059.                             test_sel_to = prev_sel_start
  4060.                     else:
  4061.                         (test_sel_start, test_sel_to) = (prev_sel_start, prev_sel_to)
  4062.                     dbg('test selection:', (test_sel_start, test_sel_to))
  4063.                     dbg('calc change: "%s"' % self._prevValue[sel_start:sel_to])
  4064.                     dbg('test change: "%s"' % self._prevValue[test_sel_start:test_sel_to])
  4065.                     if sel_start != sel_to and test_sel_to < len(self._template) and self._prevValue[test_sel_start:test_sel_to] == self._prevValue[sel_start:sel_to]:
  4066.                         (sel_start, sel_to) = (test_sel_start, test_sel_to)
  4067.                     
  4068.             
  4069.             dbg('sel_start, sel_to:', sel_start, sel_to)
  4070.             dbg('previous value: "%s"' % self._prevValue)
  4071.             self._SetValue(self._prevValue)
  4072.             self._SetInsertionPoint(sel_start)
  4073.             self._SetSelection(sel_start, sel_to)
  4074.         else:
  4075.             dbg('no difference between previous value')
  4076.         dbg(indent = 0)
  4077.  
  4078.     
  4079.     def _OnClear(self, event):
  4080.         self.ClearValue()
  4081.  
  4082.     
  4083.     def _OnContextMenu(self, event):
  4084.         dbg('wxMaskedEditMixin::OnContextMenu()', indent = 1)
  4085.         menu = wxMenu()
  4086.         menu.Append(wxID_UNDO, 'Undo', '')
  4087.         menu.AppendSeparator()
  4088.         menu.Append(wxID_CUT, 'Cut', '')
  4089.         menu.Append(wxID_COPY, 'Copy', '')
  4090.         menu.Append(wxID_PASTE, 'Paste', '')
  4091.         menu.Append(wxID_CLEAR, 'Delete', '')
  4092.         menu.AppendSeparator()
  4093.         menu.Append(wxID_SELECTALL, 'Select All', '')
  4094.         EVT_MENU(menu, wxID_UNDO, self._OnCtrl_Z)
  4095.         EVT_MENU(menu, wxID_CUT, self._OnCtrl_X)
  4096.         EVT_MENU(menu, wxID_COPY, self._OnCtrl_C)
  4097.         EVT_MENU(menu, wxID_PASTE, self._OnCtrl_V)
  4098.         EVT_MENU(menu, wxID_CLEAR, self._OnClear)
  4099.         EVT_MENU(menu, wxID_SELECTALL, self._OnCtrl_A)
  4100.         EVT_UPDATE_UI(self, wxID_UNDO, self._UndoUpdateUI)
  4101.         self._contextMenu = menu
  4102.         self.PopupMenu(menu, event.GetPosition())
  4103.         menu.Destroy()
  4104.         self._contextMenu = None
  4105.         dbg(indent = 0)
  4106.  
  4107.     
  4108.     def _UndoUpdateUI(self, event):
  4109.         if self._prevValue is None or self._prevValue == self._curValue:
  4110.             self._contextMenu.Enable(wxID_UNDO, False)
  4111.         else:
  4112.             self._contextMenu.Enable(wxID_UNDO, True)
  4113.  
  4114.  
  4115.  
  4116. class wxMaskedTextCtrl(wxTextCtrl, wxMaskedEditMixin):
  4117.     
  4118.     def __init__(self, parent, id = -1, value = '', pos = wxDefaultPosition, size = wxDefaultSize, style = wxTE_PROCESS_TAB, validator = wxDefaultValidator, name = 'maskedTextCtrl', setupEventHandling = True, **kwargs):
  4119.         wxTextCtrl.__init__(self, parent, id, value = '', pos = pos, size = size, style = style, validator = validator, name = name)
  4120.         self.controlInitialized = True
  4121.         wxMaskedEditMixin.__init__(self, name, **kwargs)
  4122.         self._SetInitialValue(value)
  4123.         if setupEventHandling:
  4124.             EVT_SET_FOCUS(self, self._OnFocus)
  4125.             EVT_KILL_FOCUS(self, self._OnKillFocus)
  4126.             EVT_LEFT_DCLICK(self, self._OnDoubleClick)
  4127.             EVT_RIGHT_UP(self, self._OnContextMenu)
  4128.             EVT_KEY_DOWN(self, self._OnKeyDown)
  4129.             EVT_CHAR(self, self._OnChar)
  4130.             EVT_TEXT(self, self.GetId(), self._OnTextChange)
  4131.         
  4132.  
  4133.     
  4134.     def __repr__(self):
  4135.         return '<wxMaskedTextCtrl: %s>' % self.GetValue()
  4136.  
  4137.     
  4138.     def _GetSelection(self):
  4139.         return self.GetSelection()
  4140.  
  4141.     
  4142.     def _SetSelection(self, sel_start, sel_to):
  4143.         return self.SetSelection(sel_start, sel_to)
  4144.  
  4145.     
  4146.     def SetSelection(self, sel_start, sel_to):
  4147.         dbg('wxMaskedTextCtrl::SetSelection(%(sel_start)d, %(sel_to)d)' % locals())
  4148.         wxTextCtrl.SetSelection(self, sel_start, sel_to)
  4149.  
  4150.     
  4151.     def _GetInsertionPoint(self):
  4152.         return self.GetInsertionPoint()
  4153.  
  4154.     
  4155.     def _SetInsertionPoint(self, pos):
  4156.         self.SetInsertionPoint(pos)
  4157.  
  4158.     
  4159.     def SetInsertionPoint(self, pos):
  4160.         dbg('wxMaskedTextCtrl::SetInsertionPoint(%(pos)d)' % locals())
  4161.         wxTextCtrl.SetInsertionPoint(self, pos)
  4162.  
  4163.     
  4164.     def _GetValue(self):
  4165.         return self.GetValue()
  4166.  
  4167.     
  4168.     def _SetValue(self, value):
  4169.         dbg('wxMaskedTextCtrl::_SetValue("%(value)s")' % locals(), indent = 1)
  4170.         self._prevSelection = self._GetSelection()
  4171.         self._prevInsertionPoint = self._GetInsertionPoint()
  4172.         wxTextCtrl.SetValue(self, value)
  4173.         dbg(indent = 0)
  4174.  
  4175.     
  4176.     def SetValue(self, value):
  4177.         dbg('wxMaskedTextCtrl::SetValue = "%s"' % value, indent = 1)
  4178.         if not (self._mask):
  4179.             wxTextCtrl.SetValue(self, value)
  4180.             return None
  4181.         
  4182.         self._SetInsertionPoint(0)
  4183.         self._SetSelection(0, self._masklength)
  4184.         if self._signOk and self._useParens:
  4185.             signpos = value.find('-')
  4186.             if signpos != -1:
  4187.                 value = value[:signpos] + '(' + value[signpos + 1:].strip() + ')'
  4188.             elif value.find(')') == -1 and len(value) < self._masklength:
  4189.                 value += ' '
  4190.             
  4191.         
  4192.         if len(value) < self._masklength:
  4193.             if (self._isFloat or self._isInt) and self._ctrl_constraints._alignRight:
  4194.                 dbg('len(value)', len(value), ' < self._masklength', self._masklength)
  4195.                 value = self._template[0:self._masklength - len(value)] + value
  4196.                 if self._isFloat and value.find('.') == -1:
  4197.                     value = value[1:]
  4198.                 
  4199.                 dbg('padded value = "%s"' % value)
  4200.             
  4201.         
  4202.         try:
  4203.             value = self._Paste(value, raise_on_invalid = True, just_return_value = True)
  4204.             if self._isFloat:
  4205.                 self._isNeg = False
  4206.                 value = self._adjustFloat(value)
  4207.             elif self._isInt:
  4208.                 self._isNeg = False
  4209.                 value = self._adjustInt(value)
  4210.             elif self._isDate and not self.IsValid(value) and self._4digityear:
  4211.                 value = self._adjustDate(value, fixcentury = true)
  4212.         except ValueError:
  4213.             if self._isDate and self._4digityear:
  4214.                 dateparts = value.split(' ')
  4215.                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury = true)
  4216.                 value = string.join(dateparts, ' ')
  4217.                 dbg('adjusted value: "%s"' % value)
  4218.                 value = self._Paste(value, raise_on_invalid = True, just_return_value = True)
  4219.             else:
  4220.                 dbg('exception thrown', indent = 0)
  4221.                 raise 
  4222.         except:
  4223.             self._4digityear
  4224.  
  4225.         self._SetValue(value)
  4226.         wxCallAfter(self._SetInsertionPoint, self._masklength)
  4227.         wxCallAfter(self._SetSelection, self._masklength, self._masklength)
  4228.         dbg(indent = 0)
  4229.  
  4230.     
  4231.     def Clear(self):
  4232.         dbg('wxMaskedTextCtrl::Clear - value reset to default value (template)')
  4233.         if self._mask:
  4234.             self.ClearValue()
  4235.         else:
  4236.             wxTextCtrl.Clear(self)
  4237.  
  4238.     
  4239.     def _Refresh(self):
  4240.         dbg('wxMaskedTextCtrl::_Refresh', indent = 1)
  4241.         wxTextCtrl.Refresh(self)
  4242.         dbg(indent = 0)
  4243.  
  4244.     
  4245.     def Refresh(self):
  4246.         dbg('wxMaskedTextCtrl::Refresh', indent = 1)
  4247.         self._CheckValid()
  4248.         self._Refresh()
  4249.         dbg(indent = 0)
  4250.  
  4251.     
  4252.     def _IsEditable(self):
  4253.         return wxTextCtrl.IsEditable(self)
  4254.  
  4255.     
  4256.     def Cut(self):
  4257.         if self._mask:
  4258.             self._Cut()
  4259.         else:
  4260.             wxTextCtrl.Cut(self)
  4261.  
  4262.     
  4263.     def Paste(self):
  4264.         if self._mask:
  4265.             self._Paste()
  4266.         else:
  4267.             wxTextCtrl.Paste(self, value)
  4268.  
  4269.     
  4270.     def Undo(self):
  4271.         if self._mask:
  4272.             self._Undo()
  4273.         else:
  4274.             wxTextCtrl.Undo(self)
  4275.  
  4276.     
  4277.     def IsModified(self):
  4278.         if not wxTextCtrl.IsModified(self):
  4279.             pass
  4280.         return self.modified
  4281.  
  4282.     
  4283.     def _CalcSize(self, size = None):
  4284.         return self._calcSize(size)
  4285.  
  4286.  
  4287.  
  4288. class wxMaskedComboBoxSelectEvent(wxPyCommandEvent):
  4289.     
  4290.     def __init__(self, id, selection = 0, object = None):
  4291.         wxPyCommandEvent.__init__(self, wxEVT_COMMAND_COMBOBOX_SELECTED, id)
  4292.         self._wxMaskedComboBoxSelectEvent__selection = selection
  4293.         self.SetEventObject(object)
  4294.  
  4295.     
  4296.     def GetSelection(self):
  4297.         return self._wxMaskedComboBoxSelectEvent__selection
  4298.  
  4299.  
  4300.  
  4301. class wxMaskedComboBox(wxComboBox, wxMaskedEditMixin):
  4302.     
  4303.     def __init__(self, parent, id = -1, value = '', pos = wxDefaultPosition, size = wxDefaultSize, choices = [], style = wxCB_DROPDOWN, validator = wxDefaultValidator, name = 'maskedComboBox', setupEventHandling = True, **kwargs):
  4304.         self._wxMaskedComboBox__readonly = style & wxCB_READONLY == wxCB_READONLY
  4305.         kwargs['choices'] = choices
  4306.         if not kwargs.has_key('compareNoCase'):
  4307.             kwargs['compareNoCase'] = True
  4308.         
  4309.         wxMaskedEditMixin.__init__(self, name, **kwargs)
  4310.         self._choices = self._ctrl_constraints._choices
  4311.         dbg('self._choices:', self._choices)
  4312.         wxComboBox.__init__(self, parent, id, value = '', pos = pos, size = size, choices = choices, style = style | wxWANTS_CHARS, validator = validator, name = name)
  4313.         self.controlInitialized = True
  4314.         self._setFont()
  4315.         if value:
  4316.             if self._ctrl_constraints._alignRight:
  4317.                 value = value.rjust(self._masklength)
  4318.             else:
  4319.                 value = value.ljust(self._masklength)
  4320.         
  4321.         if self._wxMaskedComboBox__readonly:
  4322.             self.SetStringSelection(value)
  4323.         else:
  4324.             self._SetInitialValue(value)
  4325.         self._SetKeycodeHandler(WXK_UP, self.OnSelectChoice)
  4326.         self._SetKeycodeHandler(WXK_DOWN, self.OnSelectChoice)
  4327.         if setupEventHandling:
  4328.             EVT_SET_FOCUS(self, self._OnFocus)
  4329.             EVT_KILL_FOCUS(self, self._OnKillFocus)
  4330.             EVT_LEFT_DCLICK(self, self._OnDoubleClick)
  4331.             EVT_RIGHT_UP(self, self._OnContextMenu)
  4332.             EVT_CHAR(self, self._OnChar)
  4333.             EVT_KEY_DOWN(self, self.OnKeyDown)
  4334.             EVT_KEY_DOWN(self, self._OnKeyDown)
  4335.             EVT_TEXT(self, self.GetId(), self._OnTextChange)
  4336.         
  4337.  
  4338.     
  4339.     def __repr__(self):
  4340.         return '<wxMaskedComboBox: %s>' % self.GetValue()
  4341.  
  4342.     
  4343.     def _CalcSize(self, size = None):
  4344.         size = self._calcSize(size)
  4345.         return (size[0] + 20, size[1])
  4346.  
  4347.     
  4348.     def _GetSelection(self):
  4349.         return self.GetMark()
  4350.  
  4351.     
  4352.     def _SetSelection(self, sel_start, sel_to):
  4353.         return self.SetMark(sel_start, sel_to)
  4354.  
  4355.     
  4356.     def _GetInsertionPoint(self):
  4357.         return self.GetInsertionPoint()
  4358.  
  4359.     
  4360.     def _SetInsertionPoint(self, pos):
  4361.         self.SetInsertionPoint(pos)
  4362.  
  4363.     
  4364.     def _GetValue(self):
  4365.         return self.GetValue()
  4366.  
  4367.     
  4368.     def _SetValue(self, value):
  4369.         if self._ctrl_constraints._alignRight:
  4370.             value = value.rjust(self._masklength)
  4371.         else:
  4372.             value = value.ljust(self._masklength)
  4373.         self._prevSelection = self._GetSelection()
  4374.         self._prevInsertionPoint = self._GetInsertionPoint()
  4375.         wxComboBox.SetValue(self, value)
  4376.         self._CheckValid()
  4377.  
  4378.     
  4379.     def SetValue(self, value):
  4380.         if not (self._mask):
  4381.             wxComboBox.SetValue(value)
  4382.             return None
  4383.         
  4384.         self._SetInsertionPoint(0)
  4385.         self._SetSelection(0, self._masklength)
  4386.         if len(value) < self._masklength:
  4387.             if (self._isFloat or self._isInt) and self._ctrl_constraints._alignRight:
  4388.                 value = self._template[0:self._masklength - len(value)] + value
  4389.                 dbg('padded value = "%s"' % value)
  4390.             elif self._ctrl_constraints._alignRight:
  4391.                 value = value.rjust(self._masklength)
  4392.             else:
  4393.                 value = value.ljust(self._masklength)
  4394.         
  4395.         try:
  4396.             value = self._Paste(value, raise_on_invalid = True, just_return_value = True)
  4397.             if self._isFloat:
  4398.                 self._isNeg = False
  4399.                 value = self._adjustFloat(value)
  4400.             elif self._isInt:
  4401.                 self._isNeg = False
  4402.                 value = self._adjustInt(value)
  4403.             elif self._isDate and not self.IsValid(value) and self._4digityear:
  4404.                 value = self._adjustDate(value, fixcentury = true)
  4405.         except ValueError:
  4406.             if self._isDate and self._4digityear:
  4407.                 dateparts = value.split(' ')
  4408.                 dateparts[0] = self._adjustDate(dateparts[0], fixcentury = true)
  4409.                 value = string.join(dateparts, ' ')
  4410.                 dbg('adjusted value: "%s"' % value)
  4411.                 value = self._Paste(value, raise_on_invalid = True, just_return_value = True)
  4412.             else:
  4413.                 raise 
  4414.         except:
  4415.             self._4digityear
  4416.  
  4417.         self._SetValue(value)
  4418.         wxCallAfter(self._SetInsertionPoint, self._masklength)
  4419.         wxCallAfter(self._SetSelection, self._masklength, self._masklength)
  4420.  
  4421.     
  4422.     def _Refresh(self):
  4423.         wxComboBox.Refresh(self)
  4424.  
  4425.     
  4426.     def Refresh(self):
  4427.         self._CheckValid()
  4428.         self._Refresh()
  4429.  
  4430.     
  4431.     def _IsEditable(self):
  4432.         return not (self._wxMaskedComboBox__readonly)
  4433.  
  4434.     
  4435.     def Cut(self):
  4436.         if self._mask:
  4437.             self._Cut()
  4438.         else:
  4439.             wxComboBox.Cut(self)
  4440.  
  4441.     
  4442.     def Paste(self):
  4443.         if self._mask:
  4444.             self._Paste()
  4445.         else:
  4446.             wxComboBox.Paste(self)
  4447.  
  4448.     
  4449.     def Undo(self):
  4450.         if self._mask:
  4451.             self._Undo()
  4452.         else:
  4453.             wxComboBox.Undo()
  4454.  
  4455.     
  4456.     def Append(self, choice, clientData = None):
  4457.         if self._mask:
  4458.             if type(choice) not in (types.StringType, types.UnicodeType):
  4459.                 raise TypeError('%s: choices must be a sequence of strings' % str(self._index))
  4460.             elif not self.IsValid(choice):
  4461.                 raise ValueError('%s: "%s" is not a valid value for the control as specified.' % (str(self._index), choice))
  4462.             
  4463.             if not (self._ctrl_constraints._choices):
  4464.                 self._ctrl_constraints._compareChoices = []
  4465.                 self._ctrl_constraints._choices = []
  4466.                 self._hasList = True
  4467.             
  4468.             compareChoice = choice.strip()
  4469.             if self._ctrl_constraints._compareNoCase:
  4470.                 compareChoice = compareChoice.lower()
  4471.             
  4472.             if self._ctrl_constraints._alignRight:
  4473.                 choice = choice.rjust(self._masklength)
  4474.             else:
  4475.                 choice = choice.ljust(self._masklength)
  4476.             if self._ctrl_constraints._fillChar != ' ':
  4477.                 choice = choice.replace(' ', self._fillChar)
  4478.             
  4479.             dbg('updated choice:', choice)
  4480.             self._ctrl_constraints._compareChoices.append(compareChoice)
  4481.             self._ctrl_constraints._choices.append(choice)
  4482.             self._choices = self._ctrl_constraints._choices
  4483.             if not self.IsValid(choice) and not self._ctrl_constraints.IsEmpty(choice) and self._ctrl_constraints.IsEmpty(choice) and self._ctrl_constraints._validRequired:
  4484.                 raise ValueError('"%s" is not a valid value for the control "%s" as specified.' % (choice, self.name))
  4485.             
  4486.         
  4487.         wxComboBox.Append(self, choice, clientData)
  4488.  
  4489.     
  4490.     def Clear(self):
  4491.         if self._mask:
  4492.             self._choices = []
  4493.             self._ctrl_constraints._autoCompleteIndex = -1
  4494.             if self._ctrl_constraints._choices:
  4495.                 self.SetCtrlParameters(choices = [])
  4496.             
  4497.         
  4498.         wxComboBox.Clear(self)
  4499.  
  4500.     
  4501.     def SetCtrlParameters(self, **kwargs):
  4502.         wxMaskedEditMixin.SetCtrlParameters(self, **kwargs)
  4503.         if self.controlInitialized and kwargs.has_key('choices') or self._choices != self._ctrl_constraints._choices:
  4504.             wxComboBox.Clear(self)
  4505.             self._choices = self._ctrl_constraints._choices
  4506.             for choice in self._choices:
  4507.                 wxComboBox.Append(self, choice)
  4508.             
  4509.         
  4510.  
  4511.     
  4512.     def GetMark(self):
  4513.         dbg(suspend = 1)
  4514.         dbg('wxMaskedComboBox::GetMark', indent = 1)
  4515.         if self._wxMaskedComboBox__readonly:
  4516.             dbg(indent = 0)
  4517.             return (0, 0)
  4518.         
  4519.         sel_start = sel_to = self.GetInsertionPoint()
  4520.         dbg('current sel_start:', sel_start)
  4521.         value = self.GetValue()
  4522.         dbg('value: "%s"' % value)
  4523.         self._ignoreChange = True
  4524.         wxComboBox.Cut(self)
  4525.         newvalue = self.GetValue()
  4526.         dbg('value after Cut operation:', newvalue)
  4527.         if newvalue != value:
  4528.             dbg('something selected')
  4529.             sel_to = sel_start + len(value) - len(newvalue)
  4530.             wxComboBox.SetValue(self, value)
  4531.             wxComboBox.SetInsertionPoint(self, sel_start)
  4532.             wxComboBox.SetMark(self, sel_start, sel_to)
  4533.         
  4534.         self._ignoreChange = False
  4535.         dbg('computed selection:', sel_start, sel_to, indent = 0, suspend = 0)
  4536.         return (sel_start, sel_to)
  4537.  
  4538.     
  4539.     def SetSelection(self, index):
  4540.         dbg('wxMaskedComboBox::SetSelection(%d)' % index)
  4541.         if self._mask:
  4542.             self._prevValue = self._curValue
  4543.             self._curValue = self._choices[index]
  4544.             self._ctrl_constraints._autoCompleteIndex = index
  4545.         
  4546.         wxComboBox.SetSelection(self, index)
  4547.  
  4548.     
  4549.     def OnKeyDown(self, event):
  4550.         if event.GetKeyCode() in self._nav + self._control:
  4551.             self._OnChar(event)
  4552.             return None
  4553.         else:
  4554.             event.Skip()
  4555.  
  4556.     
  4557.     def OnSelectChoice(self, event):
  4558.         dbg('wxMaskedComboBox::OnSelectChoice', indent = 1)
  4559.         if not (self._mask):
  4560.             event.Skip()
  4561.             return None
  4562.         
  4563.         value = self.GetValue().strip()
  4564.         if self._ctrl_constraints._compareNoCase:
  4565.             value = value.lower()
  4566.         
  4567.         if event.GetKeyCode() == WXK_UP:
  4568.             direction = -1
  4569.         else:
  4570.             direction = 1
  4571.         (match_index, partial_match) = self._autoComplete(direction, self._ctrl_constraints._compareChoices, value, self._ctrl_constraints._compareNoCase, current_index = self._ctrl_constraints._autoCompleteIndex)
  4572.         if match_index is not None:
  4573.             dbg('setting selection to', match_index)
  4574.             self._OnAutoSelect(self._ctrl_constraints, match_index = match_index)
  4575.             self._CheckValid()
  4576.             keep_processing = False
  4577.         else:
  4578.             pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  4579.             field = self._FindField(pos)
  4580.             if self.IsEmpty() or not (field._hasList):
  4581.                 dbg('selecting 1st value in list')
  4582.                 self._OnAutoSelect(self._ctrl_constraints, match_index = 0)
  4583.                 self._CheckValid()
  4584.                 keep_processing = False
  4585.             else:
  4586.                 dbg(indent = 0)
  4587.                 keep_processing = self._OnAutoCompleteField(event)
  4588.         dbg('keep processing?', keep_processing, indent = 0)
  4589.         return keep_processing
  4590.  
  4591.     
  4592.     def _OnAutoSelect(self, field, match_index):
  4593.         dbg('wxMaskedComboBox::OnAutoSelect', field._index, indent = 1)
  4594.         if field == self._ctrl_constraints:
  4595.             self.SetSelection(match_index)
  4596.             dbg('issuing combo selection event')
  4597.             self.GetEventHandler().ProcessEvent(wxMaskedComboBoxSelectEvent(self.GetId(), match_index, self))
  4598.         
  4599.         self._CheckValid()
  4600.         dbg('field._autoCompleteIndex:', match_index)
  4601.         dbg('self.GetSelection():', self.GetSelection())
  4602.         dbg(indent = 0)
  4603.  
  4604.     
  4605.     def _OnReturn(self, event):
  4606.         dbg('wxMaskedComboBox::OnReturn', indent = 1)
  4607.         dbg('current value: "%s"' % self.GetValue(), 'current index:', self.GetSelection())
  4608.         if self.GetSelection() == -1 and self.GetValue().lower().strip() in self._ctrl_constraints._compareChoices:
  4609.             wxCallAfter(self.SetSelection, self._ctrl_constraints._autoCompleteIndex)
  4610.         
  4611.         event.m_keyCode = WXK_TAB
  4612.         event.Skip()
  4613.         dbg(indent = 0)
  4614.  
  4615.  
  4616.  
  4617. class wxIpAddrCtrl(wxMaskedTextCtrl):
  4618.     
  4619.     def __init__(self, parent, id = -1, value = '', pos = wxDefaultPosition, size = wxDefaultSize, style = wxTE_PROCESS_TAB, validator = wxDefaultValidator, name = 'wxIpAddrCtrl', setupEventHandling = True, **kwargs):
  4620.         if not kwargs.has_key('mask'):
  4621.             kwargs['mask'] = mask = '###.###.###.###'
  4622.         
  4623.         if not kwargs.has_key('formatcodes'):
  4624.             kwargs['formatcodes'] = 'F_Sr<'
  4625.         
  4626.         if not kwargs.has_key('validRegex'):
  4627.             kwargs['validRegex'] = '(  \\d| \\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))(\\.(  \\d| \\d\\d|(1\\d\\d|2[0-4]\\d|25[0-5]))){3}'
  4628.         
  4629.         wxMaskedTextCtrl.__init__(self, parent, id = id, value = value, pos = pos, size = size, style = style, validator = validator, name = name, setupEventHandling = setupEventHandling, **kwargs)
  4630.         field_params = { }
  4631.         field_params['validRegex'] = '(   |  \\d| \\d |\\d  | \\d\\d|\\d\\d |\\d \\d|(1\\d\\d|2[0-4]\\d|25[0-5]))'
  4632.         field_params['formatcodes'] = 'V'
  4633.         if field_params:
  4634.             for i in self._field_indices:
  4635.                 self.SetFieldParameters(i, **field_params)
  4636.             
  4637.         
  4638.         self._AddNavKey('.', handler = self.OnDot)
  4639.         self._AddNavKey('>', handler = self.OnDot)
  4640.  
  4641.     
  4642.     def OnDot(self, event):
  4643.         dbg('wxIpAddrCtrl::OnDot', indent = 1)
  4644.         pos = self._adjustPos(self._GetInsertionPoint(), event.GetKeyCode())
  4645.         oldvalue = self.GetValue()
  4646.         (edit_start, edit_end, slice) = self._FindFieldExtent(pos, getslice = True)
  4647.         if not event.ShiftDown():
  4648.             if pos > edit_start and pos < edit_end:
  4649.                 newvalue = oldvalue[:pos] + ' ' * (edit_end - pos) + oldvalue[edit_end:]
  4650.                 self._SetValue(newvalue)
  4651.                 self._SetInsertionPoint(pos)
  4652.             
  4653.         
  4654.         dbg(indent = 0)
  4655.         return self._OnChangeField(event)
  4656.  
  4657.     
  4658.     def GetAddress(self):
  4659.         value = wxMaskedTextCtrl.GetValue(self)
  4660.         return value.replace(' ', '')
  4661.  
  4662.     
  4663.     def _OnCtrl_S(self, event):
  4664.         dbg('wxIpAddrCtrl::_OnCtrl_S')
  4665.         if self._demo:
  4666.             print 'value:', self.GetAddress()
  4667.         
  4668.         return False
  4669.  
  4670.     
  4671.     def SetValue(self, value):
  4672.         dbg('wxIpAddrCtrl::SetValue(%s)' % str(value), indent = 1)
  4673.         if type(value) not in (types.StringType, types.UnicodeType):
  4674.             dbg(indent = 0)
  4675.             raise ValueError('%s must be a string', str(value))
  4676.         
  4677.         bValid = True
  4678.         parts = value.split('.')
  4679.         if len(parts) != 4:
  4680.             bValid = False
  4681.         else:
  4682.             for i in range(4):
  4683.                 part = parts[i]
  4684.                 if not None if len(part) <= len(part) else len(part) <= 3:
  4685.                     bValid = False
  4686.                     break
  4687.                     continue
  4688.                 if part.strip():
  4689.                     
  4690.                     try:
  4691.                         j = string.atoi(part)
  4692.                         if not None if j <= j else j <= 255:
  4693.                             bValid = False
  4694.                             break
  4695.                         else:
  4696.                             parts[i] = '%3d' % j
  4697.                     bValid = False
  4698.                     break
  4699.  
  4700.                     continue
  4701.                 parts[i] = '   '
  4702.             
  4703.         if not bValid:
  4704.             dbg(indent = 0)
  4705.             raise ValueError('value (%s) must be a string of form n.n.n.n where n is empty or in range 0-255' % str(value))
  4706.         else:
  4707.             dbg('parts:', parts)
  4708.             value = string.join(parts, '.')
  4709.             wxMaskedTextCtrl.SetValue(self, value)
  4710.         dbg(indent = 0)
  4711.  
  4712.  
  4713.  
  4714. def movetofloat(origvalue, fmtstring, neg, addseparators = False, sepchar = ',', fillchar = ' '):
  4715.     fmt0 = fmtstring.split('.')
  4716.     fmt1 = fmt0[0]
  4717.     fmt2 = fmt0[1]
  4718.     val = origvalue.split('.')[0].strip()
  4719.     ret = fillchar * (len(fmt1) - len(val)) + val + '.' + '0' * len(fmt2)
  4720.     if neg:
  4721.         ret = '-' + ret[1:]
  4722.     
  4723.     return (ret, len(fmt1))
  4724.  
  4725.  
  4726. def isDateType(fmtstring):
  4727.     dateMasks = ('^##/##/####', '^##-##-####', '^##.##.####', '^####/##/##', '^####-##-##', '^####.##.##', '^##/CCC/####', '^##.CCC.####', '^##/##/##$', '^##/##/## ', '^##/CCC/##$', '^##.CCC.## ')
  4728.     reString = '|'.join(dateMasks)
  4729.     filter = re.compile(reString)
  4730.     if re.match(filter, fmtstring):
  4731.         return True
  4732.     
  4733.     return False
  4734.  
  4735.  
  4736. def isTimeType(fmtstring):
  4737.     reTimeMask = '^##:##(:##)?( (AM|PM))?'
  4738.     filter = re.compile(reTimeMask)
  4739.     if re.match(filter, fmtstring):
  4740.         return True
  4741.     
  4742.     return False
  4743.  
  4744.  
  4745. def isFloatingPoint(fmtstring):
  4746.     filter = re.compile('[ ]?[#]+\\.[#]+\n')
  4747.     if re.match(filter, fmtstring + '\n'):
  4748.         return True
  4749.     
  4750.     return False
  4751.  
  4752.  
  4753. def isInteger(fmtstring):
  4754.     filter = re.compile('[#]+\n')
  4755.     if re.match(filter, fmtstring + '\n'):
  4756.         return True
  4757.     
  4758.     return False
  4759.  
  4760.  
  4761. def getDateParts(dateStr, dateFmt):
  4762.     if len(dateStr) > 11:
  4763.         clip = dateStr[0:11]
  4764.     else:
  4765.         clip = dateStr
  4766.     if clip[-2] not in string.digits:
  4767.         clip = clip[:-1]
  4768.     
  4769.     dateSep = ('/' in clip) * '/' + ('-' in clip) * '-' + ('.' in clip) * '.'
  4770.     slices = clip.split(dateSep)
  4771.     if dateFmt == 'MDY':
  4772.         (y, m, d) = (slices[2], slices[0], slices[1])
  4773.     elif dateFmt == 'DMY':
  4774.         (y, m, d) = (slices[2], slices[1], slices[0])
  4775.     elif dateFmt == 'YMD':
  4776.         (y, m, d) = (slices[0], slices[1], slices[2])
  4777.     else:
  4778.         (y, m, d) = (None, None, None)
  4779.     if not y:
  4780.         return None
  4781.     else:
  4782.         return (y, m, d)
  4783.  
  4784.  
  4785. def getDateSepChar(dateStr):
  4786.     clip = dateStr[0:10]
  4787.     dateSep = ('/' in clip) * '/' + ('-' in clip) * '-' + ('.' in clip) * '.'
  4788.     return dateSep
  4789.  
  4790.  
  4791. def makeDate(year, month, day, dateFmt, dateStr):
  4792.     sep = getDateSepChar(dateStr)
  4793.     if dateFmt == 'MDY':
  4794.         return '%s%s%s%s%s' % (month, sep, day, sep, year)
  4795.     elif dateFmt == 'DMY':
  4796.         return '%s%s%s%s%s' % (day, sep, month, sep, year)
  4797.     elif dateFmt == 'YMD':
  4798.         return '%s%s%s%s%s' % (year, sep, month, sep, day)
  4799.     else:
  4800.         return none
  4801.  
  4802.  
  4803. def getYear(dateStr, dateFmt):
  4804.     parts = getDateParts(dateStr, dateFmt)
  4805.     return parts[0]
  4806.  
  4807.  
  4808. def getMonth(dateStr, dateFmt):
  4809.     parts = getDateParts(dateStr, dateFmt)
  4810.     return parts[1]
  4811.  
  4812.  
  4813. def getDay(dateStr, dateFmt):
  4814.     parts = getDateParts(dateStr, dateFmt)
  4815.     return parts[2]
  4816.  
  4817.  
  4818. class test(wxPySimpleApp):
  4819.     
  4820.     def OnInit(self):
  4821.         RowColSizer = RowColSizer
  4822.         import wxPython.lib.rcsizer
  4823.         self.frame = wxFrame(NULL, -1, 'wxMaskedEditMixin 0.0.7 Demo Page #1', size = (700, 600))
  4824.         self.panel = wxPanel(self.frame, -1)
  4825.         self.sizer = RowColSizer()
  4826.         self.labels = []
  4827.         self.editList = []
  4828.         rowcount = 4
  4829.         (id, id1) = (wxNewId(), wxNewId())
  4830.         self.command1 = wxButton(self.panel, id, '&Close')
  4831.         self.command2 = wxButton(self.panel, id1, '&AutoFormats')
  4832.         self.sizer.Add(self.command1, row = 0, col = 0, flag = wxALL, border = 5)
  4833.         self.sizer.Add(self.command2, row = 0, col = 1, colspan = 2, flag = wxALL, border = 5)
  4834.         EVT_BUTTON(self.panel, id, self.onClick)
  4835.         EVT_BUTTON(self.panel, id1, self.onClickPage)
  4836.         self.check1 = wxCheckBox(self.panel, -1, 'Disallow Empty')
  4837.         self.check2 = wxCheckBox(self.panel, -1, 'Highlight Empty')
  4838.         self.sizer.Add(self.check1, row = 0, col = 3, flag = wxALL, border = 5)
  4839.         self.sizer.Add(self.check2, row = 0, col = 4, flag = wxALL, border = 5)
  4840.         EVT_CHECKBOX(self.panel, self.check1.GetId(), self._onCheck1)
  4841.         EVT_CHECKBOX(self.panel, self.check2.GetId(), self._onCheck2)
  4842.         label = 'Press ctrl-s in any field to output the value and plain value. Press ctrl-x to clear and re-set any field.\nNote that all controls have been auto-sized by including F in the format code.\nTry entering nonsensical or partial values in validated fields to see what happens (use ctrl-s to test the valid status).'
  4843.         label2 = '\nNote that the State and Last Name fields are list-limited (Name:Smith,Jones,Williams).'
  4844.         self.label1 = wxStaticText(self.panel, -1, label)
  4845.         self.label2 = wxStaticText(self.panel, -1, 'Description')
  4846.         self.label3 = wxStaticText(self.panel, -1, 'Mask Value')
  4847.         self.label4 = wxStaticText(self.panel, -1, 'Format')
  4848.         self.label5 = wxStaticText(self.panel, -1, 'Reg Expr Val. (opt)')
  4849.         self.label6 = wxStaticText(self.panel, -1, 'wxMaskedEdit Ctrl')
  4850.         self.label7 = wxStaticText(self.panel, -1, label2)
  4851.         self.label7.SetForegroundColour('Blue')
  4852.         self.label1.SetForegroundColour('Blue')
  4853.         self.label2.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4854.         self.label3.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4855.         self.label4.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4856.         self.label5.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4857.         self.label6.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4858.         self.sizer.Add(self.label1, row = 1, col = 0, colspan = 7, flag = wxALL, border = 5)
  4859.         self.sizer.Add(self.label7, row = 2, col = 0, colspan = 7, flag = wxALL, border = 5)
  4860.         self.sizer.Add(self.label2, row = 3, col = 0, flag = wxALL, border = 5)
  4861.         self.sizer.Add(self.label3, row = 3, col = 1, flag = wxALL, border = 5)
  4862.         self.sizer.Add(self.label4, row = 3, col = 2, flag = wxALL, border = 5)
  4863.         self.sizer.Add(self.label5, row = 3, col = 3, flag = wxALL, border = 5)
  4864.         self.sizer.Add(self.label6, row = 3, col = 4, flag = wxALL, border = 5)
  4865.         controls = [
  4866.             ('Phone No', '(###) ###-#### x:###', '', 'F!^-R', '^\\(\\d\\d\\d\\) \\d\\d\\d-\\d\\d\\d\\d', (), [], ''),
  4867.             ('Last Name Only', 'C{14}', '', 'F {list}', '^[A-Z][a-zA-Z]+', (), ('Smith', 'Jones', 'Williams'), ''),
  4868.             ('Full Name', 'C{14}', '', 'F_', '^[A-Z][a-zA-Z]+ [A-Z][a-zA-Z]+', (), [], ''),
  4869.             ('Social Sec#', '###-##-####', '', 'F', '\\d{3}-\\d{2}-\\d{4}', (), [], ''),
  4870.             ('U.S. Zip+4', '#{5}-#{4}', '', 'F', '\\d{5}-(\\s{4}|\\d{4})', (), [], ''),
  4871.             ('U.S. State (2 char)\n(with default)', 'AA', '', 'F!', '[A-Z]{2}', (), states, 'AZ'),
  4872.             ('Customer No', '\\CAA-###', '', 'F!', 'C[A-Z]{2}-\\d{3}', (), [], ''),
  4873.             ('Date (MDY) + Time\n(with default)', '##/##/#### ##:## AM', 'BCDEFGHIJKLMNOQRSTUVWXYZ', 'DFR!', '', (), [], '03/05/2003 12:00 AM'),
  4874.             ('Invoice Total', '#{9}.##', '', 'F-R,', '', (), [], ''),
  4875.             ('Integer (signed)\n(with default)', '#{6}', '', 'F-R', '', (), [], '0     '),
  4876.             ('Integer (unsigned)\n(with default), 1-399', '######', '', 'F', '', (1, 399), [], '1     '),
  4877.             ('Month selector', 'XXX', '', 'F', '', (), [
  4878.                 'Jan',
  4879.                 'Feb',
  4880.                 'Mar',
  4881.                 'Apr',
  4882.                 'May',
  4883.                 'Jun',
  4884.                 'Jul',
  4885.                 'Aug',
  4886.                 'Sep',
  4887.                 'Oct',
  4888.                 'Nov',
  4889.                 'Dec'], ''),
  4890.             ('fraction selector', '#/##', '', 'F', '^\\d\\/\\d\\d?', (), [
  4891.                 '2/3',
  4892.                 '3/4',
  4893.                 '1/2',
  4894.                 '1/4',
  4895.                 '1/8',
  4896.                 '1/16',
  4897.                 '1/32',
  4898.                 '1/64'], '')]
  4899.         for control in controls:
  4900.             self.sizer.Add(wxStaticText(self.panel, -1, control[0]), row = rowcount, col = 0, border = 5, flag = wxALL)
  4901.             self.sizer.Add(wxStaticText(self.panel, -1, control[1]), row = rowcount, col = 1, border = 5, flag = wxALL)
  4902.             self.sizer.Add(wxStaticText(self.panel, -1, control[3]), row = rowcount, col = 2, border = 5, flag = wxALL)
  4903.             self.sizer.Add(wxStaticText(self.panel, -1, control[4][:20]), row = rowcount, col = 3, border = 5, flag = wxALL)
  4904.             if control in controls[:]:
  4905.                 newControl = wxMaskedTextCtrl(self.panel, -1, '', mask = control[1], excludeChars = control[2], formatcodes = control[3], includeChars = '', validRegex = control[4], validRange = control[5], choices = control[6], defaultValue = control[7], demo = True)
  4906.                 if control[6]:
  4907.                     newControl.SetCtrlParameters(choiceRequired = True)
  4908.                 
  4909.             else:
  4910.                 newControl = wxMaskedComboBox(self.panel, -1, '', choices = control[7], choiceRequired = True, mask = control[1], formatcodes = control[3], excludeChars = control[2], includeChars = '', validRegex = control[4], validRange = control[5], demo = True)
  4911.             self.editList.append(newControl)
  4912.             self.sizer.Add(newControl, row = rowcount, col = 4, flag = wxALL, border = 5)
  4913.             rowcount += 1
  4914.         
  4915.         self.sizer.AddGrowableCol(4)
  4916.         self.panel.SetSizer(self.sizer)
  4917.         self.panel.SetAutoLayout(1)
  4918.         self.frame.Show(1)
  4919.         self.MainLoop()
  4920.         return True
  4921.  
  4922.     
  4923.     def onClick(self, event):
  4924.         self.frame.Close()
  4925.  
  4926.     
  4927.     def onClickPage(self, event):
  4928.         self.page2 = test2(self.frame, -1, '')
  4929.         self.page2.Show(True)
  4930.  
  4931.     
  4932.     def _onCheck1(self, event):
  4933.         value = event.Checked()
  4934.         if value:
  4935.             for control in self.editList:
  4936.                 control.SetCtrlParameters(emptyInvalid = True)
  4937.                 control.Refresh()
  4938.             
  4939.         else:
  4940.             for control in self.editList:
  4941.                 control.SetCtrlParameters(emptyInvalid = False)
  4942.                 control.Refresh()
  4943.             
  4944.         self.panel.Refresh()
  4945.  
  4946.     
  4947.     def _onCheck2(self, event):
  4948.         value = event.Checked()
  4949.         if value:
  4950.             for control in self.editList:
  4951.                 control.SetCtrlParameters(emptyBackgroundColour = 'Aquamarine')
  4952.                 control.Refresh()
  4953.             
  4954.         else:
  4955.             for control in self.editList:
  4956.                 control.SetCtrlParameters(emptyBackgroundColour = 'White')
  4957.                 control.Refresh()
  4958.             
  4959.         self.panel.Refresh()
  4960.  
  4961.  
  4962.  
  4963. class test2(wxFrame):
  4964.     
  4965.     def __init__(self, parent, id, caption):
  4966.         wxFrame.__init__(self, parent, id, 'wxMaskedEdit control 0.0.7 Demo Page #2 -- AutoFormats', size = (550, 600))
  4967.         RowColSizer = RowColSizer
  4968.         import wxPython.lib.rcsizer
  4969.         self.panel = wxPanel(self, -1)
  4970.         self.sizer = RowColSizer()
  4971.         self.labels = []
  4972.         self.texts = []
  4973.         rowcount = 4
  4974.         label = 'All these controls have been created by passing a single parameter, the AutoFormat code.\nThe class contains an internal dictionary of types and formats (autoformats).\nTo see a great example of validations in action, try entering a bad email address, then tab out.'
  4975.         self.label1 = wxStaticText(self.panel, -1, label)
  4976.         self.label2 = wxStaticText(self.panel, -1, 'Description')
  4977.         self.label3 = wxStaticText(self.panel, -1, 'AutoFormat Code')
  4978.         self.label4 = wxStaticText(self.panel, -1, 'wxMaskedEdit Control')
  4979.         self.label1.SetForegroundColour('Blue')
  4980.         self.label2.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4981.         self.label3.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4982.         self.label4.SetFont(wxFont(9, wxSWISS, wxNORMAL, wxBOLD))
  4983.         self.sizer.Add(self.label1, row = 1, col = 0, colspan = 3, flag = wxALL, border = 5)
  4984.         self.sizer.Add(self.label2, row = 3, col = 0, flag = wxALL, border = 5)
  4985.         self.sizer.Add(self.label3, row = 3, col = 1, flag = wxALL, border = 5)
  4986.         self.sizer.Add(self.label4, row = 3, col = 2, flag = wxALL, border = 5)
  4987.         (id, id1) = (wxNewId(), wxNewId())
  4988.         self.command1 = wxButton(self.panel, id, '&Close')
  4989.         self.command2 = wxButton(self.panel, id1, '&Print Formats')
  4990.         EVT_BUTTON(self.panel, id, self.onClick)
  4991.         self.panel.SetDefaultItem(self.command1)
  4992.         EVT_BUTTON(self.panel, id1, self.onClickPrint)
  4993.         controls = [
  4994.             ('Phone No', 'USPHONEFULLEXT'),
  4995.             ('US Date + Time', 'USDATETIMEMMDDYYYY/HHMM'),
  4996.             ('US Date MMDDYYYY', 'USDATEMMDDYYYY/'),
  4997.             ('Time (with seconds)', 'TIMEHHMMSS'),
  4998.             ('Military Time\n(without seconds)', 'MILTIMEHHMM'),
  4999.             ('Social Sec#', 'USSOCIALSEC'),
  5000.             ('Credit Card', 'CREDITCARD'),
  5001.             ('Expiration MM/YY', 'EXPDATEMMYY'),
  5002.             ('Percentage', 'PERCENT'),
  5003.             ("Person's Age", 'AGE'),
  5004.             ('US Zip Code', 'USZIP'),
  5005.             ('US Zip+4', 'USZIPPLUS4'),
  5006.             ('Email Address', 'EMAIL'),
  5007.             ('IP Address', '(derived control wxIpAddrCtrl)')]
  5008.         for control in controls:
  5009.             self.sizer.Add(wxStaticText(self.panel, -1, control[0]), row = rowcount, col = 0, border = 5, flag = wxALL)
  5010.             self.sizer.Add(wxStaticText(self.panel, -1, control[1]), row = rowcount, col = 1, border = 5, flag = wxALL)
  5011.             if control in controls[:-1]:
  5012.                 self.sizer.Add(wxMaskedTextCtrl(self.panel, -1, '', autoformat = control[1], demo = True), row = rowcount, col = 2, flag = wxALL, border = 5)
  5013.             else:
  5014.                 self.sizer.Add(wxIpAddrCtrl(self.panel, -1, '', demo = True), row = rowcount, col = 2, flag = wxALL, border = 5)
  5015.             rowcount += 1
  5016.         
  5017.         self.sizer.Add(self.command1, row = 0, col = 0, flag = wxALL, border = 5)
  5018.         self.sizer.Add(self.command2, row = 0, col = 1, flag = wxALL, border = 5)
  5019.         self.sizer.AddGrowableCol(3)
  5020.         self.panel.SetSizer(self.sizer)
  5021.         self.panel.SetAutoLayout(1)
  5022.  
  5023.     
  5024.     def onClick(self, event):
  5025.         self.Close()
  5026.  
  5027.     
  5028.     def onClickPrint(self, event):
  5029.         for format in masktags.keys():
  5030.             sep = '+------------------------+'
  5031.             print '%s\n%s  \n  Mask: %s \n  RE Validation string: %s\n' % (sep, format, masktags[format]['mask'], masktags[format]['validRegex'])
  5032.         
  5033.  
  5034.  
  5035. if __name__ == '__main__':
  5036.     app = test()
  5037.  
  5038. i = 1
  5039.