Gtk3 TextView modifying font size and keeping the current visible text on screen - gtk3

I'm working with a Textview widget and I can modify the font-size of the Textview widget with the Ctrl key + mouse scroll wheel but my displayed text scrolls when I do it.... How do I keep the current text from scrolling up or down while I modify the font size of the Textview widget?
A few notable things about the code. The font-size is changed via styles and provider because I can't figure out how to get Pango to work with a TextView widget. I hardwired the code to retrieve some data from my $str = qx(perldoc -T -t -f split 2>&1)... You'll probably have to change this to your own data source.
#! /usr/bin/env perl
use feature qw(state);
use warnings;
use strict;
use utf8;
use CSS::Tiny;
use constant FONT_UNIT => 4;
use constant MAX_FONT_SIZE => 200;
use constant MIN_FONT_SIZE => 12;
use Glib qw(TRUE FALSE);
use Gtk3 qw(init);
sub killKey {
my ($object, $event) = #_;
my $c = lc chr ($event->keyval);
Gtk3->main_quit if $c eq 'q';
FALSE;
}
sub changeFontSize {
my ($font_size, $amount) = #_;
$font_size += $amount;
$font_size .= "px";
$font_size;
}
sub changeStyle {
my ($css, $provider, $style) = #_;
$style->remove_provider($provider);
$provider->load_from_data($css->write_string);
$style->add_provider(
$provider,
Gtk3::STYLE_PROVIDER_PRIORITY_APPLICATION
);
}
sub catchScroll {
my ($object, $event, $data) = #_;
my ($css, $provider, $style) = #{$data};
my $dir = $event->direction;
my ($ctrl, $mod) = #{$event->state};
my $font_size = $css->{textview}->{q<font-size>};
{
no warnings qw<numeric>;
$font_size += 0;
}
if ($ctrl eq q<control-mask> && $mod eq q<mod2-mask>) {
if ($dir eq q<up>) {
$css->{textview}->{q<font-size>} =
changeFontSize($font_size, FONT_UNIT)
if $font_size < MAX_FONT_SIZE;
}else{
$css->{textview}->{q<font-size>} =
changeFontSize($font_size, -FONT_UNIT)
if $font_size > MIN_FONT_SIZE;
}
changeStyle($css, $provider, $style);
}
FALSE;
}
my $window = Gtk3::Window->new(q<toplevel>);
my $scrWin = Gtk3::ScrolledWindow->new();
my $tView = Gtk3::TextView->new();
my $provider = Gtk3::CssProvider->new();
$provider->load_from_path("the.css");
my $style = $tView->get_style_context();
$style->add_provider($provider, Gtk3::STYLE_PROVIDER_PRIORITY_APPLICATION);
my $css = CSS::Tiny->read_string($provider->to_string);
#hardwired data for the TextView
my $str = qx(perldoc -T -t -f split 2>&1);
my $buf = $tView->get_buffer();
$buf->set_text($str);
my $sIter = $buf->get_start_iter();
$buf->place_cursor($sIter);
$tView->set_editable(FALSE);
$window->add_events(q<GDK_KEY_PRESS_MASK>);
$window->add_events(q<GDK_SCROLL_MASK>);
$window->signal_connect(delete_event => sub{Gtk3->main_quit; FALSE});
$window->signal_connect(key_press_event => \&killKey);
$window->signal_connect(
scroll_event =>
\&catchScroll,
[$css, $provider, $style]
);
$window->set_decorated(FALSE);
$window->set_position(q<center>);
$window->fullscreen;
$scrWin->add($tView);
$window->add($scrWin);
$window->show_all;
Gtk3::main;
the.css
text {
background-color: rgb(0,0,0);
color: rgb(0,255,0);
}
textview {
font-size: 40px;
}

Related

Parsing a dynamic page with Perl and Gtk3::WebKit

