view release on metacpan or search on metacpan
lib/ChordPro/Config.pm view on Meta::CPAN
415416417418419420421422423424425426427428429430431432433434435
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
867868869870871872873874875876877878879880881882883884885886887
$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
22232425262728293031323334353637383940414243444546474849505152535455565758use
ChordPro::Paths;
use
ChordPro::Utils;
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
464465466467468469470471472473474475476477478479480481482483484
$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
1314151617181920212223242526272829303132333435no
warnings
"experimental::signatures"
;
use
utf8;
use
File::Spec;
use
File::Temp ();
use
File::LoadLines;
use
ChordPro::Utils;
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
139140141142143144145146147148149150151152153154155156157158159160161162163164
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
4567891011121314151617181920212223242526use
strict;
use
warnings;
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
2930313233343536373839404142434445464748
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
3132333435363738394041424344454647484950515253# 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
171172173174175176177178179180181182183
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
666768697071727374757677787980818283848586
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
737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117# 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
121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
}
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
2345678910111213141516171819202122package
ChordPro::Output::PDF::Grid;
use
strict;
use
warnings;
use
Carp;
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
204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
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
8910111213141516171819202122232425262728class 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
File::HomeDir;
field
$home
:reader;
# dir
field
$configdir
:reader;
# dir
lib/ChordPro/Paths.pm view on Meta::CPAN
349350351352353354355356357358359360361362363
$ENV
{
uc
(
$packager
).
"_PACKAGED"
};
}
################ Export ################
# For convenience.
our
@EXPORT
;
sub
CP() { __PACKAGE__->get }
push
(
@EXPORT
,
'CP'
);
1;
lib/ChordPro/Utils.pm view on Meta::CPAN
287288289290291292293294295296297298299300301302303304305306307
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
310311312313314315316317318319320321322323324325326327328329330
# 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 ) {
return
{
parser
=>
"JSON::Relaxed"
,
version
=>
$JSON::Relaxed::VERSION
}
}
else
{
return
{
parser
=>
"JSON::PP"
,
relaxed
=>
$relax
,
lib/ChordPro/Utils.pm view on Meta::CPAN
469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512}
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
551552553554555556557558559560561562563564565566567568569570571}
push
(
@EXPORT
,
"is_corefont"
);
# Progress reporting.
# 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
611612613614615616617618619620621622623624625626627628629630631632633
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
126127128129130131132133134135136137138139140141142143144145146
dumpstate
=> 0,
expert
=> 0,
);
# 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
5678910111213141516171819202122232425no
warnings
'experimental::signatures'
;
use
utf8;
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::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
323334353637383940414243444546474849505152package
ChordPro::Wx::STCEditor;
use
parent
https://metacpan.org/pod/qw( -norequire Wx::StyledTextCtrl )">qw( -norequire Wx::StyledTextCtrl )
;
#use parent qw( -norequire Wx::Scintilla::TextCtrl );
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
126127128129130131132133134135136137138139140141142143144145146
$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.
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
160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
# 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
ChordPro::Wx::Config;
use
ChordPro::Wx::Utils;
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
171819202122232425262728293031323334353637use
ChordPro::Wx::Utils;
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
121314151617181920212223242526272829303132333435363738394041package
ChordPro::Wx::WxChordPro;
use
ChordPro::Paths;
use
ChordPro::Wx::Config;
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
171819202122232425262728293031323334353637use
ChordPro::Wx::Utils;
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
636465666768697071727374757677787980818283848586878889909192
{
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
289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
# 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
389390391392393394395396397398399400401402403404405
$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
836837838839840841842843844845846847848849850851852853854855856
$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
871872873874875876877878879880881882883884885886887888889890891892
}
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
552553554555556557558559560561562563564565566567568569570571572573
" (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
888990919293949596979899100101102103104105106is_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"
} ] }
}