Matt Maycock
11/11/2006 7:27:00 PM
actually, update ~ line 154 from
Tk.callback_break unless key =~ /^(\d|\w)$/
to
Tk.callback_break unless removal_keys.include?(key) || key =~ /^(\d|\w)$/
(Sorry, I just added the functionality of dealing with backspace /
delete correctly, and forgot that part).
~Matthew Maycock!
On 11/11/06, Matt Maycock <ummaycoc@gmail.com> wrote:
> Hi.
>
> I wrote the following to make some of my input validation from
> TkEntry's a bit easier. I thought some others might be interested
> (still has some parts that could use work)
>
>
> #
> # Usage:
> # num = TkEntry.new(...)
> # num.filter.integer.nosign # 123, 625, 86434, etc
> #
> # year = TkEntry.new(...)
> # year.filter.integer.nosign.left_digit(4) # any number up to four
> digits - no sign
> #
> # float = TkEntry.new(...)
> # float.filter.real.right_digit(3) # any real number with at most 3
> places past the decimal
> #
> # month = TkEntry.new(...)
> # month.filter.month.letters(3) # A month - can be either 1-12 or
> names (up to 3 letters)
> #
>
> require 'tk'
>
> class TkEntry
> def acceptable_keys
> # This part is probably what needs more info in it...
> %w{Tab ISO_Left_Tab Left Right Shift Shift_L Shift_R}
> end
>
> def removal_keys
> %w{BackSpace Delete}
> end
>
> def month_names
> %w{january february march april may june july august september
> october november december}
> end
>
> def generic_remove(key)
> s = self.value.dup
> if self.selection_present then
> s[(self.index('sel.first'))...(self.index('sel.last'))] = ""
> elsif key == "BackSpace" && self.cursor > 0 then
> s[self.cursor - 1, 1] = ""
> elsif key == "Delete" && self.cursor < s.length
> s[self.cursor, 1] = ""
> end
> s
> end
>
> def generic_insert(key)
> return generic_remove(key) if removal_keys.include?(key)
> s = self.value.dup
> if self.selection_present then
> s[(self.index('sel.first'))...(self.index('sel.last'))] = key
> else
> s.insert(self.cursor, key)
> end
> s
> end
>
> def generic_number_insert(key)
> return generic_remove(key) if removal_keys.include?(key)
> s = self.value.dup
> if key =~ /^\d$/ then
> if self.selection_present then
> s[(self.index('sel.first'))...(self.index('sel.last'))] = key
> else
> s.insert(self.cursor, key)
> end
> elsif %w{minus plus}.include?(key) then
> v = {'minus' => '-', 'plus' => '+'}[key]
> if self.selection_present then
> s[(self.index('sel.first'))...(self.index('sel.last'))] = v
> else
> s.insert(self.cursor, v)
> end
> elsif key == 'period'
> if self.selection_present then
> s[(self.index('sel.first'))...(self.index('sel.last'))] = '.'
> else
> s.insert(self.cursor, '.')
> end
> else
> Tk.callback_break # Not something to be used with numbers?
> end
> s
> end
>
> def filter
> me, f = self, Object.new
>
> f.instance_eval {
> @filter_target = me;
> }
>
> def f.method_missing(sym, *args, &block)
> if [:positive, :negative, :nosign, :integer, :real, :left_digit,
> :right_digit, :range, :month, :letters, :alphanumeric].include?(sym)
> then
> @filter_target.key_filter([sym, *args])
> self
> else
> super(sym, *args, &block)
> end
> end
>
> f
> end
>
> def key_filter_proc(type, *args)
> case type
> when :positive
> Proc.new {|key|
> next if self.acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /^.+\+/ || s =~ /\+.*\+/
> Tk.callback_break if s =~ /-/
> Tk.callback_break if s =~ /^\+?0/
> }
> when :negative
> Proc.new {|key|
> next if self.acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /^.+-/ || s =~ /-.*-/
> Tk.callback_break if s =~ /\+/
> Tk.callback_break unless s.length == 0 || s =~ /^-/
> Tk.callback_break if s =~ /^-0/
> }
> when :nosign
> Proc.new {|key|
> next if self.acceptable_keys.include?(key)
> Tk.callback_break if %w{plus minus}.include?(key)
> }
> when :integer
> Proc.new {|key|
> next if self.acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /^(-|\+)?0\d/
> Tk.callback_break if s =~ /^.+(-|\+)/
> Tk.callback_break if s =~ /\./
> }
> when :real
> Proc.new {|key|
> next if self.acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /^(-|\+)?0\d/
> Tk.callback_break if s =~ /^.+(-|\+)/
> Tk.callback_break if s.scan(/\./).length > 1
> }
> when :left_digit
> raise(ArgumentError, "Need a number of digits to allow on the
> left of the decimal") if args.empty?
> n = args[0].to_i
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /^(-|\+)?(\d*)/ && $2.length > n
> }
> when :right_digit
> raise(ArgumentError, "Need a number of digits to allow on the
> left of the decimal") if args.empty?
> n = args[0].to_i
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s =~ /\.(\d+)$/ && $1.length > n
> }
> when :range
> raise(ArgumentError, "Need a range to test against") if args.length < 1
> r = (args[0].to_i)..(args[1].to_i)
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> s = self.generic_number_insert(key)
> Tk.callback_break if s.to_f < r.begin || s.to_f > r.end
> }
> when :month
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> Tk.callback_break unless key =~ /^(\d|\w)$/
> s = self.generic_insert(key)
> Tk.callback_break if s =~ /\d/ && s =~ /[A-Za-z]/
>
> if s =~ /^\d+$/ then
> Tk.callback_break unless (1..12).include?(s.to_i)
> else
> match = Regexp.compile("^#{s}", true)
> Tk.callback_break unless self.month_names.any? {|i| i =~ match}
> end
> }
> when :letters
> if args.empty? then
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> Tk.callback_break unless key =~ /^[A-Za-z]$/
> }
> else
> n = args[0].to_i
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> Tk.callback_break unless key =~ /^[A-Za-z]$/ &&
> generic_insert(key).length <= n
> }
> end
> when :alphanumeric
> if args.empty? then
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> Tk.callback_break unless key =~ /^\w$/
> }
> else
> Proc.new {|key|
> next if acceptable_keys.include?(key)
> next if key =~ /^\w$/
> next if args.include?(key)
> Tk.callback_break
> }
> end
> else
> raise(ArgumentError, "Could not determine type of filter.")
> end
> end
>
>
> def key_filter(*constraints)
> constraints.each {|c|
> self.bind_append('Key', case c
> # Allow only positive numbers (but only checks that we have
> only plus sign and only one in the front
> when Symbol then
> key_filter_proc(c)
> when Array # Special Cases
> raise(ArgumentError, "Array based filters cannot be empty.")
> if c.empty?
> raise(ArgumentError, "Array based filters must start with a
> symbol.") unless Symbol === c[0]
> key_filter_proc(*c)
> end, '%K')
> }
> end
> end
>
> def Tk.datetime_filter
> [fyr, fdy, fhr, fmi, fsc].each {|i|
> i.key_filter(:integer, :nosign)
> }
> fyr.key_filter([:left_digit, 4])
> fmo.key_filter(:month, [:letters, 3])
> fdy.key_filter([:range, 1, 31])
> fhr.key_filter([:range, 0, 23])
> [fmi, fsc].each {|i|
> i.key_filter([:range, 0, 59])
> }
> nil
> end
>
--
There's no word in the English language for what you do to a dead
thing to make it stop chasing you.