#! /usr/bin/perl # # Implements graphical user interface for McStas # # # This file is part of the McStas neutron ray-trace simulation package # Copyright (C) 1997-2004, All rights reserved # Risoe National Laborartory, Roskilde, Denmark # Institut Laue Langevin, Grenoble, France # # 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 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Config module needed for various platform checks. # PW, 20030314 use Config; use POSIX; # Determine the path to the McStas system directory. This must be done # in the BEGIN block so that it can be used in a "use lib" statement # afterwards. BEGIN { # default configuration (for all high level perl scripts) if($ENV{"MCSTAS"}) { $MCSTAS::sys_dir = $ENV{"MCSTAS"}; } else { if ($Config{'osname'} eq 'MSWin32') { $MCSTAS::sys_dir = "c:\\mcstas\\lib"; } else { $MCSTAS::sys_dir = "/usr/local/lib/mcstas"; } } $MCSTAS::perl_dir = "$MCSTAS::sys_dir/tools/perl"; # custom configuration (this script) $MCSTAS::perl_modules = "$MCSTAS::perl_dir/modules"; } use lib $MCSTAS::perl_dir; use lib $MCSTAS::perl_modules; require "mcstas_config.perl"; use strict; use FileHandle; use Tk; use Tk::TextUndo; use Tk::ROText; use Tk::DialogBox; use File::Path; require "mcfrontlib.pl"; require "mcguilib.pl"; # Requirement for mcplotlib.pl removed, will be loaded only # if mcdisplay PGPLOT backend is used. # PW, 20030314 # require "mcplotlib.pl"; require "mcrunlib.pl"; my $current_sim_file; my $current_sim_def = ""; my ($inf_instr, $inf_sim, $inf_data); my %inf_param_map; my ($main_window,$edit_window, $edit_control, $edit_label); my ($status_label, $current_results_label, $cmdwin, $current_instr_label); my $prefix = $MCSTAS::mcstas_config{'PREFIX'}; my $suffix = $MCSTAS::mcstas_config{'SUFFIX'}; my $background; # Only really makes sense on Unix systems... if ($Config{'osname'} ne 'MSWin32') { $background = '&'; } my $external_editor = $MCSTAS::mcstas_config{'EXTERNAL_EDITOR'}; my $compinfo; # Cache of parsed component definitions my @compdefs; # List of available component definitions # Our own Tk:Error function, to trap errors in TextUndo->Save(). See # Tk documentation of Tk::Error. my $error_override; # Temporary override Tk::Error. sub Tk::Error { my ($w, $err, @loc) = @_; if($error_override) { &$error_override($w, $err, @loc); } else { print STDERR "Tk::Error###: $err "; print STDERR join("\n ", @loc), "\n"; } } sub ask_save_before_simulate { my ($w) = @_; if($edit_control && $edit_control->numberChanges() > 0) { my $ret = $w->messageBox( -message => "Save instrument \"$current_sim_def\" first?", -title => "Save file?", -type => 'YesNoCancel', -icon => 'question', -default => 'yes'); menu_save($w) if lc($ret) eq "yes"; return $ret eq "Cancel" ? 0 : 1; } else { return 1; } } sub is_erase_ok { my ($w) = @_; if($edit_control && $edit_control->numberChanges() > 0) { my $ret = $w->messageBox(-message => "Ok to loose changes?", -title => "Erase ok?", -type => 'okcancel', -icon => 'question', -default => 'cancel'); # Make response all lowercase: $ret = lc($ret); return $ret eq "ok" ? 1 : 0; } else { return 1; } } sub menu_quit { if(is_erase_ok($main_window)) { $main_window->destroy; } } sub menu_edit_current { if($edit_control) { $edit_window->raise(); } else { if ($MCSTAS::mcstas_config{'EDITOR'} eq 0) { setup_edit_1_7($main_window); } elsif ($MCSTAS::mcstas_config{'EDITOR'} eq 1 && $MCSTAS::mcstas_config{'CODETEXT'}) { setup_edit($main_window); } else { menu_spawn_editor($main_window); } } } sub menu_spawn_editor { my ($w) = @_; my $pid; if ($external_editor eq "no") { return 0; } # Must be handled differently on Win32 vs. unix platforms... if($Config{'osname'} eq "MSWin32") { system("$external_editor $current_sim_def"); } else { $pid = fork(); if(!defined($pid)) { $w->messageBox(-message => "Failed to spawn editor \"$external_editor $current_sim_def\".", -title => "Command failed", -type => 'OK', -icon => 'error'); return 0; } elsif($pid > 0) { waitpid($pid, 0); return 1; } else { # Double fork to avoid having to wait() for the editor to # finish (or having it become a zombie). See man perlfunc. unless(fork()) { exec("$external_editor $current_sim_def"); # If we get here, the exec() failed. print STDERR "Error: exec() of $external_editor failed!\n"; POSIX::_exit(1); # CORE:exit needed to avoid Perl/Tk failure. } POSIX::_exit(0); # CORE:exit needed to avoid Perl/Tk failure. } } } sub mcdoc_current { my $cmd = "$prefix mcdoc$suffix $current_sim_def $background"; if (-e $current_sim_def) { putmsg($cmdwin, "Opening instrument docs: $cmd\n", 'msg'); system("$cmd"); } } sub mcdoc_web { my $cmd = "$prefix mcdoc$suffix --web $background"; putmsg($cmdwin, "Opening Web Page: $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_manual { my $cmd = "$prefix mcdoc$suffix --manual $background"; putmsg($cmdwin, "Opening User Manual: $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_compman { my $cmd = "$prefix mcdoc$suffix --comp $background"; putmsg($cmdwin, "Opening Component Manual: $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_tutorial { my $cmd = "$prefix mcdoc$suffix --tutorial $background"; putmsg($cmdwin, "Opening Tutorial: $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_components { my $cmd = "$prefix mcdoc$suffix $background"; putmsg($cmdwin, "Opening Library help: $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_generate { my $cmd = "$prefix mcdoc$suffix --force $background"; putmsg($cmdwin, "Generating Library help (local): $cmd\n", 'msg'); system("$cmd"); } sub mcdoc_test { my $status; my $printer = sub { putmsg($cmdwin, "$_[0]\n", 'msg'); $main_window->update;}; $status = do_test($printer, 1, $MCSTAS::mcstas_config{'PLOTTER'}, 'compatible graphics'); if (defined $status) { putmsg($cmdwin, "$status", 'msg'); } } sub mcdoc_about { my ($w) = @_; my $version = `mcstas --version`; my $ret = $w->messageBox(-message => "This is the McStas Graphical User Interface. McStas is a tool for Monte Carlo neutron scattering simulations. It provides a complete set of tools, components, and example instruments.\n This software required a significant effort to be brought to you. If you enjoy it, please use following references in your work:\n P. Willendrup, E. Farhi and K. Lefmann, Physica B, 350 (2004) 735.\n K. Lefmann and K. Nielsen, Neutron News 10, 20, (1999).\n $version Please visit ", -title => "McGUI: About McStas", -type => 'OK', -icon => 'info', -width => 40, -default => 'cancel'); } sub new_simulation_results { my ($w) = @_; my $text = $current_sim_file ? $current_sim_file : ""; $current_results_label->configure(-text => "Simulation results: $text"); } sub new_sim_def_name { my ($w, $name) = @_; unless($current_sim_def ne "" && $name eq $current_sim_def) { undef($current_sim_file); new_simulation_results($w); } $current_sim_def = $name; # Strip any repeated "/" charactors (ie. "///" -> "/"). $current_sim_def =~ s!//!/!g; # On NON-Win32 platforms, replace ' ' by '\ ' to ensure correct # handling of spaces in filenames... Unfortunately, this is a # more complicated matter on Win32 - has to be handled in each # subroutine... :( if (!$Config{'osname'} eq 'MSWin32') { $current_sim_def =~ s! !\ !g; } # Strip any redundant leading "./". while($current_sim_def =~ m!^\./(.*)$!) { $current_sim_def = $1; } # Strip any redundant "dir/../". # Problem: Needs to handle "/../../" correctly to work ... # while($current_sim_def =~ m!^[^/]+/\.\./(.*)$!) { # $current_sim_def = $1; # } # while($current_sim_def =~ m!^(.*)/[^/]+/\.\./(.*)$!) { # $current_sim_def = "$1/$2"; # } $main_window->title("McStas: $current_sim_def"); my $text = "Instrument file: " . ($current_sim_def ne "" ? $current_sim_def : ""); if ($current_sim_def ne "" && $edit_window) { $edit_window->title("Edit: $current_sim_def"); } $current_instr_label->configure(-text => $text); # On Win32, doing a chdir is probably better at this point... if ($Config{'osname'} eq 'MSWin32') { chdir(dirname($current_sim_def)); } putmsg($cmdwin, "$text\n", 'msg'); } sub open_instr_def { my ($w, $file) = @_; $edit_control->Load($file) if $edit_control; new_sim_def_name($w, $file); } sub menu_open { my ($w) = @_; return 0 unless(is_erase_ok($w)); my $file = $w->getOpenFile(-defaultextension => ".instr", -title => "Select instrument file", -initialdir => "$ENV{'PWD'}"); return 0 unless $file; open_instr_def($w, $file); return 1; } sub menu_save { my ($w) = @_; if($current_sim_def ne "") { $edit_control->Save($current_sim_def); $edit_window->title("Edit: $current_sim_def"); open_instr_def($w, $current_sim_def); } else { $error_override = sub { # Temporary Tk::Error override $w->messageBox(-message => "Could not save file:\n$_[1].", -title => "Save failed", -type => 'OK', -icon => 'error'); }; menu_saveas($w); $error_override = undef; # Reinstall default Tk::Error handler/ } } sub menu_saveas { my ($w) = @_; my $file; if($current_sim_def) { my ($inidir, $inifile); if($current_sim_def =~ m!^(.*)/([^/]*)$!) { ($inidir, $inifile) = ($1, $2); } else { ($inidir, $inifile) = ("", $current_sim_def); } $file = $w->getSaveFile(-defaultextension => ".instr", -title => "Select instrument file name", -initialdir => $inidir, -initialfile => $inifile); } else { $file = $w->getSaveFile(-defaultextension => ".instr", -title => "Select instrument file name"); } return 0 unless $file; $edit_control->FileName($file); new_sim_def_name($w, $file); menu_save($w); return 1; } sub menu_new { my ($w) = @_; return 0 unless(is_erase_ok($w)); my $file = $w->getSaveFile(-defaultextension => ".instr", -title => "Select instrument file name"); return 0 unless $file; $edit_control->delete("1.0", "end"); $edit_control->FileName($file); new_sim_def_name($w, $file); return 1; } sub menu_undo { my ($w) = @_; if($edit_control->numberChanges() <= 0) { $w->messageBox(-message => "There is no further undo information.", -title => "Undo not possible", -type => 'OK', -icon => 'error'); } else { $edit_control->eventGenerate("<>"); } } sub read_sim_data { my ($w) = @_; return 0 unless $current_sim_file && -r $current_sim_file; my ($ii, $si, $di) = read_sim_file($current_sim_file); return 0 unless $ii && $si && $di; # Save old settings of "plot results". $si->{'Autoplot'} = $inf_sim->{'Autoplot'}; $inf_instr = $ii; $inf_sim = $si; $inf_data = $di; my $i; foreach $i (keys %{$si->{'Params'}}) { $inf_param_map{$i} = $si->{'Params'}{$i}; } $si->{'Params'} = \%inf_param_map; return 1; } sub load_sim_file { my ($w) = @_; my $file = $w->getOpenFile(-defaultextension => ".sim", -title => "Select simulation file", -initialdir => "$ENV{'PWD'}"); if($file && -r $file) { $current_sim_file = $file ; new_simulation_results($w); } read_sim_data($w); } sub save_disp_file { # Function for saving mcdisplay type output # PW 20030314 my ($w,$ext) = @_; my $file = $w->getSaveFile(-defaultextension => $ext, -title => "Select output filename", -initialdir => "$ENV{'PWD'}", -initialfile => "mcdisplay_output.$ext"); return $file; } sub putmsg { my ($t, $m, $tag) = @_; $cmdwin->insert('end', $m, $tag); $cmdwin->see('end'); } sub run_dialog_create { my ($w, $title, $text, $cancel_cmd) = @_; my $dlg = $w->Toplevel(-title => $title); $dlg->transient($dlg->Parent->toplevel); $dlg->withdraw; $dlg->protocol("WM_DELETE_WINDOW" => sub { } ); # Add labels $dlg->Label(-text => $text, -anchor => 'w', -justify => 'left')->pack(-fill => 'x'); my $bot_frame = $dlg->Frame(-relief => "raised", -bd => 1); $bot_frame->pack(-side => "top", -fill => "both", -ipady => 3, -ipadx => 3); my $but = $bot_frame->Button(-text => "Cancel", -command => $cancel_cmd); $but->pack(-side => "left", -expand => 1, -padx => 1, -pady => 1); return $dlg; } sub run_dialog_popup { my ($dlg) = @_; # Display the dialog box my $old_focus = $dlg->focusSave; my $old_grab = $dlg->grabSave; $dlg->Popup; $dlg->grab; return [$old_focus, $old_grab]; } sub run_dialog_retract { my ($dlg, $oldfg) = @_; $dlg->grabRelease; $dlg->destroy; &{$oldfg->[0]} if $oldfg; &{$oldfg->[1]} if $oldfg; } sub run_dialog_reader { my ($w, $fh, $rotext, $state, $success) = @_; my $s; my $len = sysread($fh, $s, 256, 0); if($len) { putmsg($rotext, $s); } else { $w->fileevent($fh,'readable', ""); return if $$state; $$state = 1; $$success = defined($len); } } sub run_dialog { my ($w, $fh, $pid, $inittext) = @_; # The $state variable is set when the simulation finishes. my ($state, $success) = (0, 0); # Initialize the dialog. my $cancel_cmd = sub { kill -15, $pid unless $state; # signal 15 is SIGTERM }; my $dlg = run_dialog_create($w, "Running simulation", "Simulation running ...", $cancel_cmd); putmsg($cmdwin, $inittext, 'msg'); # Must appear before any other output # Set up the pipe reader callback my $reader = sub { run_dialog_reader($w, $fh, $cmdwin, \$state, \$success); }; $w->fileevent($fh, 'readable', $reader); $status_label->configure(-text => "Status: Running simulation"); my $savefocus = run_dialog_popup($dlg); do { $w->waitVariable(\$state); } until $state; run_dialog_retract($dlg, $savefocus); my $status = close($fh); $status_label->configure(-text => "Status: Done"); if(!$success || (! $status && ($? != 0 || $!))) { putmsg($cmdwin, "Simulation exited abnormally.\n"); return undef; } else { putmsg($cmdwin, "Simulation finished.\n", 'msg'); return 1; } } sub dialog_get_out_file { my ($w, $file, $force) = @_; # The $state variable is set when the spawned command finishes. my ($state, $cmd_success); my $success = 0; my ($fh, $pid, $out_name); # Initialize the dialog. my $cancel_cmd = sub { kill -15, $pid if $pid && !$state; # signal 15 is SIGTERM }; my $dlg = run_dialog_create($w, "Compiling simulation ...", "Compiling simulation", $cancel_cmd); my $printer = sub { putmsg($cmdwin, "$_[0]\n", 'msg'); }; # Set up the pipe reader callback $status_label->configure(-text => "Status: Compiling simulation"); # The dialog isn't actually popped up unless/until a command is # run or an error occurs. my $savefocus; my ($compile_data, $msg) = get_out_file_init($file, $force); if(!$compile_data) { &$printer("Could not compile simulation:\n$msg"); } else { $state = 0; for(;;) { my ($type, $val) = get_out_file_next($compile_data, $printer); if($type eq 'FINISHED') { $success = 1; $out_name = $val; last; } elsif($type eq 'RUN_CMD') { $savefocus = run_dialog_popup($dlg) unless $savefocus; my $fh = new FileHandle; # Open calls must be handled according to # platform... # PW 20030314 if ($Config{'osname'} eq 'MSWin32') { $pid = open($fh, "@$val 2>&1 |"); } else { $pid = open($fh, "-|"); } unless(defined($pid)) { &$printer("Could not spawn command."); last; } if($pid) { # Parent $state = 0; # Clear "command done" flag. $cmd_success = 0; my $reader = sub { run_dialog_reader($w, $fh, $cmdwin, \$state, \$cmd_success); }; $dlg->fileevent($fh, 'readable', $reader); do { $dlg->waitVariable(\$state); } until $state; my $ret = close($fh); undef($pid); unless($cmd_success && ($ret || ($? == 0 && ! $!))) { &$printer("** Error exit **."); last; } } else { # Child open(STDERR, ">&STDOUT") || die "Can't dup stdout"; # Make the child the process group leader, so that # we can kill off any subprocesses it may have # spawned when the user selects CANCEL. setpgrp(0,0); exec @$val if @$val; # The "if @$val" avoids a Perl warning. # If we get here, the exec() failed. print STDERR "Error: exec() of $val->[0] failed!\n"; POSIX::_exit(1); # CORE:exit needed to avoid Perl/Tk failure. } } elsif($type eq 'ERROR') { &$printer("Error: $msg"); last; } elsif($type eq 'CONTINUE') { next; } else { die "Internal: compile_dialog(): $type, $msg"; } } } run_dialog_retract($dlg, $savefocus); my $donetype = $success ? "Done" : "Compile failed"; $status_label->configure(-text => "Status: $donetype"); &$printer("$donetype.") unless $success && !$savefocus; return $success ? $out_name : undef; } sub compile_instrument { my ($w, $force) = @_; return undef unless ask_save_before_simulate($w); my $out_name = dialog_get_out_file($w, $current_sim_def, $force); unless($out_name && -x $out_name) { $w->messageBox(-message => "Could not compile simulation.", -title => "Compile failed", -type => 'OK', -icon => 'error'); return undef; } return $out_name; } sub menu_compile{ my ($w) = @_; unless($current_sim_def) { $w->messageBox(-message => "No simulation definition loaded.", -title => "Compilation error", -type => 'OK', -icon => 'error'); return undef; } compile_instrument($w, 1); # Force recompilation. return 1; } sub my_system { my ($w, $inittext, @sysargs) = @_; my $fh = new FileHandle; my $child_pid; # Open calls must be handled according to # platform... # PW 20030314 if ($Config{'osname'} eq 'MSWin32') { $child_pid = open($fh, "@sysargs 2>&1 |"); } else { $child_pid = open($fh, "-|"); } unless(defined($child_pid)) { $w->messageBox(-message => "Could not run simulation.", -title => "Run failed", -type => 'OK', -icon => 'error'); return undef; } if($child_pid) { # Parent return run_dialog($w, $fh, $child_pid, $inittext); } else { # Child open(STDERR, ">&STDOUT") || die "Can't dup stdout"; # Make the child the process group leader, so that # we can kill off any subprocesses it may have # spawned when the user selects CANCEL. setpgrp(0,0); exec @sysargs if @sysargs; # The "if @sysargs" avoids a Perl warning. # If we get here, the exec() failed. print STDERR "Error: exec() of $sysargs[0] failed!\n"; POSIX::_exit(1); # CORE:exit needed to avoid Perl/Tk failure. } } sub menu_run_simulation { my ($w) = @_; unless($current_sim_def) { return undef unless menu_open($w); } my $out_name = compile_instrument($w); return 0 unless $out_name; # Attempt to avoid problem with missing "." in $PATH. Unix only. if (!($Config{'osname'} eq 'MSWin32')) { unless($out_name =~ "/") { $out_name = "./$out_name"; } } my $out_info = get_sim_info($out_name); unless($out_info) { $w->messageBox(-message => "Could not run simulation.", -title => "Run failed", -type => 'OK', -icon => 'error'); return 0; } my ($bt, $newsi) = simulation_dialog($w, $out_info, $inf_sim); if($bt eq 'Start') { my @command = (); # Check 'Plotter' setting my $plotter = $MCSTAS::mcstas_config{'PLOTTER'}; # Check 'Trace' setting if a scan or trace is # requested if ($newsi->{'Trace'}) { # Here, a check is done for selected mcdisplay "backend" # Also, various stuff must be done differently on unix # type plaforms and on lovely Win32... :) # PW 20030314 # # Check if this is Win32, call perl accordingly... if ($Config{'osname'} eq 'MSWin32') { if ($newsi->{'Trace'} eq 1 ) { # Win32 'start' command needed to background the process.. push @command, "$MCSTAS::mcstas_config{'PREFFIX'}"; # Also, disable plotting of results after mcdisplay run... $newsi->{'Autoplot'}=0; } } # 'mcdisplay' trace mode push @command, "${prefix}mcdisplay$suffix"; if ($plotter =~ /PGPLOT|McStas/i) { push @command, "--plotter=PGPLOT"; # Users seem to dislike multi-views with mcdisplay/PGPLOT # We ought to have a switchbutton somewhere controlling this # push @command, "--multi"; # Be sure to read mcplotlib.pl in this case... require "mcplotlib.pl"; # Standard mcdisplay.pl with PGPLOT bindings # Make sure the PGPLOT server is already started. If the # PGPLOT server is not started, mcdisplay will start it, # and the server will keep the pipe to mcdisplay open # until the server exits, hanging mcgui. ensure_pgplot_xserv_started(); } elsif ($plotter =~ /Matlab/i && $plotter =~ /scriptfile/i) { push @command, "--plotter=Matlab"; my $output_file = save_disp_file($w,'m'); if (!$output_file) { putmsg($cmdwin, "Trace cancelled...\n"); return; } $output_file = "\"$output_file\""; push @command, "-f$output_file"; } elsif ($plotter =~ /Matlab/i) { push @command, "--plotter=Matlab"; } elsif ($plotter =~ /Scilab/i && $plotter =~ /scriptfile/i) { push @command, "--plotter=Scilab"; my $output_file = save_disp_file($w,'sci'); if (!$output_file) { putmsg($cmdwin, "Trace cancelled...\n"); return; } $output_file =~ s! !\ !g; push @command, "-f$output_file"; } elsif ($plotter =~ /Scilab/i) { push @command, "--plotter=Scilab"; # If this is Win32, make a check for # of neutron histories, # should be made small to avoid waiting a long time for # mcdisplay... if ($Config{'osname'} eq "MSWin32") { # Subtract 0 to make sure $num_histories is treated as a # number... my $num_histories = $newsi->{'Ncount'} - 0; if ($num_histories >=1e3) { my $break = $w->messageBox(-message => "$num_histories is a very large number\nof neutron histories when using\nScilab on Win32.\nContinue ?", -title => "Warning: large number", -type => 'yesnocancel', -icon => 'error', -default => 'no'); # Make first char lower case - default on # Win32 upper case default on Unix... (perl 5.8) $break = lcfirst($break); if ((lc($break) eq "no")||(lc($break) eq "cancel")) { return 0; } } } } push @command, "-i$newsi->{'Inspect'}" if $newsi->{'Inspect'}; push @command, "--first=$newsi->{'First'}" if $newsi->{'First'}; push @command, "--last=$newsi->{'Last'}" if $newsi->{'Last'}; # push @command, "--save" if ($newsi->{'Trace'} eq 1); } push @command, "$MCSTAS::mcstas_config{'prefix'}mcrun$suffix" if $newsi->{'NScan'} > 1 && !$newsi->{'Trace'}; push @command, "$out_name"; my ($OutDir,$OutDirBak); # In the special case of --dir, we simply replace ' ' with '_' # on Win32 (also giving out a warning message). This is done # because Win32::GetShortPathName only works on directories that # actually exist... :( if ($newsi->{'Dir'}) { $OutDir=$newsi->{'Dir'}; if ($Config{'osname'} eq 'MSWin32') { $OutDirBak = $OutDir; $OutDir =~ s! !_!g; if (! "$OutDir" == "$OutDirBak") { putmsg($cmdwin, "You have requested output directory \"$OutDirBak\"\n"); putmsg($cmdwin, "For compatibility reasons, spaces are replaced by underscores.\n"); putmsg($cmdwin, "Your output files will go to \"$OutDir\"\n"); $newsi->{'Dir'} = $OutDir; } } else { $OutDir =~ s! !\ !g; } } if ($newsi->{'mpi'} > 1) { push @command, "--mpi=$newsi->{'mpi'}"; } elsif ($newsi->{'Multi'} > 0) { push @command, "--multi"; } push @command, "--ncount=$newsi->{'Ncount'}"; push @command, "--trace" if ($newsi->{'Trace'} eq 1); push @command, "--seed=$newsi->{'Seed'}" if $newsi->{'Seed'}; push @command, "--dir=$OutDir" if $newsi->{'Dir'}; if ($newsi->{'Force'} eq 1) { if (-e $OutDir) { rmtree($OutDir,0,1); } } push @command, "--format='$plotter'"; my @unset = (); my @multiple = (); for (@{$out_info->{'Parameters'}}) { if (length($newsi->{'Params'}{$_})>0) { # Check for comma separated values my @values = split(',',$newsi->{'Params'}{$_}); my $value = $newsi->{'Params'}{$_}; if (@values > 1) { @multiple = (@multiple, $_); if ($newsi->{'Trace'} eq 1 || $newsi->{'NScan'} < 2 || $newsi->{'NScan'} eq ''){ my $j; my $meanvalue=0; for ($j=0; $j<@values; $j++) { $meanvalue = $values[$j]; } $meanvalue = $meanvalue / @values; $value = $meanvalue; } } push @command, "$_=$value"; } else { push @unset, $_; } } if (@unset>0) { $w->messageBox(-message => "Unset parameter(s):\n\n@unset\n\nPlease fill all fields!", -title => "Unset parameters!", -type => 'OK', -icon => 'error'); return; } if (@multiple > 0 && ($newsi->{'Trace'} eq 1 || $newsi->{'NScan'} < 2 || $newsi->{'NScan'} eq '')) { $w->messageBox(-message => "Scan range(s) not applicable. Mean value subsituted for parameter(s):\n\n@multiple", -title => "No scan here!", -type => 'OK', -icon => 'info'); } if (@multiple eq 0 && $newsi->{'NScan'} > 1) { if (!$newsi->{'Trace'}) { $w->messageBox(-message => "No scan range(s) given! Performing single simulation", -title => "No scan here!", -type => 'OK', -icon => 'info'); $newsi->{'NScan'} = 0; } } if ($newsi->{'gravity'} eq 1 && !$newsi->{'Trace'}) { if ($newsi->{'GravityWarn'} eq 0) { $w->messageBox(-message => "Only use --gravitation with components that support this!", -title => "BEWARE!", -type => 'OK', -icon => 'warning'); $newsi->{'GravityWarn'} = 1; } push @command, "--gravitation"; } push @command, "-N$newsi->{'NScan'}" if $newsi->{'NScan'} > 1 && !$newsi->{'Trace'} ; my $inittext = "Running simulation '$out_name' ...\n" . join(" ", @command) . "\n"; my $success = my_system $w, $inittext, @command; $inf_sim=$newsi; return unless $success; my $ext; if ($plotter =~ /PGPLOT|McStas/i) { $ext="sim"; } elsif ($plotter =~ /Matlab/i) { $ext="m"; } elsif ($plotter =~ /Scilab/i) { $ext="sci"; } elsif ($plotter =~ /HTML/i) { $ext="html"; } $current_sim_file = $newsi->{'Dir'} ? "$newsi->{'Dir'}/mcstas.$ext" : "mcstas.$ext"; new_simulation_results($w); # In case of non-PGPLOT plotter, we can not read the data from disk. # Instead, we simply keep $newsi information in $inf_sim if ($plotter eq 0) { read_sim_data($w); } else { $inf_sim=$newsi; } $inf_sim->{'Autoplot'} = $newsi->{'Autoplot'}; $inf_sim->{'Trace'} = $newsi->{'Trace'}; if ($newsi->{'Autoplot'}) { # Is beeing set to 0 above if Win32 + trace plot_dialog($w, $inf_instr, $inf_sim, $inf_data, $current_sim_file); } } } sub menu_plot_results { my ($w) = @_; unless($current_sim_file) { my $ret = load_sim_file($w); return 0 unless $ret && -e $current_sim_file; } plot_dialog($w, $inf_instr, $inf_sim, $inf_data, $current_sim_file); return 1; } sub menu_preferences { # sub for selection of mcdisplay "backend". # Default read from $MCSTAS::mcstas_config{'PLOTTER'} # PW 20030314 # Added entry for selection of internal editor # PW 20040527 my ($w) = @_; my $ret; ($ret) = preferences_dialog($w); } sub menu_read_sim_file { my ($w) = @_; load_sim_file($w); menu_plot_results($w); } # Build the text (McStas metalanguage) representation of a component # using data fillied in by the user. sub make_comp_inst { my ($cdata, $r) = @_; my ($p, $s); $s = "\n"; $s .= "COMPONENT $r->{'INSTANCE'} = $r->{'DEFINITION'}(\n"; my @ps = (); my $col = ""; for $p (@{$cdata->{'inputpar'}}) { my $add; my @p_splitted = split(" ", $p); my $length = scalar @p_splitted; my $p_last_word = $p_splitted[$length-1]; if(defined($r->{'VALUE'}{$p}) && $r->{'VALUE'}{$p} !~ /^\s*$/) { # Take care of the special case where the parameter is 'filename' if (lc($p) eq 'filename') { # Firstly, remove existing quotes :) $r->{'VALUE'}{$p} =~ s!\"!!g; $r->{'VALUE'}{$p} =~ s!\'!!g; # Next, add quotes... $r->{'VALUE'}{$p} = "\"$r->{'VALUE'}{$p}\""; } $add .= "$p_last_word = $r->{'VALUE'}{$p}"; } elsif(defined($cdata->{'parhelp'}{$p}{'default'})) { next; # Omit non-specified default parameter } else { $add.= "$p_last_word = "; } if(length($col) > 0) { if(length("$col, $add") > 60) { push @ps, $col; $col = $add; } else { $col = "$col, $add"; } } else { $col = $add; } } push @ps, $col if length($col) > 0; $s .= " " . join(",\n ", @ps) . ")\n"; $s .= " AT (". $r->{'AT'}{'x'} . ", " . $r->{'AT'}{'y'} . ", " . $r->{'AT'}{'z'} . ") RELATIVE " . $r->{'AT'}{'relative'} . "\n"; $s .= " ROTATED (" . $r->{'ROTATED'}{'x'} . ", " . $r->{'ROTATED'}{'y'} . ", " . $r->{'ROTATED'}{'z'} . ") RELATIVE " . $r->{'ROTATED'}{'relative'} . "\n" if($r->{'ROTATED'}{'x'} || $r->{'ROTATED'}{'y'} || $r->{'ROTATED'}{'z'} || $r->{'ROTATED'}{'relative'}); return $s; } # The text for the instrument template. my $instr_template_start = < * * %Parameters * Par1: [unit] Parameter1 description * * %Link * A reference/HTML link for more information * * %End *******************************************************************************/ /* Change name of instrument and input parameters with default values */ DEFINE INSTRUMENT test(Par1=1) /* The DECLARE section allows us to declare variables or small */ /* functions in C syntax. These may be used in the whole instrument. */ DECLARE %{ %} /* The INITIALIZE section is executed when the simulation starts */ /* (C code). You may use them as component parameter values. */ INITIALIZE %{ %} /* Here comes the TRACE section, where the actual */ /* instrument is defined as a sequence of components. */ TRACE /* The Arm() class component defines reference points and orientations */ /* in 3D space. Every component instance must have a unique name. Here, */ /* Origin is used. This Arm() component is set to define the origin of */ /* our global coordinate system (AT (0,0,0) ABSOLUTE). It may be used */ /* for further RELATIVE reference, Other useful keywords are : ROTATED */ /* EXTEND GROUP PREVIOUS. Also think about adding a neutron source ! */ /* Progress_bar is an Arm displaying simulation progress. */ COMPONENT Origin = Progress_bar() AT (0,0,0) ABSOLUTE INSTR_FINISH my $instr_template_end = <insert('1.0', $instr_template_start); # Save the current cursor position so that we can move it to # before the last part of the template if necessary. my $currentpos = $edit_control->index('insert'); $edit_control->insert('end', $instr_template_end); $edit_control->markSet('insert', $currentpos); if (not $current_sim_def) { $edit_window->title("Edit: Insert components in TRACE and save your instrument"); } } } # Allow the user to populate a given component definition in a dialog # window, and produce a corresponding component instance. sub menu_insert_x { my ($w, $path) = @_; my $cdata = fetch_comp_info($path, $compinfo); my $r = comp_instance_dialog($w, $cdata); return undef unless $r; die "No values given" unless $r; if($edit_control) { $edit_control->see('insert'); $edit_control->insert('insert', make_comp_inst($cdata, $r)); $edit_control->see('insert'); } return 1; } # Choose a component definition from a list in a dialog window, and # then allow the user to populate it in another dialog. sub menu_insert_component { my ($w) = @_; my $comp = comp_select_dialog($w, \@compdefs, $compinfo); return undef unless $comp; return menu_insert_x($w, $comp); } # Directories containing component definitions. # MOD: E. Farhi, Oct 2nd, 2001: add obsolete dir. Aug 27th, 2002: contrib my @comp_sources = (["Source", ["$MCSTAS::sys_dir/sources"]], ["Optics", ["$MCSTAS::sys_dir/optics"]], ["Sample", ["$MCSTAS::sys_dir/samples"]], ["Monitor", ["$MCSTAS::sys_dir/monitors"]], ["Misc", ["$MCSTAS::sys_dir/misc"]], ["Contrib", ["$MCSTAS::sys_dir/contrib"]], ["Obsolete", ["$MCSTAS::sys_dir/obsolete"]], ["Other", ["$MCSTAS::sys_dir", "."]]); # Fill out the menu for building component instances. sub make_insert_menu { my ($w, $menu) = @_; @compdefs = (); my @menudefs = (); my ($sec,$dir); for $sec (@comp_sources) { my $sl = [$sec->[0], []]; for $dir (@{$sec->[1]}) { if(opendir(DIR, $dir)) { my @comps = readdir(DIR); closedir DIR; next unless @comps; my @paths = map("$dir/$_", grep(/\.(comp|cmp|com)$/, @comps)); push(@compdefs, @paths); push(@{$sl->[1]}, map([compname($_), $_], @paths)); } } push @menudefs, $sl; } $menu->command(-label => "Instrument template", -command => sub { menu_insert_instr_template($w) }, -underline => 0); $menu->command(-label => "Component ...", -accelerator => 'Alt+M', -command => sub { menu_insert_component($w) }, -underline => 0); $w->bind('' => sub { menu_insert_component($w) }); # Now build all the menu entries for direct selection of component # definitions. my $p; for $p (@menudefs) { # $p holds title and component list my $m2 = $menu->cascade(-label => $p->[0]); my $c; for $c (@{$p->[1]}) { # $c holds name and path $m2->command(-label => "$c->[0] ...", -command => sub { menu_insert_x($w, $c->[1]) }); } } $menu->pack(-side=>'left'); } sub setup_menu { my ($w) = @_; my $menu = $w->Frame(-relief => 'raised', -borderwidth => 2); $menu->pack(-fill => 'x'); my $filemenu = $menu->Menubutton(-text => 'File', -underline => 0); $filemenu->command(-label => 'Open instrument ...', -accelerator => 'Alt+O', -command => [\&menu_open, $w], -underline => 0); $w->bind('' => [\&menu_open, $w]); $filemenu->command(-label => 'Edit current/New', -underline => 0, -command => \&menu_edit_current); if($external_editor) { my $shortname = (split " ", $external_editor)[0]; $shortname = (split "/", $shortname)[-1]; $filemenu->command(-label => 'Spawn editor "' . $shortname . '"', -command => sub { menu_spawn_editor($w) } ); } $filemenu->command(-label => 'Compile instrument', -underline => 0, -command => sub {menu_compile($w)}); $filemenu->command(-label => 'Clear output', -underline => 1, -command => sub { $cmdwin->delete("1.0", "end") }); $filemenu->separator; $filemenu->command(-label => 'Quit', -underline => 0, -accelerator => 'Alt+Q', -command => \&menu_quit); $w->bind('' => \&menu_quit); $filemenu->pack(-side=>'left'); my $simmenu = $menu->Menubutton(-text => 'Simulation', -underline => 2); $simmenu->command(-label => 'Read old simulation ...', -underline => 0, -command => sub { menu_read_sim_file($w) }); $simmenu->separator; $simmenu->command(-label => 'Run simulation ...', -underline => 1, -accelerator => 'Alt+U', -command => sub {menu_run_simulation($w);}); $w->bind('' => [\&menu_run_simulation, $w]); $simmenu->command(-label => 'Plot results ...', -underline => 0, -accelerator => 'Alt+P', -command => sub {menu_plot_results($w);}); $w->bind('' => [\&menu_plot_results, $w]); $simmenu->separator; $simmenu->command(-label => 'Configuration options', -underline => 0, -accelerator => 'Alt+C', -command => sub {menu_preferences($w);}); $w->bind('' => [\&menu_preferences, $w]); $simmenu->pack(-side=>'left'); sitemenu_build($w,$menu); my $helpmenu = $menu->Menubutton(-text => 'Help (McDoc)', -underline => 0); $helpmenu->command(-label => 'McStas User manual', -command => sub {mcdoc_manual()}); $helpmenu->command(-label => 'McStas Component manual', -command => sub {mcdoc_compman()}); $helpmenu->command(-label => 'Component Library index', -command => sub {mcdoc_components()}); $helpmenu->separator; $helpmenu->command(-label => 'McStas web page (web)', -underline => 7, -command => sub {mcdoc_web()}); $helpmenu->command(-label => 'McStas tutorial', -command => sub {mcdoc_tutorial()}); $helpmenu->command(-label => 'Current instrument info', -command => sub {mcdoc_current()}); $helpmenu->separator; $helpmenu->command(-label => 'Test McStas installation', -command => sub {mcdoc_test($w)}); $helpmenu->command(-label => 'Generate component index', -command => sub {mcdoc_generate()}); $helpmenu->command(-label => 'About McStas', -command => sub {mcdoc_about($w)}); $helpmenu->pack(-side=>'right'); } sub setup_cmdwin { my ($w) = @_; my $f2 = $w->Frame(); $f2->pack(-fill => 'x'); my $instr_lab = $f2->Label(-text => "Instrument file: ", -anchor => 'w', -justify => 'left'); $instr_lab->pack(-side => 'left'); my $instr_run = $f2->Button(-text => "Run", -command => sub { menu_run_simulation($w) }); $instr_run->pack(-side => "right", -padx => 1, -pady => 1); my $instr_but = $f2->Button(-text => "Edit/New", -command => \&menu_edit_current); $instr_but->pack(-side => "right", -padx => 1, -pady => 1); my $f3 = $w->Frame(); $f3->pack(-fill => 'x'); my $res_lab = $f3->Label(-text => "Simulation results: ", -anchor => 'w', -justify => 'left'); $res_lab->pack(-side => 'left'); my $plot_but = $f3->Button(-text => "Plot", -command => sub {menu_plot_results($w);}); $plot_but->pack(-side => "right", -padx => 1, -pady => 1); my $sim_but = $f3->Button(-text => "Read", -command => sub { menu_read_sim_file($w) }); $sim_but->pack(-side => "right", -padx => 1, -pady => 1); my $status_lab = $w->Label(-text => "Status: Ok", -anchor => 'w', -justify => 'left'); $status_lab->pack(-fill => 'x'); # Add the main text field, with scroll bar my $rotext = $w->ROText(-relief => 'sunken', -bd => '2', -setgrid => 'true', -height => 24, -width => 80); my $s = $w->Scrollbar(-command => [$rotext, 'yview']); $rotext->configure(-yscrollcommand => [$s, 'set']); $s->pack(-side => 'right', -fill => 'y'); $rotext->pack(-expand => 'yes', -fill => 'both'); $rotext->mark('set', 'insert', '0.0'); $rotext->tagConfigure('msg', -foreground => 'blue'); $current_instr_label = $instr_lab; $current_results_label = $res_lab; $status_label = $status_lab; $cmdwin = $rotext; # Insert "mcstas --version" message in window. Do it a line at the # time, since otherwise the tags mechanism seems to get confused. my $l; for $l (split "\n", `mcstas --version`) { $cmdwin->insert('end', "$l\n", 'msg'); } if ($MCSTAS::mcstas_config{'MPIRUN'} ne "no" || $MCSTAS::mcstas_config{'SSH'} ne "no") { if ($MCSTAS::mcstas_config{'MPIRUN'} ne "no") { $cmdwin->insert('end', "MPI parallelisation is available\n"); } elsif ($MCSTAS::mcstas_config{'SSH'} ne "no") { $cmdwin->insert('end', "Distributed scans (grid) computing is available\n"); } if ($MCSTAS::mcstas_config{'HOSTFILE'} eq "") { $cmdwin->insert('end', "No MPI/grid machine list. MPI/grid disabled... Define $ENV{'HOME'}/.mcstas-hosts first.\n"); $MCSTAS::mcstas_config{'MPIRUN'} = "no"; $MCSTAS::mcstas_config{'SSH'} = "no"; } } if ($MCSTAS::mcstas_config{'SCILAB'} eq "no") { $cmdwin->insert('end', "Scilab plotter is NOT available\n"); } if ($MCSTAS::mcstas_config{'MATLAB'} eq "no") { $cmdwin->insert('end', "Matlab plotter is NOT available\n"); } if ($MCSTAS::mcstas_config{'PGPLOT'} eq "no") { $cmdwin->insert('end', "Perl/PGPLOT plotter is NOT available\n"); } } sub editor_quit { my ($w) = @_; if(is_erase_ok($w)) { $w->destroy; $edit_window = undef; $edit_control = undef; } } sub Tk::CodeText::selectionModify { my ($cw, $char, $mode) = @_; my @ranges = $cw->tagRanges('sel'); my $charlength = length($char); if (@ranges >= 2) { my $start = $cw->index($ranges[0]); my $end = $cw->index($ranges[1]); my $firststart = $start; while ($cw->compare($start, "<=", $end)) { if ($mode) { if ($cw->get("$start linestart", "$start linestart + $charlength chars") eq $char) { $cw->delete("$start linestart", "$start linestart + $charlength chars"); } } else { $cw->insert("$start linestart", $char); } $start = $cw->index("$start + 1 lines"); } if (!$mode) { @ranges = $cw->tagRanges('sel'); @ranges = ($firststart, $ranges[@ranges-1]); } $cw->tagAdd('sel', @ranges); } } sub setup_edit_1_7 { # BEWARE: The code in this sub is from McStas 1.7, # added only for those users unable to use the CodeText # based highlighting editor below. Other features are # also missing. my ($mw) = @_; # Create the editor window. my $w = $mw->Toplevel; my $e; # Create the editor menus. my $menu = $w->Frame(-relief => 'raised', -borderwidth => 2); $menu->pack(-fill => 'x'); my $filemenu = $menu->Menubutton(-text => 'File', -underline => 0); $filemenu->command(-label => 'New instrument', -command => [\&menu_new, $w], -underline => 0); $filemenu->command(-label => 'Save instrument', -accelerator => 'Alt+S', -command => [\&menu_save, $w], -underline => 0); $w->bind('' => [\&menu_save, $w]); $filemenu->command(-label => 'Save instrument as ...', -underline => 16, -command => sub {menu_saveas($w)}); $filemenu->separator; $filemenu->command(-label => 'Close', -underline => 0, -accelerator => 'Alt+C', -command => sub { editor_quit($w) } ); $w->bind('' => sub { editor_quit($w) } ); $filemenu->pack(-side=>'left'); my $editmenu = $menu->Menubutton(-text => 'Edit', -underline => 0); $editmenu->command(-label => 'Undo', -accelerator => 'Ctrl+Z', -command => [\&menu_undo, $w], -underline => 0); $w->bind('' => [\&menu_undo, $w]); $editmenu->separator; $editmenu->command(-label => 'Cut', -accelerator => 'Ctrl+X', -command => sub { $e->clipboardCut(); }, -underline => 0); $editmenu->command(-label => 'Copy', -accelerator => 'Ctrl+C', -command => sub { $e->clipboardCopy(); }, -underline => 1); $editmenu->command(-label => 'Paste', -accelerator => 'Ctrl+V', -command => sub { $e->clipboardPaste(); }, -underline => 0); $editmenu->pack(-side=>'left'); my $insert_menu = $menu->Menubutton(-text => 'Insert', -underline => 0); make_insert_menu($w, $insert_menu); # Create the editor text widget. $e = $w->TextUndo(-relief => 'sunken', -bd => '2', -setgrid => 'true', -height => 24); my $s = $w->Scrollbar(-command => [$e, 'yview']); $e->configure(-yscrollcommand => [$s, 'set']); $s->pack(-side => 'right', -fill => 'y'); $e->pack(-expand => 'yes', -fill => 'both'); $e->mark('set', 'insert', '0.0'); $e->Load($current_sim_def) if $current_sim_def && -r $current_sim_def; $w->protocol("WM_DELETE_WINDOW" => sub { editor_quit($w) } ); $edit_control = $e; $edit_window = $w; if ($current_sim_def) { $w->title("Edit: $current_sim_def"); if (-r $current_sim_def) { $e->Load($current_sim_def); } } else { $w->title("Edit: Start with Insert/Instrument template"); } } sub setup_edit { my ($mw) = @_; # Create the editor window. my $w = $mw->Toplevel; my $e; # Create the editor text widget. require Tk::CodeText; require Tk::CodeText::McStas; $e = $w->Scrolled('CodeText',-relief => 'sunken', -bd => '2', -setgrid => 'true', -height => 24, wrap => 'none', -scrollbars =>'se', -commentchar => '// ', -indentchar => " ", -updatecall => \&update_line, -syntax => 'McStas', -background => 'white'); my $menu = $e->menu; $w->bind('' => [\&Tk::CodeText::selectionIndent]); $w->bind('' => [\&Tk::CodeText::selectionUnIndent]); $w->bind('' => [\&Tk::CodeText::selectionComment]); $w->bind('' => [\&Tk::CodeText::selectionUnComment]); $w->configure(-menu => $menu); my $insert_menu = $menu->Menubutton(-text => 'Insert', -underline => 0, -tearoff => 0); # This is only done for backward compatibility - we want to use Alt+s for saving... my $filemenu = $menu->Menubutton(-text => 'Search', -underline => 1); $w->bind('' => [\&menu_save, $w]); make_insert_menu($w, $insert_menu); my $label = $w->Label(-bd => '1', -text => 'Current line: 1'); $e->pack(-expand => 'yes', -fill => 'both'); $label->pack(-side => 'left', -expand => 'no', -fill => 'x'); $e->mark('set', 'insert', '0.0'); $w->protocol("WM_DELETE_WINDOW" => sub { editor_quit($w) } ); $edit_control = $e; $edit_window = $w; $edit_label = $label; if ($current_sim_def) { $w->title("Edit: $current_sim_def"); if (-r $current_sim_def) { $e->Load($current_sim_def); } } else { $w->title("Edit: Start with Insert/Instrument template"); } } sub Tk::TextUndo::FileSaveAsPopup { my ($w)=@_; menu_saveas($w); } sub Tk::TextUndo::FileLoadPopup { my ($w)=@_; my $name = $w->CreateFileSelect('getOpenFile',-title => 'File Load'); if (defined($name) && length($name)){ open_instr_def($w, $name); return 1; } return 0; } # GUI callback function for updating line numbers etc. sub update_line { if (defined($edit_control)) { my ($line,$col) = split(/\./,$edit_control->index('insert')); my ($last_line,$last_col) = split(/\./,$edit_control->index('end')); $last_line=$last_line-1; $edit_label->configure(-text => " Line: $line of $last_line total, Column: $col"); } } # Check if simulation needs recompiling. sub check_if_need_recompile { my ($simname) = @_; my $exename; if($simname =~ /^(.*)\.(instr|ins)$/) { $exename = $1; } else { $exename = "$simname.$MCSTAS::mcstas_config{'EXE'}"; } return "not found" unless -f $exename; return "not executable" unless -x $exename; my @stat1 = stat($simname); my @stat2 = stat($exename); return "source is newer" unless $stat1[9] < $stat2[9]; return ""; } my $win = new MainWindow; $main_window = $win; setup_menu($win); setup_cmdwin($win); $inf_sim->{'ssh'} = ($MCSTAS::mcstas_config{'SSH'} ne "no" ? 1 : 0); $inf_sim->{'mpi'} = ($MCSTAS::mcstas_config{'MPIRUN'} ne "no" ? 1 : 0); my $open_editor = 0; if(@ARGV>0 && @ARGV<3) { # Check if one of the input arguments is '--open' # if so, start the editor of choice immediately my $j; my $filenames; for ($j=0; $j<@ARGV; $j++) { if ($ARGV[$j] eq "--open") { $open_editor = 1; # menu_edit_current($win); } else { $filenames = "$ARGV[$j]"; } } # Most likely, everything on the commandline is a filename... Join using # spaces, e.g. mcgui.pl My Documents\My Simulation.instr open_instr_def($win, $filenames); if ($open_editor == 1) { menu_edit_current($win); } } else { # menu_open($win); } MainLoop;