App-Music-ChordPro/lib/ChordPro/Wx/PreferencesDialog.pm
#! perl
use strict;
use warnings;
use utf8;
# Implementation of ChordPro::Wx::PreferencesDialog_wxg details.
package ChordPro::Wx::PreferencesDialog;
# ChordPro::Wx::PreferencesDialog_wxg is generated by wxGlade and contains
# all UI associated code.
use parent qw( ChordPro::Wx::PreferencesDialog_wxg );
use Wx qw[:everything];
use Wx::Locale gettext => '_T';
use ChordPro::Utils qw(is_macos);
# BUilt-in descriptions for some notation systems.
my $notdesc =
{ "common" => "C, D, E, F, G, A, B",
"dutch" => "C, D, E, F, G, A, B",
"german" => "C, ... A, Ais/B, H",
"latin" => "Do, Re, Mi, Fa, Sol, ...",
"scandinavian" => "C, ... A, A#/Bb, H",
"solfege" => "Do, Re, Mi, Fa, So, ...",
"solfège" => "Do, Re, Mi, Fa, So, ...",
"nashville" => "1, 2, 3, ...",
"roman" => "I, II, III, ...",
};
my $is_macos_crippled = is_macos();
sub get_configfile {
my ( $self ) = @_;
# warn("CF: ", $self->GetParent->{prefs_configfile} || "");
$self->GetParent->{prefs_configfile} || ""
}
# As of wxGlade 1.0 __set_properties and __do_layout are gone.
sub new {
my $self = shift->SUPER::new(@_);
$self->fetch_prefs();
$self;
}
sub _enablecustom {
my ( $self ) = @_;
my $n = $self->{cb_configfile}->IsChecked;
for ( $self->{t_configfiledialog}, $self->{b_configfiledialog} ) {
$_->Enable($n);
}
$n = $self->{cb_customlib}->IsChecked;
for ( $self->{t_customlibdialog}, $self->{b_customlibdialog} ) {
$_->Enable($n);
}
$n = $self->{cb_tmplfile}->IsChecked;
for ( $self->{t_tmplfiledialog}, $self->{b_tmplfiledialog} ) {
$_->Enable($n);
}
}
sub fetch_prefs {
my ( $self ) = @_;
# Fetch preferences from parent.
my $parent = $self->GetParent;
# Skip default (system, user, song) configs.
$self->{cb_skipstdcfg}->SetValue($parent->{prefs_skipstdcfg});
# Presets.
$self->{cb_presets}->SetValue($parent->{prefs_enable_presets});
$self->{ch_presets}->Enable($parent->{prefs_enable_presets});
my $ctl = $self->{ch_presets};
$ctl->Clear;
for ( @{ $parent->stylelist } ) {
my $t = ucfirst(lc($_));
$t =~ s/_/ /g;
$t =~ s/ (.)/" ".uc($1)/eg;
$ctl->Append($t);
}
my $p = $parent->{prefs_cfgpreset};
foreach ( @$p ) {
if ( $_ eq "custom" ) {
$self->{cb_configfile}->SetValue(1);
next;
}
my $t = ucfirst(lc($_));
$t =~ s/_/ /g;
$t =~ s/ (.)/" ".uc($1)/eg;
my $n = $ctl->FindString($t);
unless ( $n == wxNOT_FOUND ) {
$ctl->Check( $n, 1 );
}
}
# Custom config file.
$self->{cb_configfile}->SetValue($parent->{prefs_enable_configfile});
$self->{t_configfiledialog}->SetValue($parent->{prefs_configfile})
if $parent->{prefs_configfile};
# Custom library.
$self->{cb_customlib}->SetValue($parent->{prefs_enable_customlib});
$self->{t_customlibdialog}->SetValue($parent->{prefs_customlib})
if $parent->{prefs_customlib};
# New song template.
$self->{cb_tmplfile}->SetValue($parent->{prefs_enable_tmplfile});
$self->{t_tmplfiledialog}->SetValue($parent->{prefs_tmplfile})
if $parent->{prefs_tmplfile};
# Editor.
$ctl = $self->{ch_editfont};
$ctl->SetSelection( $parent->{prefs_editfont} );
$ctl = $self->{sp_editfont};
$ctl->SetValue( $parent->{prefs_editsize} );
# Notation.
$ctl = $self->{ch_notation};
$ctl->Clear;
my $n = 0;
my $check = 0;
for ( @{ $parent->notationlist } ) {
my $s = ucfirst($_);
$check = $n if $_ eq lc $parent->{prefs_notation};
$s .= " (" . $notdesc->{lc($s)} .")" if $notdesc->{lc($s)};
$ctl->Append($s);
$ctl->SetClientData( $n, $_);
$n++;
}
$ctl->SetSelection($check);
# Transpose.
# Transcode.
$ctl = $self->{ch_transcode};
$ctl->Clear;
$ctl->Append("-----");
$n = 1;
for ( @{ $parent->notationlist } ) {
my $s = ucfirst($_);
$check = $n if $_ eq lc $parent->{prefs_xcode};
$s .= " (" . $notdesc->{lc($s)} .")" if $notdesc->{lc($s)};
$ctl->Append($s);
$ctl->SetClientData( $n, $_);
$n++;
}
$ctl->SetSelection($check);
# PDF Viewer.
$self->{t_pdfviewer}->SetValue($parent->{prefs_pdfviewer})
if $parent->{prefs_pdfviewer};
$self->_enablecustom;
if ( $is_macos_crippled ) {
# Cannot use chooser, hide button and change tooltip.
for ( qw( configfile customlib tmplfile ) ) {
$self->{"sz_$_"}->Hide($self->{"b_${_}dialog"});
$self->{"sz_$_"}->Layout;
my $t = $self->{"t_${_}dialog"}->GetToolTip->GetTip;
$t =~ s/ by pressing .* button//;
$self->{"t_${_}dialog"}->SetToolTipString($t);
}
}
}
# C D E F G A B C
my @xpmap = qw( 0 1 1 2 3 3 4 5 6 6 7 8 8 9 10 10 11 12 );
my @sfmap = qw( 0 7 -5 2 9 -3 4 -1 6 -6 1 8 -4 3 10 -2 5 0 );
sub store_prefs {
my ( $self ) = @_;
# Transfer all preferences to the parent.
my $parent = $self->GetParent;
# Skip default (system, user, song) configs.
$parent->{prefs_skipstdcfg} = $self->{cb_skipstdcfg}->IsChecked;
# Presets.
$parent->{prefs_enable_presets} = $self->{cb_presets}->IsChecked;
my $ctl = $self->{ch_presets};
my $cnt = $ctl->GetCount;
my @p;
my $styles = $parent->stylelist;
for ( my $n = 0; $n < $cnt; $n++ ) {
next unless $ctl->IsChecked($n);
push( @p, $styles->[$n] );
if ( $n == $cnt - 1 ) {
my $c = $self->{t_configfiledialog}->GetValue;
if ( $is_macos_crippled && ! -r $c ) {
my $md = Wx::MessageDialog->new
( $self,
"Custom config file $c can not be read.\n".
"Please enter the name of an existing config file.",
"Config file can not be read",
0 | wxOK | wxICON_QUESTION );
my $ret = $md->ShowModal;
$md->Destroy;
return;
}
$parent->{_cfgpresetfile} =
$parent->{prefs_configfile} = $c;
}
}
$parent->{prefs_cfgpreset} = \@p;
# Custom config file.
$parent->{prefs_enable_configfile} = $self->{cb_configfile}->IsChecked;
$parent->{prefs_configfile} = $self->{t_configfiledialog}->GetValue;
# Custom library.
$parent->{prefs_enable_customlib} = $self->{cb_customlib}->IsChecked;
$parent->{prefs_customlib} = $ENV{CHORDPRO_LIB} // $self->{t_customlibdialog}->GetValue;
# New song template.
$parent->{prefs_enable_tmplfile} = $self->{cb_tmplfile}->IsChecked;
$parent->{prefs_tmplfile} = $self->{t_tmplfiledialog}->GetValue;
# Editor.
$parent->{prefs_editfont} = $self->{ch_editfont}->GetSelection;
$parent->{prefs_editsize} = $self->{sp_editfont}->GetValue;
# Notation.
my $n = $self->{ch_notation}->GetSelection;
if ( $n > 0 ) {
$parent->{prefs_notation} =
$self->{ch_notation}->GetClientData($n);
}
else {
$parent->{prefs_notation} = "";
}
# Transpose.
$parent->{prefs_xpose_from} = $xpmap[$self->{ch_xpose_from}->GetSelection];
$parent->{prefs_xpose_to } = $xpmap[$self->{ch_xpose_to }->GetSelection];
$parent->{prefs_xpose_acc} = $self->{ch_acc}->GetSelection;
$n = $parent->{prefs_xpose_to} - $parent->{prefs_xpose_from};
$n += 12 if $n < 0;
$n += 12 if $parent->{prefs_xpose_acc} == 1; # sharps
$n -= 12 if $parent->{prefs_xpose_acc} == 2; # flats
$parent->{prefs_xpose} = $n;
# Transcode.
$n = $self->{ch_transcode}->GetSelection;
if ( $n > 0 ) {
$parent->{prefs_xcode} =
$self->{ch_transcode}->GetClientData($n);
}
else {
$parent->{prefs_xcode} = "";
}
# PDF Viewer.
$parent->{prefs_pdfviewer} = $self->{t_pdfviewer}->GetValue;
}
################ Event handlers ################
# Event handlers override the subs generated by wxGlade in the _wxg class.
sub OnConfigFile {
my ( $self, $event ) = @_;
my $n = $self->{cb_configfile}->IsChecked;
for ( $self->{t_configfiledialog}, $self->{b_configfiledialog} ) {
$_->Enable($n);
}
$event->Skip;
}
sub OnConfigFileDialog {
my ( $self, $event ) = @_;
my $fd = Wx::FileDialog->new
($self, _T("Choose config file"),
"", $self->GetParent->{prefs_configfile} || "",
"Config files (*.prp,*.json)|*.prp;*.json|All files|*.*",
0|wxFD_OPEN,
wxDefaultPosition);
my $ret = $fd->ShowModal;
if ( $ret == wxID_OK ) {
my $file = $fd->GetPath;
if ( -f $file ) {
$self->{t_configfiledialog}->SetValue($file);
}
else {
my $md = Wx::MessageDialog->new
( $self,
"Create new config $file?",
"Creating a config file",
wxYES_NO | wxICON_INFORMATION );
my $ret = $md->ShowModal;
$md->Destroy;
if ( $ret == wxID_YES ) {
my $fd;
if ( open( $fd, ">:utf8", $file )
and print $fd ChordPro::Config::default_config()
and close($fd) ) {
$self->{t_configfiledialog}->SetValue($file);
}
else {
my $md = Wx::MessageDialog->new
( $self,
"Error creating $file: $!",
"File open error",
wxOK | wxICON_ERROR );
$md->ShowModal;
$md->Destroy;
}
}
}
}
$fd->Destroy;
}
sub OnCustomLib {
my ( $self, $event ) = @_;
my $n = $self->{cb_customlib}->IsChecked;
for ( $self->{t_customlibdialog}, $self->{b_customlibdialog} ) {
$_->Enable($n);
}
}
sub OnCustomLibDialog {
my ( $self, $event ) = @_;
my $fd = Wx::DirDialog->new
($self, _T("Choose custom library"),
$self->GetParent->{prefs_customlib} || "",
0|wxDD_DIR_MUST_EXIST,
wxDefaultPosition);
my $ret = $fd->ShowModal;
if ( $ret == wxID_OK ) {
my $file = $fd->GetPath;
$self->{t_customlibdialog}->SetValue($file);
$ENV{CHORDPRO_LIB} = $file;
}
$fd->Destroy;
}
sub OnTmplFile {
my ( $self, $event ) = @_;
my $n = $self->{cb_tmplfile}->IsChecked;
for ( $self->{t_tmplfiledialog}, $self->{b_tmplfiledialog} ) {
$_->Enable($n);
}
}
sub OnTmplFileDialog {
my ( $self, $event ) = @_;
my $fd = Wx::FileDialog->new
($self, _T("Choose template for new songs"),
"", $self->GetParent->{prefs_tmplfile} || "",
"ChordPro files (*.cho,*.crd,*.chopro,*.chord,*.chordpro,*.pro)|*.cho;*.crd;*.chopro;*.chord;*.chordpro;*.pro|All files|*.*",
0|wxFD_OPEN|wxFD_FILE_MUST_EXIST,
wxDefaultPosition);
my $ret = $fd->ShowModal;
if ( $ret == wxID_OK ) {
my $file = $fd->GetPath;
$self->{t_tmplfiledialog}->SetValue($file);
}
$fd->Destroy;
}
sub OnAccept {
my ( $self, $event ) = @_;
$self->store_prefs();
$event->Skip;
}
sub OnCancel {
my ( $self, $event ) = @_;
$event->Skip;
}
sub OnSkipStdCfg {
my ( $self, $event ) = @_;
$event->Skip;
}
sub OnPresets {
my ( $self, $event ) = @_;
$self->{ch_presets}->Enable( $self->{cb_presets}->GetValue );
$event->Skip;
}
sub OnXposeFrom {
my ( $self, $event ) = @_;
$self->OnXposeTo($event);
}
sub OnXposeTo {
my ( $self, $event ) = @_;
my $sel = $self->{ch_xpose_to}->GetSelection;
my $sf = $sfmap[$sel];
if ( $sf == 0 ) {
$sf = $sel - $self->{ch_xpose_from}->GetSelection;
}
if ( $sf < 0 ) {
$self->{ch_acc}->SetSelection(2);
}
elsif ( $sf > 0 ) {
$self->{ch_acc}->SetSelection(1);
}
else {
$self->{ch_acc}->SetSelection(0);
}
$event->Skip;
}
sub OnChNotation {
my ( $self, $event ) = @_;
my $n = $self->{ch_notation}->GetSelection;
$event->Skip;
}
sub OnChTranscode {
my ( $self, $event ) = @_;
my $n = $self->{ch_transcode}->GetSelection;
$event->Skip;
}
sub OnChEditFont {
my ($self, $event) = @_;
my $p = $self->GetParent;
my $n = $self->{ch_editfont}->GetSelection;
my $ctl = $p->{t_source};
my $font = $p->fonts->[$n]->{font};
$font->SetPointSize($p->{prefs_editsize});
$ctl->SetFont($font);
$p->{prefs_editfont} = $n;
$event->Skip;
}
sub OnSpEditFont {
my ($self, $event) = @_;
my $p = $self->GetParent;
my $n = $self->{sp_editfont}->GetValue;
my $ctl = $p->{t_source};
my $font = $ctl->GetFont;
$font->SetPointSize($n);
$ctl->SetFont($font);
$p->{prefs_editsize} = $n;
$event->Skip;
}
1;