# Copyright (C) 2007-13 Stephane Galland <
[email protected]>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
=pod
=head1 NAME
AutoLaTeX::GUI::Gtk::TranslatorPanel - A GTK panel for translator management
=head1 DESCRIPTION
AutoLaTeX::GUI::Gtk::TranslatorPanel is a Perl module, which permits to
display a Gtk panel that manages AutoLaTeX translators.
=head1 METHOD DESCRIPTIONS
This section contains only the methods in GtkTranslatorPanel.pm itself.
=over
=cut
package AutoLaTeX::GUI::Gtk::TranslatorPanel;
@ISA = qw( Gtk2::Table AutoLaTeX::GUI::Gtk::WidgetUtil AutoLaTeX::GUI::AbstractTranslatorPanel );
@EXPORT = qw();
@EXPORT_OK = qw();
require 5.014;
use strict;
use utf8;
use vars qw(@ISA @EXPORT @EXPORT_OK $VERSION);
use Exporter;
use Glib qw(TRUE FALSE);
use Gtk2;
use Gtk2::SimpleList;
use AutoLaTeX::Core::Util;
use AutoLaTeX::Core::IntUtils;
use AutoLaTeX::Core::Config;
use AutoLaTeX::Core::Translator;
use AutoLaTeX::GUI::AbstractTranslatorPanel;
use AutoLaTeX::GUI::Gtk::WidgetUtil;
#------------------------------------------------------
#
# Global vars
#
#------------------------------------------------------
# Version number
my $VERSION = "7.0" ;
#------------------------------------------------------
#
# Functions
#
#------------------------------------------------------
=pod
=item * new(\%\%\%\%;$)
Contructor.
Parameters are:
=over 4
=item the current configuration extracted from the configuration files.
=item the system configuration extracted from the configuration file.
=item the user configuration extracted from the configuration file.
=item the project configuration extracted from the configuration file.
=item an object on which the subroutine C<onTranslatorPanelStateChanged()> is available.
=back
=cut
sub new(\%\%\%\%;$) : method {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = bless( $class->SUPER::new(
2, #rows
2, #columns
FALSE), #non uniform
$class ) ;
die("The first parameter of AutoLaTeX::GUI::Gtk::TranslatorPanel::new() should be a hastable\nIf you pass a %v variable, please use the \%v syntax instead.\n")
unless ((!defined($_[0]))||(isHash($_[0])));
$self->attr('CONFIGURATIONS','FULL') = $_[0];
die("The second parameter of AutoLaTeX::GUI::Gtk::TranslatorPanel::new() should be a hastable\nIf you pass a %v variable, please use the \%v syntax instead.\n")
unless ((!defined($_[1]))||(isHash($_[1])));
$self->attr('CONFIGURATIONS','SYSTEM') = $_[1];
die("The third parameter of AutoLaTeX::GUI::Gtk::TranslatorPanel::new() should be a hastable\nIf you pass a %v variable, please use the \%v syntax instead.\n")
unless ((!defined($_[2]))||(isHash($_[2])));
$self->attr('CONFIGURATIONS','USER') = $_[2];
die("The forth parameter of AutoLaTeX::GUI::Gtk::TranslatorPanel::new() should be a hastable\nIf you pass a %v variable, please use the \%v syntax instead.\n")
unless ((!defined($_[3]))||(isHash($_[3])));
$self->attr('CONFIGURATIONS','PROJECT') = $_[3];
$self->attr('PANEL_LISTENERS') = [ $_[4] ] if ($_[4]);
# Initialization
$self->initializeTranslatorPanel();
return $self;
}
=pod
=item * initControls()
Initializing the controls.
=cut
sub initControls() : method {
my $self = shift;
# Label
my $label = Gtk2::Label->new(_T("List of available translators:\n(click on the three left columns to change the loading state of the translators)"));
$self->attach ($label,
0,1,0,1, # left, right, top and bottom columns
'expand','shrink', # x and y options
0,0); # horizontal and vertical paddings
# Translator list and associated text
my $translatorList = Gtk2::SimpleList->new (
'system', 'pixbuf',
'user', 'pixbuf',
'project', 'pixbuf',
'name', 'text',
'description', 'text');
$translatorList->set_headers_clickable (FALSE);
$translatorList->set_headers_visible (FALSE);
$self->attr('TRANSLATOR_LIST') = $translatorList;
my $translatorListScroll = Gtk2::ScrolledWindow-> new();
$translatorListScroll->add ($translatorList);
$translatorListScroll->set_size_request (500,400);
$translatorListScroll->set_policy ('automatic','automatic');
$translatorListScroll->set_shadow_type ('in');
$self->attach ($translatorListScroll,
0,1,1,2, # left, right, top and bottom columns
['fill','expand'],['fill','expand'], # x and y options
5,5); # horizontal and vertical paddings
# Management buttons
my $buttonAlignment = Gtk2::VButtonBox->new ();
$buttonAlignment->set_layout_default('start');
$self->attach ($buttonAlignment,
1,2,0,2, # left, right, top and bottom columns
'shrink',['fill','expand'], # x and y options
5,5); # horizontal and vertical paddings
my $addButton = Gtk2::Button->new_from_stock ('gtk-add');
$self->connectSignal($addButton,'clicked','onTranslatorAdded');
$addButton->set_sensitive( FALSE ) ;
$self->attr('BUTTONS','addTranslator') = $addButton;
$buttonAlignment->add ($addButton);
my $editButton = Gtk2::Button->new_from_stock ('gtk-edit');
$self->connectSignal($editButton,'clicked','onTranslatorEdited');
$editButton->set_sensitive( FALSE ) ;
$self->attr('BUTTONS','editTranslator') = $editButton;
$buttonAlignment->add ($editButton);
my $deleteButton = Gtk2::Button->new_from_stock ('gtk-delete');
$self->connectSignal($deleteButton,'clicked','onTranslatorDeleted');
$deleteButton->set_sensitive( FALSE ) ;
$self->attr('BUTTONS','deleteTranslator') = $deleteButton;
$buttonAlignment->add ($deleteButton);
# Help
$buttonAlignment->add (Gtk2::HSeparator->new ());
$buttonAlignment->add ($self->makeLegend($self->getLevelIcon('system'), _T('All users')));
$buttonAlignment->add ($self->makeLegend($self->getLevelIcon('user'), _T('Current usr')));
$buttonAlignment->add ($self->makeLegend($self->getLevelIcon('project'), _T('Current project')));
$buttonAlignment->add (Gtk2::HSeparator->new ());
$buttonAlignment->add ($self->makeLegend($self->getLevelIcon('system'), _T('Loaded, no conflict')));
$buttonAlignment->add ($self->makeLegend($self->getConflictLevelIcon('system'), _T('Loaded, conflict')));
$buttonAlignment->add ($self->makeLegend($self->getRedLevelIcon('system'), _T('Not loaded')));
$buttonAlignment->add ($self->makeLegend($self->getGrayedLevelIcon('system'), _T('Unspecified, no conflict')));
$buttonAlignment->add ($self->makeLegend($self->getGrayedConflictLevelIcon('system'), _T('Unspecified, conflict')));
}
sub makeLegend($$;$) {
my $self = shift;
my $icon = shift;
my $textlabel = shift;
my $topPadding = shift || 0;
my $legendAlignment = Gtk2::Table->new (5,2);
my $iconLabel = Gtk2::Image->new_from_pixbuf($icon);
$legendAlignment->attach ($iconLabel, 0, 1, 0, 1, 'fill', 'fill', 0, $topPadding);
my $text = Gtk2::Label->new ($textlabel);
$legendAlignment->attach ($text, 1, 2, 0, 1, 'fill', 'fill', 5, $topPadding);
return $legendAlignment;
}
=pod
=item * initializeTranslatorPanel()
Initializing the panel content before displaying.
This method read the list of the translators and
fill the attribute C<{'DATA'}{'translators'}>.
=cut
sub initializeTranslatorPanel() : method {
my $self = shift;
$self->SUPER::initializeTranslatorPanel();
$self->initControls();
$self->detectTranslatorConflicts();
$self->fillTranslatorList();
$self->notifyListeners();
$self->connectGtkSignals();
}
=pod
=item * getInclusionStateIcon($$)
Replies the icon that corresponds to the inclusion state of a translator at the specified level.
=over 4
=item level
=item translator name
=cut
sub getInclusionStateIcon($$) : method {
my $self = shift;
my $level = shift;
my $name = shift;
# Does the translator has a conflict?
my $conflict = $self->hasTranslatorConflict($level,$name);
if ($self->hasattr('TRANSLATORS',"$name",'included',"$level")) {
my $val = $self->attr('TRANSLATORS',"$name",'included',"$level");
if (defined($val)) {
if ($val) {
return $self->getConflictLevelIcon($level) if ($conflict);
return $self->getLevelIcon($level);
}
else {
return $self->getIcon("warning.png") if ($conflict);
return $self->getRedLevelIcon($level);
}
}
}
return $self->getGrayedConflictLevelIcon($level) if ($conflict);
return $self->getGrayedLevelIcon($level);
}
=pod
=item * detectTranslatorConflicts()
Detect translator conflicts.
=cut
sub detectTranslatorConflicts() : method {
my $self = shift;
my %conflicts = detectConflicts(%{$self->attr('TRANSLATORS')});
# Parse conflict data structure to extract the names of the
# translators under conflict
foreach my $k (keys %conflicts) {
my %trs = ();
foreach my $source (keys %{$conflicts{$k}}) {
foreach my $t (keys %{$conflicts{$k}{$source}}) {
$trs{$t} = 1;
}
}
my @trs = keys %trs;
$conflicts{$k} = \@trs;
}
if (!$self->hasProject()) {
delete $conflicts{'project'};
}
$self->attr('TRANSLATOR_CONFLICTS') = \%conflicts;
}
=pod
=item * hasTranslatorConflict()
Replies if a conflict exists for the specified translator and level.
=over 4
=item level
=item translator name
=back
=cut
sub hasTranslatorConflict($$) : method {
my $self = shift;
my $level = shift;
my $name = shift;
if (($self->hasattr('TRANSLATOR_CONFLICTS',"$level"))&&
(isArray($self->attr('TRANSLATOR_CONFLICTS',"$level")))) {
return arrayContains(@{$self->attr('TRANSLATOR_CONFLICTS',"$level")},$name);
}
return 0;
}
=pod
=item * isUnderConflict()
Replies if a conflict exists.
=cut
sub isUnderConflict() : method {
my $self = shift;
if ($self->hasattr('TRANSLATOR_CONFLICTS')) {
my $conflicts = $self->attr('TRANSLATOR_CONFLICTS');
my $lastlevel = $self->hasProject() ? $ALL_LEVELS[$#ALL_LEVELS] : $ALL_LEVELS[$#ALL_LEVELS-1];
return 1 if ((isArray($conflicts->{"$lastlevel"}))&&
(@{$conflicts->{"$lastlevel"}}));
}
return 0;
}
=pod
=item * fillTranslatorList()
Fill the list of translators.
=cut
sub fillTranslatorList() : method {
my $self = shift;
my @keys = sort keys %{$self->attr('TRANSLATORS')};
@{$self->attr('TRANSLATOR_LIST')->{'data'}} = ();
my ($systemIcon, $userIcon, $projectIcon);
foreach my $trans (@keys) {
$systemIcon = $self->getInclusionStateIcon('system',$trans);
$userIcon = $self->getInclusionStateIcon('user',$trans);
$projectIcon = $self->hasProject() ? $self->getInclusionStateIcon('project',$trans) : undef;
push @{$self->attr('TRANSLATOR_LIST')->{'data'}},
[
$systemIcon,
$userIcon,
$projectIcon,
$trans,
$self->attr('TRANSLATORS',"$trans",'human-readable'),
];
}
}
=pod
=item * connectGtkSignals()
Differed connection of GTK signals to widgets.
=cut
sub connectGtkSignals() : method {
my $self = shift;
$self->connectSignal($self->attr('TRANSLATOR_LIST'),'row-activated','onTranslatorEdit');
$self->connectSignal($self->attr('TRANSLATOR_LIST'),'button-press-event','onTranslatorClick');
$self->connectSignal($self->attr('TRANSLATOR_LIST'),'cursor-changed','onTranslatorSelected');
}
=pod
=item * getTranslatorDataAt($)
Replies the data associated to the translator at the specified index
=cut
sub getTranslatorDataAt($) : method {
my $self = shift;
my $row = $self->attr('TRANSLATOR_LIST')->{'data'}[$_[0]];
$self->attr('TRANSLATORS',$row->[3]);
}
=pod
=item * updateIcons($$)
Update the icons for the translator that support the given source
extension from the specified level.
=over 4
=item the level for which the update must be done.
=item the source extension that is the source of this update.
=back
=cut
sub updateIcons($$) : method {
my $self = shift;
my $level = shift;
my $source = shift;
my $levelIndex = arrayIndexOf(@ALL_LEVELS,$level);
for(my $column=$levelIndex; $column<@ALL_LEVELS; $column++) {
my $clevel = $ALL_LEVELS[$column];
if (($clevel ne 'project')||($self->hasProject())) {
for(my $idxTrans=0; $idxTrans<@{$self->attr('TRANSLATOR_LIST')->{'data'}}; $idxTrans++) {
my $transData = $self->getTranslatorDataAt($idxTrans);
if ($source eq $transData->{'full-source'}) {
my $icon = $self->getInclusionStateIcon($clevel,$transData->{'name'});
$self->attr('TRANSLATOR_LIST')->{'data'}[$idxTrans][$column] = $icon;
}
}
}
}
}
=pod
=item * notifyListeners()
Notify listeners about a change of the stateof this panel.
=cut
sub notifyListeners() : method {
my $self = shift;
my $conflict = $self->isUnderConflict();
if (($self->hasattr('PANEL_LISTENERS'))&&(isArray($self->attr('PANEL_LISTENERS')))) {
foreach my $listener (@{$self->attr('PANEL_LISTENERS')}) {
if ($listener->can('onTranslatorPanelStateChanged')) {
$listener->onTranslatorPanelStateChanged($self,$conflict);
}
}
}
}
#
#------------------------------------- SIGNALS
#
sub onTranslatorSelected(@) : method {
my $self = shift;
my $canWrite = 0;
#my @sel = $self->attr('TRANSLATOR_LIST')->get_selected_indices ();
#if (@sel) {
# my $sel = pop @sel;
# if ($sel>=0) {
# my $data = $self->getTranslatorDataAt($sel);
# if ($data) {
# $canWrite = ((($data->{'level'} eq 'system')&&($self->isAdminUser()))||
# ($data->{'level'} eq 'user')||
# (($data->{'level'} eq 'project')&&($self->hasProject())));
# }
# }
#}
$self->attr('BUTTONS','editTranslator')->set_sensitive ($canWrite);
$self->attr('BUTTONS','deleteTranslator')->set_sensitive ($canWrite);
}
sub onTranslatorClick(@) : method {
my $self = shift;
my $event = $_[1]; #Gtk2::Gdk::Event
my ($x,$y) = $event->get_coords ();
my ($path,$column,$cell_x,$cell_y) = $_[0]->get_path_at_pos ($x,$y);
if ($path) {
my @indices = $path->get_indices ();
my $title = $column->get_title ();
if ((($title eq 'system')&&($self->isAdminUser()))||
($title eq 'user')||
(($title eq 'project')&&($self->hasProject()))) {
my $index = pop @indices;
my $data = $self->getTranslatorDataAt($index);
if (!defined($data->{'included'}{"$title"})) {
$data->{'included'}{"$title"} = 1;
}
elsif ($data->{'included'}{"$title"}) {
$data->{'included'}{"$title"} = 0;
}
else {
$data->{'included'}{"$title"} = undef;
}
$self->detectTranslatorConflicts();
# Update the icons
$self->updateIcons($title,$data->{'full-source'});
# Notify listeners
$self->notifyListeners();
}
}
}
sub onTranslatorAdded(@) : method {
my $self = shift;
}
sub onTranslatorDeleted(@) : method {
my $self = shift;
#
#my @sel = $self->attr('TRANSLATOR_LIST')->get_selected_indices ();
#if (@sel) {
# foreach my $sel (@sel) {
# my $data = $self->getTranslatorDataAt($sel);
# if ($data) {
# my $canWrite = ((($data->{'level'} eq 'system')&&($self->isAdminUser()))||
# ($data->{'level'} eq 'user')||
# (($data->{'level'} eq 'project')&&($self->hasProject())));
# }
# }
#}
}
1;
__END__
=back
=head1 COPYRIGHT
(c) Copyright 2007-13 Stephane Galland E<lt>
[email protected]<gt>, under GPL.
=head1 AUTHORS
=over
=item *
Conceived and initially developed by Stéphane Galland E<lt>
[email protected]<gt>.
=back
=head1 SEE ALSO
L<autolatex>