Source

Kephra / lib / Kephra / App / Bar / Document.pm

use v5.10;
use strict;
use warnings;
use Wx;
use Wx::AUI;
use Kephra::API;
use Kephra::App::Panel;
use Kephra::App::Util;

package Kephra::App::Bar::Document;
our @ISA = 'Wx::AuiNotebook';

Kephra::API::Command::register({
 'docbar-select-left'     =>{sub=>'$docbar->select_page_left',     label=>'Previous Tab',  keys=>'ctrl+pageup'},
 'docbar-select-right'    =>{sub=>'$docbar->select_page_right',    label=>'Next Tab',      keys=>'ctrl+pagedown'},
 'docbar-select-leftmost' =>{sub=>'$docbar->select_page_leftmost', label=>'First Tab',     keys=>'ctrl+shift+pageup'},
 'docbar-select-rightmost'=>{sub=>'$docbar->select_page_rightmost',label=>'Last Tab',      keys=>'ctrl+shift+pagedown'},
 'docbar-move-left'       =>{sub=>'$docbar->move_page_left',       label=>'Move Left',     keys=>'alt+pageup'},
 'docbar-move-right'      =>{sub=>'$docbar->move_page_right',      label=>'Move Right',    keys=>'alt+pagedown'},
 'docbar-move-leftmost'   =>{sub=>'$docbar->move_page_leftmost',   label=>'Move Leftmost', keys=>'alt+shift+pageup'},
 'docbar-move-rightmost'  =>{sub=>'$docbar->move_page_rightmost',  label=>'Move Rightmost',keys=>'alt+shift+pagedown'},
});


my ($is_panel, $is_widget) = Kephra::App::Util::get(qw/is_panel is_widget/);

sub new {
	my( $class, $parent) = @_;
	my $self = $class->SUPER::new( $parent, -1, [-1,-1], [-1,-1],
		&Wx::wxAUI_NB_TOP | &Wx::wxAUI_NB_TAB_MOVE | &Wx::wxAUI_NB_WINDOWLIST_BUTTON |
		&Wx::wxAUI_NB_SCROLL_BUTTONS | &Wx::wxAUI_NB_CLOSE_ON_ACTIVE_TAB
	);

	$self->{'visual_page_order'} = [];   # visual order of internal indexes : vis -> int
	$self->{'internal_page_order'} = []; # internal order of visual indexes : int -> vis
	$self->activate_events();

	return $self;
}

sub activate_events {
	my ($self) = @_;
	Wx::Event::EVT_AUINOTEBOOK_BEGIN_DRAG( $self, -1, sub {
		my ($nb, $event ) = @_;
		$self->{'DND_page_nr'} = $event->GetSelection;
	});
	Wx::Event::EVT_AUINOTEBOOK_END_DRAG($self, -1, sub {
		my ($nb, $event ) = @_;
		return unless defined $self->{'DND_page_nr'};
		$nb->move_page_index_visually($self->{'DND_page_nr'}, $event->GetSelection);
		$self->{'DND_page_nr'} = undef;
	});
	Wx::Event::EVT_AUINOTEBOOK_PAGE_CHANGED( $self, -1, sub {
		my ($nb, $event ) = @_;
		my $new_page = $nb->GetPage( $event->GetSelection );
		Kephra::API::Doc::set_active_doc( $new_page );
		Kephra::API::main_window()->refresh_title();
		Wx::Window::SetFocus( $new_page );
		$event->Skip;
	});
	Wx::Event::EVT_AUINOTEBOOK_PAGE_CLOSE( $self, -1, sub {
		my ($nb, $event ) = @_; 
		$event->Veto;
		Kephra::File::close_active();
	});
	#Wx::Event::EVT_AUINOTEBOOK_PAGE_CHANGED(
	#);
	# keep focus on editor even when klicking on tab bar
	#Wx::Event::EVT_LEFT_DOWN($self, sub { Kephra::API::active_editor()->focus; print "--"; });
	#$self->Connect( -1, -1, &Wx::wxEVT_SET_FOCUS, sub {  print "--"; } )
}
sub silence_events {
	my ($self) = @_;
	Wx::Event::EVT_AUINOTEBOOK_BEGIN_DRAG( $self, -1, sub {});
	Wx::Event::EVT_AUINOTEBOOK_END_DRAG  ( $self, -1, sub {});
	Wx::Event::EVT_AUINOTEBOOK_PAGE_CHANGED( $self, -1, sub {});
	Wx::Event::EVT_AUINOTEBOOK_PAGE_CLOSE  ( $self, -1, sub {});
}

