# vim: set et ts=4 sw=4:
# Copyright 2012 Steven Tucker
#
# This file is part of AdminPanel
#
# AdminPanel 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.
#
# AdminPanel 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 AdminPanel. If not, see .
package AdminPanel::MainDisplay;
#============================================================= -*-perl-*-
=head1 NAME
AdminPanel::MainDisplay - class for AdminPaneol main window
=head1 SYNOPSIS
$mainDisplay = new AdminPanel::MainDisplay();
$mainDisplay->start();
$mainDisplay->destroy();
=head1 METHODS
=head1 DESCRIPTION
Long_description
=head1 EXPORT
exported
=head1 SUPPORT
You can find documentation for this module with the perldoc command:
perldoc AdminPanel::MainDisplay
=head1 SEE ALSO
SEE_ALSO
=head1 AUTHOR
Steven Tucker
=head1 COPYRIGHT and LICENSE
Copyright (C) 2012, Steven Tucker
Copyright (C) 2014, Angelo Naselli.
AdminPanel 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.
AdminPanel 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 AdminPanel. If not, see .
=head1 FUNCTIONS
=cut
=head1 VERSION
Version 0.01
=cut
our $VERSION = '1.0.0';
use strict;
use warnings;
use diagnostics;
use AdminPanel::SettingsReader;
use AdminPanel::ConfigReader;
use AdminPanel::Category;
use AdminPanel::Module;
use AdminPanel::Shared;
use AdminPanel::Shared::GUI;
use AdminPanel::Shared::Locales;
use File::ShareDir ':ALL';
use yui;
#=============================================================
=head2 new
=head3 DESCRIPTION
This method instanziates the MainWindo object, and setups
the startup GUI.
=cut
#=============================================================
sub new {
my $self = {
categories => 0,
event => 0,
factory => 0,
mainWin => 0,
mainLayout => 0,
menuLayout => 0,
menus => {
file => 0,
help => 0
},
layout => 0,
leftPane => 0,
rightPane => 0,
currCategory => 0,
confDir => 0,
title => 0,
settings => 0,
exitButton => 0,
loc => 0,
replacePoint => 0,
};
bless $self, 'AdminPanel::MainDisplay';
## Default values
$self->{name} = "Administration panel";
$self->{categories} = [];
$self->{confDir} = "/etc/mpan",
$self->{title} = "mpan",
my $cmdline = new yui::YCommandLine;
## TODO add parameter check
my $pos = $cmdline->find("--name");
if ($pos > 0)
{
$self->{title} = $cmdline->arg($pos+1);
}
$pos = $cmdline->find("--conf_dir");
if ($pos > 0)
{
$self->{confDir} = $cmdline->arg($pos+1);
}
else
{
$self->{confDir} = "/etc/$self->{title}";
}
# TODO localize
$self->{loc} = AdminPanel::Shared::Locales->new(domain_name => 'libDrakX-standalone');
$self->setupGui();
return $self;
}
## Begin the program event loop
sub start {
my ($self) = shift;
my $reqExit = 0;
##Default category selection
if (!$self->{currCategory}) {
$self->{currCategory} = @{$self->{categories}}[0];
}
$self->{currCategory}->addButtons($self->{rightPane}, $self->{factory});
$self->{rightPaneFrame}->setLabel($self->{currCategory}->{name});
$self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 );
my $launch = 0;
while(!$launch) {
## Grab Event
$self->{event} = $self->{mainWin}->waitForEvent();
my $eventType = $self->{event}->eventType();
## Check for window close
if ($eventType == $yui::YEvent::CancelEvent) {
last;
}
elsif ($eventType == $yui::YEvent::MenuEvent) {
### MENU ###
my $item = $self->{event}->item();
my $menuLabel = $item->label();
if ($menuLabel eq $self->{menus}->{file}->{ quit }->label()) {
## quit menu item
last;
}
elsif ($menuLabel eq $self->{menus}->{help}->{ about }->label()) {
my $translators = $self->{loc}->N("_: Translator(s) name(s) & email(s)\n");
$translators =~ s/\\<\;/g;
$translators =~ s/\>/\>\;/g;
my $sh_gui = AdminPanel::Shared::GUI->new();
$sh_gui->AboutDialog({ name => $self->{name},
version => $AdminPanel::MainDisplay::VERSION,
credits => $self->{loc}->N("Copyright (C) %s Mageia community", '2013-2014'),
license => $self->{loc}->N("GPLv2"),
description => $self->{loc}->N("mpan is the administration panel that collects all the utilities."),
authors => $self->{loc}->N("
Developers
Translators
",
"Angelo Naselli <anaselli\@linux.it>",
"Matteo Pasotti <matteo.pasotti\@gmail.com>",
$translators
),
});
}
elsif ($menuLabel eq $self->{menus}->{help}->{ help }->label()) {
# TODO Help
}
}
elsif ($eventType == $yui::YEvent::WidgetEvent) {
my $widget = $self->{event}->widget();
## Check for Exit button push or menu
if($widget == $self->{exitButton}) {
last;
}
else {
# category button selected?
my $isCat = $self->_categorySelected($widget);
if (!$isCat) {
# module button selected?
$launch = $self->_moduleSelected($widget);
}
}
}
}
return $launch;
}
#=============================================================
=head2 destroy
=head3 INPUT
$self: this object
=head3 DESCRIPTION
This method destroyes the main window and all the
relevanto bojects (category and modules buttons).
=cut
#=============================================================
sub destroy {
my ($self) = shift;
$self->{mainWin}->destroy();
for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) {
@{$self->{categories}}[$cat]->{button} = 0;
@{$self->{categories}}[$cat]->removeButtons();
}
}
#=============================================================
=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->_loadSettings();
yui::YUILog::setLogFileName($self->{settings}->{log}) if defined($self->{settings}->{log});
$self->{name} = $self->{settings}->{title};
yui::YUI::app()->setApplicationTitle($self->{name});
my $icon = defined($self->{settings}->{icon}) ?
$self->{settings}->{icon} :
File::ShareDir::dist_file(AdminPanel::Shared::distName(), 'images/mageia.png');
yui::YUI::app()->setApplicationIcon($icon);
$self->{factory} = yui::YUI::widgetFactory;
$self->{mainWin} = $self->{factory}->createMainDialog;
$self->{mainLayout} = $self->{factory}->createVBox($self->{mainWin});
$self->{menuLayout} = $self->{factory}->createHBox($self->{mainLayout});
## Menu File
my $align = $self->{factory}->createAlignment($self->{menuLayout}, 1, 0);
$self->{menus}->{file} = {
widget => $self->{factory}->createMenuButton($align, $self->{loc}->N("File")),
quit => new yui::YMenuItem($self->{loc}->N("&Exit")),
};
my @ordered_menu_lines = qw(quit);
foreach (@ordered_menu_lines) {
$self->{menus}->{file}->{ widget }->addItem($self->{menus}->{file}->{ $_ });
}
$self->{menus}->{file}->{ widget }->rebuildMenuTree();
$align = $self->{factory}->createAlignment($self->{menuLayout}, 2, 0);
$self->{menus}->{help} = {
widget => $self->{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) {
$self->{menus}->{help}->{ widget }->addItem($self->{menus}->{help}->{ $_ });
}
$self->{menus}->{help}->{ widget }->rebuildMenuTree();
$self->{layout} = $self->{factory}->createHBox($self->{mainLayout});
#create left Panel Frame no need to add a label for title
$self->{leftPaneFrame} = $self->{factory}->createFrame($self->{layout}, $self->{settings}->{category_title});
#create right Panel Frame no need to add a label for title (use setLabel when module changes)
$self->{rightPaneFrame} = $self->{factory}->createFrame($self->{layout}, "");
#create replace point for dynamically created widgets
$self->{replacePoint} = $self->{factory}->createReplacePoint($self->{rightPaneFrame});
$self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint});
$self->{leftPane} = $self->{factory}->createVBox($self->{leftPaneFrame});
#logo from settings
my $logofile = defined($self->{settings}->{logo}) ?
$self->{settings}->{logo} :
File::ShareDir::dist_file(AdminPanel::Shared::distName(), 'images/logo_mageia.png');
my $logo = $self->{factory}->createImage($self->{leftPane}, $logofile);
$logo->setAutoScale(1);
# $self->{leftPaneFrame}->setWeight(0, 1);
$self->{rightPaneFrame}->setWeight(0, 2);
$self->_loadCategories();
$self->{factory}->createVStretch($self->{leftPane});
$self->{exitButton} = $self->{factory}->createPushButton($self->{leftPane}, "Exit");
my $quitIcon = File::ShareDir::dist_file(AdminPanel::Shared::distName(), 'images/quit.png');
$self->{exitButton}->setIcon($quitIcon);
$self->{exitButton}->setStretchable(0, 1);
}
## 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
$self->{mainWin}->startMultipleChanges();
## Remove existing modules
$self->{replacePoint}->deleteChildren();
$self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint});
## Change Current Category to the selected one
$self->{currCategory} = $_;
## Add new Module Buttons to Right Pane
$self->{currCategory}->addButtons($self->{rightPane}, $self->{factory});
$self->{rightPaneFrame}->setLabel($self->{currCategory}->{name});
$self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 );
$self->{replacePoint}->showChild();
$self->{mainWin}->recalcLayout();
$self->{mainWin}->doneMultipleChanges();
return 1;
}
}
return 0;
}
## adpanel settings
sub _loadSettings {
my ($self, $force_load) = @_;
# configuration file name
my $fileName = "$self->{confDir}/settings.conf";
die "Configuration file missing" if (! -e $fileName);
if (!$self->{settings} || $force_load) {
$self->{settings} = new AdminPanel::SettingsReader($fileName);
}
}
#=============================================================
# _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 );
@{$self->{categories}}[-1]->{button} = $self->{factory}->createPushButton(
$self->{leftPane},
$self->{categories}[-1]->{name}
);
@{$self->{categories}}[-1]->setIcon();
@{$self->{categories}}[-1]->{button}->setStretchable(0, 1);
}
else {
for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) {
if( @{$self->{categories}}[$cat]->{name} eq $category->{name} &&
!@{$self->{categories}}[$cat]->{button}) {
@{$self->{categories}}[$cat]->{button} = $self->{factory}->createPushButton(
$self->{leftPane},
$self->{categories}[$cat]->{name}
);
@{$self->{categories}}[$cat]->setIcon();
@{$self->{categories}}[$cat]->{button}->setStretchable(0, 1);
last;
}
}
}
}
sub _loadCategories {
my ($self) = @_;
# category files
my @categoryFiles;
my $fileName = "$self->{confDir}/categories.conf";
# configuration file dir
my $directory = "$self->{confDir}/categories.conf.d";
push(@categoryFiles, $fileName);
push(@categoryFiles, <$directory/*.conf>);
my $currCategory;
foreach $fileName (@categoryFiles) {
my $inFile = new AdminPanel::ConfigReader($fileName);
my $tmpCat;
my $tmp;
my $hasNextCat = $inFile->hasNextCat();
while( $hasNextCat ) {
$tmp = $inFile->getNextCat();
$tmpCat = $self->_getCategory($tmp->{title});
if (!$tmpCat) {
$tmpCat = new AdminPanel::Category($tmp->{title}, $tmp->{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}) {
if (not $currCategory->moduleLoaded($tmp->{title})) {
$tmpMod = AdminPanel::Module->create(name => $tmp->{title},
icon => $tmp->{icon},
launch => $tmp->{launcher}
);
}
}
elsif (exists $tmp->{class}) {
if (not $currCategory->moduleLoaded(-CLASS => $tmp->{class})) {
$tmpMod = AdminPanel::Module->create(-CLASS => $tmp->{class});
}
}
if ($tmpMod) {
$loaded = $currCategory->loadModule($tmpMod);
undef $tmpMod if !$loaded;
}
$hasNextMod = $inFile->hasNextMod();
}
}
}
}
1;
=pod
=head2 start
contains the main loop of the application
where we can check for events
=head2 setupGui
creates a popupDialog using a YUI::WidgetFactory
and then populate this dialog with some components
=cut