package PACScripts;
###################################################################
# This file is part of PAC( Perl Auto Connector)
#
# Copyright (C) 2010-2016 David Torrejon Vaquerizas
#
# This program 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.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
###################################################################
$|++;
###################################################################
# Import Modules
# Standard
use strict;
use warnings;
use FindBin qw ( $RealBin $Bin $Script );
use Encode qw ( encode );
use File::Copy;
use File::Basename;
use Storable qw ( dclone nstore_fd );
eval { require Gtk2::SourceView2; };
my $SOURCEVIEW = ! $@;
my $PERL = `which perl 2>&1`;
# GTK2
use Gtk2 '-init';
# PAC modules
use PACUtils;
# END: Import Modules
###################################################################
###################################################################
# Define GLOBAL CLASS variables
my $APPNAME = $PACUtils::APPNAME;
my $APPVERSION = $PACUtils::APPVERSION;
my $APPICON = $RealBin . '/res/pac64x64.png';
my $BANNER = $RealBin . '/res/pac_banner_scripts.png';
my $CFG_DIR = $ENV{'HOME'} . '/.config/pac';
my $SCRIPTS_DIR = $CFG_DIR . '/scripts';
# PAC Help
my $PAC_SCRIPTS_HELP = '#PAC Scripts are simple Perl programs, so:
use strict; # Get serious :)
use warnings; # Really serious! ;)
# Inherited Variables, containing both variables and subroutine references
# It is *MANDATORY* to declare them (as "our" or "local")
our %SHARED; # Hash to pass data from SESSION to CONNECTION
our %COMMON; # Common PAC utilities (substitutions, etc...) to be used *ANYWHERE* in script
our %PAC; # GUI/Terminals manipulation to be used under *SESSION* subroutine
our %TERMINAL; # Command/Prompt manipulation to be used under *CONNECTION* subroutine
# *ATTENTION* Anything written OUT of SESSION and CONNECTION, will be executed *at least*:
# once for SESSION management and once for every started/selected CONNECTION.
# As a rule of thumb, you should not write big chunks of code outside SESSION or CONNECTION
# ( For all you Perl Gurus: in fact, this script is "eval\'ed" twice )
####################################################
# %SHARED hash definition
####################################################
# %SHARED hash is a simple container to pass data from SESSION
# to CONNECTION subroutine, which do not share any other variable.
# So, if want to retrieve any data at sessions start/selection, use
# %SHARED as your data storage
%{ $list } = $SHARED{_list_};
# This special pre-populated variable contains the list of connections
# started/selected at SESSION subroutine.
# It will only be populated once SESSION has been executed, so,
# it will *only* be availabe on CONNECTION subroutine
####################################################
# %COMMON hash definition
####################################################
%{ $cfg } = $COMMON{cfg}( [0|1] );
# Retrieve a copy (0 -> default) or a reference (1) to *whole* PAC\'s configuration
# *ATTENTION* Retrieving a REFERENCE (1) to PAC\'s config may lead to PAC misconfiguration!!
# NOT RECOMMENDED!! USE AT YOUR VERY OWN RISK!!!!!!
$txt = $COMMON{subst}( <text> );
# Substitute given text with PAC internal variables (<ASK:desc|opt1|opt2|...|optN>, <GV:2>, ...)
# Returns a string
$txt = $COMMON{del_esc}( <text> )
# Remove ESCape sequences (mainly used for removing ANSI colouring output)
# Returns a string
$COMMON{cfg_sanity}( \%cfg );
# Perform a sanity check for given %cfg (reference!), which may be empty, in which case it will
# construct an empty PAC cfg skeleton over *referenced* %cfg variable
# Returns a string
####################################################
# %PAC hash definition
####################################################
( $uuid, $tmp_uuid ) = $PAC{start_uuid}( <UUID>[, <cluster> ] );
# Start session with given "UUID" (optionally in cluster "cluster")
# Returns an array with UUID and temporal UUID of started connection
( $uuid, $tmp_uuid ) = $PAC{start_uuid_manual}( <UUID> );
# Start session with given "UUID" with manual login flag set
# Returns an array with UUID and temporal UUID of started connection
( $uuid, $tmp_uuid ) = $PAC{start}( <name>[, <cluster> ] );
# Start session named "name" (optionally in cluster "cluster")
# Returns an array with UUID and temporal UUID of started connection
( $uuid, $tmp_uuid ) = $PAC{start_manual}( <name> );
# Start session named "name" with manual login flag set
# Returns an array with UUID and temporal UUID of started connection
( $uuid, $tmp_uuid ) = $PAC{select}( <name> );
# Select an existing session named "name"
# Returns an array with UUID and temporal UUID of selected connection
%{ $connections_list } = $PAC{select}( "*ALL*" );
# Select ALL open sessions
# Return a hash ref { tmp_uuid => uuid } with the list of selected terminals
$PAC{stop}( <$tmp_uuid> );
# Stop and close session identified by $tmp_uuid
$PAC{msg}( [ <message text>[, 0|1] ] );
# Shows a message window with given "message_text" or
# hide any existing message window if no "message_text" is provided, and
# an OK (1) or not (0 -> default) button
$PAC{split}( <$tmp_uuid1>, <$tmp_uuid2>[, 0|1] );
# Split horizontally (0 -> default) or vertically (1) given connections
$PAC{unsplit}( <$tmp_uuid> );
# Unsplit given connection
$PAC{to_window}( <$tmp_uuid> );
# Untab given connection
$PAC{to_tab}( <$tmp_uuid> );
# Retab given connection
$file = $PAC{cfg_save}();
# Save current PAC cfg to standard PAC config file.
# Returns the name of such file
$file = $PAC{cfg_export}( [file] );
# Save current PAC cfg to given file (or ask for a file through a GUI if no file provided)
# in YAML format, suitable to be imported in any PAC instance
# Returns the name of saved file
####################################################
# %TERMINAL hash definition
####################################################
$filehandle = $TERMINAL{log}( [<log file>] );
# Function to set/remove output log file
# Returns the filehandle for given log file
$input = $TERMINAL{ask}( <text>[, 0|1] );
# Ask user for input, showing (1 -> default) or not (0) user input
# Returns a string with user provided text, or undef
$expect_obj = $TERMINAL{exp}();
# Returns the Perl Expect object for this connection (see "perldoc Expect")
# *WARNING*, Low level! Not recommended!
$TERMINAL{name};
# Constant with the Name of the connection
$TERMINAL{uuid};
# Constant with the UUID of the connection
$TERMINAL{tmp_uuid};
# Constant with the Temporal UUID of current executing connection
$TERMINAL{error};
# Variable with the latest error from the "expect" function
$TERMINAL{msg}( <text> );
# Function to print an ANSI coloured message on the Terminal
$prompt = $TERMINAL{get_prompt}( [0|1] );
# Function to GUESS this connection prompt and remove (1 -> default) or not (0) any ESCape sequence (tipically for colouring)
# Returns a Quoted string "\Qprompt_sting\E" valid for RegExp matching or $TERMINAL{expect}( $prompt ) API function
# Prompt is guessed by simply sending an INTRO to the connection, and checking the difference between current and previous line,
# so, it may not work for dynamic or menu-driven prompts
$TERMINAL{send}( <string[\n]> );
# Function to SEND some string (input) to the connection (\n sends a <INTRO>)
$out = $TERMINAL{send_get}( <string>[, 0] );
# Function to SEND some string (input) to the connection (\n is auto-inserted unless ", 0" is appended as parameter) and RETRIEVE
# its output in a best-effort manner (may not work on some kind of terminals), or UNDEF otherwise.
# First line of $out (which contains the command executed) is removed, and the
# $COMMON{del_esq} function is applied, in order to remove every "ugly" character
# This function has a timeout to retrieve the output defined by any "default" PAC config
# or per connection defined on "time out command" variable (40 seconds default)
$res = $TERMINAL{expect}( <string>[, <seconds> ] );
# Function to EXPECT for some STRING (Perl RegExp) from the connection for given SECONDS, or 1 second if undefined
# Returns, TRUE (1) on STRING found or FALSE (0) otherwise
# Sets $TERMINAL{error} variable with the error that produced the EXPECT not matching given STRING.
# If STRINGS matches, then other two variables are set:
# $TERMINAL{out1} -> contains the output of the latest string sent UP TO the matched STRING (the one you usually want!)
# Its first line will probably be the command executed
# $TERMINAL{out2} -> contains the output of the latest string sent FROM the matched STRING onwards
# It is *CRUCIAL* that "string" matches correctly what you want or you will end up with
# wrong data collected in $TERMINAL{out1} and $TERMINAL{out2}, and an unresponsive terminal (until TIMEOUT reaches)
# All $TERMINAL{error|out1|out2} are resetted every time a SEND command is executed
sub SESSION
{
# Subroutine SESSION -> return 1 (true) to identify a good ending, or 0 (false) to notice a controlled bad ending
# Here you START (or SELECT alreday started) connections where you want to run this script (the CONNECTION subroutine)
# This definition is mandatory if you want to execute this script from PAC\'s "Scripts" GUI, but optional
# if you want to execute it from a connection\'s right-click context menu
# This routine is executed *ONCE*
# If you want to ask for some variable and then reuse the output on every connection, you should
# write something like:
# my $SHARE{cmd} = $COMMON{subst}( "<ASK:Command to send|ls -laF|df -h|uptime|date>" );
# From now on, $SHARE{cmd} will contain the output from the user input, and will be also available
# on the CONNECTION subroutine
#
# Here you can use: %COMMON, %PAC and %SHARED ( NOT pre-populated at all )
return 1;
}
sub CONNECTION
{
# Subroutine CONNECTION -> return 1 (true) to identify a good ending, or 0 (false) to notice a controlled bad ending
# Here you decide what to do on every previously selected connection( what to send, what to expect for, ...)
# This subroutine will be executed on *every started/selected* connection from the SESSION definition above,
# so you may want to make some kind of check before sending/expecting anything.
# %SHARED hash is available under CONNECTION
#
# Here you can use: %COMMON, %TERMINAL and %SHARED ( pre-populated with $SHARED{_list_} )
#
# It is always a *GOOD* idea to start the scripts by EXPECting a regular command prompt IF the connection was
# started by us (the script, with $PAC{start}). Other else ($PAC{select}), you should not need to make any initial expect.
# $TERMINAL{expect}( "\[your_user_name@your_hostname ~\]" );
return 1;
}
return 1;';
# PAC New Script Body
my $PAC_SCRIPTS_NEW = '# PAC Script
use strict; # Get serious :)
use warnings; # Really serious! ;)
our ( %COMMON, %PAC, %TERMINAL, %SHARED );
sub SESSION
{
# Start/select connections
return 1;
}
sub CONNECTION
{
# Do things on every started/selected connection
# my $prompt = $TERMINAL{get_prompt}();
return 1;
}
return 1;';
# END: Define GLOBAL CLASS variables
###################################################################
###################################################################
# START: Define PUBLIC CLASS methods
sub new {
my $class = shift;
my $self = {};
$self -> {_WINDOWSCRIPTS} = undef;
$self -> {_SCRIPTS} = {};
$self -> {_UNDO} = ();
$self -> {_SELECTED} = '';
$self -> {_PREVENT_UPDATES} = 0;
$self -> {_SYNTAX_CHANGED} = 0;
$self -> {_TIMER_CHECK} = undef;
# Build the GUI
_initGUI( $self ) or return 0;
# Setup callbacks
_setupCallbacks( $self );
# Load the scripts list
_reloadDir( $self );
bless( $self, $class );
return $self;
}
# DESTRUCTOR
sub DESTROY {
my $self = shift;
undef $self;
return 1;
}
# Start GUI and launch connection
sub show {
my $self = shift;
$$self{_WINDOWSCRIPTS}{main} -> show_all;
$$self{_WINDOWSCRIPTS}{main} -> present;
$self -> _reloadDir unless $$self{_WINDOWSCRIPTS}{main} -> window -> is_visible;
$self -> _updateGUI;
# Setup a timer to check syntax of selected script
( ! defined $$self{_TIMER_CHECK} && $PERL ) and $$self{_TIMER_CHECK} = Glib::Timeout -> add_seconds( 1, sub {
return 1 unless $$self{_WINDOWSCRIPTS}{main} -> get_property( 'has-toplevel-focus' ) && $$self{_SYNTAX_CHANGED};
my $selection = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model;
my @sel = $selection -> get_selected_rows;
if ( scalar @sel != 1 ) {
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_markup( '' );
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_tooltip_text( '' );
return 1;
}
my $file = $model -> get_value( $model -> get_iter( $sel[0] ), 0 );
my $name = $model -> get_value( $model -> get_iter( $sel[0] ), 1 );
my $tmpfile = $CFG_DIR . '/tmp/' . $name . '.check';
$self -> _saveFile( $sel[0], $tmpfile );
my @lines = `perl -cw $tmpfile 2>&1`;
my $err = $?;
my $result = pop( @lines ); chomp $result;
$result =~ s/^\Q$tmpfile\E\s+(.+)$/$1/g;
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_markup( '<span foreground="' . ( $err ? 'red' : '#00D206' ) . '">' . "<b>" . __( "$name: $result" ) . "</b>" . '</span>' );
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_tooltip_text( ' * ' . localtime( time ) . " :\n" . ( $err ? join( '', @lines ) : 'syntax ok' ) );
$$self{_SYNTAX_CHANGED} = 0;
return 1;
} );
return 1;
}
sub scriptsList { return wantarray ? keys %{ $_[0]{_SCRIPTS} } : $_[0]{_SCRIPTS}; }
# END: Define PUBLIC CLASS methods
###################################################################
###################################################################
# START: Define PRIVATE CLASS functions
sub _initGUI {
my $self = shift;
# Create the 'windowFind' dialog window,
$$self{_WINDOWSCRIPTS}{main} = Gtk2::Window -> new;
# and setup some dialog properties.
$$self{_WINDOWSCRIPTS}{main} -> set_title( "$APPNAME : Scripts" );
$$self{_WINDOWSCRIPTS}{main} -> set_position( 'center' );
$$self{_WINDOWSCRIPTS}{main} -> set_icon_from_file( $APPICON );
$$self{_WINDOWSCRIPTS}{main} -> set_default_size( 800, 500 );
$$self{_WINDOWSCRIPTS}{main} -> set_resizable( 1 );
$$self{_WINDOWSCRIPTS}{main} -> maximize;
$$self{_WINDOWSCRIPTS}{gui}{vbox} = Gtk2::VBox -> new( 0, 0 );
$$self{_WINDOWSCRIPTS}{main} -> add( $$self{_WINDOWSCRIPTS}{gui}{vbox} );
my $hboxaux = Gtk2::HBox -> new( 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{vbox} -> pack_start( $hboxaux, 0, 1, 0 );
$hboxaux -> pack_start( Gtk2::Image -> new_from_file( $BANNER ), 0, 1, 0 );
# Create an hpane
$$self{_WINDOWSCRIPTS}{gui}{hpane} = Gtk2::HPaned -> new;
$$self{_WINDOWSCRIPTS}{gui}{vbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{hpane}, 1, 1, 0 );
# Terminals list
$$self{_WINDOWSCRIPTS}{gui}{scroll2} = Gtk2::ScrolledWindow -> new;
$$self{_WINDOWSCRIPTS}{gui}{hpane} -> pack1( $$self{_WINDOWSCRIPTS}{gui}{scroll2}, 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scroll2} -> set_policy( 'automatic', 'automatic' );
$$self{_WINDOWSCRIPTS}{treeScripts} = Gtk2::Ex::Simple::List -> new_from_treeview (
Gtk2::TreeView -> new,
'FILE' => 'hidden',
'SCRIPT' => 'text'
);
$$self{_WINDOWSCRIPTS}{gui}{scroll2} -> add( $$self{_WINDOWSCRIPTS}{treeScripts} );
$$self{_WINDOWSCRIPTS}{treeScripts} -> set_tooltip_text( 'PAC Scripts. You may Drag \'n Drop Perl (.pl) files here to import them.' );
$$self{_WINDOWSCRIPTS}{treeScripts} -> set_headers_visible( 1 );
$$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection -> set_mode( 'GTK_SELECTION_MULTIPLE' );
# Create a notebook
$$self{_WINDOWSCRIPTS}{nb} = Gtk2::Notebook -> new;
$$self{_WINDOWSCRIPTS}{gui}{hpane} -> pack2( $$self{_WINDOWSCRIPTS}{nb}, 1, 0 );
# PAC Script Editor
my $tablbl = Gtk2::HBox -> new( 0, 0 );
$tablbl -> pack_start( Gtk2::Label -> new( ' Script Editor ' ), 1, 1, 0 );
$tablbl -> pack_start( Gtk2::Image -> new_from_stock( 'pac-script', 'menu' ), 0, 1, 0 );
$tablbl -> show_all;
$$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc} = Gtk2::HPaned -> new;
$$self{_WINDOWSCRIPTS}{nb} -> append_page( $$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc}, $tablbl );
$$self{_WINDOWSCRIPTS}{nb} -> set_tab_reorderable( $$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc}, 0 );
$$self{_WINDOWSCRIPTS}{nb} -> set_tab_detachable( $$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc}, 0 );
$$self{_WINDOWSCRIPTS}{gui}{vboxedit} = Gtk2::VBox -> new( 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc} -> pack1( $$self{_WINDOWSCRIPTS}{gui}{vboxedit}, 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scrollMultiText} = Gtk2::ScrolledWindow -> new;
$$self{_WINDOWSCRIPTS}{gui}{vboxedit} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{scrollMultiText}, 1, 1, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scrollMultiText} -> set_policy( 'automatic', 'automatic' );
$$self{_WINDOWSCRIPTS}{gui}{scrollMultiText} -> set_border_width( 5 );
if ( $SOURCEVIEW ) {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} = Gtk2::SourceView2::Buffer -> new( undef );
$$self{_WINDOWSCRIPTS}{gui}{textScript} = Gtk2::SourceView2::View -> new_with_buffer( $$self{_WINDOWSCRIPTS}{multiTextBuffer} );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_smart_home_end( 'before' );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_show_line_numbers( 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_tab_width( 4 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_indent_on_tab( 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_auto_indent( 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set( 'auto-indent', 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} ->set_highlight_current_line( 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> modify_font( Pango::FontDescription -> from_string( 'monospace' ) );
} else {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} = Gtk2::TextBuffer -> new;
$$self{_WINDOWSCRIPTS}{gui}{textScript} = Gtk2::TextView -> new_with_buffer( $$self{_WINDOWSCRIPTS}{multiTextBuffer} );
}
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_border_width( 5 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_size_request( 320, 200 );
$$self{_WINDOWSCRIPTS}{gui}{scrollMultiText} -> add( $$self{_WINDOWSCRIPTS}{gui}{textScript} );
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_wrap_mode( 'GTK_WRAP_WORD' );
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_sensitive( 1 );
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set( 'can_focus', 1 );
if ( ! $SOURCEVIEW ) {
$$self{_WINDOWSCRIPTS}{gui}{statusLib} = Gtk2::Statusbar -> new;
$$self{_WINDOWSCRIPTS}{gui}{vboxedit} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{statusLib}, 0, 1, 0 );
$$self{_WINDOWSCRIPTS}{gui}{statusLib} -> push( 1, "Install 'libgtk2-sourceview2-perl' to enjoy Syntax Highlight" );
}
$$self{_WINDOWSCRIPTS}{gui}{status} = Gtk2::Label -> new;
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_justify( 'center' );
$$self{_WINDOWSCRIPTS}{gui}{vboxedit} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{status}, 0, 1, 0 );
# API functions list
$$self{_WINDOWSCRIPTS}{gui}{scrollfunc} = Gtk2::ScrolledWindow -> new;
$$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc} -> pack2( $$self{_WINDOWSCRIPTS}{gui}{scrollfunc}, 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scrollfunc} -> set_policy( 'automatic', 'automatic' );
$$self{_WINDOWSCRIPTS}{treeFuncs} = Gtk2::Ex::Simple::List -> new_from_treeview (
Gtk2::TreeView -> new,
'API NAME' => 'hidden',
'API CALL' => 'markup',
);
$$self{_WINDOWSCRIPTS}{gui}{scrollfunc} -> add( $$self{_WINDOWSCRIPTS}{treeFuncs} );
$$self{_WINDOWSCRIPTS}{treeFuncs} -> set_headers_visible( 1 );
$$self{_WINDOWSCRIPTS}{treeFuncs} -> get_selection -> set_mode( 'GTK_SELECTION_SINGLE' );
push( @{ $$self{_WINDOWSCRIPTS}{treeFuncs}{data} },
['$SHARED{_list_}' , '<span foreground="#606060">' . __( '$SHARED{_list_}' ) . '</span>'],
['$COMMON{cfg}( [0|1] )' , '<span foreground="#007710">' . __( '$COMMON{cfg}( [0|1] )' ) . '</span>'],
['$COMMON{subst}( <text> )' , '<span foreground="#007710">' . __( '$COMMON{subst}( <text> )' ) . '</span>'],
['$COMMON{del_esc}( <text> )' , '<span foreground="#007710">' . __( '$COMMON{del_esc}( <text> )' ) . '</span>'],
['$COMMON{cfg_sanity}( \%cfg )' , '<span foreground="#007710">' . __( '$COMMON{cfg_sanity}( \%cfg )' ) . '</span>'],
['$PAC{start_uuid}( <UUID>[, <cluster> ] )' , '<span foreground="blue">' . __( '$PAC{start_uuid}( <UUID>[, <cluster> ] )' ) . '</span>'],
['$PAC{start_uuid_manual}( <UUID> )' , '<span foreground="blue">' . __( '$PAC{start_uuid_manual}( <UUID> )' ) . '</span>'],
['$PAC{start}( <name>[, <cluster> ] )' , '<span foreground="blue">' . __( '$PAC{start}( <name>[, <cluster> ] )' ) . '</span>'],
['$PAC{start_manual}( <name> )' , '<span foreground="blue">' . __( '$PAC{start_manual}( <name> )' ) . '</span>'],
['$PAC{select}( <name> )' , '<span foreground="blue">' . __( '$PAC{select}( <name> )' ) . '</span>'],
['$PAC{select}( "*ALL*" )' , '<span foreground="blue">' . __( '$PAC{select}( "*ALL*" )' ) . '</span>'],
['$PAC{stop}( <$tmp_uuid> )' , '<span foreground="blue">' . __( '$PAC{stop}( <$tmp_uuid> )' ) . '</span>'],
['$PAC{msg}( <text>[, 0|1] )' , '<span foreground="blue">' . __( '$PAC{msg}( <text>[, 0|1] )' ) . '</span>'],
['$PAC{split}( <$tmp_uuid1>, <$tmp_uuid2>[, 0|1] )' , '<span foreground="blue">' . __( '$PAC{split}( <$tmp_uuid1>, <$tmp_uuid2>[, 0|1] )' ) . '</span>'],
['$PAC{unsplit}( <$tmp_uuid> )' , '<span foreground="blue">' . __( '$PAC{unsplit}( <$tmp_uuid> )' ) . '</span>'],
['$PAC{to_window}( <$tmp_uuid> )' , '<span foreground="blue">' . __( '$PAC{to_window}( <$tmp_uuid> )' ) . '</span>'],
['$PAC{to_tab}( <$tmp_uuid> )' , '<span foreground="blue">' . __( '$PAC{to_tab}( <$tmp_uuid> )' ) . '</span>'],
['$PAC{cfg_save}()' , '<span foreground="blue">' . __( '$PAC{cfg_save}()' ) . '</span>'],
['$PAC{cfg_export}( <filename> )' , '<span foreground="blue">' . __( '$PAC{cfg_export}( <filename> )' ) . '</span>'],
['$TERMINAL{log}( [<log file>] )' , '<span foreground="red">' . __( '$TERMINAL{log}( [<log file>] )' ) . '</span>'],
['$TERMINAL{ask}( <text>[, 0|1] )' , '<span foreground="red">' . __( '$TERMINAL{ask}( <text>[, 0|1] )' ) . '</span>'],
['$TERMINAL{exp}()' , '<span foreground="red">' . __( '$TERMINAL{exp}()' ) . '</span>'],
['$TERMINAL{name}' , '<span foreground="red">' . __( '$TERMINAL{name}' ) . '</span>'],
['$TERMINAL{uuid}' , '<span foreground="red">' . __( '$TERMINAL{uuid}' ) . '</span>'],
['$TERMINAL{tmp_uuid}' , '<span foreground="red">' . __( '$TERMINAL{tmp_uuid}' ) . '</span>'],
['$TERMINAL{error}' , '<span foreground="red">' . __( '$TERMINAL{error}' ) . '</span>'],
['$TERMINAL{msg}( <text> )' , '<span foreground="red">' . __( '$TERMINAL{msg}( <text> )' ) . '</span>'],
['$TERMINAL{get_prompt}( [0|1] )' , '<span foreground="red">' . __( '$TERMINAL{get_prompt}( [0|1] )' ) . '</span>'],
['$TERMINAL{send}( <string[\n]> )' , '<span foreground="red">' . __( '$TERMINAL{send}( <string[\n]> )' ) . '</span>'],
['$TERMINAL{send_get}( <string>[, 0] )' , '<span foreground="red">' . __( '$TERMINAL{send_get}( <string>[, 0] )' ) . '</span>'],
['$TERMINAL{expect}( <string>[, <seconds> ] )' , '<span foreground="red">' . __( '$TERMINAL{expect}( <string>[, <seconds> ] )' ) . '</span>']
);
# PAC Script Help
my $tablbl2 = Gtk2::HBox -> new( 0, 0 );
$tablbl2 -> pack_start( Gtk2::Label -> new( ' PAC Script Help ' ), 1, 1, 0 );
$tablbl2 -> pack_start( Gtk2::Image -> new_from_stock( 'gtk-help', 'menu' ), 0, 1, 0 );
$tablbl2 -> show_all;
$$self{_WINDOWSCRIPTS}{gui}{vboxhelp} = Gtk2::VBox -> new( 0, 0 );
$$self{_WINDOWSCRIPTS}{nb} -> append_page( $$self{_WINDOWSCRIPTS}{gui}{vboxhelp}, $tablbl2 );
$$self{_WINDOWSCRIPTS}{nb} -> set_tab_reorderable( $$self{_WINDOWSCRIPTS}{gui}{vboxhelp}, 0 );
$$self{_WINDOWSCRIPTS}{nb} -> set_tab_detachable( $$self{_WINDOWSCRIPTS}{gui}{vboxhelp}, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scrollHelp} = Gtk2::ScrolledWindow -> new;
$$self{_WINDOWSCRIPTS}{gui}{vboxhelp} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{scrollHelp}, 1, 1, 0 );
$$self{_WINDOWSCRIPTS}{gui}{scrollHelp} -> set_policy( 'automatic', 'automatic' );
$$self{_WINDOWSCRIPTS}{gui}{scrollHelp} -> set_border_width( 5 );
if ( $SOURCEVIEW ) {
$$self{_WINDOWSCRIPTS}{helpBuffer} = Gtk2::SourceView2::Buffer -> new( undef );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} = Gtk2::SourceView2::View -> new_with_buffer( $$self{_WINDOWSCRIPTS}{helpBuffer} );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} ->set_show_line_numbers( 0 );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} ->set_tab_width( 4 );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} ->set_indent_on_tab( 1 );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} ->set_highlight_current_line( 1 );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} -> modify_font( Pango::FontDescription -> from_string( 'monospace' ) );
$$self{_WINDOWSCRIPTS}{helpBuffer} -> begin_not_undoable_action;
$$self{_WINDOWSCRIPTS}{helpBuffer} -> set_text( encode( 'iso-8859-1', $PAC_SCRIPTS_HELP ) );
$$self{_WINDOWSCRIPTS}{helpBuffer} -> end_not_undoable_action;
$$self{_WINDOWSCRIPTS}{helpBuffer} -> place_cursor( $$self{_WINDOWSCRIPTS}{helpBuffer} -> get_start_iter );
my $manager = Gtk2::SourceView2::LanguageManager -> get_default;
my $language = $manager -> get_language( 'perl' );
$$self{_WINDOWSCRIPTS}{helpBuffer} -> set_language( $language );
} else {
$$self{_WINDOWSCRIPTS}{helpBuffer} = Gtk2::TextBuffer -> new;
$$self{_WINDOWSCRIPTS}{gui}{helpScript} = Gtk2::TextView -> new_with_buffer( $$self{_WINDOWSCRIPTS}{helpBuffer} );
$$self{_WINDOWSCRIPTS}{helpBuffer} -> set_text( encode( 'iso-8859-1', $PAC_SCRIPTS_HELP ) );
$$self{_WINDOWSCRIPTS}{helpBuffer} -> place_cursor( $$self{_WINDOWSCRIPTS}{helpBuffer} -> get_start_iter );
}
$$self{_WINDOWSCRIPTS}{gui}{helpScript} -> set_border_width( 5 );
$$self{_WINDOWSCRIPTS}{gui}{scrollHelp} -> add( $$self{_WINDOWSCRIPTS}{gui}{helpScript} );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} -> set_wrap_mode( 'GTK_WRAP_WORD' );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} -> set_editable( 0 );
$$self{_WINDOWSCRIPTS}{gui}{helpScript} -> set( 'can_focus', 1 );
# Set notebook properties
$$self{_WINDOWSCRIPTS}{nb} -> set_scrollable( 1 );
$$self{_WINDOWSCRIPTS}{nb} -> set_tab_pos( 'top' );
$$self{_WINDOWSCRIPTS}{nb} -> set( 'homogeneous', 1 );
$$self{_WINDOWSCRIPTS}{nb} -> set_current_page( 0 );
# Action buttons
$$self{_WINDOWSCRIPTS}{gui}{btnbox} = Gtk2::HBox -> new( 0, 0 );
$$self{_WINDOWSCRIPTS}{gui}{vbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnbox}, 0, 1, 0 );
# Put a 'execute' button
$$self{_WINDOWSCRIPTS}{gui}{btnexec} = Gtk2::Button -> new( 'E_xecute' );
$$self{_WINDOWSCRIPTS}{gui}{btnexec} -> set_image( Gtk2::Image -> new_from_stock( 'gtk-media-play', 'button' ) );
$$self{_WINDOWSCRIPTS}{gui}{btnexec} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnexec}, 1, 1, 0 );
# Put a 'add' button
$$self{_WINDOWSCRIPTS}{gui}{btnadd} = Gtk2::Button -> new_from_stock( 'gtk-new' );
$$self{_WINDOWSCRIPTS}{gui}{btnadd} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnadd}, 1, 1, 0 );
# Put a 'import' button
$$self{_WINDOWSCRIPTS}{gui}{btnimport} = Gtk2::Button -> new( 'Import...' );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> set_image( Gtk2::Image -> new_from_stock( 'gtk-open', 'button' ) );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnimport}, 1, 1, 0 );
# Put a 'remove' button
$$self{_WINDOWSCRIPTS}{gui}{btnremove} = Gtk2::Button -> new_from_stock( 'gtk-delete' );
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnremove}, 1, 1, 0 );
# Put a 'reload' button
$$self{_WINDOWSCRIPTS}{gui}{btnreload} = Gtk2::Button -> new_from_stock( 'gtk-refresh' );
$$self{_WINDOWSCRIPTS}{gui}{btnreload} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnreload}, 1, 1, 0 );
# Put a 'save' button
$$self{_WINDOWSCRIPTS}{gui}{btnsave} = Gtk2::Button -> new_from_stock( 'gtk-save' );
$$self{_WINDOWSCRIPTS}{gui}{btnsave} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnsave}, 1, 1, 0 );
# Put a 'close' button
$$self{_WINDOWSCRIPTS}{gui}{btnclose} = Gtk2::Button -> new_from_stock( 'gtk-close' );
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> set( 'can_focus', 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnbox} -> pack_start( $$self{_WINDOWSCRIPTS}{gui}{btnclose}, 1, 1, 0 );
$$self{_WINDOWSCRIPTS}{gui}{hpane} -> set_position( 100 );
$$self{_WINDOWSCRIPTS}{gui}{hpanededitfunc} -> set_position( 600 );
return 1;
}
sub _setupCallbacks {
my $self = shift;
# Asign a callback to populate this textview with its own context menu
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> signal_connect( 'button_press_event' => sub {
my ( $widget, $event ) = @_;
return 0 unless $event -> button eq 3;
my @menu_items;
# COMMON
my @comm_menu_items;
push( @comm_menu_items,
{
label => 'Del ESCape sequences ( $COMMON{del_esc}( <text> ) )',
tooltip => "Remove ESCape sequences (mainly used for removing ANSI colouring output)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{del_esc}( <text> ) " ); }
} );
push( @comm_menu_items,
{
label => 'Var substitution ( $COMMON{subst}( <text> ) )',
tooltip => "Substitute given text with PAC internal variables (<ASK:a>, <GV:2>, ...)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{subst}( <text> ) " ); }
} );
push( @comm_menu_items,
{
label => 'Get PAC Config ( $COMMON{cfg}( [0|1] ) )',
tooltip => "Retrieve a copy (0->default) or a reference (1) to *whole* PAC's configuration
*ATTENTION* Retrieving a REFERENCE (1) to PAC's config may lead to PAC misconfiguration!!
NOT RECOMMENDED!! USE AT YOUR VERY OWN RISK!!!!!!",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{cfg}( [0|1] ) " ); }
} );
push( @comm_menu_items,
{
label => 'Run Config sanity check( $COMMON{sanity}( \%cfg ) )',
tooltip => "Perform a sanity check for given %cfg, which may be empty, in which case it will",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{sanity}( \%sanity] ) " ); }
} );
push( @menu_items,
{
label => 'COMMON methods',
stockicon => 'pac-script',
submenu => \@comm_menu_items
} );
# SESSION
my @session_menu_items;
push( @session_menu_items,
{
label => 'Start session with "UUID"( $PAC{start_uuid}( <UUID>[, <cluster> ] ) )',
tooltip => "Start session with 'UUID' (optionally in cluster 'cluster')",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{start_uuid}( <UUID>[, <cluster> ] )" ); }
} );
push( @session_menu_items,
{
label => 'Start session "UUID" with manual login( ""PAC{start_uuid_manual}( <UUID> ) )',
tooltip => "Start session with 'UUID' with manual login (you must write your login code under CONNECTION)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{start_uuid_manual}( <UUID> )" ); }
} );
push( @session_menu_items,
{
label => 'Start session named "name"( $PAC{start}( <name>[, <cluster> ] ) )',
tooltip => "Start session named 'name' (optionally in cluster 'cluster')",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{start}( <name>[, <cluster> ] )" ); }
} );
push( @session_menu_items,
{
label => 'Start session "name" with manual login( ""PAC{start_manual}( <name> ) )',
tooltip => "Start session named 'name' with manual login (you must write your login code under CONNECTION)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{start_manual}( <name> )" ); }
} );
push( @session_menu_items,
{
label => 'Select existing session "name"( $PAC{select}( <name> ) )',
tooltip => "Select an existing session named 'name', returning ( uuid, \$tmp_uuid ) (array)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{select}( <name> )" ); }
} );
push( @session_menu_items,
{
label => "Select ALL open sessions( \$PAC{select}( '*ALL*' ) )",
tooltip => "Select ALL open sessions, return (hash ref) the whole list { tmp_uuid => uuid }",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{select}( '*ALL*' )" ); }
} );
push( @session_menu_items,
{
label => 'Stop session $tmp_uuid( $PAC{stop}( <$tmp_uuid> ) )',
tooltip => 'Stop and close session identified by $tmp_uuid',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{stop}( <\$tmp_uuid> )" ); }
} );
push( @session_menu_items,
{
label => 'Show/hide a message( $PAC{msg}( [ <message text>[, 0|1] ] ) )',
tooltip => "Show/hide a message window with an OK (1) or not (0 - default) button",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{msg}( [ <message text>[, 0|1] ] )" ); }
} );
push( @session_menu_items,
{
label => 'Split connections( $PAC{split}( <$tmp_uuid1>, <$tmp_uuid2>[, 0|1] ) )',
tooltip => "Split horizontally (0 -> default) or vertically (1) given connections",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{split}( <\$tmp_uuid1>, <\$tmp_uuid2>[, 0|1] )" ); }
} );
push( @session_menu_items,
{
label => 'Unsplit( $PAC{unsplit}( <$tmp_uuid> ) )',
tooltip => "Unsplit given connection",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{unsplit}( <\$tmp_uuid> )" ); }
} );
push( @session_menu_items,
{
label => 'Untab( $PAC{to_window}( <$tmp_uuid> ) )',
tooltip => "Untab given connection",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{to_window}( <\$tmp_uuid> )" ); }
} );
push( @session_menu_items,
{
label => 'Retab( $PAC{to_tab}( <$tmp_uuid> ) )',
tooltip => "Retab given connection",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{to_tab}( <\$tmp_uuid> )" ); }
} );
push( @session_menu_items,
{
label => 'Save current PAC CFG( $PAC{cfg_save}() )',
tooltip => "Save current PAC cfg to standard PAC config file",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{cfg_save}()" ); }
} );
push( @session_menu_items,
{
label => 'Export PAC CFG to file( $PAC{cfg_export}( [file] ) )',
tooltip => "Save current PAC cfg to given file (or ask for a file through a GUI if no file provided)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$PAC{cfg_export}( [file] )" ); }
} );
push( @menu_items,
{
label => 'PAC methods',
stockicon => 'pac-tab',
submenu => \@session_menu_items
} );
# CONNECTION
my @connection_menu_items;
push( @connection_menu_items,
{
label => 'Set output log file( $TERMINAL{log}( [<log file>] ) )',
tooltip => 'Function to set/remove output log file. Returns the log filehandle applied',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{log}( [<log file>] )" ); }
} );
push( @connection_menu_items,
{
label => 'User input( $TERMINAL{ask}( <text>[, 0|1] ) )',
tooltip => 'Ask user for input, showing (1 -> default) or not (0) user input',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{ask}( <text>[, 0|1] )" ); }
} );
push( @connection_menu_items,
{
label => 'Get EXPecxt object( $TERMINAL{exp}() )',
tooltip => 'Returns the Perl Expect object for this connection (*WARNING*, dangerous function! Not recommended!)',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{exp}()" ); }
} );
push( @connection_menu_items,
{
label => 'Connection name( $TERMINAL{name} )',
tooltip => 'Variable with the Name of the connection',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{name}" ); }
} );
push( @connection_menu_items,
{
label => 'Connection UUID( $TERMINAL{uuid} )',
tooltip => 'Variable with the UUID of the connection',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{uuid}" ); }
} );
push( @connection_menu_items,
{
label => 'Connection TMP_UUID( $TERMINAL{tmp_uuid} )',
tooltip => 'Variable with the Temporal UUID of current executing connection',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{tmp_uuid}" ); }
} );
push( @connection_menu_items,
{
label => 'Latest error( $TERMINAL{error} )',
tooltip => 'Variable with the latest error from the "expect" function',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{error}" ); }
} );
push( @connection_menu_items,
{
label => 'Print MSG on terminal( $TERMINAL{msg}( <message> ) )',
tooltip => 'Function to print a message on the Terminal',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{msg}( <message> )" ); }
} );
push( @connection_menu_items,
{
label => 'Guess connection prompt( $TERMINAL{get_prompt}( [0|1] ) )',
tooltip => 'Function retrieve connection prompt and remove (1) or not (0 -> default) any ESCape sequence. Returns a Quoted string \Qprompt_sting\E valid for RegExp matching',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{get_prompt}( [0|1] )" ); }
} );
push( @connection_menu_items,
{
label => 'Send input to terminal( $TERMINAL{send}( <string[\n]> ) )',
tooltip => 'Function to SEND some string (input) to the connection (\n sends a <INTRO>)',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{send}( <string[\n]> )" ); }
} );
push( @connection_menu_items,
{
label => 'Send input to terminal( $TERMINAL{send_get}( <string>[, 0] ) )',
tooltip => 'Function to SEND some string (input) to the connection (\n is automatically appended)',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{send_get}( <string>[, 0] )" ); }
} );
push( @connection_menu_items,
{
label => 'Send input to terminal( $TERMINAL{expect}( <string>[, <seconds> ] ) )',
tooltip => 'Function to EXPECT for some STRING from the connection for given SECONDS, or indefinetly if undefined
Returns, TRUE (1) on STRING found or FALSE (0) otherwise, setting the $TERMINAL{error} variable with
the error that produced the EXPECT not matching given STRING.
If STRINGS matches, then other two variables are set:
$TERMINAL{out1} -> contains the output of the latest string SENT UP TO the matched STRING
$TERMINAL{out2} -> contains the output of the latest string SENT FROM the matched STRING onwards
All $CONNECTIONS{error|out1|out2} are resetted every time a SEND command is executed',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$TERMINAL{expect}( <string>[, <seconds> ] )" ); }
} );
push( @menu_items,
{
label => 'TERMINAL methods',
stockicon => 'pac-shell',
submenu => \@connection_menu_items
} );
push( @menu_items, { separator => 1 } );
# Populate with global defined variables
my @global_variables_menu;
foreach my $var ( sort { $a cmp $b } keys %{ $PACMain::FUNCS{_MAIN}{_CFG}{'defaults'}{'global variables'} } )
{
my $val = $PACMain::FUNCS{_MAIN}{_CFG}{'defaults'}{'global variables'}{$var}{'value'};
push( @global_variables_menu,
{
label => "<GV:$var> ($val)",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{subst}( '<GV:$var>' )" ); }
} );
}
push( @menu_items,
{
label => 'Global variables...',
sensitive => scalar( @global_variables_menu ),
submenu => \@global_variables_menu
} );
# Populate with environment variables
my @environment_menu;
foreach my $key ( sort { $a cmp $b } keys %ENV )
{
my $value = $ENV{$key};
push( @environment_menu,
{
label => "<ENV:$key>",
tooltip => "$key=$value",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{subst}( '<ENV:$key>' )" ); }
} );
}
push( @menu_items,
{
label => 'Environment variables...',
submenu => \@environment_menu
} );
# Put an option to ask user for variable substitution
push( @menu_items,
{
label => 'Runtime substitution (<ASK:change_by_number>)',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{subst}( '<ASK:change_by_number>' )" ); }
} );
# Populate with <ASK:*|> special string
push( @menu_items,
{
label => 'Interactive user choose from list',
tooltip => 'User will be prompted to choose a value form a user defined list separated with "|" (pipes)',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$COMMON{subst}( '<ASK:descriptive_line|opt1|opt2|...|optN>' )" ); }
} );
# Populate with <CMD:*> special string
push( @menu_items,
{
label => 'Use a command output as value',
tooltip => 'The given command line will be locally executed, and its output (both STDOUT and STDERR) will be used to replace this value',
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$SUBST( '<CMD:command_to_launch>' )" ); }
} );
# Populate with <KPX_(title|username|url):*> special string
if ( $PACMain::FUNCS{_MAIN}{_CFG}{'defaults'}{'keepass'}{'use_keepass'} )
{
my ( @titles, @usernames, @urls );
foreach my $hash ( $PACMain::FUNCS{_KEEPASS} -> find )
{
push( @titles,
{
label => "<KPX_title:$$hash{title}>",
tooltip => "The given command line will be substituted real-timefor it's corresponding KeePassX value",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$SUBST( <KPX_title:$$hash{title}> )" ); }
} );
push( @usernames,
{
label => "<KPX_username:$$hash{username}>",
tooltip => "The given command line will be substituted real-timefor it's corresponding KeePassX value",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$SUBST( <KPX_username:$$hash{username}> )" ); }
} );
push( @urls,
{
label => "<KPX_url:$$hash{url}>",
tooltip => "The given command line will be substituted real-timefor it's corresponding KeePassX value",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "\$SUBST( <KPX_url:$$hash{url}> )" ); }
} );
}
push( @menu_items,
{
label => 'KeePassX',
stockicon => 'pac-keepass',
submenu =>
[
{
label => 'KeePassX title values',
submenu => \@titles
}, {
label => 'KeePassX username values',
submenu => \@usernames
}, {
label => 'KeePassX URL values',
submenu => \@urls
}, {
label => "KeePass Extended Query",
tooltip => "This allows you to select the value to be returned, based on another value's match againt a Perl Regular Expression",
code => sub { $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( "<KPXRE_GET_(title|username|password|url)_WHERE_(title|username|password|url)==Your_RegExp_here==>" ); }
}
]
} );
}
_wPopUpMenu( \@menu_items, $event );
return 1;
} );
# Capture <ctrl><z> for undo
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> signal_connect( 'key_press_event' => sub {
my ( $widget, $event ) = @_;
my $keyval = Gtk2::Gdk -> keyval_name( $event -> keyval );
my $unicode = Gtk2::Gdk -> keyval_to_unicode( $event -> keyval); # 0 if not a character
my $state = $event -> get_state;
my $ctrl = $state * ['control-mask'];
my $shift = $state * ['shift-mask'];
my $alt = $state * ['mod1-mask'];
# Ctr-Enter
if ( $ctrl && ( ( $keyval eq 'Return' ) || ( $keyval eq 'KP_Enter' ) ) ) {
$$self{_WINDOWSCRIPTS}{gui}{btnAll} -> clicked;
}
# Ctrl-y
elsif ( $ctrl && ( lc $keyval eq 'y' ) && $SOURCEVIEW ) {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> redo if $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> can_redo;
}
# Ctrl-z
elsif ( $ctrl && ( lc $keyval eq 'z' ) && ! $SOURCEVIEW && ( scalar @{ $$self{_UNDO} } ) ) {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_text( encode( 'iso-8859-1', pop( @{ $$self{_UNDO} } ) ) );
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_modified( scalar( @{ $$self{_UNDO} } ) );
}
else { return 0; }
return 1;
} );
# Capture text changes on multitext widget
! $SOURCEVIEW && $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> signal_connect( 'begin_user_action' => sub {
push( @{ $$self{_UNDO} }, $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_property( 'text' ) );
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_modified( 1 );
return 0;
} );
# Set a flag to check for syntax when text has changed
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> signal_connect( 'changed' => sub { $$self{_SYNTAX_CHANGED} = 1; $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_modified( 1 ); } );
$$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection -> signal_connect( 'changed' => sub { $self -> _updateGUI; return 1; } );
$$self{_WINDOWSCRIPTS}{treeFuncs} -> signal_connect( 'row_activated' => sub {
return 1 unless ( scalar( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection -> get_selected_rows ) && $$self{_WINDOWSCRIPTS}{gui}{textScript} -> get_sensitive );
my $selection = $$self{_WINDOWSCRIPTS}{treeFuncs} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeFuncs} -> get_model;
my @sel = $selection -> get_selected_rows;
return 1 unless scalar @sel == 1;
my $apicall = $model -> get_value( $model -> get_iter( $sel[0] ), 0 );
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> insert_at_cursor( $apicall );
return 1;
} );
# Setup Drag 'n Drop
$$self{_WINDOWSCRIPTS}{treeScripts} -> drag_dest_set( 'all', ['copy', 'move'], {'target' => "STRING", 'flags' => [], 'info' => 0} );
$$self{_WINDOWSCRIPTS}{treeScripts} -> signal_connect( 'drag_data_received' => sub {
my ( $me, $context, $x, $y, $data, $info, $time ) = @_;
return 0 if ( ( $data -> length < 0 ) || ( $data -> type -> name ne 'STRING' ) );
foreach my $line ( split( /\R/, $data -> data ) ) {
$line =~ s/\R//go;
next unless $line =~ /file:\/\/(.+)/go;
my $file = $1;
my ( $filename, $directories, $suffix ) = fileparse ( $file, '.pl' );
if ( $suffix ne '.pl' ) {
_wMessage( $$self{_WINDOWSCRIPTS}{main}, "File '$file' does not end with a '.pl' Perl extension", 1 );
next;
}
next if -f "$SCRIPTS_DIR/$filename.pl" && ! _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "File '$filename.pl' already exists. Overwrite it?" );
$self -> _import( $file );
}
$self -> _reloadDir;
$self -> _updateGUI;
return 1;
} );
$$self{_WINDOWSCRIPTS}{main} -> signal_connect( 'key_press_event' => sub {
my ( $widget, $event ) = @_;
my $keyval = '' . ( $event -> keyval );
return 0 unless $keyval == 65307;
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> activate;
return 1;
} );
$$self{_WINDOWSCRIPTS}{main} -> signal_connect( 'delete_event' => sub { $$self{_WINDOWSCRIPTS}{gui}{btnclose} -> activate; return 1; } );
$$self{_WINDOWSCRIPTS}{treeScripts} -> signal_connect( 'row_activated' => sub { $$self{_WINDOWSCRIPTS}{gui}{btnexec} -> activate; } );
$$self{_WINDOWSCRIPTS}{gui}{btnexec} -> signal_connect( 'clicked' => sub {
my $selection = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model;
my @sel = $selection -> get_selected_rows;
return 1 unless scalar @sel == 1;
my $name = $model -> get_value( $model -> get_iter( $sel[0] ), 1 );
$self -> _execScript( $name );
return 1;
} );
$$self{_WINDOWSCRIPTS}{gui}{btnadd} -> signal_connect( 'clicked' => sub {
my $name = _wEnterValue( $$self{_WINDOWSCRIPTS}{main}, "<b>Creating new Script</b>" , "Enter a name for the new PAC Script" );
return 1 if ( ( ! defined $name ) || ( $name =~ /^\s*$/go ) );
return 1 if -f "$SCRIPTS_DIR/$name.pl" && ! _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "File '$name.pl' already exists. Overwrite it?" );
if ( ! open( F, ">$SCRIPTS_DIR/$name.pl" ) ) {
_wMessage( $$self{_WINDOWSCRIPTS}{main}, "ERROR: Can not open file '$name.pl' for writting ($!)" );
return 1;
}
print F $PAC_SCRIPTS_NEW;
close F;
$self -> _reloadDir;
$self -> _selectFile( $name . '.pl' );
return 1;
} );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> signal_connect( 'clicked' => sub {
my $choose = Gtk2::FileChooserDialog -> new(
"$APPNAME (v.$APPVERSION) Choose a text file to Import",
$$self{_WINDOWSCRIPTS}{main},
'GTK_FILE_CHOOSER_ACTION_OPEN',
'Open' , 'GTK_RESPONSE_ACCEPT',
'Cancel' , 'GTK_RESPONSE_CANCEL',
);
$choose -> set_current_folder( $ENV{'HOME'} // '/tmp' );
$choose -> set_select_multiple( 1 );
my $filter = Gtk2::FileFilter -> new;
$filter -> set_name( 'Perl files (.pl)' );
$filter -> add_pattern( '*.pl' );
$choose -> add_filter( $filter );
my $out = $choose -> run;
my @files = $choose -> get_filenames;
$choose -> destroy;
return 1 unless $out eq 'accept' && scalar( @files );
foreach my $file ( @files ) { $self -> _import( $file ); }
$self -> _reloadDir;
$self -> _updateGUI;
return 1;
} );
$$self{_WINDOWSCRIPTS}{gui}{btnreload} -> signal_connect( 'clicked' => sub { $self -> _reloadDir; return 1; } );
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> signal_connect( 'clicked' => sub {
if ( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified ) {
my $name = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $$self{_SELECTED} ), 0 );
my $out = _wYesNoCancel( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before closing PAC Script window?" );
$out eq 'yes' and $self -> _saveFile( $$self{_SELECTED} );
$out eq 'cancel' and return 1;
$out eq 'no' and $self -> _loadFile( $$self{_SELECTED} );
}
$$self{_WINDOWSCRIPTS}{main} -> hide;
Glib::Source -> remove( $$self{_TIMER_CHECK} ) if defined $$self{_TIMER_CHECK};
$$self{_TIMER_CHECK} = undef;
return 1;
} );
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> signal_connect( 'clicked' => sub {
my $selection = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model;
# Check for changes before removing
if ( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified ) {
my $name = $model -> get_value( $model -> get_iter( $$self{_SELECTED} ), 0 );
$self -> _saveFile( $$self{_SELECTED} ) if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before loading another script?" );
}
my @sel = $selection -> get_selected_rows;
return 1 unless scalar( @sel );
return 1 unless _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "Are you sure you want to remove ". ( scalar( @sel ) ) . " PAC Scripts?" );
# Delete selected files
foreach my $path ( @sel ) {
my ( $file, $name ) = $model -> get_value( $model -> get_iter( $path ) );
unlink( $file );
}
$$self{_SELECTED} = '';
$self -> _reloadDir;
$self -> _updateGUI;
return 1;
} );
$$self{_WINDOWSCRIPTS}{gui}{btnsave} -> signal_connect( 'clicked' => sub {
my ( $path ) = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection -> get_selected_rows;
defined $path or return 1;
my $file = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $path ), 0 );
$self -> _saveFile( $path );
return 1;
} );
return 1;
}
sub _selectFile {
my $self = shift;
my $sel = shift;
my $selection = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model;
# Locate every connection under $uuid
$model -> foreach( sub {
my ( $store, $path, $iter, $tmp ) = @_;
my ( $file, $name ) = $store -> get_value( $iter );
return 0 unless $name eq $sel;
$selection -> select_path( $path );
return 1;
} );
return 1;
}
sub _saveFile {
my $self = shift;
my $path = shift;
my $file = shift // $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $path ), 0 );
if ( ! open F, ">$file" ) {
_wMessage( $$self{_WINDOWSCRIPTS}{main}, "ERROR: Can not open for writting '$file' ($!)" );
return 0;
}
print F $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_text( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_start_iter, $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_end_iter, 0 );
close F;
return 1;
}
sub _loadFile {
my $self = shift;
my $path = shift;
my $selection = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection;
my $model = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model;
my $file = $model -> get_value( $model -> get_iter( $path ), 0 );
my $name = $model -> get_value( $model -> get_iter( $path ), 1 );
# Loading a file should not be undoable.
my $content = '';
if ( ! open F, $file ) {
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> clicked if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "ERROR: Can not read file '$file' ($!)\nDelete it?" );
return 0;
}
while ( my $line = <F> ) { $content .= $line; }
close F;
if ( $SOURCEVIEW ) {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> begin_not_undoable_action;
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_text( encode( 'iso-8859-1', $content ) );
if ( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_text( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_start_iter, $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_end_iter, 0 ) eq '' ) {
_wMessage( $$self{_WINDOWSCRIPTS}{main}, "WARNING: file '$file' is " . ( -z $file ? 'empty' : 'not a valid text file!' ) );
}
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> end_not_undoable_action;
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> place_cursor( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_start_iter );
my $manager = Gtk2::SourceView2::LanguageManager -> get_default;
my $language = $manager -> guess_language( $file );
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_language( $language );
} else {
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_text( encode( 'iso-8859-1', $content ) );
}
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_modified( 0 );
$$self{_SYNTAX_CHANGED} = 1;
$$self{_WINDOWSCRIPTS}{nb} -> set_current_page( 0 );
return 1;
}
sub _import {
my $self = shift;
my $file = shift // '';
return 0 unless -f $file;
my ( $filename, $directories, $suffix ) = fileparse ( $file, '.pl' );
return 0 unless $suffix eq '.pl';
if ( defined $$self{_SCRIPTS}{$filename} ) { _wMessage( $$self{_WINDOWSCRIPTS}{main}, "ERROR: It already exists a file named '$file' in '$SCRIPTS_DIR'" ); }
elsif ( ! copy( $file, $SCRIPTS_DIR ) ) { _wMessage( $$self{_WINDOWSCRIPTS}{main}, "ERROR: Could not import file '$file' into '$SCRIPTS_DIR' ($!)" ); }
else { return 1; }
return 0;
}
sub _reloadDir {
my $self = shift;
# Check for changes before reloading
if ( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified && $$self{_SELECTED} ) {
my $name = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $$self{_SELECTED} ), 0 );
$self -> _saveFile( $$self{_SELECTED} ) if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before loading another script?" );
}
my $dh;
my @files;
# Read file from the directory
if ( ! opendir( $dh, $SCRIPTS_DIR ) ) {
_wMessage( $$self{_WINDOWSCRIPTS}{main}, "ERROR: Could not open directory '$SCRIPTS_DIR' for reading ($!)" );
return 0;
}
delete $$self{_SCRIPTS};
while ( my $f = readdir( $dh ) ) {
my ( $filename, $directories, $suffix ) = fileparse ( $SCRIPTS_DIR . '/' . $f, '.pl' );
next unless $suffix eq '.pl';
$$self{_SCRIPTS}{$f} = $SCRIPTS_DIR . '/' . $f;
}
closedir $dh;
# Delete and re-populate the scripts list
$$self{_SELECTED} = '';
$$self{_PREVENT_UPDATES} = 1;
@{ $$self{_WINDOWSCRIPTS}{treeScripts}{data} } = ();
foreach my $name ( sort { lc( $a ) cmp lc( $b ) } keys %{ $$self{_SCRIPTS} } ) {
my $file = $SCRIPTS_DIR . '/' . $name;
push( @{ $$self{_WINDOWSCRIPTS}{treeScripts}{data} }, [ $file, $name ] );
}
$$self{_PREVENT_UPDATES} = 0;
return 1;
}
sub _updateGUI {
my $self = shift;
return 1 if $$self{_PREVENT_UPDATES};
my @sel = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_selection -> get_selected_rows;
my $default = "* PAC Scripts *\n
- Take a look at PAC example scripts to see how they work\n
- Now, 'Import' or create your own brand 'New' scripts, and 'Execute' them!\n
- You may also 'Import' scripts by Drag and Drop Perl '.pl' files to the scripts list on the left side of this window\n
- Remember: only one script can be executed at a time, and while executing, connection will be user-unresponsive (non-interactive)
Feel free to send me any PAC Script you may find useful to the community!";
if ( ! scalar( @sel ) ) {
if ( $$self{_SELECTED} && $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified ) {
my $name = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $$self{_SELECTED} ), 0 );
$self -> _saveFile( $$self{_SELECTED} ) if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before loading another script?" );
}
$SOURCEVIEW and $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> begin_not_undoable_action;
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_text( $default );
$SOURCEVIEW and $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> end_not_undoable_action;
$$self{_SELECTED} = '';
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_sensitive( 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnadd} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnreload} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> set_sensitive( 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnsave} -> set_sensitive( 0 );
$$self{_SYNTAX_CHANGED} = 0;
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_markup( '' );
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_tooltip_text( '' );
} elsif ( scalar( @sel ) == 1 ) {
return 1 if ( $$self{_SELECTED} && ( $sel[0] -> to_string eq $$self{_SELECTED} -> to_string ) );
my $file = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $sel[0] ), 0 );
my $name = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $sel[0] ), 0 );
if ( $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified && ( $sel[0] ne $$self{_SELECTED} ) ) {
$self -> _saveFile( $$self{_SELECTED} ) if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before loading another script?" );
}
$$self{_SELECTED} = $sel[0];
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnadd} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnreload} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnsave} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$self -> _loadFile( $sel[0] );
$$self{_SYNTAX_CHANGED} = 1;
} elsif ( scalar( @sel ) > 1 ) {
if ( $$self{_SELECTED} && $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> get_modified ) {
my $name = $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_value( $$self{_WINDOWSCRIPTS}{treeScripts} -> get_model -> get_iter( $$self{_SELECTED} ), 0 );
$self -> _saveFile( $$self{_SELECTED} ) if _wConfirm( $$self{_WINDOWSCRIPTS}{main}, "PAC Script '$name' has changed.\nSave data before loading another script?" );
}
$SOURCEVIEW and $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> begin_not_undoable_action;
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_text( $default );
$SOURCEVIEW and $$self{_WINDOWSCRIPTS}{multiTextBuffer} -> end_not_undoable_action;
$$self{_SELECTED} = '';
$$self{_WINDOWSCRIPTS}{gui}{textScript} -> set_sensitive( 0 );
$$self{_WINDOWSCRIPTS}{gui}{btnadd} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnimport} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnreload} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnclose} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnremove} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_WINDOWSCRIPTS}{gui}{btnsave} -> set_sensitive( ! $PACMain::FUNCS{_MAIN}{_READONLY} );
$$self{_SYNTAX_CHANGED} = 0;
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_markup( '' );
$$self{_WINDOWSCRIPTS}{gui}{status} -> set_tooltip_text( '' );
}
$$self{_WINDOWSCRIPTS}{multiTextBuffer} -> set_modified( 0 );
return 1;
}
sub _execScript {
my $self = shift;
my $name = shift;
my @uuid_tmps = @_;
return 1 unless defined $name;
# %SESSION and %CONNECTION *MUST* be reset every time we are called!!
our %COMMON; undef %COMMON;
our %PAC; undef %PAC;
our %TERMINAL; undef %TERMINAL;
our %SHARED; undef %SHARED;
$COMMON{subst} = sub { return _subst( shift // '', $PACMain::FUNCS{_MAIN}{_CFG} ); };
$COMMON{cfg} = sub { my $ref = shift // 0; return $ref ? $PACMain::FUNCS{_MAIN}{_CFG} : dclone( $PACMain::FUNCS{_MAIN}{_CFG} ); };
$COMMON{cfg_sanity} = sub { _cfgSanityCheck( shift ); };
$COMMON{del_esc} = sub { return _removeEscapeSeqs( shift // '' ); };
$PAC{cfg_save} = sub { return $PACMain::FUNCS{_MAIN} -> _saveConfiguration( shift ); };
$PAC{cfg_export} = sub { return $PACMain::FUNCS{_MAIN}{_CONFIG} -> _exporter( 'yaml', shift ); };
$PAC{to_window} = sub {
my $uuid_tmp = shift;
defined $uuid_tmp or return 0;
$uuid_tmp = _subst( $uuid_tmp, $PACMain::FUNCS{_MAIN}{_CFG} );
( ! defined $PACMain::RUNNING{$uuid_tmp} || ! $PACMain::RUNNING{$uuid_tmp}{terminal}{_TABBED} ) and return 0;
$PACMain::RUNNING{$uuid_tmp}{terminal} -> _tabToWin;
return 1;
};
$PAC{to_tab} = sub {
my $uuid_tmp = shift;
defined $uuid_tmp or return 0;
$uuid_tmp = _subst( $uuid_tmp, $PACMain::FUNCS{_MAIN}{_CFG} );
( ! defined $PACMain::RUNNING{$uuid_tmp} || $PACMain::RUNNING{$uuid_tmp}{terminal}{_TABBED} ) and return 0;
$PACMain::RUNNING{$uuid_tmp}{terminal} -> _winToTab;
return 1;
};
$PAC{split} = sub {
my $uuid_tmp1 = shift;
my $uuid_tmp2 = shift;
my $vertical = shift // 1;
defined $uuid_tmp1 or return 0;
defined $uuid_tmp2 or return 0;
$uuid_tmp1 = _subst( $uuid_tmp1, $PACMain::FUNCS{_MAIN}{_CFG} );
$uuid_tmp2 = _subst( $uuid_tmp2, $PACMain::FUNCS{_MAIN}{_CFG} );
( ! defined $PACMain::RUNNING{$uuid_tmp1} || $PACMain::RUNNING{$uuid_tmp1}{terminal}{_SPLIT} ) and return 0;
( ! defined $PACMain::RUNNING{$uuid_tmp2} || $PACMain::RUNNING{$uuid_tmp2}{terminal}{_SPLIT} ) and return 0;
$PACMain::RUNNING{$uuid_tmp1}{terminal} -> _split( $uuid_tmp2, $vertical );
return 1;
};
$PAC{unsplit} = sub {
my $uuid_tmp = shift;
defined $uuid_tmp or return 0;
$uuid_tmp = _subst( $uuid_tmp, $PACMain::FUNCS{_MAIN}{_CFG} );
( ! defined $PACMain::RUNNING{$uuid_tmp} || ! $PACMain::RUNNING{$uuid_tmp}{terminal}{_SPLIT} ) and return 0;
$PACMain::RUNNING{$uuid_tmp}{terminal} -> _unsplit;
return 1;
};
$PAC{start_uuid} = sub {
my $uuid = shift;
my $cluster = shift // '';
defined $uuid or return 0;
$cluster = _subst( $cluster, $PACMain::FUNCS{_MAIN}{_CFG} );
my @idx;
push( @idx, [ $uuid, undef, $cluster ] );
my $terminals = $PACMain::FUNCS{_MAIN} -> _launchTerminals( \@idx );
$PAC{list}{ $$terminals[0]{_UUID_TMP} } = $$terminals[0]{_UUID};
return $$terminals[0]{_UUID}, $$terminals[0]{_UUID_TMP};
};
$PAC{start} = sub {
my $name = shift;
my $cluster = shift // '';
defined $name or return 0;
$name = _subst( $name, $PACMain::FUNCS{_MAIN}{_CFG} );
$cluster = _subst( $cluster, $PACMain::FUNCS{_MAIN}{_CFG} );
my $uuid = $self -> _getUUID( $name );
defined $uuid or return 0;
my @idx;
push( @idx, [ $uuid, undef, $cluster ] );
my $terminals = $PACMain::FUNCS{_MAIN} -> _launchTerminals( \@idx );
$PAC{list}{ $$terminals[0]{_UUID_TMP} } = $$terminals[0]{_UUID};
return $$terminals[0]{_UUID}, $$terminals[0]{_UUID_TMP};
};
$PAC{start_manual} = sub {
my $name = shift;
defined $name or return 0;
$name = _subst( $name, $PACMain::FUNCS{_MAIN}{_CFG} );
my $uuid = $self -> _getUUID( $name );
defined $uuid or return 0;
my @idx;
push( @idx, [ $uuid, undef, undef, 'manual' ] );
my $terminals = $PACMain::FUNCS{_MAIN} -> _launchTerminals( \@idx );
$PAC{list}{ $$terminals[0]{_UUID_TMP} } = $$terminals[0]{_UUID};
return $$terminals[0]{_UUID}, $$terminals[0]{_UUID_TMP};
};
$PAC{start_uuid_manual} = sub {
my $uuid = shift;
defined $uuid or return 0;
my @idx;
push( @idx, [ $uuid, undef, undef, 'manual' ] );
my $terminals = $PACMain::FUNCS{_MAIN} -> _launchTerminals( \@idx );
$PAC{list}{ $$terminals[0]{_UUID_TMP} } = $$terminals[0]{_UUID};
return $$terminals[0]{_UUID}, $$terminals[0]{_UUID_TMP};
};
$PAC{stop} = sub {
my $tmp_uuid = shift;
defined $tmp_uuid or return 0;
$tmp_uuid = _subst( $tmp_uuid, $PACMain::FUNCS{_MAIN}{_CFG} );
defined $PACMain::RUNNING{$tmp_uuid} and $PACMain::RUNNING{$tmp_uuid}{terminal} -> stop( 1, 0 );
delete $PAC{list}{$tmp_uuid};
return 1;
};
$PAC{select} = sub {
my $name = shift;
defined $name or return 0;
if ( $name eq '*ALL*' ) {
map ( $PAC{list}{$_} = $PACMain::RUNNING{$_}{uuid}, keys %PACMain::RUNNING );
return $PAC{list};
}
$name = _subst( $name, $PACMain::FUNCS{_MAIN}{_CFG} );
my $tmp_uuid = $self -> _getTmpUUID( $name );
return 0 unless defined $tmp_uuid;
my $uuid = $self -> _getUUID( $name );
$PAC{list}{$tmp_uuid} = $uuid;
return $uuid, $tmp_uuid;
};
$PAC{msg} = sub {
my $msg = shift;
my $modal = shift // 0;
if ( ( defined $msg ) && ( $msg ne '' ) ) {
$msg = _subst( $msg, $PACMain::FUNCS{_MAIN}{_CFG} );
if ( defined $PAC{_msg_wid} ) {
$PAC{_msg_wid} -> destroy;
delete $PAC{_msg_wid};
}
$PAC{_msg_wid} = _wMessage( $PACMain::FUNCS{_MAIN}{_GUI}{main}, __( $msg ), $modal );
} else {
$PAC{_msg_wid} -> destroy if defined $PAC{_msg_wid};
undef $PAC{_msg_wid};
}
};
my $file = $$self{_SCRIPTS}{$name};
defined &SESSION and undef &SESSION;
if ( ! open( F, $file ) ) { _wMessage( undef, "Could not open PAC Script file '$file' for reading: $!" ); return 1; }
my @lines = <F>;
my $txt = join( '', @lines );
close F;
no warnings ( 'redefine' );
eval $txt;
use warnings;
if ( $@ ) { _wMessage( undef, "Error parsing PAC Script: $@" ); $PAC{msg}(); return 0; }
# SESSION execution (local)
if ( scalar @uuid_tmps ) {
foreach my $uuid_tmp ( @uuid_tmps ) {
next unless defined $PACMain::RUNNING{$uuid_tmp};
$PAC{list}{ $uuid_tmp } = $PACMain::RUNNING{$uuid_tmp}{uuid};
}
} else {
if ( ! defined &SESSION ) {
_wMessage( undef, "Error executing PAC Script:\nNo 'SESSION' function declaration found, and script not being executed directly from any Terminal!" );
$PAC{msg}();
return 0;
} else {
eval { &SESSION; };
if ( $@ ) { _wMessage( undef, "Error executing PAC Script: $@" ); $PAC{msg}(); return 0; }
}
}
# Save the list of started connections in $SHARED{_list_}
$SHARED{_list_} = $PAC{list};
# CONNECTION execution (pac_conn)
foreach my $tmp_uuid ( keys %{ $PAC{list} } ) {
next unless defined $PACMain::RUNNING{$tmp_uuid};
if ( $PACMain::RUNNING{$tmp_uuid}{terminal}{_SCRIPT_STATUS} ne 'STOP' ) {
_wMessage( undef, "ERROR: Can not start a new PAC Script while another one is still running:\nTerminal '$PACMain::RUNNING{$tmp_uuid}{terminal}{_NAME}' is running '$PACMain::RUNNING{$tmp_uuid}{terminal}{_SCRIPT_NAME}'", 1 ) ;
next;
}
Glib::Timeout -> add( 500, sub {
# Skip if this tmp_uuid has disappeared for any reason
return 0 unless defined $PACMain::RUNNING{$tmp_uuid};
# Continue waiting if this tmp_uuid is not yet in "CONNECTED" state
return 1 unless $PACMain::RUNNING{$tmp_uuid}{terminal}{CONNECTED};
# Advise pac_conn to receive script name
kill( 12, $PACMain::RUNNING{$tmp_uuid}{terminal}{_PID} );
my %tmp;
$tmp{name} = $name;
$tmp{script} = $txt;
$tmp{shared} = \%SHARED;
nstore_fd( \%tmp, $PACMain::RUNNING{$tmp_uuid}{terminal}{_SOCKET_CLIENT} ) or die "ERROR:$!";
# Stop
return 0;
} );
}
return 1;
}
sub _getUUID {
my $self = shift;
my $name = shift;
defined $name or return undef;
my $cfg = $PACMain::FUNCS{_MAIN}{_CFG};
my $uuid;
foreach my $cuuid ( keys %{ $$cfg{'environments'} } ) {
next unless ( ( $$cfg{'environments'}{$cuuid}{'name'} // '' ) eq $name );
$uuid = $cuuid;
last;
}
return $uuid;
}
sub _getTmpUUID {
my $self = shift;
my $name = shift;
defined $name or return undef;
my $tmp_uuid;
foreach my $tuuid ( keys %PACMain::RUNNING ) {
next unless defined $PACMain::RUNNING{$tuuid} && $PACMain::RUNNING{$tuuid}{terminal}{'_NAME'} eq $name;
$tmp_uuid = $tuuid;
last;
}
return $tmp_uuid;
}
# END: Define PRIVATE CLASS functions
###################################################################
1;