Convert KPAD from TK to GTK3 and add new features

This commit is contained in:
kake26 2025-04-30 11:42:00 -05:00
parent 7891d2d65b
commit 868aabae0a
Signed by: kake26
GPG key ID: E0A989B571D1F99F
10 changed files with 1598 additions and 375 deletions

6
.windsurfrules Executable file
View file

@ -0,0 +1,6 @@
1. This application is written in Perl
2. It uses the TK module to provide GUI
3. If this needs to be tested use /usr/bin/perl
4. We are converting it from TK to GTK3
5. Old TK code is in kpad.old.pl
6. New code goes into kpad.pl

0
LICENSE Normal file → Executable file
View file

0
README.md Normal file → Executable file
View file

0
The Offical KAKE PAD Macro Tutorial.doc Normal file → Executable file
View file

375
kpad.old.pl Executable file
View file

@ -0,0 +1,375 @@
# KAKE PAD version 5.5
# Future versions will look at using Perl 6
# Please note that KAKE PAD depends on Tk and some bugs
# are due to Tk and not KAKE PAD itself. No promises on how
# good or bad Tk is.
# A few Tk modules need to be declared for PDK purposes
use Tk;
use Tk::TextUndo;
use Tk::DialogBox;
use File::Glob;
use File::Find;
use FileHandle;
use LWP::Simple;
#most of the above is only put in so I can compile to stand alone exe
#If you use as script keep the last two and use Tk;
# Below is the init section
# A few thing to do is check OS to see if its Windows or not
# I still don't know what I smoked when I wrote this but this section is important
($ar) = @ARGV; #Let the program do its thing with the arguments
# Bah check OS
# Just to make sure test $0, this is a work around for a minor bug
# Detect : a trade mark of a DOS system
# The setion below is so odd and complex vodoo that is quite nessacry
# yes I can't spell, only code
if($^O ne "Win32") {
if($0 =~/\//g){
@hd = split "/", $0;
$hdl = pop(@hd); # Knock the filename off the array we don't need it
$basedir = join('/', @hd);
}else{
$basedir = ".";
}
}else{
$basedir = ".";
}
$main = MainWindow->new(-title=> "kPad"); #Generates the main window
$menubar = $main->Frame()->pack(-side => "top", -fill => "x"); #Notice I create the menu bar as
# a frame not a Tk::Menu menubar, this makes things easier
#Below I define all the DialogBoxes,note they can be globaly used
$about = $main->DialogBox(-title=>"About...",-buttons=>["OK"]); #Creates the about Dialog
$aabout = $about->add("Label",-text=>"kPad\n by Paul Malcher\nVersion 6 Release\n")->pack; #Adds a label to $about
#$kweabt = $main->DialogBox(-title=>"About kWebedit...",-buttons=>["OK"]); #Creates the About kWebedit Dialog
#$akweabt =$kweabt->add("Label",-text=>"kWebedit v.2.0\nby Paul Malcher\nBased on kWebedit v.1.0.3 by Chris Litwin ")->pack; #That's the about for kWebedit.
$help = $main->DialogBox(-title=>"Help Topics",-buttons=>["OK"]); #Creates Help Dialog
$ahelp = $help->add("Label",-text=>"Help topics for KPAD\nWell, this is a text/file editor mainly meant for scripting and programming use.
Like notepad but made for the programmer.")->pack;
$nsave = $main->DialogBox(-title=>"Warning File Has changed!",-buttons=>["Save","Exit"]);
$ansave = $nsave->add("Label",-text=>"The documents contents have changed since you opened.\nDo you wish to save?.")->pack;
$nimp = $main->DialogBox(-title=>"Non-implementation Error",-buttons=>["OK"]);
$animp = $nimp->add("Label",-text=>"This function is not yet implemented!")->pack;
$fetch = $main->DialogBox(-title =>'HTML Source Fetch',-buttons=>["OK"]);
$afetch = $fetch->add("Label",-text=>'Fetch what:')->pack;
$bfetch = $fetch->add("Entry",-text=>'http://')->pack;
$dummy = $main->DialogBox(-title=>'Dummy Box');
$adummy = $dummy->add("Text")->pack;
$ftapp = $main->DialogBox(-title =>'File Has Changed!',-buttons=>["Yes","No"]);
$aftapp = $ftapp->add("Label", -text=>"File contents have changed, save now?!")->pack;
$track = "init";
# Begin new Kpad 4.0 features
# Plugin/Macros or whatever you want to call them
# First we find and autoload plugin, yes we use grep, makes life good
$pls = 0;
# Heck with lets do it all in one loop
while(<*.kpd>){
push(@plugins,$_);
open pin,"<$_";
@gn = split "::" , <pin>;
if(@gn[2] eq "auto"){
@n[$pls] = "auto";
}else{
@n[$pls] = @gn[1];
}
$pls++;
}
# determin the number of plugins, so we can size the list accordingly
$nop = scalar(@n); #notice @n does not get shortend, this is important later on
foreach(@n) {
if($_ eq "auto"){
$nop--; # make sure auto plugins are not listed
}
}
# Build the menu with list box
$plugin = $main->DialogBox(-title=>'Macro Execution Menu',-buttons=>["Close"]);
$bplugin = $plugin->add("Label",-text=>'Double Click To Execute Macro')->pack;
$aplugin = $plugin->Listbox("-width"=>40, "-height"=> $nop)->pack;
foreach(@n) {
if($_ eq "auto"){
$arun = 0;
}else{
$aplugin->insert('end', "$_");
}
}
$aplugin->bind('<Double-1>' , \&eplugin); # Plugin name now can be different from the file name
$filemenu = $menubar->Menubutton(-text => 'File', -underline => 0,-tearoff => 0)->pack(-side=>'left'); #This puts
#the file button on the frame used for the menu bar
#Below are the commands for that button
#note How I included the subs into the command function
$filemenu->command(-label => 'New',-command => sub{
$text->delete('1.0','end');
});
$filemenu->command(-label => 'Open',-command => sub{
$text->delete('1.0','end');
my $types = [
['Perl Scripts', '.pl'],
['All Files', '*', ],
];
$open = $main->getOpenFile(-filetypes=>$types);
#open FILE, "<$open"; #took weeks to get this right,its there so te whole file loads correctly
# and only 3 sec to comment out for the 5.0 release
$text->Load($open);
$text ->pack;
});
$filemenu->command(-label => 'Save',-command => sub{
$data = $text->get('1.0','end'); #Saving for widget to file is a piece of cake
if($ar eq ""){
$text->Save($open);
# Easy indeed
}else{
$text->Save($ar);
}
});
$filemenu->command(-label => 'Save As',-command => sub{
#my $types = [['All Files', '*', ],];
my $types = [
['Perl Scripts', '.pl' ],
['All Files', '.*', ],
];
my $save = $main->getSaveFile(-filetypes=>$types);
$text->Save($save);
$open = $save;
});
$filemenu->separator;
$filemenu->command(-label => 'Exit',-command => sub{
tapp();
});
$editmenu = $menubar->Menubutton(-text => 'Edit', -underline => 0,-tearoff => 0)->pack(-side=>'left');
$editmenu->command(-label => 'Undo',-command => sub{
my ($w) = @_;
$text->undo;
});
$editmenu->command(-label => 'Redo',-command => sub{
my ($w) = @_;
$text->redo;
});
$editmenu->separator;
$editmenu->command(-label => 'Cut',-command => sub{
my ($w) = @_;
$text->Column_Copy_or_Cut(1);
});
$editmenu->command(-label => 'Copy',-command => sub{
my ($w) = @_;
$text->Column_Copy_or_Cut(0);
});
$editmenu->command(-label => 'Paste',-command => sub{
$text->clipboardColumnPaste();
});
$editmenu->separator;
$editmenu->command(-label => 'Select All',-command => sub{
$text->selectAll();
});
$editmenu->command(-label => 'Unselect All',-command => sub{
$text->unselectAll();
});
$editmenu->separator;
$editmenu->command(-label => 'Find',-command => sub{
$text->findandreplacepopup(1);
});
$editmenu->command(-label => 'Find and Replace',-command => sub{
$text->findandreplacepopup(0);
});
$viewmenu = $menubar->Menubutton(-text=>'View',-underline => 0,-tearoff => 0)->pack(-side=>'left');
$vm = $viewmenu->cascade(-label => 'Wrap',-underline => 0,-tearoff => 0);
$vm->radiobutton(-label => "Word", -command => sub { $text->configure(-wrap => 'word'); } );
$vm->radiobutton(-label => "Char",-command => sub { $text->configure(-wrap => 'char'); } );
$vm->radiobutton(-label => "None",-command => sub { $text->configure(-wrap => 'none'); } );
$toolsmenu = $menubar->Menubutton(-text => 'Tools', -underline => 0,-tearoff => 0)->pack(-side=>'left');
$toolsmenu->command(-label => 'Goto Line',-command => sub{
$text->GotoLineNumberPopUp();
});
$toolsmenu->command(-label => 'Which Line?',-command => sub{
$text->WhatLineNumberPopUp();
});
$htmlmenu = $menubar->Menubutton(-text => 'HTML', -underline => 0,-tearoff => 0)->pack(-side=>'left');
$htmlmenu->command(-label => 'Fetch a web resource...',-command => sub{#$fdisc->Show;
$fetch->Show;
$htm = $bfetch->get;
$contents = get($htm);
open ttt, ">temp.dat";
print ttt "$contents";
close ttt;
open FILE, "<temp.dat"; #took weeks to get this right,its there so te whole file loads correctly
$text->delete('1.0','end');
while (! eof FILE){
$text->insert('end',FILE -> getline);
}
close FILE;
unlink(<temp.dat>);
$text ->pack;
});
$pluginmenu = $menubar->Menubutton(-text => 'Macros', -underline => 0,-tearoff => 0)->pack(-side=>'left');
$pluginmenu->command(-label => 'Execute Macro',-command => sub{$plugin->Show;});
$aboutmenu = $menubar->Menubutton(-text => 'Help', -underline => 0,-tearoff => 0)->pack(-side=>'left');
$aboutmenu->command(-label => 'Help Topics...',-command => sub{$help->Show;});
$aboutmenu->command(-label => 'About KPAD...',-command => sub{$about->Show;});
#$aboutmenu->command(-label => 'About kWebedit...',-command => sub{$kweabt->Show;});
# Text widget and configs
$statbar = $main->Frame()->pack(-side => "bottom", -fill => "x");
$statinfo = $statbar->Label(-text =>'Info: ')->pack(-side=>'left');
$statln = $statbar->Label(-text =>'Line: 0')->pack(-side=>'left');
$text = $main->Scrolled(TextUndo,-scrollbars=>'osoe',-background=>'white', -wrap => 'word')->pack(-fill=>'both',-expand=>1); #Scrolled Text
#widget that adapts to the size of the window
$main->protocol('WM_DELETE_WINDOW', \&tapp);
# This replaces $track
$text->bind('<<Modified>>' => sub{
$track = $text->get('1.0','end');
my ($w)=$text;
my ($line,$col) = split(/\./,$w->index('insert'));
#$statln = $statbar->Label(-text =>"Line $line")->pack(-side=>'left');
$statln->configure(-text =>"Line $line");
});
if($ar ne ""){
$text->Load($ar);
$text ->pack;
}
sub eplugin { # Plugin executor, non-auto
$v = $aplugin->get('active');
# Fix for plugin vs. filename fix
# @plugins @n
$fp = 0;
while(@n[$fp] ne $v){ # assume the names in @plugin match with @n
# which they will unless you screw with the way plugins are handled
$fp++;
}
$v = @plugins[$fp];
# Hope it works
open pe, "<$basedir/$v"; # presto it does
# The same damn bug in Tk again, yes the one that took weeks to work around
# I got to do this the hard way
$adummy->delete('1.0','end');
while (! eof pe){
$adummy->insert('end', pe -> getline);
}
$tdata = $adummy->get('2.0','end'); # this is the only way to load an entire plugin into a var the right way
eval ( $tdata );
if($@){ # Only way to to make so it can trap multiple errors without the app having a fatal error itself
$error = $@;
&merr($error);
}
}
sub aeplugin { # Auto plugin executor
$apc = 0;
while(@n[$apc] ne ""){
if(@n[$apc] eq "auto"){
$v = @plugins[$apc];
# Hope it works
open pe, "<$basedir/$v"; # presto it does
# The same damn bug in Tk again, yes the one that took weeks to work around
# I got to do this the hard way
$adummy->delete('1.0','end');
while (! eof pe){
$adummy->insert('end', pe -> getline);
}
$tdata = $adummy->get('2.0','end'); # this is the only way to load an entire plugin into a var the right way
eval ( $tdata );
if($@){ # Only way to to make so it can trap multiple errors without the app having a fatal error itself
$error = $@;
&merr($error);
}
}
$apc++;
}
$arun = 1;
}
sub merr { # merr, macro/plugins error
$merr = $main->DialogBox(-title =>'Macro Error',-buttons=>["OK"]);
$amerr = $merr->add("Label", -text=>"Error: $error")->pack;
$merr->Show;
undef $merr;
}
if($arun eq "0"){
&aeplugin();
}
sub tapp { # shutdown handler
if($track ne "init"){
$result = $ftapp->Show;
if($result eq "No"){
exit(0);
}
if($open){
$text->Save($open);
$saved = 1;
}
if($save){
$text->Save($save);
$saved = 1;
}
if($ar){
$text->Save($ar);
$saved = 1;
}else{
if($saved ne "1"){
my $types = [
['Perl Scripts', '.pl' ],
['All Files', '.*', ],
];
my $save = $main->getSaveFile(-filetypes=>$types);
$text->Save($save);
}
}
}
exit(0);
}
MainLoop; #The main processing loop

1073
kpad.pl Normal file → Executable file

File diff suppressed because it is too large Load diff

515
kpadgtk3.pl Normal file
View file

@ -0,0 +1,515 @@
#!/usr/bin/perl
use strict;
use warnings;
use Gtk3 '-init';
use Glib qw(TRUE FALSE);
use File::Glob;
use FileHandle;
use LWP::Simple;
use Gtk3::SourceView;
# Declare global variables
our $textbuffer; # SourceView buffer for the editor
our $textview; # SourceView widget
our $statusbar; # Status bar for feedback
our $open; # Current file path
our $track = 'init'; # Tracks modifications
# Base directory setup (reused from your code)
my ($ar) = @ARGV;
my $basedir;
if ($^O ne "Win32") {
if ($0 =~ /\//g) {
my @hd = split "/", $0;
pop @hd; # Remove filename
$basedir = join('/', @hd) || ".";
} else {
$basedir = ".";
}
} else {
$basedir = ".";
}
# Main window
my $window = Gtk3::Window->new('toplevel');
$window->set_title('KPad');
$window->set_default_size(800, 600);
$window->signal_connect(destroy => sub { Gtk3->main_quit });
# Main layout
my $vbox = Gtk3::Box->new('vertical', 0);
$window->add($vbox);
# Menu bar
my $menubar = Gtk3::MenuBar->new;
$vbox->pack_start($menubar, FALSE, FALSE, 0);
# File menu
my $file_menu = Gtk3::Menu->new;
my $file_item = Gtk3::MenuItem->new_with_label('File');
$file_item->set_submenu($file_menu);
$menubar->append($file_item);
my $new_item = Gtk3::MenuItem->new_with_label('New');
$new_item->signal_connect(activate => sub {
$textbuffer->set_text('');
$statusbar->push(0, 'New file');
});
$file_menu->append($new_item);
my $open_item = Gtk3::MenuItem->new_with_label('Open');
$open_item->signal_connect(activate => \&open_file);
$file_menu->append($open_item);
my $save_item = Gtk3::MenuItem->new_with_label('Save');
$save_item->signal_connect(activate => \&save_file);
$file_menu->append($save_item);
my $save_as_item = Gtk3::MenuItem->new_with_label('Save As');
$save_as_item->signal_connect(activate => \&save_as_file);
$file_menu->append($save_as_item);
my $exit_item = Gtk3::MenuItem->new_with_label('Exit');
$exit_item->signal_connect(activate => \&tapp);
$file_menu->append($exit_item);
# Edit menu
my $edit_menu = Gtk3::Menu->new;
my $edit_item = Gtk3::MenuItem->new_with_label('Edit');
$edit_item->set_submenu($edit_menu);
$menubar->append($edit_item);
my $undo_item = Gtk3::MenuItem->new_with_label('Undo');
$undo_item->signal_connect(activate => sub {
$textbuffer->undo if $textbuffer->can_undo;
$statusbar->push(0, 'Undo');
});
$edit_menu->append($undo_item);
my $redo_item = Gtk3::MenuItem->new_with_label('Redo');
$redo_item->signal_connect(activate => sub {
$textbuffer->redo if $textbuffer->can_redo;
$statusbar->push(0, 'Redo');
});
$edit_menu->append($redo_item);
my $cut_item = Gtk3::MenuItem->new_with_label('Cut');
$cut_item->signal_connect(activate => sub {
$textbuffer->cut_clipboard(Gtk3::Clipboard::get('CLIPBOARD'), TRUE);
});
$edit_menu->append($cut_item);
my $copy_item = Gtk3::MenuItem->new_with_label('Copy');
$copy_item->signal_connect(activate => sub {
$textbuffer->copy_clipboard(Gtk3::Clipboard::get('CLIPBOARD'));
});
$edit_menu->append($copy_item);
my $paste_item = Gtk3::MenuItem->new_with_label('Paste');
$paste_item->signal_connect(activate => sub {
$textbuffer->paste_clipboard(Gtk3::Clipboard::get('CLIPBOARD'), undef, TRUE);
});
$edit_menu->append($paste_item);
my $select_all_item = Gtk3::MenuItem->new_with_label('Select All');
$select_all_item->signal_connect(activate => sub {
$textbuffer->select_range($textbuffer->get_start_iter, $textbuffer->get_end_iter);
});
$edit_menu->append($select_all_item);
my $find_item = Gtk3::MenuItem->new_with_label('Find');
$find_item->signal_connect(activate => sub { find_and_replace_dialog(1); });
$edit_menu->append($find_item);
my $replace_item = Gtk3::MenuItem->new_with_label('Find and Replace');
$replace_item->signal_connect(activate => sub { find_and_replace_dialog(0); });
$edit_menu->append($replace_item);
# View menu (wrap options)
my $view_menu = Gtk3::Menu->new;
my $view_item = Gtk3::MenuItem->new_with_label('View');
$view_item->set_submenu($view_menu);
$menubar->append($view_item);
my $wrap_menu = Gtk3::Menu->new;
my $wrap_item = Gtk3::MenuItem->new_with_label('Wrap');
$wrap_item->set_submenu($wrap_menu);
$view_menu->append($wrap_item);
my $wrap_word = Gtk3::CheckMenuItem->new_with_label('Word');
$wrap_word->signal_connect(toggled => sub {
$textview->set_wrap_mode($wrap_word->get_active ? 'word' : 'none');
});
$wrap_menu->append($wrap_word);
my $wrap_char = Gtk3::CheckMenuItem->new_with_label('Char');
$wrap_char->signal_connect(toggled => sub {
$textview->set_wrap_mode($wrap_char->get_active ? 'char' : 'none');
});
$wrap_menu->append($wrap_char);
my $wrap_none = Gtk3::CheckMenuItem->new_with_label('None');
$wrap_none->signal_connect(toggled => sub {
$textview->set_wrap_mode($wrap_none->get_active ? 'none' : 'none');
});
$wrap_menu->append($wrap_none);
# Tools menu
my $tools_menu = Gtk3::Menu->new;
my $tools_item = Gtk3::MenuItem->new_with_label('Tools');
$tools_item->set_submenu($tools_menu);
$menubar->append($tools_item);
my $goto_line_item = Gtk3::MenuItem->new_with_label('Goto Line');
$goto_line_item->signal_connect(activate => \&goto_line_dialog);
$tools_menu->append($goto_line_item);
my $which_line_item = Gtk3::MenuItem->new_with_label('Which Line?');
$which_line_item->signal_connect(activate => \&which_line_dialog);
$tools_menu->append($which_line_item);
# HTML menu
my $html_menu = Gtk3::Menu->new;
my $html_item = Gtk3::MenuItem->new_with_label('HTML');
$html_item->set_submenu($html_menu);
$menubar->append($html_item);
my $fetch_item = Gtk3::MenuItem->new_with_label('Fetch a web resource...');
$fetch_item->signal_connect(activate => \&fetch_html);
$html_menu->append($fetch_item);
# Macros menu (plugins)
my $macros_menu = Gtk3::Menu->new;
my $macros_item = Gtk3::MenuItem->new_with_label('Macros');
$macros_item->set_submenu($macros_menu);
$menubar->append($macros_item);
my $exec_macro_item = Gtk3::MenuItem->new_with_label('Execute Macro');
$exec_macro_item->signal_connect(activate => \&show_plugin_dialog);
$macros_menu->append($exec_macro_item);
# Help menu
my $help_menu = Gtk3::Menu->new;
my $help_item = Gtk3::MenuItem->new_with_label('Help');
$help_item->set_submenu($help_menu);
$menubar->append($help_item);
my $help_topics_item = Gtk3::MenuItem->new_with_label('Help Topics...');
$help_topics_item->signal_connect(activate => sub {
my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'info', 'ok',
"Help topics for KPAD\nWell, this is a text/file editor mainly meant for scripting and programming use.\nLike notepad but made for the programmer.");
$dialog->run;
$dialog->destroy;
});
$help_menu->append($help_topics_item);
my $about_item = Gtk3::MenuItem->new_with_label('About KPAD...');
$about_item->signal_connect(activate => sub {
my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'info', 'ok',
"kPad\nby Paul Malcher\nVersion 6 Release\n");
$dialog->run;
$dialog->destroy;
});
$help_menu->append($about_item);
# Text view with SourceView
my $scrolled = Gtk3::ScrolledWindow->new(undef, undef);
$vbox->pack_start($scrolled, TRUE, TRUE, 0);
$textview = Gtk3::SourceView::View->new;
$textview->set_wrap_mode('word');
$textview->set_show_line_numbers(TRUE);
$textview->set_auto_indent(TRUE);
$scrolled->add($textview);
$textbuffer = $textview->get_buffer;
my $lang_manager = Gtk3::SourceView::LanguageManager->get_default;
my $language = $lang_manager->get_language('perl');
$textbuffer->set_language($language) if $language;
$textbuffer->set_highlight_syntax(TRUE);
# CSS styling for Kubuntu
my $provider = Gtk3::CssProvider->new;
$provider->load_from_data('
textview { font-family: Monospace; font-size: 12pt; }
button { color: black; background: #d3d3d3; }
');
Gtk3::StyleContext::add_provider_for_screen(
Gtk3::Gdk::Screen::get_default,
$provider,
Gtk3::STYLE_PROVIDER_PRIORITY_APPLICATION
);
# Status bar
$statusbar = Gtk3::Statusbar->new;
$vbox->pack_start($statusbar, FALSE, FALSE, 0);
$textbuffer->signal_connect('mark-set' => sub {
my ($buf, $iter, $mark) = @_;
if ($mark->get_name eq 'insert') {
my $line = $iter->get_line + 1;
$statusbar->push(0, "Line: $line");
}
});
# Track modifications
$textbuffer->signal_connect('modified-changed' => sub {
$track = $textbuffer->get_text($textbuffer->get_start_iter, $textbuffer->get_end_iter, TRUE);
});
# Load file from ARGV
if ($ar) {
if (open my $fh, '<', $ar) {
local $/;
$textbuffer->set_text(<$fh>);
close $fh;
$statusbar->push(0, "Opened $ar");
$open = $ar;
} else {
$statusbar->push(0, "Failed to open $ar");
}
}
# Plugin system
my @plugins;
my @n;
my $pls = 0;
while (<*.kpd>) {
push @plugins, $_;
open my $pin, '<', $_ or next;
my @gn = split '::', <$pin>;
$n[$pls] = $gn[2] eq 'auto' ? 'auto' : $gn[1];
$pls++;
}
my $nop = grep { $_ ne 'auto' } @n;
# File handling
sub open_file {
my $dialog = Gtk3::FileChooserDialog->new(
'Open File', $window, 'open',
'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'
);
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*.pl');
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*');
if ($dialog->run eq 'ok') {
my $filename = $dialog->get_filename;
if (open my $fh, '<', $filename) {
local $/;
$textbuffer->set_text(<$fh>);
close $fh;
$statusbar->push(0, "Opened $filename");
$open = $filename;
} else {
$statusbar->push(0, "Failed to open $filename");
}
}
$dialog->destroy;
}
sub save_file {
if ($open) {
my ($start, $end) = $textbuffer->get_bounds;
my $text = $textbuffer->get_text($start, $end, TRUE);
if (open my $fh, '>', $open) {
print $fh $text;
close $fh;
$statusbar->push(0, "Saved $open");
} else {
$statusbar->push(0, "Failed to save $open");
}
} else {
save_as_file();
}
}
sub save_as_file {
my $dialog = Gtk3::FileChooserDialog->new(
'Save File', $window, 'save',
'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok'
);
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*.pl');
$dialog->add_filter(Gtk3::FileFilter->new)->add_pattern('*');
if ($dialog->run eq 'ok') {
my $filename = $dialog->get_filename;
my ($start, $end) = $textbuffer->get_bounds;
my $text = $textbuffer->get_text($start, $end, TRUE);
if (open my $fh, '>', $filename) {
print $fh $text;
close $fh;
$statusbar->push(0, "Saved $filename");
$open = $filename;
} else {
$statusbar->push(0, "Failed to save $filename");
}
}
$dialog->destroy;
}
# Find and replace dialog
sub find_and_replace_dialog {
my ($find_only) = @_;
my $dialog = Gtk3::Dialog->new($find_only ? 'Find' : 'Find and Replace', $window, 'modal',
'gtk-ok', 'ok', 'gtk-cancel', 'cancel');
my $find_entry = Gtk3::Entry->new;
my $replace_entry = Gtk3::Entry->new;
my $find_label = Gtk3::Label->new('Find:');
$dialog->get_content_area->pack_start($find_label, FALSE, FALSE, 0);
$dialog->get_content_area->pack_start($find_entry, FALSE, FALSE, 0);
unless ($find_only) {
my $replace_label = Gtk3::Label->new('Replace with:');
$dialog->get_content_area->pack_start($replace_label, FALSE, FALSE, 0);
$dialog->get_content_area->pack_start($replace_entry, FALSE, FALSE, 0);
}
$dialog->show_all;
if ($dialog->run eq 'ok') {
my $search = $find_entry->get_text;
my $replace = $replace_entry->get_text;
my $search_context = Gtk3::SourceView::SearchContext->new($textbuffer);
$search_context->set_search_text($search, []);
my $iter = $textbuffer->get_start_iter;
if (my ($match_start, $match_end) = $search_context->forward($iter)) {
$textbuffer->select_range($match_start, $match_end);
unless ($find_only) {
$search_context->replace($match_start, $match_end, $replace, -1);
$statusbar->push(0, "Replaced '$search' with '$replace'");
} else {
$statusbar->push(0, "Found '$search'");
}
} else {
$statusbar->push(0, "Text not found");
}
}
$dialog->destroy;
}
# Goto line dialog
sub goto_line_dialog {
my $dialog = Gtk3::Dialog->new('Goto Line', $window, 'modal', 'gtk-ok', 'ok', 'gtk-cancel', 'cancel');
my $entry = Gtk3::Entry->new;
$dialog->get_content_area->pack_start($entry, FALSE, FALSE, 0);
$dialog->show_all;
if ($dialog->run eq 'ok') {
my $line = $entry->get_text;
if ($line =~ /^\d+$/) {
my $iter = $textbuffer->get_iter_at_line($line - 1);
$textbuffer->place_cursor($iter);
$textview->scroll_to_iter($iter, 0, TRUE, 0, 0.5);
}
}
$dialog->destroy;
}
# Which line dialog
sub which_line_dialog {
my $iter = $textbuffer->get_iter_at_mark($textbuffer->get_insert);
my $line = $iter->get_line + 1;
my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'info', 'ok', "Current line: $line");
$dialog->run;
$dialog->destroy;
}
# HTML fetch
sub fetch_html {
my $dialog = Gtk3::Dialog->new('HTML Source Fetch', $window, 'modal', 'gtk-ok', 'ok', 'gtk-cancel', 'cancel');
my $label = Gtk3::Label->new('Fetch what:');
my $entry = Gtk3::Entry->new;
$entry->set_text('http://');
$dialog->get_content_area->pack_start($label, FALSE, FALSE, 0);
$dialog->get_content_area->pack_start($entry, FALSE, FALSE, 0);
$dialog->show_all;
if ($dialog->run eq 'ok') {
my $url = $entry->get_text;
my $contents = get($url);
if ($contents) {
$textbuffer->set_text($contents);
$statusbar->push(0, "Fetched $url");
} else {
$statusbar->push(0, "Failed to fetch $url");
}
}
$dialog->destroy;
}
# Plugin dialog
sub show_plugin_dialog {
my $dialog = Gtk3::Dialog->new('Macro Execution Menu', $window, 'modal', 'gtk-close', 'close');
my $label = Gtk3::Label->new('Double Click To Execute Macro');
my $listbox = Gtk3::ListBox->new;
$dialog->get_content_area->pack_start($label, FALSE, FALSE, 0);
$dialog->get_content_area->pack_start($listbox, TRUE, TRUE, 0);
foreach my $name (@n) {
next if $name eq 'auto';
my $row = Gtk3::ListBoxRow->new;
my $lbl = Gtk3::Label->new($name);
$row->add($lbl);
$listbox->add($row);
}
$listbox->signal_connect(row_activated => sub {
my ($lb, $row) = @_;
my $name = $row->get_child->get_text;
eplugin($name);
});
$dialog->show_all;
$dialog->run;
$dialog->destroy;
}
# Plugin execution
sub eplugin {
my ($v) = @_;
my $fp = 0;
while ($n[$fp] ne $v) { $fp++; }
$v = $plugins[$fp];
open my $pe, '<', "$basedir/$v" or return;
my $tdata = do { local $/; <$pe> };
close $pe;
$tdata =~ s/.*?\n//; # Skip first line
eval $tdata;
if ($@) {
my $error = $@;
my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'error', 'ok', "Error: $error");
$dialog->run;
$dialog->destroy;
}
}
# Auto plugin execution
sub aeplugin {
for my $i (0..$#n) {
next unless $n[$i] eq 'auto';
my $v = $plugins[$i];
open my $pe, '<', "$basedir/$v" or next;
my $tdata = do { local $/; <$pe> };
close $pe;
$tdata =~ s/.*?\n//;
eval $tdata;
if ($@) {
my $error = $@;
my $dialog = Gtk3::MessageDialog->new($window, 'modal', 'error', 'ok', "Error: $error");
$dialog->run;
$dialog->destroy;
}
}
}
# Shutdown handler
sub tapp {
if ($track ne 'init') {
my $dialog = Gtk3::MessageDialog->new(
$window, 'modal', 'question', 'yes-no', 'File contents have changed, save now?!'
);
my $response = $dialog->run;
$dialog->destroy;
if ($response eq 'yes') {
save_file();
}
}
Gtk3->main_quit;
}
# Run auto plugins
my $arun = 0;
if ($arun eq '0') {
aeplugin();
}
# Show and start
$window->show_all;
Gtk3->main;

0
normal.kpd Normal file → Executable file
View file

1
test.txt Executable file
View file

@ -0,0 +1 @@
moo

3
test2.txt Executable file
View file

@ -0,0 +1,3 @@
moo beer
moo beer
moo beer beef