I am using the Gtk3::WebKit to parse a website which loads content to itself with JavaScript. When the site is loaded it adds multiple divs with content to the DOM:
<div class="product-card">...</div>
How do I get this content with Gtk3::WebKit? How to get the nested tag's content? Is there a normal documentation about Gtk3::WebKit, because everything I've already seen is very poor documented.
Here is an example using Gtk3::WebKit2 :
use feature qw(say);
use strict;
use warnings;
use Gtk3 -init;
use Gtk3::WebKit2;
use Gtk3::JavaScriptCore;
{
my $url = 'https://metacpan.org/pod/Gtk3::WebKit2';
my $window = Gtk3::Window->new('toplevel');
$window->set_default_size(800, 600);
$window->signal_connect(destroy => sub { Gtk3->main_quit() });
my $ctx = Gtk3::WebKit2::WebContext::get_default();
my $view = Gtk3::WebKit2::WebView->new_with_context($ctx);
$view->signal_connect('load-changed', sub {
my ($view, $load_event) = #_;
if ($load_event eq 'finished') {
run_javascript(
$view,
'document.getElementsByClassName("logged_out")[1].innerHTML;'
);
}
});
$view->load_uri($url);
my $scrolls = Gtk3::ScrolledWindow->new();
$scrolls->add($view);
$window->add($scrolls);
$window->show_all();
Gtk3::main_iteration while Gtk3::events_pending;
Gtk3->main;
}
sub run_javascript {
my ($view, $javascript_string) = #_;
my $done = 0;
$view->run_javascript($javascript_string, undef, sub {
my ($object, $result, $user_data) = #_;
my $value = $view->run_javascript_finish($result)->get_js_value;
say $value->to_string;
$done = 1;
return "ok";
}, undef);
Gtk3::main_iteration while Gtk3::events_pending and not $done;
}
Output:
<a href="" onclick="alert('Please sign in to add favorites'); return false" class="favorite highlight" title="Add to favorites">
<span>2</span> ++</a>

PERL tk module handling

