Difference between revisions of "Amulet pl src"

From AMule Project FAQ
Jump to: navigation, search
 
Line 1: Line 1:
test
+
= aMulet.pl source =
 +
<pre><nowiki>
 +
#!/usr/bin/perl -w
 +
#
 +
# file aMulet.pl
 +
# Copyright by AnimAlf
 +
#
 +
# This lines are under the GNU General Public License.
 +
# http://www.gnu.org/copyleft/gpl.html
 +
#
 +
# aMulet - la t final en catalán le indica diminutivo
 +
#          También se traduce como amuleto
 +
 
 +
use strict;
 +
 
 +
my $binaMuled = 'amuled -f';
 +
my $apppid = "$ENV{HOME}/.tx2z.pid";
 +
my $logMode = "warning"; # debug warning msg
 +
my $verbose = 1; # true false
 +
 
 +
if (!defined($ENV{'DISPLAY'})){
 +
  print &appRel . "\nATENCIÓN: Este script necesita ser ejecutado bajo las X Window\n";
 +
  exit -1;
 +
}
 +
 
 +
eval "use Gtk2::TrayIcon;";
 +
if ($@) {
 +
  print &appRel;
 +
  print "\n\nATENCIÓN: Este script necesita el módulo Gtk2::TrayIcon\n\n";
 +
  print "Puede instalarlo a través de CPAN del siguiente modo:\n\n";
 +
  print "\t\tperl -MCPAN -e shell\n";
 +
  print "\t\tinstall Gtk2::TrayIcon\n\n";
 +
  print "bye\n";
 +
  exit -1;
 +
}
 +
 
 +
my $modUniCode;
 +
eval "use Unicode::MapUTF8 qw(from_utf8);";
 +
$modUniCode = !$@;
 +
 
 +
my $config = {};
 +
my @appList = ( 'aMuleGui', 'wxCas', 'aLinkCreator');
 +
 
 +
# Los procesos hijo envían esta señal al padre cuando terminan y ésta
 +
# debe ser atendida, si no se quedan zombies hasta que termine el padre.
 +
$SIG{CHLD} = \&finalHijo;
 +
 
 +
# Si se recibe cualquier señal que pueda provocar la interrupción del
 +
# normal funcionamiento, entonces nos encargamos de finalizar correctamente.
 +
$SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = \&finalizar;
 +
 
 +
# Iniciando Gtk2
 +
Gtk2->init;
 +
 
 +
# Comprobaciones iniciales. Cargar configuración.
 +
if (!&inicializar){
 +
  logsys ("msg", &appRel ." ha abortado el arranque.");
 +
  unlink $apppid or die "No puedo eliminar el archivo de bloqueo";
 +
  exit -1;
 +
}
 +
logsys ("msg", &appRel ." iniciado");
 +
 
 +
# Formas Gtk2
 +
my $menu = Gtk2::Menu->new();
 +
my $menu_opciones = Gtk2::MenuItem->new("opciones");
 +
my $menu_aLinkCreator = Gtk2::MenuItem->new("aLinkCreator");
 +
my $menu_wxCas = Gtk2::MenuItem->new("wxCas");
 +
my $menu_aMuleGui = Gtk2::MenuItem->new("aMuleGui");
 +
my $menu_verLog = Gtk2::MenuItem->new("aMule log");
 +
my $menuSalir = Gtk2::MenuItem->new("Salir");
 +
my $pic;
 +
eval {$pic = Gtk2::Gdk::Pixbuf->new_from_file ($config->{'iconPath'});};
 +
if ($@){
 +
  my @aMulet_xpm = ('18 24 17 1', '      c None', '.      c #312A28', '+      c #524842',
 +
          '@      c #D42238', '#      c #835128', '$      c #985A20', '%      c #6A6B6A',
 +
          '&      c #AA7B50', '*      c #EC5867', '=      c #BF7B3F', '-      c #C9792F',
 +
          ';      c #898989', '>      c #E39854', ',      c #CB9D83', '"      c #A9AAAA',
 +
          ')      c #CDCBC9', '!      c #F4F5F3', '                  ',
 +
          '          $      ',
 +
          '        !>      ',
 +
          '  #!    )$$      ',
 +
          '  ##    #-$      ',
 +
          '  )##$  -,#      ',
 +
          '  .+##)$,)      ',
 +
          '    ...$,!"      ',
 +
          '    ".$=-$"      ',
 +
          '    ;==-!=      ',
 +
          '    " >!  "      ',
 +
          '    ; !&  ;      ',
 +
          '    " !)))"      ',
 +
          '    """"""!      ',
 +
          '    %!."""");    ',
 +
          '    ;))";;;"%;;)  ',
 +
          '  ">-)";;;;"->""  ',
 +
          '  )-=$,;;%@,->$)  ',
 +
          '  ;%--#=>>**+%%;  ',
 +
          ' +....&=>>>>#!))! ',
 +
          '%"-=##$>>>>=&$--;;',
 +
          '+)-=##&=>>>=#=->)+',
 +
          '%;;=$+.+>>++$->,;%', ' "              . ');
 +
  $pic = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@aMulet_xpm);
 +
  logsys ("warning", "Error al cargar el icono: " . $config->{'iconPath'});
 +
  logsys ("msg", "Se procede a cargar el icono interno.");
 +
}
 +
my $img= Gtk2::Image->new_from_pixbuf($pic->scale_simple(18, 24, "tiles"));
 +
my $tray= Gtk2::TrayIcon->new("trayicon");
 +
my $paraEvent = Gtk2::EventBox->new;
 +
my $trayTooltip = Gtk2::Tooltips->new;
 +
my $SeparadorMenu1 = Gtk2::SeparatorMenuItem->new;
 +
my $SeparadorMenu2 = Gtk2::SeparatorMenuItem->new;
 +
my $SeparadorMenu3 = Gtk2::SeparatorMenuItem->new;
 +
 
 +
$menu->append($menu_verLog);
 +
$menu->append($SeparadorMenu1);
 +
$menu->append($menu_opciones);
 +
$menu->append($SeparadorMenu2);
 +
$menu->append($menu_aLinkCreator);
 +
$menu->append($menu_wxCas);
 +
$menu->append($menu_aMuleGui);
 +
$menu->append($SeparadorMenu3);
 +
$menu->append($menuSalir);
 +
 
 +
$paraEvent->add($img);
 +
$tray->add($paraEvent);
 +
&statusText;
 +
 
 +
# Tratamiento de señales
 +
logsys ("debug", "Asignación de señales");
 +
$tray->signal_connect ('button-press-event' => \&ClickOnIcon);
 +
$menu_opciones->signal_connect('activate' => \&opcionesGui);
 +
$menu_verLog->signal_connect('activate' => \&visorTexto);
 +
$menu_aLinkCreator->signal_connect('activate' => sub {&extAppStatus('aLinkCreator')});
 +
$menu_wxCas->signal_connect('activate' => sub {&extAppStatus('wxCas')});
 +
$menu_aMuleGui->signal_connect('activate' => sub {&extAppStatus('aMuleGui')});
 +
$menuSalir->signal_connect('activate' => \&finalizar);
 +
Glib::Timeout->add($config->{'signOnTime'}*1000, \&statusText) if $config->{'signOn'};
 +
 
 +
# show sobre los witgets para que sean visibles
 +
logsys ("debug", "Show sobre los witgets");
 +
$menu->show_all;
 +
$tray->show_all;
 +
&popUpsAct;
 +
# iniciando bucle de mensajes
 +
logsys ("debug", "Inicio del bucle de mensajes");
 +
Gtk2->main;
 +
# Fin. Aquí termina el flujo.
 +
 
 +
#############
 +
# Funciones #
 +
#############
 +
# statusText Establece el texto del toolTip
 +
sub statusText{
 +
  my $tmpStr = &appRel ." - Click derecho para opciones";
 +
  if ($config->{'signOn'}){
 +
    &onlineInfo;
 +
    # my $tmpStr;
 +
    if ($config->{'ConectStatus'}){
 +
      if ($config->{'typeId'}eq"H"){$tmpStr = "[ID alta]"}
 +
        elsif ($config->{'typeId'}eq"L"){$tmpStr = "[ID baja]"}
 +
        else {$tmpStr = "[Conectando]"}
 +
      $tmpStr .= " Down:" . $config->{'downSpeed'};
 +
      $tmpStr .= " kb/s Up:" . $config->{'upSpeed'} ." Kb/s ";
 +
      if ($config->{'kadStatus'}==2) {$tmpStr .= "[kad ok]";
 +
        } elsif ($config->{'kadStatus'}==1) {$tmpStr .= "[kad firewalled]";
 +
        } else {$tmpStr .= "[kad off]"};
 +
    } else {$tmpStr = &appRel . " [aMuled No Conectado]"}
 +
   
 +
  }
 +
  $trayTooltip->set_tip($tray, $tmpStr);
 +
  $tray->send_message($config->{'signOnTime'}*1000, "[AnimAlf dice] No entiendo");
 +
}
 +
 
 +
# signatureOn Comprueba OnlineSignature en amule.conf
 +
sub readSignatureConf {
 +
  $config->{'signOn'} = 0;
 +
  $config->{'signOnTime'} = 0;
 +
  if (-f $config->{'aMuleDir'} . "/amule.conf"){
 +
    if ( open( CONFIGFILE, $config->{'aMuleDir'} . "/amule.conf" ) ) {
 +
      while (<CONFIGFILE>) {
 +
        if ( $_ =~ /^OnlineSignature=(.+)/ ) { $config->{'signOn'} = $1 }
 +
        if ( $_ =~ /^OnlineSignatureUpdate=(.+)/ ) { $config->{'signOnTime'} = $1 }
 +
      }
 +
      close CONFIGFILE;
 +
    }
 +
  }
 +
}
 +
 
 +
# onlineInfo Recoge la información de amulesig.dat
 +
sub onlineInfo {
 +
  if ( open( SIGNFILE, $config->{'aMuleDir'} . "/amulesig.dat" ) ) {
 +
    my @signFile;
 +
    while (<SIGNFILE>) {
 +
      $_ =~ /(.+)\n$/; push (@signFile, $1)
 +
    }
 +
    close SIGNFILE;
 +
    $config->{'ConectStatus'} = $signFile[0];
 +
    $config->{'nameServ'}    = $signFile[1];
 +
    $config->{'ipServ'}      = $signFile[2];
 +
    $config->{'portServ'}    = $signFile[3];
 +
    $config->{'typeId'}      = $signFile[4];
 +
    $config->{'kadStatus'}    = $signFile[5];
 +
    $config->{'downSpeed'}    = $signFile[6];
 +
    $config->{'upSpeed'}      = $signFile[7];
 +
    $config->{'colaCli'}      = $signFile[8];
 +
    $config->{'filesShared'}  = $signFile[9];
 +
    $config->{'nickName'}    = $signFile[10];
 +
    $config->{'totalDown'}    = $signFile[11];
 +
    $config->{'totalUp'}      = $signFile[12];
 +
    $config->{'aMuleVer'}    = $signFile[13];
 +
    $config->{'SessionDown'}  = $signFile[14];
 +
    $config->{'SessionUp'}    = $signFile[15];
 +
    $config->{'TimeRun'}      = $signFile[16];
 +
  }
 +
}
 +
 
 +
