package GCPanel;
###################################################
#
# Copyright 2005-2010 Christian Jodar
#
# This file is part of GCstar.
#
# GCstar 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.
#
# GCstar 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 GCstar; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#
###################################################
use utf8;
use Gtk2;
use GCBorrowings;
use GCGraphicComponents::GCBaseWidgets;
use strict;
{
package GCItemPanel;
@GCItemPanel::ISA = ('Gtk2::Frame');
sub new
{
my ($proto, $parent, $options, $layout) = @_;
my $class = ref($proto) || $proto;
my $self = $class->SUPER::new();
bless ($self, $class);
$self->{disableBorrowerChange} = 0;
$self->{autoUpdate} = 1;
$self->{previouslyHidden} = 0;
$self->{expanders} = [];
$self->{dateFields} = [];
$self->{longTextFields} = [];
$self->{formatted} = [];
#Stores for each fields the widgets which depends on it
# (used for formatted and launcher)
$self->{dependencies} = {};
$self->{parent} = $parent;
$self->{lang} = $parent->{lang};
$self->{options} = $options;
$self->{layout} = $layout;
$self->set_shadow_type('none');
#$self->{defaultImage} = $parent->getDefaultImage;
$self->{tooltips} = $parent->{tooltips};
$self->{infoBox} = new Gtk2::VBox(1,0);
$self->{viewAllBox} = new Gtk2::EventBox;
$self->{viewAllVBox} = new Gtk2::VBox(0,0);
$self->{viewAll} = new Gtk2::HBox(0,0);
$self->{viewAllButton} = new Gtk2::Button->new_from_stock('gtk-refresh');
$self->{viewAll}->pack_start($self->{viewAllButton},1,0,50);
$self->{viewAllVBox}->pack_start($self->{viewAll},0,0,0);
$self->{viewAllBox}->add($self->{viewAllVBox});
$self->{viewAllButton}->signal_connect('clicked' => sub {
$self->{parent}->viewAllItems;
});
$self->{margin} = $GCUtils::margin;
$self->{halfMargin} = $GCUtils::halfMargin;
$self->{quarterMargin} = $GCUtils::quarterMargin;
return $self;
}
sub loadStyles
{
my ($self, $style) = @_;
}
sub setExpandersMode
{
my ($self, $mode) = @_;
$_->setMode($mode)
foreach (@{$self->{expanders}});
}
sub setDateFormat
{
my ($self, $format) = @_;
foreach (@{$self->{dateFields}})
{
$_->setFormat($format);
$_->resetChanged;
}
}
sub setSpellChecking
{
my ($self, $activate, $lang) = @_;
foreach (@{$self->{longTextFields}})
{
$_->setSpellChecking($activate, $lang);
}
}
sub createContent
{
my ($self, $model) = @_;
return if $self->{model}
&& ($self->{model}->getName eq $model->getName);
if ($self->{model})
{
my @children = $self->{mainBox}->get_children;
$self->{mainBox}->remove($_) foreach (@children);
foreach (@{$self->{toDestroy}})
{
$_->destroy if $_;
}
}
$self->{model} = $model;
$self->{fieldsInfo} = $self->{model}->{fieldsInfo};
$self->{toDestroy} = [];
foreach (@{$model->{fieldsNames}})
{
$self->{$_} = $self->createItem($self->{model}->{fieldsInfo}->{$_});
push @{$self->{toDestroy}}, $self->{$_};
$self->{$_.'Label'} = $self->createLabel($self->{model}->{fieldsInfo}->{$_}, $self->{withSeparator});
push @{$self->{toDestroy}}, $self->{$_.'Label'};
}
$self->createSpecial;
$self->{displayedValues} = {};
$self->createLayout($self->{layout}, $self->{layoutBox});
$self->show_all;
}
sub initShow
{
my ($self, $show, $hasToShow) = @_;
$self->{show} = $show;
if ($hasToShow)
{
$self->show_all;
$self->{infoBox}->hide;
return 1;
}
else
{
$self->hide(0);
$self->{previouslyHidden} = 1;
return 0;
}
}
sub show
{
my $self = shift;
$self->{mainBox}->show;
$self->{infoBox}->hide;
$self->setShowOption($self->{show}, 1) if $self->{previouslyHidden};
$self->{previouslyHidden} = 0;
}
sub setShowOption
{
my ($self, $show, $hasToShow) = @_;
return if ! $self->initShow($show, $hasToShow);
my %parentsToHide;
foreach (keys %{$show})
{
$self->{$_.'Label'}->hide if (! $show->{$_}) && ($self->{$_.'Label'});
my $widget = $self->{$_};
if ($widget)
{
if ($show->{$_})
{
# Useful for GCLinkedComponent
$widget->show if !$widget->isa('Gtk2::Widget');
#$parentsToHide{$widget->{realParent}} = 0;
my $parent = $widget->{realParent};
while ($parent)
{
$parentsToHide{$parent} = 0;
$parent = $parent->{realParent};
}
foreach my $dependency(@{$self->{dependencies}->{$_}})
{
$parentsToHide{$dependency->{realParent}} = 0;
}
}
else
{
$widget->hide;
$parentsToHide{$widget->{realParent}} = $widget->{realParent} if ! exists $parentsToHide{$widget->{realParent}};
foreach my $dependency(@{$self->{dependencies}->{$_}})
{
$dependency->hide;
}
}
}
}
foreach (keys %parentsToHide)
{
if ($parentsToHide{$_})
{
$parentsToHide{$_}->hide;
$parentsToHide{$_}->{realParent}->hide
if ($parentsToHide{$_}->{realParent})
&& ($parentsToHide{$_}->{realParent}->isa('GCExpander'));
}
}
$self->setShowOptionForSpecials($show, $hasToShow);
}
sub setShowOptionForSpecials
{
my ($self, $show, $hasToShow) = @_;
}
sub hide
{
my ($self, $filtering) = @_;
$self->{infoBox}->show_all;
if ($filtering)
{
$self->{warning}->set_markup(''.$self->{lang}->{AllItemsFiltered}.'');
}
else
{
$self->{warning}->set_markup($self->{lang}->{Warning}) if !$filtering;
$self->{viewAllBox}->hide;
}
$self->{mainBox}->hide;
}
sub showMe
{
my $self = shift;
my $parent = $self->parent;
while ($parent)
{
last if ($parent->isa('Gtk2::Window'));
$parent = $parent->parent;
}
$parent->present if $parent;
}
sub isReadOnly
{
return 1;
}
sub getAsHash
{
my $self = shift;
my $hash = {};
$hash->{$_} = $self->$_ foreach(@{$self->{parent}->{model}->{fieldsNames}});
return $hash;
}
sub getValue
{
my ($self, $field) = @_;
return $self->$field;
}
sub disableBorrowerChange
{
my $self = shift;
$self->{disableBorrowerChange} = 1;
}
sub disableAutoUpdate
{
my $self = shift;
$self->{autoUpdate} = 0;
}
sub dataChanged
{
my ($self, $info, $withoutPanel) = @_;
if (!$withoutPanel)
{
foreach (@{$self->{expanders}})
{
$self->setExpanderLabel($_, 1);
}
}
foreach (@{$self->{formatted}})
{
$self->setFormattedLabel($_, 1, $info, $withoutPanel);
}
}
sub createLabel
{
my ($self, $info, $withSeparator) = @_;
my $label = $self->{model}->getDisplayedText($info->{label});
$label .= $self->{parent}->{lang}->{Separator} if $withSeparator;
my $widget = new Gtk2::Label($label);
$widget->set_alignment(0, 0.5);
return $widget;
}
sub createExpander
{
my ($self, $label) = @_;
return new GCExpander($label);
}
sub formatLabel
{
my ($self, $format, $info) = @_;
my $label = $format;
# Remove from format conditional part using this format:
# =field.....=
$label =~ s/\=(\w*)([^\=]*)\=/($self->{show}->{$1}) ? $2 : ''/eg;
# Allow formatting using %fields[format]%
if ($info)
{
$label =~ s/%(.*?)(\[(.*?)\])*%/$3 ? sprintf($3,$info->{$1}) : $info->{$1}/eg;
}
else
{
$label =~ s/%(.*?)(\[(.*?)\])*%/$3 ? sprintf($3,$self->{$1}->getValue(1)) : $self->{$1}->getValue(1)/eg;
}
if ($label =~ /^=(.*)$/)
{
$label = eval $1;
}
$label =~ s/@(.*?)@/$self->{model}->getDisplayedText($1)/eg;
# Strings between ^ cannot be on the end or on the beginning.
# Typically used for separators
$label =~ s/^\^([^\^]*)\^//;
$label =~ s/\^([^\^]*)\^$//;
$label =~ s/\^//g;
return $label;
}
sub setExpanderLabel
{
my ($self, $expander, $init) = @_;
if ($expander->{collapsedTemplate}
&& ($expander->get_expanded xor $init))
{
my $label = $self->formatLabel($expander->{collapsedTemplate});
$expander->setValue($expander->{originalLabel}.($label ? $self->{parent}->{lang}->{Separator} : ""),
$self->formatLabel($expander->{collapsedTemplate}));
}
else
{
$expander->setValue($expander->{originalLabel});
}
}
sub setAsFormatted
{
my ($self, $widget, $name, $format) = @_;
$widget->{format} = $format;
$widget->{name} = $name;
push @{$self->{formatted}}, $widget;
while ($format =~ /%(.*?)%/g)
{
push @{$self->{dependencies}->{$1}}, $widget;
}
}
sub setFormattedLabel
{
my ($self, $label, $init, $info, $withoutPanel) = @_;
my $name = $label->{name};
if ($withoutPanel)
{
$info->{$name} = $self->formatLabel($label->{format}, $info);
}
else
{
$self->$name($self->formatLabel($label->{format}));
# We store here the value so it could be used by caller if needed
# The user is GCData::updateSelectedItemInfoFromPanel
$info->{$name} = $self->$name if defined $info;
}
}
sub prepareDate
{
my ($self, $value) = @_;
return GCUtils::timeToStr($value, $self->{parent}->{options}->dateFormat)
}
sub addToContainer
{
my ($self, $container, $widget, $info) = @_;
$info->{expand} = 'default' if !exists $info->{expand};
if ($container->isa('Gtk2::Notebook'))
{
$container->append_page($widget,
$self->{model}->getDisplayedText($info->{title}));
}
elsif ($container->isa('Gtk2::Table'))
{
my $left = $info->{col};
my $right = $left + ($info->{colspan} ? $info->{colspan} : 1);
my $top = $info->{row};
my $bottom = $top + ($info->{rowspan} ? $info->{rowspan} : 1);
my $fill = [];
push @$fill, 'fill' if $info->{expand} ne 'false';
push @$fill, 'expand' if $info->{expand} eq 'true';
if ($info->{expand} eq 'shrink')
{
$fill = ['expand', 'fill'];
my $hbox = new Gtk2::HBox(0,0);
$hbox->pack_start($widget,0,0,0);
$widget = $hbox;
}
if ($info->{expand} eq 'horizontal')
{
$container->attach($widget,
$left, $right, $top, $bottom,
['expand', 'fill'], [],
0, 0);
}
else
{
$container->attach($widget,
$left, $right, $top, $bottom,
$fill, $fill,
0, 0);
}
}
else
{
my $expand = ($info->{expand} eq 'true') ? 1 : 0;
if ($container->isa('Gtk2::VBox'))
{
my $margin = $self->{halfMargin};
$margin = 0 if (exists $info->{nomargin}) && ($info->{nomargin} eq 'true');
if ((exists $info->{place}) && ($info->{place} eq 'end'))
{
$container->pack_end($widget, $expand, $expand, $margin);
}
else
{
$container->pack_start($widget, $expand, $expand, $margin);
}
}
else
{
my $margin = $self->{halfMargin};
$margin = 0 if (exists $info->{nomargin}) && ($info->{nomargin} eq 'true');
if (($container->get_children) && (!$container->get_homogeneous) && ($margin > 0))
{
my $marginBox = new Gtk2::HBox(0,0);
$container->pack_start($marginBox, 0, 0, $margin);
}
$container->pack_start($widget, $expand, $expand, 0)
if ref($widget) ne 'HASH';
}
}
}
sub setWidgetStyle
{
my ($self, $widget, $name, $style) = @_;
}
sub getWidget
{
my ($self, $field, $info) = @_;
return $self->{$field};
}
sub createLayout
{
my ($self, $xml, $container, $parent, $noTab) = @_;
#
foreach (@{$xml->{item}})
{
my $widget;
my $box;
my $isContainer = 0;
# TODO We should return an error here
$_->{type} = 'default' if ! exists $_->{type};
if ($_->{type} eq 'formatted')
{
my $info = {};
$info->{type} = 'short text';
$info->{value} = $_->{name};
$widget = $self->createItem($info);
$self->{$_->{name}} = $widget;
$self->setAsFormatted($widget, $_->{name}, $_->{value});
$_->{for} = $widget->{name};
}
elsif ($_->{type} eq 'value')
{
$widget = $self->getWidget($_->{for}, $_);
if ($widget)
{
$widget->setWidth($_->{width}) if $_->{width};
$widget->setHeight($_->{height}) if $_->{height};
$widget->expand if (exists $_->{expand}) && ($_->{expand} eq 'true');
if ($_->{values} || $self->{model}->{fieldsInfo}->{$_->{for}}->{values})
{
$self->{displayedValues}->{$_->{for}} =
$_->{values}
|| $self->{model}->{fieldsInfo}->{$_->{for}}->{values};
}
# If the item is related to another, it has to be hidden with it
if (exists $self->{model}->{fieldsInfo}->{$_->{for}}->{relatedto})
{
my $relatedTo = $self->{model}->{fieldsInfo}->{$_->{for}}->{relatedto};
push @{$self->{dependencies}->{$relatedTo}}, $widget;
}
}
}
elsif ($_->{type} eq 'label')
{
if ($_->{for})
{
$widget = $self->{$_->{for}.'Label'};
}
else
{
$widget = $self->createLabel($_, $self->{withSeparator});
}
if ((exists $_->{align}) && ($_->{align} eq 'center'))
{
$widget->set_alignment(0.5, 0.5);
if ($self->{withSeparator})
{
my $label = $widget->get_label;
my $sep = $self->{parent}->{lang}->{Separator};
$label =~ s/$sep$//;
$widget->set_label($label);
}
}
$widget->set_alignment(0, 0) if (exists $_->{align}) && ($_->{align} eq 'top');
}
elsif ($_->{type} eq 'special')
{
$widget = $self->{$_->{for}};
$widget->{param} = $_->{param};
}
elsif ($_->{type} eq 'launcher')
{
my $valueWidget = $self->{$_->{for}};
my $format = $self->{model}->{fieldsInfo}->{$_->{for}}->{format};
# If the launcher is for a video or music file, label the button 'play'
if (($format eq 'video') || ($format eq 'audio'))
{
$widget = Gtk2::Button->new_from_stock('gtk-media-play');
}
elsif ($format eq 'program')
{
# For program launchers, label the button 'launch'
$widget = Gtk2::Button->new($self->{parent}->{lang}->{PanelLaunch});
}
else
{
# Label the button 'open'
$widget = Gtk2::Button->new_from_stock('gtk-open');
}
$widget->signal_connect('clicked' => sub {
$self->{parent}->launch($valueWidget->getValue, $format);
});
push @{$self->{dependencies}->{$_->{for}}}, $widget;
}
elsif ($_->{type} eq 'extractor')
{
$widget = Gtk2::Button->new($self->{parent}->{lang}->{ExtractButton});
my $valueWidget = $self->{$_->{for}};
$widget->signal_connect('clicked' => sub {
$self->{parent}->extractInfo($valueWidget->getValue, $self);
});
push @{$self->{dependencies}->{$_->{for}}}, $widget;
}
elsif ($_->{type} eq 'line')
{
my $homogeneous = (exists $_->{homogeneous} && ($_->{homogeneous} eq 'true'))
? 1 : 0;
$widget = new Gtk2::HBox($homogeneous, $self->{margin});
#$widget->set_border_width($self->{halfMargin});
$widget->set_size_request(-1, $_->{height}) if $_->{height};
$box = $widget;
$isContainer = 1;
}
elsif ($_->{type} eq 'box')
{
$widget = new Gtk2::VBox(0,0);
#$widget->set_border_width($self->{halfMargin});
$box = $widget;
if ($_->{width})
{
# $widget = new Gtk2::EventBox;
# $widget->add($box);
$widget->set_size_request($_->{width}, -1);
}
$isContainer = 1;
}
elsif ($_->{type} eq 'notebook')
{
if ($noTab)
{
$widget = new Gtk2::VBox(0,0);
}
else
{
$widget = new Gtk2::Notebook;
}
#$widget->set_border_width($self->{margin});
$box = $widget;
$isContainer = 1;
}
elsif ($_->{type} eq 'tab')
{
$widget = new Gtk2::VBox(0,0);
if ($noTab)
{
my $label = new Gtk2::Label;
$label->set_markup(''.$self->{model}->getDisplayedText($_->{title}).'');
$label->set_alignment(0,0);
$widget->pack_start($label, 0, 0, 0);
$widget->set_border_width(0);
$box = new Gtk2::VBox(0,0);
$box->set_border_width($self->{margin});
$widget->pack_start($box, 1, 1, 0);
}
else
{
$widget->set_border_width($self->{margin});
$box = $widget;
}
$isContainer = 1;
}
elsif ($_->{type} eq 'table')
{
$widget = new Gtk2::Table($_->{rows},$_->{cols});
#$widget->set_border_width($self->{margin});
$widget->set_col_spacings($self->{colSpacing});
$widget->set_row_spacings($self->{rowSpacing});
$box = $widget;
$isContainer = 1;
}
elsif ($_->{type} eq 'frame')
{
$widget = new GCGroup($self->{model}->getDisplayedText($_->{title}));
$widget->set_border_width(0);
$widget->setPadding(0);
$box = new Gtk2::Table($_->{rows},$_->{cols});
$box->set_border_width($self->{halfMargin});
$box->set_col_spacings($self->{margin});
$box->set_row_spacings($self->{halfMargin});
$widget->addWidget($box);
$isContainer = 1;
}
elsif ($_->{type} eq 'expander')
{
#$widget = new GCExpander($self->{model}->getDisplayedText($_->{title}));
$widget = $self->createExpander($self->{model}->getDisplayedText($_->{title}));
$widget->setMode($self->{parent}->{options}->expandersMode);
$widget->{originalLabel} = $self->{model}->getDisplayedText($_->{title});
$widget->{collapsedTemplate} = $_->{collapsed};
$self->setExpanderLabel($widget, 1);
$widget->signal_connect('activate' => sub {
$self->setExpanderLabel($widget, 0);
});
$widget->set_expanded(1) if (defined $_->{expanded}) && ($_->{expanded} eq 'true');
push @{$self->{expanders}}, $widget;
$box = new Gtk2::VBox(0,0);
$box->set_border_width($self->{halfMargin});
$widget->add($box);
$isContainer = 1;
$_->{style} = 'expander' if !exists $_->{style};
}
if (exists $_->{border})
{
$widget->set_border_width($_->{border});
}
if (exists $_->{collapse})
{
$widget->{collapse} = $_->{collapse};
}
if ($isContainer)
{
$self->createLayout($_, $box, $widget, $noTab);
}
push @{$self->{toDestroy}}, $widget;
$widget->{realParent} = $parent if $parent;
$self->setWidgetStyle($widget, $_->{for}, $_->{style}) if $_->{style};
$self->addToContainer($container, $widget, $_) if $widget;
}
}
}
{
package GCReadOnlyPanel;
use Glib::Object::Subclass
Gtk2::Frame::
;
@GCReadOnlyPanel::ISA = ('GCItemPanel');
use File::Basename;
sub new
{
my ($proto, $parent, $options, $layout) = @_;
my $class = ref($proto) || $proto;
my $self = $class->SUPER::new($parent, $options, $layout);
bless ($self, $class);
$self->{withSeparator} = 0;
$self->{colSpacing} = $self->{quarterMargin};
$self->{rowSpacing} = $self->{quarterMargin};
$self->loadStyles($options->panelStyle);
$self->{inBox} = new Gtk2::VBox(0, 0);
$self->{inBox}->set_border_width($self->{margin});
$self->set_name('GCItemFrame');
$self->{inBox} = new Gtk2::VBox(0, 0);
my $hboxMargins = new Gtk2::HBox(0,0);
$hboxMargins->pack_start($self->{inBox}, 1, 1, 50);
$self->{mainBox} = new Gtk2::EventBox;
$self->{mainBox}->modify_bg('normal', $self->{styles}->{page}->{bgColor});
$self->{mainBox}->add($hboxMargins);
my $realMain = new Gtk2::VBox(0,0);
# GCUtils::setWidgetPixmap($self->{mainBox},$ENV{GCS_SHARE_DIR}.'/logos/splash.png');
# $self->{backgroundPixbuf} = Gtk2::Gdk::Pixbuf->new_from_file($ENV{GCS_SHARE_DIR}.'/logos/splash.png');
# $self->{mainBox}->signal_connect('expose-event' => sub {
# $self->paintBg;
# });
$realMain->pack_start($self->{mainBox},1,1,0);
$self->{layoutBox} = $self->{inBox};
$self->add($realMain);
$self->show_all;
$self->{warning} = new Gtk2::Label;
$self->{infoBox}->pack_start($self->{warning},1,1,0);
$self->{infoBox}->pack_start($self->{viewAllBox},1,1,0);
$realMain->pack_start($self->{infoBox},1,1,0);
return $self;
}
sub loadStyles
{
my ($self, $style) = @_;
$self->{styles} = {};
my $styleFile = $ENV{GCS_SHARE_DIR}.'/panels/'.$style;
open STYLE, $styleFile;
while (