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 (