# finalHijo atender la señal de los hijos (cuando terminan la mandan)
 +
sub finalHijo {
 +
  $SIG{CHLD} = \&finalHijo;
 +
  my $pid = wait;
 +
  return unless $config->{$pid}; # El system() realiza un fork() también.
 +
  logsys ("msg", "Acaba de finalizar: " . $config->{$pid} .
 +
    " [" . $config->{$config->{$pid} . 'Pid'} . "]");
 +
  undef $config->{$config->{$pid} . 'Pid'};
 +
  undef $config->{$pid};
 +
}
 +
 
 +
# finalizar correctamente y eliminar bloqueo
 +
sub finalizar {
 +
  $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = \&finalizar;
 +
  logsys ("msg", "Finalizando ...");
 +
  #Finalizar el bucle de mensajes
 +
  Gtk2->main_quit;
 +
  #Comprobar que no quede ninguna aplicación hijo en funcionamiento
 +
  #y si existe eliminarlas antes de finalizar no vayan a quedar zombies.
 +
  logsys ("msg", "Finalizando posibles hijos");
 +
  # Ahora la señal de retorno del hijo la trataremos aquí.
 +
  $SIG{CHLD} = 'DEFAULT';
 +
  foreach my $app (@appList) {
 +
    if (defined $config->{$app . "Pid"}){
 +
      logsys ("msg", "[$app] está en marcha. Finalizándolo ...");
 +
      kill ('SIGTERM', $config->{$app . "Pid"});
 +
      waitpid($config->{$app . "Pid"}, 0);
 +
    }
 +
  }
 +
  logsys ("msg", "No quedan procesos hijo de " . &appRel . " en el sistema");
 +
  #TODO Si archivo de bloqueo es usado para log volcar a otro sitio.
 +
  logsys ("warning", "TODO: volcar archivo log a log final [si se le ordena]");
 +
  logsys ("msg", "Terminada la ejecución");
 +
  logsys ("msg", "bye ;-)\n\n".&appRel."\n");
 +
  # borrar archivo de bloqueo
 +
  unlink $apppid or die "No puedo eliminar el archivo de bloqueo $apppid";
 +
}
 +
 
 +
# flushChars transforma utf8 a iso-8859-1
 +
sub flushChars {
 +
  my ($tmpStr) = @_;
 +
  $tmpStr =~ s/([\200-\377]+)/from_utf8({ -string => $1, -charset => 'ISO-8859-1'})/eg
 +
    if $modUniCode;
 +
  return $tmpStr;
 +
}
 +
 
 +
#logsys Mini sistema de logs.
 +
sub logsys {
 +
  my ($mode, $text) = @_;
 +
  my $tmpStr;
 +
  if (open LOG, ">>$apppid") {
 +
    if (($logMode eq "debug" && ($mode eq "debug" or $mode eq "warning")) ||
 +
      ($logMode eq "warning" && ($mode eq "warning" or $mode eq "msg")) ||
 +
($logMode eq "msg" && $mode eq "msg")){
 +
          my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time);
 +
          $year += 1900; $mon ++;
 +
          $tmpStr="[" . uc($mode) . "] " . sprintf("%02d",$mday) . "-" .
 +
          sprintf("%02d",$mon) . "-$year " . sprintf("%02d",$hour) . ":" .
 +
          sprintf("%02d",$min) . ":" . sprintf("%02d",$sec) . " $text\n";
 +
          print LOG $tmpStr; print $tmpStr if $verbose;
 +
    }
 +
    close LOG;
 +
  }
 +
}
 +
 
 +
# ClickOnIcon se lanzará tras un click en el TrayIcon.
 +
# Para diferenciar el botón del ratón que se pulsó.
 +
sub ClickOnIcon {
 +
  logsys ("debug", "sub: ClickOnIcon start");
 +
  my ($widget, $event) = @_;
 +
  return 1 if $event->button == 2;
 +
  $menu->popup(undef, undef, undef, undef, $event->button,$event->time) if $event->button == 3;
 +
  &extAppStatus($config->{'appOnClick'}) if $event->button == 1;
 +
}
 +
 
 +
# extAppStatus lanzará una instancia de la aplicación si no existe
 +
sub extAppStatus {
 +
  logsys ("debug", "sub: extAppStatus start");
 +
  my ($refApp) = @_;
 +
  $config->{ $refApp . 'Pid'} = fork()
 +
    unless ($config->{ $refApp . 'Pid'}); # Crea un proceso hijo
 +
  die "error al hacer fork: $!" if !defined($config->{$refApp . 'Pid'});
 +
  if ( $config->{$refApp . 'Pid'} == 0) { # Si se trata del proceso hijo
 +
    logsys ("msg", "Iniciando proceso hijo con [$refApp]");
 +
    if ($config->{$refApp . 'Ok'}) {
 +
      $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = 'DEFAULT';
 +
      logsys ("warning", "Redirección de STDOUT para $refApp a $apppid");
 +
      open STDOUT, ">>$apppid";
 +
      exec ($config->{$refApp . 'App'}) ||
 +
        die "ATENCIÓN: Falló la ejecución de $refApp";
 +
    } else {exit -1}
 +
  } else { # Circuito del proceso padre
 +
    logsys ("msg", "Circuito del proceso padre [hijo=>$refApp pid:"
 +
      . $config->{$refApp . 'Pid'} . "]");
 +
    # Asigno una clave en la configuración con el valor de la pid con el padre
 +
    $config->{$config->{$refApp . 'Pid'}} = $refApp
 +
      unless $config->{$config->{$refApp . 'Pid'}};
 +
    # a ejecutar por el proceso padre
 +
    # TODO: Enviar setFocus a la aplicación
 +
    #
 +
  }
 +
}
 +
 
 +
# appRel nombre y versión del script
 +
sub appRel {
 +
  $0=~/.*\/(.*)$/;
 +
  my $fileRun = $1;
 +
  $fileRun = $0 unless $fileRun;
 +
  $fileRun = "aMulet.pl [$1]" if $fileRun ne 'aMulet.pl';
 +
  return $fileRun . " v.1.2 beta";
 +
}
 +
 
 +
# popUpsAct des/activar apartados del menú
 +
sub popUpsAct {
 +
  $menu_aLinkCreator->set_sensitive($config->{'aLinkCreatorOk'});
 +
  $menu_wxCas->set_sensitive($config->{'wxCasOk'});
 +
  $menu_aMuleGui->set_sensitive($config->{'aMuleGuiOk'});
 +
}
 +
 
 +
# pathsVerify comprobar la ruta de las aplicaciones
 +
sub pathsVerify{
 +
  foreach my $app (@appList) {
 +
    $config->{$app . 'Ok'} = (-x $config->{$app . 'App'});
 +
  }
 +
}
 +
 
 +
# inicializar comprobaciones iniciales
 +
sub inicializar {
 +
  my $tmpStr;
 +
  my $runDaemon;
 +
 
 +
  # Comprobar si ya estamos en marcha
 +
  if (-f $apppid){
 +
    my $appVerify=0;
 +
    # Comprobar si realmente la aplicación está en marcha.
 +
    if (open(APPPID, $apppid)){
 +
      my $line;
 +
      while ($line = <APPPID>) {
 +
        chop($line); last;
 +
      }
 +
      close(APPPID);
 +
      $appVerify=&isRunProcess($line);
 +
    }
 +
    if ($appVerify){
 +
      $tmpStr = "<b>" .appRel . "</b> ya está funcionando!\n\n";
 +
      $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = 'DEFAULT';
 +
      &showMessage( 'error', flushChars($tmpStr), 'ok');
 +
      exit -1;
 +
    } else {unlink $apppid}
 +
  }
 +
 
 +
  # Iniciar Bloqueo
 +
  logsys ("msg", "Iniciando archivo de bloqueo");
 +
  if (open LOG, ">$apppid"){print LOG "$$\n"}
 +
  # Cargar la configuración
 +
  logsys ("msg", "Cargando la configuración");
 +
  &loadConfig;
 +
  readSignatureConf;
 +
 
 +
  # Comprobar el estado del aMule como servicio
 +
  if ($config->{'aMuledOn'}){
 +
    logsys ("msg", "Iniciando la verificación de la existencia de amuled");
 +
    if (open(PROCESOS,"ps -A |")){
 +
      while (<PROCESOS>){
 +
        $runDaemon = 1 if ($_ =~ /amuled/);
 +
      }
 +
      close PROCESOS;
 +
    }
 +
    if (!$runDaemon) { $runDaemon = 1;
 +
      logsys ("msg", "No se detecta. Iniciando amuled ...");
 +
      if (open(AMULED,"$binaMuled |")){
 +
        my $aMuleRun;
 +
        while (<AMULED>){
 +
          logsys ("msg", $_);
 +
          logsys ("msg", "ATENCIÓN: Cambie la orden [$binaMuled] bajo su responsabilidad") if $_ =~ /binary requires/;
 +
          undef $runDaemon if $_ =~ /binary requires/;
 +
          $aMuleRun = 1 if $_ =~ /Raising current running instance/;
 +
        }
 +
        close AMULED;
 +
        if ($aMuleRun){
 +
          logsys ("msg", "*"x56 );
 +
          logsys ("msg", "*"x5 ." Se ha detectado que el aMule está en marcha " ."*"x6 );
 +
          logsys ("msg", "*"x56 );
 +
          logsys ("msg", "aMulet.pl se diseñó pensando en las herramientas remotas" );
 +
          logsys ("msg", "*"x56 );
 +
          logsys ("msg", "* FUNCIONARÍA, pero no veo la necesidad de ello.  STOP!!" );
 +
          logsys ("msg", "*"x56 );
 +
          undef $runDaemon;
 +
        };
 +
        logsys ("msg", "El amuled ha sido puesto en marcha.") if $runDaemon;
 +
      } else {
 +
        undef $runDaemon;
 +
        logsys ("msg", "No se ha podido ejecutar el amuled!");
 +
        logsys ("msg", "Se puede indicar en la configuración que se salte este paso");
 +
        $tmpStr = "<u><b>No se ha podido ejecutar aMuled</b></u>.\n\n";
 +
        $tmpStr .= "¿Desactivo la opción de arrancar el amuled en la configuración?\n\n";
 +
        $tmpStr .= "Desactive esta opción si el aMuled está residente en otra máquina de su red.";
 +
        $tmpStr .= "\n\nNo he detectado el aMuleGui." unless $config->{'aMuleGuiOk'};
 +
        $tmpStr .= "\nDeberá indicar donde está situado éste en la configuración" unless $config->{'aMuleGuiOk'};
 +
        my $confirmacion= &showMessage( 'question', flushChars($tmpStr), 'yes-no');
 +
        if ($confirmacion eq 'yes'){
 +
          $config->{'aMuledOn'}=0;
 +
          &saveConfig;
 +
          $runDaemon = 1;
 +
        };
 +
      }
 +
    } else {logsys ("msg", "El aMuled ha sido detectado :))");}
 +
  } else {
 +
    $runDaemon = 1;
 +
    logsys ("msg", "ATENCIÓN No se detectará ni arrancará el aMuled");
 +
    logsys ("msg", "        Se puede modificar esto en la configuración");
 +
  }
 +
 
 +
  # Comprobar que la aplicación que arranca tras el click en el icono existe.
 +
  # Esto no afectará al retorno de esta función.
 +
  if ($runDaemon){
 +
    logsys ("msg", "Verificando la aplicación que responde al click del icono.");
 +
    if (!$config->{$config->{'appOnClick'} . 'Ok'}){
 +
        $tmpStr = "Debe indicar en la configuración dónde residen el ejecutable\n";
 +
        $tmpStr .= "de la aplicación <b>$config->{'appOnClick'}</b> o escoger otra\n";
 +
        $tmpStr .= "aplicación que responda al click en el icono";
 +
      &showMessage( 'warning', flushChars($tmpStr), 'ok');
 +
      &opcionesGui;
 +
    }
 +
  } # TODO: Intentar mostrarlo como modal y esperar respuesta.
 +
  return $runDaemon;
 +
}
 +
 
 +
