Source

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

use strict;
use warnings;
use Wx;
use Wx::DND;
use Kephra::API;
use Kephra::App::Editor;
use Kephra::App::Panel;
use Kephra::App::Util;
use Kephra::CommandList;
use Kephra::DocumentStash;

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

Kephra::CommandList::register_cmd_namespace('document');
Kephra::CommandList::register_switch_var('Kephra::API::docbar');
Kephra::CommandList::register_cmd({
	'-select-left' => { 
		sub => '$_->select_page_left',    label => 'Previous Tab',keys => 'ctrl+pgup', 
		help => ''},
	'-select-right' => {
		sub => '$_->select_page_right',   label => 'Next Tab',keys => 'ctrl+pgdown',
		help => ''},
	'-select-leftmost'=> {
		sub => '$_->select_page_leftmost',label => 'First Tab',keys => 'ctrl+shift+pgup',
		help => ''},
	'-select-rightmost' => {
		sub => '$_->select_page_rightmost',label => 'Last Tab',keys => 'ctrl+shift+pgdown',
		help => ''},
	'-move-left' => {
		sub => '$_->move_page_left',     label => 'Move Left',keys => 'alt + pgup',
		help => ''},
	'-move-right' => {
		sub => '$_->move_page_right',    label => 'Move Right', keys => 'alt + pgdown',
		help => ''},
	'-move-leftmost'  => {
		sub => '$_->move_page_leftmost', label => 'Move Leftmost',keys => 'alt + shift + pgup',
		help => ''},
	'-move-rightmost' => {
		sub => '$_->move_page_rightmost',label => 'Move Rightmost',keys => 'alt + shift + pgdown',
		help => ''},
});


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::DocumentStash::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();
	});
	# 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, $label, $position, $set_active, $panel_candidate ) = @_;
	return unless $panel_candidate 
		and ($is_panel->($panel_candidate) or $is_widget->($panel_candidate) or
		     $panel_candidate->isa('Kephra::Document')
	);
	my $new_panel  = $panel_candidate->isa('Kephra::Document')
		? $panel_candidate->editor
		: $panel_candidate;
	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;

	unless ($new_panel->isa('Wx::Panel')) { # put panel under a none-panel
		$new_panel = Kephra::App::Panel->new( $self, $new_panel);
	}
	else { $new_panel->Reparent( $self ) }

	# document property panel ref has to be set before EVT_AUINOTEBOOK_PAGE_CHANGED 
	$panel_candidate->panel( $new_panel ) if $panel_candidate->isa('Kephra::Document');

	$self->InsertPage( $position, $new_panel, $label, $set_active );
	$self->set_page_title( $position, $label);
	Wx::Window::SetFocus( $new_panel ) if $set_active;

	# 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 $position;
}

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->SetSelection( $self->next_page_pos_rot_left( $self->GetSelection ) );
}
sub select_page_right { 
	my ($self) = @_;
	$self->SetSelection( $self->next_page_pos_rot_right( $self->GetSelection ) );
}
sub select_page_leftmost  { $_[0]->SetSelection( $_[0]->leftmost_page_pos ) }
sub select_page_rightmost { $_[0]->SetSelection( $_[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, $page) = @_;
	$self->SetSelection( $self->GetPageIndex($page) );
}

sub set_page_title {
	my ($self, $position, $label) = @_;
	return unless $position >= 0 and $position < $self->GetPageCount;
	$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 );
}

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.