I am using tk module to build a checklist for user to choose. I am totally beginner to this module. Currently the checklist created is all in the same series column, since it will be a long list, any idea how could I separate few of the options to another column? Also, any idea how to align those text to the left while checkbox to the right? To display it cleanly and neatly.
#!/usr/bin/perl
use Tk;
$main = MainWindow->new();
$label = $main->Label(-text => "Presence Check");
$label->pack();
$frame = $main->Frame(-relief=>"groove", -borderwidth=>2);
#$frame = $main->Frame(-relief=>"groove", -borderwidth=>2)->pack(-side => 'top', -expand => 1, -fill =>'both');
#$frame = $main->Frame->pack(-side => 'left', -fill => 'x');
$check1 = $frame->Checkbutton(-text=>"Document A (docx, pdf)",
-variable=>\$a,
-onvalue=>"APRESENT",
-offvalue=>"AABSENT");
#$check1->pack(-side=>"top");
$check2 = $frame->Checkbutton(-text=>"Document B (xlsx)",
-variable=>\$b,
-onvalue=>"BPRESENT",
-offvalue=>"BABSENT");
$check2->pack(-side=>"top");
$check3 = $frame->Checkbutton(-text=>"C specification",
-variable=>\$c,
-onvalue=>"CPRESENT",
-offvalue=>"CABSENT");
$check3->pack(-side=>"top");
$check4 = $frame->Checkbutton(-text=>"A-Specification",
-variable=>\$aspec,
-onvalue=>"ASPECPRESENT",
-offvalue=>"ASPECSABSENT");
$check4->pack(-side=>"top");
$check5 = $frame->Checkbutton(-text=>"Important Report",
-variable=>\$report,
-onvalue=>"REPORTPRESENT",
-offvalue=>"REPORTSABSENT");
$check5->pack(-side=>"top");
$check6 = $frame->Checkbutton(-text=>"Handbook",
-variable=>\$handbook,
-onvalue=>"HANDBOOKPRESENT",
-offvalue=>"HANDBOOKABSENT");
$check6->pack(-side=>"top");
$check7 = $frame->Checkbutton(-text=>"Data Spreadsheet",
-variable=>\$dataxls,
-onvalue=>"DATAPRESENT",
-offvalue=>"DATAABSENT");
$check7->pack(-side=>"top");
$check8 = $frame->Checkbutton(-text=>"D file",
-variable=>\$dfile,
-onvalue=>"DFILEPRESENT",
-offvalue=>"DFILEABSENT");
$check8->pack(-side=>"top");
$check10 = $frame->Checkbutton(-text=>"xx doc",
-variable=>\$xxdoc,
-onvalue=>"XXDOCPRESENT",
-offvalue=>"XXDOCABSENT");
$check10->pack(-side=>"top");
$check18 = $frame->Checkbutton(-text=>"yy Doc",
-variable=>\$yydoc,
-onvalue=>"YYDOCPRESENT",
-offvalue=>"YYDOCABSENT");
$check18->pack(-side=>"top");
$frame->pack();
$button = $main->Button(-text => "Exit",
-command => \&exit_button);
$button->pack();
MainLoop();
sub exit_button {
print "$a $b $c $aspec $report $handbook $dataxls $dfile $xxdoc $yydoc \n";
#print "$rv\n";
exit,
}
... any idea how could I separate few of the options to another
column? Also, any idea how to align those text to the left while
checkbox to the right?
Here is an example of how you can use the grid geometry manager to align the labels and checkboxes:
use feature qw(say);
use strict;
use warnings;
use Tk;
my $mw = MainWindow->new();
my #label_texts = (
"Document A (docx, pdf)",
"Document B (xlsx)",
"C specification"
);
my #labels;
my #checkvars;
my #check_buttons;
for my $text ( #label_texts ) {
my $label = $mw->Label(-text => $text);
push #labels, \$label;
my $check_var;
my $check = $mw->Checkbutton(
-text=>"",
-variable=>\$check_var,
);
push #checkvars, \$check_var;
push #check_buttons, \$check;
Tk::grid($label, $check, -sticky => 'w');
}
my $button = $mw->Button(
-text => "Exit",
-command => \&exit_button
);
Tk::grid($button, "-");
MainLoop();
sub exit_button {
for my $i (0..$#checkvars) {
my $var = $checkvars[$i];
my $state = $$var;
$state = 'undef' if !defined $state;
say "Variable $i: ", $state;
}
exit;
}
Please see attached screenshot to this code snippet if it is what you desired
!/usr/bin/perl
#
# vim: ai:ts=4:sw=4
#
use strict;
use warnings;
use feature 'say';
use Tk;
use Data::Dumper;
my $main = MainWindow->new( -title => 'Checkboxes' );
my $label = $main->Label(-text => 'Presence Check');
$label->pack();
my $frame = $main->Frame(-relief=>'groove', -borderwidth=>2);
my $values;
my #chk_boxes = (
[\$values->{doc_a},'Document A (docx, pdf)'],
[\$values->{doc_b},'Document B (xlsx)'],
[\$values->{spec_c},'C specification'],
[\$values->{spec_a},'A-Specification'],
[\$values->{report},'Important Report'],
[\$values->{hand_b},'Handbook'],
[\$values->{data_s},'Data Spreadsheet'],
[\$values->{data_d},'D file'],
[\$values->{doc_xx},'xx doc'],
[\$values->{doc_yy},'yy Doc']
);
check_box(\#chk_boxes);
$frame->pack();
my $button = $main->Button(
-text => 'Exit',
-command => \&exit_button
);
$button->pack();
MainLoop();
sub exit_button {
while( my($k,$v) = each %{$values} ) {
say "$k => $v" if $v;
}
exit;
}
sub check_box {
my $data = shift;
my $index = 0;
for my $chk_box ( #{$data} ) {
my $column = $index % 2;
my $row = int($index/2);
$frame->Checkbutton(
-text => $chk_box->[1],
-variable => $chk_box->[0]
)->grid( -column => $column, -row => $row, -sticky => 'w');
$index++;
}
}
Perhaps following form of data input may be preferable
#!/usr/bin/perl
#
# vim: ai:ts=4:sw=4
#
use strict;
use warnings;
use feature 'say';
use Tk;
use Data::Dumper;
my $main = MainWindow->new( -title => 'Checkboxes' );
my $label = $main->Label(-text => 'Presence Check');
$label->pack();
my $frame = $main->Frame(-relief=>'groove', -borderwidth=>2);
my $values;
my #chk_boxes;
while( <DATA> ) {
chomp;
my($var,$desc) = split '\t';
push #chk_boxes, [\$values->{$var}, $desc];
}
check_box(\#chk_boxes);
$frame->pack();
my $button = $main->Button(
-text => 'Exit',
-command => \&exit_button
);
$button->pack();
MainLoop();
sub exit_button {
while( my($k,$v) = each %{$values} ) {
say "$k => $v" if $v;
}
exit;
}
sub check_box {
my $data = shift;
my $index = 0;
for my $chk_box ( #{$data} ) {
my $column = $index % 2;
my $row = int($index/2);
$frame->Checkbutton(
-text => $chk_box->[1],
-variable => $chk_box->[0]
)->grid( -column => $column, -row => $row, -sticky => 'w');
$index++;
}
}
__DATA__
doc_a Document A (docx, pdf)
doc_b Document B (xlsx)
spec_c C specification
spec_a A-Specification
report Important Report
hand_b Handbook
data_s Data Spreadsheet
data_d D file
doc_xx xx Doc
doc_yy yy Doc
Add-on:
sub check_box {
my $data = shift;
my $index = 0;
for my $chk_box ( #{$data} ) {
my $column = $index % 2;
my $row = int($index/2);
my $chk_x = $frame->Checkbutton(
-text => $chk_box->[1],
-variable => $chk_box->[0]
)->grid( -column => $column, -row => $row, -sticky => 'w');
$chk_x->select if $index == 4;
$index++;
}
}

Change font size for a TextView widget in a running Gtk3 Perl app

I'm trying to modify the font size, in a TextView widget, in a running Perl application and I'm having a difficult time of it. Here's one of my attempts(the relevant code is in the function setFontSize).
#! /usr/bin/env perl
use feature qw(state);
use local::lib;
use warnings;
use strict;
use utf8;
use constant MAX_FONT_SIZE => 200;
use constant MIN_FONT_SIZE => 12;
use constant FONT_UNIT => 4;
use Glib qw(TRUE FALSE);
use Gtk3 qw(init);
use Pango;
sub killKey {
my ($object, $event) = #_;
my $c = lc chr ($event->keyval);
Gtk3->main_quit if $c eq 'q';
FALSE;
}
sub setFontSize {
my ($tView, $font_size) = #_;
my $pCont = $tView->get_pango_context();
my $fDescr = $pCont->get_font_description();
$fDescr->set_absolute_size($font_size * Pango::SCALE);
my $buf = $tView->get_buffer();
my $end = $buf->get_end_iter();
my $start = $buf->get_start_iter();
my $str = $buf->get_text($start, $end, TRUE);
my $newBuf = Gtk3::TextBuffer->new();
$newBuf->set_text($str);
#$buf->set_text($buf->get_text($start, $end, TRUE), -1);
$tView->set_buffer($newBuf);
STDOUT->print("called: ", $fDescr->to_string(),"\n");
# {
# no strict "refs";
#
# for my $elem (sort keys %{ref($fDescr)."::"}) {
# STDOUT->print("${elem}\n");
# }
# }
}
sub catchScroll {
my ($object, $event, $tView) = #_;
state $font_size = 40;
my $dir = $event->direction;
my ($ctrl, $mod) = #{$event->state};
if ($ctrl eq q<control-mask> && $mod eq q<mod2-mask>) {
if ($dir eq q<up>) {
$font_size += FONT_UNIT if $font_size < MAX_FONT_SIZE;
setFontSize($tView, $font_size);
return FALSE;
}elsif ($dir eq q<down>) {
$font_size -= FONT_UNIT if $font_size > MIN_FONT_SIZE;
setFontSize($tView, $font_size);
return FALSE;
}
}
FALSE;
}
my $window = Gtk3::Window->new(q<toplevel>);
my $scrWin = Gtk3::ScrolledWindow->new();
my $tView = Gtk3::TextView->new();
my $provider = Gtk3::CssProvider->new();
$provider->load_from_path("the.css");
my $style = $tView->get_style_context();
$style->add_provider($provider, 600);
my $str = qx(perldoc -T -t -f split 2>&1);
my $buf = $tView->get_buffer();
$buf->set_text($str);
my $sIter = $buf->get_start_iter();
$buf->place_cursor($sIter);
#$tView->set_editable(FALSE);
$window->add_events(q<GDK_KEY_PRESS_MASK>);
$window->add_events(q<GDK_SCROLL_MASK>);
$window->signal_connect(delete_event => sub{Gtk3->main_quit; FALSE});
$window->signal_connect(key_press_event => \&killKey);
$window->signal_connect(scroll_event => \&catchScroll, $tView);
$window->set_border_width(0);
$window->fullscreen();
$window->set_decorated(FALSE);
$scrWin->add($tView);
$window->add($scrWin);
$window->show_all;
Gtk3->main;
I can hack the font values from the provider and it works perfectly... Kind of makes you wonder why the Gtk3 folks don't have that functionality available for these types of problems.
the.css
text {
background-color: rgb(0,0,0);
color: rgb(0, 255, 0);
}
textview {
font-size: 40px;
}
The final version: I added CSS::Tiny to handle the css in provider and added functionality to freeze the vertical scroll bar while I modify the font size with Ctrl+mouse wheel.
#! /usr/bin/env perl
use feature qw(state);
use warnings;
use strict;
use utf8;
use constant MAX_FONT_SIZE => 200;
use constant MIN_FONT_SIZE => 20;
use constant FONT_UNIT => 4;
use constant FONT_START_SIZE => 40;
use CSS::Tiny;
use Glib qw(TRUE FALSE);
use Gtk3 qw(init);
sub killKey {
my ($object, $event) = #_;
my $c = lc chr ($event->keyval);
Gtk3->main_quit if $c eq 'q';
}
sub changeFontSize {
my ($style, $provider, $font_size) = #_;
my $cssStr = $provider->to_string();
my $css = CSS::Tiny->new();
$css = CSS::Tiny->read_string($cssStr);
$css->{textview}->{q<font-size>} = "${font_size}px";
$style->remove_provider($provider);
$provider = Gtk3::CssProvider->new();
$provider->load_from_data($css->write_string());
$style->add_provider($provider, 600);
}
sub catchScroll {
my ($object, $event, $data) = #_;
my ($style, $provider, $vadj) = #{$data};
my ($ctrl, $mod) = #{$event->state};
my $dir = $event->direction;
my $retval = Gtk3::get_current_event();
state $font_size = FONT_START_SIZE;
if ($ctrl eq q<control-mask> && $mod eq q<mod2-mask>) {
if ($dir eq q<up>) {
$font_size += FONT_UNIT if $font_size < MAX_FONT_SIZE;
changeFontSize($style, $provider, $font_size);
}elsif ($dir eq q<down>) {
$font_size -= FONT_UNIT if $font_size > MIN_FONT_SIZE;
changeFontSize($style, $provider, $font_size);
}else {
}
$vadj->set_value(0.0);
}
FALSE;
}
my $window = Gtk3::Window->new(q<toplevel>);
my $scrWin = Gtk3::ScrolledWindow->new();
my $tView = Gtk3::TextView->new();
my $provider = Gtk3::CssProvider->new();
$provider->load_from_path(q<the.css>);
my $style = $tView->get_style_context();
$style->add_provider($provider, 600);
my $str = qx(perldoc -T -t -f split 2>&1);
my $buf = $tView->get_buffer();
$buf->set_text($str);
my $sIter = $buf->get_start_iter();
$buf->place_cursor($sIter);
$tView->set_editable(FALSE);
$window->add_events(q<GDK_KEY_PRESS_MASK>);
$window->add_events(q<GDK_SCROLL_MASK>);
$window->signal_connect(delete_event => sub{Gtk3->main_quit; FALSE});
$window->signal_connect(key_press_event => \&killKey);
$window->signal_connect(
scroll_event =>
\&catchScroll,
[$style, $provider, $scrWin->get_vadjustment()]
);
$window->set_border_width(0);
$window->set_position(q<center>);
$window->fullscreen();
$window->set_decorated(FALSE);
$scrWin->add($tView);
$window->add($scrWin);
$window->show_all();
Gtk3->main;
the.css
text {
background-color: rgb(0, 0, 0);
color: rgb(0, 255, 0);
}
textview {
font-size: 40px;
}
Currently I am not able to make the Pango font description to work either. Seems like it is difficult to find a good documentation of how this is supposed to work :)
Anyway, as a workaround, a CSS provider can be used:
sub setFontSize {
my ($tView, $font_size) = #_;
state $provider;
my $context = $tView->get_style_context();
if ( $provider ) {
$context->remove_provider( $provider );
}
$provider = Gtk3::CssProvider->new();
my $css = sprintf "textview {font-size: %spx;}", $font_size;
$provider->load_from_data($css);
$context->add_provider($provider, Gtk3::STYLE_PROVIDER_PRIORITY_APPLICATION);
}

Find unused "use'd" Perl modules

I am working on a very large, very old "historically grown" codebase. In the past, there were often people thinking "Oh, I may need this and that module, so I just include it...", and later, people often "cached" Data inside of modules ("use ThisAndThat" needing a few seconds to load some hundred MB from DB to RAM, yeah, its really a stupid Idea, we are working on that too) and so, often, we have a small module use'ing like 20 or 30 modules, from who 90% are totally unused in the source itself, and, because of "caching" in several use'd submodules, modules tend to take up one minute to load or even more, which is, of course, not acceptable.
So, Im trying to get that done better. Right now, my way is looking through all the modules, understanding them as much as possible and I look at all the modules including them and see whether they are needed or not.
Is there any easier way? I mean: There are functions returning all subs a module has like
...
return grep { defined &{"$module\::$_"} } keys %{"$module\::"}
, so, aint there any simple way to see which ones are exported by default and which ones come from where and are used in the other modules?
A simple example is Data::Dumper, which is included in nearly every file, even, when all debug-warns and prints and so on arent in the script anymore. But still the module has to load Data::Dumper.
Is there any simple way to check that?
Thanks!
The following code could be part of your solution - it will show you which symbols are imported for each instance of use:
package traceuse;
use strict;
use warnings;
use Devel::Symdump;
sub import {
my $class = shift;
my $module = shift;
my $caller = caller();
my $before = Devel::Symdump->new($caller);
my $args = \#_;
# more robust way of emulating use?
eval "package $caller; require $module; $module\->import(\#\$args)";
my $after = Devel::Symdump->new($caller);
my #added;
my #after_subs = $after->functions;
my %before_subs = map { ($_,1) } $before->functions;
for my $k (#after_subs) {
push(#added, $k) unless $before_subs{$k};
}
if (#added) {
warn "using module $module added: ".join(' ', #added)."\n";
} else {
warn "no new symbols from using module $module\n";
}
}
1;
Then just replace "use module ..." with "use traceuse module ...", and you'll get a list of the functions that were imported.
Usage example:
package main;
sub foo { print "debug: foo called with: ".Dumper(\#_)."\n"; }
use traceuse Data::Dumper;
This will output:
using module Data::Dumper added: main::Dumper
i.e. you can tell which functions were imported in robust way. And you can easily extend this to report on imported scalar, array and hash variables - check the docs on Devel::Symdump.
Determine which functions are actually used is the other half of the equation. For that you might be able to get away with a simple grep of your source code - i.e. does Dumper appear in the module's source code that's not on a use line. It depends on what you know about your source code.
Notes:
there may be a module which does what traceuse does - I haven't checked
there might be a better way to emulate "use" from another package
I kind of got of got it to work with PPI. It looks like this:
#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;
use Term::ANSIColor;
use PPI;
use PPI::Dumper;
my %doneAlready = ();
$" = ", ";
our $maxDepth = 2;
my $showStuffOtherThanUsedOrNot = 0;
parse("/modules/Test.pm", undef, undef, 0);
sub parse {
my $file = shift;
my $indent = shift || 0;
my $caller = shift || $file;
my $depth = shift || 0;
if($depth && $depth >= $maxDepth) {
return;
}
return unless -e $file;
if(exists($doneAlready{$file}) == 1) {
return;
}
$doneAlready{$file} = 1;
my $skript = PPI::Document->new($file);
my #included = ();
eval {
foreach my $x (#{$skript->find("PPI::Statement::Include")}) {
foreach my $y (#{$x->{children}}) {
push #included, $y->{content} if (ref $y eq "PPI::Token::Word" && $y->{content} !~ /^(use|vars|constant|strict|warnings|base|Carp|no)$/);
}
}
};
my %double = ();
print "===== $file".($file ne $caller ? " (Aufgerufen von $caller)" : "")."\n" if $showStuffOtherThanUsedOrNot;
if($showStuffOtherThanUsedOrNot) {
foreach my $modul (#included) {
next unless -e createFileName($modul);
my $is_crap = ((exists($double{$modul})) ? 1 : 0);
print "\t" x $indent;
print color("blink red") if($is_crap);
print $modul;
print color("reset") if($is_crap);
print "\n";
$double{$modul} = 1;
}
}
foreach my $modul (#included) {
next unless -e createFileName($modul);
my $anyUsed = 0;
my $modulDoc = parse(createFileName($modul), $indent + 1, $file, $depth + 1);
if($modulDoc) {
my #exported = getExported($modulDoc);
print "Exported: \n" if(scalar #exported && $showStuffOtherThanUsedOrNot);
foreach (#exported) {
print(("\t" x $indent)."\t");
if(callerUsesIt($_, $file)) {
$anyUsed = 1;
print color("green"), "$_, ", color("reset") if $showStuffOtherThanUsedOrNot;
} else {
print color("red"), "$_, ", color("reset") if $showStuffOtherThanUsedOrNot;
}
print "\n" if $showStuffOtherThanUsedOrNot;
}
print(("\t" x $indent)."\t") if $showStuffOtherThanUsedOrNot;
print "Subs: " if $showStuffOtherThanUsedOrNot;
foreach my $s (findAllSubs($modulDoc)) {
my $isExported = grep($s eq $_, #exported) ? 1 : 0;
my $rot = callerUsesIt($s, $caller, $modul, $isExported) ? 0 : 1;
$anyUsed = 1 unless $rot;
if($showStuffOtherThanUsedOrNot) {
print color("red") if $rot;
print color("green") if !$rot;
print "$s, ";
print color("reset");
}
}
print "\n" if $showStuffOtherThanUsedOrNot;
print color("red"), "=========== $modul wahrscheinlich nicht in Benutzung!!!\n", color("reset") unless $anyUsed;
print color("green"), "=========== $modul in Benutzung!!!\n", color("reset") if $anyUsed;
}
}
return $skript;
}
sub createFileName {
my $file = shift;
$file =~ s#::#/#g;
$file .= ".pm";
$file = "/modules/$file";
return $file;
}
sub getExported {
my $doc = shift;
my #exported = ();
eval {
foreach my $x (#{$doc->find("PPI::Statement")}) {
my $worthATry = 0;
my $isMatch = 0;
foreach my $y (#{$x->{children}}) {
$worthATry = 1 if(ref $y eq "PPI::Token::Symbol");
if($y eq '#EXPORT') {
$isMatch = 1;
} elsif($isMatch && ref($y) ne "PPI::Token::Whitespace" && ref($y) ne "PPI::Token::Operator" && $y->{content} ne ";") {
push #exported, $y->{content};
}
}
}
};
my #realExported = ();
foreach (#exported) {
eval "\#realExported = $_";
}
return #realExported;
}
sub callerUsesIt {
my $subname = shift;
my $caller = shift;
my $namespace = shift || undef;
my $isExported = shift || 0;
$caller = `cat $caller`;
unless($namespace) {
return 1 if($caller =~ /\b$subname\b/);
} else {
$namespace = createPackageName($namespace);
my $regex = qr#$namespace(?:::|->)$subname#;
if($caller =~ $regex) {
return 1;
}
}
return 0;
}
sub findAllSubs {
my $doc = shift;
my #subs = ();
eval {
foreach my $x (#{$doc->find("PPI::Statement::Sub")}) {
my $foundName = 0;
foreach my $y (#{$x->{children}}) {
no warnings;
if($y->{content} ne "sub" && ref($y) eq "PPI::Token::Word") {
push #subs, $y;
}
use warnings;
}
}
};
return #subs;
}
sub createPackageName {
my $name = shift;
$name =~ s#/modules/##g;
$name =~ s/\.pm$//g;
$name =~ s/\//::/g;
return $name;
}
Its really ugly and maybe not 100% working, but it seems, with the tests that Ive done now, that its good for a beginning.

Can't locate object method "add" via package "Heap"

I'm not sure why perl isn't recognizing the Heap's method add. Getting message given in question title. Here are the most relevant files.
#!/usr/bin/perl -w
use strict;
use Util;
use Heap;
use HuffTree;
my $heap = Heap->new;
my $test = 3;
$heap->add($test); # <--------ERROR HERE-----------
package Heap;
use strict;
use warnings;
use POSIX ();
sub new {
my $class = shift;
my $self = { "aref" => [""],
"next" => 1,
#_};
bless $self, $class;
}
sub print {
my $self = shift;
my $next = $self->{"next"};
my $aref = $self->{"aref"};
print "array => #$aref\n";
print "next => $next\n";
}
sub compare {
my ($self, $i, $j) = #_;
my $x = $self->{"aref"}[$i];
my $y = $self->{"aref"}[$j];
if (!defined $x) {
if (!defined $y) {
return 0;
} else {
return -1;
}
}
return 1 if !defined $y;
return $x->priority <=> $y->priority;
}
sub swap {
my ($self, $i, $j) = #_;
my $aref = $self->{"aref"};
($aref->[$i], $aref->[$j]) = ($aref->[$j], $aref->[$i]);
}
sub add {
my ($self, $value) = #_;
my $i = $self->{"next"};
$self->{"aref"}[$i] = $value;
while ($i > 1) {
my $parent = POSIX::floor($i/2);
last if $self->compare($i, $parent) <= 0;
$self->swap($i, $parent);
$i = $parent;
}
$self->{"next"}++;
}
sub reheapify {
my ($self, $i) = #_;
my $left = 2 * $i;
my $right = 2 * $i + 1;
my $winleft = $self->compare($i, $left) >= 0;
my $winright = $self->compare($i, $right) >= 0;
return if $winleft and $winright;
if ($self->compare ($left, $right) > 0) {
$self->swap($i, $left);
$self->reheapify($left);
} else {
$self->swap($i, $right);
$self->reheapify($right);
}
}
sub remove {
my $self = shift;
my $aref = $self->{"aref"};
my $result = $aref->[1];
$aref->[1] = pop #$aref;
$self->{"next"}--;
$self->reheapify(1);
return $result;
}
sub empty {
my $self = shift;
return $self->{"next"} == 1;
}
1;
package HuffTree;
use warnings;
use strict;
use Pair;
our #ISA = "Pair";
sub priority {
my $self = shift;
# lowest count highest priority
return -$self->{frequency};
}
sub left {
my $self = shift;
return $self->{left};
}
sub right {
my $self = shift;
return $self->{right};
}
1;
package Pair;
use warnings;
use strict;
sub new {
my $class = shift;
my $self = { #_ };
bless $self, $class;
}
sub letter {
my $self = shift;
return $self->{letter};
}
sub frequency {
my $self = shift;
return $self->{frequency};
}
sub priority {
my $self = shift;
return $self->{frequency};
}
1;
package Util;
use strict;
use warnings;
sub croak { die "$0: #_: $!\n"; }
sub load_arg_file {
my $path_name = shift #ARGV;
my $fh;
open($fh, $path_name) || croak "File not found.\n";
return $fh;
}
1;
You have a Heap.pm installed from CPAN. That's what gets loaded, not your own Heap.pm. The new sub in the Heap.pm from CPAN looks like this:
sub new {
use Heap::Fibonacci;
return &Heap::Fibonacci::new;
}
Which is actually a bug in said module, because Heap::Fibonacci uses the
standard bless \$h, $class; thing in its new sub,
so the reference is blessed into the Heap package, which
does indeed not have a sub called add (Heap::Fibonacci does).
To solve your immediate problem, you can:
make sure that your module is picked up before the "other" Heap (by modifying #INC with use lib, for example;
or not reinvent the wheel and actually use Heap::Fibonacci).
At any rate, it might be a good idea to report this problem
to the Heap module author - because even if you did not have
your own Heap.pm, your code would still fail with the same message.