# visorTexto Crea una ventana Gtk en la que muestra el logfile del aMule
 +
sub visorTexto {
 +
  logsys ("debug", "sub: visorTexto start");
 +
  my ($widget, $event) = @_;
 +
  logsys ("debug", "Deshabilitando la llamada en el menú");
 +
  $widget->set_sensitive(0);
 +
  my $visorTextWin = Gtk2::Window->new("toplevel");
 +
  my $vbox = Gtk2::VBox->new;
 +
  my $scrolledwindow = Gtk2::ScrolledWindow->new;
 +
  my $textBuffer = Gtk2::TextBuffer->new;
 +
  my $ok = Gtk2::Button->new_from_stock("gtk-close");
 +
  my $textView = Gtk2::TextView->new_with_buffer($textBuffer);
 +
  my $LineaLog;
 +
 
 +
  $visorTextWin->set_title('Visor de Texto');
 +
  $visorTextWin->set_position('center');
 +
  $visorTextWin->set_default_size(640, 300);
 +
  logsys ("debug", "Leyendo " . $config->{'aMuleDir'} . "/logfile");
 +
  open (LOGFILE, $config->{'aMuleDir'} . "/logfile") || logsys ("warning", $config->{'aMuleDir'} . "/logfile: $!");
 +
  while (<LOGFILE>) {$LineaLog .= $_;}
 +
  close LOGFILE;
 +
  $textView->set_editable(0);
 +
  $textView->set_cursor_visible(0);
 +
  $textBuffer->set_text($LineaLog);
 +
  $scrolledwindow->set_policy('automatic', 'automatic');
 +
 
 +
  $scrolledwindow->add($textView);
 +
  $vbox->add($scrolledwindow );
 +
  $vbox->pack_end($ok, 0, 0, 3);
 +
  $visorTextWin->add($vbox);
 +
 
 +
  $ok->signal_connect("clicked", sub { $visorTextWin->destroy; } );
 +
  $visorTextWin->signal_connect('destroy' => sub { $widget->set_sensitive(1); });
 +
  logsys ("msg", "Mostrando la ventana de logs");
 +
  $visorTextWin->show_all;
 +
}
 +
 
 +
# isRunProcess comprobará si un proceso está en ejecución
 +
sub isRunProcess {
 +
  my ($proceso) = @_;
 +
  my $runDaemon = 0;
 +
  if (open(PROCESOS,"ps -A |")){
 +
    while (<PROCESOS>){$runDaemon = 1 if $_ =~ /^$proceso\s/;}
 +
    close PROCESOS;
 +
  }
 +
  return $runDaemon;
 +
}
 +
 
 +
sub infoOnLine {
 +
  my $tmpStr = '';
 +
  &onlineInfo if $config->{'signOn'};
 +
  if ($config->{'signOn'} && $config->{'ConectStatus'}==1){
 +
    $tmpStr = "\n\n<b>" . $config->{'nickName'} . "</b> con <b>aMule</b> [<i>";
 +
    $tmpStr .= $config->{'aMuleVer'} . "</i>]\n";
 +
    $tmpStr .= "está conectado al puerto <b>" . $config->{'portServ'};
 +
    $tmpStr .= "</b> de <b>" . $config->{'nameServ'} . "</b>\ncon <b>ID ";
 +
    $tmpStr .= $config->{'typeId'}eq"H"?"alta</b>":"baja</b>";
 +
    $tmpStr .= " y la <b>red kad ";
 +
    if ($config->{'kadStatus'}==2) {$tmpStr .= "bien conectada</b>\n";
 +
      } elsif ($config->{'kadStatus'}==1) {$tmpStr .= "en firewalled</b>\n";
 +
      } else {$tmpStr .= "desconectada</b>\n"};
 +
    $tmpStr .= "lleva ";
 +
    my $tmpStr2 = $config->{'TimeRun'} % 60 . " segundos";
 +
    my $tiempo = sprintf("%d",$config->{'TimeRun'} / 60);
 +
    $tmpStr2 = $tiempo % 60 . " minutos y $tmpStr2" if $tiempo;
 +
    $tiempo = sprintf("%d",$tiempo / 60) if $tiempo;
 +
    $tmpStr2 = $tiempo % 24 . " horas $tmpStr2" if $tiempo;
 +
    $tiempo = sprintf("%d",$tiempo / 24) if $tiempo;
 +
    $tmpStr2 = $tiempo . " dias $tmpStr2" if $tiempo;
 +
    $tmpStr .= $tmpStr2 . " conectado\n";
 +
    $tmpStr .= "y en este tiempo ha descargado: <b>";
 +
    $tmpStr .= sprintf("%.2f",$config->{'SessionDown'}/1048576) . " Mb</b>\n";
 +
    $tmpStr .= "con lo que tiene un total descagado de ";
 +
    $tmpStr .= sprintf("%.2f",$config->{'totalDown'}/1048576) . " Mb\n";
 +
    $tmpStr .= "Además comparte <b>" . $config->{'filesShared'} . "</b> archivos y en esta sesión\n";
 +
    $tmpStr .= "se han subido <b>" .  sprintf("%.2f",$config->{'SessionUp'}/1048576);
 +
    $tmpStr .= " Mb</b> obteniendo un\ntotal de " .  sprintf("%.2f",$config->{'totalUp'}/1048576);
 +
    $tmpStr .= " Mb subidos a la Red,\nen cola tiene ahora mismo ";
 +
    $tmpStr .= $config->{'colaCli'} . " clientes";
 +
  }
 +
  return flushChars($tmpStr);
 +
}
 +
 
 +
# opcionesGui muestra la ventana de opciones
 +
