#!/usr/bin/perl -w eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}' if 0; # not running under some shell use Tk; use Tk::DropSite qw(Sun); use Tk::DragDrop qw(Sun); require Tk::TextUndo; require Tk::FileSelect; require Tk::Menubar; use Tk::ErrorDialog; my $top = MainWindow->new(); $top->bind('Tk::TextUndo','<Alt-KeyPress>','NoOp'); $top->bind('Tk::TextUndo','<Escape>',['tag','remove','sel','1.0','end' +]); my $mb = $top->Menubar; $top->optionAdd('*TextUndo.Background' => '#fff5e1'); my $fs = $top->Component(FileSelect => 'fs',-width => 25, -height => +8, '-accept' => sub { my $file = shift ; return 0 if (-s $file && !(stat +(_))[12]); return 1 unless -e $file; return (-r $file) && (-T $file) +; }, Name => 'fs', -filter => '*'); my $t = $top->Scrolled(TextUndo, -wrap => 'none', -scrollbars => 'osre +'); my $dd = $t->DragDrop(-event => '<Meta-B1-Motion>'); $dd->configure(-startcommand => sub { return 1 unless (eval { $t->tagNextrange(sel => '1.0' +,'end')}); $dd->configure(-text => $t->get('sel.first','sel.last +')); }); $t->DropSite(-motioncommand => sub { my ($x,$y) = @_; $t->markSet(insert => "\@$x,$y"); }, -dropcommand => sub { my ($seln,$x,$y) = @_; $t->markSet(insert => "\@$x,$y"); $t->insert(insert => $t->SelectionGet(-selection => $ +seln)); } ); $mb->Menubutton(-text => '~File', -menuitems => [[Button => '~Open', -command => sub { my $file = $fs->Show(-popover => +$top, -create => 0, -verify => ['-r']); $t->Load($file) if (defined $fil +e); }]]); $mb->Menubutton(-text => '~File', -menuitems => [['Button' => '~Save', -command => [ $t , 'Save' ]], ['Button' => 'Save ~As', -command => sub { my $file = $fs->Show(-popover => $top, -cre +ate => 1, -verify => ['-w']); $t->Save($file) if (defined $file); }], ['Button' => '~Empty', -command => [ $t,'delete','1. +0','end']], '',['Button' => 'E~xit', -command => [ \&CheckSave, +$t ]], ]); $mb->Menubutton(-text => '~Edit', -menuitems => [ ['Button' => '~Undo', -command => [$t, 'undo']],'', ['Button' => '~Copy', -command => [$t, 'clipboardCop +y']], ['Button' => 'Cu~t', -command => [$t, 'clipboardCut +']], ['Button' => '~Paste', -command => [$t, 'clipboardPas +te']], '',['Button' => 'Select All', -command => [$t, 'selec +tAll']], ]); $mb->Menubutton(-text => '~Search', -menuitems => [ ['Button' => '~Find', -command => [\&AskFind, $t]], ['Button' => '~Replace', -command => [\&AskReplace, $ +t]], ]); $mb->Menubutton(-text => '~View', -menuitems => [ ['Button' => '~Line...', -command => [\&AskLine,$t]], ]); $mb->Menubutton(-text => '~Help', -side => 'right'); $t->pack(-expand => 1, -fill => 'both'); $top->protocol('WM_DELETE_WINDOW',[\&CheckSave,$t]); if (@ARGV) { if (! -e $ARGV[0]) { open(FILE,">$ARGV[0]") or die "Could not open $ARGV[0]: $!"; close(FILE) or die "There was trouble with $ARGV[0]: $!"; } $t->Load($ARGV[0]); } $t->bind('<F3>',\&DoFind); $t->update; $t->focus; MainLoop; sub CheckSave { my $t = shift; if ($t->numberChanges) { my $d = $t->toplevel->Dialog(-text => $t->FileName."\nFile has Chan +ged\nSave Edits ?", -buttons => ['Yes','No','Cancel'], -po +pover => $t); my $rep = $d->Show; return if $rep eq 'Cancel'; if ($rep eq 'Yes') { $t->Save or return; } } $t->toplevel->destroy; } my $str; sub DoFind { my $t = shift; $str = shift if (@_); my $posn = $t->index('insert+1c'); $t->tag('remove','sel','1.0','end'); local $_; while ($t->compare($posn,'<','end')) { my ($line,$col) = split(/\./,$posn); $_ = $t->get("$line.0","$posn lineend"); pos($_) = $col; if (/\G(.*)$str/g) { $col += length($1); $posn = "$line.$col"; $t->SetCursor($posn); $t->tag('add','sel',$posn,"$line.".pos($_)); $t->focus; return; } $posn = $t->index("$posn lineend + 1c"); } } sub AskFind { my ($t) = @_; unless (exists $t->{'AskFind'}) { my $d = $t->{'AskFind'} = $t->Toplevel(-popover => 'cursor', -popan +chor => 'nw'); $d->title('Find...'); $d->withdraw; $d->transient($t->toplevel); my $e = $d->Entry->pack; $e->bind('<Return>', sub { $d->withdraw; DoFind($t,$e->get); }); $d->protocol(WM_DELETE_WINDOW =>[withdraw => $d]); } $t->{'AskFind'}->Popup; $t->update; $t->{'AskFind'}->focusNext; } sub AskLine { my ($t) = @_; unless (exists $t->{'AskLine'}) { my $d = $t->{'AskLine'} = $t->Toplevel(-popover => 'cursor', -popan +chor => 'nw'); $d->title('Goto Line ...'); $d->withdraw; $d->transient($t->toplevel); my $e = $d->Entry->pack; $e->bind('<Return>', sub { my $e = shift; $d->withdraw; my $posn = $e->get.'.0'; $t->SetCursor($posn); $t->focus; }); $d->protocol(WM_DELETE_WINDOW =>[withdraw => $d]); } $t->{'AskLine'}->Popup; $t->update; $t->{'AskLine'}->focusNext; } __END__


cheers,
rass

Replies are listed 'Best First'.
Re: Simple Text Editor using the tk Wrapper
by TheoPetersen (Priest) on Feb 15, 2001 at 23:06 UTC
    I thought the find function was broken at first, because it fails silently when the search string isn't present or is before the cursor. Some feedback for those cases would be good.

    This part is a nice trick:

    $d->protocol(WM_DELETE_WINDOW =>[withdraw => $d]);
    It keeps the TopLevel object around even if the user deletes it. I do something similar but the other way around, binding code to <Destroy> that undefs the variable holding the window object. I don't see recreating trivial windows as having much cost.
A reply falls below the community's threshold of quality. You may see it by logging in.