#======================================================== # TkEditorWindow #======================================================== package TkEditorWindow; use MyTk::MyMainWindow; @ISA = qw(MyMainWindow); use strict; use Jcode; use Tk; use Tk::Balloon; use MyTk::Dialog; use MyTk::MyFrame; use MyTk::MyLabel; use MyTk::MyEntry; use MyTk::MyListbox; use MyTk::MyBrowseEntry; use MyTk::MyText; use MyTk::MyButton; use MyTk::MyMenubutton; use MyTk::MyCanvas; use MyTk::MyAdjuster; use MyTk::GraphFrameArray; #============================================================ # 変数等取得関数 #============================================================ #============================================================ # 大域変数 #============================================================ my $filetypes = [ ["All files", '*' ], ["All files", '*' ] ]; #============================================================ # コンストラクタ、デストラクタ #============================================================ sub new { my $class = shift; my $self = MyMainWindow->new(@_); my $this = bless $self, $class; $this->protocol('WM_DELETE_WINDOW' => sub { $this->Close(0); }); return $this; } sub DESTROY { my $this = shift; $this->SUPER::DESTROY(@_); } sub CreateMenu { my ($this) = @_; my $menu = $this->CreateMainMenu(); } sub ModifyMenu { my ($this) = @_; my $IniFile = $this->IniFile()->IniFile(); my $Editor = $this->App()->{'EditorPath'}; my $menu = $this->MainMenu(); # メニュー作成 # メニューバーのエントリ $menu->add('cascade', -label => "File", -underline => 0); $menu->add('cascade', -label => "Edit", -underline => 0); # Fileのプルダウンメニュー my $item1 = $menu->Menu(); $menu->entryconfigure("File", -menu => $item1); # $item1->add('command', -label => "Open", -underline => 0, -command => [ \&LoadFile, $this ] ); $item1->add('command', -label => "Save", -underline => 0, -command => [ \&SaveFile, $this ] ); $item1->add('command', -label => "Save As...", -underline => 5, -command => [ \&SaveAsFile, $this ] ); $item1->separator; $item1->add('command', -label => "New TkEditor", -underline => 0, -command => [ \&MakeClone, $this ] ); $item1->add('command', -label => "IniFile", -underline => 0, -command => sub { system("$Editor $IniFile"); } ); $item1->add('command', -label => "Environment Vars", -underline => 0, -command => sub { Dialog::ShowEnvDialog($this); } ); $item1->add('command', -label => "Information", -underline => 0, -command => sub { $this->App()->ShowInfoDialog($this); } ); $item1->add('command', -label => "Option", -underline => 0, -command => [\&SetupOption, $this] ); $item1->separator; $item1->add('command', -label => "Close", -underline => 0, -accelerator => "Ctrl+Q", -command => [\&Close, $this, 0] ); # Editのプルダウンメニュー my $item2 = $menu->Menu(); $menu->entryconfigure("Edit", -menu => $item2); $item2->add('command', -label => "Undo", -underline => 0, -accelerator => "Ctrl-U", -command => [$this->{'TextBox'}, 'undo']); $item2->add('command', -label => "Redo", -underline => 0, # -accelerator => "Ctrl-shift-U", -command => [$this->{'TextBox'}, 'redo']); $item2->separator; $item2->add('command', -label => "Copy", -underline => 0, -accelerator => "Ctrl-C", -command => [$this->{'TextBox'}, 'clipboardCopy']); $item2->add('command', -label => "Cut", -underline => 2, -accelerator => "Ctrl-X", # -command => [$this->{'TextBox'}, 'clipboardCut']); -command => [$this->{'TextBox'}, 'ClipboardCopyAndCut']); $item2->add('command', -label => "Paste", -underline => 0, -accelerator => "Ctrl-V", -command => [$this->{'TextBox'}, 'clipboardPaste']); $item2->add('command', -label => "Select All", -underline => 0, -accelerator => "Ctrl-A", -command => [$this->{'TextBox'}, 'selectAll']); $item2->add('command', -label => "Unselect All", -underline => 0, -command => [$this->{'TextBox'}, 'unselectAll']); $item2->separator; $item2->add('command', -label => "Find", -underline => 0, -accelerator => "F3", -command => [$this->{'TextBox'}, 'FindPopUp']); $item2->add('command', -label => "Find Next", -underline => 5, -accelerator => "F4", -command => [$this->{'TextBox'}, 'FindSelectionNext']); $item2->add('command', -label => "Find Previous", -underline => 5, -accelerator => "Shift-F4", -command => [$this->{'TextBox'}, 'FindSelectionPrevious']); $item2->add('command', -label => "Replace", -underline => 0, -accelerator => "F5", -command => [$this->{'TextBox'}, 'FindAndReplacePopUp']); tie $this->{'TextBoxWrapMode'}, 'Tk::Configure',$this->{'TextBox'}, '-wrap'; my $ViewMenu = $menu->cascade( -label => 'View', -underline => 0, -tearoff => 'no', -menuitems => [ ['command'=>'Goto ~Line...', -command => [$this->{'TextBox'} => 'GotoLineNumberPopUp']], ['cascade'=> 'Wrap', -tearoff => 0, -menuitems => [ [radiobutton => 'Word', -variable => \$this->{'TextBoxWrapMode'}, -value => 'word'], [radiobutton => 'Character', -variable => \$this->{'TextBoxWrapMode'}, -value => 'char'], [radiobutton => 'None', -variable => \$this->{'TextBoxWrapMode'}, -value => 'none'], ] ], [command => 'Text Font', -underline => 0, -command => [\&SelectTextBoxFont, $this, $this->{'TextBox'}, "TextBoxFont"] ], ] ); my $HelpMenu = $menu->cascade( -label => 'Help', -underline => 0, -tearoff => 'no', -menuitems => [ [command => 'About', -underline => 0, -command => [\&About, $this]], ]); # $this->bind("", [$this->{'TextBox'}, 'selectAll'] ); # $this->bind("", [$this->{'TextBox'}, 'FindSelectionNext'] ); # $this->bind("", [$this->{'TextBox'}, 'FindSelectionPrevious'] ); # $this->bind("", [$this->{'TextBox'}, 'FindAndReplacePopUp'] ); $this->bind("", [$this->{'TextBox'}, 'undo'] ); # $this->bind("", [$this->{'TextBox'}, 'redo'] ); $this->bind("", [$this->{'TextBox'}, 'FindPopUp'] ); $this->bind("", [$this->{'TextBox'}, 'FindSelectionNext'] ); $this->bind("", [$this->{'TextBox'}, 'FindAndReplacePopUp'] ); $this->bind("", [$this->{'TextBox'}, 'FindSelectionPrevious'] ); # $this->bind("", [$this->{'TextBox'}, 'ClipboardCopyAndCut'] ); # $this->bind("", [$this->{'TextBox'}, 'clipboardCut'] ); # $this->bind("", [$this->{'TextBox'}, 'ClipboardCopyAndCut'] ); # $this->bind("", [$this->{'TextBox'}, 'clipboardCut'] ); # $this->bind("", [$this->{'TextBox'}, 'clipboardCopy'] ); # $this->bind("", [$this->{'TextBox'}, 'clipboardPaste'] ); # $this->bind("", [$this->{'TextBox'}, 'clipboardPaste'] ); $this->bind("", [$this->{'TextBox'}, 'KeyIn', Tk::Ev('k'), Tk::Ev('K'), Tk::Ev('A') ] ); $this->bind("", sub { $this->Close(1); } ); return $this->MainMenu(); } sub CreateWidgets { my ($this) = @_; my $App = $this->App(); my $args = $App->Args(); $this->{'ToolBar'} = $this->CreateToolBar("top"); $this->{'StatusBar'} = $this->CreateStatusBar("bottom"); $this->{'TextBox'} = $this->Scrolled('MyText', -background => 'white', -width => 80, -height => 25, -wrap => 'word', -scrollbars => 'se'); $this->{'TextBox'}->pack(-side => 'left', -fill => 'both', -expand => 'yes'); $this->{'TextBox'}->SetApplication($this->App()); # Balloonの作成。StatusBarにも表示する。 my $balloon = $this->Balloon(-statusbar => $this->StatusBar() ); $this->SetBalloon($balloon); #=================================================== # 初期設定 #=================================================== my ($FontName, $FontSize, $FontStyle) = split(/,/, $App->{'TextBoxFont'}) if($App->{'TextBoxFont'}); if($FontName) { $this->{'TextBox'}->UpdateFont($FontName, $FontSize, $FontStyle); } my $geo = $App->{'geometry'}; if($geo) { # $geo =~ s/^(\d+x\d+)\+/\+/ if($geo); $this->SetGeometry($geo); } #=================================================== # コマンドラインオプションの処理 #=================================================== #=================================================== # 小さくなり過ぎないように設定 #=================================================== $this->SetMinSize(); #=================================================== # ウィンドウを更新する #=================================================== $this->update(); #=================================================== # 引数にファイルが指定されていた場合 #=================================================== my $filepath = $this->GetGetArg(0); if($filepath) { unless(-f $filepath) { print "\nError: $filepath does not exist.\n"; exit 1; } # my ($drive, $dir, $filename, $ext, $lastdir, $filebody) # = Deps::SplitFilePath($filepath); # $dir = Deps::MakePath($drive, $dir); $this->{'TextBox'}->Load($filepath); } return $this; } sub CreateToolBar { my ($this, $position) = @_; # 枠作成 $this->{'ToolBarFrame'} = $this->MyFrame( -borderwidth => 2, -relief => 'groove', )->pack(-side => $position, -fill => 'x'); $this->{'OpenButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Open', -takefocus => 1, -command => [ \&LoadFile, $this ], )->pack(-side => 'left', -fill => 'x'); $this->{'SaveButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Save', -takefocus => 1, -command => sub { $this->SaveFile(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'FindButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Find', -takefocus => 1, -command => sub { $this->{'TextBox'}->FindPopUp(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'FindNextButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Next', -takefocus => 1, -command => sub { $this->{'TextBox'}->FindSelectionNext(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'FindPrevButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Prev', -takefocus => 1, -command => sub { $this->{'TextBox'}->FindSelectionPrevious(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'UndoButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Undo', -takefocus => 1, -command => sub { $this->{'TextBox'}->undo(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'RedoButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Redo', -takefocus => 1, -command => sub { $this->{'TextBox'}->redo(); }, )->pack(-side => 'left', -fill => 'x'); $this->{'CloseButton'} = $this->{'ToolBarFrame'}->MyButton( -text => 'Close', -takefocus => 1, -command => [ \&Close, $this, 1 ], )->pack(-side => 'right', -fill => 'x'); # $this->{'SetupOptionButton'} = $this->{'ToolBarFrame'}->MyButton( # -text => 'Setup Option', # -takefocus => 1, # -command => [\&SetupOption, $this], # )->pack(-side => 'right', -fill => 'x'); # my $balloon = $this->Balloon(); # $balloon->attach($this->{'OpenButton'}, -msg => "Open directory", # -balloonposition => 'mouse'); # $balloon->attach($this->{'ShowPosButton'}, -msg => "Toggle Show Mouse Poisition", # -balloonposition => 'mouse'); } #=================================================== # 一般関数 #=================================================== #======================================================================= # bindされた応答関数 #======================================================================= # ファイルの読み込み sub LoadFile { my ($this) = @_; if($this->{'TextBox'}->IsModified()) { my $button = $this->messageBox( -icon => 'warning', -type => 'YesNoCancel', -default => 'Yes', -title => 'Text was modified', -message => 'Text was modified. Save it?'); return if($button eq 'Cancel'); $this->SaveFile() if($button eq 'Yes'); } my $filename = $this->getOpenFile( -filetypes => $filetypes, # -defaultextension => '.txt' ); if (defined $filename) { $this->{'TextBox'}->SetModified(0); $this->{'TextBox'}->Load($filename); $this->SetTitle($this->{'TextBox'}->FileName() . ": TkEditor 2006/pl"); } } # ファイルの保存 sub SaveFile { my ($this) = @_; my $filename = $this->{'TextBox'}->FileName(); if (defined $filename) { $this->{'TextBox'}->Save($filename); $this->{'TextBox'}->SetModified(0); } else { &SaveAsFile(); } return 1; } # 名前をつけて保存 sub SaveAsFile { my ($this) = @_; my $filename = $this->getSaveFile(-filetypes => $filetypes, -defaultextension => '.txt'); if (defined $filename) { $this->{'TextBox'}->Save($filename); $this->{'TextBox'}->SetModified(0); } } sub MakeClone { my ($this) = @_; my $args = $this->App()->Args(); my $command = $args->BuildCommandLine(); $command = "perl.exe $command"; Deps::ExecBackground($command); } sub SetupOption { my ($this) = @_; my $App = $this->App(); my $dlg = $this->DialogBox( -title => "Setup Option", -default_button => 'OK', -buttons => ['OK', 'Cancel'], ); my $width = 50; my $Frame1 = $dlg->add('Frame')->pack(-expand => 'yes', -fill => 'x'); my $EditorLabel = $Frame1->Label(-text => "Editor Path:")->pack(-side => 'left'); my $EditorText = $Frame1->MyEntry( -relief => 'sunken', -takefocus => 1, -width => $width, ); my $EditorPathButton = $Frame1->MyButton( -text => "Path", -command => [ \&ChooseFile, $this, $EditorText ], )->pack(-side => 'right'); $EditorText->pack(-side => 'right', -fill => 'x'); $EditorText->focus; $EditorText->SetText($App->{"EditorPath"}); my $ret = $dlg->Show; if($ret eq 'OK') { $App->{"EditorPath"} = $EditorText->GetText(); } } sub ChooseFile { my ($this, $widget) = @_; my $dir = ''; unless($widget) { $dir = $this->App()->{'WorkDir'}; chdir($dir); } my $fmask = '*.*'; my $defstr = ''; my $message = 'Choose file'; my $filepath = Dialog::OpenFileDialog($this, 'open', $fmask, $defstr, $message, $dir); if($filepath) { my $DirPath = $this->SetWorkDir($filepath); if($widget) { $widget->SetText($filepath); } } return $filepath; } sub About { my ($this) = @_; Dialog::ShowAboutDialog($this, "TkEditor 2006/pl"); } sub Close { my ($this, $DoConfirm) = @_; my $App = $this->{'Application'}; my $geo; if($App) { $geo = $App->{'geometry'} = $this->geometry(); } $App->SaveSetting(); if($this->{'TextBox'}->IsModified()) { my $button = $this->messageBox( -icon => 'warning', -type => 'YesNoCancel', -default => 'Yes', -title => 'Text was modified', -message => 'Text was modified. Save it?'); return if($button eq 'Cancel'); $this->SaveFile() if($button eq 'Yes'); $DoConfirm = 0; } if($DoConfirm) { my $button = $this->messageBox( -icon => 'warning', -type => 'YesNo', -default => 'No', -title => 'Confirm', -message => 'Really quit?'); return if($button eq 'No'); } $this->destroy(); } sub SelectTextBoxFont { my ($this, $widget, $VarName) = @_; my $App = $this->App(); return undef unless($App); my ($FontName, $FontSize, $FontStyle) = ("times", 10.5, "normal"); ($FontName, $FontSize, $FontStyle) = split(/,/, $App->{$VarName}) if($App->{$VarName}); ($FontName, $FontSize, $FontStyle) = Dialog::ShowFontDialog($this, $FontName, $FontSize, $FontStyle); if($FontName) { $App->{$VarName} = "$FontName,$FontSize,$FontStyle"; $widget->UpdateFont($FontName, $FontSize, $FontStyle); } return $App->{$VarName}; } 1;