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