#!/usr/bin/perl
#
#KanDeLa.
#Copyright (C) 2011 Alain Barbason
#
#This file is part of KanDeLa.
#
#KanDeLa is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#KanDeLa is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with KanDeLa.  If not, see <http://www.gnu.org/licenses/>.
#
# rewrite : how do manage object foreground background (see upper grid in Image.pm)
use warnings;
use strict;
use utf8;
use Tk;
use Data::Dumper;
use Epingle;
use Line;
use Linepique;
use Image;
use Toeval;
use Handle;
use Trait;
use Curve;
use Math::Trig;
# init
my %liste;
my @drawing_classes=("Epingle", "Line", "Linepique", "Trait", "Curve")
  ; # list of class to draw. Keep "Epingle" in first place because it's necessary for the respect of color, to use Epingle before Line.
my @all_classes=(
    "Image", "Toeval", "Epingle", "Line", "Linepique", "Trait", "Curve"
);
my $open_file;    # name of the file to save the drawing
my $step=40;
my $sf_image;
my $lang="fr";
my $mode;         # rewrite the mode system
my @mode;
$mode[1]=gettext("Select a thread");
$mode[2]=gettext("Select multiple threads");
$mode[3]=gettext("Multiple select");
$mode[4]=gettext("Multiple copy");
$mode[14]=gettext("Parallel threads");
$mode[15]=gettext("Cross threads");
$mode[17]=gettext("Square of pins");
$mode[19]=gettext("Eraser");
$mode[21]=gettext("Create a thread");
$mode[22]=gettext("Add twist");
$mode[31]=gettext("Move");
$mode[32]=gettext("Normal mode");
$mode[33]=gettext("Append mode");
$mode[34]=gettext("Modify Curve");
$mode[35]=gettext("Create Curve");
$mode[36]=gettext("Create Pique");
$mode[37]=gettext("Lozenge Select");
my $affmode;
my $currentcouleur="red";
my $currentcouleur2="green";
my ($i, $j, $ip, $jp);
my $o=90;    # angle de l'orignie d'une dorite  en degree
my $op=270;
my @liste;    # la liste de tous les objets
my $id_c;     # id que le canvas renvoie (createXyz)
my $select="";
my $select_area;    # element canevas pour dessiner un rectangle de selection.
my $zoom=1;         # facteur de zoom
my $grille=1;       # active la grille, defaut actif
my $angle=0;        # angle pour un pave
my $x_press=0;      # memorise ou on a c liqué
my $y_press=0;
my $x_release=0;    # memorise ou on a c liqué
my $y_release=0;
my $type_zone;      # rewrite
my $rect_width;
my $rect_hight;
my $pattern_marging=0;
my $pattern_file;
my $state_pique=0;
my $background
  ;    # presently, just one image as background, bad manage of global variable