sub opcionesGui {
 +
  my ($widget, $event) = @_;
 +
  logsys ("debug", "Deshabilitando la llamada en el menú");
 +
  $widget->set_sensitive(0) if defined $widget;
 +
  my $guiOpciones = Gtk2::Window->new;
 +
  my $vboxPrincipal = Gtk2::VBox->new;
 +
  my $notebook = Gtk2::Notebook->new;
 +
  my $select_aMuleGui = Gtk2::RadioButton->new('appOnClick', 'aMuleGui');
 +
  my @grupo = $select_aMuleGui->get_group;
 +
  my $select_alc = Gtk2::RadioButton->new_with_label(@grupo, 'aLinkCreator');
 +
  my $select_wxcas = Gtk2::RadioButton->new_with_label(@grupo, 'wxCas');
 +
  my $check_aMuled_on = new Gtk2::CheckButton->new(flushChars('Arrancar aMule como servicio al principio (si no lo está)'));
 +
  my $vBoxTabRutas = Gtk2::VBox->new;
 +
  my $vBoxTabAbout = Gtk2::VBox->new;
 +
  my $hboxPrincipal = Gtk2::HBox->new;
 +
  my $hBoxTabRutas1 = Gtk2::HBox->new;
 +
  my $hBoxTabRutas2 = Gtk2::HBox->new;
 +
  my $hBoxTabRutas3 = Gtk2::HBox->new(1);
 +
  my $hseparator1 = Gtk2::HSeparator->new;
 +
  my $hseparator2 = Gtk2::HSeparator->new;
 +
  my $hseparator3 = Gtk2::HSeparator->new;
 +
  my $labelTabRutas = Gtk2::Label->new('General');
 +
  my $labelTabAbout = Gtk2::Label->new('About');
 +
  my $labelTabRut1 = Gtk2::Label->new('Relacionado con el Servicio amuled');
 +
  my $labelTabRut2 = Gtk2::Label->new('aMule Home Dir');
 +
  my $labelTabRut3 = Gtk2::Label->new('Ruta a las aplicaciones');
 +
  my $labelTabRut4 = Gtk2::Label->new('aMuleGui: ');
 +
  my $labelTabRut5 = Gtk2::Label->new('wxCas: ');
 +
  my $labelTabRut6 = Gtk2::Label->new('aLinkCreator: ');
 +
  my $labelTabRut7 = Gtk2::Label->new(flushChars('Icono que se mostrará en la barra'));
 +
  my $labelTabRut8 = Gtk2::Label->new('Icono:');
 +
  my $labelTabRut9 = Gtk2::Label->new(flushChars('¿Qué aplicación arrancará tras hacer click en el icono?'));
 +
  my $labelAbout1 = Gtk2::Label->new('aMulet.pl');
 +
  my $labelAbout2 = Gtk2::Label->new('by AnimAlf - AnimAlf@engendro.sytes.net');
 +
  my $labelAbout3 = Gtk2::Label->new('una idea de tx2z en http://forum.amule.org');
 +
  my $labelAbout4 = Gtk2::Label->new('Distribuido bajo la licencia GPL');
 +
  my $labelAbout5 = Gtk2::Label->new('http://www.gnu.org/copyleft/gpl.html');
 +
  my $labelAbout6 = Gtk2::Label->new;
 +
  my $edit_home_aMule = Gtk2::Entry->new;
 +
  my $edit_aMuleGui_path = Gtk2::Entry->new;
 +
  my $edit_wxcas_path = Gtk2::Entry->new;
 +
  my $edit_alc_path = Gtk2::Entry->new;
 +
  my $edit_icon_path = Gtk2::Entry->new;
 +
  my $btnOk = Gtk2::Button->new_from_stock("gtk-apply");
 +
  my $btnCancel = Gtk2::Button->new_from_stock("gtk-cancel");
 +
  my $btnRutas1 = Gtk2::Button->new('...');
 +
  my $btnRutas2 = Gtk2::Button->new('...');
 +
  my $btnRutas3 = Gtk2::Button->new('...');
 +
  my $btnRutas4 = Gtk2::Button->new('...');
 +
  my $btnRutas5 = Gtk2::Button->new('Selecciona');
 +
  my $tableTabRutas = Gtk2::Table->new(3, 3, 0 );
 +
 
 +
  # Posicionando en la ventana principal
 +
  $guiOpciones->add($vboxPrincipal);
 +
  # Posicionando en el notebook (contenedor de pestañas)
 +
  $vboxPrincipal->add($notebook);
 +
 
 +
  # primera pestaña
 +
  $notebook->append_page($vBoxTabRutas, $labelTabRutas);
 +
 
 +
  $vBoxTabRutas->pack_start($labelTabRut1,    0, 0, 3);
 +
  $vBoxTabRutas->pack_start($hBoxTabRutas1,    0, 0, 3);
 +
 
 +
  $hBoxTabRutas1->pack_start($labelTabRut2,    0, 0, 3);
 +
  $hBoxTabRutas1->add($edit_home_aMule);
 +
  $hBoxTabRutas1->pack_end($btnRutas1,        0, 0, 3);
 +
  $vBoxTabRutas->pack_start($check_aMuled_on,  0, 0, 3);
 +
 
 +
  $vBoxTabRutas->pack_start($hseparator2,      0, 0, 3);
 +
  $vBoxTabRutas->pack_start($labelTabRut3,    0, 0, 3);
 +
  $vBoxTabRutas->pack_start($tableTabRutas,    0, 0, 3);
 +
   
 +
  $tableTabRutas->attach($labelTabRut4,        0, 1, 0, 1, ['fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($edit_aMuleGui_path,  1, 2, 0, 1, ['expand', 'fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($btnRutas2,          2, 3, 0, 1, ['fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($labelTabRut5,        0, 1, 1, 2, ['fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($edit_wxcas_path,    1, 2, 1, 2, ['expand', 'fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($btnRutas3,          2, 3, 1, 2, ['fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($labelTabRut6,        0, 1, 2, 3, ['fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($edit_alc_path,      1, 2, 2, 3, ['expand', 'fill'], [], 0, 0 );
 +
  $tableTabRutas->attach($btnRutas4,          2, 3, 2, 3, ['fill'], [], 0, 0 );
 +
 
 +
  $vBoxTabRutas->pack_start($hseparator1,      0, 0, 3);
 +
  $vBoxTabRutas->pack_start($labelTabRut9,    0, 0, 3);
 +
  $vBoxTabRutas->pack_start($hBoxTabRutas3,    0, 0, 3);
 +
  $hBoxTabRutas3->pack_start($select_aMuleGui, 0, 0, 3);
 +
  $hBoxTabRutas3->pack_start($select_alc,      0, 0, 3);
 +
  $hBoxTabRutas3->pack_start($select_wxcas,    0, 0, 3);
 +
 
 +
  $vBoxTabRutas->pack_end($hBoxTabRutas2,      0, 0, 3);
 +
 
 +
  $hBoxTabRutas2->pack_start($labelTabRut8,    0, 0, 3);
 +
  $hBoxTabRutas2->add($edit_icon_path);
 +
  $hBoxTabRutas2->pack_end($btnRutas5,        0, 0, 3);
 +
 
 +
  $vBoxTabRutas->pack_end($labelTabRut7,      0, 0, 3);
 +
  $vBoxTabRutas->pack_end($hseparator3,        0, 0, 3);
 +
 
 +
  # Segunda pestaña
 +
  $notebook->append_page($vBoxTabAbout, $labelTabAbout);
 +
  $vBoxTabAbout->pack_start($labelAbout1,      0, 0, 3);
 +
  $vBoxTabAbout->pack_start($labelAbout2,      0, 0, 3);
 +
  $vBoxTabAbout->pack_start($labelAbout3,      0, 0, 3);
 +
  $vBoxTabAbout->pack_start($labelAbout4,      0, 0, 3);
 +
  $vBoxTabAbout->pack_start($labelAbout5,      0, 0, 3);
 +
  $vBoxTabAbout->pack_start($labelAbout6,      0, 0, 3);
 +
 
 +
  # Posicionando en la parte inferior de la ventana principal
 +
  $vboxPrincipal->pack_end($hboxPrincipal,    0, 0, 3);
 +
  $hboxPrincipal->pack_start($btnOk,          0, 0, 3);
 +
  $hboxPrincipal->pack_end($btnCancel,        0, 0, 3);
 +
 
 +
  $labelAbout1->set_markup('<span foreground="DarkRed" size="x-large"><b>' . appRel . ' </b></span>');
 +
  #TODO: Intentar conseguir una UrlLabel, fácil con gnome-open (pero y si no existe gnome?)
 +
  #http://forum.amule.org/thread.php?threadid=9751
 +
  $labelAbout3->set_markup('<span>Una idea de <b>tx2z</b> en http://forum.amule.org</span>');
 +
  #Añado un mini resumen de la información disponible en amulesig.dat
 +
  $labelAbout6->set_markup(infoOnLine);
 +
  if ($config->{'signOn'}){
 +
    Glib::Timeout->add($config->{'signOnTime'}*1000, sub {
 +
      $labelAbout6->set_markup(infoOnLine);
 +
      $tray->send_message($config->{'signOnTime'}*1000, "[AnimAlf dice] No entiendo");});
 +
  }
 +
 
 +
  # Propiedades de la ventana principal
 +
  $guiOpciones->set_title('Opciones aMulet.pl');
 +
  $guiOpciones->set_position('center');
 +
  #TODO: Intentar que actuase como una ventana modal, como el showMessage
 +
  #      y el punto de la llamada esperase la respuesta ok-cancel
 +
  logsys ("msg", "Mostrando la ventana de opciones");
 +
  logsys ("warning", "*"x36) unless $widget;
 +
  logsys ("warning", "TODO: Que sea modal tipo showMessage") unless $widget;
 +
  logsys ("warning", "*"x36) unless $widget;
 +
 
 +
  # Propiedades del notebook
 +
  $notebook->set_tab_pos('top');
 +
  $notebook->set_show_tabs  (1);
 +
  $notebook->set_show_border(1);
 +
  $notebook->set_scrollable (0);
 +
  $notebook->set_tab_hborder(2);
 +
  $notebook->set_tab_vborder(2);
 +
 
 +
  # alineando de algunas etiquetas
 +
  $labelTabRut4->set_alignment(1,0.5);
 +
  $labelTabRut5->set_alignment(1,0.5);
 +
  $labelTabRut6->set_alignment(1,0.5);
 +
 
 +
  # Asignando la configuración.
 +
  $edit_home_aMule->set_text($config->{'aMuleDir'});
 +
  $edit_aMuleGui_path->set_text($config->{'aMuleGuiApp'});
 +
  $edit_wxcas_path->set_text($config->{'wxCasApp'});
 +
  $edit_alc_path->set_text($config->{'aLinkCreatorApp'});
 +
  $edit_icon_path->set_text($config->{'iconPath'});
 +
 
 +
  $check_aMuled_on->set_active($config->{'aMuledOn'});
 +
 
 +
  $select_wxcas->set_active($config->{'appOnClick'} eq 'wxCas');
 +
  $select_alc->set_active($config->{'appOnClick'} eq 'aLinkCreator');
 +
  $select_aMuleGui->set_active($config->{'appOnClick'} eq 'aMuleGui');
 +
 
 +
  $select_wxcas->set_sensitive($config->{'wxCasOk'});
 +
  $select_alc->set_sensitive($config->{'aLinkCreatorOk'});
 +
  $select_aMuleGui->set_sensitive($config->{'aMuleGuiOk'});
 +
 
 +
  # Lo mostramos todo
 +
  $guiOpciones->show_all;
 +
  # La asignación de señales
 +
  $btnOk->signal_connect("clicked", sub {
 +
      my ($tmpStr, $tmpStr1);
 +
 
 +
      $tmpStr1 = 'wxCas' if $select_wxcas->get_active;
 +
      $tmpStr1 = 'aLinkCreator' if $select_alc->get_active;
 +
      $tmpStr1 = 'aMuleGui' if $select_aMuleGui->get_active;
 +
 
 +
      $tmpStr .= $edit_home_aMule->get_text . ": No es un directorio\n"
 +
        if !(-d $edit_home_aMule->get_text);
 +
      $tmpStr .= $edit_aMuleGui_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
 +
        if !(-x $edit_aMuleGui_path->get_text);
 +
      $tmpStr .= $edit_wxcas_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
 +
        if !(-x $edit_wxcas_path->get_text);
 +
      $tmpStr .= $edit_alc_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
 +
        if !(-x $edit_alc_path->get_text);
 +
      $tmpStr .= $edit_icon_path->get_text . ": <b>No parece que exista</b>\n"
 +
        if !(-e $edit_icon_path->get_text);
 +
 
 +
      if (defined $tmpStr){
 +
          $tmpStr = "<b>Se ha detectado lo siguiente:</b>\n\n$tmpStr\n\n" . flushChars("¿Desea continuar de todos modos?");
 +
          my $confirmacion= &showMessage( 'question', $tmpStr, 'yes-no');
 +
          return if ($confirmacion eq 'no');
 +
      }
 +
 
 +
      $config->{'appOnClick'} = 'wxCas' if $select_wxcas->get_active;
 +
      $config->{'appOnClick'} = 'aLinkCreator' if $select_alc->get_active;
 +
      $config->{'appOnClick'} = 'aMuleGui' if $select_aMuleGui->get_active;
 +
 
 +
      $config->{'aMuleDir'} = $edit_home_aMule->get_text;
 +
      $config->{'aMuleGuiApp'} = $edit_aMuleGui_path->get_text;
 +
      $config->{'wxCasApp'} = $edit_wxcas_path->get_text;
 +
      $config->{'aLinkCreatorApp'} = $edit_alc_path->get_text;
 +
      &showMessage( 'info', flushChars("El icono cambiará cuando reinicie la aplicación."), 'ok')
 +
        if $config->{'iconPath'} ne $edit_icon_path->get_text;
 +
      $config->{'iconPath'} = $edit_icon_path->get_text;
 +
 
 +
      $config->{'aMuledOn'} = $check_aMuled_on->get_active;
 +
 
 +
      &saveConfig; pathsVerify; popUpsAct; readSignatureConf;
 +
      $guiOpciones->destroy;
 +
    }
 +
  );
 +
  $btnCancel->signal_connect("clicked", sub { $guiOpciones->destroy; } );
 +
  $guiOpciones->signal_connect('destroy' => sub {$widget->set_sensitive(1) if defined $widget;});
 +
  $btnRutas1->signal_connect('clicked' => sub { dialogOpen($edit_home_aMule,
 +
    'Seleccione el directorio home de aMule', 'select-folder'); });
 +
  $btnRutas2->signal_connect('clicked' => sub { dialogOpen($edit_aMuleGui_path,
 +
    flushChars('Seleccione la aplicación aMuleGui'), 'open'); });
 +
  $btnRutas3->signal_connect('clicked' => sub { dialogOpen($edit_wxcas_path,
 +
    flushChars('Seleccione la aplicación wxCas'), 'open'); });
 +
  $btnRutas4->signal_connect('clicked' => sub { dialogOpen($edit_alc_path,
 +
    flushChars('Seleccione la aplicación aLinCreator'), 'open'); });
 +
  $btnRutas5->signal_connect('clicked' => sub { dialogOpen($edit_icon_path,
 +
    flushChars('Seleccione el icono que utilizará aMulet'), 'open'); });
 +
}
 +
 
 +
# loadConfig carga la configuración o la crea si no existe
 +
sub loadConfig {
 +
  if ( open( CONFIGFILE, "$ENV{HOME}/.aMule/amulet.rc" ) ) {
 +
    while (<CONFIGFILE>) {
 +
      if ( $_ =~ /(\w+)\s=\s(.+)/ ) { $config->{$1} = $2 }
 +
    }
 +
    close CONFIGFILE;
 +
  } else {
 +
    # es la primera vez.
 +
    mkdir "$ENV{HOME}/.aMule" unless (-d "$ENV{HOME}/.aMule");
 +
    $config = { # Por defecto
 +
      'aMuleDir'        => "$ENV{'HOME'}/.aMule",
 +
      'aMuleGuiApp'    => 'amulegui',
 +
      'wxCasApp'        => 'wxcas',
 +
      'aLinkCreatorApp' => 'alc',
 +
      'iconPath'        => '/usr/share/pixmaps/amule.xpm',
 +
      'appOnClick'      => 'aMuleGui',
 +
      'aMuledOn'        => 1
 +
    };
 +
    foreach my $app (@appList) {
 +
      $config->{$app . 'App'} = which($config->{$app . 'App'});
 +
    }
 +
    pathsVerify;
 +
    &saveConfig;
 +
  }
 +
  pathsVerify;
 +
}
 +
 
 +
# saveConfig guarda la configuración en un archivo
 +
sub saveConfig {
 +
  my @keyList = ('aMuleDir', 'aMuleGuiApp', 'wxCasApp',
 +
    'aLinkCreatorApp', 'iconPath', 'appOnClick', 'aMuledOn');
 +
  if ( open( CONFIGFILE, '>', "$ENV{HOME}/.aMule/amulet.rc" ) ) {
 +
    foreach my $key (@keyList) {
 +
      print CONFIGFILE "$key = $config->{$key}\n" if $config->{$key} ne '';
 +
    }
 +
    close CONFIGFILE;
 +
  }else {logSys("warning", "No se puede escribir en el archivo de configuración!");}
 +
};
 +
 
 +
# showMessage Muestra una ventana con un mensaje, y espera respuesta.
 +
sub showMessage {
 +
  #$icon info warning error question
 +
  #$type none ok close cancel yes-no ok-cancel
 +
  my ($icon,$text,$type) = @_;
 +
  my $dialog = Gtk2::MessageDialog->new_with_markup (undef,
 +
    [qw/modal destroy-with-parent/], $icon, $type, sprintf "$text");
 +
  my $retval = $dialog->run;
 +
  $dialog->destroy;
 +
  return $retval;
 +
}
 +
 
 +
# which Devuelve el path completo a la aplicación.
 +
sub which {
 +
  my $appFile = shift;
 +
  foreach (split(':', $ENV{PATH})) {
 +
    my $tmpStr = "$_/$appFile";
 +
    if (-x $tmpStr) {
 +
      $appFile = $tmpStr;
 +
      last;
 +
    }
 +
  }
 +
  return $appFile;
 +
}
 +
 
 +
# dialogOpen ventana de selección de archivos o directorios y escribe
 +
# el resultado en en el entry que debe ser el primer parámetro.
 +
sub dialogOpen {
 +
  # $widget debe ser un entry
 +
  # $type open save select-folder create-folder
 +
  my($widget,$caption,$type) =@_;
 +
  my $chooserDialog =  Gtk2::FileChooserDialog->new ($caption, undef, $type,
 +
    'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok');
 +
  my $filename = $chooserDialog->get_filename if 'ok' eq $chooserDialog->run;
 +
  $widget->set_text ($filename) if defined $filename;
 +
  $chooserDialog->destroy;
 +
  return;
 +
}
 +
</nowiki></pre>

Revision as of 01:20, 7 December 2006

aMulet.pl source

#!/usr/bin/perl -w
#
# file aMulet.pl
# Copyright by AnimAlf
#
# This lines are under the GNU General Public License.
# http://www.gnu.org/copyleft/gpl.html
#
# aMulet - la t final en catalán le indica diminutivo
#          También se traduce como amuleto

use strict;

my $binaMuled = 'amuled -f';
my $apppid = "$ENV{HOME}/.tx2z.pid";
my $logMode = "warning"; # debug warning msg
my $verbose = 1; # true false

if (!defined($ENV{'DISPLAY'})){
  print &appRel . "\nATENCIÓN: Este script necesita ser ejecutado bajo las X Window\n";
  exit -1;
}

eval "use Gtk2::TrayIcon;";
if ($@) {
  print &appRel;
  print "\n\nATENCIÓN: Este script necesita el módulo Gtk2::TrayIcon\n\n";
  print "Puede instalarlo a través de CPAN del siguiente modo:\n\n";
  print "\t\tperl -MCPAN -e shell\n";
  print "\t\tinstall Gtk2::TrayIcon\n\n";
  print "bye\n";
  exit -1;
}

my $modUniCode;
eval "use Unicode::MapUTF8 qw(from_utf8);";
$modUniCode = !$@;

my $config = {};
my @appList = ( 'aMuleGui', 'wxCas', 'aLinkCreator');

# Los procesos hijo envían esta señal al padre cuando terminan y ésta
# debe ser atendida, si no se quedan zombies hasta que termine el padre.
$SIG{CHLD} = \&finalHijo;

# Si se recibe cualquier señal que pueda provocar la interrupción del
# normal funcionamiento, entonces nos encargamos de finalizar correctamente.
$SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = \&finalizar;

# Iniciando Gtk2
Gtk2->init;

# Comprobaciones iniciales. Cargar configuración.
if (!&inicializar){
  logsys ("msg", &appRel ." ha abortado el arranque.");
  unlink $apppid or die "No puedo eliminar el archivo de bloqueo";
  exit -1;
}
logsys ("msg", &appRel ." iniciado");

# Formas Gtk2
my $menu = Gtk2::Menu->new();
my $menu_opciones = Gtk2::MenuItem->new("opciones");
my $menu_aLinkCreator = Gtk2::MenuItem->new("aLinkCreator");
my $menu_wxCas = Gtk2::MenuItem->new("wxCas");
my $menu_aMuleGui = Gtk2::MenuItem->new("aMuleGui");
my $menu_verLog = Gtk2::MenuItem->new("aMule log");
my $menuSalir = Gtk2::MenuItem->new("Salir");
my $pic;
eval {$pic = Gtk2::Gdk::Pixbuf->new_from_file ($config->{'iconPath'});};
if ($@){
  my @aMulet_xpm = ('18 24 17 1', '       c None', '.      c #312A28', '+      c #524842',
           '@      c #D42238', '#      c #835128', '$      c #985A20', '%      c #6A6B6A',
           '&      c #AA7B50', '*      c #EC5867', '=      c #BF7B3F', '-      c #C9792F',
           ';      c #898989', '>      c #E39854', ',      c #CB9D83', '"      c #A9AAAA',
           ')      c #CDCBC9', '!      c #F4F5F3', '                  ',
           '          $       ',
           '         !>       ',
           '  #!    )$$       ',
           '  ##    #-$       ',
           '  )##$  -,#       ',
           '   .+##)$,)       ',
           '    ...$,!"       ',
           '    ".$=-$"       ',
           '     ;==-!=       ',
           '     " >!  "      ',
           '     ; !&  ;      ',
           '     " !)))"      ',
           '     """"""!      ',
           '    %!."""");     ',
           '    ;))";;;"%;;)  ',
           '  ">-)";;;;"->""  ',
           '  )-=$,;;%@,->$)  ',
           '  ;%--#=>>**+%%;  ',
           ' +....&=>>>>#!))! ',
           '%"-=##$>>>>=&$--;;',
           '+)-=##&=>>>=#=->)+',
           '%;;=$+.+>>++$->,;%', ' "              . ');
  $pic = Gtk2::Gdk::Pixbuf->new_from_xpm_data (@aMulet_xpm);
  logsys ("warning", "Error al cargar el icono: " . $config->{'iconPath'});
  logsys ("msg", "Se procede a cargar el icono interno.");
}
my $img= Gtk2::Image->new_from_pixbuf($pic->scale_simple(18, 24, "tiles"));
my $tray= Gtk2::TrayIcon->new("trayicon");
my $paraEvent = Gtk2::EventBox->new;
my $trayTooltip = Gtk2::Tooltips->new;
my $SeparadorMenu1 = Gtk2::SeparatorMenuItem->new;
my $SeparadorMenu2 = Gtk2::SeparatorMenuItem->new;
my $SeparadorMenu3 = Gtk2::SeparatorMenuItem->new;

$menu->append($menu_verLog);
$menu->append($SeparadorMenu1);
$menu->append($menu_opciones);
$menu->append($SeparadorMenu2);
$menu->append($menu_aLinkCreator);
$menu->append($menu_wxCas);
$menu->append($menu_aMuleGui);
$menu->append($SeparadorMenu3);
$menu->append($menuSalir);

$paraEvent->add($img);
$tray->add($paraEvent);
&statusText;

# Tratamiento de señales
logsys ("debug", "Asignación de señales");
$tray->signal_connect ('button-press-event' => \&ClickOnIcon);
$menu_opciones->signal_connect('activate' => \&opcionesGui);
$menu_verLog->signal_connect('activate' => \&visorTexto);
$menu_aLinkCreator->signal_connect('activate' => sub {&extAppStatus('aLinkCreator')});
$menu_wxCas->signal_connect('activate' => sub {&extAppStatus('wxCas')});
$menu_aMuleGui->signal_connect('activate' => sub {&extAppStatus('aMuleGui')});
$menuSalir->signal_connect('activate' => \&finalizar);
Glib::Timeout->add($config->{'signOnTime'}*1000, \&statusText) if $config->{'signOn'};

# show sobre los witgets para que sean visibles
logsys ("debug", "Show sobre los witgets");
$menu->show_all;
$tray->show_all;
&popUpsAct;
# iniciando bucle de mensajes
logsys ("debug", "Inicio del bucle de mensajes");
Gtk2->main;
# Fin. Aquí termina el flujo.

#############
# Funciones #
#############
# statusText Establece el texto del toolTip
sub statusText{
  my $tmpStr = &appRel ." - Click derecho para opciones";
  if ($config->{'signOn'}){
    &onlineInfo;
    # my $tmpStr;
    if ($config->{'ConectStatus'}){
      if ($config->{'typeId'}eq"H"){$tmpStr = "[ID alta]"} 
        elsif ($config->{'typeId'}eq"L"){$tmpStr = "[ID baja]"}
        else {$tmpStr = "[Conectando]"}
      $tmpStr .= " Down:" . $config->{'downSpeed'};
      $tmpStr .= " kb/s Up:" . $config->{'upSpeed'} ." Kb/s ";
      if ($config->{'kadStatus'}==2) {$tmpStr .= "[kad ok]";
        } elsif ($config->{'kadStatus'}==1) {$tmpStr .= "[kad firewalled]";
        } else {$tmpStr .= "[kad off]"};
    } else {$tmpStr = &appRel . " [aMuled No Conectado]"}
    
  }
  $trayTooltip->set_tip($tray, $tmpStr);
  $tray->send_message($config->{'signOnTime'}*1000, "[AnimAlf dice] No entiendo");
}

# signatureOn Comprueba OnlineSignature en amule.conf
sub readSignatureConf {
  $config->{'signOn'} = 0;
  $config->{'signOnTime'} = 0;
  if (-f $config->{'aMuleDir'} . "/amule.conf"){
    if ( open( CONFIGFILE, $config->{'aMuleDir'} . "/amule.conf" ) ) {
      while (<CONFIGFILE>) {
        if ( $_ =~ /^OnlineSignature=(.+)/ ) { $config->{'signOn'} = $1 }
        if ( $_ =~ /^OnlineSignatureUpdate=(.+)/ ) { $config->{'signOnTime'} = $1 }
      }
      close CONFIGFILE;
    }
  }
}

# onlineInfo Recoge la información de amulesig.dat
sub onlineInfo {
  if ( open( SIGNFILE, $config->{'aMuleDir'} . "/amulesig.dat" ) ) {
    my @signFile;
    while (<SIGNFILE>) {
      $_ =~ /(.+)\n$/; push (@signFile, $1)
    }
    close SIGNFILE;
    $config->{'ConectStatus'} = $signFile[0];
    $config->{'nameServ'}     = $signFile[1];
    $config->{'ipServ'}       = $signFile[2];
    $config->{'portServ'}     = $signFile[3];
    $config->{'typeId'}       = $signFile[4];
    $config->{'kadStatus'}    = $signFile[5];
    $config->{'downSpeed'}    = $signFile[6];
    $config->{'upSpeed'}      = $signFile[7];
    $config->{'colaCli'}      = $signFile[8];
    $config->{'filesShared'}  = $signFile[9];
    $config->{'nickName'}     = $signFile[10];
    $config->{'totalDown'}    = $signFile[11];
    $config->{'totalUp'}      = $signFile[12];
    $config->{'aMuleVer'}     = $signFile[13];
    $config->{'SessionDown'}  = $signFile[14];
    $config->{'SessionUp'}    = $signFile[15];
    $config->{'TimeRun'}      = $signFile[16];
  }
}

# finalHijo atender la señal de los hijos (cuando terminan la mandan)
sub finalHijo {
  $SIG{CHLD} = \&finalHijo;
  my $pid = wait;
  return unless $config->{$pid}; # El system() realiza un fork() también.
  logsys ("msg", "Acaba de finalizar: " . $config->{$pid} . 
    " [" . $config->{$config->{$pid} . 'Pid'} . "]");
  undef $config->{$config->{$pid} . 'Pid'};
  undef $config->{$pid};
}

# finalizar correctamente y eliminar bloqueo
sub finalizar {
  $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = \&finalizar;
  logsys ("msg", "Finalizando ..."); 
  #Finalizar el bucle de mensajes
  Gtk2->main_quit;
  #Comprobar que no quede ninguna aplicación hijo en funcionamiento
  #y si existe eliminarlas antes de finalizar no vayan a quedar zombies.
  logsys ("msg", "Finalizando posibles hijos");
  # Ahora la señal de retorno del hijo la trataremos aquí.
  $SIG{CHLD} = 'DEFAULT';
  foreach my $app (@appList) {
    if (defined $config->{$app . "Pid"}){
      logsys ("msg", "[$app] está en marcha. Finalizándolo ...");
      kill ('SIGTERM', $config->{$app . "Pid"});
      waitpid($config->{$app . "Pid"}, 0);
    }
  }
  logsys ("msg", "No quedan procesos hijo de " . &appRel . " en el sistema");
  #TODO Si archivo de bloqueo es usado para log volcar a otro sitio.
  logsys ("warning", "TODO: volcar archivo log a log final [si se le ordena]");
  logsys ("msg", "Terminada la ejecución");
  logsys ("msg", "bye ;-)\n\n".&appRel."\n");
  # borrar archivo de bloqueo
  unlink $apppid or die "No puedo eliminar el archivo de bloqueo $apppid";
}

# flushChars transforma utf8 a iso-8859-1
sub flushChars {
  my ($tmpStr) = @_;
  $tmpStr =~ s/([\200-\377]+)/from_utf8({ -string => $1, -charset => 'ISO-8859-1'})/eg
    if $modUniCode;
  return $tmpStr;
}

#logsys Mini sistema de logs.
sub logsys {
  my ($mode, $text) = @_;
  my $tmpStr;
  if (open LOG, ">>$apppid") {
    if (($logMode eq "debug" && ($mode eq "debug" or $mode eq "warning")) ||
      ($logMode eq "warning" && ($mode eq "warning" or $mode eq "msg")) || 
	($logMode eq "msg" && $mode eq "msg")){
          my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime (time);
          $year += 1900; $mon ++;
          $tmpStr="[" . uc($mode) . "] " . sprintf("%02d",$mday) . "-" .
           sprintf("%02d",$mon) . "-$year " . sprintf("%02d",$hour) . ":" .
           sprintf("%02d",$min) . ":" . sprintf("%02d",$sec) . " $text\n";
          print LOG $tmpStr; print $tmpStr if $verbose;
    }
    close LOG;
  }
}

# ClickOnIcon se lanzará tras un click en el TrayIcon.
# Para diferenciar el botón del ratón que se pulsó.
sub ClickOnIcon {
  logsys ("debug", "sub: ClickOnIcon start");
  my ($widget, $event) = @_;
  return 1 if $event->button == 2;
  $menu->popup(undef, undef, undef, undef, $event->button,$event->time) if $event->button == 3;
  &extAppStatus($config->{'appOnClick'}) if $event->button == 1;
}

# extAppStatus lanzará una instancia de la aplicación si no existe
sub extAppStatus {
  logsys ("debug", "sub: extAppStatus start");
  my ($refApp) = @_;
  $config->{ $refApp . 'Pid'} = fork() 
    unless ($config->{ $refApp . 'Pid'}); # Crea un proceso hijo
  die "error al hacer fork: $!" if !defined($config->{$refApp . 'Pid'});
  if ( $config->{$refApp . 'Pid'} == 0) { # Si se trata del proceso hijo
    logsys ("msg", "Iniciando proceso hijo con [$refApp]");
    if ($config->{$refApp . 'Ok'}) {
      $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = 'DEFAULT';
      logsys ("warning", "Redirección de STDOUT para $refApp a $apppid");
      open STDOUT, ">>$apppid";
      exec ($config->{$refApp . 'App'}) || 
        die "ATENCIÓN: Falló la ejecución de $refApp";
    } else {exit -1}
  } else { # Circuito del proceso padre
    logsys ("msg", "Circuito del proceso padre [hijo=>$refApp pid:"
      . $config->{$refApp . 'Pid'} . "]");
    # Asigno una clave en la configuración con el valor de la pid con el padre
    $config->{$config->{$refApp . 'Pid'}} = $refApp 
      unless $config->{$config->{$refApp . 'Pid'}};
    # a ejecutar por el proceso padre
    # TODO: Enviar setFocus a la aplicación
    #
  }
}

# appRel nombre y versión del script
sub appRel {
  $0=~/.*\/(.*)$/;
  my $fileRun = $1;
  $fileRun = $0 unless $fileRun;
  $fileRun = "aMulet.pl [$1]" if $fileRun ne 'aMulet.pl';
  return $fileRun . " v.1.2 beta";
}

# popUpsAct des/activar apartados del menú
sub popUpsAct {
  $menu_aLinkCreator->set_sensitive($config->{'aLinkCreatorOk'});
  $menu_wxCas->set_sensitive($config->{'wxCasOk'});
  $menu_aMuleGui->set_sensitive($config->{'aMuleGuiOk'});
}

# pathsVerify comprobar la ruta de las aplicaciones
sub pathsVerify{
  foreach my $app (@appList) {
    $config->{$app . 'Ok'} = (-x $config->{$app . 'App'});
  }
}

# inicializar comprobaciones iniciales
sub inicializar {
  my $tmpStr;
  my $runDaemon;

  # Comprobar si ya estamos en marcha
  if (-f $apppid){
    my $appVerify=0;
    # Comprobar si realmente la aplicación está en marcha. 
    if (open(APPPID, $apppid)){
      my $line;
      while ($line = <APPPID>) {
        chop($line); last;
      }
      close(APPPID);
      $appVerify=&isRunProcess($line);
    }
    if ($appVerify){
      $tmpStr = "<b>" .appRel . "</b> ya está funcionando!\n\n";
      $SIG{TERM} = $SIG{QUIT} = $SIG{HUP} = $SIG{INT} = 'DEFAULT';
      &showMessage( 'error', flushChars($tmpStr), 'ok');
      exit -1;
    } else {unlink $apppid}
  }

  # Iniciar Bloqueo
  logsys ("msg", "Iniciando archivo de bloqueo");
  if (open LOG, ">$apppid"){print LOG "$$\n"}
  # Cargar la configuración
  logsys ("msg", "Cargando la configuración");
  &loadConfig;
  readSignatureConf;

  # Comprobar el estado del aMule como servicio
  if ($config->{'aMuledOn'}){
    logsys ("msg", "Iniciando la verificación de la existencia de amuled");
    if (open(PROCESOS,"ps -A |")){
      while (<PROCESOS>){
        $runDaemon = 1 if ($_ =~ /amuled/);
      }
      close PROCESOS;
    }
    if (!$runDaemon) { $runDaemon = 1;
      logsys ("msg", "No se detecta. Iniciando amuled ...");
      if (open(AMULED,"$binaMuled |")){
        my $aMuleRun; 
        while (<AMULED>){
          logsys ("msg", $_);
          logsys ("msg", "ATENCIÓN: Cambie la orden [$binaMuled] bajo su responsabilidad") if $_ =~ /binary requires/;
          undef $runDaemon if $_ =~ /binary requires/;
          $aMuleRun = 1 if $_ =~ /Raising current running instance/;
        }
        close AMULED;
        if ($aMuleRun){ 
          logsys ("msg", "*"x56 );
          logsys ("msg", "*"x5 ." Se ha detectado que el aMule está en marcha " ."*"x6 );
          logsys ("msg", "*"x56 );
          logsys ("msg", "aMulet.pl se diseñó pensando en las herramientas remotas" );
          logsys ("msg", "*"x56 );
          logsys ("msg", "* FUNCIONARÍA, pero no veo la necesidad de ello.  STOP!!" );
          logsys ("msg", "*"x56 );
          undef $runDaemon;
        };
        logsys ("msg", "El amuled ha sido puesto en marcha.") if $runDaemon;
      } else {
        undef $runDaemon;
        logsys ("msg", "No se ha podido ejecutar el amuled!");
        logsys ("msg", "Se puede indicar en la configuración que se salte este paso");
        $tmpStr = "<u><b>No se ha podido ejecutar aMuled</b></u>.\n\n";
        $tmpStr .= "¿Desactivo la opción de arrancar el amuled en la configuración?\n\n";
        $tmpStr .= "Desactive esta opción si el aMuled está residente en otra máquina de su red.";
        $tmpStr .= "\n\nNo he detectado el aMuleGui." unless $config->{'aMuleGuiOk'};
        $tmpStr .= "\nDeberá indicar donde está situado éste en la configuración" unless $config->{'aMuleGuiOk'};
        my $confirmacion= &showMessage( 'question', flushChars($tmpStr), 'yes-no');
        if ($confirmacion eq 'yes'){
          $config->{'aMuledOn'}=0;
          &saveConfig;
          $runDaemon = 1;
        };
      }
    } else {logsys ("msg", "El aMuled ha sido detectado :))");}
  } else {
    $runDaemon = 1;
    logsys ("msg", "ATENCIÓN No se detectará ni arrancará el aMuled");
    logsys ("msg", "         Se puede modificar esto en la configuración");
  }

  # Comprobar que la aplicación que arranca tras el click en el icono existe.
  # Esto no afectará al retorno de esta función.
  if ($runDaemon){
    logsys ("msg", "Verificando la aplicación que responde al click del icono.");
    if (!$config->{$config->{'appOnClick'} . 'Ok'}){
         $tmpStr = "Debe indicar en la configuración dónde residen el ejecutable\n";
         $tmpStr .= "de la aplicación <b>$config->{'appOnClick'}</b> o escoger otra\n";
         $tmpStr .= "aplicación que responda al click en el icono";
      &showMessage( 'warning', flushChars($tmpStr), 'ok');
      &opcionesGui;
    }
  } # TODO: Intentar mostrarlo como modal y esperar respuesta.
  return $runDaemon;
}

# visorTexto Crea una ventana Gtk en la que muestra el logfile del aMule
sub visorTexto {
  logsys ("debug", "sub: visorTexto start");
  my ($widget, $event) = @_;
  logsys ("debug", "Deshabilitando la llamada en el menú");
  $widget->set_sensitive(0);
  my $visorTextWin = Gtk2::Window->new("toplevel");
  my $vbox = Gtk2::VBox->new;
  my $scrolledwindow = Gtk2::ScrolledWindow->new;
  my $textBuffer = Gtk2::TextBuffer->new;
  my $ok = Gtk2::Button->new_from_stock("gtk-close");
  my $textView = Gtk2::TextView->new_with_buffer($textBuffer);
  my $LineaLog;

  $visorTextWin->set_title('Visor de Texto');
  $visorTextWin->set_position('center');
  $visorTextWin->set_default_size(640, 300);
  logsys ("debug", "Leyendo " . $config->{'aMuleDir'} . "/logfile");
  open (LOGFILE, $config->{'aMuleDir'} . "/logfile") || logsys ("warning", $config->{'aMuleDir'} . "/logfile: $!");
  while (<LOGFILE>) {$LineaLog .= $_;}
  close LOGFILE;
  $textView->set_editable(0);
  $textView->set_cursor_visible(0);
  $textBuffer->set_text($LineaLog);
  $scrolledwindow->set_policy('automatic', 'automatic');

  $scrolledwindow->add($textView);
  $vbox->add($scrolledwindow );
  $vbox->pack_end($ok, 0, 0, 3);
  $visorTextWin->add($vbox);

  $ok->signal_connect("clicked", sub { $visorTextWin->destroy; } );
  $visorTextWin->signal_connect('destroy' => sub { $widget->set_sensitive(1); });
  logsys ("msg", "Mostrando la ventana de logs");
  $visorTextWin->show_all;
}

# isRunProcess comprobará si un proceso está en ejecución
sub isRunProcess {
  my ($proceso) = @_;
  my $runDaemon = 0;
  if (open(PROCESOS,"ps -A |")){
    while (<PROCESOS>){$runDaemon = 1 if $_ =~ /^$proceso\s/;}
    close PROCESOS;
  }
  return $runDaemon;
}

sub infoOnLine {
  my $tmpStr = '';
  &onlineInfo if $config->{'signOn'};
  if ($config->{'signOn'} && $config->{'ConectStatus'}==1){
    $tmpStr = "\n\n<b>" . $config->{'nickName'} . "</b> con <b>aMule</b> [<i>";
    $tmpStr .= $config->{'aMuleVer'} . "</i>]\n";
    $tmpStr .= "está conectado al puerto <b>" . $config->{'portServ'};
    $tmpStr .= "</b> de <b>" . $config->{'nameServ'} . "</b>\ncon <b>ID ";
    $tmpStr .= $config->{'typeId'}eq"H"?"alta</b>":"baja</b>";
    $tmpStr .= " y la <b>red kad ";
    if ($config->{'kadStatus'}==2) {$tmpStr .= "bien conectada</b>\n";
      } elsif ($config->{'kadStatus'}==1) {$tmpStr .= "en firewalled</b>\n";
      } else {$tmpStr .= "desconectada</b>\n"};
    $tmpStr .= "lleva ";
    my $tmpStr2 = $config->{'TimeRun'} % 60 . " segundos";
    my $tiempo = sprintf("%d",$config->{'TimeRun'} / 60);
    $tmpStr2 = $tiempo % 60 . " minutos y $tmpStr2" if $tiempo;
    $tiempo = sprintf("%d",$tiempo / 60) if $tiempo;
    $tmpStr2 = $tiempo % 24 . " horas $tmpStr2" if $tiempo;
    $tiempo = sprintf("%d",$tiempo / 24) if $tiempo;
    $tmpStr2 = $tiempo . " dias $tmpStr2" if $tiempo;
    $tmpStr .= $tmpStr2 . " conectado\n";
    $tmpStr .= "y en este tiempo ha descargado: <b>";
    $tmpStr .= sprintf("%.2f",$config->{'SessionDown'}/1048576) . " Mb</b>\n";
    $tmpStr .= "con lo que tiene un total descagado de ";
    $tmpStr .= sprintf("%.2f",$config->{'totalDown'}/1048576) . " Mb\n";
    $tmpStr .= "Además comparte <b>" . $config->{'filesShared'} . "</b> archivos y en esta sesión\n";
    $tmpStr .= "se han subido <b>" .  sprintf("%.2f",$config->{'SessionUp'}/1048576);
    $tmpStr .= " Mb</b> obteniendo un\ntotal de " .  sprintf("%.2f",$config->{'totalUp'}/1048576);
    $tmpStr .= " Mb subidos a la Red,\nen cola tiene ahora mismo ";
    $tmpStr .= $config->{'colaCli'} . " clientes";
  }
  return flushChars($tmpStr);
}

# opcionesGui muestra la ventana de opciones
sub opcionesGui {
  my ($widget, $event) = @_;
  logsys ("debug", "Deshabilitando la llamada en el menú");
  $widget->set_sensitive(0) if defined $widget;
  my $guiOpciones = Gtk2::Window->new;
  my $vboxPrincipal = Gtk2::VBox->new;
  my $notebook = Gtk2::Notebook->new;
  my $select_aMuleGui = Gtk2::RadioButton->new('appOnClick', 'aMuleGui');
  my @grupo = $select_aMuleGui->get_group;
  my $select_alc = Gtk2::RadioButton->new_with_label(@grupo, 'aLinkCreator');
  my $select_wxcas = Gtk2::RadioButton->new_with_label(@grupo, 'wxCas');
  my $check_aMuled_on = new Gtk2::CheckButton->new(flushChars('Arrancar aMule como servicio al principio (si no lo está)'));
  my $vBoxTabRutas = Gtk2::VBox->new;
  my $vBoxTabAbout = Gtk2::VBox->new;
  my $hboxPrincipal = Gtk2::HBox->new;
  my $hBoxTabRutas1 = Gtk2::HBox->new;
  my $hBoxTabRutas2 = Gtk2::HBox->new;
  my $hBoxTabRutas3 = Gtk2::HBox->new(1);
  my $hseparator1 = Gtk2::HSeparator->new;
  my $hseparator2 = Gtk2::HSeparator->new;
  my $hseparator3 = Gtk2::HSeparator->new;
  my $labelTabRutas = Gtk2::Label->new('General');
  my $labelTabAbout = Gtk2::Label->new('About');
  my $labelTabRut1 = Gtk2::Label->new('Relacionado con el Servicio amuled');
  my $labelTabRut2 = Gtk2::Label->new('aMule Home Dir');
  my $labelTabRut3 = Gtk2::Label->new('Ruta a las aplicaciones');
  my $labelTabRut4 = Gtk2::Label->new('aMuleGui: ');
  my $labelTabRut5 = Gtk2::Label->new('wxCas: ');
  my $labelTabRut6 = Gtk2::Label->new('aLinkCreator: ');
  my $labelTabRut7 = Gtk2::Label->new(flushChars('Icono que se mostrará en la barra'));
  my $labelTabRut8 = Gtk2::Label->new('Icono:');
  my $labelTabRut9 = Gtk2::Label->new(flushChars('¿Qué aplicación arrancará tras hacer click en el icono?'));
  my $labelAbout1 = Gtk2::Label->new('aMulet.pl');
  my $labelAbout2 = Gtk2::Label->new('by AnimAlf - AnimAlf@engendro.sytes.net');
  my $labelAbout3 = Gtk2::Label->new('una idea de tx2z en http://forum.amule.org');
  my $labelAbout4 = Gtk2::Label->new('Distribuido bajo la licencia GPL');
  my $labelAbout5 = Gtk2::Label->new('http://www.gnu.org/copyleft/gpl.html');
  my $labelAbout6 = Gtk2::Label->new;
  my $edit_home_aMule = Gtk2::Entry->new;
  my $edit_aMuleGui_path = Gtk2::Entry->new;
  my $edit_wxcas_path = Gtk2::Entry->new;
  my $edit_alc_path = Gtk2::Entry->new;
  my $edit_icon_path = Gtk2::Entry->new;
  my $btnOk = Gtk2::Button->new_from_stock("gtk-apply");
  my $btnCancel = Gtk2::Button->new_from_stock("gtk-cancel");
  my $btnRutas1 = Gtk2::Button->new('...');
  my $btnRutas2 = Gtk2::Button->new('...');
  my $btnRutas3 = Gtk2::Button->new('...');
  my $btnRutas4 = Gtk2::Button->new('...');
  my $btnRutas5 = Gtk2::Button->new('Selecciona');
  my $tableTabRutas = Gtk2::Table->new(3, 3, 0 );

  # Posicionando en la ventana principal
  $guiOpciones->add($vboxPrincipal);
  # Posicionando en el notebook (contenedor de pestañas)
  $vboxPrincipal->add($notebook);	

  # primera pestaña
  $notebook->append_page($vBoxTabRutas, $labelTabRutas);

  $vBoxTabRutas->pack_start($labelTabRut1,     0, 0, 3);
  $vBoxTabRutas->pack_start($hBoxTabRutas1,    0, 0, 3);

  $hBoxTabRutas1->pack_start($labelTabRut2,    0, 0, 3);
  $hBoxTabRutas1->add($edit_home_aMule);
  $hBoxTabRutas1->pack_end($btnRutas1,         0, 0, 3);
  $vBoxTabRutas->pack_start($check_aMuled_on,  0, 0, 3);

  $vBoxTabRutas->pack_start($hseparator2,      0, 0, 3);
  $vBoxTabRutas->pack_start($labelTabRut3,     0, 0, 3);
  $vBoxTabRutas->pack_start($tableTabRutas,    0, 0, 3);
		    
  $tableTabRutas->attach($labelTabRut4,        0, 1, 0, 1, ['fill'], [], 0, 0 );
  $tableTabRutas->attach($edit_aMuleGui_path,  1, 2, 0, 1, ['expand', 'fill'], [], 0, 0 );
  $tableTabRutas->attach($btnRutas2,           2, 3, 0, 1, ['fill'], [], 0, 0 );
  $tableTabRutas->attach($labelTabRut5,        0, 1, 1, 2, ['fill'], [], 0, 0 );
  $tableTabRutas->attach($edit_wxcas_path,     1, 2, 1, 2, ['expand', 'fill'], [], 0, 0 );
  $tableTabRutas->attach($btnRutas3,           2, 3, 1, 2, ['fill'], [], 0, 0 );
  $tableTabRutas->attach($labelTabRut6,        0, 1, 2, 3, ['fill'], [], 0, 0 );
  $tableTabRutas->attach($edit_alc_path,       1, 2, 2, 3, ['expand', 'fill'], [], 0, 0 );
  $tableTabRutas->attach($btnRutas4,           2, 3, 2, 3, ['fill'], [], 0, 0 );

  $vBoxTabRutas->pack_start($hseparator1,      0, 0, 3);
  $vBoxTabRutas->pack_start($labelTabRut9,     0, 0, 3);
  $vBoxTabRutas->pack_start($hBoxTabRutas3,    0, 0, 3);
  $hBoxTabRutas3->pack_start($select_aMuleGui, 0, 0, 3);
  $hBoxTabRutas3->pack_start($select_alc,      0, 0, 3);
  $hBoxTabRutas3->pack_start($select_wxcas,    0, 0, 3);

  $vBoxTabRutas->pack_end($hBoxTabRutas2,      0, 0, 3);

  $hBoxTabRutas2->pack_start($labelTabRut8,    0, 0, 3);
  $hBoxTabRutas2->add($edit_icon_path);
  $hBoxTabRutas2->pack_end($btnRutas5,         0, 0, 3);

  $vBoxTabRutas->pack_end($labelTabRut7,       0, 0, 3);
  $vBoxTabRutas->pack_end($hseparator3,        0, 0, 3);

  # Segunda pestaña
  $notebook->append_page($vBoxTabAbout, $labelTabAbout);
  $vBoxTabAbout->pack_start($labelAbout1,      0, 0, 3);
  $vBoxTabAbout->pack_start($labelAbout2,      0, 0, 3);
  $vBoxTabAbout->pack_start($labelAbout3,      0, 0, 3);
  $vBoxTabAbout->pack_start($labelAbout4,      0, 0, 3);
  $vBoxTabAbout->pack_start($labelAbout5,      0, 0, 3);
  $vBoxTabAbout->pack_start($labelAbout6,      0, 0, 3);

  # Posicionando en la parte inferior de la ventana principal
  $vboxPrincipal->pack_end($hboxPrincipal,     0, 0, 3);
  $hboxPrincipal->pack_start($btnOk,           0, 0, 3);
  $hboxPrincipal->pack_end($btnCancel,         0, 0, 3);

  $labelAbout1->set_markup('<span foreground="DarkRed" size="x-large"><b>' . appRel . ' </b></span>');
  #TODO: Intentar conseguir una UrlLabel, fácil con gnome-open (pero y si no existe gnome?)
  #http://forum.amule.org/thread.php?threadid=9751
  $labelAbout3->set_markup('<span>Una idea de <b>tx2z</b> en http://forum.amule.org</span>');
  #Añado un mini resumen de la información disponible en amulesig.dat
  $labelAbout6->set_markup(infoOnLine);
  if ($config->{'signOn'}){
    Glib::Timeout->add($config->{'signOnTime'}*1000, sub {
      $labelAbout6->set_markup(infoOnLine);
      $tray->send_message($config->{'signOnTime'}*1000, "[AnimAlf dice] No entiendo");});
  }

  # Propiedades de la ventana principal
  $guiOpciones->set_title('Opciones aMulet.pl');
  $guiOpciones->set_position('center');
  #TODO: Intentar que actuase como una ventana modal, como el showMessage
  #      y el punto de la llamada esperase la respuesta ok-cancel
  logsys ("msg", "Mostrando la ventana de opciones");
  logsys ("warning", "*"x36) unless $widget;
  logsys ("warning", "TODO: Que sea modal tipo showMessage") unless $widget;
  logsys ("warning", "*"x36) unless $widget;

  # Propiedades del notebook
  $notebook->set_tab_pos('top');
  $notebook->set_show_tabs  (1);
  $notebook->set_show_border(1);
  $notebook->set_scrollable (0);
  $notebook->set_tab_hborder(2);
  $notebook->set_tab_vborder(2);

  # alineando de algunas etiquetas
  $labelTabRut4->set_alignment(1,0.5);
  $labelTabRut5->set_alignment(1,0.5);
  $labelTabRut6->set_alignment(1,0.5);

  # Asignando la configuración.
  $edit_home_aMule->set_text($config->{'aMuleDir'});
  $edit_aMuleGui_path->set_text($config->{'aMuleGuiApp'});
  $edit_wxcas_path->set_text($config->{'wxCasApp'});
  $edit_alc_path->set_text($config->{'aLinkCreatorApp'});
  $edit_icon_path->set_text($config->{'iconPath'});

  $check_aMuled_on->set_active($config->{'aMuledOn'});

  $select_wxcas->set_active($config->{'appOnClick'} eq 'wxCas');
  $select_alc->set_active($config->{'appOnClick'} eq 'aLinkCreator');
  $select_aMuleGui->set_active($config->{'appOnClick'} eq 'aMuleGui');

  $select_wxcas->set_sensitive($config->{'wxCasOk'});
  $select_alc->set_sensitive($config->{'aLinkCreatorOk'});
  $select_aMuleGui->set_sensitive($config->{'aMuleGuiOk'});

  # Lo mostramos todo
  $guiOpciones->show_all;
  # La asignación de señales
  $btnOk->signal_connect("clicked", sub { 
      my ($tmpStr, $tmpStr1);

      $tmpStr1 = 'wxCas' if $select_wxcas->get_active;
      $tmpStr1 = 'aLinkCreator' if $select_alc->get_active;
      $tmpStr1 = 'aMuleGui' if $select_aMuleGui->get_active;

      $tmpStr .= $edit_home_aMule->get_text . ": No es un directorio\n"
        if !(-d $edit_home_aMule->get_text);
      $tmpStr .= $edit_aMuleGui_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
        if !(-x $edit_aMuleGui_path->get_text);
      $tmpStr .= $edit_wxcas_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
        if !(-x $edit_wxcas_path->get_text);
      $tmpStr .= $edit_alc_path->get_text . ": <b>No es un ejecutable</b> (o no existe)\n"
        if !(-x $edit_alc_path->get_text);
      $tmpStr .= $edit_icon_path->get_text . ": <b>No parece que exista</b>\n"
        if !(-e $edit_icon_path->get_text);

      if (defined $tmpStr){
          $tmpStr = "<b>Se ha detectado lo siguiente:</b>\n\n$tmpStr\n\n" . flushChars("¿Desea continuar de todos modos?");
          my $confirmacion= &showMessage( 'question', $tmpStr, 'yes-no');
          return if ($confirmacion eq 'no');
      }

      $config->{'appOnClick'} = 'wxCas' if $select_wxcas->get_active;
      $config->{'appOnClick'} = 'aLinkCreator' if $select_alc->get_active;
      $config->{'appOnClick'} = 'aMuleGui' if $select_aMuleGui->get_active;

      $config->{'aMuleDir'} = $edit_home_aMule->get_text;
      $config->{'aMuleGuiApp'} = $edit_aMuleGui_path->get_text;
      $config->{'wxCasApp'} = $edit_wxcas_path->get_text;
      $config->{'aLinkCreatorApp'} = $edit_alc_path->get_text;
      &showMessage( 'info', flushChars("El icono cambiará cuando reinicie la aplicación."), 'ok')
        if $config->{'iconPath'} ne $edit_icon_path->get_text;
      $config->{'iconPath'} = $edit_icon_path->get_text;

      $config->{'aMuledOn'} = $check_aMuled_on->get_active;

      &saveConfig; pathsVerify; popUpsAct; readSignatureConf;
      $guiOpciones->destroy; 
    } 
  );
  $btnCancel->signal_connect("clicked", sub { $guiOpciones->destroy; } );
  $guiOpciones->signal_connect('destroy' => sub {$widget->set_sensitive(1) if defined $widget;});
  $btnRutas1->signal_connect('clicked' => sub { dialogOpen($edit_home_aMule,
    'Seleccione el directorio home de aMule', 'select-folder'); });
  $btnRutas2->signal_connect('clicked' => sub { dialogOpen($edit_aMuleGui_path,
    flushChars('Seleccione la aplicación aMuleGui'), 'open'); });
  $btnRutas3->signal_connect('clicked' => sub { dialogOpen($edit_wxcas_path,
    flushChars('Seleccione la aplicación wxCas'), 'open'); });
  $btnRutas4->signal_connect('clicked' => sub { dialogOpen($edit_alc_path,
    flushChars('Seleccione la aplicación aLinCreator'), 'open'); });
  $btnRutas5->signal_connect('clicked' => sub { dialogOpen($edit_icon_path,
    flushChars('Seleccione el icono que utilizará aMulet'), 'open'); });
}

# loadConfig carga la configuración o la crea si no existe
sub loadConfig {
  if ( open( CONFIGFILE, "$ENV{HOME}/.aMule/amulet.rc" ) ) {
    while (<CONFIGFILE>) {
      if ( $_ =~ /(\w+)\s=\s(.+)/ ) { $config->{$1} = $2 }
    }
    close CONFIGFILE;
  } else {
    # es la primera vez.
    mkdir "$ENV{HOME}/.aMule" unless (-d "$ENV{HOME}/.aMule");
    $config = { # Por defecto
      'aMuleDir'        => "$ENV{'HOME'}/.aMule",
      'aMuleGuiApp'     => 'amulegui',
      'wxCasApp'        => 'wxcas',
      'aLinkCreatorApp' => 'alc',
      'iconPath'        => '/usr/share/pixmaps/amule.xpm',
      'appOnClick'      => 'aMuleGui',
      'aMuledOn'        => 1
    };
    foreach my $app (@appList) {
      $config->{$app . 'App'} = which($config->{$app . 'App'});
    }
    pathsVerify;
    &saveConfig;
  }
  pathsVerify;
}

# saveConfig guarda la configuración en un archivo
sub saveConfig {
  my @keyList = ('aMuleDir', 'aMuleGuiApp', 'wxCasApp',
    'aLinkCreatorApp', 'iconPath', 'appOnClick', 'aMuledOn');
  if ( open( CONFIGFILE, '>', "$ENV{HOME}/.aMule/amulet.rc" ) ) {
    foreach my $key (@keyList) {
      print CONFIGFILE "$key = $config->{$key}\n" if $config->{$key} ne '';
    }
    close CONFIGFILE;
  }else {logSys("warning", "No se puede escribir en el archivo de configuración!");}
};

# showMessage Muestra una ventana con un mensaje, y espera respuesta.
sub showMessage {
  #$icon info warning error question
  #$type none ok close cancel yes-no ok-cancel
  my ($icon,$text,$type) = @_;
  my $dialog = Gtk2::MessageDialog->new_with_markup (undef,
     [qw/modal destroy-with-parent/], $icon, $type, sprintf "$text");
  my $retval = $dialog->run;
  $dialog->destroy;
  return $retval;
}

# which Devuelve el path completo a la aplicación.
sub which {
  my $appFile = shift;
  foreach (split(':', $ENV{PATH})) {
    my $tmpStr = "$_/$appFile";
    if (-x $tmpStr) {
      $appFile = $tmpStr;
      last;
    }
  }
  return $appFile;
}

# dialogOpen ventana de selección de archivos o directorios y escribe
# el resultado en en el entry que debe ser el primer parámetro.
sub dialogOpen {
  # $widget debe ser un entry
  # $type open save select-folder create-folder
  my($widget,$caption,$type) =@_;
  my $chooserDialog =  Gtk2::FileChooserDialog->new ($caption, undef, $type,
    'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok');
  my $filename = $chooserDialog->get_filename if 'ok' eq $chooserDialog->run;
  $widget->set_text ($filename) if defined $filename;
  $chooserDialog->destroy;
  return;
}