# vim: set et ts=4 sw=4:
package ManaTools::MainDisplay;
#============================================================= -*-perl-*-
=head1 NAME
ManaTools::MainDisplay - class for ManaTools main window
=head1 SYNOPSIS
$mainDisplay = new ManaTools::MainDisplay();
$mainDisplay->start();
$mainDisplay->cleanup();
=head1 METHODS
=head1 DESCRIPTION
ManaTools::MainDisplay implements the main window panel adding buttons
reading the configuration for every categories and modules
=head1 SUPPORT
You can find documentation for this module with the perldoc command:
perldoc ManaTools::MainDisplay
=head1 AUTHOR
Steven Tucker
=head1 COPYRIGHT and LICENSE
Copyright (C) 2012-2015, Angelo Naselli.
Copyright (C) 2012, Steven Tucker.
ManaTools 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.
ManaTools 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 ManaTools. If not, see .
=head1 METHODS
=cut
use Moose;
extends qw( ManaTools::Module );
use I18N::LangTags::Detect;
use diagnostics;
use ManaTools::SettingsReader;
use ManaTools::ConfigReader;
use ManaTools::Category;
use ManaTools::Module;
use ManaTools::Shared;
use ManaTools::Shared::GUI;
use ManaTools::Shared::GUI::Dialog;
use ManaTools::Shared::Locales;
use File::ShareDir ':ALL';
use yui;
with 'ManaTools::LoggingRole';
has 'configDir' => (
is => 'ro',
isa => 'Str',
);
with 'ManaTools::ConfigDirRole';
#=============================================================
=head2 new
=head3 INPUT
hash ref containing
configDir: configuration files directory
name: application name, logging identity,
configuration subdirectory
=head3 other attributes
title: window title got from configuration file,
default is name.
categories: ArrayRef[ManaTools::Category]
settings: HashRef containing settings file content
currCategory: Selected category
mainWin: Main Dialog window
factory: yui::YUI::widgetFactory
menus: HashRef containing menu items
leftPane: left panel layout
rightPane: right panel layout
rightPaneFrame: right frame (needed for category title)
replacePoint: replace point where to set new layout on
category selection
selectedModule: module to be returned when selected
=head3 DESCRIPTION
This method instanziates the MainWindo object, and setups
the startup GUI.
=cut
#=============================================================
has '+name' => (
is => 'ro',
isa => 'Str',
default => 'mpan',
);
has '+icon' => (
is => 'rw',
isa => 'Str',
default => File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/mageia.png'),
);
has 'title' => (
is => 'rw',
isa => 'Str',
init_arg => undef,
lazy => 1,
builder => '_titleInitialize',
);
sub _titleInitialize {
my $self = shift;
return $self->name();
}
has 'settings' => (
is => 'rw',
isa => 'HashRef',
init_arg => undef,
default => sub {return {};},
);
has 'categories' => (
is => 'rw',
isa => 'ArrayRef[ManaTools::Category]',
init_arg => undef,
lazy => 1,
default => sub {[];},
);
has 'currCategory' => (
is => 'rw',
isa => 'Maybe[ManaTools::Category]',
init_arg => undef,
default => undef,
);
has 'factory' => (
is => 'ro',
isa => 'Maybe[yui::YWidgetFactory]',
lazy => 1,
init_arg => undef,
default => sub {
return yui::YUI::widgetFactory;
},
);
has 'mainWin' => (
is => 'rw',
isa => 'Maybe[ManaTools::Shared::GUI::Dialog]',
init_arg => undef,
default => undef,
);
has 'menus' => (
is => 'rw',
isa => 'HashRef',
init_arg => undef,
default => sub {
{
file => {},
help => {},
};
},
);
has 'leftPane' => (
is => 'rw',
isa => 'Maybe[yui::YLayoutBox]',
init_arg => undef,
default => undef,
);
has 'rightPane' => (
is => 'rw',
isa => 'Maybe[yui::YLayoutBox]',
init_arg => undef,
default => undef,
);
has 'rightPaneFrame' => (
is => 'rw',
isa => 'Maybe[yui::YFrame]',
init_arg => undef,
default => undef,
);
has 'replacePoint' => (
is => 'rw',
isa => 'Maybe[yui::YReplacePoint]',
init_arg => undef,
default => undef,
);
has 'selectedModule' => (
is => 'rw',
isa => 'Maybe[ManaTools::Module]',
init_arg => undef,
default => undef,
);
#=============================================================
=head2 configName
=head3 INPUT
$self: this object
=head3 OUTPUT
name: application name
=head3 DESCRIPTION
Returns the application name as configuration subdirectory.
This method is required by ConfifDirRole
=cut
#=============================================================
sub configName {
my $self = shift;
return $self->name();
}
#=============================================================
=head2 identifier
=head3 INPUT
$self: this object
=head3 OUTPUT
name: application name
=head3 DESCRIPTION
Returns the application name as logging identifier.
This method is required by LoggingRole
=cut
#=============================================================
sub identifier {
my $self = shift;
return $self->name();
}
sub _showAboutDialog {
my $self = shift;
my $translators = $self->{loc}->N("_: Translator(s) name(s) & email(s)\n");
$translators =~ s/\\<\;/g;
$translators =~ s/\>/\>\;/g;
my $sh_gui = ManaTools::Shared::GUI->new();
$sh_gui->AboutDialog({ name => $self->{name},
version => $self->Version(),
credits => $self->loc()->N("Copyright (C) %s Mageia community", '2013-2015'),
license => $self->loc()->N("GPLv2"),
description => $self->loc()->N("mpan is the ManaTools panel that collects all the utilities."),
authors => $self->loc()->N("
Developers
Translators
",
"Angelo Naselli <anaselli\@linux.it>",
"Matteo Pasotti <matteo.pasotti\@gmail.com>",
$translators
),
});
}
## Begin the program event loop
=head2 start
contains the main loop of the application
where we can check for events
=cut
sub start {
my $self = shift;
$self->_setupGui();
return $self->selectedModule();
}
#=============================================================
=head2 cleanup
=head3 INPUT
$self: this object
=head3 DESCRIPTION
This method cleanup data for a further start.
=cut
#=============================================================
sub cleanup {
my $self = shift;
$self->mainWin(undef);
$self->menus({
file => {},
help => {},
});
for (my $cat=0; $cat < scalar(@{$self->categories()}); $cat++ ) {
my $catSel = @{$self->categories()}[$cat];
$catSel->button(undef);
$catSel->removeButtons();
}
$self->leftPane(undef);
$self->rightPane(undef);
$self->rightPaneFrame(undef);
$self->replacePoint(undef);
}
#=============================================================
=head2 _setupGui
=head3 INPUT
$self: this object
=head3 DESCRIPTION
This method load configuration and build the GUI layout.
=cut
#=============================================================
sub _setupGui {
my $self = shift;
$self->selectedModule(undef);
# fill $self->settings from settings.conf
$self->_loadSettings();
$DB::single = 1;
$self->title($self->settings()->{title});
$self->icon($self->settings()->{icon}) if $self->settings()->{icon};
my $dialog = ManaTools::Shared::GUI::Dialog->new(
module => $self,
dialogType => ManaTools::Shared::GUI::Dialog::mainDialog,
title => $self->title(),
icon => $self->icon,
buttons => {
ManaTools::Shared::GUI::Dialog::aboutButton => sub {
my $event = shift; ## ManaTools::Shared::GUI::Event
my $self = $event->parentDialog()->module(); #this object
$self->_showAboutDialog();
return 1;
},
ManaTools::Shared::GUI::Dialog::closeButton => sub {return 0;},
},
layout => sub {
my $self = shift;
my $layoutstart = shift;
my $ydialog = $self->dialog();
my $module = $self->module();
my $info = $self->info();
my $factory = $self->factory();
my $optFactory = $self->optFactory();
my $mainLayout = $factory->createVBox($layoutstart);
my $menuLayout = $factory->createHBox($mainLayout);
## Menu File
my $align = $factory->createAlignment($menuLayout, 1, 0);
$module->menus()->{file} = {
widget => $factory->createMenuButton($align, $self->loc()->N("File")),
quit => new yui::YMenuItem($self->loc()->N("&Quit")),
};
my @ordered_menu_lines = qw(quit);
foreach (@ordered_menu_lines) {
$module->menus()->{file}->{widget}->addItem($module->menus()->{file}->{ $_ });
}
$module->menus()->{file}->{ widget }->rebuildMenuTree();
$align = $factory->createAlignment($menuLayout, 2, 0);
$module->menus()->{help} = {
widget => $factory->createMenuButton($align, $self->loc()->N("Help")),
help => new yui::YMenuItem($self->loc()->N("Help")),
about => new yui::YMenuItem($self->loc()->N("&About")),
};
## Menu Help
@ordered_menu_lines = qw(help about);
foreach (@ordered_menu_lines) {
$module->menus()->{help}->{ widget }->addItem($module->menus()->{help}->{ $_ });
}
$module->menus()->{help}->{ widget }->rebuildMenuTree();
ManaTools::Shared::GUI::Event->new(
name => 'AboutMenuEvent',
eventHandler => $self,
eventType => $yui::YEvent::MenuEvent,
item => $module->menus()->{help}->{about},
event => sub {
my $event = shift;
my $dialog = $event->parentDialog();
my $self = $dialog->module(); #this object
$self->_showAboutDialog();
return 1;
},
);
ManaTools::Shared::GUI::Event->new(
name => 'QuitMenuEvent',
eventHandler => $self,
eventType => $yui::YEvent::MenuEvent,
item => $module->menus()->{file}->{quit},
event => sub {
return 0;
},
);
my $layout = $factory->createHBox($mainLayout);
#create left Panel Frame no need to add a label for title
my $leftPaneFrame = $factory->createFrame($layout, $module->settings()->{category_title});
#create right Panel Frame no need to add a label for title (use setLabel when module changes)
my $rightPaneFrame = $factory->createFrame($layout, "");
#create replace point for dynamically created widgets
$module->replacePoint($factory->createReplacePoint($rightPaneFrame));
$module->rightPane($factory->createVBox($module->replacePoint()));
$module->leftPane($factory->createVBox($leftPaneFrame));
$module->rightPaneFrame($rightPaneFrame);
#logo from settings
my $logofile = defined($module->settings()->{logo}) ?
$module->settings()->{logo} :
File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/logo_mageia.png'
);
my $logo = $factory->createImage($module->leftPane(), $logofile);
$logo->setAutoScale(1);
#$leftPaneFrame->setWeight(0, 1);
$rightPaneFrame->setWeight(0, 2);
$module->_loadCategories();
$factory->createVStretch($module->leftPane());
my $closeButton = $self->widget('closeButton');
my $quitIcon = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/quit.png');
$closeButton->setIcon($quitIcon);
if (!$module->currCategory()) {
$module->currCategory(@{$module->categories()}[0]);
}
$module->currCategory()->addButtons($module);
$module->rightPaneFrame()->setLabel($module->currCategory()->name());
$factory->createSpacing($module->rightPane(), 1, 1, 1.0 );
# Note that Since mpan is a Manatools::Module and creates other modules
# title and application are set into BUILD (e.g. constructor) so last
# built one is the one shown. Forcing setting again here
yui::YUI::app()->setApplicationTitle($self->title);
yui::YUI::app()->setApplicationIcon($self->icon);
return $self->widget('layout');
},
);
$self->mainWin($dialog);
return $dialog->call();
}
## internal methods
## Check if event is from current Category View
## If icon click, returns the module to be launched
sub _moduleSelected {
my ($self, $selectedWidget) = @_;
for(@{$self->currCategory()->modules()}) {
if( $_->button() == $selectedWidget ){
return $_;
}
}
return 0;
}
## Discover if a category button was selected.
## If category button is selected, sets right panel to display
## the selected Category Modules
## returns 1 if category button is selected
sub _categorySelected {
my ($self, $selectedWidget) = @_;
for (@{$self->categories()}) {
if( $_->button() == $selectedWidget ) {
#if current is already set then skips
if ($self->currCategory() == $_) {
## returns 1 to skip any other checks on
## the selected widget
return 1;
}
## Menu item selected, set right pane
my $ydialog = $self->mainWin()->dialog();
$ydialog->startMultipleChanges();
## NOTE widget and item events (Shared::GUI:Event) created in the
## previous replacePoint MUST be cleaned up
my $currCategory = $self->currCategory();
## Change Current Category to the selected one
$self->currCategory($_);
foreach my $mod (@{$currCategory->modules()}) {
$self->mainWin()->delWidget(
$self->mainWin()->widget($mod->name())
);
}
## Remove existing modules
$self->replacePoint()->deleteChildren();
$self->rightPane($self->factory()->createVBox($self->replacePoint()));
## Add new Module Buttons to Right Pane
$self->currCategory()->addButtons($self);
$self->rightPaneFrame()->setLabel($self->currCategory()->name());
$self->factory()->createSpacing($self->rightPane(), 1, 1, 1.0 );
$self->replacePoint()->showChild();
$ydialog->recalcLayout();
$ydialog->doneMultipleChanges();
return 1;
}
}
return 0;
}
# return the localized string from a hash and given the key
# by default en value
sub _localizedValue {
my ($self, $hash, $key) = @_;
return if !defined($hash->{$key});
if (ref($hash->{$key}) ne "HASH") {
$self->logger()->W($self->loc()->N("Bad configuration file, %s has not xml:lang attribute, guessing it is a string", $key));
# Force array is set for "title"
return $hash->{$key}[0];
}
my @lang = I18N::LangTags::Detect::detect();
# Adding default value as English (en)
push @lang, 'en';
foreach my $l ( @lang ) {
return $hash->{$key}->{$l} if defined($hash->{$key}->{$l});
}
return;
}
## mpan settings
sub _loadSettings {
my ($self, $force_load) = @_;
# configuration file name
my $fileName = $self->configPathName() . "/settings.conf";
if (! -e $fileName) {
my $err = $self->loc()->N("Configuration file %s is missing", $fileName);
$self->logger()->E($err);
die $err;
}
$self->logger()->I($self->loc()->N("Reading configuration file %s", $fileName));
if (! scalar %{$self->settings()} || $force_load) {
my $settingsReader = ManaTools::SettingsReader->new({fileName => $fileName});
my $settings;
my @lang = I18N::LangTags::Detect::detect();
my $read = $settingsReader->settings();
foreach (keys %{$read}) {
my $key = $_;
# localized strings
if ($key eq "title" or $key eq "category_title") {
# default is en
$settings->{$key} = $self->_localizedValue(
$read,
$key
);
$self->logger()->I($self->loc()->N("Load settings: %s content is <<%s>>", $key, $settings->{$key}));
}
elsif (($key eq "icon" || $key eq "logo") && (substr( $read->{$key}, 0, 1) ne '/')) {
# icon with relative path?
$settings->{$key} = File::ShareDir::dist_file(ManaTools::Shared::distName(), $read->{$key});
}
else {
$settings->{$key} = $read->{$key};
}
}
$self->settings($settings);
}
}
#=============================================================
# _categoryLoaded
#
# INPUT
#
# $self: this object
# $category: category to look for
#
# OUTPUT
#
# $present: category is present or not
#
# DESCRIPTION
#
# This method looks for the given category and if already in
# returns true.
#
#=============================================================
sub _categoryLoaded {
my ($self, $category) = @_;
my $present = 0;
if (!$category) {
return $present;
}
foreach my $cat (@{$self->categories()}) {
if ($cat->name() eq $category->name()) {
$present = 1;
last;
}
}
return $present;
}
#=============================================================
# _getCategory
#
# INPUT
#
# $self: this object
# $name: category name
#
# OUTPUT
#
# $category: category object if exists
#
# DESCRIPTION
#
# This method looks for the given category name and returns
# the realte object.
#=============================================================
sub _getCategory {
my ($self, $name) = @_;
my $category = undef;
foreach $category (@{$self->categories()}) {
if ($category->name() eq $name) {
return $category;
}
}
return $category;
}
# _loadCategory
#
# creates a new button representing a category
#
sub _loadCategory {
my ($self, $category) = @_;
if (!$self->_categoryLoaded($category)) {
push ( @{$self->categories()}, $category );
my $cat = @{$self->categories()}[-1];
$cat->button(
$self->factory()->createPushButton(
$self->leftPane(),
$cat->name()
)
);
$cat->setIcon();
$cat->button()->setStretchable(0, 1);
$self->mainWin()->addWidget(
$cat->name(),
$cat->button(), sub {
my $event = shift; ## ManaTools::Shared::GUI::Event
my $self = $event->parentDialog()->module(); #this object
$self->_categorySelected($event->widget());
return 1;
}
);
}
else {
for (my $cat=0; $cat < scalar(@{$self->categories()}); $cat++ ) {
my $catSelected = @{$self->categories()}[$cat];
if( $catSelected->name() eq $category->name() &&
!$catSelected->button()) {
$catSelected->button(
$self->factory()->createPushButton(
$self->leftPane(),
$catSelected->name()
)
);
$catSelected->setIcon();
$catSelected->button()->setStretchable(0, 1);
$self->mainWin()->addWidget(
$catSelected->name(),
$catSelected->button(), sub {
my $event = shift; ## ManaTools::Shared::GUI::Event
my $self = $event->parentDialog()->module(); #this object
$self->_categorySelected($event->widget());
return 1;
}
);
last;
}
}
}
}
sub _loadCategories {
my $self = shift;
# category files
my @categoryFiles;
my $fileName = $self->configPathName() . "/categories.conf";
# configuration file dir
my $directory = $self->configPathName() . "/categories.conf.d";
push(@categoryFiles, $fileName);
push(@categoryFiles, <$directory/*.conf>);
my $currCategory;
foreach $fileName (@categoryFiles) {
$self->logger()->I($self->loc()->N("Parsing category file %s", $fileName));
my $inFile = new ManaTools::ConfigReader({fileName => $fileName});
my $tmpCat;
my $tmp;
my $hasNextCat = $inFile->hasNextCat();
while( $hasNextCat ) {
$tmp = $inFile->getNextCat();
my $title = $self->_localizedValue(
$tmp,
'title'
);
$self->logger()->D($self->loc()->N("Load categories: title content is <<%s>>", $title));
my $icon = $tmp->{icon};
if ((substr( $icon, 0, 1) ne '/')) {
# icon with relative path?
$icon = File::ShareDir::dist_file(ManaTools::Shared::distName(), $tmp->{icon});
}
$tmpCat = $self->_getCategory($title);
if (!$tmpCat) {
$tmpCat = new ManaTools::Category({
name => $title,
icon => $icon,
});
}
$self->_loadCategory($tmpCat);
$hasNextCat = $inFile->hasNextCat();
$currCategory = $tmpCat;
my $hasNextMod = $inFile->hasNextMod();
while( $hasNextMod ) {
$tmp = $inFile->getNextMod();
my $tmpMod;
my $loaded = 0;
if (exists $tmp->{title}) {
my $title = $self->_localizedValue(
$tmp,
'title'
);
my $icon = $tmp->{icon};
if ((substr( $icon, 0, 1) ne '/')) {
# icon with relative path?
$icon = File::ShareDir::dist_file(ManaTools::Shared::distName(), $tmp->{icon});
}
$self->logger()->D($self->loc()->N("Load categories: module title is <<%s>>", $title));
if (not $currCategory->moduleLoaded($title)) {
$tmpMod = ManaTools::Module->create(
name => $title,
icon => $icon,
launch => $tmp->{launcher}
);
}
}
elsif (exists $tmp->{class}) {
if (not $currCategory->moduleLoaded(-CLASS => $tmp->{class})) {
$tmpMod = ManaTools::Module->create(-CLASS => $tmp->{class});
}
}
if ($tmpMod) {
$loaded = $currCategory->loadModule($tmpMod);
undef $tmpMod if !$loaded;
}
$hasNextMod = $inFile->hasNextMod();
}
}
}
}
no Moose;
1;