#!/usr/local/ActivePerl-5.8/bin/perl -w # # astGUIclient_1.1.0.pl version 1.1.0 for Perl/Tk # by MattF started 2003/10/29 # # Description: # - Grabs live call info from a DB updated every second # - Displays live status of users phones and Zap/IAX/SIP/Local channels # - Displays number of voicemail messages new and old # - Allows calls to be placed from GUI and directed to phone # - Allows blind internal, external, voicemail call transfers # - Allows call recording by click of button # - Allows conference calling of up to 6 channels through GUI # - Administrative Hangup of any live Zap/IAX/SIP/Local channel # - Administrative switch user function # - Call Parking sends calls to park ext and then redirects to phone ext # # SUMMARY: # This program was designed for people using the Asterisk PBX with Digium # Zaptel T1 cards or IAX trunks and SIP VOIP hardphones or softphones as # extensions, it could be adapted to other functions, but I designed it # specifically for Zap/IAX/SIP users. The program will run on UNIX Xwindows and # Win32 providing the following criteria are met: # # Win32 - ActiveState Perl 5.8.0 # UNIX - Gnome or KDE with Tk/Tcl and perl Tk/Tcl modules loaded # ActiveState Perl is recommended on UNIX due to memory leak in generic perl # Both - Net::MySQL and Net::Telnet perl modules loaded # # For this program to work you also need to have the "asterisk" MySQL database # created and create the tables listed in the CONF_MySQL.txt file, also make sure # that the machine running this program has read/write/update/delete access # to that database # # On the server side the AST_update.pl program must always be running on a # machine somewhere(it is recommended that AST_update be run on the Asterisk # server locally) for the client app to receive live information and function # properly, also there are Asterisk conf file settings and # MySQL databases that must be present for the client app to work. # Information on these is detailed in the README file # # You now have the option for this program to send Action commands either # directly to the Asterisk machine, or have them entered into the central # queue system. It is recommended to use the central queue system and if this # program will be heavily used it is strongly encouraged for you to use the # central queue system to avoid the problem of Manager buffer-overflow # deadlocking that can occur with remote manager connections. You can find # directions on how to install the central queue system in the README.txt file # To enable the central queue system for this client set: # $QUEUE_ACTION_enabled = 1; in the AST_VICI_conf.pl file # # Use this command to debug with ptkdb and uncomment Devel::ptkdb module below # perl -d:ptkdb C:\AST_VICI\astGUIclient_1.1.0.pl # # # Copyright (C) 2006 Matt Florell LICENSE: GPLv2 # # version changes: # 50110-1040 - modified to add Zap and IAX2 clients (differentiate from trunks) # 50120-0955 - modified to change configuration location to DB # 50225-1829 - fixed Zap client recording bug # 50311-1420 - optimized recently-dialed-numbers queries # some script specific initial values $build = '50311-1420'; $version = '1.1.0'; $VM_messages='0'; $VM_old_messages='0'; $DASH='-'; $USUS='__'; $SLASH='/'; $AMP='@'; $park_frame_list_refresh = 0; $Default_window = 1; $zap_frame_list_refresh = 1; $updater_duplicate_counter = 0; $updater_duplicate_value = ''; $updater_warning_up=0; $open_dialpad_window_again = 0; $LookupID_and_log = ''; # DB table variables for testing $parked_channels = 'parked_channels'; $live_channels = 'live_channels'; $live_sip_channels = 'live_sip_channels'; $server_updater = 'server_updater'; # $parked_channels = 'TEST_parked_channels'; # $live_channels = 'TEST_live_channels'; # $live_sip_channels = 'TEST_live_sip_channels'; # $server_updater = 'TEST_server_updater'; require 5.002; ### libs path, where the program looks for config files and perl libs use lib ".\\",".\\libs", './', './libs', '../libs', '/usr/local/perl_TK/libs', 'C:\\AST_VICI\\libs', 'C:\\cygwin\\usr\\local\\perl_TK\\libs'; ### Make sure this file is in a libs path or put the absolute path to it require("AST_VICI_conf.pl"); # local configuration file if (!$DB_port) {$DB_port='3306';} #use Devel::ptkdb; # uncomment if you want to debug with ptkdb use Net::MySQL; use Net::Telnet (); sub idcheck_connect; sub connect_to_db; &idcheck_connect; ### connect and define custom variables if ($enable_persistant_mysql) {&connect_to_db;} use English; use Tk; use Tk::DialogBox; use Tk::BrowseEntry; use Tk::Animation; sub connect_to_db; sub start_recording; sub stop_recording; sub get_online_sip_users; sub get_online_channels; sub get_parked_channels; sub Open_Help ; sub Dial_Number; sub Local_Xfer; sub Voicemail_Xfer; sub Start_Conference; sub Stop_Conference; sub Switch_Your_ID; sub Dial_Outside_Number; sub Dial_Line; sub Park_Line; sub Join_Line; sub Hangup_Line; sub Hijack_Line; sub Pickup_Line; sub validate_conf_lines; sub Join_Conference; sub dtmf_dialpad_window; sub conf_send_dtmf; sub Zap_Hangup; sub SIP_Hangup; sub Zap_Monitor; sub View_Parked; ### Create new Perl Tk window instance and name it my $MW = MainWindow->new; $MW->title("astGUIclient - $version"); $MW->Label(-text => "Build $build ")->pack(-side => 'bottom'); my $ans; ### Time/Date display at the top of the screen my $time_frame = $MW->Frame()->pack(-expand => '1', -fill => 'both', -side => 'top'); my $time_value = $time_frame->Entry(-width => '26', -relief => 'sunken')->pack(-side => 'top'); ### config file defined phone ID my $login_frame = $MW->Frame()->pack(-expand => '1', -fill => 'both', -side => 'top'); $login_frame->Label(-text => "your ID:")->pack(-side => 'left'); my $login_value = $login_frame->Entry(-width => '15', -relief => 'sunken')->pack(-side => 'left'); $login_value->insert('0', $SIP_user); ### if enabled show the voicemail button bitmap frame if ($voicemail_button_enabled) { my $voicemail_button_frame = $login_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'left'); $voicemail_button_frame->Label(-text => " ")->pack(-side => 'left'); my $vm_ind = "$install_directory/libs/vm_indicator_anim.gif"; $vm_ind_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_ind); $vm_ind_anim->set_image(0); $voicemail_button_frame->Label(-image => $vm_ind_anim)->pack(-side => 'left'); $vm_ind_anim->start_animation(50); $vm_button = $voicemail_button_frame->Photo(-file => "$install_directory/libs/voicemail_up.gif"); $vm_new = $voicemail_button_frame->Photo(-file => "$install_directory/libs/voicemail_new.gif"); $vm_old = $voicemail_button_frame->Photo(-file => "$install_directory/libs/voicemail_old.gif"); my $vm_digits = "$install_directory/libs/voicemail_digits.gif"; $vm_new_1_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_new_2_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_new_3_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_old_1_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_old_2_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_old_3_anim = $voicemail_button_frame->Animation(-format => 'gif', -file => $vm_digits); $vm_new_1_anim->set_image(10); $vm_new_2_anim->set_image(10); $vm_new_3_anim->set_image(10); $vm_old_1_anim->set_image(10); $vm_old_2_anim->set_image(10); $vm_old_3_anim->set_image(10); $vm_ind_anim->stop_animation(0); ### button below current phones box that allows user to change IDs my $voicemail_button = $voicemail_button_frame->Button(-text => 'VOICEMAIL', -width => -1, -image => $vm_button, -relief => 'flat', -command => sub { $VM_button_dial=1; Dial_Outside_Number; }); $voicemail_button->pack(-side => 'left', -expand => 'no', -fill => 'none'); $voicemail_button_frame->Label(-image => $vm_new)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_new_1_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_new_2_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_new_3_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_old)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_old_1_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_old_2_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); $voicemail_button_frame->Label(-image => $vm_old_3_anim, -borderwidth => 0)->pack(-side => 'left', -anchor =>'w'); } ### Active outside channel if the phone is on one my $channel_value = $login_frame->Entry(-width => '15', -relief => 'sunken')->pack(-side => 'right'); $channel_value->insert('0', ''); $login_frame->Label(-text => "current channel:")->pack(-side => 'right'); ### main frame for the buttons and list of the app my $main_frame = $MW->Frame()->pack(-expand => '1', -fill => 'both', -side => 'top'); ######################################################## ### Frame for current enabled phones and optional switch ID button my $current_phones_frame = $main_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'left'); $current_phones_frame->Label(-text => "Live Extensions:")->pack(-side => 'top'); ### list box for current phones in system my $SIP_user_list = $current_phones_frame->Listbox(-relief => 'sunken', -width => -1, # Shrink to fit -selectmode => 'single', -exportselection => 0, -height => 20, -setgrid => 'yes'); my @items = qw(One Two Three Four Five Six Seven Eight Nine Ten Eleven Twelve); foreach (@items) { $SIP_user_list->insert('end', $_); } my $scroll = $main_frame->Scrollbar(-command => ['yview', $SIP_user_list]); $SIP_user_list->configure(-yscrollcommand => ['set', $scroll]); $SIP_user_list->pack(-side => 'top', -fill => 'both', -expand => 'yes'); $scroll->pack(-side => 'left', -fill => 'y'); $SIP_user_list->selectionSet('0'); if ($user_switching_enabled) { ### button below current phones box that allows user to change IDs my $switch_your_id = $current_phones_frame->Button(-text => 'SWITCH YOUR ID', -width => -1, -command => sub { Switch_Your_ID; }); $switch_your_id->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); } ######################################################## ### Frame for current live phones, live Zap/IAX Channels(trunks) and live SIP/Local Channels(clients) my $live_phones_frame = $main_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'right'); $live_phones_frame->Label(-text => "Busy: Outside Lines: Local Extensions: ")->pack(-side => 'top'); my $zap_frame = $live_phones_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'right'); my $zap_list_frame = $zap_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'top'); ### list box for current sips/locals that are on live calls right now my $live_sip = $zap_list_frame->Listbox(-relief => 'sunken', -width => 19, # Shrink to fit -selectmode => 'single', -exportselection => 0, -height => 20, -setgrid => 'yes'); foreach (@items) { $live_sip->insert('end', $_); } my $scrollS = $zap_list_frame->Scrollbar(-command => ['yview', $live_sip]); $live_sip->configure(-yscrollcommand => ['set', $scrollS]); $live_sip->pack(-side => 'right', -fill => 'both', -expand => 'yes'); $scrollS->pack(-side => 'right', -fill => 'y'); $live_sip->selectionSet('0'); ### list box for current Zaps/IAXs that are on live calls right now my $live_zap = $zap_list_frame->Listbox(-relief => 'sunken', -width => 15, # Shrink to fit -selectmode => 'single', -exportselection => 0, -height => 20, -setgrid => 'yes'); foreach (@items) { $live_zap->insert('end', $_); } my $scrollA = $zap_list_frame->Scrollbar(-command => ['yview', $live_zap]); $live_zap->configure(-yscrollcommand => ['set', $scrollA]); $live_zap->pack(-side => 'right', -fill => 'both', -expand => 'yes'); $scrollA->pack(-side => 'left', -fill => 'y'); $live_zap->selectionSet('0'); if ($admin_hangup_enabled) { my $hangup_buttons_frame = $zap_frame->Frame()->pack(-expand => 'no', -fill => 'x', -side => 'bottom'); ### button at the bottom of the Zap/IAX listbox to hangup Zap/IAX channels my $zap_hangup_button = $hangup_buttons_frame->Button(-text => 'Trunk Hangup', -width => -1, -command => sub { $zap_frame_list_refresh = 1; Zap_Hangup; }); $zap_hangup_button->pack(-side => 'left', -expand => '1', -fill => 'both'); ### button at the bottom of the sip/local listbox to hangup sip channels my $sip_hangup_button = $hangup_buttons_frame->Button(-text => 'Local Hangup', -width => -1, -command => sub { $sip_frame_list_refresh = 1; SIP_Hangup; }); $sip_hangup_button->pack(-side => 'right', -expand => '1', -fill => 'both'); } my $ext_frame = $live_phones_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'right'); my $ext_list_frame = $ext_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'top'); ### list box for current live busy extensions that are on Zap/IAX calls my $live_ext = $ext_list_frame->Listbox(-relief => 'sunken', -width => 8, # Shrink to fit -selectmode => 'single', -exportselection => 0, -height => 20, -setgrid => 'yes'); foreach (@items) { $live_ext->insert('end', $_); } my $scrollB = $ext_list_frame->Scrollbar(-command => ['yview', $live_ext]); $live_ext->configure(-yscrollcommand => ['set', $scrollB]); $live_ext->pack(-side => 'right', -fill => 'both', -expand => 'yes'); $scrollB->pack(-side => 'right', -fill => 'y'); $live_ext->selectionSet('0'); ### button at the bottom of the busy extensions listbox to monitor Zap channels my $zap_monitor_button = $ext_frame->Button(-text => 'Monitor', -width => -1, -command => sub { Zap_Monitor; }); $zap_monitor_button->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); ######################################################## ### Frame for middle buttons and entries my $middle_bottons_frame = $main_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'bottom'); ### button at the bottom to show parked channels my $view_park_gif = "$install_directory/libs/buttons.gif"; $view_park_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $view_park_gif); my $view_park_button = $middle_bottons_frame->Button(-text => 'VIEW PARKED CALLS', -width => -1, -image => $view_park_gif_anim, -relief => 'flat', -command => sub { View_Parked; }); $view_park_button->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $view_park_gif_anim->set_image(14); $view_park_button->configure(-state => 'disabled'); ### display field that shows the line that was last parked my $last_parked_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'bottom'); $last_parked_frame->Label(-text => "Parked:")->pack(-side => 'left'); my $last_parked_value = $last_parked_frame->Entry(-width => '10', -relief => 'sunken')->pack(-side => 'right'); $last_parked_value->insert('0', ''); my $last_parked_spacer_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'top'); $last_parked_spacer_frame->Label(-text => "----- -----")->pack(-side => 'bottom'); ### button to put the current Zap/IAX channel you are on in Park my $call_park_gif = "$install_directory/libs/buttons.gif"; $call_park_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $call_park_gif); my $call_park_button = $middle_bottons_frame->Button(-text => 'PARK THIS CALL', -width => -1, -image => $call_park_gif_anim, -relief => 'flat', -command => sub { $main_call_park=1; $PARK = $channel_value->get; $CHAN=''; $last_parked_value->delete('0', 'end'); $last_parked_value->insert('0', "$PARK"); Park_Line; $main_call_park=0; }); $call_park_button->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $call_park_gif_anim->set_image(12); $call_park_button->configure(-state => 'disabled'); ### button to blind transfer a call to an outside number my $xfer_out_gif = "$install_directory/libs/buttons.gif"; $xfer_out_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $xfer_out_gif); my $xfer_outside_number = $middle_bottons_frame->Button(-text => 'XFER TO OUTSIDE NUM', -width => -1, -image => $xfer_out_gif_anim, -relief => 'flat', -command => sub { $xfer_out_blind=1; Dial_Outside_Number; }); $xfer_outside_number->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $xfer_outside_number->configure(-state => 'disabled'); $xfer_out_gif_anim->set_image(16); ### button at the bottom to dial either a typed in number or a browsebox selected number my $dial_out_gif = "$install_directory/libs/buttons.gif"; $dial_out_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $dial_out_gif); my $dial_outside_number = $middle_bottons_frame->Button(-text => 'DIAL OUTSIDE NUMBER', -width => -1, -image => $dial_out_gif_anim, -relief => 'flat', -command => sub { Dial_Outside_Number; }); $dial_outside_number->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $dial_out_gif_anim->set_image(1); if ($AGI_call_logging_enabled) { $recent_dial_listbox=$middle_bottons_frame->BrowseEntry(-background => '#CCCCCC',-label => "Recent:",-width => '2')->pack(-expand => '1', -fill => 'both', -side => 'bottom'); $recent_dial_listbox->insert('end', ''); } my $dial_number_frame = $middle_bottons_frame->Frame()->pack(-expand => '1', -fill => 'both', -side => 'bottom'); # $dial_number_frame->Label(-text => "----- -----")->pack(-side => 'top'); $dial_number_frame->Label(-text => "Outside number to dial:")->pack(-side => 'top'); my $dial_number_value = $dial_number_frame->Entry(-width => '12', -relief => 'sunken')->pack(-side => 'bottom'); ### button for a popup HELP screen with user directions my $help_gif = "$install_directory/libs/buttons.gif"; $help_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $help_gif); my $Open_Help = $middle_bottons_frame->Button(-text => 'HELP', -width => -1, -image => $help_gif_anim, -relief => 'flat', -command => sub { Open_Help; }); $Open_Help->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $Open_Help->configure(-state => 'normal'); $help_gif_anim->set_image(9); ### button for a blind transfer live call within asterisk to another extension my $local_xfer_gif = "$install_directory/libs/buttons.gif"; $local_xfer_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $local_xfer_gif); my $local_xfer = $middle_bottons_frame->Button(-text => 'blind int xfer', -width => -1, -image => $local_xfer_gif_anim, -relief => 'flat', -command => sub { Local_Xfer; }); $local_xfer->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $local_xfer->configure(-state => 'disabled'); $local_xfer_gif_anim->set_image(18); ### button for a blind transfer live call within asterisk to voicemailbox my $vmail_xfer_gif = "$install_directory/libs/buttons.gif"; $vmail_xfer_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $vmail_xfer_gif); my $vmail_xfer = $middle_bottons_frame->Button(-text => 'blind vmail xfer', -width => -1, -image => $vmail_xfer_gif_anim, -relief => 'flat', -command => sub { Voicemail_Xfer; }); $vmail_xfer->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $vmail_xfer->configure(-state => 'disabled'); $vmail_xfer_gif_anim->set_image(20); ### button for a local call within asterisk to another extension my $local_call_gif = "$install_directory/libs/buttons.gif"; $local_call_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $local_call_gif); my $local_conf = $middle_bottons_frame->Button(-text => 'Intrasystem Call', -width => -1, -image => $local_call_gif_anim, -relief => 'flat', -command => sub { Dial_Number; }); $local_conf->pack(-side => 'bottom', -expand => 'no', -fill => 'both'); $local_conf->configure(-state => 'normal'); $local_call_gif_anim->set_image(7); ### button to start recording, only activated if the phone is on an active outside line my $start_rec_gif = "$install_directory/libs/buttons.gif"; $start_rec_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $start_rec_gif); my $start_recording = $middle_bottons_frame->Button(-text => 'START RECORDING', -width => -1, -image => $start_rec_gif_anim, -relief => 'flat', -command => sub { start_recording; $RECORD='-'; }); $start_recording->pack(-side => 'top', -expand => 'no', -fill => 'both'); $start_recording->configure(-state => 'disabled'); $start_rec_gif_anim->set_image(4); ### button to stop recording my $stop_rec_gif = "$install_directory/libs/buttons.gif"; $stop_rec_gif_anim = $middle_bottons_frame->Animation(-format => 'gif', -file => $stop_rec_gif); my $stop_recording = $middle_bottons_frame->Button(-text => 'STOP RECORDING', -width => -1, -image => $stop_rec_gif_anim, -relief => 'flat', -command => sub { stop_recording; }); $stop_recording->pack(-side => 'top', -expand => 'no', -fill => 'both'); $stop_rec_gif_anim->set_image(5); ### display field that shows -- RECORDING-- during recording active and the length of recording when recording is stopped my $rec_msg_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'top'); $rec_msg_frame->Label(-text => "RECORDING MESSAGE:")->pack(-side => 'top'); my $rec_msg_value = $rec_msg_frame->Entry(-width => '24', -relief => 'sunken')->pack(-side => 'top'); $rec_msg_value->insert('0', ''); ### display field that shows the filename as soon as recording is started my $rec_fname_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'top'); $rec_fname_frame->Label(-text => "RECORDING FILENAME:")->pack(-side => 'top'); my $rec_fname_value = $rec_fname_frame->Entry(-width => '24', -relief => 'sunken')->pack(-side => 'top'); $rec_fname_value->insert('0', ''); ### display field that shows the unique recording ID in the database only after the recording session is finished my $rec_recid_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'top'); $rec_recid_frame->Label(-text => "RECORDING ID:")->pack(-side => 'left'); my $rec_recid_value = $rec_recid_frame->Entry(-width => '10', -relief => 'sunken')->pack(-side => 'right'); $rec_recid_value->insert('0', ''); my $rec_recid_spacer_frame = $middle_bottons_frame->Frame()->pack(-expand => '2', -fill => 'both', -side => 'top'); $rec_recid_spacer_frame->Label(-text => "----- -----")->pack(-side => 'bottom'); ######################################################## ### Frame for Conference control my $conf_frame_control = $MW->Frame()->pack(-expand => '1', -fill => 'both', -side => 'bottom'); ### button at the bottom to enter a conference call with the current line my $start_conf_gif = "$install_directory/libs/buttons.gif"; $start_conf_gif_anim = $conf_frame_control->Animation(-format => 'gif', -file => $start_conf_gif); my $initiate_conference = $conf_frame_control->Button(-text => 'START CONFERENCE', -width => -1, -image => $start_conf_gif_anim, -relief => 'flat', -command => sub { Start_Conference; }); $initiate_conference->pack(-side => 'right', -expand => 'no', -fill => 'both'); $initiate_conference->configure(-state => 'disabled'); $start_conf_gif_anim->set_image(22); my $stop_conf_gif = "$install_directory/libs/buttons.gif"; $stop_conf_gif_anim = $conf_frame_control->Animation(-format => 'gif', -file => $stop_conf_gif); my $destroy_conference = $conf_frame_control->Button(-text => 'STOP CONFERENCE', -width => -1, -image => $stop_conf_gif_anim, -relief => 'flat', -command => sub { Stop_Conference; }); $destroy_conference->pack(-side => 'right', -expand => 'no', -fill => 'both', -before => $initiate_conference); $destroy_conference->configure(-state => 'disabled'); $stop_conf_gif_anim->set_image(24); ################################################################################################################ ### Frame for Conferencing