package Kephra::Document::Data;
our $VERSION = '0.08';
use strict;
use warnings;
# global values
my %values; # global doc values
sub values { \%values }
sub get_value { $values{$_[0]} if defined $values{$_[0]} }
sub set_value { $values{$_[0]} = $_[1] if defined $_[1] }
sub inc_value { $values{$_[0]}++ }
sub dec_value { $values{$_[0]}-- }
sub del_value { delete $values{$_[0]} if defined $values{$_[0]}}
# values per dos
my @attributes; # data per doc for all open docs
my $current_attr; # data of current doc
my $current_nr = 0;
my $previous_nr = 0;
# global attr functions
sub _attributes { \@attributes }
sub _values { \%values }
sub _hash { $attributes[$_[0]] }
sub _ep {
my $nr = valid_or_current_doc_nr($_[0]);
return if $nr < 0;
my $ep = $attributes[$nr]{ep_ref};
$ep if Kephra::App::EditPanel::is($ep);
}
sub count { @attributes }
sub last_nr { $#attributes }
sub previous_nr { $previous_nr }
sub current_nr { $current_nr }
sub next_nr {
my $inc = shift;
return unless defined $inc and $inc;
my $base = shift;
$base = current_nr() unless defined $base;
my $last_nr = last_nr();
my $nr = $base + $inc;
$nr += $last_nr+1 if $nr < 0;
$nr -= $last_nr+1 if $nr > $last_nr;
return validate_doc_nr($nr);
}
sub all_nr { [0..last_nr()] }
sub get_previous_nr { $previous_nr }
sub set_previous_nr { $previous_nr = $_[0] if defined $_[0] }
sub get_current_nr { $current_nr }
sub set_current_nr {
$current_nr = $_[0] if defined $_[0] and validate_doc_nr($_[0]) > -1;
$current_attr = $attributes[$current_nr];
Kephra::App::EditPanel::_set_ref( _ep($current_nr) );
my $fconf = Kephra::File::_config();
$fconf->{current}{directory} = get_attribute('directory', $current_nr)
if ref $fconf;
}
sub validate_doc_nr {
my $nr = shift;
return -1 unless defined $nr;
return -1 unless $nr eq int $nr;
$nr = exists $attributes[$nr] ? $nr : -1;
}
sub valid_or_current_doc_nr {
my $nr = validate_doc_nr(shift);
$nr == -1 ? current_nr() : $nr;
}
sub create_slot {
my $nr = shift;
$attributes[$_+1] = $attributes[$_] for reverse $nr .. last_nr();
$attributes[$nr] = {};
set_current_nr($current_nr+1) if $current_nr >= $nr;
$previous_nr++ if $previous_nr >= $nr;
}
sub empty_slot {
my $nr = shift;
return if $nr < 0 or exists $attributes[$nr];
$attributes[$nr] = {}
}
sub delete_slot {
my $nr = validate_doc_nr(shift);
return if $nr < 0;
splice @attributes, $nr, 1;
}
# generic attr data accessors on any value and any doc
sub get_attribute {
my $attr = shift;
return unless defined $attr or $attr;
my $nr = shift;
$nr = defined $nr ? validate_doc_nr($nr) : current_nr();
return if $nr < 0;
$attributes[ $nr ]{ $attr } if defined $attributes[ $nr ]{ $attr };
}
sub set_attribute {
my $attr = shift;
my $value = shift;
return unless defined $value;
my $nr = shift;
$nr = defined $nr ? validate_doc_nr($nr) : current_nr();
return if $nr < 0;
$attributes[ $nr ]{ $attr } = $value;
$value;
}
sub set_all_attributes { # all attr of one doc
my $attr = shift;
my $nr = validate_doc_nr(shift);
return if $nr < 0 or ref $attr ne 'HASH';
$attributes[ $nr ] = $attr;
}
# shortcut accessors just for current doc and many values
sub attributes {
my $params = shift;
my $nr = validate_doc_nr(shift);
return if $nr < 0;
my $attr = $attributes[$nr];
if (ref $params eq 'ARRAY') {
my @result;
push @result, $attr->{ $_ } for @$params;
return \@result;
}
elsif (ref $params eq 'HASH') {
$attr->{$_} = $params->{$_} for keys %$params;
}
}
# shortcut accessors just for current doc and one value
sub attr {
if (defined $_[1]){ $current_attr->{$_[0]} = $_[1]}
else { $current_attr->{$_[0]} }
}
# specific data (attribute) accessors
sub first_name { get_attribute('firstname', $_[0]) }
sub file_name { get_attribute('file_name', $_[0]) }
sub file_path { defined $_[0] ? set_file_path($_[0]) : get_file_path() }
sub get_file_path { get_attribute('file_path', $_[0]) }
sub set_file_path {
my ( $file_path, $doc_nr ) = @_;
$doc_nr = valid_or_current_doc_nr($doc_nr);
set_attribute('file_path', $file_path, $doc_nr);
dissect_path( $file_path, $doc_nr );
}
sub dissect_path {
my ($file_path, $doc_nr) = @_;
$doc_nr = validate_doc_nr($doc_nr);
return if $doc_nr < 0;
my $attr = $attributes[$doc_nr];
my ($volume, $directories, $file) = File::Spec->splitpath( $file_path );
$directories = $volume.$directories if $volume;
$attr->{directory} = $directories;
$attr->{file_name} = $file;
if ( length($file) > 0 ) {
my @filenameparts = split /\./, $file ;
$attr->{ending} = pop @filenameparts if @filenameparts > 1;
$attr->{firstname}= join '.', @filenameparts;
}
}
sub all_file_pathes {
my @pathes;
push @pathes, $_->{file_path} for @attributes;
return \@pathes;
}
sub all_file_names {
my @names;
$names[$_] = $_->{file_name} for @attributes;
return \@names;
}
sub nr_from_file_path {
my $given_path = shift;
return -1 unless $given_path;
for ( 0 .. $#attributes ) {
if (defined $attributes[$_]{'file_path'}
and $attributes[$_]{'file_path'} eq $given_path) {
return $_;
}
}
return -1;
}
sub file_already_open { 1 if nr_from_file_path(shift) > -1 }
sub cursor_pos {
$attributes[$current_nr]{cursor_pos} if $values{loaded};
}
sub nr_from_ep {
my $ep = shift;
for (@{all_nr()}) {
return $_ if $ep eq _ep($_);
}
return -1;
}
sub get_all_ep {
my @ep;
for (@{all_nr()}) {
my $ep = _ep($_);
push @ep, $ep if $ep;
}
\@ep;
}
# more complex operations
sub set_missing_attributes_to_default {
my ($nr, $file) = @_;
$nr = validate_doc_nr($nr);
return if $nr < 0;
$file = get_file_path($nr) unless defined $file;
my $default = Kephra::File::_config()->{defaultsettings};
}
sub set_attributes_to_default {
my ($nr, $file) = @_;
$nr = validate_doc_nr($nr);
return if $nr < 0;
my $config = Kephra::File::_config()->{defaultsettings};
return unless ref $config eq 'HASH';
$file = get_file_path($nr) unless defined $file;
my $attr = {
'edit_pos' => -1,
'ep_ref' => _ep($nr),
'file_path' => $file,
};
my $default = (defined $file and -e $file) ? $config->{open} : $config->{new};
$attr->{$_} = $default->{$_}
for qw(EOL codepage cursor_pos readonly syntaxmode tab_size tab_use);
set_all_attributes($attr, $nr);
dissect_path($file, $nr);
set_current_nr($nr) if $nr == current_nr();
}
sub evaluate_attributes {
my $doc_nr = validate_doc_nr(shift);
return if $doc_nr < 0;
my $config = Kephra::File::_config();
my $attr = $attributes[$doc_nr];
my $ep = Kephra::App::EditPanel::_ref();
Kephra::EventTable::freeze('document.text.change');
Kephra::Document::Property::set( {$_ => $attr->{$_} } )
for qw(codepage tab_use tab_size EOL readonly syntaxmode);
Kephra::EventTable::thaw('document.text.change');
# setting selection and caret position
if ($attr->{selstart} and $attr->{selstart}) {
$attr->{cursor_pos} < $attr->{selend}
? $ep->SetSelection( $attr->{selend},$attr->{selstart})
: $ep->SetSelection( $attr->{selstart},$attr->{selend});
}
else { $ep->GotoPos( $attr->{cursor_pos} ) }
if ($config->{open}{in_current_dir}){
$config->{current}{directory} = $attr->{directory}
if $attr->{directory};
}
else { $config->{current}{directory} = '' }
Kephra::App::EditPanel::set_word_chars($ep);
Kephra::App::EditPanel::Indicator::paint_bracelight($ep)
if Kephra::App::EditPanel::Indicator::bracelight_visible();
Kephra::App::EditPanel::Margin::autosize_line_number();
Kephra::App::EditPanel::Fold::restore($doc_nr);
Kephra::App::StatusBar::refresh_cursor();
Kephra::Edit::Marker::restore($doc_nr);
Kephra::Edit::_let_caret_visible();
}
sub update_attributes { # was named save_properties
my $doc_nr = valid_or_current_doc_nr(shift);
return if $doc_nr < 0;
my $attr = _hash($doc_nr);
my $ep = _ep($doc_nr);
$attr->{cursor_pos}= $ep->GetCurrentPos;
$attr->{selstart} = $ep->GetSelectionStart;
$attr->{selend} = $ep->GetSelectionEnd;
Kephra::App::EditPanel::Fold::store($doc_nr);
Kephra::Edit::Marker::store($doc_nr);
}
1;
=head1 NAME
Kephra::Document::Data - API for data assotiated with opened documents
=head1 DESCRIPTION
=cut