File Coverage

blib/lib/Tk/MouseGesture.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1              
2             package Tk::MouseGesture;
3              
4 1     1   6834 use Carp;
  1         3  
  1         84  
5 1     1   6 use strict;
  1         3  
  1         50  
6 1     1   1598 use Tk;
  0            
  0            
7              
8             use vars qw/$VERSION/;
9             $VERSION = 0.03;
10              
11             Construct Tk::Widget 'MouseGesture';
12              
13             # the following hash defines the set of predefined gestures.
14             my %gestures = (
15             'B1-left' => \&_ges_b1_left,
16             'B2-left' => \&_ges_b2_left,
17             'B3-left' => \&_ges_b3_left,
18              
19             'B1-right' => \&_ges_b1_right,
20             'B2-right' => \&_ges_b2_right,
21             'B3-right' => \&_ges_b3_right,
22              
23             'B1-up' => \&_ges_b1_up,
24             'B2-up' => \&_ges_b2_up,
25             'B3-up' => \&_ges_b3_up,
26              
27             'B1-down' => \&_ges_b1_down,
28             'B2-down' => \&_ges_b2_down,
29             'B3-down' => \&_ges_b3_down,
30              
31             'B1-diag-UL' => \&_ges_b1_diag_ul,
32             'B2-diag-UL' => \&_ges_b2_diag_ul,
33             'B3-diag-UL' => \&_ges_b3_diag_ul,
34              
35             'B1-diag-UR' => \&_ges_b1_diag_ur,
36             'B2-diag-UR' => \&_ges_b2_diag_ur,
37             'B3-diag-UR' => \&_ges_b3_diag_ur,
38              
39             'B1-diag-LL' => \&_ges_b1_diag_ll,
40             'B2-diag-LL' => \&_ges_b2_diag_ll,
41             'B3-diag-LL' => \&_ges_b3_diag_ll,
42              
43             'B1-diag-LR' => \&_ges_b1_diag_lr,
44             'B2-diag-LR' => \&_ges_b2_diag_lr,
45             'B3-diag-LR' => \&_ges_b3_diag_lr,
46             );
47              
48             my @objects;
49              
50             sub new {
51             my ($class, $parent, $gesture, %args) = @_;
52              
53             # make sure the parent is a toplevel.
54             unless ($parent->isa('Tk::Toplevel')) {
55             #carp "Parent of $class must be a toplevel widget!";
56             # return undef;
57              
58             # get the parent.
59             $parent = $parent->toplevel;
60             }
61              
62             # make sure the gesture exists and is one that is known.
63             unless ($gesture) {
64             carp "Wrong arguments. Must be MouseGesture(gesture_name, callback)";
65             return undef;
66             }
67              
68             unless (exists $gestures{$gesture}) {
69             carp "Unknown mouse gesture '$gesture'!";
70             return undef;
71             }
72              
73             my $obj = bless {
74             PARENT => $parent,
75             XRES => $args{-xres} || 20,
76             YRES => $args{-yres} || 20,
77             SUB => $args{-command} || sub {},
78             MIN => $args{-min} || 50,
79             EN => 1,
80             } => $class;
81              
82             $obj->addGesture($gesture);
83              
84             push @objects => $obj;
85              
86             return $obj;
87             }
88              
89             sub disable { $_[0]{EN} = 0 }
90             sub enable { $_[0]{EN} = 1 }
91              
92             sub disableAll { $_->disable for @objects }
93             sub enableAll { $_->enable for @objects }
94              
95             sub addGesture {
96             my ($self, $gesture) = @_;
97              
98             # make sure the gesture is one that is known.
99             unless (exists $gestures{$gesture}) {
100             carp "Unknown mouse gesture '$gesture'!";
101             return undef;
102             }
103              
104             $gestures{$gesture}->($self);
105             }
106              
107             sub command {
108             my ($self, $sub) = @_;
109              
110             $self->{SUB} = $sub if $sub;
111              
112             return $self->{SUB};
113             }
114              
115             sub _ges_b1_left { _generic_straight(1, -1, 0, @_) }
116             sub _ges_b2_left { _generic_straight(2, -1, 0, @_) }
117             sub _ges_b3_left { _generic_straight(3, -1, 0, @_) }
118              
119             sub _ges_b1_right { _generic_straight(1, 1, 0, @_) }
120             sub _ges_b2_right { _generic_straight(2, 1, 0, @_) }
121             sub _ges_b3_right { _generic_straight(3, 1, 0, @_) }
122              
123             sub _ges_b1_up { _generic_straight(1, 0, -1, @_) }
124             sub _ges_b2_up { _generic_straight(2, 0, -1, @_) }
125             sub _ges_b3_up { _generic_straight(3, 0, -1, @_) }
126              
127             sub _ges_b1_down { _generic_straight(1, 0, 1, @_) }
128             sub _ges_b2_down { _generic_straight(2, 0, 1, @_) }
129             sub _ges_b3_down { _generic_straight(3, 0, 1, @_) }
130              
131             sub _generic_straight {
132             # arguments are:
133             # 1. button number.
134             # 2. horizontal-sensitivity: if 0 => vertical gesture
135             # 1 => right
136             # -1 => left
137             # 3. veritcal -sensitivity: if 0 => horizontal gesture
138             # 1 => bottom
139             # -1 => top
140             # 4. self.
141              
142             my ($b, $X, $Y, $self) = @_;
143              
144             my $p = $self->{PARENT};
145             my $xres = $self->{XRES};
146             my $yres = $self->{YRES};
147             my $min = $self->{MIN};
148             my $cb = Tk::Callback->new($self->{SUB});
149              
150             my ($x, $y, $xc, $yc, $within);
151              
152             # make sure any other bindings are preserved.
153             my $old1 = $p->bind("<$b>");
154             my $old2 = $p->bind("");
155             my $old3 = $p->bind("");
156              
157             $p->bind("<$b>" => sub {
158             $old1 && $old1->Call;
159             return unless $self->{EN};
160              
161             $within = 1;
162             ($x, $y) = $p->pointerxy;
163             ($xc, $yc) = ($x, $y);
164             });
165             $p->bind("" => sub {
166             $old2 && $old2->Call;
167             return unless $self->{EN};
168             return unless $within;
169              
170             my ($nx, $ny) = $p->pointerxy;
171              
172             if ($Y) {
173             if ($Y > 0) {
174             $within = 0 if $ny < $yc;
175             } else {
176             $within = 0 if $ny > $yc;
177             }
178             } else {
179             $within = 0 if abs($ny - $y) > $yres;
180             }
181              
182             if ($X) {
183             if ($X > 0) {
184             $within = 0 if $nx < $xc;
185             } else {
186             $within = 0 if $nx > $xc;
187             }
188             } else {
189             $within = 0 if abs($nx - $x) > $yres;
190             }
191              
192             $xc = $nx;
193             $yc = $ny;
194             });
195             $p->bind("" => sub {
196             $old3 && $old3->Call;
197             return unless $self->{EN};
198              
199             $within or return;
200              
201             my ($nx, $ny) = $p->pointerxy;
202             my $ok = 0;
203              
204             if ($X) {
205             $ok = 1 if abs($nx - $x) >= $min;
206             } else {
207             $ok = 1 if abs($ny - $y) >= $min;
208             }
209             $ok && $cb->Call;
210             });
211             }
212              
213             sub _ges_b1_diag_ul { _generic_diag(1, -1, -1, @_) }
214             sub _ges_b2_diag_ul { _generic_diag(2, -1, -1, @_) }
215             sub _ges_b3_diag_ul { _generic_diag(3, -1, -1, @_) }
216              
217             sub _ges_b1_diag_ur { _generic_diag(1, 1, -1, @_) }
218             sub _ges_b2_diag_ur { _generic_diag(2, 1, -1, @_) }
219             sub _ges_b3_diag_ur { _generic_diag(3, 1, -1, @_) }
220              
221             sub _ges_b1_diag_ll { _generic_diag(1, -1, 1, @_) }
222             sub _ges_b2_diag_ll { _generic_diag(2, -1, 1, @_) }
223             sub _ges_b3_diag_ll { _generic_diag(3, -1, 1, @_) }
224              
225             sub _ges_b1_diag_lr { _generic_diag(1, 1, 1, @_) }
226             sub _ges_b2_diag_lr { _generic_diag(2, 1, 1, @_) }
227             sub _ges_b3_diag_lr { _generic_diag(3, 1, 1, @_) }
228              
229             sub _generic_diag {
230             my ($b, $X, $Y, $self) = @_;
231              
232             my $p = $self->{PARENT};
233             my $res = $self->{XRES} > $self->{YRES} ? $self->{XRES} : $self->{YRES};
234             my $min = $self->{MIN};
235             my $cb = Tk::Callback->new($self->{SUB});
236              
237             my ($x, $y, $xc, $yc, $within);
238              
239             my $slope = $X^$Y ? -1 : 1;
240              
241             # dist of point (xo, yo) to line ax + by + c = 0
242             # d = abs(axo + bxo + c) / sqrt(a^2 + b^2) trust me
243              
244             my $A = $slope;
245             my $B = -1;
246             my $C;
247             my $den = sqrt($A**2 + 1);
248              
249             # make sure any other bindings are preserved.
250             my $old1 = $p->bind("<$b>");
251             my $old2 = $p->bind("");
252             my $old3 = $p->bind("");
253              
254             $p->bind("<$b>" => sub {
255             $old1->Call if $old1;
256             return unless $self->{EN};
257              
258             $within = 1;
259             ($x, $y) = $p->pointerxy;
260             ($xc, $yc) = ($x, $y);
261             $C = $y - $slope * $x;
262             });
263             $p->bind("" => sub {
264             $old2->Call if $old2;
265             return unless $self->{EN};
266             return unless $within;
267              
268             my ($nx, $ny) = $p->pointerxy;
269              
270             # get dist to line with slope +/-1
271             my $dist = abs($A * $nx + $B * $ny + $C) / $den;
272              
273             $dist > $res and return $within = 0;
274              
275             if ($X > 0) { # right
276             $within = 0 if $nx < $xc;
277             if ($Y > 0) { # down => DR
278             $within = 0 if $ny < $yc;
279             } else { # up => UR
280             $within = 0 if $ny > $yc;
281             }
282             } else { # left
283             $within = 0 if $nx > $xc;
284             if ($Y > 0) { # down => DL
285             $within = 0 if $ny < $yc;
286             } else { # up => UL
287             $within = 0 if $ny > $yc;
288             }
289             }
290             });
291              
292             $p->bind("" => sub {
293             $old3 && $old3->Call;
294             return unless $self->{EN};
295             $within or return;
296              
297             my ($nx, $ny) = $p->pointerxy;
298             my $ok = 0;
299             $ok = 1 if $min < sqrt(($x-$nx)**2 + ($y-$ny)**2);
300              
301             $ok && $cb->Call;
302             });
303             }
304              
305             "one";
306              
307             __END__