view release on metacpan or search on metacpan
lib/ChordPro/Config.pm view on Meta::CPAN
delete( $fc->{$k} );
# And insert individual entries.
$fc->{$_} = dclone($v) for @k;
}
}
}
}
# Reverse of config_expand_font_shortcuts.
sub simplify_fonts( $cfg ) {
return $cfg unless $cfg->{pdf}->{fonts};
foreach my $font ( keys %{$cfg->{pdf}->{fonts}} ) {
for ( $cfg->{pdf}->{fonts}->{$font} ) {
next unless is_hashref($_);
delete $_->{color}
if $_->{color} && $_->{color} eq "foreground";
delete $_->{background}
lib/ChordPro/Config.pm view on Meta::CPAN
$path =~ s/\.$//;
if ( $self eq $orig ) {
warn("I: $path\n") if DEBUG;
return 'I';
}
warn("M $path $self\n") if DEBUG;
return 'M';
}
sub hmerge( $left, $right, $path = "" ) {
# Merge hashes. Right takes precedence.
# Based on Hash::Merge::Simple by Robert Krimen.
my %res = %$left;
for my $key ( keys(%$right) ) {
warn("Config error: unknown item $path$key\n")
unless exists $res{$key}
lib/ChordPro/Delegate/ABC.pm view on Meta::CPAN
use feature 'state';
use Encode 'decode_utf8';
use ChordPro::Paths;
use ChordPro::Utils;
use Text::ParseWords qw(shellwords);
use constant { QUICKJS => "QuickJS",
QUICKJSXS => "QuickJS_XS" };
sub DEBUG() { $config->{debug}->{abc} }
# ABC processing using abc2svg and custom SVG processor.
# See info() below how the method is determined.
# Song and PDF module uses 'can' to get at this.
sub can( $class, $method ) {
if ( $method eq "options" ) {
return \&options;
}
# abc2svg handlers are sorted out by info().
return \&abc2svg;
}
# Default entry point.
sub abc2svg( $song, %args ) {
my $abc2svg = info();
if ( DEBUG() ) {
::dump($abc2svg);
}
state $cfg_checked;
unless ( $cfg_checked++ ) {
if ( ($config->{delegates}{abc}{config} // "default") ne "default" ) {
lib/ChordPro/Delegate/ABC.pm view on Meta::CPAN
$info->{handler} = $handler;
$info->{method} = CP->display($exe);
$info->{info} = $info->{method};
$info->{command} = [ $exe ];
}
return $info;
}
# Pre-scan.
sub options( $data ) {
my @pre;
my @data = @$data;
while ( @data ) {
last if $data[0] =~ /^([A-Z]:|\%)/;
push( @pre, shift(@data) );
}
@pre = () if @pre && !@data; # no data found
my $kv = {};
$kv = parse_kvm( @pre ) if @pre;
lib/ChordPro/Delegate/Lilypond.pm view on Meta::CPAN
no warnings "experimental::signatures";
use utf8;
use File::Spec;
use File::Temp ();
use File::LoadLines;
use feature 'state';
use ChordPro::Utils;
use Text::ParseWords qw(shellwords);
sub DEBUG() { $config->{debug}->{ly} }
sub ly2svg( $self, %args ) {
my ( $elt, $pw ) = @args{qw(elt pagewidth)};
state $imgcnt = 0;
state $td = File::Temp::tempdir( CLEANUP => !$config->{debug}->{ly} );
$imgcnt++;
my $src = File::Spec->catfile( $td, "tmp${imgcnt}.ly" );
my $svg = File::Spec->catfile( $td, "tmp${imgcnt}.svg" );
my $fd;
lib/ChordPro/Delegate/Lilypond.pm view on Meta::CPAN
subtype => "svg",
uri => "$im1.cropped.svg",
opts => { maybe id => $kv->{id},
maybe align => $kv->{align},
maybe spread => $kv->{spread},
maybe scale => $scale,
maybe design_scale => $design_scale,
} };
}
sub ly2image( $s, $pw, $elt ) {
croak("Lilypond: Please adjust your delegate config to use handler \"ly2svg\" instead of \"ly2image\"");
}
# Pre-scan.
sub options( $data ) {
my @pre;
my @data = @$data;
while ( @$data ) {
last if $data[0] =~ /^[%\\]/; # LP data
push( @pre, shift(@data) );
}
@pre = () if @pre && !@$data; # no LP found
my $kv = {};
$kv = parse_kvm( @pre ) if @pre;
lib/ChordPro/Delegate/SVG.pm view on Meta::CPAN
use strict;
use warnings;
use feature qw( signatures );
no warnings "experimental::signatures";
use utf8;
package ChordPro::Delegate::SVG;
use ChordPro::Utils;
sub DEBUG() { $::config->{debug}->{svg} }
sub svg2svg( $self, %args ) {
my $elt = $args{elt};
my @data = @{ $elt->{data} };
my @pre;
while ( $data[0] !~ /<svg/ ) {
push( @pre, shift(@data) );
}
my $kv = parse_kvm( @pre ) if @pre;
$kv->{split} //= 1; # less overhead. really.
lib/ChordPro/Delegate/SVG.pm view on Meta::CPAN
return
{ type => "image",
subtype => "svg",
line => $elt->{line},
data => \@data,
opts => { %$kv, %{$elt->{opts}//{}} },
};
}
# Pre-scan.
sub options( $data ) {
my @pre;
while ( $data->[0] !~ /<svg/ ) {
push( @pre, shift(@$data) );
}
my $kv = parse_kvm( @pre ) if @pre;
$kv;
}
lib/ChordPro/Delegate/TextBlock.pm view on Meta::CPAN
# background: Background color of the object.
#
# Common attributes:
#
# id: Make asset instead of image.
# align: Image alignment (left, center, right)
# border: Draw border around the image.
use ChordPro::Utils;
sub DEBUG() { $::config->{debug}->{txtblk} }
sub txt2xform( $self, %args ) {
my $elt = $args{elt};
my $ps = $self->{_ps};
my $pr = $ps->{pr};
my $opts = { %{$elt->{opts}} };
# Text style must be one of the known styles (text, chord, comment, ...).
my $style = delete($opts->{textstyle}) // "text";
unless ( defined($ps->{fonts}->{$style} ) ) {
warn("TextBlock: Unknown font style \"$style\", using \"text\"\n");
lib/ChordPro/Delegate/TextBlock.pm view on Meta::CPAN
subtype => "xoform",
line => $elt->{line},
data => $xo,
width => $width + 2*$padding,
height => $height + 2*$padding,
opts => { align => "left", %$opts },
};
}
# Pre-scan.
sub options( $data ) { {} }
1;
lib/ChordPro/Dumper.pm view on Meta::CPAN
return ref($ref) . " [@bb]";
} },
{ 'PDF::API2::Resource::XObject::Image' => sub ( $ref, $ddp ) {
return join( "", ref($ref),
" [", $ref->width, "x", $ref->height, "]",
);
} },
];
sub ddp( $ref, %options ) {
my %o = ( filters => $filters, %options );
if ( $o{as} =~ /^(.*)\n\Z/s ) {
$o{as} = $1;
$o{caller_message_newline} = 1;
}
defined(wantarray)
? np( $ref, %o )
: ( -t STDERR )
? p( $ref, %o )
: warn( np( $ref, %o ), "\n" );
lib/ChordPro/Output/Common.pm view on Meta::CPAN
# Copyright (c) 1995 OZAWA Sakuro. All rights reserved. This program
# is free software; you can redistribute it and/or modify it under the
# same terms as Perl itself.
our %roman2arabic = qw(I 1 V 5 X 10 L 50 C 100 D 500 M 1000);
my %roman_digit = qw(1 IV 10 XL 100 CD 1000 MMMMMM);
my @figure = reverse sort keys %roman_digit;
#my %roman_digit;
$roman_digit{$_} = [split(//, $roman_digit{$_}, 2)] foreach @figure;
sub isroman($) {
my $arg = shift;
$arg ne '' and
$arg =~ /^(?: M{0,3})
(?: D?C{0,3} | C[DM])
(?: L?X{0,3} | X[LC])
(?: V?I{0,3} | I[VX])$/ix;
}
push( @EXPORT_OK, 'isroman' );
sub arabic($) {
my $arg = shift;
isroman $arg or return undef;
my($last_digit) = 1000;
my($arabic);
foreach (split(//, uc $arg)) {
my($digit) = $roman2arabic{$_};
$arabic -= 2 * $last_digit if $last_digit < $digit;
$arabic += ($last_digit = $digit);
}
$arabic;
}
push( @EXPORT_OK, 'arabic' );
sub Roman($) {
my $arg = shift;
0 < $arg and $arg < 4000 or return undef;
my($x, $roman);
foreach (@figure) {
my($digit, $i, $v) = (int($arg / $_), @{$roman_digit{$_}});
if (1 <= $digit and $digit <= 3) {
$roman .= $i x $digit;
} elsif ($digit == 4) {
$roman .= "$i$v";
} elsif ($digit == 5) {
lib/ChordPro/Output/Common.pm view on Meta::CPAN
} elsif ($digit == 9) {
$roman .= "$i$x";
}
$arg -= $digit * $_;
$x = $i;
}
$roman;
}
push( @EXPORT_OK, 'Roman' );
sub roman($) {
lc( Roman(shift) );
}
push( @EXPORT_OK, 'roman' );
# Prepare outlines.
# This mainly untangles alternative names when being sorted on.
# Returns a book array where each element consists of the sort items,
# and the song.
#sub PODBG() { $config->{debug}->{x1} }
sub PODBG() { 0 }
# Suppress toc entry.
sub _suppresstoc {
my ( $meta ) = @_;
return !is_true($meta->{_TOC}->[0]) if exists($meta->{_TOC});
# return unless exists($meta->{sorttitle});
# my $st = $meta->{sorttitle};
# defined($st) && ( $st->[0] eq "" || $st->[0] eq '""' );
return;
}
lib/ChordPro/Output/PDF/Grid.pm view on Meta::CPAN
package ChordPro::Output::PDF::Grid;
use strict;
use warnings;
use Carp;
use feature 'state';
use feature 'signatures';
no warnings 'experimental::signatures';
sub gridline( $elt, $x, $y, $cellwidth, $barwidth, $margin, $ps, %opts ) {
# Grid context.
my $pr = $ps->{pr};
my $fonts = $ps->{fonts};
# Use the chords font for the chords, and for the symbols size.
my $fchord = { %{ $fonts->{grid} || $fonts->{chord} } };
delete($fchord->{background});
$y -= font_bl($fchord);
lib/ChordPro/Output/PDF/Grid.pm view on Meta::CPAN
if ( $t->{chords} ) {
$t->{text} = "";
for ( 0..$#{ $t->{chords} } ) {
$t->{text} .= $t->{chords}->[$_]->chord_display . $t->{phrases}->[$_];
}
}
$pr->text( " " . $t->{text}, $x, $y, $fonts->{grid_margin} );
}
}
sub is_bar( $elt ) {
exists( $elt->{class} ) && $elt->{class} eq "bar";
}
sub pr_cellline( $x, $y, $lcr, $sz, $w, $col, $pr ) {
$x -= $w / 2 * ($lcr + 1);
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
}
sub pr_barline( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = $w
$x -= $w / 2 * ($lcr + 1);
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
}
sub pr_dbarline( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = 3 * $w
$x -= 1.5 * $w * ($lcr + 1);
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
$x += 2 * $w;
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
}
sub pr_rptstart( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = 3 * $w
$x -= 1.5 * $w * ($lcr + 1);
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
$x += 2 * $w;
$y += 0.55 * $sz;
$pr->line( $x, $y, $x, $y+$w, $w, $col );
$y -= 0.4 * $sz;
$pr->line( $x, $y, $x, $y+$w, $w, $col );
}
sub pr_rptvolta( $x, $y, $lcr, $sz, $symcol, $pr, $token ) {
my $w = $sz / 10; # glyph width = 3 * $w
my $col = $pr->{ps}->{grids}->{volta}->{color};
my $ret = $x -= 1.5 * $w * ($lcr + 1);
$pr->vline( $x, $y+0.9*$sz, $sz, $w, $col );
$x += 2 * $w;
my $font = $pr->{ps}->{fonts}->{grid};
$pr->setfont($font);
$pr->text( "<span color='$col'><sup>" . $token->{volta} . "</sup></span>",
$x-$w/2, $y, $font );
$ret;
}
sub pr_voltafinish( $x, $y, $width, $sz, $symcol, $pr ) {
my $w = $sz / 10; # glyph width = 3 * $w
my ( $col, $span ) = @{$pr->{ps}->{grids}->{volta}}{qw(color span)};
$pr->hline( $x, $y+0.9*$sz+$w/4, $width*$span, $w/2, $col );
}
sub pr_rptend( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = 3 * $w
$x -= 1.5 * $w * ($lcr + 1);
$pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $w, $col );
$y += 0.55 * $sz;
$pr->line( $x, $y, $x, $y+$w, $w, $col );
$y -= 0.4 * $sz;
$pr->line( $x, $y, $x, $y+$w, $w, $col );
}
sub pr_rptendstart( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = 5 * $w
$x -= 2.5 * $w * ($lcr + 1);
$pr->vline( $x + 2*$w, $y+0.9*$sz, $sz, $w, $col );
$y += 0.55 * $sz;
$pr->line( $x, $y, $x , $y+$w, $w, $col );
$pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $w, $col );
$y -= 0.4 * $sz;
$pr->line( $x, $y, $x, $y+$w, $w, $col );
$pr->line( $x+4*$w, $y, $x+4*$w, $y+$w, $w, $col );
}
sub pr_repeat( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 3; # glyph width = 3 * $w
$x -= 1.5 * $w * ($lcr + 1);
my $lw = $sz / 10;
$x -= $w / 2;
$pr->line( $x, $y+0.2*$sz, $x + $w, $y+0.7*$sz, $lw );
$pr->line( $x, $y+0.6*$sz, $x + 0.07*$sz , $y+0.7*$sz, $lw );
$x += $w;
$pr->line( $x - 0.05*$sz, $y+0.2*$sz, $x + 0.02*$sz, $y+0.3*$sz, $lw );
}
sub pr_endline( $x, $y, $lcr, $sz, $col, $pr ) {
my $w = $sz / 10; # glyph width = 2 * $w
$x -= 0.75 * $w * ($lcr + 1);
$pr->vline( $x, $y+0.85*$sz, 0.9*$sz, 2*$w );
}
################ Hooks ################
*font_bl = *ChordPro::Output::PDF::font_bl;
*pr_label_maybe = *ChordPro::Output::PDF::pr_label_maybe;
lib/ChordPro/Paths.pm view on Meta::CPAN
class ChordPro::Paths;
my $instance;
# Work around Object::Pad 0.817 breakage.
#method get :common ( $reset = 0 ) {
# undef $instance if $reset;
# $instance //= $class->new;
#}
sub get( $class, $reset = 0 ) {
undef $instance if $reset;
$instance //= $class->new;
}
use Cwd qw(realpath);
use File::Spec::Functions qw( catfile catdir splitpath catpath file_name_is_absolute );
use File::HomeDir;
field $home :reader; # dir
field $configdir :reader; # dir
lib/ChordPro/Paths.pm view on Meta::CPAN
$ENV{uc($packager)."_PACKAGED"};
}
################ Export ################
# For convenience.
use Exporter 'import';
our @EXPORT;
sub CP() { __PACKAGE__->get }
push( @EXPORT, 'CP' );
1;
lib/ChordPro/Utils.pm view on Meta::CPAN
return "" if $suppressundef;
$val = "<undef>"
}
defined wantarray ? $label.$val : warn($label.$val."\n");
}
push( @EXPORT, 'pv' );
# Processing JSON.
sub json_load( $json, $source = "<builtin>" ) {
my $info = json_parser();
if ( $info->{parser} eq "JSON::Relaxed" ) {
state $pp = JSON::Relaxed::Parser->new( croak_on_error => 0,
strict => 0,
prp => 1 );
my $data = $pp->decode($json);
return $data unless $pp->is_error;
$source .= ": " if $source;
die("${source}JSON error: " . $pp->err_msg . "\n");
}
lib/ChordPro/Utils.pm view on Meta::CPAN
# Glue lines, so we have at lease some relaxation.
$json =~ s/"\s*\\\n\s*"//g;
$pp->relaxed if $info->{relaxed};
$pp->decode($json);
}
}
# JSON parser, what and how (also used by runtimeinfo().
sub json_parser() {
my $relax = $ENV{CHORDPRO_JSON_RELAXED} // 2;
if ( $relax > 1 ) {
require JSON::Relaxed;
return { parser => "JSON::Relaxed",
version => $JSON::Relaxed::VERSION }
}
else {
require JSON::PP;
return { parser => "JSON::PP",
relaxed => $relax,
lib/ChordPro/Utils.pm view on Meta::CPAN
}
push( @EXPORT, "maybe" );
# Min/Max.
sub min { $_[0] < $_[1] ? $_[0] : $_[1] }
sub max { $_[0] > $_[1] ? $_[0] : $_[1] }
push( @EXPORT, "min", "max" );
# Plural
sub plural( $n, $tag, $plural=undef ) {
$plural //= $tag . "s";
( $n || "no" ) . ( $n == 1 ? $tag : $plural );
}
push( @EXPORT, "plural" );
# Dimensions.
# Fontsize allows typical font units, and defaults to ref 12.
sub fontsize( $size, $ref=12 ) {
if ( $size && $size =~ /^([.\d]+)(%|e[mx]|p[tx])$/ ) {
return $ref/100 * $1 if $2 eq '%';
return $ref * $1 if $2 eq 'em';
return $ref/2 * $1 if $2 eq 'ex';
return $1 if $2 eq 'pt';
return $1 * 0.75 if $2 eq 'px';
}
$size || $ref;
}
push( @EXPORT, "fontsize" );
# Dimension allows arbitrary units, and defaults to ref 12.
sub dimension( $size, %sz ) {
return unless defined $size;
my $ref;
if ( ( $ref = $sz{fsize} )
&& $size =~ /^([.\d]+)(%|e[mx])$/ ) {
return $ref/100 * $1 if $2 eq '%';
return $ref * $1 if $2 eq 'em';
return $ref/2 * $1 if $2 eq 'ex';
}
if ( ( $ref = $sz{width} )
&& $size =~ /^([.\d]+)(%)$/ ) {
lib/ChordPro/Utils.pm view on Meta::CPAN
}
push( @EXPORT, "is_corefont" );
# Progress reporting.
use Ref::Util qw(is_coderef);
# Progress can return a false result to allow caller to stop.
sub progress(%args) {
state $callback;
state $phase = "";
state $index = 0;
state $total = '';
unless ( %args ) { # reset
undef $callback;
$phase = "";
$index = 0;
return;
}
lib/ChordPro/Utils.pm view on Meta::CPAN
warn( $msg, "\n" ) if $msg;
}
return $ret;
}
push( @EXPORT, "progress" );
# Common items for property directives ({textsize} etc.).
sub propitems() {
qw( chord chorus diagrams footer grid label tab text title toc );
}
sub propitems_re() {
my $re = join( '|', propitems() );
qr/(?:$re)/;
}
push( @EXPORT, "propitems_re" );
push( @EXPORT_OK, "propitems" );
1;
lib/ChordPro/Wx/Config.pm view on Meta::CPAN
dumpstate => 0,
expert => 0,
);
use constant MAXRECENTS => 10;
# Establish a connection with the persistent data store.
#method Setup :common ($options) {
sub Setup( $class, $options ) {
if ( $options->{config} ) {
Wx::ConfigBase::Set
( $cb = Wx::FileConfig->new
( "WxChordPro",
"ChordPro_ORG",
$options->{config},
'',
wxCONFIG_USE_LOCAL_FILE,
));
lib/ChordPro/Wx/Editor.pm view on Meta::CPAN
no warnings 'experimental::signatures';
use utf8;
use Wx ':everything';
package ChordPro::Wx::Editor;
use ChordPro::Wx::Config;
use ChordPro::Wx::Utils;
sub new( $class, $parent, $id ) {
my $widget;
$::options->{stc} //= 1;
if ( $::options->{stc} && eval { require Wx::STC; 1 } ) {
# if ( $::options->{stc} && eval { require Wx::Scintilla; 1 } ) {
$widget = Wx::StyledTextCtrl->new($parent);
# $widget = Wx::Scintilla::TextCtrl->new($parent);
$state{have_stc} = 1;
return bless $widget => 'ChordPro::Wx::STCEditor';
}
lib/ChordPro/Wx/Editor.pm view on Meta::CPAN
package ChordPro::Wx::STCEditor;
use parent qw( -norequire Wx::StyledTextCtrl );
#use parent qw( -norequire Wx::Scintilla::TextCtrl );
use Wx ':everything';
use ChordPro::Wx::Config;
use ChordPro::Wx::Utils;
sub refresh( $self, $prefs = undef ) {
my $stc = $self;
$prefs //= \%preferences;
# RTI loading is currently too slow.
# $state{rti} = ChordPro::runtime_info();
$state{rti}->{directive_abbrevs} = ChordPro::Song::_directive_abbrevs();
$stc->SetLexer(wxSTC_LEX_CONTAINER);
$stc->SetKeyWords(0,
lib/ChordPro/Wx/Editor.pm view on Meta::CPAN
$stc->SetWrapMode(0); # wxSTC_WRAP_NONE );
}
$self->style_text;
# Expert...
$stc->SetViewEOL( $state{vieweol} );
$stc->SetViewWhiteSpace( $state{viewws} );
}
sub style_text( $self ) {
my $stc = $self;
# Scintilla uses byte indices.
use Encode;
my $text = Encode::encode_utf8($stc->GetText);
my $style = sub {
my ( $re, @styles ) = @_;
pos($text) = 0;
while ( $text =~ m/$re/g ) {
lib/ChordPro/Wx/Editor.pm view on Meta::CPAN
# Comments/
$style->( qr/^(#.*)/m, 1 );
# Directives.
$style->( qr/^([ \t]*)(\{)([-\w!]+)(.*)(\})/m, 7, 3, 5, 6, 3 );
$style->( qr/^([ \t]*)(\{)([-\w!]+)([: ])(.*)(\})/m, 7, 3, 5, 3, 6, 3 );
# Chords.
$style->( qr/(\[)([^\[\]\s]*)(\])/m, 3, 4, 3 );
}
sub prepare_annotations( $self ) {
return unless $state{have_stc};
my $stc = $self;
$stc->AnnotationClearAll;
$stc->AnnotationSetVisible(wxSTC_ANNOTATION_BOXED);
if ( $stc->can("StyleGetSizeFractional") ) { # Wx 3.002
$stc->StyleSetSizeFractional # size * 100
( $self->{astyle},
( $stc->StyleGetSizeFractional
( wxSTC_STYLE_DEFAULT ) * 4 ) / 5 );
}
return 1;
}
sub add_annotation( $self, $line, $message ) {
return unless $state{have_stc};
my $stc = $self;
$stc->AnnotationSetText( $line, $message );
$stc->AnnotationSetStyle( $line, $self->{astyle} );
}
unless ( __PACKAGE__->can("IsModified") ) {
*IsModified = sub($self) {
$self->{_modified} || $self->CanUndo;
};
}
unless ( __PACKAGE__->can("DiscardEdits") ) {
*DiscardEdits = sub($self) {
$self->EmptyUndoBuffer;
$self->{_modified} = 0;
};
}
sub SetModified( $self, $mod ) {
if ( $mod ) {
$self->{_modified} = 1;
}
else {
$self->DiscardEdits;
}
}
sub SetFont( $self, $font ) {
die("XXX\n") unless $font->IsOk;
$self->StyleSetFont( $_, $font ) for 0..7;
$self->{font} = $font;
}
sub GetFont( $self ) {
$self->{font} // $self->StyleGetFont(0);
}
sub OSXDisableAllSmartSubstitutions( $self ) {
}
sub OnStyleNeeded( $self, $event ) { # scintilla
$self->style_text;
}
sub Replace( $self, $from=-1, $to=-1, $text="" ) {
# We will only call this to replace the selection.
$self->ReplaceSelection($text);
}
################ Methods ################
package ChordPro::Wx::TextEditor;
use parent qw( -norequire Wx::TextCtrl );
use Wx ':everything';
use ChordPro::Wx::Config;
use ChordPro::Wx::Utils;
use ChordPro::Utils qw( is_macos );
sub new( $class, $parent, $id=undef ) {
my $self = $class->SUPER::new( $parent, wxID_ANY, "",
wxDefaultPosition, wxDefaultSize,
wxHSCROLL|wxTE_MULTILINE );
return $self;
}
sub refresh( $self, $prefs = undef ) {
my $ctrl = $self;
$prefs //= \%preferences;
my $mod = $self->IsModified;
# TextCtrl only supports background colour and font.
my $theme = $prefs->{editortheme};
my $c = $prefs->{editcolour}{$theme};
my $bgcol = Wx::Colour->new( $c->{bg} );
my $fgcol = Wx::Colour->new( $c->{fg} );
$ctrl->SetBackgroundColour($bgcol);
$ctrl->SetStyle( 0, $ctrl->GetLastPosition,
Wx::TextAttr->new( $fgcol, $bgcol ) );
$ctrl->SetFont( Wx::Font->new($prefs->{editfont}) );
$ctrl->SetModified($mod);
}
sub AddText( $self, $text ) {
$self->WriteText($text);
}
sub GetLineCount( $self ) {
$self->GetNumberOfLines;
}
sub GetSelectedText( $self ) {
$self->GetStringSelection;
}
sub GetText( $self ) {
$self->GetValue;
}
sub SetText( $self, $text ) {
$self->SetValue($text);
}
sub SetColour( $self, $colour ) {
$self->SetStyle( 0, $self->GetLastPosition,
Wx::TextAttr->new( Wx::Colour->new($colour) ) );
}
sub EmptyUndoBuffer($self) {
}
sub OSXDisableAllSmartSubstitutions( $self ) {
return unless is_macos;
$self->SUPER::OSXDisableAllSmartSubstitutions;
}
################
1;
lib/ChordPro/Wx/EditorPanel.pm view on Meta::CPAN
use ChordPro::Wx::Utils;
use ChordPro::Utils qw( max demarkup is_macos is_msw plural );
use ChordPro::Paths;
use File::Basename;
# WhoamI
field $panel :accessor = "editor";
# Just fill in the defaults.
sub BUILDARGS( $class, $parent=undef, $id=wxID_ANY,
$pos=wxDefaultPosition, $size=wxDefaultSize,
$style=0, $name="" ) {
return( $parent, $id, $pos, $size, $style, $name );
}
BUILD {
# By default the TextCtrl on MacOS substitutes smart quotes and dashes.
# Note that OSXDisableAllSmartSubstitutions requires an augmented
# version of wxPerl.
$self->{t_editor}->OSXDisableAllSmartSubstitutions;
lib/ChordPro/Wx/Main.pm view on Meta::CPAN
package ChordPro::Wx::WxChordPro;
use parent qw( Wx::App ChordPro::Wx::Main );
use ChordPro::Paths;
use ChordPro::Wx::Config;
use Wx qw( wxACCEL_CTRL WXK_CONTROL_Q wxID_EXIT );
sub run( $self, $opts ) {
$options = $opts;
#### Start ################
ChordPro::Wx::WxChordPro->new->MainLoop();
}
sub OnInit( $self ) {
$self->SetAppName("ChordPro");
$self->SetVendorName("ChordPro.ORG");
Wx::InitAllImageHandlers();
ChordPro::Wx::Config->Setup($options);
ChordPro::Wx::Config->Load($options);
my $main = ChordPro::Wx::Main->new;
return 0 unless $main->init($options);
lib/ChordPro/Wx/SongbookExportPanel.pm view on Meta::CPAN
use ChordPro::Wx::Utils;
use Encode qw( decode_utf8 encode_utf8 );
use File::LoadLines;
use File::Basename;
# WhoamI
field $panel :accessor = "sbexport";
# Just fill in the defaults.
sub BUILDARGS( $class, $parent=undef, $id=wxID_ANY,
$pos=wxDefaultPosition, $size=wxDefaultSize,
$style=0, $name="" ) {
return( $parent, $id, $pos, $size, $style, $name );
}
BUILD {
# Setup logger.
$self->setup_logger;
# Setup WebView, if possible.
lib/ChordPro/Wx/Utils.pm view on Meta::CPAN
{ M_ALL => 0xff,
M_MAIN => 0x01,
M_EDITOR => 0x02,
M_SONGBOOK => 0x04,
};
push( @EXPORT, qw( M_MAIN M_EDITOR M_SONGBOOK ) );
my @swingers;
sub update_menubar( $self, $sel ) {
die unless @swingers;
for ( @swingers ) {
my ( $mi, $mask ) = @$_;
$mi->Enable( $mask & $sel );
}
}
sub setup_menubar( $self ) {
state $expert = $ChordPro::Wx::Config::state{preferences}{expert};
state $ctl =
[ [ wxID_FILE,
[ [ wxID_HOME, M_EDITOR|M_SONGBOOK, "Start Screen",
"Return to the Start Screen.", "OnStart" ],
[],
[ wxID_NEW, M_ALL, "",
"Create another ChordPro document", "OnNew" ],
lib/ChordPro/Wx/Utils.pm view on Meta::CPAN
# Add menu bar.
$target->SetMenuBar($mb);
return $mb;
}
push( @EXPORT, "setup_menubar", "update_menubar" );
################ ################
sub savewinpos( $win, $name ) {
return unless $name eq "main";
$ChordPro::Wx::Config::state{windows}->{$name} =
join( " ", $win->GetPositionXY, $win->GetSizeWH );
}
sub restorewinpos( $win, $name ) {
return unless $name eq "main";
$win = $Wx::wxTheApp->GetTopWindow;
my $t = $ChordPro::Wx::Config::state{windows}->{$name};
if ( $t ) {
my @a = split( ' ', $t );
if ( is_msw || is_macos ) {
$win->SetSizeXYWHF( $a[0],$a[1],$a[2],$a[3], 0 );
}
else {
# Linux WM usually prevent placement.
$win->SetSize( $a[2],$a[3] );
}
}
}
push( @EXPORT, 'savewinpos', 'restorewinpos' );
################ ################
sub panels() {
my @panels = qw( p_editor p_sbexport );
wantarray ? @panels : \@panels;
}
push( @EXPORT, 'panels' );
################ ################
sub ellipsize( $widget, %opts ) {
my $text = $opts{text} // $widget->GetText;
my $home = ChordPro::Paths->get->home;
$text =~ s/^\Q$home\E\/*/~\//;
my $width = ($widget->GetSizeWH)[0];
$text = Wx::Control::Ellipsize( $text, Wx::ClientDC->new($widget),
$opts{type} // wxELLIPSIZE_END(),
$width-10, wxELLIPSIZE_FLAGS_DEFAULT() )
if Wx::Control->can("Ellipsize");
# Change w/o triggering a EVT_TEXT event.
$widget->ChangeValue($text);
}
push( @EXPORT, "ellipsize" );
################ ################
sub kbdkey( $key ) {
$key =~ s/Shift-/â§/;
$key =~ s/Alt-/â/;
$key =~ s/Option-/â¥/;
my $c = is_macos ? "â" : "Ctrl-";
$key =~ s/Ctrl-/$c/;
return $key;
}
push( @EXPORT, "kbdkey" );
use Storable();
################ ################
sub clone( $struct ) { Storable::dclone($struct) }
push( @EXPORT, "clone" );
################ ################
sub Wx::ColourPickerCtrl::GetAsHTML( $self ) {
$self->GetColour->GetAsString(wxC2S_HTML_SYNTAX);
}
################ ################
lib/ChordPro/Wx/Utils.pm view on Meta::CPAN
$rest[0] = ChordPro::Paths->get->findres( basename($rest[0]),
class => "icons" );
$rest[0] ||= ChordPro::Paths->get->findres( "missing.png",
class => "icons" );
$::wxbitmapnew->($self, @rest);
};
}
################ ################
sub has_appearance() {
Wx::SystemSettings->can("GetAppearance");
}
push( @EXPORT, 'has_appearance' );
################ ################
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
$s =~ s/^\n*//s;
$s .= "\n" if $s !~ /\n$/;
}
return $s;
}
################ Subroutines ################
# resolve processes $ref, allOf etc nodes.
sub resolve( $d, $schema ) {
if ( is_hash($d) ) {
while ( my ($k,$v) = each %$d ) {
if ( $k eq 'allOf' ) {
delete $d->{$k}; # yes, safe to do
$d = merge( resolve( $_, $schema ), $d ) for @$v;
}
elsif ( $k eq 'oneOf' || $k eq 'anyOf' ) {
delete $d->{$k}; # yes, safe to do
$d = merge( resolve( $v->[0], $schema ), $d );
lib/ChordPro/lib/JSON/Relaxed/Parser.pm view on Meta::CPAN
}
elsif ( is_array($d) ) {
$d = [ map { resolve( $_, $schema ) } @$d ];
}
else {
}
return $d;
}
sub is_hash($o) { UNIVERSAL::isa( $o, 'HASH' ) }
sub is_array($o) { UNIVERSAL::isa( $o, 'ARRAY' ) }
sub merge ( $left, $right ) {
return $left unless $right;
my %merged = %$left;
for my $key ( keys %$right ) {
my ($hr, $hl) = map { is_hash($_->{$key}) } $right, $left;
lib/ChordPro/lib/SVGPDF.pm view on Meta::CPAN
" (initial)");
$xo->stroke_color( $_ eq 'currentColor' ? 'black' : $_ );
}
$svg->traverse;
$svg->css_pop;
$self->_dbg( "==== end ", $e->{name}, " ====" );
}
sub min( $a, $b ) { $a < $b ? $a : $b }
sub max( $a, $b ) { $a > $b ? $a : $b }
method combine_svg( $forms, %opts ) {
my $type = $opts{type} // "stacked";
return $forms if $type eq "none";
my ( $xmin, $ymin, $xmax, $ymax );
my $y = 0;
my $x = 0;
my $sep = $opts{sep} || 0;
my $nx;
t/410_prp.t view on Meta::CPAN
is_deeply( prpadd2cfg( [qw(x y z)], "<1" => "a" ), [qw(x a y z)], "<1" );
is_deeply( prpadd2cfg( [qw(x y z)], "<-1" => "a" ), [qw(x y a z)], "<-1" );
is_deeply( prpadd2cfg( [qw(x y z a)], "/" => "" ), [qw(x y z)], "/" );
is_deeply( prpadd2cfg( [qw(x y z a)], "/0" => "" ), [qw(y z a)], "/0" );
is_deeply( prpadd2cfg( [qw(x y z a)], "/-1" => "" ), [qw(x y z)], "/-1" );
################ Helpers ################
# use DDP;
sub testit( $struct, @delta ) {
# p($struct, as => "before" );
prpadd2cfg( $struct, @delta );
# p($struct, as => "after" );
}
sub struct {
{ a => [ "b", ["c"], { f => "g" } ] }
}