gimp/plug-ins/perl/examples/PDB
2000-03-28 13:30:29 +00:00

515 lines
14 KiB
Perl
Executable file

#!/usr/app/bin/perl
eval 'exec /usr/app/bin/perl -S $0 ${1+"$@"}'
if 0; # not running under some shell
#BEGIN {$^W=1};
use Gimp::Feature qw(perl-5.005 gtk-1.2);
use Gimp (':consts','__','N_');
use Gimp::Fu;
use Gtk;
use Gtk::Gdk;
use Gimp::UI (); # for the logo
use POSIX 'strftime';
#Gimp::set_trace(TRACE_ALL);
my $ex; # average font width for default font
my $ey; # average font height for default font
my $window; # the main window
my $clist; # the list of completions
my $rlist; # the results list
my $inputline; # the input entry
my $result; # the result entry
my $synopsis; # the synopsis label
my $statusbar; # the statusbar
my $cinfo; # command info
my $idle; # the idle function id
my($blurb,$help,$author,$copyright,$date,$type,$args,$results);
my @args; # the arguments of the current function
my @function; # the names of all functions
my %function; # the same as hash
my @completion; # list of completions
my @compdata; # index completion -> data
my %plugin_info;# function -> [...]
sub refresh {
undef %function;
@function = Gimp->procedural_db_query("","","","","","","");
@function{@function}=(1) x @function;
eval {
my ($a, $b, $c, $d, $e, $f) = Gimp->plugins_query("");
for $i (0..$#$a) {
$plugin_info{$f->[$i]} = [map $_->[$i], $a, $b, $c, $d, $e];
}
}
}
sub get_words {
my $text = $inputline->get_text;
my $i = 0;
my($p,$idx,$pos);
my $word;
my @words;
substr($text,$inputline->get('text_position'),0,"\0");
while ($text =~ /("(?:[^"\\]*(?:\\.[^"\\]*)*)")[ ,]*|([^ ,]+)[ ,]*|[ ,]+/g) {
$word = defined $1 ? $1 : $2;
if (($p = index($word, "\0")) >= 0) {
$idx=$i; $pos=$p;
substr ($word, $p, 1, "");
}
$i++;
push(@words,$word);
}
($idx,$pos,@words);
}
sub set_words {
my $text=shift;
$text.=" ".join(",",@_) if scalar@_;
my $pos=index($text,"\0");
if ($pos) {
substr($text,$pos,1,"");
$inputline->set_text($text);
$inputline->set_position($pos);
} else {
$inputline->set_text($text);
}
}
my $last_func;
my $last_arg;
my %type2str = (
&PARAM_BOUNDARY => 'BOUNDARY',
&PARAM_CHANNEL => 'CHANNEL',
&PARAM_COLOR => 'COLOR',
&PARAM_DISPLAY => 'DISPLAY',
&PARAM_DRAWABLE => 'DRAWABLE',
&PARAM_FLOAT => 'FLOAT',
&PARAM_IMAGE => 'IMAGE',
&PARAM_INT32 => 'INT32',
&PARAM_FLOATARRAY => 'FLOATARRAY',
&PARAM_INT16 => 'INT16',
&PARAM_PARASITE => 'PARASITE',
&PARAM_STRING => 'STRING',
&PARAM_PATH => 'PATH',
&PARAM_INT16ARRAY => 'INT16ARRAY',
&PARAM_INT8 => 'INT8',
&PARAM_INT8ARRAY => 'INT8ARRAY',
&PARAM_LAYER => 'LAYER',
&PARAM_REGION => 'REGION',
&PARAM_STRINGARRAY => 'STRINGARRAY',
&PARAM_SELECTION => 'SELECTION',
&PARAM_STATUS => 'STATUS',
&PARAM_INT32ARRAY => 'INT32ARRAY',
);
sub setheight {
my($w,$y)=@_;
$w->set_usize(-1, ($w->style->font->ascent + $w->style->font->descent) * $y);
}
sub new_cinfo {
$cinfo->freeze;
$cinfo->clear;
my $add_split = sub {
my($t,$n,$d)=@_;
$d=~s/^(.{40,60})[ \t]*([\{[:\(])/$1\n$2/mg;
for(split/\n/,Gimp::wrap_text($d,60)) {
$cinfo->append("",$t,$n,$_);
$t=$n="";
}
};
if($args) {
$cinfo->append("In:","","","");
for(@args) {
$add_split->($type2str{$_->[0]},$_->[1],$_->[2]);
}
}
if($results) {
$cinfo->append("Out:","","","");
for(0..$results-1) {
my($type,$name,$desc)=Gimp->procedural_db_proc_val ($last_func, $_);
$add_split->($type2str{$type},$name,$desc);
}
}
$cinfo->thaw;
}
sub set_current_function {
my $fun = shift;
return if $last_func eq $fun || !$function{$fun};
$last_func = $fun;
$last_arg = 0;
@args=();
eval {
$function{$fun} or die;
($blurb,$help,$author,$copyright,$date,$type,$args,$results)=
Gimp->procedural_db_proc_info($fun);
$blurb_label->set($blurb);
for(0..$args-1) {
push(@args,[Gimp->procedural_db_proc_arg($fun,$_)]);
}
new_cinfo;
$help_text->delete_text(0,-1);
$help_text->insert_text($help,0);
$author_label->set($author);
$copyright_label->set($copyright);
$date_label->set($date);
eval {
my ($menupath, $accel, $path, $imagetypes, $mtime) = @{$plugin_info{$fun}};
$menupath_label->set($menupath);
$accelerator_label->set($accel);
$plugin_path_label->set($path);
$imagetypes_label->set($imagetypes);
$last_modified_label->set(strftime("%Y-%m-%d %H:%M:%S (%Z)", localtime ($mtime)));
};
}
}
sub set_clist {
$clist->freeze;
$clist->clear;
@completion = ();
@compdata = ();
while(@_) {
$clist->append(@_[0]);
push @completion, shift;
push @compdata, shift;
}
$clist->thaw;
}
sub complete_function {
my $name = shift;
$name=~s/[-_]/[-_]/g;
my @matches = eval { sort grep /$name/i,@function };
if(@matches>1) {
set_clist map(($_,$_),@matches);
$synopsis->set(scalar@matches.__" matching functions");
} else {
set_clist @matches,@matches;
$synopsis->set($matches[0].__" (press Tab to complete)");
}
}
sub complete_type {
my($type,$name,$desc)=@_;
if($type==PARAM_IMAGE) {
set_clist(map(("$$_: ".$_->get_filename,$$_),Gimp->image_list));
} elsif($type==PARAM_LAYER) {
set_clist(map { my $i = $_; map(("$$_: ".$i->get_filename."/".$_->get_name,$$_),$i->get_layers)} Gimp->image_list);
} elsif($type==PARAM_CHANNEL) {
set_clist(map { my $i = $_; map(("$$_: ".$i->get_filename."/".$_->get_name,$$_),$i->get_channels)} Gimp->image_list);
} elsif($type==PARAM_DRAWABLE) {
set_clist(map { my $i = $_; map(("$$_: ".$i->get_filename."/".$_->get_name,$$_),($i->get_layers,$i->get_channels))} Gimp->image_list);
} elsif ($type==PARAM_INT32) {
if ($name eq "run_mode") {
set_clist("RUN_NONINTERACTIVE","RUN_NONINTERACTIVE",
"RUN_INTERACTIVE","RUN_INTERACTIVE",
"RUN_WITH_LAST_VALS","RUN_WITH_LAST_VALS");
} elsif ($desc=~s/(?::\s*)?{(.*)}.*?$//) {
$_=$1;
my @args;
while(s/^.*?([A-Za-z_-]+)\s*\(\s*(\d+)\s*\)//) {
push(@args,"$2: $1",$2);
}
set_clist(@args);
} else {
set_clist;
}
} else {
set_clist;
}
$synopsis->set($desc);
}
sub update_completion {
my($idx,$pos,@words)=get_words;
return unless $idx ne $last_arg;
$last_arg=$idx;
set_current_function $words[0];
if ($idx == 0) {
complete_function($words[0]);
} elsif ($idx>@args) {
$synopsis->set(__"too many arguments");
set_clist;
} else {
complete_type(@{$args[$idx-1]});
}
}
sub do_completion {
update_completion;
my($idx,$pos,@words)=get_words;
my($word)=$words[$idx];
$word=~s/[-_]/[-_]/g;
my(@matches)=grep /$word/i,@completion;
my $new;
if (@matches>1) {
if (join("\n",@matches) =~ ("^(".$words[$idx].".*).*?".("\n\\1.*" x scalar@matches-1))) {
$new=$1;
}
} elsif(@matches==1) {
$new=$compdata[0];
} else {
Gtk::Gdk->beep;
}
if (defined $new) {
$words[$idx]=$new;
set_current_function $words[0] if $idx==0;
if($idx<@args) {
$words[$idx+1]="\0".$words[$idx+1];
} else {
$words[$idx].="\0";
}
set_words @words;
}
undef $last_arg;
}
sub execute_command {
my($idx,$pos,$fun,@args)=get_words;
$res=eval { Gimp->$fun(@args) };
if ($@) {
$statusbar->set($@);
$result->set_text("");
Gtk::Gdk->beep;
} else {
$statusbar->set('');
$result->set_text($res);
$rlist->prepend_items(new Gtk::ListItem $res);
}
}
sub idle {
Gtk->idle_remove($idle) if $idle;
undef $idle;
update_completion;
}
sub do_idle {
$idle=Gtk->idle_add(\&idle) unless $idle;
}
eval "use Gtk::Keysyms ()";
$Gtk::Keysyms{Tab} ||= 0xFF09;
sub inputline {
my $e = new Gtk::Entry;
$e->set_text("");
$e->signal_connect("changed",sub {
undef $last_arg;
do_idle;
});
$e->signal_connect("focus_in_event",\&do_idle);
$e->signal_connect("button_press_event",\&do_idle);
$e->signal_connect("key_press_event",sub {
undef $last_arg;
do_idle;
if ($_[1]->{keyval} == $Gtk::Keysyms{Tab}) {
$_[0]->signal_emit_stop_by_name('key_press_event');
do_completion;
1;
} else {
();
}
});
$e->signal_connect("activate",\&execute_command);
#$e->set_usize($ex*40,0);
$inputline=$e;
my $c = new Gtk::CList(1);
setheight $c, 6;
$clist = $c;
$c->set_selection_mode(-extended);
$c->signal_connect("select_row", sub {
eval {
my($idx,$pos,@words)=get_words;
$words[$idx]=$compdata[$_[1]]."\0";
set_words (@words);
set_current_function (substr($words[0],0,-1)) unless $idx;
};
do_idle;
});
my $r = new Gtk::List;
$rlist = $r;
$r->set_selection_mode(-single);
$r->set_selection_mode(-browse);
}
sub info {
my $info = new Gtk::Dialog;
$info->set_title(__"Function Info");
$info->signal_connect(delete_event => sub { $info->hide });
my $close = new Gtk::Button __"Close";
$close->signal_connect(clicked => sub { $info->hide });
$info->action_area->add($close);
my $table = new Gtk::Table 2,9+1,0;
my $y = 0;
$info->vbox->add($table);
local *add_info = sub {
my ($label, $widget, $large) = @_;
$label = new Gtk::Label $label.": ";
$label->set_alignment(0, 0);
$table->attach($label,0,1,$y,$y+1,[-fill],[-fill],0,0);
if ($large) {
$y++;
$table->attach($widget,0,2,$y,$y+$large,[-expand,-fill],[-expand,-fill],0,0);
$y+=$large;
} else {
$widget->set_alignment(0, 0);
$table->attach($widget,1,2,$y,$y+1,[-fill],[-fill],0,0);
$y++;
}
};
$blurb_label = new Gtk::Label;
add_info(__"Menu Path", $menupath_label = new Gtk::Label);
add_info(__"Accelerator", $accelerator_label = new Gtk::Label);
add_info(__"Image Types", $imagetypes_label = new Gtk::Label);
add_info(__"Author", $author_label = new Gtk::Label);
add_info(__"Copyright", $copyright_label = new Gtk::Label);
add_info(__"Date/Version", $date_label = new Gtk::Label);
add_info(__"Last Modified", $last_modified_label = new Gtk::Label);
add_info(__"Plug-In Path", $plugin_path_label = new Gtk::Label);
$help_text = new Gtk::Text;
$help_text->set_editable(0);
$help_text->set_word_wrap(1);
my $cs = new Gtk::ScrolledWindow undef,undef;
$cs->set_policy(-automatic,-automatic);
$cs->add ($help_text);
add_info (__"Description", $cs, 2);
my $h = new Gtk::HBox(0,5);
my $more = new Gtk::Button __"More...";
$more->signal_connect(clicked => sub { $info->visible ? $info->hide : $info->show_all });
$h->add($blurb_label);
$h->add($more);
$h;
}
sub create_main {
my $b;
my $t;
$t = new Gtk::Tooltips;
my $w = new Gtk::Dialog;
$window = $w;
$w->realize;
$ex = $w->style->font->string_width ('Mn')*0.5;
$ey = $w->style->font->string_width ('My');
$w->set_title(__"PDB Explorer - the olof edition (yet still an alpha version)");
$w->signal_connect("destroy",sub {main_quit Gtk});
$b = new Gtk::Button __"Close";
$w->action_area->add($b);
$b->signal_connect("clicked",sub {main_quit Gtk});
my $vpane = new Gtk::VPaned; $w->vbox->add($vpane);
$vpane->add1(my $f1 = new Gtk::VBox 0,0);
$vpane->add2(my $f2 = new Gtk::VBox 0,0);
my $h = new Gtk::HBox (0,5);
$f1->pack_start ($h,0,0,0);
inputline;
$synopsis = new Gtk::Label "";
$synopsis->set_justify(-left);
my $table = new Gtk::Table 3,4,0;
$f1->pack_start($table,1,1,0);
my $cs = new Gtk::ScrolledWindow undef,undef;
$cs->set_policy(-automatic,-automatic);
$cs->add ($clist);
#$cs->set_usize(0,$ey*6);
my $rs = new Gtk::ScrolledWindow undef,undef;
$rs->set_policy(-automatic,-automatic);
$rs->add_with_viewport ($rlist);
$result = new Gtk::Entry;
$result->set_editable(0);
#$result->set_usize($ex*30,0);
$statusbar = new Gtk::Label;
$table->border_width(10);
$table->attach(new Gtk::Label(__"Synopsis") ,0,1,0,1,{},{},0,0);
$table->attach($synopsis ,1,2,0,1,{},{},0,0);
$table->attach(Gimp::UI::logo($w),2,3,0,1,{},{},0,0);
$table->attach(new Gtk::Label(__"Command") ,0,1,1,2,{},{},0,0);
$table->attach($inputline,1,3,1,2,['expand','fill'],{},0,0);
# $table->attach($result,2,3,1,2,['expand','fill'],{},0,0);
$table->attach(new Gtk::Label(__"Shortcuts"),0,1,2,3,{},{},0,0);
$table->attach($cs ,1,3,2,3,['expand','fill'],['expand','fill'],0,0);
# $table->attach($rs,2,3,2,3,['expand','fill'],['expand','fill'],0,0);
$table->attach(new Gtk::Label(__"Status"),0,1,3,4,{},{},0,0);
$table->attach($statusbar,1,3,3,4,{},{},0,0);
$f2->pack_start(info,0,1,5);
my $sw = new Gtk::ScrolledWindow;
$sw->set_policy(-automatic, -automatic);
$cinfo = new_with_titles Gtk::CList '',__"TYPE",__"NAME",__"DESCRIPTION";
$cinfo->set_column_auto_resize (0,1);
$cinfo->set_column_auto_resize (1,1);
$cinfo->set_column_auto_resize (2,1);
$cinfo->set_selection_mode('single');
setheight $cinfo, 8;
$sw->add ($cinfo);
$f2->pack_start ($sw,1,1,5);
idle;
$w->realize;
show_all $w;
}
register "extension_pdb_explorer",
"Procedural Database Explorer",
"This is a more interactive and less broken / script-fu-centric version of the DB Browser",
"Marc Lehmann",
"Marc Lehmann",
"0.4alpha",
N_"<Toolbox>/Xtns/PDB Explorer",
"",
[],
sub {
Gimp::gtk_init;
refresh;
create_main;
main Gtk;
();
};
exit main;