12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346 |
- #!/usr/bin/env perl
- #-------------------------------------------------------------------------------
- # (C) Crown copyright Met Office. All rights reserved.
- # For further details please refer to the file COPYRIGHT.txt
- # which you should have received as part of this distribution.
- #-------------------------------------------------------------------------------
- use strict;
- use warnings;
- use FindBin;
- use lib "$FindBin::Bin/../lib";
- use Cwd;
- use Fcm::Config;
- use Fcm::Keyword;
- use Fcm::Timer;
- use Fcm::Util;
- use File::Basename;
- use File::Spec;
- use Tk;
- use Tk::ROText;
- # ------------------------------------------------------------------------------
- # Argument
- if (@ARGV) {
- my $dir = shift @ARGV;
- chdir $dir if -d $dir;
- }
- # Get configuration settings
- my $config = Fcm::Config->new ();
- $config->get_config ();
- # ------------------------------------------------------------------------------
- # FCM subcommands
- my @subcmds = qw/CHECKOUT BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT
- UPDATE SWITCH/;
- # Subcommands allowed when CWD is not a WC
- my @nwc_subcmds = qw/CHECKOUT BRANCH/;
- # Subcommands allowed, when CWD is a WC
- my @wc_subcmds = qw/STATUS BRANCH DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
- SWITCH/;
- # Subcommands that apply to WC only
- my @wco_subcmds = qw/BRANCH STATUS DIFF ADD DELETE MERGE CONFLICTS COMMIT UPDATE
- SWITCH/;
- # Subcommands that apply to top level WC only
- my @wcto_subcmds = qw/BRANCH MERGE COMMIT UPDATE SWITCH/;
- # Selected subcommand
- my $selsubcmd = '';
- # Selected subcommand is running?
- my $cmdrunning = 0;
- # PID of running subcommand
- my $cmdpid = undef;
- # List of subcommand frames
- my %subcmd_f;
- # List of subcommand buttons
- my %subcmd_b;
- # List of subcommand button help strings
- my %subcmd_help = (
- BRANCH => 'list information about, create or delete a branch.',
- CHECKOUT => 'check out a working copy from a repository.',
- STATUS => 'print the status of working copy files and directories.',
- DIFF => 'display the differences in modified files.',
- ADD => 'put files and directories under version control.',
- DELETE => 'remove files and directories from version control.',
- MERGE => 'merge changes into your working copy.',
- CONFLICTS => 'use a graphical tool to resolve conflicts in your working copy.',
- COMMIT => 'send changes from your working copy to the repository.',
- UPDATE => 'bring changes from the repository into your working copy.',
- SWITCH => 'update your working copy to a different URL.',
- );
- for (keys %subcmd_help) {
- $subcmd_help{$_} = 'Select the "' . lc ($_) . '" sub-command - ' .
- $subcmd_help{$_};
- }
- # List of subcommand button bindings (key name and underline position)
- my %subcmd_bind = (
- BRANCH => {KEY => '<Alt-Key-b>', U => 0},
- CHECKOUT => {KEY => '<Alt-Key-o>', U => 5},
- STATUS => {KEY => '<Alt-Key-s>', U => 0},
- DIFF => {KEY => '<Alt-Key-d>', U => 0},
- ADD => {KEY => '<Alt-Key-a>', U => 0},
- DELETE => {KEY => '<Alt-Key-t>', U => 4},
- MERGE => {KEY => '<Alt-Key-m>', U => 0},
- CONFLICTS => {KEY => '<Alt-Key-f>', U => 3},
- COMMIT => {KEY => '<Alt-Key-c>', U => 0},
- UPDATE => {KEY => '<Alt-Key-u>', U => 0},
- SWITCH => {KEY => '<Alt-Key-w>', U => 1},
- );
- # List of subcommand variables
- my %subcmdvar = (
- CWD => cwd (),
- WCT => '',
- CWD_URL => '',
- WCT_URL => '',
- BRANCH => {
- OPT => 'info',
- URL => '',
- NAME => '',
- TYPE => 'DEV',
- REVFLAG => 'NORMAL',
- REV => '',
- TICKET => '',
- SRCTYPE => 'trunk',
- S_CHD => 0,
- S_SIB => 0,
- S_OTH => 0,
- VERBOSE => 0,
- OTHER => '',
- },
- CHECKOUT => {
- URL => '',
- REV => 'HEAD',
- PATH => '',
- OTHER => '',
- },
- STATUS => {
- USEWCT => 0,
- UPDATE => 0,
- VERBOSE => 0,
- OTHER => '',
- },
- DIFF => {
- USEWCT => 0,
- TOOL => 'graphical',
- BRANCH => 0,
- URL => '',
- OTHER => '',
- },
- ADD => {
- USEWCT => 0,
- CHECK => 1,
- OTHER => '',
- },
- DELETE => {
- USEWCT => 0,
- CHECK => 1,
- OTHER => '',
- },
- MERGE => {
- USEWCT => 1,
- SRC => '',
- MODE => 'automatic',
- DRYRUN => 0,
- VERBOSE => 0,
- REV => '',
- OTHER => '',
- },
- CONFLICTS => {
- USEWCT => 0,
- OTHER => '',
- },
- COMMIT => {
- USEWCT => 1,
- DRYRUN => 0,
- OTHER => '',
- },
- UPDATE => {
- USEWCT => 1,
- OTHER => '',
- },
- SWITCH => {
- USEWCT => 1,
- URL => '',
- OTHER => '',
- },
- );
- # List of action buttons
- my %action_b;
- # List of action button help strings
- my %action_help = (
- QUIT => 'Quit fcm gui',
- HELP => 'Print help to the output text box for the selected sub-command',
- CLEAR => 'Clear the output text box',
- RUN => 'Run the selected sub-command',
- );
- # List of action button bindings
- my %action_bind = (
- QUIT => {KEY => '<Control-Key-q>', U => undef},
- HELP => {KEY => '<F1>' , U => undef},
- CLEAR => {KEY => '<Alt-Key-l>' , U => 1},
- RUN => {KEY => '<Alt-Key-r>' , U => 0},
- );
- # List of branch subcommand options
- my %branch_opt = (
- INFO => undef,
- CREATE => undef,
- DELETE => undef,
- LIST => undef,
- );
- # List of branch create types
- my %branch_type = (
- 'DEV' => undef,
- 'DEV::SHARE' => undef,
- 'TEST' => undef,
- 'TEST::SHARE' => undef,
- 'PKG' => undef,
- 'PKG::SHARE' => undef,
- 'PKG::CONFIG' => undef,
- 'PKG::REL' => undef,
- );
- # List of branch create source type
- my %branch_srctype = (
- TRUNK => undef,
- BRANCH => undef,
- );
- # List of branch create revision prefix option
- my %branch_revflag = (
- NORMAL => undef,
- NUMBER => undef,
- NONE => undef,
- );
- # List of branch info/delete options
- my %branch_info_opt = (
- S_CHD => 'Show children',
- S_SIB => 'Show siblings',
- S_OTH => 'Show other',
- VERBOSE => 'Print extra information',
- );
- # List of diff display options
- my %diff_display_opt = (
- default => 'Default mode',
- graphical => 'Graphical tool',
- trac => 'Trac (only for diff relative to the base of the branch)',
- );
- # Text in the status bar
- my $statustext = '';
- # ------------------------------------------------------------------------------
- my $mw = MainWindow->new ();
- my $mw_title = 'FCM GUI';
- $mw->title ($mw_title);
- # Frame containing subcommand selection buttons
- my $top_f = $mw->Frame ()->grid (
- '-row' => 0,
- '-column' => 0,
- '-sticky' => 'w',
- );
- # Frame containing subcommand options
- my $mid_f = $mw->Frame ()->grid (
- '-row' => 1,
- '-column' => 0,
- '-sticky' => 'ew',
- );
- # Frame containing action buttons
- my $bot_f = $mw->Frame ()->grid (
- '-row' => 2,
- '-column' => 0,
- '-sticky' => 'ew',
- );
- # Text box to display output
- my $out_t = $mw->Scrolled ('ROText', '-scrollbars' => 'osow')->grid (
- '-row' => 3,
- '-column' => 0,
- '-sticky' => 'news',
- );
- # Text box - allow scroll with mouse wheel
- $out_t->bind (
- '<4>' => sub {
- $_[0]->yview ('scroll', -1, 'units') unless $Tk::strictMotif;
- },
- );
- $out_t->bind (
- '<5>' => sub {
- $_[0]->yview ('scroll', +1, 'units') unless $Tk::strictMotif;
- },
- );
- # Status bar
- $mw->Label (
- '-textvariable' => \$statustext,
- '-relief' => 'groove',
- )->grid (
- '-row' => 4,
- '-column' => 0,
- '-sticky' => 'ews',
- );
- # Main window grid configure
- {
- my ($cols, $rows) = $mw->gridSize ();
- $mw->gridColumnconfigure ($_, '-weight' => 1) for (0 .. $cols - 1);
- $mw->gridRowconfigure ( 3, '-weight' => 1);
- }
- # Frame grid configure
- {
- my ($cols, $rows) = $mid_f->gridSize ();
- $bot_f->gridColumnconfigure (3, '-weight' => 1);
- }
- $mid_f->gridRowconfigure (0, '-weight' => 1);
- $mid_f->gridColumnconfigure (0, '-weight' => 1);
- # ------------------------------------------------------------------------------
- # Buttons to select subcommands
- {
- my $col = 0;
- for my $name (@subcmds) {
- $subcmd_b{$name} = $top_f->Button (
- '-text' => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
- '-command' => [\&button_clicked, $name],
- '-width' => 8,
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- $subcmd_b{$name}->bind ('<Enter>', sub {$statustext = $subcmd_help{$name}});
- $subcmd_b{$name}->bind ('<Leave>', sub {$statustext = ''});
- $subcmd_b{$name}->configure ('-underline' => $subcmd_bind{$name}{U})
- if defined $subcmd_bind{$name}{U};
- $mw->bind ($subcmd_bind{$name}{KEY}, sub {$subcmd_b{$name}->invoke});
- }
- }
- # ------------------------------------------------------------------------------
- # Frames to contain subcommands options
- {
- my %row = ();
- for my $name (@subcmds) {
- $subcmd_f{$name} = $mid_f->Frame ();
- $subcmd_f{$name}->gridColumnconfigure (1, '-weight' => 1);
- $row{$name} = 0;
- # Widgets common to all sub-commands
- $subcmd_f{$name}->Label ('-text' => 'Current working directory: ')->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Label ('-textvariable' => \($subcmdvar{CWD}))->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- }
- # Widgets common to all sub-commands that apply to working copies
- for my $name (@wco_subcmds) {
- my @labtxts = (
- 'Corresponding URL: ',
- 'Working copy top: ',
- 'Corresponding URL: ',
- );
- my @varrefs = \(
- $subcmdvar{URL_CWD},
- $subcmdvar{WCT},
- $subcmdvar{URL_WCT},
- );
- for my $i (0 .. $#varrefs) {
- $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Label ('-textvariable' => $varrefs[$i])->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- }
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Apply sub-command to working copy top',
- '-variable' => \($subcmdvar{$name}{USEWCT}),
- '-state' => (grep ({$_ eq $name} @wcto_subcmds) ? 'disabled' : 'normal'),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- }
- # Widget for the Branch sub-command
- {
- my $name = 'BRANCH';
- # Radio buttons to select the sub-option of the branch sub-command
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (sort keys %branch_opt) {
- my $opt = lc $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $opt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{OPT}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- # Label and entry box for specifying URL
- $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{URL}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- # Label and entry box for specifying create branch name
- $subcmd_f{$name}->Label (
- '-text' => 'Branch name (create only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{NAME}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- # Label and entry box for specifying create branch source revision
- $subcmd_f{$name}->Label (
- '-text' => 'Source revision (create/list only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{REV}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- # Label and radio buttons box for specifying create branch type
- $subcmd_f{$name}->Label (
- '-text' => 'Branch type (create only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (sort keys %branch_type) {
- my $txt = lc $key;
- my $opt = $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $txt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{TYPE}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- # Label and radio buttons box for specifying create source type
- $subcmd_f{$name}->Label (
- '-text' => 'Source type (create only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (sort keys %branch_srctype) {
- my $txt = lc $key;
- my $opt = lc $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $txt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{SRCTYPE}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- # Label and radio buttons box for specifying create prefix option
- $subcmd_f{$name}->Label (
- '-text' => 'Prefix option (create only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (sort keys %branch_revflag) {
- my $txt = lc $key;
- my $opt = $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $txt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{REVFLAG}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- # Label and entry box for specifying ticket number
- $subcmd_f{$name}->Label (
- '-text' => 'Related Trac ticket(s) (create only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{TICKET}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- # Check button for info/delete
- # --show-children, --show-siblings, --show-other, --verbose
- $subcmd_f{$name}->Label (
- '-text' => 'Options for info/delete only: ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (sort keys %branch_info_opt) {
- $opt_f->Checkbutton (
- '-text' => $branch_info_opt{$key},
- '-variable' => \($subcmdvar{$name}{$key}),
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- }
- # Widget for the Checkout sub-command
- {
- my $name = 'CHECKOUT';
- # Label and entry boxes for specifying URL and revision
- my @labtxts = (
- 'URL: ',
- 'Revision: ',
- 'Path: ',
- );
- my @varrefs = \(
- $subcmdvar{$name}{URL},
- $subcmdvar{$name}{REV},
- $subcmdvar{$name}{PATH},
- );
- for my $i (0 .. $#varrefs) {
- $subcmd_f{$name}->Label ('-text' => $labtxts[$i])->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => $varrefs[$i],
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- }
- }
- # Widget for the Status sub-command
- {
- my $name = 'STATUS';
- # Checkbuttons for various options
- my @labtxts = (
- 'Display update information',
- 'Print extra information',
- );
- my @varrefs = \(
- $subcmdvar{$name}{UPDATE},
- $subcmdvar{$name}{VERBOSE},
- );
- for my $i (0 .. $#varrefs) {
- $subcmd_f{$name}->Checkbutton (
- '-text' => $labtxts[$i],
- '-variable' => $varrefs[$i],
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- }
- }
- # Widget for the Diff sub-command
- {
- my $name = 'DIFF';
- my $entry;
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Show differences relative to the base of the branch',
- '-variable' => \($subcmdvar{$name}{BRANCH}),
- '-command' => sub {
- $entry->configure (
- '-state' => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
- );
- },
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- # Label and radio buttons box for specifying tool
- $subcmd_f{$name}->Label (
- '-text' => 'Display diff in: ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (qw/default graphical trac/) {
- my $txt = $diff_display_opt{$key};
- my $opt = $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $txt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{TOOL}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- $subcmd_f{$name}->Label ('-text' => 'Branch URL')->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $entry = $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{URL}),
- '-state' => ($subcmdvar{$name}{BRANCH} ? 'normal' : 'disabled'),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- }
- # Widget for the Add/Delete sub-command
- for my $name (qw/ADD DELETE/) {
- # Checkbuttons for various options
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Check for files or directories not under version control',
- '-variable' => \($subcmdvar{$name}{CHECK}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- }
- # Widget for the Merge sub-command
- {
- my $name = 'MERGE';
- # Label and radio buttons box for specifying merge mode
- $subcmd_f{$name}->Label (
- '-text' => 'Mode: ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- {
- my $opt_f = $subcmd_f{$name}->Frame ()->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'w',
- );
- my $col = 0;
- for my $key (qw/automatic custom reverse/) {
- my $txt = lc $key;
- my $opt = $key;
- $branch_opt{$key} = $opt_f->Radiobutton (
- '-text' => $txt,
- '-value' => $opt,
- '-variable' => \($subcmdvar{$name}{MODE}),
- '-state' => 'normal',
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => 'w',
- );
- }
- }
- # Check buttons for dry-run
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Dry run',
- '-variable' => \($subcmdvar{$name}{DRYRUN}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- # Check buttons for verbose mode
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Print extra information',
- '-variable' => \($subcmdvar{$name}{VERBOSE}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- # Label and entry boxes for specifying merge source
- $subcmd_f{$name}->Label (
- '-text' => 'Source (automatic/custom only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{SRC}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- # Label and entry boxes for specifying merge revision (range)
- $subcmd_f{$name}->Label (
- '-text' => 'Revision (custom/reverse only): ',
- )->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{REV}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- }
- # Widget for the Commit sub-command
- {
- my $name = 'COMMIT';
- # Checkbuttons for various options
- $subcmd_f{$name}->Checkbutton (
- '-text' => 'Dry run',
- '-variable' => \($subcmdvar{$name}{DRYRUN}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 0,
- '-columnspan' => 2,
- '-sticky' => 'w',
- );
- }
- # Widget for the Switch sub-command
- {
- my $name = 'SWITCH';
- # Label and entry boxes for specifying switch URL
- $subcmd_f{$name}->Label ('-text' => 'URL: ')->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{URL}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- }
- # Widgets common to all sub-commands
- for my $name (@subcmds) {
- $subcmd_f{$name}->Label ('-text' => 'Other options: ')->grid (
- '-row' => $row{$name},
- '-column' => 0,
- '-sticky' => 'w',
- );
- $subcmd_f{$name}->Entry (
- '-textvariable' => \($subcmdvar{$name}{OTHER}),
- )->grid (
- '-row' => $row{$name}++,
- '-column' => 1,
- '-sticky' => 'ew',
- );
- }
- }
- # ------------------------------------------------------------------------------
- # Buttons to perform main actions
- {
- my $col = 0;
- for my $name (qw/QUIT HELP CLEAR RUN/) {
- $action_b{$name} = $bot_f->Button (
- '-text' => uc (substr ($name, 0, 1)) . lc (substr ($name, 1)),
- '-command' => [\&button_clicked, $name],
- '-width' => 8,
- )->grid (
- '-row' => 0,
- '-column' => $col++,
- '-sticky' => ($name eq 'RUN' ? 'ew' : 'w'),
- );
- $action_b{$name}->bind ('<Enter>', sub {$statustext = $action_help{$name}});
- $action_b{$name}->bind ('<Leave>', sub {$statustext = ''});
- $action_b{$name}->configure ('-underline' => $action_bind{$name}{U})
- if defined $action_bind{$name}{U};
- $mw->bind ($action_bind{$name}{KEY}, sub {$action_b{$name}->invoke});
- }
- }
- &change_cwd ($subcmdvar{CWD});
- # ------------------------------------------------------------------------------
- # Handle the situation when the user attempts to quit the window while a
- # sub-command is running
- $mw->protocol ('WM_DELETE_WINDOW', sub {
- if (defined $cmdpid) {
- my $ans = $mw->messageBox (
- '-title' => $mw_title,
- '-message' => $selsubcmd . ' is still running. Really quit?',
- '-type' => 'YesNo',
- '-default' => 'No',
- );
- if ($ans eq 'Yes') {
- kill 9, $cmdpid; # Need to kill the sub-process before quitting
- } else {
- return; # Do not quit
- }
- }
- exit;
- });
- MainLoop;
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # &change_cwd ($dir);
- #
- # DESCRIPTION
- # Change current working directory to $dir
- # ------------------------------------------------------------------------------
- sub change_cwd {
- my $dir = $_[0];
- my @allowed_subcmds = (&is_wc ($dir) ? @wc_subcmds : @nwc_subcmds);
- for my $subcmd (@subcmds) {
- if (grep {$_ eq $subcmd} @allowed_subcmds) {
- $subcmd_b{$subcmd}->configure ('-state' => 'normal');
- } else {
- $subcmd_b{$subcmd}->configure ('-state' => 'disabled');
- }
- }
- &display_subcmd_frame ($allowed_subcmds[0])
- if not grep {$_ eq $selsubcmd} @allowed_subcmds;
- chdir $dir;
- $subcmdvar{CWD} = $dir;
- if (&is_wc ($dir)) {
- $subcmdvar{WCT} = &get_wct ($dir);
- $subcmdvar{URL_CWD} = &get_url_of_wc ($dir);
- $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT});
- $branch_opt{INFO} ->configure ('-state' => 'normal');
- $branch_opt{DELETE}->configure ('-state' => 'normal');
- $subcmdvar{BRANCH}{OPT} = 'info';
- } else {
- $branch_opt{INFO} ->configure ('-state' => 'disabled');
- $branch_opt{DELETE}->configure ('-state' => 'disabled');
- $subcmdvar{BRANCH}{OPT} = 'create';
- }
- return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # &button_clicked ($name);
- #
- # DESCRIPTION
- # Call back function to handle a click on a command button named $name.
- # ------------------------------------------------------------------------------
- sub button_clicked {
- my $name = $_[0];
- if (grep {$_ eq $name} keys %subcmd_b) {
- &display_subcmd_frame ($name);
- } elsif ($name eq 'CLEAR') {
- $out_t->delete ('1.0', 'end');
- } elsif ($name eq 'QUIT') {
- exit;
- } elsif ($name eq 'HELP') {
- &invoke_cmd ('help ' . lc ($selsubcmd));
- } elsif ($name eq 'RUN') {
- &invoke_cmd (&setup_cmd ($selsubcmd));
- } else {
- $out_t->insert ('end', $name . ': function to be implemented' . "\n");
- $out_t->yviewMoveto (1);
- }
- return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # &display_subcmd_frame ($name);
- #
- # DESCRIPTION
- # Change selected subcommand to $name, and display the frame containing the
- # widgets for configuring the options and arguments of that subcommand.
- # ------------------------------------------------------------------------------
- sub display_subcmd_frame {
- my $name = $_[0];
- if ($selsubcmd ne $name and not $cmdrunning) {
- $subcmd_b{$name }->configure ('-relief' => 'sunken');
- $subcmd_b{$selsubcmd}->configure ('-relief' => 'raised') if $selsubcmd;
- $subcmd_f{$name }->grid ('-sticky' => 'new');
- $subcmd_f{$selsubcmd}->gridForget if $selsubcmd;
- $selsubcmd = $name;
- }
- return;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $pos = &get_wm_pos ();
- #
- # DESCRIPTION
- # Returns the position part of the geometry string of the main window.
- # ------------------------------------------------------------------------------
- sub get_wm_pos {
- my $geometry = $mw->geometry ();
- $geometry =~ /^=?(?:\d+x\d+)?([+-]\d+[+-]\d+)$/;
- return $1;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # $command = &setup_cmd ($name);
- #
- # DESCRIPTION
- # Setup the the system command for the sub-command $name.
- # ------------------------------------------------------------------------------
- sub setup_cmd {
- my $name = $_[0];
- my $cmd = '';
- if ($name eq 'BRANCH') {
- $cmd .= lc ($name);
- if ($subcmdvar{$name}{OPT} eq 'create') {
- $cmd .= ' -c --svn-non-interactive';
- $cmd .= ' -n ' . $subcmdvar{$name}{NAME} if $subcmdvar{$name}{NAME};
- $cmd .= ' -t ' . $subcmdvar{$name}{TYPE};
- $cmd .= ' --rev-flag ' . $subcmdvar{$name}{REVFLAG};
- $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
- $cmd .= ' -k ' . $subcmdvar{$name}{TICKET} if $subcmdvar{$name}{TICKET};
- $cmd .= ' --branch-of-branch ' if $subcmdvar{$name}{SRCTYPE} eq 'branch';
- } elsif ($subcmdvar{$name}{OPT} eq 'delete') {
- $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
- $cmd .= ' -d --svn-non-interactive';
- } elsif ($subcmdvar{$name}{OPT} eq 'list') {
- $cmd .= ' -l';
- $cmd .= ' -r ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
- } else {
- $cmd .= ' -i';
- $cmd .= ' --show-children' if $subcmdvar{$name}{S_CHD};
- $cmd .= ' --show-siblings' if $subcmdvar{$name}{S_SIB};
- $cmd .= ' --show-other' if $subcmdvar{$name}{S_OTH};
- $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
- }
- $cmd .= ' ' . $subcmdvar{$name}{URL} if $subcmdvar{$name}{URL};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'CHECKOUT') {
- $cmd .= lc ($name);
- $cmd .= ' -r' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- $cmd .= ' ' . $subcmdvar{$name}{URL};
- $cmd .= ' ' . $subcmdvar{$name}{PATH} if $subcmdvar{$name}{PATH};
- } elsif ($name eq 'STATUS') {
- $cmd .= lc ($name);
- $cmd .= ' -u' if $subcmdvar{$name}{UPDATE};
- $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'DIFF') {
- $cmd .= lc ($name);
- $cmd .= ' -g' if $subcmdvar{$name}{TOOL} eq 'graphical';
- if ($subcmdvar{$name}{BRANCH}) {
- $cmd .= ' -b';
- $cmd .= ' -t' if $subcmdvar{$name}{TOOL} eq 'trac';
- $cmd .= ' ' . $subcmdvar{$name}{URL} if $subcmdvar{$name}{URL};
- }
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'ADD' or $name eq 'DELETE') {
- $cmd .= lc ($name);
- $cmd .= ' -c' if $subcmdvar{$name}{CHECK};
- $cmd .= ' --non-interactive'
- if $name eq 'DELETE' and not $subcmdvar{$name}{CHECK};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'MERGE') {
- $cmd .= lc ($name);
- if ($subcmdvar{$name}{MODE} ne 'automatic') {
- $cmd .= ' --' . $subcmdvar{$name}{MODE};
- $cmd .= ' --revision ' . $subcmdvar{$name}{REV} if $subcmdvar{$name}{REV};
- }
- $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
- $cmd .= ' -v' if $subcmdvar{$name}{VERBOSE};
- $cmd .= ' ' . $subcmdvar{$name}{SRC} if $subcmdvar{$name}{SRC};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'CONFLICTS') {
- $cmd .= lc ($name);
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'COMMIT') {
- $cmd .= lc ($name);
- $cmd .= ' --dry-run' if $subcmdvar{$name}{DRYRUN};
- $cmd .= ' --svn-non-interactive';
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'SWITCH') {
- $cmd .= lc ($name);
- $cmd .= ' ' . $subcmdvar{$name}{URL} if $subcmdvar{$name}{URL};
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- } elsif ($name eq 'UPDATE') {
- $cmd .= lc ($name);
- $cmd .= ' ' . $subcmdvar{$name}{OTHER} if $subcmdvar{$name}{OTHER};
- }
- return $cmd;
- }
- # ------------------------------------------------------------------------------
- # SYNOPSIS
- # &invoke_cmd ($cmd);
- #
- # DESCRIPTION
- # Invoke the command $cmd.
- # ------------------------------------------------------------------------------
- sub invoke_cmd {
- my $cmd = $_[0];
- return unless $cmd;
- my $disp_cmd = 'fcm ' . $cmd;
- $cmd = (index ($cmd, 'help ') == 0)
- ? $disp_cmd
- : ('fcm gui-internal ' . &get_wm_pos () . ' ' . $cmd);
- # Change directory to working copy top if necessary
- if ($subcmdvar{$selsubcmd}{USEWCT} and $subcmdvar{WCT} ne $subcmdvar{CWD}) {
- chdir $subcmdvar{WCT};
- $out_t->insert ('end', 'cd ' . $subcmdvar{WCT} . "\n");
- $out_t->yviewMoveto (1);
- }
- # Report start of command
- $out_t->insert ('end', timestamp_command ($disp_cmd, 'Start'));
- $out_t->yviewMoveto (1);
- # Open the command as a pipe
- if ($cmdpid = open CMD, '-|', $cmd . ' 2>&1') {
- # Disable all action buttons
- $action_b{$_}->configure ('-state' => 'disabled') for (keys %action_b);
- $cmdrunning = 1;
- # Set up a file event to read output from the command
- $mw->fileevent (\*CMD, readable => sub {
- if (sysread CMD, my ($buf), 1024) {
- # Insert text into the output text box as it becomes available
- $out_t->insert ('end', $buf);
- $out_t->yviewMoveto (1);
- } else {
- # Delete the file event and close the file when the command finishes
- $mw->fileevent(\*CMD, readable => '');
- close CMD;
- $cmdpid = undef;
- # Check return status
- if ($?) {
- $out_t->insert (
- 'end', '"' . $disp_cmd . '" failed (' . $? . ')' . "\n",
- );
- $out_t->yviewMoveto (1);
- }
- # Report end of command
- $out_t->insert ('end', timestamp_command ($disp_cmd, 'End'));
- $out_t->yviewMoveto (1);
- # Change back to CWD if necessary
- if ($subcmdvar{$selsubcmd}{USEWCT} and
- $subcmdvar{WCT} ne $subcmdvar{CWD}) {
- chdir $subcmdvar{CWD};
- $out_t->insert ('end', 'cd ' . $subcmdvar{CWD} . "\n");
- $out_t->yviewMoveto (1);
- }
- # Enable all action buttons again
- $action_b{$_}->configure ('-state' => 'normal') for (keys %action_b);
- $cmdrunning = 0;
- # If the command is "checkout", change directory to working copy
- if (lc ($selsubcmd) eq 'checkout' && $subcmdvar{CHECKOUT}{URL}) {
- my $url = Fcm::Keyword::expand($subcmdvar{CHECKOUT}{URL});
- my $dir = $subcmdvar{CHECKOUT}{PATH}
- ? $subcmdvar{CHECKOUT}{PATH}
- : basename $url;
- $dir = File::Spec->rel2abs ($dir);
- &change_cwd ($dir);
- # If the command is "switch", change URL
- } elsif (lc ($selsubcmd) eq 'switch') {
- $subcmdvar{URL_CWD} = &get_url_of_wc ($subcmdvar{CWD}, 1);
- $subcmdvar{URL_WCT} = &get_url_of_wc ($subcmdvar{WCT}, 1);
- }
- }
- 1;
- });
- } else {
- $mw->messageBox (
- '-title' => 'Error',
- '-message' => 'Error running "' . $cmd . '"',
- '-icon' => 'error',
- );
- }
- return;
- }
- # ------------------------------------------------------------------------------
- __END__
- =head1 NAME
- fcm_gui
- =head1 SYNOPSIS
- fcm_gui [DIR]
- =head1 DESCRIPTION
- The fcm_gui command is a simple graphical user interface for some of the
- commands of the FCM system. The optional argument DIR modifies the initial
- working directory.
- =head1 COPYRIGHT
- (C) Crown copyright Met Office. All rights reserved.
- =cut
|