my $back_name;
my $back_x;
my $back_y;
my $view_grid=1;          # to view or view in grey the grid.
my $view_text=1;          # to view or not the text for each pin.
my $view_background=1;    # to view or not the text for each pin.
my $view_number="";       # to view numer of a pin.
my $curve_select;
my %handle;
my @curve;
my $polygon;
my $obj_press;
my $angle_trait;
my $sf_line;    # sub frame with button, scale,... to modify line properties
my $value_tordre;
my $value_segment=50;
my $value_segment2=50;
my %gettext;
my $cadre;
sub gettext
{ # rewrite it's a bad patch because i don't know how use the paquage gettext, maketext,....
    my $param=shift;
    if (($lang ne "en") && ($gettext{$param})) {
        return ($gettext{$param});
    }
    else {
        return ($param);
    }
}
sub initGetText {
    open my $CONF_INI, "<", "config.ini"
      || die
      "fail to open config.ini";    # fixme-m create config.ini if not exist
    my $line=<$CONF_INI>;
    chomp $line;                    # fixme-m create a good code for read .ini
    (my $tmp, $lang)=split("=", $line);
    close $CONF_INI;
    if ($lang ne "en") {
        my $file_get_text="$lang/LC_MESSAGES/kandela.po";
        open my $LANG, "<", $file_get_text
          || die "traduction file is missing";    # fixme-m how fix that ?
        while (my $ligne=<$LANG>) {
            if ($ligne =~ /msgid/) {
                my ($tmp, $msg)=split("\"", $ligne);
                if ($msg) {
                    my $line2=<$LANG>;
                    my ($tmp, $msg_trad)=split("\"", $line2);
                    #print "$msg $msg_trad\n";
                    $gettext{$msg}=$msg_trad;
                }
            }
        }
        close $LANG;
    }
    return;
}
initGetText();
initWindow();
my $trace_zone=$::drawing->createPolygon(
    0, 0, 0, 0, 0, 0, 0, 0,
    -fill    => "white",
    -outline => "black",
    -state   => "hidden",
);
my $draw_line;
sub round {
    my $number=shift;
    return (int($number+.5*($number <=> 0)));
}
sub mytitle {
    my $open_file=shift;
    my @nameFile=
      split(/\//, $open_file); # rewrite. Ok for Linux, not Windows but works !
    $window->title("KanDeLa - " . pop(@nameFile));
    return;
}
sub writePs {
    if (!$open_file) {         # necessary to have a filename to save
        saveAsFile();
        if (!$open_file) {return;}
    }
    gridDisable();
    deselect();
    my ($x, $y, $xx, $yy)=$drawing->bbox("!grille");
    my $page_height=
      ($yy-$y)/$step/2*4;      # todo defined size between 2 pin with menu
    $Commun::view_technique=0;
    $Commun::view_pique=1;
    setView();
    my $nameFileNoExt=$open_file;
    $nameFileNoExt =~ s/.lace//;    # rewrite try s/.lace/_pique.ps/
    my $nameFile=$nameFileNoExt . "_pique.ps";
    $drawing->postscript(
        -file       => $nameFile,
        -x          => $x,
        -y          => $y,
        -width      => $xx-$x,
        -height     => $yy-$y,
        -pageheight => "$page_height m"
    );
    system("ps2pdf $nameFile");     # fixme-m find a solution for windows
                                    # may be http://www.ps2pdf.com/convert.htm
        # may be find a program to print directly in .ps
        # desactivate this line if os==windows
#    Image->normal(); # todo create 3th impression with imagebackground if image exist :-)
#    technique();
#    $nameFile=$nameFileNoExt . "_image.ps";
#    $drawing->postscript(
#        -file       => $nameFile,
#        -x          => $x,
#        -y          => $y,
#        -width      => $xx-$x,
#        -height     => $yy-$y,
#        -pageheight => "17c"      # calculer en focntion du pas et de la taille
#    );
#    system("ps2pdf $nameFile");
    $Commun::view_technique=1;
    $Commun::view_pique=0;
    setView();
    Image->hidden();
    $nameFile=$nameFileNoExt . "_technique.ps";
    $page_height *= 3;    # fixme-m Change size otherway
    if ($page_height > 277) {
        $page_height=277;
    }    # todo : limit for a A4 page, create a dialog box for different size
    my $page_width=($xx-$x)/$step/2*4*3;
    if ($page_width > 190) {$page_height=190;}
    $drawing->postscript(
        -file       => $nameFile,
        -x          => $x,
        -y          => $y,
        -width      => $xx-$x,
        -height     => $yy-$y,
        -pageheight => "$page_height m"
    );
    system("ps2pdf $nameFile");
    return;
}
sub subFrameLine
{ # fixme-m better initialised values. When select item, have a %init to initialized subFrame ?
        # and analyse how  -command work and when (on a "pack" ?)
    my $cadre=shift;
    $sf_line=$cadre->Frame();
    my $k=0;
    $sf_line->Scale(
        -from       => 0,
        -to         => 360,
        -resolution => 30,
        -orient     => "horizontal",
        -variable   => \$o,
        -command    => sub {changeAngleOrigine()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Scale(
        -from       => 0,
        -to         => 360,
        -resolution => 30,
        -orient     => "horizontal",
        -orient     => "horizontal",
        -variable   => \$op,
        -command    => sub {changeAngleExtremite()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Button(
        -text    => "Nbre Torsion",
        -command => sub {ajoutUneTorsion()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Scale(
        -from     => 0,
        -to       => 100,
        -orient   => "horizontal",
        -variable => \$value_tordre,
        -command  => sub {changeTordre()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Scale(
        -from     => 0,
        -to       => 100,
        -orient   => "horizontal",
        -variable => \$value_segment,
        -command  => sub {changeSegment()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Scale(
        -from     => 0,
        -to       => 100,
        -orient   => "horizontal",
        -variable => \$value_segment2,
        -command  => sub {changeSegment2()}
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Button(
        -text    => "color",
        -command => sub {
            $currentcouleur=Line->colorSelect($currentcouleur);
        }
    )->grid(-column => 0, -row => $k, -columnspan => 2);
    $k++;
    $sf_line->Checkbutton(
        -text     => "Pique",
        -variable => \$state_pique,
        -command  => sub {flipPique();}
    )->grid(-column => 0, -row => $k);
    return;
}
sub subFrameImage {
    my $cadre=shift;
    $sf_image=$cadre->Frame();
    my $e_name=
      $sf_image->Label(-textvariable => \$back_name)->grid(-columnspan => 4);
    my $b_mx=$sf_image->Button(
        -text    => "<",
        -command => sub {
            $background->x($back_x--);
        }
    );
    my $b_px=$sf_image->Button(
        -text    => ">",
        -command => sub {
            $background->x($back_x++);
        }
    );
    my $l_x=$sf_image->Label(-text => "X");
    $sf_image->Entry(-textvariable => \$back_x,)->grid($b_mx, $b_px, $l_x);
    my $b_my=$sf_image->Button(
        -text    => "^",
        -command => sub {
            $background->y($back_y--);
        }
    );
    my $b_py=$sf_image->Button(
        -text    => "v",
        -command => sub {
            $background->y($back_y++);
        }
    );
    my $l_y=$sf_image->Label(-text => "Y");
    $sf_image->Entry(-textvariable => \$back_y,)->grid($b_my, $b_py, $l_y);
    my $b_ok=$sf_image->Button(
        -text    => "Ok",
        -command => sub {
            $sf_image->packForget;
        }
    );
    $sf_image->Button(
        -text    => "Supprime",
        -command => sub {
            $background->supprime;
            undef($background);
            undef($back_name);
            $back_x=0;
            $back_y=0;
            $sf_image->packForget;
        }
    )->grid($b_ok);
}
sub subFrameCurve {
    my $cadre=shift;
    $sf_curve=$cadre->Frame();
    $sf_curve->Button(
        -text    => "Ok",
        -command => \&createCurve
    )->pack();
    $sf_curve->Button(
        -text    => "color",
        -command => sub {
            $currentcouleur=Curve->colorSelect($currentcouleur);
        }
    )->pack(-side => 'left');
    # todo for change smooth
    #    $sf_curve->Scale(
    #        -from     => 0,
    #        -to       => 180,
    #        -orient   => "horizontal",
    #        -variable => \$angle_trait,
    #        -command  => sub {angleTrait();}
    #    )->pack();
}
sub subFrameTwist {
    my $cadre=shift;
    $sf_trait=$cadre->Frame();
    $sf_trait->Scale(
        -from     => 0,
        -to       => 180,
        -orient   => "horizontal",
        -variable => \$angle_trait,
        -command  => sub {angleTrait();}
    )->pack();
}
sub subFramePin {
    my $cadre=shift;
    $sf_pin=$cadre->Frame();
    $sf_pin->Button(
        -text    => "color",
        -command => sub {
            $currentcouleur=Epingle->colorSelect($currentcouleur);
        }
    )->grid();
    my $b_ok=$sf_pin->Button(
        -text    => "Ok",
        -command => sub {
            $view_number =~ s/ /./g;
            Epingle::configureAll(-number => $view_number);
            redrawAll();    # fixme-m to remove
        }
    );
    $sf_pin->Entry(-textvariable => \$view_number)->grid($b_ok);
}
sub buttonFrame {
    # right frame to change color, size, ... for each object.
    my $cadre=shift;
    subFrameImage($cadre);
    subFrameLine($cadre);
    subFrameCurve($cadre);
    subFrameTwist($cadre);
    subFramePin($cadre);
    $cadre->Label(-textvariable => \$affmode)->pack(-side => 'bottom');
    return;
}
sub angleTrait {
    foreach my $trait (values %Trait::liste) {
        if ($trait->select) {
            $trait->configure(-angle => $angle_trait);
            $trait->redraw();
        }
    }
    return;
}
# rewrite create "sub align" with parameter
sub alignStar2 {
    my $x=shift;
    if   ($x > 0) {return (int($x/$step/2+0.5)*$step*2);}
    else          {return (int($x/$step/2-0.5)*$step*2);}
}
sub align {
    my $x=shift;
    if   ($x > 0) {return (int($x/$step+0.5)*$step);}
    else          {return (int($x/$step-0.5)*$step);}
}
sub alignSlash2 {    # in fact slash4
    my $x=shift;
    if   ($x > 0) {return (int($x/$step*4+0.5)*$step/4);}
    else          {return (int($x/$step*4-0.5)*$step/4);}
}
sub alignIfGrid {
    my $k=shift;
    return $grille ? align($k) : round($k);
}
sub gridEnable {
    $grille=1;
    $drawing->itemconfigure("grille", -outline => "black");
    return;
}
sub gridDisable {
    $grille=0;
    $drawing->itemconfigure("grille", -outline => "grey");
    return;
}
sub gridDraw {
    for (my $i=-20; $i < 40; $i++) {
        for (my $j=$i%2-20; $j < 40; $j += 2) {
            my $ii=$i*$step;
            my $jj=$j*$step;
            $::drawing->createOval(
                $jj-1,
                $ii-1,
                $jj+1,
                $ii+1,
                -outline => "black",
                -width   => 1,
                -tags    => ["noprint", "grille"],
            );
        }
    }
    return;
}
sub gridAlign {
    foreach my $epingle (values %Epingle::liste) {
        if ($epingle->select()) {
            my $number=$epingle->i/$step;
            $number=int($number+.5*($number <=> 0));
            $epingle->i($number*$step);
            $number=$epingle->j/$step;
            $number=int($number+.5*($number <=> 0));
            $epingle->j($number*$step);
        }
    }
    redrawAll();
    return;
}
sub saveLang {
    my $lang=shift;
    open my $LANG, ">", "config.ini" || die "probleme fichier";
    print $LANG "Lang=$lang\n";
    close $LANG;
    return;
}
sub setMode {
    $mode=shift;
    $affmode=$mode[$mode];
    return;
}
sub escape {
    deselect();
    $drawing->delete($polygon);
    undef($polygon);
    initDrawLine(32);
    return;
}
my $menu;
sub menuFrame {
    my $window=shift;
    $menu=$window->Frame()->pack(-side => "top");
    $menu->Menubutton(
        -text      => gettext("File"),
        -menuitems => [
            [
                'command'    => gettext('New'),
                -command     => \&clear,
                -accelerator => "ctrl-n"
            ],
            [
                'command'    => gettext('Open'),
                -command     => \&openFile,
                -accelerator => "ctrl-o"
            ],
            "-",
            [
                'command'    => gettext('Save'),
                -command     => \&saveFile,
                -accelerator => "ctrl-s"
            ],
            [
                'command' => gettext('Save as ...'),
                -command  => \&saveAsFile,
            ],
            "-",
            ['command' => "ps & pdf", -command => \&writePs],  # fixme-m ctrl-p
            "-",
            [
                'command'    => gettext('Quit'),
                -command     => \&safeClose,
                -accelerator => "alt-F4"
            ]
        ]
    )->pack(-side => "left");
    my $myedit=$menu->Menubutton(
        -text      => gettext("Edit"),
        -menuitems => [
            [
                'command'    => gettext("Undo"),
                -command     => sub {deselect(); Commun::unDo();},
                -accelerator => "ctrl-z"
            ],
            [
                'command'    => gettext("Redo"),
                -command     => sub {deselect(); Commun::reDo();},
                -accelerator => "shift-ctrl-z"
            ],
            "-",
            [
                'command' => gettext("Eraser"),
                -command  => sub {
                    deselect();
                    setMode(19);
                    unBind();
                    $drawing->CanvasBind('all', "<B1-Motion>",
                        [\&efface, Ev('x'), Ev('y')]);
                    $drawing->configure(-cursor => "dot");
                  }
            ],
            "-",
            [
                'command' => gettext("Move"),
                -command  => sub {
                    initMove();
                  }
            ],
            [
                'command' => gettext("Change curve"),
                -command  => sub {
                    setMode(34);
                    $drawing->bind("all", "<Button-1>",
                        [\&action, Ev('x'), Ev('y')]);
                    $drawing->bind("all", "<B1-Motion>",
                        [\&lache, Ev('x'), Ev('y')]);
                    $drawing->configure(-cursor => "crosshair");
                  }
            ],
            "-",
            [
                'command'    => gettext("Normal mode"),
                -command     => sub {escape(), initDrawLine(32)},
                -accelerator => gettext("esc"),
            ],
            [
                'command' => gettext("Create a curve"),
                -command  => [\&initDrawCurve],
            ],
            [
                'command' => gettext("Create a black line"),
                -command  => [\&initDrawLinePique],
            ],
            [
                'command' => gettext("Add twist"),
                -command  => sub {setMode(22); deselect()}
            ],
            "-",
            [
                'command' => gettext("Select a thread"),
                -command  => sub {setMode(1); deselect()}
            ],
            [
                'command' => gettext("Copy a thread"),
                -command  => sub {setMode(2);}
            ],
            "-",
            [
                'command' => gettext("Multiple select"),
                -command  => sub {
                    deselect();
                    $window->configure(-cursor => "left_ptr");
                    setMode(3);
                  }
            ],
            [
                'command' => gettext("Lozenge select"),
                -command  => [\&initDrawLozenge, 37],
            ],
            [
                'command' => gettext("Multiple copy"),
                -command  => sub {
                    $window->configure(-cursor => "circle");
                    # fixme-m test grid/no grid
                    setMode(4);
                    calculSelectionneMultiple();
                    $drawing->CanvasBind("<Motion>",
                        [\&drawRectangle, Ev('x'), Ev('y')]);
                  }
            ],
            "-",
            [
                'command'    => gettext("Select All"),
                -command     => sub {selectAll();},
                -accelerator => "ctrl-a"
            ],
            [
                'command' => gettext("Delet select"),
                -command  => sub {deleteSelect();},
            ],
            [
                'command' => gettext("Fusion select"),
                -command  => sub {fusionSelect();},
            ],
            "-",
            [
                'command' => gettext("Grid align"),
                -command  => \&gridAlign,
            ],
            [
                'command' => "Horizontal flip",
                -command  => sub {flipHorizontal();}
            ],
            [
                'command' => gettext("Copy 3x"),
                -command  => sub {copie90()}
            ],
#            [
#                'command' => "Supprime ligne en double",
#                -command =>
#                  sub
#                { # supprime ligne en double # bug car efface dans uneligne en cours
#                    my $i=0;
#                    foreach my $liste (values %::liste) {
#                        if (ref($liste) eq "Line") {
#                            print $i++, " ";
#                            foreach my $key (keys %::liste) {
#                                if (   (ref($::liste{$key}) eq "Line")
#                                    && ($liste ne $::liste{$key}))
#                                {
#                                    if (   ($liste->o eq $::liste{$key}->o)
#                                        && ($liste->e eq $::liste{$key}->e))
#                                    {
#                                        delete($::liste{$key});
#                                    }
#                                }
#                            }
#                        }
#                    }
#                  }
#            ],
        ]
    )->pack(-side => "left");
    my $myajout=$menu->Menubutton(
        -text      => gettext("Append"),
        -menuitems => [
            [
                'command' => gettext("Parallel threads"),
                -command  => [\&initDrawLine, 14],
            ],
            [
                'command' => gettext("Cross threads"),
                -command  => [\&initDrawLine, 15],
            ],
            "-",
            [
                'command' => gettext("Rectangular set of pins"),
                -command  => sub {setMode(17); deselect();}
            ],
            "-",
        ]
    )->pack(-side => "left");
    # test pattern_with_margin/ on windows. It's OK, just test better.
    foreach my $file (glob("pattern_with_margin/*.lace")) {
        $myajout->AddItems(
            ["command" => $file, -command => [\&initFill, 1, $file]]);
    }
    $myajout->AddItems("separator");
    foreach my $file (glob("pattern_without_margin/*.lace")) {
        $myajout->AddItems(
            ["command" => $file, -command => [\&initFill, 0, $file]]);
    }
    my $menu_view=$menu->Menubutton(
        -text      => gettext("View"),
        -menuitems => [
            [
                'command' => gettext("Insert/configure background"),
                -command  => sub {
                    escape();
                    if ($background) {
                        $back_name=$background->name;
                        $back_x=$background->x;
                        $back_y=$background->y;
                    }
                    else {
                        my $open_file_tmp=$window->getOpenFile(
                            -filetypes =>
                              [['Gif files', '.gif'], ['All files', '*'],],
                            -defaultextension => '.gif'
                        );
                        if (($open_file_tmp))
                        {    # fixme-m check the existence off the file.
                            $back_name=$open_file_tmp;
                            confirmImage("OK");
                        }
                        $back_x=0;
                        $back_y=0;
                    }
                    $sf_image->pack;
                  }
            ],
            "-",
 #            [
 #                'command' => "agrandir",
 #                -command =>
 #                  sub {$zoom *= 1.5; $drawing->scale("all", 0, 0, 1.5, 1.5);}
 #            ],
 #            [
 #                'command' => "reduire",
 #                -command  => sub {
 #                    $zoom *= 0.6666;
 #                    $drawing->scale("all", 0, 0, 0.6666, 0.6666);
 #                  }
 #            ],
        ]
    )->pack(-side => "left");
    $menu_view->checkbutton(
        -label    => gettext("Technical draw"),
        -command  => \&setView,
        -variable => \$Commun::view_technique
    );
    $menu_view->checkbutton(
        -label    => gettext("Pique"),
        -command  => \&setView,
        -variable => \$Commun::view_pique
    );
    $menu_view->checkbutton(
        -label    => gettext("Pin text"),
        -command  => \&setViewText,
        -variable => \$view_text
    );
    $menu_view->checkbutton(
        -label    => gettext("View ()"),
        -command  => \&setViewParenthese,
        -variable => \$view_parenthese
    );
    $menu_view->checkbutton(
        -label    => gettext("Grid"),
        -command  => \&setViewGrid,
        -variable => \$view_grid
    );
    $menu_view->checkbutton(
        -label    => gettext("Image Background"),
        -command  => \&setViewBackground,
        -variable => \$view_background
    );
    #    $menu->Menubutton(
    #        -text      => "Divers",
    #        -menuitems => [
    #            ['command' => "0°",   -command => sub {$angle=0;}],
    #            ['command' => "90°",  -command => sub {$angle=90;}],
    #            ['command' => "180°", -command => sub {$angle=180;}],
    #            ['command' => "270°", -command => sub {$angle=270;}],
    #        ]
    #    )->pack(-side => "left");
    $menu->Menubutton(
        -text      => gettext("Help"),
        -menuitems => [
            [
                'command' => gettext("About"),
                -command  => sub {
                    $window->messageBox(
                        -icon  => 'info',
                        -type  => 'Yes',
                        -title => gettext("About"),
                        -message =>
"Realisation : Alain Barbason 2010\nversion 0.1.1\nkandela\@barbason.be\nhttp://www.barbason.be"
                    );
                  }
            ],
            "-",
            [
                'command' => "Français",
                -command  => sub {
                    saveLang("fr");
                  }
            ],
            [
                'command' => "Nederlands",
                -command  => sub {
                    saveLang("nl");
                  }
            ],
            #            [
            #                'command' => "German",
            #                -command  => sub {
            #                    saveLang("de");
            #                  }
            #            ],
            [
                'command' => "English",
                -command  => sub {
                    saveLang("en");
                  }
            ],
        ]
    )->pack(-side => "right");
    return;
}
sub initBind {
    $window->Tk::bind("<Escape>", \&escape);
    $dr_reel->Tk::bind("<Button-1>", [\&action, Ev('x'), Ev('y')]);
# active the next line to examine the canvas
# $drawing->bind("all","<Enter>", sub{my ($id_c)=$drawing->find("withtag", "current");print $id_c;});
# fixme-m How call a simple letter Key-g and don't have bad interaction with
# the filling of a textbox, the bind is activate also.
    $window->bind("<Control-a>", sub {selectAll()});
    $window->bind("<Delete>",    sub {deleteSelect()});
    # fixme-m if use undo, dont change the mode, not sure, to test
    $window->bind("<Control-z>", sub {deselect(); Commun::unDo()});
    $window->bind("<Control-Z>", sub {deselect(); Commun::reDo()});
    $window->bind('<Control-n>', [\&clear]);
    $window->bind('<Control-o>', [\&openFile]);
    $window->bind('<Control-s>', [\&saveFile]);
    $window->bind('<Control-q>', [\&onClose]);
    $window->bind('<Alt-z>', sub {Commun->undoShow()});
    #    todo erase alone pins (without threat/line). Code follow
    #    $window->bind(
    #        "<Key-v>",
    #        sub {
    #            foreach my $epi (values %Epingle::liste) {
    #                print "nbre epi ", $epi->nbre_fil;
    #                if ($epi->nbre_fil==0) {$epi->supprime}
    #            }
    #        }
    #    );
    return;
}
sub unBind {
    $window->bind("<Motion>", "");
    $drawing->bind("all", "<Button-1>",    "");
    $drawing->bind("all", "<B1-Motion>",   "");
    $drawing->bind("all", "<ButtonPress>", "");
    $drawing->CanvasBind("<B1-Motion>",     "");
    $drawing->CanvasBind('all',             "<B1-Motion>", "");
    $drawing->CanvasBind("<ButtonRelease>", "");
    $drawing->CanvasBind("<Motion>",        "");
    return;
}
sub initWindow {
    $window=MainWindow->new;
    $window->title("KanDeLa");
    menuFrame($window);
    $cadre=$window->Frame()->pack(-side => "right");
    $drawing=$window->Scrolled(
        'Canvas',
        #        -width      => 800,
        #       -height     => 400,
        -confine    => 0,
        -background => "white"
      )->pack(
        -side   => "bottom",
        -expand => 1,
        -fill   => "both",
      );
    $dr_reel=$drawing->Subwidget("canvas");
    buttonFrame($cadre);
    initBind();
    $window->protocol('WM_DELETE_WINDOW' => \&safeClose);
    return;
}
sub onClose {
# fixme-m : when undo is programming by block, open a block, clear all, close the block
    Commun::undoDesactivated();
    clear();
    $window->destroy;
    return;
}
sub safeClose {
    my $rep=$window->messageBox(
        -icon    => 'warning',
        -type    => 'YesNoCancel',
        -default => 'yes',
        -title   => gettext("Quit"),
        -message => gettext("Save your last modifications ?")
    );
    # rewrite test below in one line ?
    # fixme-m: no question save file if not modification
    if (($rep eq 'Yes') && (saveFile())) {
        onClose();
    }
    if ($rep eq 'No') {
        onClose();
    }
    return;
}
sub setViewParenthese {
    print "$view_parenthese ";
    redrawAll();
}
sub setViewText {
    $::drawing->itemconfigure("text",
        -state => $view_text ? "normal" : "hidden");
    return;
}
sub setViewGrid {
    $grille=$view_grid;    # rewrite remove $grille
    $drawing->itemconfigure("grille",
        -outline => $view_grid ? "black" : "grey");
    return;
}
sub setViewBackground {
    if ($view_background) {
        Image->normal();
    }
    else {
        Image->hidden();
    }
    return;
}
sub findCurrent {
    my @liste=$drawing->find("withtag", "current");
    if   (@liste > 0) {return ($::liste{$liste[0]});}
    else              {return;}
}
sub changeAngleOrigine {    # rewrite put in Line.pm
    Line::forSelect(-oa => $o);
    return;
}
sub changeAngleExtremite {
    Line::forSelect(-ea => $op);
    return;
}
sub ajoutUneTorsion {
    foreach my $liste (values %Line::liste) {
        if ($liste->select) {
            $liste->nbre_tordre($liste->nbre_tordre+1);
            $liste->redraw();
        }
    }
    return;
}
# rewrite when make zoom
#sub change_rayon_epingle {
#    my $inc=shift;
#    foreach my $epingle (%Epingle::liste) {
#        if ($epingle->select) {    # fixme-m error if push "o"
#            $epingle->r($epingle->r()+$inc);
#            $epingle->redraw();
#            foreach my $line (values %::liste) {
#                if ((ref($line) eq "Line")) {
#                    if ($line->o eq $epingle) {
#                        $line->redraw();
#                    }
#                    elsif ($line->e eq $epingle) {
#                        $line->redraw();
#                    }
#                }
#            }
#        }
#    }
#    return;
#}
sub efface {
    my ($canvas, $x, $y)=@_;
    $x=$canvas->canvasx($x)
      ; # fixme-m generate error message when I click on menu and move (out of canvas)
    $y=$canvas->canvasy($y);
    my @tags=$drawing->find('overlapping', $x, $y, $x, $y);
    if (@tags) {
        my $obasupprimer=$::liste{$tags[0]};
        if (   (ref($obasupprimer) eq "Line")
            || (ref($obasupprimer) eq "Epingle")
            || (ref($obasupprimer) eq "Trait")
            || (ref($obasupprimer) eq "Curve")
            || (ref($obasupprimer) eq "Linepique"))
        {
            if (exists($Commun::group{$obasupprimer->id})) {
                $Commun::group{$obasupprimer->id}->supprime();
                delete($::liste{$obasupprimer->id});
            }
            else {
                $obasupprimer->supprime();
            }
        }
    }
    return;
}
sub setView {
    # hide all
    # view if viewXyz is set to true
    Image->hidden();
    Toeval->hidden();
    # fixme-m : obsolete, remove the system of tag of tk
    $drawing->itemconfigure("technique", -state => "hidden");
    $drawing->itemconfigure("noprint",   -state => "hidden");
    $drawing->itemconfigure("pique",     -state => "hidden");
    if ($Commun::view_technique) {
        Image->normal();
        Toeval->normal();
        $drawing->itemconfigure("noprint",   -state => "normal");
        $drawing->itemconfigure("technique", -state => "normal");
    }
    if ($Commun::view_pique) {
        $drawing->itemconfigure("pique", -state => "normal");
    }
    redrawAll();
    return;
}
sub flipPique {
    foreach my $liste (values %Line::liste) {
        if ($liste->select) {
            $state_pique=$liste->flipPique();
            $liste->redraw;
        }
    }
    return;
}
sub changeSegment {
    if ($value_segment > $value_segment2) {
        $value_segment2=$value_segment;
    }    # fixme-m put this test in Line.pm
    foreach my $liste (values %Line::liste) {
        if ($liste->select) {
            $liste->seg($value_segment/100);
            $liste->seg2($value_segment2/100);
            $liste->redraw;
        }
    }
    return;
}
sub changeSegment2 {
    if ($value_segment > $value_segment2) {$value_segment=$value_segment2}
    foreach my $liste (values %Line::liste) {
        if ($liste->select) {
            $liste->seg($value_segment/100);
            $liste->seg2($value_segment2/100);
            $liste->redraw;
        }
    }
    return;
}
sub changeTordre {
    foreach my $liste (values %Line::liste) {
        if ($liste->select) {
            $liste->tordre($value_tordre/100);
        }
    }
    return;
}
sub redrawAll {
    foreach my $liste (values %::liste) {    # rewrite
        $liste->redraw();
    }
    return;
}
sub confirmImage {
    if (shift eq "OK") {
        if (!$background) {$background=Image->new()}
        $background->configure(
            -name => $back_name,
            -x    => $back_x,
            -y    => $back_y
        );
    }
    return;
}
sub openFile {
    Commun::undoDesactivated();
    # fixme-m best manage global variable
    my $open_file_tmp=$window->getOpenFile(
        -filetypes => [['Lace files', '.lace'], ['All files', '*'],],
        -defaultextension => '.lace'
    );
    if (($open_file_tmp) && (open my $FILE, "<", $open_file_tmp)) {
        clear();    # fixme-m check undo run
        $Commun::view_technique=1;
        $Commun::view_pique=1;
        $open_file=$open_file_tmp;
        mytitle($open_file);
        my $k;
        while (my $ligne=<$FILE>) {
            chomp $ligne;
            my %param=split(/ /, $ligne);
            my $type=$param{"-type"};
            if ($type eq "Image") {    # fixme-m better Image exception case
                $background=$type->new(%param);
            }
            else {
                $type->new(%param);
            }
            print $k++, "\r";
            $window->update;
        }
        close($FILE);
    }
    $drawing->configure(-scrollregion => [$drawing->bbox("all")]);
    Commun::undoSave();
    return;
}
sub init {
    gridDraw();
    # todo if argument is .lace file, load this file
    $drawing->configure(-scrollregion => [$drawing->bbox("all")])
      ;    # fixme-m not "all" but "all && !grid"
    Commun::undoSave();
    initDrawLine(32);
    return;
}
sub clear {
    # fixme-m ask confirmation because current drawing is erased
    deselect();
    undef($open_file);
    $window->title("KanDeLa");
    undef(%::liste);
    undef(%Epingle::liste);
    undef(%Epingle::champ);
    undef(%Line::liste);
    undef(%Line::created);
    undef(%Image::liste);
    undef(%Curve::liste);
    undef(%Trait::liste);
    undef(%Linepique::liste);
    Commun::reset_id();
    $drawing->configure(-scrollregion => [$drawing->bbox("all")]);
    initDrawLine(32);
    return;
}
sub saveFile {
    if ($open_file) {
        mytitle($open_file);
        open my $FILE, ">", "$open_file";
        foreach my $class (@all_classes) {
            $class->saveAll($FILE);
        }
        close($FILE);
    }
    else {
        saveAsFile();
    }
    return;
}
sub saveAsFile {
    $open_file=$window->getSaveFile(
        -filetypes => [['Lace files', '.lace'], ['All files', '*'],],
        -initialfile      => 'test',
        -defaultextension => '.lace'
    );
    if ($open_file) {saveFile();}
    return ($open_file);
}
sub selectAll {
    deselect();    # fixme-m en fait pour désactiver tous les restes
    foreach my $class (@drawing_classes) {
        $class->selectAll();
    }
    calculSelectionneMultiple();
    return;
}
sub deleteSelect {
    foreach my $class (@drawing_classes) {
        $class->deleteSelect();
    }
    escape()
      ; # rewrite c'est pas necessaire de faire un escape, il faut garder lemode employé, mais sans doute désactiver certaines choses mais je sais plus quoi...
    return;
}
sub fusionSelect {
    # just for pin
    my %liste=Epingle->getListe();
    my @selectedPins;
    foreach my $liste (values %liste) {
        if ($liste->select) {
            push(@selectedPins, $liste);
        }
    }
    # just for 2 pin # todo for multiple pins
    my $pin1=pop(@selectedPins);
    my $pin2=pop(@selectedPins);
    Epingle->fusion($pin1, $pin2);
    escape();
    return;
}
sub copie90 {
    #recherche le pivot (extrème gauche pour l'instant)
    my $min_j=10000;
    my $pivot;
    foreach my $epi (values %Epingle::liste) {
        if ($min_j > $epi->j) {
            $min_j=$epi->j;
            $pivot=$epi;
        }
    }
    my $pi=$pivot->i-$step;
    my $pj=$pivot->j+$step;
    print "pivot $pi $pj ";
    # rewrite best writing code below
    foreach my $liste (values %Epingle::liste) {
        for (my $k=0; $k < 3; $k++) {
            Epingle::findOrCreate( # fixme-m put this in Epingle.pm (idem below)
                Commun::rot3(
                    $pi+(1-$k)*$step, $pj-(1+($k%2))*$step,
                    (1+$k)*90,        $liste->i-$pi,
                    $liste->j-$pj
                ),
                $liste->color
            );
            $window->update;
        }
    }
    foreach my $liste (values %Line::liste) {
        for (my $k=0; $k < 3; $k++) {
            # rewrite duplicate line, and after modify $o,$oa,...
            my ($oi, $oj)=Commun::rot3(
                $pi+(1-$k)*$step, $pj-(1+($k%2))*$step, (1+$k)*90,
                $liste->o->i-$pi, $liste->o->j-$pj
            );
            my ($ei, $ej)=Commun::rot3(
                $pi+(1-$k)*$step, $pj-(1+($k%2))*$step, (1+$k)*90,
                $liste->e->i-$pi, $liste->e->j-$pj
            );
            my $o=Epingle::findOrCreate($oi, $oj);
            my $e=Epingle::findOrCreate($ei, $ej);
            Line->new(
                -ido         => $o->id,
                -oa          => $liste->oa+(1+$k)*90,
                -ide         => $e->id,
                -ea          => $liste->ea+(1+$k)*90,
                -seg         => $liste->seg,
                -seg2        => $liste->seg2,
                -couleur     => $liste->couleur,
                -torde       => $liste->tordre,
                -nbre_tordre => $liste->nbre_tordre,
                -pique       => $liste->pique,
            );
            $window->update;
        }
    }
    foreach my $liste (values %Trait::liste) {
        for (my $k=0; $k < 3; $k++) {
            my ($i, $j)=Commun::rot3(
                $pi+(1-$k)*$step,
                $pj-(1+($k%2))*$step,
                (1+$k)*90, $liste->i-$pi, $liste->j-$pj
            );
            Trait->new(
                -i     => $i,
                -j     => $j,
                -angle => $liste->angle+(1+$k)*90,
            );
            $window->update;
        }
    }
    foreach my $liste (values %Linepique::liste) {
        for (my $k=0; $k < 3; $k++) {
            my ($oy, $ox)=Commun::rot3(
                $pi+(1-$k)*$step,
                $pj-(1+($k%2))*$step,
                (1+$k)*90, $liste->oy-$pi, $liste->ox-$pj
            );
            my ($ey, $ex)=Commun::rot3(
                $pi+(1-$k)*$step,
                $pj-(1+($k%2))*$step,
                (1+$k)*90, $liste->ey-$pi, $liste->ex-$pj
            );
            Linepique->new(
                -ox => $ox,
                -oy => $oy,
                -ex => $ex,
                -ey => $ey,
            );
            $window->update;
        }
    }
    foreach my $liste (values %Curve::liste) {
        for (my $k=0; $k < 3; $k++) {
            my @coords=$liste->getCoords();
            for (my $m=0; $m < @coords; $m += 2) {
                ($coords[$m+1], $coords[$m])=Commun::rot3(
                    $pi+(1-$k)*$step, $pj-(1+($k%2))*$step,
                    (1+$k)*90,        $coords[$m+1]-$pi,
                    $coords[$m]-$pj
                );
            }
            my %param;
            $param{"-coord"}=join(",", @coords);
            $param{"-couleur"}=$liste->color();
            Curve->new(%param);
            $window->update;
        }
    }
    return;
}
sub createEpingle {
#print "createEpingle";
# fixme-M problem with vertical scrollbar with the touch pad and mouse wheel, it create multiple pins on same place. (view with "alt-z" or save file)
    my ($i, $j)=@_;
    $i=alignIfGrid($i);
    $j=alignIfGrid($j);
    my @tag=$drawing->find("withtag", "current");
    my $epingle;
    if (@tag) {
        $epingle=$::liste{$tag[0]};
        if (ref($epingle) eq "Epingle") {
            if ($epingle->select) {
                $currentcouleur=$epingle->changeColor($currentcouleur);
            }
        }
        else {    # crée une epingle
            $epingle=Epingle->new(
                -i     => $i,
                -j     => $j,
                -color => $currentcouleur
            );
        }
    }
    else {
        $epingle=Epingle->new(-i => $i, -j => $j, -color => $currentcouleur);
    }
    deselect();
    $epingle->selectionne;
    initSFPin($epingle);
    return;
}
sub createTrait {
    my ($i, $j)=@_;
    my $trait;
    $trait=Trait->new(-i => $i, -j => $j, -angle => 0);
    deselect();
    $trait->selectionne;
    return;
}
sub detectOrCreatePin {
    my ($i, $j)=@_;
    $i=alignIfGrid($i);
    $j=alignIfGrid($j);
    my @tag=$drawing->find("withtag", "current");
    my $epingle;
    if (@tag) {
        $epingle=$::liste{$tag[0]};
        if (ref($epingle) eq "Epingle") {
            return ($epingle);
        }
        else {    # crée une epingle
            $epingle=Epingle->new(
                -i     => $i,
                -j     => $j,
                -color => $currentcouleur
            );
        }
    }
    else {
        $epingle=Epingle->new(-i => $i, -j => $j, -color => $currentcouleur);
    }
    deselect();
    return ($epingle);
}
sub initMove {
    unBind();
    setMode(31);
    $drawing->bind("all", "<Button-1>",  [\&action, Ev('x'), Ev('y')]);
    $drawing->bind("all", "<B1-Motion>", [\&lache,  Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    return;
}
sub deselect {
    foreach my $class (@drawing_classes) {
        $class->deselectAll();
    }
    $::drawing->delete($select_area);
    undef($select_area);
    unBind();
    $drawing->configure(-cursor => "");
    $sf_pin->packForget;
    $sf_line->packForget;
    $sf_image->packForget;
    $sf_trait->packForget;
    $sf_curve->packForget;
    undef($curve_select);
    undef($obj_press);
    $::drawing->coords($trace_zone, 0, 0, 0, 0, 0, 0, 0, 0)
      ; # fixme-m create problem for générate the .ps because the printer zone is bad defined
    $::drawing->itemconfigure($trace_zone, -state => "normal");
    foreach my $handle (values %Handle::liste) {$handle->supprime();}
    return;
}
sub initSFPin {
    my $pin=shift;
    $view_number=$pin->number;
    $sf_pin->pack;
    return;
}
sub initSFLine {
    my $line=shift;
    $o=$line->oa;
    $op=$line->ea;
    $value_tordre=$line->tordre*100;
    $value_segment=$line->seg*100;
    $value_segment2=$line->seg2*100;
    $state_pique=$line->pique;
    $sf_line->pack;
    return;
}
sub selectionneUnFil {
    my $line=shift;
    if (ref($line) eq "Line") {
        if ($line->select) {$line->inverse();}
        else {
            deselect();
            $line->selectionne;
            initSFLine($line);
        }
    }
    return;
}
sub calculSelectionneMultiple {
    my $min_i=1000000;    # fixme-m : search un sub min for a array
    my $min_j=1000000;
    foreach my $liste (values(%::liste))
    { # rewrite car on ne devrait plus focntionner avec cette liste, mias bon, ca marche...
        if (   ((ref($liste) eq "Line") || (ref($liste) eq "Epingle"))
            && ($liste->select))
        {
            if ($liste->min_i < $min_i) {$min_i=$liste->min_i;}
            if ($liste->min_j < $min_j) {$min_j=$liste->min_j;}
        }
    }
    my $max_i=-1000000;
    my $max_j=-1000000;
    foreach my $liste (values(%::liste)) {
        if (   ((ref($liste) eq "Line") || (ref($liste) eq "Epingle"))
            && ($liste->select))
        {
            if ($liste->max_i > $max_i) {$max_i=$liste->max_i;}
            if ($liste->max_j > $max_j) {$max_j=$liste->max_j;}
        }
    }
    #print " $min_j, $min_i, $max_j, $max_i";
    if ($select_area) {
        $::drawing->coords(
            $select_area, ($min_j-$r), ($min_i-$r), ($max_j)+$r,
            ($max_i)+$r
        );
    }
    else {
        $select_area=
          $::drawing->createRectangle(($min_j-$r), ($min_i-$r), ($max_j)+$r,
            ($max_i)+$r);
    }
    $rect_width=$max_j-$min_j;
    $rect_hight=$max_i-$min_i;
    # fixme-m use also trait, curve, linepique
    return;
}
sub createHandleForCurve {
    $curve_select=shift;
    undef(@curve);
    my @coord=$curve_select->getCoords();
    $polygon=$drawing->createLine(@coord);
    for (my $k=0; $k < @coord; $k += 2) {
        my $handle=Handle->new(-x => $coord[$k], -y => $coord[$k+1]);
        $handle{$handle->tk_id}=$k;
    }
    return;
}
sub selectionneMultiple {
    my $obj=shift;
    $obj->selectionne();
    if (ref($obj) eq "Line") {    # selectionnne les deux épingles
        $obj->o->selectionne();
        $obj->e->selectionne();
        initSFLine($obj);
    }
    if (ref($obj) eq "Curve") {
        createHandleForCurve($obj);
    }
    calculSelectionneMultiple();
    return;
}
sub chercheEpingle {
    my ($i, $j)=@_;
    my $epingle;
    foreach my $liste (values %::liste) {
        if (ref($liste) eq "Epingle") {
            if (($liste->i eq $i) && ($liste->j eq $j)) {
                $epingle=$liste;
            }
        }
    }
    return $epingle;
}
sub copyThread {
    my $obj=shift;
    if (ref($obj) eq "Line") {    # selectionne un autre fil
        deselect();
        $obj->selectionne;
    }
    else {
        $obj=Epingle::findOrCreate($y_press, $x_press);
        my $select;
        foreach my $line (values %Line::liste) {
            if ($line->select) {
                $select=$line;
            }
        }
        if (ref($select) eq "Line")
        {    # rewrite : may be put this in foreach before
# cree le delta pour la nouvelle extrémité, rajouter a l'epingle,chercher l'epingle, crée si necessaire
            my $origine=$obj;
            $obj->color($select->o->color);
            my $nei=$origine->i+$select->di;
            my $nej=$origine->j+$select->dj;
            my $extremite=chercheEpingle($nei, $nej);
            if (!$extremite) {
                $extremite=Epingle->new(
                    -i     => $nei,
                    -j     => $nej,
                    -color => $select->e->color
                );
            }
            Line->new(
                -ido         => $origine->id,
                -oa          => $select->oa,
                -ide         => $extremite->id,
                -ea          => $select->ea,
                -seg         => $select->seg,
                -seg2        => $select->seg2,
                -couleur     => $select->couleur,
                -tordre      => $select->tordre,
                -nbre_tordre => $select->nbre_tordre,
            );
        }
    }
    return;
}
sub copieMulti {
    my ($ori_x, $ori_y)=@_;
    # rewrite : create a sub findMinMax
    my $min_i=1000000;
    my $min_j=1000000;
    foreach my $liste (values(%::liste)) {
        if (   ((ref($liste) eq "Line") || (ref($liste) eq "Epingle"))
            && ($liste->select))
        {
            if ($liste->min_i < $min_i) {$min_i=$liste->min_i;}
            if ($liste->min_j < $min_j) {$min_j=$liste->min_j;}
        }
    }
    my $move_i=$ori_y-$min_i;
    my $move_j=$ori_x-$min_j;
    foreach my $class (@drawing_classes) {
        $class->copySelectTranslate($move_i, $move_j);
    }
    return;
}
my $nbre_selectionne=0;
my $vb_o;
my $vb_e;
sub rectangleEpingle {
    my ($i, $j)=(align(shift), align(shift));
    deselect();
    if ($nbre_selectionne==0) {
        $vb_o=Epingle::findOrCreate($i, $j);
        $vb_o->selectionne();
        $nbre_selectionne++;    # rewrite a traiter en tant qu'objet
    }
    elsif ($nbre_selectionne==1) {
        $vb_e=Epingle::findOrCreate($i, $j);
        my $nbre_i=abs(($vb_e->i-$vb_o->i)/$step);
        my $nbre_j=abs(($vb_e->j-$vb_o->j)/$step);
        my $line;
        if ($vb_e->j > $vb_o->j) {    #de gauche à droite
            for (my $i=0; $i <= $nbre_i; $i++) {
                for (my $j=$i%2; $j <= $nbre_j; $j += 2) {
                    my $ori=Epingle::findOrCreate($vb_o->i+$i*$step,
                        $vb_o->j+$j*$step);
                    $ori->selectionne();
                    $ori->redraw();
                    $::liste{$ori->id}=$ori;
                }
            }
        }
        else {
            for (my $i=0; $i <= $nbre_i; $i++) {
                for (my $j=$i%2; $j <= $nbre_j; $j += 2) {
                    my $ori=Epingle::findOrCreate($vb_o->i+$i*$step,
                        $vb_o->j-$j*$step);
                    $ori->selectionne();
                    $ori->redraw();
                    $::liste{$ori->id}=$ori;
                }
            }
        }
        $nbre_selectionne=0;
    }
    return;
}
sub simpleParallelLine {
    $vb_o=shift;
    $vb_e=shift;
    deselect();
    my $nbre=abs(($vb_e->i-$vb_o->i)/$step);
    my $line;
    if ($vb_e->j > $vb_o->j) {    #de gauche à droite
        if ($vb_e->i > $vb_o->i) {    #de haut en bas
            for (my $i=0; $i < $nbre; $i++) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i+$i*$step, $vb_o->j+$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j+$i*$step-$step);
                $line=Line->new(
                    -ido   => $ori->id,
                    -oa    => 90,
                    -ide   => $ext->id,
                    -ea    => 270,
                    -pique => 0
                );
                $line->selectionne();
                initSFLine($line);
            }
        }
        else {
            for (my $i=0; $i < $nbre; $i++) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i-$i*$step, $vb_o->j+$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j+$i*$step-$step);
                $line=Line->new(
                    -ido   => $ori->id,
                    oa     => 270,
                    -ide   => $ext->id,
                    -ea    => 90,
                    -pique => 0
                );
                $line->selectionne();
                initSFLine($line);
            }
        }
    }
    else {
        if ($vb_e->i > $vb_o->i) {    #de haut en bas
            for (my $i=0; $i < $nbre; $i++) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i+$i*$step, $vb_o->j-$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j-$i*$step+$step);
                $line=Line->new(
                    -ido   => $ori->id,
                    -oa    => 90,
                    -ide   => $ext->id,
                    -ea    => 270,
                    -pique => 0
                );
                $line->selectionne();
                initSFLine($line);
            }
        }
        else {
            for (my $i=0; $i < $nbre; $i++) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i-$i*$step, $vb_o->j-$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j-$i*$step+$step);
                $line=Line->new(
                    -ido   => $ori->id,
                    -oa    => 270,
                    -ide   => $ext->id,
                    -ea    => 90,
                    -pique => 0
                );
                $line->selectionne();
                initSFLine($line);
            }
        }
    }
    return;
}
sub suiteCroisement {
    $vb_o=shift;
    $vb_e=shift;
    deselect();
    my $line;
    my $nbre=abs(($vb_e->i-$vb_o->i)/$step);
    if ($vb_e->j > $vb_o->j) {    #de gauche à droite
        if ($vb_e->i > $vb_o->i) {    #de haut en bas
            for (my $i=0; $i < $nbre; $i += 2) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i+$i*$step, $vb_o->j+$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i+$i*$step+2*$step,
                    $vb_o->j+$i*$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 0,
                    -ide     => $ext->id,
                    -ea      => 180,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
                $ori=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j+$i*$step+$step);
                $ext=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j+$i*$step-$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 90,
                    -ide     => $ext->id,
                    -ea      => 270,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
            }
        }
        else {
            for (my $i=0; $i < $nbre; $i += 2) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i-$i*$step, $vb_o->j+$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i-$i*$step-2*$step,
                    $vb_o->j+$i*$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 0,
                    -ide     => $ext->id,
                    -ea      => 180,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
                $ori=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j+$i*$step+$step);
                $ext=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j+$i*$step-$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 270,
                    -ide     => $ext->id,
                    -ea      => 90,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
            }
        }
    }
    else {    # de droite à gauche
        if ($vb_e->i > $vb_o->i) {    #de haut en bas
            for (my $i=0; $i < $nbre; $i += 2) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i+$i*$step, $vb_o->j-$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i+$i*$step+2*$step,
                    $vb_o->j-$i*$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 180,
                    -ide     => $ext->id,
                    -ea      => 0,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
                $ori=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j-$i*$step-$step);
                $ext=Epingle::findOrCreate($vb_o->i+$i*$step+$step,
                    $vb_o->j-$i*$step+$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 90,
                    -ide     => $ext->id,
                    -ea      => 270,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
            }
        }
        else {
            for (my $i=0; $i < $nbre; $i += 2) {
                my $ori=
                  Epingle::findOrCreate($vb_o->i-$i*$step, $vb_o->j-$i*$step);
                my $ext=Epingle::findOrCreate($vb_o->i-$i*$step-2*$step,
                    $vb_o->j-$i*$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 180,
                    -ide     => $ext->id,
                    -ea      => 0,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
                $ori=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j-$i*$step+$step);
                $ext=Epingle::findOrCreate($vb_o->i-$i*$step-$step,
                    $vb_o->j-$i*$step-$step);
                $line=Line->new(
                    -ido     => $ori->id,
                    -oa      => 90,
                    -ide     => $ext->id,
                    -ea      => 270,
                    -seg     => 0.7,
                    -seg2    => 0.3,
                    -couleur => 'red',
                    -pique   => 0
                );
            }
        }
    }
    return;
}
sub lache {    # rewrite change the name by "move" ?
        # fixme-m desactivate grid for this subroutine
        # rewrite change $epingle by $obj_draw
        # rewrite why different sub for Epingle,Trait,Curve and Epingle tdi
    my $canev=shift;
    $x_release=shift;
    $y_release=shift;
    $x_release=$canev->canvasx($x_release);
    $y_release=$canev->canvasy($y_release);
    @liste=$drawing->find("withtag", "current");
    my $epingle=$::liste{$id_c};

    if (ref($epingle) eq "Epingle") {    # rewrite put below in *.pm
        if ($epingle->id_draw==$id_c) {
            $epingle->j($x_release);
            $epingle->i($y_release);
            $epingle->redraw();
            foreach my $line (values %Line::liste) {
                if ($line->o eq $epingle) {
                    $line->redraw();
                }
                elsif ($line->e eq $epingle) {
                    $line->redraw();
                }
            }
        }
        if ($epingle->id_text==$id_c) {
            $epingle->tdi($epingle->tdi+$y_release-$y_press);
            $epingle->tdj($epingle->tdj+$x_release-$x_press);
            $x_press=$x_release;
            $y_press=$y_release;
            $epingle->redraw();
        }
    }
    elsif (ref($epingle) eq "Trait") {
        $epingle->j($x_release);
        $epingle->i($y_release);
        $epingle->redraw();
    }
    elsif (ref($epingle) eq "Handle") {
        # move handle
        $epingle->x($x_release);
        $epingle->y($y_release);
        $epingle->redraw();
        # move curve
        my $tk_id=$liste[0];
        my $k=$handle{$tk_id};
        my @coord=$curve_select->getCoords();
        $coord[$k]=$x_release;
        $coord[$k+1]=$y_release;
        $curve_select->changeCoord(@coord);
        $::drawing->coords($polygon, @coord);
    }
    return;
}
my ($i_max, $j_max);
sub initDrawLozenge {
    # rewrite use quadrilateral ?
    # rewrite fusion with "sub zone"
    setMode(shift);
    unBind();
    $drawing->CanvasBind("<B1-Motion>", [\&drawLozenge, Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    $drawing->CanvasBind("<ButtonRelease>", \&useLozenge);
    return;
}
sub drawLozenge {
    my $canev=shift;
    $x_release=$canev->canvasx(shift);
    $y_release=$canev->canvasy(shift);
    my $j=$x_release-$x_press;
    my $i=$y_release-$y_press;
    ($i_max, $j_max)=Commun::rot3(0, 0, -45, $i, $j);
    $i_max=round($i_max/sqrt(2));
    $j_max=round($j_max/sqrt(2));
    #    print "$i $j $i_max $j_max\n";
    $::drawing->coords(
        $trace_zone,              $x_press,
        $y_press,                 $x_press+$j_max,
        $y_press+$j_max,          $x_press+($j_max-$i_max),
        $y_press+($i_max+$j_max), $x_press-$i_max,
        $y_press+$i_max,
    );
    $::drawing->itemconfigure($trace_zone, -state => "normal");
    return;
}
sub drawRectangle {
    my $canev=shift;
    my $x=$canev->canvasx(shift);
    my $y=$canev->canvasy(shift);
    $::drawing->coords($trace_zone, $x, $y, $x+$rect_width, $y, $x+$rect_width,
        $y+$rect_hight, $x, $y+$rect_hight, $x, $y);
    $::drawing->itemconfigure($trace_zone, -state => "normal");
    return;
}
sub useLozenge {
    if ($mode==37) {
        if (   ($y_press != $y_release)
            || ($x_press != $x_release))
        {
            my $j=$x_release-$x_press;
            my $i=$y_release-$y_press;
            ($i_max, $j_max)=Commun::rot3(0, 0, -45, $i, $j);
            $i_max=round($i_max/sqrt(2));
            $j_max=round($j_max/sqrt(2));
            my $x0=$x_press;
            my $y0=$y_press;
            my $x1=$x_press+$j_max;    # rewrite $x1=$x0+$j_max ?
            my $y1=$y_press+$j_max;
            my $x2=$x_press+($j_max-$i_max);
            my $y2=$y_press+($i_max+$j_max);
            my $x3=$x_press-$i_max;
            my $y3=$y_press+$i_max;

            foreach my $class (@drawing_classes) {
                $class->selectInLozenge($x0, $y0, $x1, $y1, $x2, $y2, $x3,
                    $y3);
            }
        }
    }
    redrawAll();
    #initDrawLine(37);
    return;
}
sub initDrawLine {
    setMode(shift);
    unBind();
    $drawing->CanvasBind("<B1-Motion>", [\&drawLine, Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    $drawing->CanvasBind("<ButtonRelease>", \&useLine);
    return;
}
sub drawLine {
    my $canev=shift;
    $x_release=alignIfGrid($canev->canvasx(shift));
    $y_release=alignIfGrid($canev->canvasy(shift));
    if (!$draw_line) {$draw_line=$::drawing->createLine(0, 0, 0, 0);}
    $::drawing->coords($draw_line, $x_press, $y_press, $x_release, $y_release,
    );
    $::drawing->itemconfigure($draw_line, -state => "normal");
    return;
}
sub useLine {
    if ($mode==32) {
        if (   ($y_press != $y_release)
            || ($x_press != $x_release))
        {
            my $pin_o=$obj_press;
            if (!$pin_o || (ref($pin_o) ne "Epingle"))
            {    # fixme-m create pin with default color
                $pin_o=Epingle::findOrCreate($y_press, $x_press);
            }
            my $pin_e=findCurrent;
            if (!$pin_e || (ref($pin_e) ne "Epingle")) {
                $pin_e=Epingle::findOrCreate($y_release, $x_release);
            }
            my $line=Line->new(
                -ido => $pin_o->id,
                -oa  => $o,
                -ide => $pin_e->id,
                -ea  => $op,
            );
            deselect();
            $line->selectionne;
            initSFLine($line);
            $::drawing->delete($draw_line);
            undef($draw_line);    # fixme-m best manage $draw_line
        }
        else {
            createEpingle($y_press, $x_press);
        }
        initDrawLine(32);
    }
    elsif ($mode==14) {
        if (   ($y_press != $y_release)
            || ($x_press != $x_release))
        {
            my $pin_o=$obj_press;
            if (!$pin_o || (ref($pin_o) ne "Epingle"))
            {    # fixme-m create pin with default color
                $pin_o=Epingle::findOrCreate($y_press, $x_press);
            }
            my $pin_e=findCurrent;
            if (!$pin_e || (ref($pin_e) ne "Epingle")) {
                $pin_e=Epingle::findOrCreate($y_release, $x_release);
            }
            $::drawing->delete($draw_line);
            undef($draw_line);
            simpleParallelLine($pin_o, $pin_e);
            initDrawLine(14);
        }
    }
    elsif ($mode==15) {
        if (   ($y_press != $y_release)
            || ($x_press != $x_release))
        {
            my $pin_o=$obj_press;
            if (!$pin_o || (ref($pin_o) ne "Epingle"))
            {    # fixme-m create pin with default color
                $pin_o=Epingle::findOrCreate($y_press, $x_press);
            }
            my $pin_e=findCurrent;
            if (!$pin_e || (ref($pin_e) ne "Epingle")) {
                $pin_e=Epingle::findOrCreate($y_release, $x_release);
            }
            $::drawing->delete($draw_line);
            undef($draw_line);
            suiteCroisement($pin_o, $pin_e);
            initDrawLine(15);
        }
    }
    return;
}
my $curve;
sub initDrawCurve {
    setMode(35);
    $drawing->CanvasBind("<B1-Motion>", [\&drawCurve, Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    $drawing->CanvasBind("<ButtonRelease>", \&useCurve);
    $window->bind("<Key-k>", \&createCurve);
    $::sf_curve->pack();
    return;
}
sub drawCurve {
    my $canev=shift;
    $x_release=shift;
    $y_release=shift;
    $x_release=$canev->canvasx($x_release);
    $y_release=$canev->canvasy($y_release);
    if (@curve==0) {
        push(@curve, $x_press);
        push(@curve, $y_press);
        $polygon=$drawing->createLine(@curve, $x_release, $y_release);
        $curve=$drawing->createLine(
            @curve, $x_release, $y_release,
            -smooth => "True",
            -width  => 3,
            -fill   => "purple"
        );
    }
    $::drawing->coords($polygon, @curve, $x_release, $y_release,);
    $::drawing->coords($curve,   @curve, $x_release, $y_release,);
    return;
}
sub useCurve {
    push(@curve, $x_release);
    push(@curve, $y_release);
    deselect();
    initDrawCurve();
    return;
}
sub createCurve {
    $drawing->delete($curve);
    $drawing->delete($polygon);
    undef($curve);
    undef($polygon);
    my $curve=Curve->new();
    $curve->coord(@curve);
    deselect();
    $curve->selectionne();
    createHandleForCurve($curve);
    $sf_curve->packForget;
    initMove();
    return;
}
sub initDrawLinePique {
    setMode(36);
    $Commun::view_pique=1;
    setView();
    $drawing->CanvasBind("<B1-Motion>", [\&drawLinePique, Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    $drawing->CanvasBind("<ButtonRelease>", \&useLinePique);
    return;
}
sub drawLinePique {
    my $canev=shift;
    $x_release=shift;
    $y_release=shift;
    $x_release=$canev->canvasx($x_release);
    $y_release=$canev->canvasy($y_release);
    if (!$draw_line) {$draw_line=$::drawing->createLine(0, 0, 0, 0);}
    $::drawing->coords(
        $draw_line,            alignSlash2($x_press),
        alignSlash2($y_press), alignSlash2($x_release),
        alignSlash2($y_release),
    );
    $::drawing->itemconfigure($draw_line, -state => "normal");
    return;
}
sub useLinePique {
    my $linePique=Linepique->new(
        -ox => alignSlash2($x_press),
        -oy => alignSlash2($y_press),
        -ex => alignSlash2($x_release),
        -ey => alignSlash2($y_release),
    );
    deselect();
    $linePique->selectionne;
    $::drawing->delete($draw_line);
    undef($draw_line);
    initDrawLinePique();
    return;
}
sub zone {
    my $canev=shift;
    $x_release=shift;
    $y_release=shift;
    $x_release=$canev->canvasx($x_release);
    $y_release=$canev->canvasy($y_release);
    my $j=$x_release-$x_press;
    my $i=$y_release-$y_press;
    ($i_max, $j_max)=Commun::rot3(0, 0, -45, $i, $j);
    $i_max /= sqrt(2);
    $j_max /= sqrt(2);
    $i_max=round($i_max/$step);
    $j_max=round($j_max/$step);
    #    print "$i $j $i_max $j_max\n";
    $::drawing->coords(
        $trace_zone,
        align($x_press),
        align($y_press),
        align($x_press)+$j_max*$step,
        align($y_press)+$j_max*$step,
        align($x_press)+($j_max-$i_max)*$step,
        align($y_press)+($i_max+$j_max)*$step,
        align($x_press)-$i_max*$step,
        align($y_press)+$i_max*$step,
    );
    $::drawing->itemconfigure($trace_zone, -state => "normal");
    return;
}
sub initFill {
    $pattern_marging=shift;
    $pattern_file=shift;
    setMode(33);
    $drawing->CanvasBind("<B1-Motion>", [\&zone, Ev('x'), Ev('y')]);
    $drawing->configure(-cursor => "crosshair");
    $drawing->CanvasBind("<ButtonRelease>", \&fillZone);
    $type_zone="Epingle";
    return;
}
sub newCoordAlign {
    my ($i, $j, $min_i, $min_j, $k, $l)=@_;
    ($i, $j)=Commun::rot3(0, 0, -45, $i, $j);
    $i=$i-$min_i*sqrt(2);
    $j=$j-$min_j*sqrt(2);
    ($i, $j)=Commun::rot3(0, 0, 45, $i, $j);
    $i=alignIfGrid($y_press+($k+$l)*$step+$i);
    $j=alignIfGrid($x_press+($l-$k)*$step+$j);
    return ($i, $j);
}
sub newCoord {
    my ($i, $j, $min_i, $min_j, $k, $l)=@_;
    my ($ii, $jj)=Commun::rot3(0, 0, -45, $i, $j);
    $i=$ii-$min_i*sqrt(2);
    $j=$jj-$min_j*sqrt(2);
    ($ii, $jj)=Commun::rot3(0, 0, 45, $i, $j);
    $i=round(align($y_press)+($k+$l)*$step+$ii);
    $j=round(align($x_press)+($l-$k)*$step+$jj);
    return ($i, $j);
}
sub fillZone {
    if (($i_max < 1)           && ($j_max < 1))           {return;}
    if (($x_release==$x_press) && ($x_release==$x_press)) {return;}
    gridDisable;    # fixme-m better manage grid and pattern
                    # search min et max
    my $min_i=1000000;
    my $min_j=1000000;
    my $max_i=-1000000;
    my $max_j=-1000000;

    if (($pattern_file) && (open my $PATTERN, "<", $pattern_file || die)) {
        while (my $ligne=<$PATTERN>) {
            chomp $ligne;
            my %param=split(/ /, $ligne);
            if ($param{"-type"} eq "Epingle") {
                my ($i, $j)=
                  Commun::rot3(0, 0, -45, $param{"-i"}, $param{"-j"});
                if ($i < $min_i) {$min_i=$i;}
                if ($j < $min_j) {$min_j=$j;}
                if ($i > $max_i) {$max_i=$i;}
                if ($j > $max_j) {$max_j=$j;}
            }
        }
        close($PATTERN);
    }
    $min_i=round($min_i/sqrt(2));
    $min_j=round($min_j/sqrt(2));
    $max_i=round($max_i/sqrt(2));
    $max_j=round($max_j/sqrt(2));
    my $width=$max_j-$min_j;
    my $height=$max_i-$min_i;
    my $w=$width/$step+$pattern_marging;
    my $h=$height/$step+$pattern_marging;
    #print "min max w h $min_i $min_j $max_i $max_j $width $height $w $h\n";
    if ($type_zone eq "Epingle") {
        for (my $k=0; $k <= $i_max-$h+$pattern_marging; $k += $h) {
            for (my $l=0; $l <= $j_max-$w+$pattern_marging; $l += $w) {
                if (($pattern_file) && (open my $PATTERN, "<", $pattern_file))
                {
                    while (my $ligne=<$PATTERN>) {
                        chomp $ligne;
                        my %param=split(/ /, $ligne);
                        if ($param{"-type"} eq "Epingle") {
                            my ($i, $j)=
                              newCoordAlign($param{"-i"}, $param{"-j"}, $min_i,
                                $min_j, $k, $l);
                            my $epingle=Epingle::find($i, $j);
                            if (!$epingle) {
                                $param{"-i"}=$i;
                                $param{"-j"}=$j;
                                delete($param{"-id"});
                                $epingle=Epingle->new(%param);
                            }
                            $window->update;
                        }
                        if ($param{"-type"} eq "Linepique") {
                            my ($oi, $oj)=
                              newCoord($param{"-oy"}, $param{"-ox"}, $min_i,
                                $min_j, $k, $l);
                            my ($ei, $ej)=
                              newCoord($param{"-ey"}, $param{"-ex"}, $min_i,
                                $min_j, $k, $l);
                            my $line_pique=Linepique->new(
                                -oy => $oi,
                                -ox => $oj,
                                -ey => $ei,
                                -ex => $ej
                            );
                        }
                        if ($param{"-type"} eq "Trait") {
                            my ($i, $j)=
                              newCoordAlign($param{"-i"}, $param{"-j"}, $min_i,
                                $min_j, $k, $l);
                            $param{"-i"}=$i;
                            $param{"-j"}=$j;
                            delete($param{"-id"});
                            my $trait=Trait->new(%param);
                        }
                        if ($param{"-type"} eq "Curve") {
                            my @coords=split(/,/, $param{"-coord"});
                            for (my $m=0; $m < @coords; $m += 2) {
                                ($coords[$m+1], $coords[$m])=
                                  newCoordAlign($coords[$m+1], $coords[$m],
                                    $min_i, $min_j, $k, $l);
                            }
                            $param{"-coord"}=join(",", @coords);
                            delete($param{"-id"});
                            my $trait=Curve->new(%param);
                        }
                        if ($param{"-type"} eq "Line") {
                            my ($i, $j)=
                              Commun::rot3(0, 0, -45, $param{"-oi"},
                                $param{"-oj"});
                            $i=$i-$min_i*sqrt(2);
                            $j=$j-$min_j*sqrt(2);
                            ($i, $j)=Commun::rot3(0, 0, 45, $i, $j);
                            $i=alignIfGrid($y_press+($k+$l)*$step+$i);
                            $j=alignIfGrid($x_press+($l-$k)*$step+$j);
                            my $pin_o=Epingle::find($i, $j);
                            if (!$pin_o) {
                                $param{"-i"}=$i;
                                $param{"-j"}=$j;
                                delete($param{"-id"});
                                $pin_o=Epingle->new(%param);
                            }
                            ($i, $j)=
                              Commun::rot3(0, 0, -45, $param{"-ei"},
                                $param{"-ej"});
                            $i=$i-$min_i*sqrt(2);
                            $j=$j-$min_j*sqrt(2);
                            ($i, $j)=Commun::rot3(0, 0, 45, $i, $j);
                            $i=alignIfGrid($y_press+($k+$l)*$step+$i);
                            $j=alignIfGrid($x_press+($l-$k)*$step+$j);
                            my $pin_e=Epingle::find($i, $j);

                            if (!$pin_o) {
                                $param{"-i"}=$i;
                                $param{"-j"}=$j;
                                delete($param{"-id"});
                                $pin_e=Epingle->new(%param);
                            }
                            if (
                                !Line::find(
                                    $pin_o->id, $param{"-oa"},
                                    $pin_e->id, $param{"-ea"}
                                )
                              )
                            {
                                delete($param{"-oi"});
                                delete($param{"-oj"});
                                delete($param{"-ei"});
                                delete($param{"-ej"});
                                delete($param{"-id"});
                                $param{"-ido"}=$pin_o->id;
                                $param{"-ide"}=$pin_e->id;
                                Line->new(%param);
                            }
                        }
                    }
                    close($PATTERN);
                }
            }
        }
    }
    gridEnable;
    return;
}
sub flipHorizontal {
    #    my ($min_x, $min_y, $max_x, $max_y)=$drawing->bbox("all");
    #    my $milieu_x=($max_x-$min_x)/2;
    my $min_i=100000;
    my $max_i=-100000;
    # search milieu
    foreach my $element (values %Epingle::liste) {
        if ($element->i < $min_i) {$min_i=$element->i;}
        if ($element->i > $max_i) {$max_i=$element->i;}
    }
    my $milieu_i=alignStar2($max_i-$min_i)/2;    # fixme-m alignGrid ??
                                                 # rewrite put below in *.pm
    foreach my $element (values %Epingle::liste) {
        $element->i($milieu_i*2-$element->i);
    }
    foreach my $element (values %Line::liste) {
        $element->oa(-$element->oa);    # fixme-m ou "180-" voir le contexte
        $element->ea(-$element->ea);
        $element->inverse();
    }
    foreach my $element (values %Trait::liste) {
        $element->i($milieu_i*2-$element->i);
        $element->angle(-$element->angle);
    }
    foreach my $element (values %Curve::liste) {
        my @coord=$element->getCoords();
        for (my $k=0; $k < @coord; $k += 2) {
            $coord[$k+1]=$milieu_i*2-$coord[$k+1];
        }
        $element->changeCoord(@coord);
    }
    foreach my $element (values %Linepique::liste) {
        $element->oy($milieu_i*2-$element->oy);
        $element->ey($milieu_i*2-$element->ey);
    }
    redrawAll();
    return;
}
sub action {
    # print "action";
    my $canev=shift;
    if ($mode==37) {    # never align for Lozenge Select (and may be other)
                        # rewrite very bad position for this test
        $x_release=$x_press=$canev->canvasx(shift);
        $y_release=$y_press=$canev->canvasy(shift);
    }
    else {
        $x_release=$x_press=alignIfGrid($canev->canvasx(shift));
        $y_release=$y_press=alignIfGrid($canev->canvasy(shift));
    }
    @liste=$drawing->find("withtag", "current");
    #print @liste;
    ($id_c)=$drawing->find("withtag", "current");
    my $obj=findCurrent();
    $obj_press=findCurrent();
    # print $obj;
    # print $obj->i, " ", $obj->j, ".";
    if ($mode==1)  {selectionneUnFil($obj);}
    if ($mode==2)  {copyThread($obj);}
    if ($mode==3)  {selectionneMultiple($obj);}
    if ($mode==4)  {copieMulti($x_press, $y_press);}
    if ($mode==17) {rectangleEpingle($y_press, $x_press);}
    if ($mode==22) {createTrait($y_press, $x_press);}
    $drawing->configure(-scrollregion => [$drawing->bbox("all")]);
    undef($obj_press);
    return;
}
# debut ------------------------------------------------------------------------
init();
MainLoop;
