diff options
author | ribet | 2007-03-21 10:19:39 +0000 |
---|---|---|
committer | ribet | 2007-03-21 10:19:39 +0000 |
commit | c5866f304210618979d03c561b1e3f6f83200bce (patch) | |
tree | 7c81ae161f78cdf952f3d3a33184f8bf322c9bd8 /src/MTools/Comp | |
parent | a023d10b564d8c29566304f7777b4ec87c5b7b4d (diff) | |
download | mtc-c5866f304210618979d03c561b1e3f6f83200bce.zip mtc-c5866f304210618979d03c561b1e3f6f83200bce.tar.gz mtc-c5866f304210618979d03c561b1e3f6f83200bce.tar.bz2 mtc-c5866f304210618979d03c561b1e3f6f83200bce.tar.xz |
Import initial
Diffstat (limited to 'src/MTools/Comp')
-rw-r--r-- | src/MTools/Comp/MAntiRecouvrement.pm | 240 | ||||
-rw-r--r-- | src/MTools/Comp/MFlicker.pm | 79 | ||||
-rw-r--r-- | src/MTools/Comp/MFocuser.pm | 89 | ||||
-rw-r--r-- | src/MTools/Comp/MInertie.pm | 153 | ||||
-rw-r--r-- | src/MTools/Comp/MMover.pm | 221 | ||||
-rw-r--r-- | src/MTools/Comp/MMultiSelection.pm | 754 | ||||
-rw-r--r-- | src/MTools/Comp/MReconizer.pm | 145 | ||||
-rw-r--r-- | src/MTools/Comp/MTremor.pm | 121 | ||||
-rw-r--r-- | src/MTools/Comp/MWritable.pm | 276 |
9 files changed, 2078 insertions, 0 deletions
diff --git a/src/MTools/Comp/MAntiRecouvrement.pm b/src/MTools/Comp/MAntiRecouvrement.pm new file mode 100644 index 0000000..1e10edd --- /dev/null +++ b/src/MTools/Comp/MAntiRecouvrement.pm @@ -0,0 +1,240 @@ +package MTools::Comp::MAntiRecouvrement; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Encapsule l'algorithme d'anti-recouvrement des objets et gere les objets anti-recouvres +# IMPORTANT : Les objets anti-recouvre doivent heriter de MTools::GUI::MAntiRecouvrementGroup +# +# BUG : La propriete '-visible' devrait permettre d'activer ou non l'anti-recouvrement global des objets. +# Ce n'est pas le cas aujourd'hui, cette popriete est inactive +# +# Fonctions : +# * addObject : permet d'inclure un objet heritant de MTools::GUI::MAntiRecouvrementGroup dans l'algorithme d'anti-recouvrement +# * removeObjet : permet de sortir un objet de l'algorithme d'anti-recouvrement + +use strict; +use MTools; +use MTools::MObjet; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class) = @_; + my $self = new MTools::MObjet (); + bless $self, $class; + + $self -> recordProperty ('-visible', 1); + $self -> recordEvent ('ITEM_MOVED'); + $self -> {__objects} = (); + $self -> {__known_objects} = (); + $self -> {__ask_move} = (); + $self -> {__ask_reaction} = (); + + return $self; +} + +sub addObject { + my ($self, $ob) = @_; + my $ever_known = 0; + if (defined $self -> {__known_objects}) + { + my @known = @{$self -> {__known_objects}}; + for (my $i = 0; $i < @known; $i ++) + { + if ($known [$i] eq $ob) + { + $ever_known = 1; + last; + } + } + } + if (!$ever_known) + { + binding ($ob, '__HANDLE_MOVING', [$self, \&__on_handle_moving_on_track]); + binding ($ob, '__PUSH_BACK', [$self, \&__on_push_back_on_track]); + binding ($ob, '__ENQUEUE_MOVING', [$self, \&__on_enqueue_moving_on_track]); + push (@{$self -> {__known_objects}}, $ob); + } + if (!defined $ob -> {__added} || $ob -> {__added} == 0) + { + push (@{$self -> {__objects}}, $ob); + } + $ob -> update_bbox (); + $ob -> {__added} = 1; + $ob -> translate (0, 0); +} + +sub removeObject { + my ($self, $target) = @_; + for (my $i = @{$self -> {__objects}} - 1; $i >= 0; $i --) + { + if (@{$self -> {__objects}} [$i] eq $target) + { + @{$self -> {__objects}} [$i] -> {__added} = 0; + splice (@{$self -> {__objects}}, $i, 1); + } + } +} + +sub __on_handle_moving_on_track { + my ($self) = @_; + my ($first_work, $first_track, $current_track, $intersection); + while (scalar @{$self -> {__ask_move}} > 0) + { + $first_work = $self -> {__ask_move} -> [0]; + $first_track = $first_work -> [0]; + my ($firstx, $firsty) = ($first_track -> mget ('x'), $first_track -> mget ('y')); + for (my $i = 0; $i < @{$self -> {__objects}}; $i++) + { + $current_track = @{$self -> {__objects}} [$i]; + next if ($current_track eq $first_track); + my ($x, $y) = ($current_track -> mget ('x'), $current_track -> mget ('y')); + $intersection = __intersection ( + $firstx, $firsty, + $first_track -> mget ('width'), $first_track -> mget ('height'), + $x, $y, + $current_track -> mget ('width'), $current_track-> mget ('height') + ); + next if (($intersection -> [2] == 0) and ($intersection -> [3] == 0)); + if ($intersection -> [2] < $intersection -> [3]) + { + if ($firstx < $x) + { + $current_track -> __try_move ($intersection -> [2], 0, $first_work -> [3]); + } + else + { + $current_track -> __try_move(-$intersection -> [2], 0, $first_work -> [3]); + } + } + else + { + if ($firsty < $y) + { + $current_track -> __try_move (0, $intersection -> [3], $first_work -> [3]); + } + else + { + $current_track -> __try_move (0, -$intersection -> [3], $first_work -> [3]); + } + } + } + shift @{$self -> {__ask_move}}; + } + $self -> notify ('ITEM_MOVED'); +} + +sub __on_push_back_on_track { + my ($self, $track, $delta_x, $delta_y, $path) = @_; + push @{$path}, $track; + my @other_path = @{$path}; + $self -> __on_enqueue_moving_on_track ($track, $delta_x, $delta_y, [@other_path]); + $track -> __update_xy ($delta_x, $delta_y); + my ($current_track, $push_x, $push_y, $intersection); + my ($firstx, $firsty) = ($track -> mget ('x'), $track -> mget ('y')); + + for (my $i = 0; $i < @{$self -> {__objects}}; $i++) + { + $current_track = @{$self -> {__objects}} [$i]; + next if $current_track eq $track; + my ($x, $y) = ($current_track -> mget ('x'), $current_track -> mget ('y')); + $intersection = __intersection ( + $firstx, $firsty, + $track -> mget ('width'), $track -> mget ('height'), + $x, $y, + $current_track -> mget ('width'), $current_track -> mget ('height') + ); + $push_x = 0; + $push_y = 0; + $push_x = $intersection -> [2] if ($intersection -> [2] <= $delta_x); + $push_x = -$intersection -> [2] if ($intersection -> [2] <= -$delta_x); + $push_y = $intersection -> [3] if ($intersection -> [3] <= $delta_y); + $push_y = -$intersection -> [3] if ($intersection -> [3] <= -$delta_y); + $self -> __on_push_back_on_track ($current_track, $push_x, $push_y, $path) + if ((($push_x != 0) or ($push_y != 0)) and (__occurences ($current_track, @{$path}) < 3)); + } + pop @{$path}; +} + +sub __intersection { + my ($x1, $y1, $w1, $h1, $x2, $y2, $w2, $h2) = @_; + my ($x, $y, $w, $h); + $x = &__max_of ($x1, $x2); + $w = &__min_of ($x1 + $w1, $x2 + $w2) - $x; + if ($w > 0) + { + $y = &__max_of ($y1, $y2); + $h = &__min_of ($y1 + $h1, $y2 + $h2) - $y; + if ($h > 0) + { + return [$x, $y, $w, $h]; + } + } + return [0, 0, 0, 0]; +} + +sub __min_of { + my (@values) = @_; + my $res = $values [0]; + foreach (@values) + { + $res = $_ if ($_ <= $res); + } + return $res; +} + +sub __max_of { + my (@values) = @_; + my $res = $values [0]; + foreach (@values) + { + $res = $_ if ($_ >= $res); + } + return $res; +} + +sub __occurences { + my ($val,@tab) = @_; + my $result = 0; + if (($#tab != -1) and (defined $val)) + { + foreach (@tab) + { + if (($_ eq $val) or ($_ =~ m/$val/)) + { + $result++; + } + } + } + return $result; +} + +sub __on_enqueue_moving_on_track { + my ($self, $track, $delta_x, $delta_y, $path) = @_; + push @{$self -> {__ask_move}}, [$track, $delta_x, $delta_y, $path]; +} + + + +1; diff --git a/src/MTools/Comp/MFlicker.pm b/src/MTools/Comp/MFlicker.pm new file mode 100644 index 0000000..fd622c8 --- /dev/null +++ b/src/MTools/Comp/MFlicker.pm @@ -0,0 +1,79 @@ +package MTools::Comp::MFlicker; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Le composant permet d'ajouter un comportement de clignotement ? un objet cible +# +# Parametres : +# +# * target : objet cible du tremblement +# * periode : periode du clignottement +# Propriete : +# * flick : demarre le clignottement +# * show : rend l'objet cible toujours visible +# * hide : rend l'objet cible toujours masque + +use strict; +use MTools; +use MTools::MObjet; +use MTools::MTimer; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MSwitch/; +} + +use Tk; + +sub new { + my ($class, $target, $periode) = @_; + my $self = new MTools::MObjet (); + $self -> {__target} = $target; + $self -> {__target} -> mconfigure (-visible => 0); + bless $self, $class; + + $self -> recordProperty ('periode', $periode); + + $self -> {__timer} = new MTools::MTimer ($periode, 1, sub { + $self -> {__target} -> mconfigure (-visible => !$self -> {__target} -> mget (-visible)); + }); + + plink ([$self, 'periode'], [$self -> {__timer}, 'timeout']); + + return $self; +} + +sub flick { + my ($self) = @_; + $self -> {__timer} -> start (); +} + +sub show { + my ($self) = @_; + $self -> {__timer} -> stop (); + $self -> {__target} -> mconfigure (-visible => 1); +} + +sub hide { + my ($self) = @_; + $self -> {__timer} -> stop (); + $self -> mconfigure ('state' => 'unvisible'); +} + +1; diff --git a/src/MTools/Comp/MFocuser.pm b/src/MTools/Comp/MFocuser.pm new file mode 100644 index 0000000..cb821ae --- /dev/null +++ b/src/MTools/Comp/MFocuser.pm @@ -0,0 +1,89 @@ +package MTools::Comp::MFocuser; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# L'objet MFocuser permet de gerer le focus entre plusieurs objets +# +# Parametres : +# * @targets : objets entre lesquels est gere le focus +# Fonctions : +# * add : permet de prendre en compte un nouvel objet +# * setFocused : force le focus sur un objet + +use strict; +use MTools; +use MTools::MObjet; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, @targets) = @_; + my $self = {}; + bless $self, $class; + + $self -> recordEvent ('GET_FOCUS'); + $self -> recordEvent ('SET_FOCUS'); + + $self -> {__targets} = \@targets; + for (my $i; $i < @targets; $i++) + { + $targets [$i] -> binding ('GET_FOCUS', ['setFocused', $self]); + } + + $self -> binding ('SET_FOCUS', ['setFocused', $self]); + + return $self; +} + +sub add { + my ($self, $tg) = @_; + push (@{$self -> {__targets}}, $tg); + $tg -> binding ('GET_FOCUS', ['setFocused', $self]); +} + +sub setFocused { + my ($self, $tg) = @_; + if (!defined $self -> {__targets}) {return;} + my @tgs = @{$self -> {__targets}}; + my $from_children = 0; + for (my $i; $i < @tgs; $i++) + { + if ($tgs [$i] ne $tg) + { + $tgs [$i] -> notify ('SET_FOCUS'); + } + else + { + $from_children = 1; + } + } + if ($from_children) + { + $self -> notify ('GET_FOCUS', $self); + } +} + +1; + + diff --git a/src/MTools/Comp/MInertie.pm b/src/MTools/Comp/MInertie.pm new file mode 100644 index 0000000..9b820a0 --- /dev/null +++ b/src/MTools/Comp/MInertie.pm @@ -0,0 +1,153 @@ +package MTools::Comp::MInertie; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +use strict; +use MTools; +use MTools::MObjet; +use Time::HiRes; + +use MTools::MTimer; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, $target, %options) = @_; + my $self = new MTools::MObjet (); + bless $self, $class; + + my $msg_pressed = defined $options {msg_pressed} ? $options {msg_pressed} : 'PRESSED'; + my $msg_moved = defined $options {msg_moved} ? $options {msg_moved} : 'MOVED'; + my $msg_released = defined $options {msg_released} ? $options {msg_released} : 'RELEASED'; + my $msg_stopped = defined $options {msg_stopped} ? $options {msg_stopped} : 'INERTIE_STOPPED'; + + delete $options {msg_pressed}; + delete $options {msg_moved}; + delete $options {msg_released}; + delete $options {msg_stopped}; + + $self -> recordProperty ('target', $target); + $self -> recordProperty ('-visible', 1); + $self -> recordProperty ('rate', 0.85); + $self -> recordProperty ('inertie_callback', 'translate'); + + $self -> recordEvent ($msg_stopped); + $self -> mconfigure (%options); + + $self -> {__old_t} = 0; + $self -> {__old_x} = 0; + $self -> {__old_y} = 0; + $self -> {__x} = 0; + $self -> {__y} = 0; + $self -> {__t} = 0; + $self -> {__vx} = 0; + $self -> {__vy} = 0; + + $self -> {__msg_stopped} = $msg_stopped; + + $target -> binding ($msg_pressed, [$self, 'target_pressed']); + $target -> binding ($msg_moved, [$self, 'target_moved']); + $target -> binding ($msg_released, [$self, 'target_released']); + + $self -> {timer_inertie} = new MTools::MTimer (1000/60, 1, [$self, 'inertie']); + + return $self; +} + +sub interrupt { + my ($self) = @_; + $self -> {timer_inertie} -> stop (); +} + +sub target_pressed { + my ($self, $x, $y, $t) = @_; + $self -> interrupt (); + $self -> {__old_x} = 0; + $self -> {__old_y} = 0; + $self -> {__old_t} = 0; + $self -> {__x} = 0; + $self -> {__y} = 0; + $self -> {__t} = 0; +} + +sub target_moved { + my ($self, $x, $y, $t) = @_; + if (!defined $t) + { + $t = Time::HiRes::gettimeofday(); + } + $self -> {__old_x} = $self -> {__x}; + $self -> {__old_y} = $self -> {__y}; + $self -> {__old_t} = $self -> {__t}; + $self -> {__x} = $x; + $self -> {__y} = $y; + $self -> {__t} = $t; +} + +sub target_released { + my ($self, $x, $y, $t) = @_; + if (!defined $t) + { + $t = Time::HiRes::gettimeofday(); + } + my $dt = $self -> {__t} - $self -> {__old_t}; + my $dx = $self -> {__x} - $self -> {__old_x}; + my $dy = $self -> {__y} - $self -> {__old_y}; + if ($dt && $self -> mget ('-visible')) + { + $self -> {__vx} = ($dx * 1000) / ($dt * 60); + $self -> {__vy} = ($dy * 1000) / ($dt * 60); + $self -> {timer_inertie} -> start(); + } + else + { + $self -> notify ($self -> {__msg_stopped}); + } + $self -> {__old_x} = 0; + $self -> {__old_y} = 0; + $self -> {__old_t} = 0; + $self -> {__x} = 0; + $self -> {__y} = 0; + $self -> {__t} = 0; +} + +sub inertie { + my ($self) = @_; + my $rate = $self -> mget ('rate'); + $self -> {__vx} *= $rate; + $self -> {__vy} *= $rate; + if ((abs $self -> {__vx} <= 2) and (abs $self -> {__vy} <= 2)) + { + $self -> notify ($self -> {__msg_stopped}); + $self -> {timer_inertie} -> stop(); + return; + } + my $dx = $self -> {__vx}; + my $dy = $self -> {__vy}; + my $target = $self -> mget ('target'); + my $callback = $self -> mget ('inertie_callback'); + $target -> $callback ($dx, $dy); +} + +1; diff --git a/src/MTools/Comp/MMover.pm b/src/MTools/Comp/MMover.pm new file mode 100644 index 0000000..8282943 --- /dev/null +++ b/src/MTools/Comp/MMover.pm @@ -0,0 +1,221 @@ +package MTools::Comp::MMover; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Le composant MMover permet de donner un comportement prehensible a un objet zinc. Les deplacements peuvent etre contraints a un espace defin +# +# Parametres : +# * src : objet source des evenements de deplacement +# * targets : objets deplaces +# * button : bouton de la souris utilise pour genere le deplacement +# Proprietes : +# * x_min, y_min, x_max, y_max : contraintes de l'espace dans lequel peut se deplacer le strip +# * allower : fonction permettre d'appliquer une contrainte plus complexe sur l'objet +# * x, y : position de l'objet modifiee par le MMover +# * targets : objets deplaces +# * -visible : active ou desactive le comportement prehensible + +use strict; +use MTools; +use MTools::MObjet; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, $src, $targets, $button, %options) = @_; + my $self = {}; + bless $self, $class; + + $self -> recordProperty ('x_min', -10000); + $self -> recordProperty ('y_min', -10000); + $self -> recordProperty ('x_max', 10000); + $self -> recordProperty ('y_max', 10000); + $self -> recordProperty ('-visible', 1); + $self -> recordProperty ('x', 0); + $self -> recordProperty ('y', 0); + $self -> recordProperty ('targets', $targets); + $self -> recordProperty ('allower', undef); + + $self -> mconfigure (%options); + + $self -> recordEvent ('PRESSED'); + $self -> recordEvent ('MOVED'); + $self -> recordEvent ('RELEASED'); + + $button = 1 if ! defined $button; + binding ($src, "<Button-$button>", [\&__pressed, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, "<Button$button-Motion>", [\&__moved, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, "<ButtonRelease-$button>", [\&__released, $self, Ev('x'), Ev('y'), Ev('t')]); + + $self -> {__last_mouse_x} = 0; + $self -> {__last_mouse_y} = 0; + + return $self; +} + +sub setPos { + my ($self, $x, $y) = @_; + my $dx = $x - $self -> mget ('x'); + my $dy = $y - $self -> mget ('y'); + my $tgs = $self -> mget ('targets'); + my @targets; + if (ref ($tgs) eq 'ARRAY') + { + @targets = @{$tgs}; + for (my $i = 0; $i < @targets; $i++) + { + $targets [$i] -> translate ($dx, $dy); + } + } + else + { + $tgs -> translate ($dx, $dy); + } + $self -> mconfigure ('x', $x); + $self -> mconfigure ('y', $y); +} + +sub __pressed { + my ($self, $x, $y, $t) = @_; + if(!$self -> mget('-visible')) {return;} + $self -> {__started} = 1; + $self -> {__last_mouse_x} = $x; + $self -> {__last_mouse_y} = $y; + $self -> notify ('PRESSED', $self -> {__last_mouse_x}, $self -> {__last_mouse_y}, $t); +} + +sub __moved { + my ($self, $x, $y, $t) = @_; + if (!$self -> {__started}) {return;} + if(!$self -> mget('-visible')) {return;} + + my $dx = $x - $self -> {__last_mouse_x}; + my $dy = $y - $self -> {__last_mouse_y}; + + my $x_min = $self -> mget('x_min'); + my $y_min = $self -> mget('y_min'); + my $x_max = $self -> mget('x_max'); + my $y_max = $self -> mget('y_max'); + my $current_x = $self -> mget ('x'); + my $current_y = $self -> mget ('y'); + if($current_x + $dx > $x_max) + { + $x -= $current_x + $dx - $x_max; + $dx -= $current_x + $dx - $x_max; + } + if($current_y + $dy > $y_max) + { + $y -= $current_y + $dy - $y_max; + $dy -= $current_y + $dy - $y_max; + } + if($current_x + $dx < $x_min) + { + + $x -= $current_x + $dx - $x_min; + $dx -= $current_x + $dx - $x_min; + } + if($current_y + $dy < $y_min) + { + $y -= $current_y + $dy - $y_min; + $dy -= $current_y + $dy - $y_min; + } + if( $dx == 0 && $dy == 0 ) + { + return; + } + my $allower = $self -> mget ('allower'); + if ($allower) + { + my ($ddx, $ddy) = executer ($allower, $current_x, $current_y, $dx, $dy); + $x -= $ddx; + $dx -= $ddx; + $y -= $ddy; + $dy -= $ddy; + if( $dx == 0 && $dy == 0 ) + { + return; + } + } + $self -> {__last_mouse_x} = $x; + $self -> {__last_mouse_y} = $y; + $current_x += $dx; + $current_y += $dy; + $self -> notify ('MOVED', $current_x, $current_y, $t); + $self -> setPos ($current_x, $current_y); +} + +sub __released { + my ($self, $x, $y, $t) = @_; + $self -> {__started} = 0; + if(!$self -> mget('-visible')) {return;} + $self -> notify ('RELEASED', $self -> {__last_mouse_x}, $self -> {__last_mouse_y}, $t); +} + +sub translate { + my ($self, $dx, $dy, $t) = @_; + my $x_min = $self -> mget('x_min'); + my $y_min = $self -> mget('y_min'); + my $x_max = $self -> mget('x_max'); + my $y_max = $self -> mget('y_max'); + my $current_x = $self -> mget ('x'); + my $current_y = $self -> mget ('y'); + if($current_x + $dx > $x_max) + { + $dx -= $current_x + $dx - $x_max; + } + if($current_y + $dy > $y_max) + { + $dy -= $current_y + $dy - $y_max; + } + if($current_x + $dx < $x_min) + { + + $dx -= $current_x + $dx - $x_min; + } + if($current_y + $dy < $y_min) + { + $dy -= $current_y + $dy - $y_min; + } + if( $dx == 0 && $dy == 0 ) + { + return; + } + my $allower = $self -> mget ('allower'); + if ($allower) + { + my ($ddx, $ddy) = executer ($allower, $current_x, $current_y, $dx, $dy); + $dx -= $ddx; + $dy -= $ddy; + if( $dx == 0 && $dy == 0 ) + { + return; + } + } + $current_x += $dx; + $current_y += $dy; + $self -> setPos ($current_x, $current_y); + $self -> notify ('MOVED', $current_x, $current_y, $t); +} + +1; diff --git a/src/MTools/Comp/MMultiSelection.pm b/src/MTools/Comp/MMultiSelection.pm new file mode 100644 index 0000000..f124720 --- /dev/null +++ b/src/MTools/Comp/MMultiSelection.pm @@ -0,0 +1,754 @@ +package MTools::Comp::MMultiSelection; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + + +# Le composant MMultiSelection associe deux comportements : +# 1. Selection multiple d'objets repartis eventuellement dans plusieurs plans. +# La selection des objets est geree en les entourant et des-entourant. +# Si un objet est selectionne dans le plan 1, les objets situes dans les plans 2, 3, etc. sont negliges. +# De meme, si un objet est selectionne dans le plan, les objets situes dans les plans 3, 4, etc. sont negliges. +# 2. Deux interactions peuvent-etre initiees depuis la zone de selection. Si l'interaction est demarree +# au-dessus d'un objet, on deplace les objets selectionnes. Si on demarre un interaction au-dessus du fond, +# on demarre une operation de designation de cibles. +# +# Les cibles et les objets selectionnables doivent declarer des proprietes particulieres. Il n'y a ce jour de +# composant definissant ces interfaces, c'est un tord... Qui est volontaire :) +# +# Les source doivent definir les prorpiete +# barycentre_x +# barycentre_y +# La position d'un objet est definie par +# la valeur de barycentre_x plus optionnelement la valeur de la propriete x si celle-ci est definie +# la valeur de barycentre_y plus optionnelement la valeur de la propriete y si celle-ci est definie +# Les cibles doivent emettre les evenements +# DRAGENTER +# DRAGLEAVE +# RELEASEDOVER +# +# NOTA : le deuxieme comportement devrait etre dissocie de l'objet multi-selection... +# Cela reste a faire egalement +# +# Parametres : +# * src : Objet sur lequel est effectue l'interaction de selection +# * dessin : Group parent du dessin de la selection +# * reference a la frame principale +# Proprietes : +# * delay : duree pendant laquelle le dessin de la selection s'estompe et pendant laquelle le demarrage de la +# seconde interaction est possible +# * inertie : coefficient d'inertie dqns le deplacement de la fleche +# Evenements : +# * PRESSED : Notifie lorsque l'on demarre la selection +# * MOVED : Notifie lorsque la selection evolue +# * RELEASED : Notifie lorsque la selection est terminee +# * FLECHEPRESSED : Notifie lorsque une seconde interaction est demarree depuis la zone de selection +# * FLECHEMOVED : Notifie lorsque la seconde interaction evolue +# * FLECHERELEASED : Notifie lorsque la seconde interaction se termine +# * SELECTION_CHANGED : Notifie lorsque le statut d'un objet a change +# Fonctions : +# * addObject : $multi -> addObject ($objet, $plan) +# Ajoute l'objet $objet dans le plan de selection $plan +# * removeObject : $multi -> removeObject ($objet, $plan) +# Supprime l'objet $objet du plan de selection $plan +# * addDropTarget : $multi -> addDropTarget ($target) +# Ajoute la cible $target +# * removeDropTarget : $multi -> removeDropTarget ($target) +# Supprime la cible $target +# * applySelection : $multi -> applySelection ($plan, $fonction, @parametres) +# Applique la fonction $fonction (@parametres) ? tous les objets selectionnes dans le plan $plan +# * applyAll : $multi -> applyAll ($plan, $fonction, @parametres) +# Applique la fonction $fonction (@parametres) ? tous les objets du plan $plan +# * getSelectedPlan : $multi -> getSelectedPlan () +# Retourne l'index du plan selectionne +# * getSelection : $multi -> getSelection ($plan) +# Retourne la liste des objets selectionnes dans le plan $plan si $plan est d?fini +# sinon retourne $multi -> getSelection ($multi -> getSelectedPlan ()) +# + + + +use strict; +use MTools; +use MTools::MObjet; +use Math::Trig; +use MTools::Anim::MOpacity; +use MTools::Comp::MInertie; +use MTools::MGroup; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, $src, $dessin, $frame, %options) = @_; + my $self = new MTools::MObjet (); + bless $self, $class; + + $self -> recordProperty ('delay', 0); + $self -> recordProperty ('inertie', 0.6); + $self -> recordEvent ('PRESSED'); + $self -> recordEvent ('MOVED'); + $self -> recordEvent ('RELEASED'); + $self -> recordEvent ('FLECHEPRESSED'); + $self -> recordEvent ('FLECHEMOVED'); + $self -> recordEvent ('FLECHERELEASED'); + $self -> recordEvent ('SELECTION_CHANGED'); + $self -> recordEvent ('DESELECT_ALL'); + $self -> mconfigure (%options); + + binding ($src, '<Button-1>', [\&__pressed, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, '<Button1-Motion>', [\&__moved, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, '<ButtonRelease-1>', [\&__released, $self, Ev('x'), Ev('y'), Ev('t')]); + + $self -> {__inertie} = my $inertie = new MTools::Comp::MInertie( + $self, + 'rate' => $self -> mget ('inertie'), + ); + binding ($inertie, 'INERTIE_STOPPED', [$self, \&__fin_moved]); + + my $inertie = new MTools::Comp::MInertie( + $self, + 'rate' => 0.75, + 'msg_pressed' => 'FLECHEPRESSED', + 'msg_moved' => 'FLECHEMOVED', + 'msg_released' => 'FLECHERELEASED', + 'msg_stopped' => 'FLECHEINERTIE_STOPPED', + 'inertie_callback' => '__translate_fleche', + ); + binding ($inertie, 'FLECHEINERTIE_STOPPED', [$self, \&__fin_fleche_moved]); + + $self -> {__frame} = $frame; + $self -> {__drop_targets} = (); + $self -> {__objects} = (); + $self -> {__points} = (); + $self -> {__owns_data} = (); + $self -> {__source} = minstance ($src); + + $self -> {__gp_anime} = my $group = new MTools::MGroup ($dessin); + + push (@{$self -> {__owns_data}}, minstance ($src)); + push (@{$self -> {__owns_data}}, minstance ($group)); + + $self -> {__anim__disparition} = new MTools::Anim::MOpacity ( + duration => 0.8, + targets => $group, + from_opacity => 100, + to_opacity => 0, + ); + + binding ($self -> {__anim__disparition}, 'ANIMATION_END', [$self, \&__clear]); + + $self -> {__curve_bck} = $zinc -> add ('curve', minstance($group), [[0,0],[0,0]], + -linecolor => "#888888;50", + -linewidth => 2, + -smoothrelief => 1, + -priority => 10, + -visible => 1, + -sensitive => 1, + -filled => 0, + ); + $self -> {__curve} = $zinc -> add ('curve', minstance($group), [[0,0],[0,0]], + -linecolor => "#FFFFFF;90", + -linewidth => 1, + -smoothrelief => 1, + -priority => 10, + -visible => 1, + -sensitive => 1, + -fillcolor => "#FFFFFF;10", + -filled => 1, + ); + push (@{$self -> {__owns_data}}, minstance ($self -> {__curve})); + + $self -> {__fleche} = $zinc -> add ('curve', minstance($dessin), [[0,0],[0,0]], + -linecolor => "black", + -linewidth => 2, + -priority => 10, + -visible => 1, + -sensitive => 0, + ); + push (@{$self -> {__owns_data}}, minstance ($self -> {__fleche})); + + $self -> {__ph_fleche} = $zinc -> add ('curve', minstance($dessin), [[0,0],[0,0]], + -linecolor => "black", + -linewidth => 2, + -priority => 10, + -visible => 1, + -sensitive => 0, + ); + push (@{$self -> {__owns_data}}, minstance ($self -> {__ph_fleche})); + + $self -> {__pb_fleche} = $zinc -> add ('curve', minstance($dessin), [[0,0],[0,0]], + -linecolor => "black", + -linewidth => 2, + -priority => 10, + -visible => 1, + -sensitive => 0, + ); + push (@{$self -> {__owns_data}}, minstance ($self -> {__pb_fleche})); + + binding ($self -> {__curve}, '<Button-1>', [\&__fleche_pressed, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($self -> {__curve}, '<Button1-Motion>', [\&__fleche_moved, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($self -> {__curve}, '<ButtonRelease-1>', [\&__fleche_released, $self, Ev('x'), Ev('y'), Ev('t')]); + $self -> {__fleche_points} = (); + $self -> {__fleche_started} = 0; + $self -> {__drag_started} = 0; + return $self; +} + +sub __hide_cursor { + my ($self) = @_; + my $frame = $self -> {__frame}; + $frame -> {window} -> Tk::configure ( + -cursor => [ '@'.Tk::findINC('emptycursor.xbm'), + Tk::findINC('emptycursor.mask'), + 'black', 'black' + ] + ); +} + +sub __show_cursor { + my ($self) = @_; + my $frame = $self -> {__frame}; + $frame -> {window} -> Tk::configure ( + -cursor => "", + ); +} + +sub addObject { + my ($self, $object, $plan) = @_; + if (!defined $plan) {$plan = 0;} + if (!$object -> propertyExists ('barycentre_x')) + { + print "MMultiSelection Error property barycentre_x isn't defined in $object\n"; + return; + } + if (!$object -> propertyExists ('barycentre_y')) + { + print "MMultiSelection Error property barycentre_y isn't defined in $object\n"; + return; + } + $object -> {__surround_angle} = 0; + push (@{$self -> {__objects} [$plan]}, $object); +} + +sub removeObject { + my ($self, $target, $plan) = @_; + if (!defined $plan) {$plan = 0;} + for (my $i = @{$self -> {__objects} [$plan]} - 1; $i >= 0; $i --) + { + if (@{$self -> {__objects} [$plan]} [$i] eq $target) + { + splice (@{$self -> {__objects} [$plan]}, $i, 1); + last; + } + } +} + +sub addDropTarget { + my ($self, $target) = @_; + push (@{$self -> {__drop_targets}}, $target); +} + +sub removeDropTarget { + my ($self, $target) = @_; + if ($self -> {__last_notified} == $target) + { + $self -> {__last_notified} = undef; + } + for (my $i = @{$self -> {__drop_targets}} - 1; $i >= 0; $i--) + { + if (@{$self -> {__drop_targets}} [$i] eq $target) + { + splice (@{$self -> {__drop_targets}}, $i, 1); + last; + } + } +} + +sub __clear { + my ($self) = @_; + $self -> {__anim__disparition} -> stop (); + $self -> {__inertie} -> interrupt (); + $self -> {__fleche_started} = 0; + $self -> {__drag_started} = 0; + $self -> {__points} = (); + $self -> {__fleche_points} = (); + $self -> __reset_surrounding (); + $zinc -> coords ($self -> {__ph_fleche}, 0, [[0,0],[0,0]]); + $zinc -> coords ($self -> {__pb_fleche}, 0, [[0,0],[0,0]]); + $zinc -> coords ($self -> {__fleche}, 0, [[0,0],[0,0]]); + $zinc -> coords ($self -> {__curve}, 0, [[0,0],[0,0]]); + $zinc -> coords ($self -> {__curve_bck}, 0, [[0,0],[0,0]]); + $self -> {__gp_anime} -> mconfigure ( + -alpha => 100, + ); +} + +sub __pressed { + my ($self, $x, $y, $t) = @_; + $self -> __clear (); + push (@{$self -> {__points}}, [$x, $y]); + $self -> notify ('PRESSED', $x, $y, $t); + $self -> notify ('DESELECT_ALL'); +} + +sub __released { + my ($self, $x, $y, $t) = @_; + my $pt = $self -> {__points}[@{$self -> {__points}} - 2]; + $self -> notify ('RELEASED', $x - $pt -> [0], $y - $pt -> [1], $t); +} + +sub __fin_moved { + my ($self) = @_; + if (!$self -> {__fleche_started} && !$self -> {__drag_started}) + { + $self -> {__anim__disparition} -> start (); + } +} + +sub __moved { + my ($self, $x, $y, $t) = @_; + push (@{$self -> {__points}}, [$x, $y]); + my @pts = @{$self -> {__points}}; + $zinc -> coords ($self -> {__curve}, 0, \@pts); + $zinc -> coords ($self -> {__curve_bck}, 0, \@pts); + my $pt = $self -> {__points}[@{$self -> {__points}} - 2]; + $self -> notify ('MOVED', $x - $pt -> [0], $y - $pt -> [1], $t); + $self -> __update_all (); +} + +sub translate { + my ($self, $dx, $dy) = @_; + my $pt = $self -> {__points}[@{$self -> {__points}} - 1]; + push (@{$self -> {__points}}, [$pt -> [0] - $dx, $pt -> [1] - $dy]); + my @pts = @{$self -> {__points}}; + $zinc -> coords ($self -> {__curve}, 0, \@pts); + $zinc -> coords ($self -> {__curve_bck}, 0, \@pts); + $self -> __update_all (); +} + +sub __fleche_pressed { + my ($self, $x, $y, $t) = @_; + my @all = $zinc -> find ('overlapping', $x, $y, $x + 1, $y +1); + if ($all [1] == $self -> {__source}) + { + $self -> {__fleche_points} = (); + push (@{$self -> {__fleche_points}}, [$x, $y]); + $self -> {__fleche_started} = 1; + $self -> __hide_cursor (); + $self -> notify ('FLECHEPRESSED', $x, $y, $t); + $self -> __stop_animation (); + } + else + { + $self -> {__drag_started} = 1; + $self -> {__lx} = $x; + $self -> {__ly} = $y; + $self -> notify ('FLECHEPRESSED', $x, $y, $t); + } +} + +sub __stop_animation { + my ($self) = @_; + $self -> {__anim__disparition} -> stop (); + $self -> {__gp_anime} -> mconfigure ( + -alpha => 100, + ); +} + +sub __fleche_released { + my ($self, $x, $y, $t) = @_; + if ($self -> {__fleche_started}) + { + my $pt = $self -> {__fleche_points}[@{$self -> {__fleche_points}} - 1]; + $self -> notify ('FLECHERELEASED', $x - $pt -> [0], $y - $pt -> [1], $t); + } + else + { + $self -> notify ('FLECHERELEASED', $x - $self -> {__lx}, $y - $self -> {__ly}, $t); + $self -> {__lx} = $x; + $self -> {__ly} = $y; + } + +} + +sub __fin_fleche_moved { + my ($self) = @_; + if ($self -> {__fleche_started}) + { + $self -> __clear (); + if (defined $self -> {__last_notified}) + { + $self -> {__last_notified} -> notify ('RELEASEDOVER', $self); + $self -> {__last_notified} = undef; + } + elsif (defined $self -> {__secondary_last_notified}) + { + $self -> {__secondary_last_notified} -> notify ('RELEASEDOVER', $self); + $self -> {__secondary_last_notified} = undef; + } + $self -> __show_cursor (); + } + else + { + } +} + +sub __fleche_moved { + my ($self, $x, $y, $t) = @_; + if ($self -> {__fleche_started}) + { + push (@{$self -> {__fleche_points}}, [$x, $y]); + my @pts = @{$self -> {__fleche_points}}; + $zinc -> coords ($self -> {__fleche}, 0, \@pts); + my $pt = $self -> {__fleche_points}[@{$self -> {__fleche_points}} - 2]; + $self -> notify ('FLECHEMOVED', $x - $pt -> [0], $y - $pt -> [1], $t); + $self -> __update_fleche_moved ($x, $y); + } + else + { + $self -> notify ('FLECHEMOVED', $x - $self -> {__lx}, $y - $self -> {__ly}, $t); + $self -> __translate_selection ($x - $self -> {__lx}, $y - $self -> {__ly}); + $self -> {__lx} = $x; + $self -> {__ly} = $y; + } +} + +sub __translate_fleche { + my ($self, $dx, $dy) = @_; + if ($self -> {__fleche_started}) + { + my $pt = $self -> {__fleche_points}[@{$self -> {__fleche_points}} - 1]; + push (@{$self -> {__fleche_points}}, [$pt -> [0] - $dx, $pt -> [1] - $dy]); + my @pts = @{$self -> {__fleche_points}}; + $zinc -> coords ($self -> {__fleche}, 0, \@pts); + $self -> __update_fleche_moved ($pt -> [0] - $dx, $pt -> [1] - $dy); + } + else + { + $self -> __translate_selection (-$dx, -$dy); + } +} + +sub __translate_selection { + my ($self, $dx, $dy) = @_; + my @oldy = $self -> applySelection (0, 'mget', 'y'); + my @oldx = $self -> applySelection (0, 'mget', 'x'); + my @selection = $self -> getSelection (); + for (my $i = 0; $i < @selection; $i ++) + { + my $x = $selection [$i] -> mget ('x'); + my $pdx = $dx - ($x - $oldx [$i]); + my $y = $selection [$i] -> mget ('y'); + my $pdy = $dy - ($y - $oldy [$i]); + $selection [$i] -> translate ($pdx, $pdy); + } +} + +sub __update_fleche_moved { + my ($self, $x, $y) = @_; + my $pt; + if (@{$self -> {__fleche_points}} > 2) + { + $pt = $self -> {__fleche_points}[@{$self -> {__fleche_points}} - 3]; + } + else + { + $pt = $self -> {__fleche_points}[@{$self -> {__fleche_points}} - 2]; + } + my $xa = $x - $pt -> [0]; + my $ya = $y - $pt -> [1]; + if ($xa + $ya == 0) {return;} + + my $norme = sqrt ($xa * $xa + $ya * $ya); + $xa = $xa * 8 / $norme; + $ya = $ya * 8 / $norme; + + if ( $xa != 0) + { + my $yb = sqrt ((10 * 10 - $xa * $xa - $ya * $ya) / (1 + $ya * $ya / ($xa * $xa))); + my $xb = -$yb * $ya / $xa; + $zinc -> coords ($self -> {__ph_fleche}, 0, [[$x, $y], [$x + $xb - $xa, $y + $yb - $ya]]); + $zinc -> coords ($self -> {__pb_fleche}, 0, [[$x, $y], [$x - $xb - $xa, $y - $yb - $ya]]); + } + else + { + my $xb = sqrt (10 * 10 - $ya * $ya); + my $yb = 0; + $zinc -> coords ($self -> {__ph_fleche}, 0, [[$x, $y], [$x + $xb - $xa, $y + $yb - $ya]]); + $zinc -> coords ($self -> {__pb_fleche}, 0, [[$x, $y], [$x - $xb - $xa, $y - $yb - $ya]]); + } + my @all = $zinc -> find ('overlapping', $x, $y, $x + 1, $y +1); + + if (defined $self -> {__drop_targets}) + { + my @selected; + my @owns = @{$self -> {__owns_data}}; + for (my $i = 0; $i < @all; $i++) + { + my $find = 0; + for (my $j = 0; $j < @owns; $j ++) + { + if ($all [$i] == $owns [$j]) + { + $find = 1; + } + } + if (!$find) + { + push (@selected, $all [$i]); + } + } + if (defined @selected) + { + my @targets = @{$self -> {__drop_targets}}; + my $last_notified = $self -> {__last_notified}; + $self -> {__last_notified} = undef; + for (my $i = 0; $i < @targets; $i ++) + { + if (minstance ($targets [$i]) == $selected [0] || minstance ($targets [$i]) == $selected [1]) + { + if ($last_notified != $targets [$i]) + { + $targets [$i] -> notify ('DRAGENTER'); + } + $self -> {__last_notified} = $targets [$i]; + } + else + { + if ($last_notified == $targets [$i]) + { + $targets [$i] -> notify ('DRAGLEAVE'); + } + } + } + } + } +} + +sub getObjectBellow { + my ($self, $x, $y, @objects) = @_; + my @all = $zinc -> find ('overlapping', $x, $y, $x + 1, $y +1); + my @owns = @{$self -> {__owns_data}}; + my @below; + for (my $i = 0; $i < @all; $i++) + { + my $find = 0; + for (my $j = 0; $j < @owns; $j ++) + { + if ($all [$i] == $owns [$j]) + { + $find = 1; + } + } + if (!$find) + { + push (@below, $all [$i]); + } + } + my $nb = @below; + my @elements; + for (my $i = 0; $i < $nb; $i ++) + { + my @fils = $zinc -> find ('ancestors', $below [$i]); + push (@elements, '-'.$below [$i], @fils); + } + my $main; + for (my $j = 0; $j < @elements; $j ++) + { + if (index($elements [$j], '-') == 0) + { + $elements [$j] = substr ($elements [$j], 1, length ($elements [$j])); + $main = $elements [$j]; + } + for (my $i = 0; $i < @objects; $i++) + { + if (minstance ($objects [$i]) == $elements [$j]) + { + return ($objects [$i], $main); + } + } + } + return undef; +} + +sub __update_all { + my ($self) = @_; + my $selection_changed = 0; + $self -> {_selected} = 0; + $self -> {__selected_plan} = -1; + if (defined $self -> {__objects}) + { + my @object_plan = @{$self -> {__objects}}; + for (my $j = 0; $j < @object_plan; $j ++) + { + if (defined $self -> {__objects} [$j]) + { + my @items = @{$self -> {__objects} [$j]}; + for (my $i = 0; $i < @items; $i++) + { + $selection_changed = $selection_changed || $self -> __update_surrouding ($items [$i], ($self -> {__selected_plan} != -1), $self -> {__points}); + } + if ($self -> {_selected} && $self -> {__selected_plan} == -1) + { + $self -> {__selected_plan} = $j; + } + } + } + } + if ($selection_changed) + { + $self -> notify ('SELECTION_CHANGED', $self) + } +} + +sub getSelectedPlan { + my ($self) = @_; + return $self -> {__selected_plan}; +} + +sub __update_surrouding { + my ($self, $item, $force_non_sel, $coords) = @_; + my $center_x = $item -> mget ('barycentre_x'); + if ($item -> propertyExists ('x')) + { + $center_x += $item -> mget ('x'); + } + my $center_y = $item -> mget ('barycentre_y'); + if ($item -> propertyExists ('y')) + { + $center_y += $item -> mget ('y'); + } + my ($tmp,$ref); + $ref = -$self -> __angle($coords, @{$coords} - 1, $center_x, $center_y, 0); + $tmp = -$self -> __angle($coords, @{$coords} - 2, $center_x, $center_y, $ref); + $item -> {__surround_angle} += $tmp; + $tmp = $self -> __angle($coords, 0, $center_x, $center_y, $ref); + if ($force_non_sel) + { + if ($item -> isSelected ()) + { + $item -> setSelected (0); + } + } + else + { + if (abs ($item -> {__surround_angle} + $tmp) > 2) + { + $self -> {_selected} ++; + if (!$item -> isSelected ()) + { + $item -> setSelected (1); + return 1; + } + } + else + { + if ($item -> isSelected ()) + { + $item -> setSelected (0); + return 1; + } + } + } + return 0; +} + +sub __angle { + my ($self, $coords, $index, $center_x, $center_y, $ref) = @_; + my $angle; + my $tab = $coords -> [$index]; + $angle = atan2 ($tab -> [1] - $center_y, $tab -> [0] - $center_x) + $ref; + $angle -= 2*pi if ($angle > pi); + $angle += 2*pi if ($angle < - pi); + return $angle; +} + +sub __reset_surrounding { + my ($self) = @_; + if (defined $self -> {__objects}) + { + my @object_plan = @{$self -> {__objects}}; + for (my $j = 0; $j < @object_plan; $j ++) + { + if (defined $self -> {__objects} [$j]) + { + my @items = @{$self -> {__objects} [$j]}; + for (my $i = 0; $i < @items; $i++) + { + $items [$i] -> {__surround_angle} = 0; + } + } + } + } +} + +sub applySelection { + my ($self, $plan, $fct, @args) = @_; + my @retour; + if (defined $self -> {__objects} [$plan]) + { + my @items = @{$self -> {__objects} [$plan]}; + for (my $i = 0; $i < @items; $i++) + { + if ($items [$i] -> isSelected ()) + { + push (@retour, $items [$i] -> $fct (@args)); + } + } + } + return @retour; +} + +sub applyAll { + my ($self, $plan, $fct, @args) = @_; + my @retour; + if (defined $self -> {__objects} [$plan]) + { + my @items = @{$self -> {__objects} [$plan]}; + for (my $i = 0; $i < @items; $i++) + { + push (@retour, $items [$i] -> $fct (@args)); + } + } + return @retour; +} + +sub getSelection { + my ($self, $plan) = @_; + if (!defined $plan) {$plan = $self -> getSelectedPlan ();} + my @selection; + if (defined $self -> {__objects} [$plan]) + { + my @items = @{$self -> {__objects} [$plan]}; + for (my $i = 0; $i < @items; $i++) + { + if ($items [$i] -> isSelected ()) + { + push (@selection, $items [$i]); + } + } + } + return @selection; +} + +1; diff --git a/src/MTools/Comp/MReconizer.pm b/src/MTools/Comp/MReconizer.pm new file mode 100644 index 0000000..9605eb9 --- /dev/null +++ b/src/MTools/Comp/MReconizer.pm @@ -0,0 +1,145 @@ +package MTools::Comp::MReconizer; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Le composant permet de rendre un objet zinc sensible a la reco de geste +# +# Parametres : +# * src : objet rendu sensible et donc source des evenements +# * button : bouton de la souris utilise pour genere la reco de geste +# * %options : table de hash permettant la configuration initiale des proprietes +# Proprietes : +# * animation_duration : duree de l'animation de disparition du feedback +# * color : couleur du feedback +# * callback : callback appelee sur reconnaissance +# Evenements : +# * START_GESTURE_RECO : evenement survenant lors du demarrage d'un geste +# * RECONIZED : evenement survenant lorsque un geste est reconnu + +use strict; +use MTools; +use MTools::MObjet; +use Recogestures; + +use MTools::Anim::MOpacity; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, $src, $button, %options) = @_; + my $self = new MTools::MObjet (); + bless $self, $class; + + $button = 1 if ! defined $button; + + $self -> recordEvent ('RECONIZED'); + $self -> recordEvent ('START_GESTURE_RECO'); + $self -> recordProperty ('animation_duration', 0.5); + $self -> recordProperty ('color', 'blue'); + $self -> recordProperty ('callback', undef); + + $self -> mconfigure (%options); + + binding ($src, "<Button-$button>", [\&__pressed, $self, Ev('x'), Ev('y')]); + binding ($src, "<Button$button-Motion>", [\&__moved, $self, Ev('x'), Ev('y')]); + binding ($src, "<ButtonRelease-$button>", [\&__released, $self, Ev('x'), Ev('y')]); + + $self -> binding ('RECONIZED', sub { + my $methode = $self -> mget ('callback'); + if (defined $methode) + { + executer ($methode, @_); + } + }); + $self -> {__dessin} = undef; + + $self -> {__dessin} = my $dessin = new MTools::MGroup (1); + $self -> {__fleche} = $zinc -> add ('curve', minstance($dessin), [[0,0],[0,0]], + -linecolor => 'blue', + -linewidth => 2, + -priority => 10, + -visible => 1, + -sensitive => 0, + ); + + $self -> {__anim__disparition} = new MTools::Anim::MOpacity ( + duration => 0.8, + targets => $dessin, + from_opacity => 100, + to_opacity => 0, + ); + binding ($self -> {__anim__disparition}, 'ANIMATION_END', [$self, \&__clear]); + plink ([$self, 'color'], [$self -> {__fleche}, '-linecolor']); + plink ([$self, 'animation_duration'], [$self -> {__anim__disparition}, 'duration']); + + return $self; +} + +sub __clear () { + my ($self, $x, $y) = @_; + $self -> {__points} = (); + $zinc -> coords ($self -> {__fleche}, 0, [[0,0],[0,0]]); + $self -> {__dessin} -> mconfigure ( + -alpha => 100, + ); +} + +sub __pressed { + my ($self, $x, $y) = @_; + ($x, $y) = $zinc -> transform('device', minstance ($self -> {__dessin}), [$x, $y]); + if (defined $self -> {__dessin}) + { + push (@{$self -> {__points}}, [$x, $y]); + } + $self -> notify ('START_GESTURE_RECO', $x, $y); +} + +sub __moved { + my ($self, $x, $y) = @_; + ($x, $y) = $zinc -> transform('device', minstance ($self -> {__dessin}), [$x, $y]); + push (@{$self -> {trace}},$x,$y); + + if (defined $self -> {__dessin}) + { + push (@{$self -> {__points}}, [$x, $y]); + my @pts = @{$self -> {__points}}; + $zinc -> coords ($self -> {__fleche}, 0, \@pts); + } +} + +sub __released { + my ($self, $x, $y) = @_; + + my ($gesture,$explanation) = AnalyzeGesture(@{$self -> {trace}}); + $self -> {trace} = (); + $self -> notify ('RECONIZED', $gesture, $explanation); + + if (defined $self -> {__dessin}) + { + $self -> {__anim__disparition} -> start (); + } +} + +1; + diff --git a/src/MTools/Comp/MTremor.pm b/src/MTools/Comp/MTremor.pm new file mode 100644 index 0000000..25b5023 --- /dev/null +++ b/src/MTools/Comp/MTremor.pm @@ -0,0 +1,121 @@ +package MTools::Comp::MTremor; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Le composant permet d'ajouter un comportement de tremblement ? un objet cible +# +# Parametres : +# * target : objet cible du tremblement +# * %options : table de hash permettant la configuration initiale des proprietes +# Propriete : +# * timeout : periode / 2 du tremblement +# * target : cible +# * amplitude_x, amplitude_y : amplitude du deplacement +# * -visible : visibilite versus activation du comportement + +use strict; +use MTools; +#use MTools::MSwitch; +use MTools::MTimer; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MObjet/; +} + +use Tk; + +sub new { + my ($class, $target, %options) = @_; + + $self -> {__timer} = my $timer = new MTools::MTimer (200, 1, \&tremble); + my $self = new MTools::MObjet (); +# $self -> {__sw} = new MTools::MSwitch ( +# $parent, +# tremble => [$timer], +# fixe => [], +# ); +# $self -> {__sw} -> mconfigure (state => 'fixe'); + $timer -> mconfigure ( callback => [$self, '__tremble']); + bless $self, $class; + + $self -> recordProperty ('timeout', 200); + $self -> recordProperty ('target', $target); + $self -> recordProperty ('amplitude_x', 2); + $self -> recordProperty ('amplitude_y', 2); + $self -> recordProperty ('-visible', 0); + $self -> plisten ('-visible', sub { + my ($src, $key, $val) = @_; + if ($val == 0) + { + $self -> __stop (); + } + else + { + $self -> __start (); + } + }); + + $self -> mconfigure (%options); + + plink ([$self, 'timeout'], [$timer, 'timeout']); + $self -> {__timer} = $timer; + $self -> {__x} = 0; + $self -> {__y} = 0; + $self -> {__started} = 0; + return $self; +} + +sub __tremble { + my ($self) = @_; + my $target = $self -> mget ('target'); + my $dx = -2 * $self -> {__x}; + my $dy = -2 * $self -> {__y}; + $self -> {__x} = -$self -> {__x}; + $self -> {__y} = -$self -> {__y}; + $target -> translate ($dx, $dy); +} + +sub __start { + my ($self) = @_; + if ($self -> {__started}) {return;} + $self -> {__started} = 1; + my $target = $self -> mget ('target'); + my $amplitudex = $self -> mget ('amplitude_x'); + my $amplitudey = $self -> mget ('amplitude_y'); + $self -> {__x} = $amplitudex; + $self -> {__y} = $amplitudey; + $target -> translate ($amplitudex, $amplitudey); +# $self -> {__sw} -> mconfigure ('state' => 'tremble'); + $self -> {__timer} -> start (); +} + +sub __stop { + my ($self) = @_; + if (!$self -> {__started}) {return;} + $self -> {__started} = 0; + my $target = $self -> mget ('target'); + $target -> translate (-$self -> {__x}, -$self -> {__y}); + $self -> {__x} = 0; + $self -> {__y} = 0; + $self -> {__sw} -> mconfigure ('state' => 'fixe'); + $self -> {__timer} -> stop (); +} + +1; diff --git a/src/MTools/Comp/MWritable.pm b/src/MTools/Comp/MWritable.pm new file mode 100644 index 0000000..8a2a2d5 --- /dev/null +++ b/src/MTools/Comp/MWritable.pm @@ -0,0 +1,276 @@ +package MTools::Comp::MWritable; +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU LGPL Libray General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library General Public License for more details. +# +# You should have received a copy of the GNU Library General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, +# or refer to http://www.gnu.org/copyleft/lgpl.html +# +################################################################## + +# Le composant MWritable permet d'associer un comportement scritptible a un objet zinc +# Le composant peut alors etre ecrit et l'ecriture effacee +# +# Parametres : +# * parent : objet parent des curves qui vont etre dessinee au cours de l'ecriture +# * src : objet source des evenements qui vont generer le dessin +# * button : bouton de la souris utilise pour genere la reco de geste +# * %options : table de hash permettant la configuration initiale des proprietes et de definir un objet clip ($options {clip}) +# l'objet clip permet de contenir l'ecriture libre dans une zone. +# Proprietes : +# * color : couleur d'ecriture +# * writing_mode : ('write' ou 'erase') permet de specifier le resultat de l'interaction sur l'objet source (ecriture ou effacement) +# Evenements : +# * BEGIN_WRITE : Message emis lors d'un debut d'ecriture +# * WRITE : Message emis lors de l'ecriture +# * END_WRITE :Message emis lors d'une fin d'ecriture +# * ERASE : Message emis lors de l'effacement +# Fonctions : +# * begin_write : force un debut d'ecriture +# * write : force l'ecriture +# * end_write : force une fin d'ecriture +# * erase : force un effacement + + +use strict; +use MTools; +use MTools::MObjet; +use MTools::GUI::MClip; + +use vars qw /@ISA/; + +BEGIN +{ + @ISA = qw /MTools::MGroup/; +} + +use Tk; + +sub new { + my ($class, $parent, $src, $button, %options) = @_; + my $self = new MTools::MGroup ($parent); + bless $self, $class; + if (defined $options {clip}) + { + $self -> {__clip} = new MTools::GUI::MClip ( + $self, + $options {clip}, + ); + } + delete $options {clip}; + $self -> mconfigure (-atomic => 1); + $self -> recordProperty ('color', '#000000'); + $self -> recordProperty ('writing_mode', 'write'); + $self -> recordEvent ('BEGIN_WRITE'); + $self -> recordEvent ('END_WRITE'); + $self -> recordEvent ('WRITE'); + $self -> recordEvent ('ERASE'); + $button = 1 if ! defined $button; + binding ($src, "<Button-$button>", [\&__beginWrite, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, "<Button$button-Motion>", [\&__writing, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($src, "<ButtonRelease-$button>", [\&__endWrite, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($self, "<Button-$button>", [\&__beginWrite, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($self, "<Button$button-Motion>", [\&__writing, $self, Ev('x'), Ev('y'), Ev('t')]); + binding ($self, "<ButtonRelease-$button>", [\&__endWrite, $self, Ev('x'), Ev('y'), Ev('t')]); + $self -> {__curves} = (); + $self -> {__points} = (); + $self -> {__current_curves} = (); + $self -> {__tmp_curves} = new MTools::MGroup ($self); + return $self; +} + +sub __beginWrite { + my ($self, $x, $y) = @_; + ($x, $y) = $zinc -> transform ('device', minstance ($self), [$x, $y]); + $self -> beginWrite ($x, $y); + $self -> notify ('BEGIN_WRITE', $x, $y) +} + +sub beginWrite { + my ($self, $x, $y) = @_; + push (@{$self -> {__old_coords}}, ($x, $y)); +} + +sub __endWrite { + my ($self, $x, $y) = @_; + if ($self -> mget ('writing_mode') eq 'write') + { + ($x, $y) = $zinc -> transform ('device', minstance ($self), [$x, $y]); + $self -> writing ($x, $y); + $self -> notify ('WRITE', $x, $y); + $self -> notify ('END_WRITE'); + $self -> endWrite (); + } + else + { + $self -> {__old_coords} = (); + } +} + +sub endWrite { + my ($self) = @_; + if (defined $self -> {__points} && @{$self -> {__points}}) + { + my @points = @{$self -> {__points}}; + push (@{$self -> {__curves}}, $zinc -> add ( + 'curve', + minstance ($self), + [@points], + -linecolor => $self -> mget ('color'), + -linewidth => 1, + -priority => 2, + -visible => 1, + -sensitive => 0 + ) + ); + $self -> {__points} = (); + $self -> {__tmp_curves} -> mdelete (); + $self -> {__tmp_curves} = new MTools::MGroup ($self); + } + $self -> {__old_coords} = (); +} + +sub __writing { + my ($self, $x, $y) = @_; + if ($self -> mget ('writing_mode') eq 'write') + { + ($x, $y) = $zinc -> transform ('device', minstance ($self), [$x, $y]); + $self -> writing ($x, $y); + $self -> notify ('WRITE', $x, $y) + } + else + { + erase ($self, $x, $y); + } +} + + +sub writing { + my ($self, $x, $y) = @_; + $self -> write ($x, $y); +} + +sub write { + my ($self, $x, $y) = @_; + my @coords; + push (@{$self -> {__old_coords}}, ($x, $y)); + my $list_size = @{$self -> {__old_coords}}; + if ($list_size >= 6) + { + my $x1 = $self -> {__old_coords} -> [$list_size - 6]; + my $y1 = $self -> {__old_coords} -> [$list_size - 5]; + my $x2 = $self -> {__old_coords} -> [$list_size - 4]; + my $y2 = $self -> {__old_coords} -> [$list_size - 3]; + my $x3 = $self -> {__old_coords} -> [$list_size - 2]; + my $y3 = $self -> {__old_coords} -> [$list_size - 1]; + my $cx2 = ($x1 - $x3) * 0.2 + $x2; + my $cy2 = ($y1 - $y3) * 0.2 + $y2; + if ($list_size == 6) + { + @coords = ([$x1,$y1],[$cx2,$cy2,'c'],[$x2,$y2]); + } + else + { + my $cx1 = ($x2 - $self -> {__old_coords} -> [$list_size - 8]) * 0.2 + $x1; + my $cy1 = ($y2 - $self -> {__old_coords} -> [$list_size - 7]) * 0.2 + $y1; + @coords = ([$x1, $y1], [$cx1, $cy1, 'c'], [$cx2, $cy2, 'c'], [$x2, $y2]); + } + push (@{$self -> {__points}}, @coords); + push (@{$self -> {__current_curves}}, $zinc -> add ( + 'curve', + minstance ($self -> {__tmp_curves}), + [@coords], + -linecolor => $self -> mget ('color'), + -linewidth => 1, + -priority => 2, + -visible => 1, + -sensitive => 0 + ) + ); + } +} + +my $k = 5; +sub erase { + my ($self, $x, $y) = @_; + ($x, $y) = $zinc -> transform ('device', minstance ($self), [$x, $y]); + if (defined $self -> {__curves}) + { + my @curves = @{$self -> {__curves}}; + for (my $i = @curves - 1; $i >= 0 ; $i --) + { + my @points = $zinc -> coords ($curves [$i], 0); + for (my $j = 0; $j < @points - 1; $j ++) + { + my $pt1 = $points [$j]; + my $pt2 = $points [$j + 1]; + + if ($pt1 -> [0] == $pt2 -> [0]) + { + if (in ($y, $pt1 -> [1], $pt2 -> [1]) && ( abs ($pt1 -> [0] - $x) <= $k)) + { + $self -> __deleteCurve ($i); + last; + } + } + else + { + my $a = ($pt1 -> [1] - $pt2 -> [1]) / ($pt1 -> [0] - $pt2 -> [0]); + my $b = $pt2 -> [1] - $a * $pt2 -> [0]; + my $ar = ($a ** 2 + 1); + my $br = (2 * $a * ($b - $y) - 2 * $x); + my $cr = ($x ** 2 + ($b - $y) ** 2 - $k ** 2); + my $d = $br ** 2 - 4 * $ar * $cr; + if ($d >= 0) + { + if (mdist ($pt1 -> [0], $pt1 -> [1], $x ,$y) || mdist ($pt2 -> [0], $pt2 -> [1], $x ,$y)) + { + $self -> __deleteCurve ($i); + last; + } + my $x1 = (-$br + sqrt ($d)) / (2 * $ar); + my $x2 = (-$br - sqrt ($d)) / (2 * $ar); + if (in ($x1, $pt1 -> [0], $pt2 -> [0]) || in ($x2, $pt1 -> [0], $pt2 -> [0])) + { + $self -> __deleteCurve ($i); + last; + } + } + } + } + } + } +} + +sub __deleteCurve { + my ($self, $index) = @_; + $self -> notify ('ERASE', $index); + $self -> deleteCurve ($index); +} + +sub deleteCurve { + my ($self, $index) = @_; + $self -> {__old_coords} = (); + mdelete ($self -> {__curves} [$index]); + splice (@{$self -> {__curves}}, $index, 1); +} + +sub in { + my ($x, $x0, $x1) = @_; + return (($x0 <= $x) && ($x <= $x1) || ($x1 <= $x) && ($x <= $x0)); +} + +sub mdist { + my ($x1, $y1, $x2, $y2) = @_; + return (($x1 - $x2) ** 2 + ($y1 - $y2) ** 2 <= ($k) ** 2); +} + +1; |