sub add_page {
	my ($self, $new_page, $label, $position) = @_;
	return unless $new_page and ($is_panel->($new_page) or $is_widget->($new_page));
	my $active_pos = $self->GetSelection;

	$label    = ''                           unless defined $label;
	$position = 'right'                      if $position eq 'default' or $position == -1;
	$position = $self->rightmost_page_pos+1  if $position eq 'rightmost';
	$position = $active_pos + 1              if $position eq 'right';
	$position = $active_pos                  if $position eq 'left';
	$position = $self->leftmost_page_pos     if $position eq 'leftmost';
	#$set_active = 1                          unless defined $set_active;

	# put panel under a none-panel
	unless ($new_page->isa('Kephra::App::Panel')) {
		my $widget = $new_page;
		$new_page = Kephra::App::Panel->new( $self, \$widget);# if ($position == 0 or $label);
		# document property: panel the editor sits on has to be set before EVT_AUINOTEBOOK_PAGE_CHANGED
		Kephra::DocumentStash::doc_by_ed( $widget )->panel( $new_page ) if $widget->isa('Kephra::Editor'); # and ($position == 0 or $label);
	}
	else { $new_page->Reparent( $self ) }

	$self->InsertPage( $position, $new_page, '');# if $position == 0 or $label; # last parameter sets new pace as active
	$self->set_page_title( $label, $new_page );

	# inserting new index to position translators
	for   (@{$self->{'visual_page_order'}}){ $_++ if $_ >= $position }
	splice @{$self->{'visual_page_order'}},  $position, 0, $position;
	$self->refresh_internal_page_order();

	return $new_page;
}

sub remove_page {
	my ($self, $internal_position) = @_;

	$self->DeletePage( $internal_position );
	my $visual_position = $self->{'internal_page_order'}[$internal_position];
	my $visual = $self->{'visual_page_order'};
	splice @$visual, $visual_position, 1;
	for (@$visual) {$_-- if $_ >= $internal_position}
	$self->{'visual_page_order'} = $visual;
	$self->refresh_internal_page_order;
}

sub refresh_internal_page_order {
	my ($self) = @_;
	my $visual = $self->{'visual_page_order'};
	return unless ref $visual eq ref [];
	my $internal;
	$internal->[ $visual->[$_] ] = $_ for 0 .. @$visual-1;
	$self->{'internal_page_order'} = $internal;
}

sub move_page_index_visually { # for dnd only
	my ($self, $from, $to ) = @_;
	return unless $from >= 0 and $from < $self->GetPageCount;
	return unless $to >= 0 and $to < $self->GetPageCount;
	my $index = splice @{$self->{'visual_page_order'}}, $from, 1;
	splice @{$self->{'visual_page_order'}}, $to, 0, $index;
	$self->refresh_internal_page_order();
}

sub move_page_visually { # for movements by keyboard
	my ($self, $from, $to ) = @_;
	my $max = $self->GetPageCount - 1;
	return if $from < 0 or $from > $max;
	return if $to < 0 or $to > $max;
	return if $from == $to;

	my $pos = $self->{'visual_page_order'}[ $from ];
	my $page = $self->GetPage( $pos );
	my $label = $self->GetPageText( $pos );
	my $visual = $self->{'visual_page_order'};

	$self->silence_events();
	$self->RemovePage( $pos );
	$self->InsertPage( $to, $page, $label);
	my $removed = splice @$visual, $from, 1;
	if ($from < $to) { for (@$visual) {$_-- if $_ > $removed and $_ <= $to} }
	else             { for (@$visual) {$_++ if $_ < $removed and $_ >= $to} }
	splice @$visual, $to, 0, $to;
	$self->{'visual_page_order'} = $visual;
	$self->refresh_internal_page_order();
	$self->SetSelection( $self->{'visual_page_order'}[$to] );
	$self->activate_events();
}

sub move_page_left  { 
	my ($self) = @_;
	$self->move_page_visually ( 
		$self->active_visual_pos, $self->next_page_pos_rot_left( $self->GetSelection )
	);
}
sub move_page_right { 
	my ($self) = @_;
	$self->move_page_visually( 
		$self->active_visual_pos, $self->next_page_pos_rot_right( $self->GetSelection )
	);
}
sub move_page_leftmost  { 
	my ($self) = @_;
	$self->move_page_visually( $self->active_visual_pos, $self->leftmost_page_pos );
}
sub move_page_rightmost { 
	my ($self) = @_;
	$self->move_page_visually( $self->active_visual_pos, $self->rightmost_page_pos );
}

sub select_page_left  { 
	my ($self) = @_;
	$self->raise_page( $self->next_page_pos_rot_left( $self->GetSelection ) );
}
sub select_page_right { 
	my ($self) = @_;
	$self->raise_page( $self->next_page_pos_rot_right( $self->GetSelection ) );
}
sub select_page_leftmost  { $_[0]->raise_page( $_[0]->leftmost_page_pos ) }
sub select_page_rightmost { $_[0]->raise_page( $_[0]->rightmost_page_pos ) }

sub leftmost_page_pos { 0 }
sub rightmost_page_pos { $_[0]->GetPageCount-1 }
sub active_visual_pos { $_[0]->{'internal_page_order'}[ $_[0]->GetSelection ] }
sub next_page_pos_rot_left {
	my ($self) = @_; # take in position of internal order
	my $pos = $self->{'internal_page_order'}[ $_[1] ];
	$self->{'visual_page_order'}[$pos == 0 ? $self->rightmost_page_pos : $pos-1]
}
sub next_page_pos_rot_right {
	my ($self) = @_; # take in position of internal order
	my $pos = $self->{'internal_page_order'}[ $_[1] ];
	$self->{'visual_page_order'}[$pos == $self->rightmost_page_pos ? 0 : $pos+1]
}

sub raise_page {
	my ($self, $iop) = @_; # can be Index Or Page (panel reference)
	my $index = int $iop eq $iop ? $iop : $self->GetPageIndex($iop);
	return if $index < 0 or $index > $self->rightmost_page_pos;
	$self->SetSelection( $index );
	my $page = $self->GetPage($index);
	#print $page->GetChildren,"||\n";
	Kephra::App::Focus::set($page);
}

sub set_page_title {
	my ($self, $label, $page) = @_;
	return if ref $page and $page ne ref $page and $page->isa(' Wx::Window');
	my $position = $page ? $self->GetPageIndex($page) : $self->GetSelection;
	return unless $position >= 0;
	$label = '<untitled>' unless $label;
	my $max_width = 15;
	if ( length($label) > $max_width and $max_width > 7 ) {
		$label = substr( $label, 0, $max_width - 2 ) . '..';
	}
	$self->SetPageText( $position, $label );
}

sub refresh_page_title {
	my $self = shift;
	my $doc = shift // Kephra::API::document();
	my $unsaved_mark = '*';
	my $position = $self->GetSelection;
	my $label = $self->GetPageText( $position);
	chop $label if substr($label,-1) eq $unsaved_mark;
	$label .= $unsaved_mark if $doc->editor->GetModify;
	$self->SetPageText($position, $label);
}

1;
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.