package ZConf::RSS::GUI::GTK;
use warnings;
use strict;
use ZConf::GUI;
use ZConf::RSS;
use ZConf::RSS::GUI;
use Gtk2;
use Gtk2::SimpleList;
=head1 NAME
ZConf::RSS::GUI::GTK - Providees the GTK backend for ZConf::RSS::GUI.
=head1 VERSION
Version 0.0.1
=cut
our $VERSION = '0.0.1';
=head1 SYNOPSIS
use ZConf::RSS::GUI::GTK;
my $zcrssgtk = ZConf::RSS::GUI::GTK->new();
...
=head1 METHODS
=head2 new
=head3 args hash
=head4 obj
This is object returned by ZConf::RSS.
my $zcrssgtk=ZConf::RSS::GUI::GTK->new({obj=>$obj});
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub new{
my %args;
if(defined($_[1])){
%args= %{$_[1]};
}
my $function='new';
my $self={error=>undef,
perror=>undef,
errorString=>undef,
module=>'ZConf-RSS-GUI-GTK',
gui=>{},
};
bless $self;
#gets the object or initiate it
if (!defined($args{obj})) {
$self->{obj}=ZConf::RSS->new;
if ($self->{obj}->{error}) {
$self->{error}=1;
$self->{perror}=1;
$self->{errorString}='Failed to initiate ZConf::RSS. error="'.
$self->{obj}->{error}.'" errorString="'.$self->{obj}->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return $self;
}
}else {
$self->{obj}=$args{obj};
}
#gets the zconf object
$self->{zconf}=$self->{obj}->{zconf};
Gtk2->init;
return $self;
}
=head2 addFeed
This calls a dialog for creating a new feed. It will also init
Gtk2 and start it's main loop.
If GTK is already inited and you don't want the main loop exited,
use addFeedDialog.
One agruement is accepted and it is the URL to set it to by default.
If not specified, '' is used.
$zcrssgtk->addFeed('http://foo.bar/rss.xml');
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub addFeed{
my $self=$_[0];
my $url=$_[1];
my $function='addFeed';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
Gtk2->init;
my $guiID=$self->addFeedDialog({
url=>$url,
killmain=>1,
});
if ($self->{error}) {
warn($self->{module}.' '.$function.': addFeedDialog errored');
return undef;
}
return 1;
}
=head2 addFeedDialog
A dialog for adding a new feed.
One arguement is taken and it is a hash.
=head3 hash args
=head4 id
This is GUI ID to update after it is called. If this is not defined,
no attempted to update it will be made.
=head4 url
This is the URL to set it to initially. If this is not defined,
it is set to ''.
$zcrssgtk->addFeedDialog({
url=>'http://foo.bar/rss.xml',
})
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub addFeedDialog{
my $self=$_[0];
my $url=$_[1]{url};
my $guiID=$_[1]{id};
my $function='addFeedDialog';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
Gtk2->init;
if (!defined($url)) {
$url='';
}
my $window = Gtk2::Dialog->new('ZConf::RSS - Add new RSS feed?',
undef,
[qw/modal destroy-with-parent/],
'gtk-cancel' => 'cancel',
'gtk-ok' => 'accept',
);
$window->set_position('center-always');
$window->set_response_sensitive ('accept', 0);
$window->set_response_sensitive ('reject', 0);
my $vbox = $window->vbox;
$vbox->set_border_width(5);
my $label = Gtk2::Label->new_with_mnemonic('Add new RSS feed?');
$vbox->pack_start($label, 0, 0, 1);
$label->show;
#url stuff
my $ubox=Gtk2::HBox->new;
$ubox->show;
my $ulabel=Gtk2::Label->new('URL: ');
$ulabel->show;
$ubox->pack_start($ulabel, 0, 1, 0);
my $uentry = Gtk2::Entry->new();
$uentry->set_editable(1);
$uentry->set_text($url);
$uentry->show;
$ubox->pack_start($uentry, 1, 1, 0);
$vbox->pack_start($ubox, 0, 0, 1);
#name stuff
my $nhbox=Gtk2::HBox->new;
$nhbox->show;
my $nlabel=Gtk2::Label->new('name: ');
$nlabel->show;
$nhbox->pack_start($nlabel, 0, 1, 0);
my $nentry = Gtk2::Entry->new();
$nentry->set_text('');
$nhbox->pack_start($nentry, 1, 1, 0);
$nentry->show;
$vbox->pack_start($nhbox, 0, 0, 1);
#top template stuff
my $tthbox=Gtk2::HBox->new;
$tthbox->show;
my $ttlabel=Gtk2::Label->new('top template: ');
$ttlabel->show;
$tthbox->pack_start($ttlabel, 0, 1, 0);
my $ttentry = Gtk2::Entry->new();
$ttentry->set_text('defaultTop');
$tthbox->pack_start($ttentry, 1, 1, 0);
$ttentry->show;
$vbox->pack_start($tthbox, 0, 0, 1);
#item template stuff
my $ithbox=Gtk2::HBox->new;
$ithbox->show;
my $itlabel=Gtk2::Label->new('item template: ');
$itlabel->show;
$ithbox->pack_start($itlabel, 0, 1, 0);
my $itentry = Gtk2::Entry->new();
$itentry->set_text('defaultItem');
$ithbox->pack_start($itentry, 1, 1, 0);
$itentry->show;
$vbox->pack_start($ithbox, 0, 0, 1);
#bottom template stuff
my $bthbox=Gtk2::HBox->new;
$bthbox->show;
my $btlabel=Gtk2::Label->new('bottom template: ');
$btlabel->show;
$bthbox->pack_start($btlabel, 0, 1, 0);
my $btentry = Gtk2::Entry->new();
$btentry->set_text('defaultBottom');
$bthbox->pack_start($btentry, 1, 1, 0);
$btentry->show;
$vbox->pack_start($bthbox, 0, 0, 1);
$uentry->signal_connect (changed => sub {
my $text = $uentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$nentry->signal_connect (changed => sub {
my $text = $nentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$ttentry->signal_connect (changed => sub {
my $text = $ttentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$itentry->signal_connect (changed => sub {
my $text = $itentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$btentry->signal_connect (changed => sub {
my $text = $btentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
my $name;
my $top;
my $item;
my $bottom;
my $pressed;
$window->signal_connect(response => sub {
$url=$uentry->get_text;
$name=$nentry->get_text;
$top=$ttentry->get_text;
$item=$itentry->get_text;
$bottom=$btentry->get_text;
$pressed=$_[1];
}
);
#runs the dailog and gets the response
#'cancel' means the user decided not to create a new set
#'accept' means the user wants to create a new set with the entered name
my $response=$window->run;
$window->destroy;
if ($pressed ne 'accept') {
return undef;
}
#add the bookmark
$self->{obj}->setFeed({
feed=>$url,
name=>$name,
topTemplate=>$top,
bottomTemplate=>$bottom,
itemTemplate=>$item,
});
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='setFeed errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
if (!defined($guiID)) {
return 1;
}
#update the stuff
$self->updateFeedList( $guiID );
return 1;
}
=head2 addTemplateDialog
A dialog for adding a new feed.
One arguement is taken and it is a the GUI ID for the manage VBox in question.
$zcrssgtk->addFeedDialog( $guiID );
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub addTemplateDialog{
my $self=$_[0];
my $guiID=$_[1];
my $function='addTemplateDialog';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
if (!defined($guiID)) {
$self->{error}=4;
$self->{errorString}='No GUI ID specified';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
my $window = Gtk2::Dialog->new('ZConf::RSS - Add new template?',
undef,
[qw/modal destroy-with-parent/],
'gtk-cancel' => 'cancel',
'gtk-ok' => 'accept',
);
$window->set_position('center-always');
$window->set_response_sensitive ('accept', 0);
$window->set_response_sensitive ('reject', 0);
my $vbox = $window->vbox;
$vbox->set_border_width(5);
my $label = Gtk2::Label->new_with_mnemonic('Add new template?');
$vbox->pack_start($label, 0, 0, 1);
$label->show;
#name stuff
my $nhbox=Gtk2::HBox->new;
$nhbox->show;
my $nlabel=Gtk2::Label->new('name: ');
$nlabel->show;
$nhbox->pack_start($nlabel, 0, 1, 0);
my $nentry = Gtk2::Entry->new();
$nentry->set_text('');
$nhbox->pack_start($nentry, 1, 1, 0);
$nentry->show;
$vbox->pack_start($nhbox, 0, 0, 1);
$nentry->signal_connect (changed => sub {
my $text = $nentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
my $name;
my $pressed;
$window->signal_connect(response => sub {
$name=$nentry->get_text;
$pressed=$_[1];
}
);
#runs the dailog and gets the response
#'cancel' means the user decided not to create a new set
#'accept' means the user wants to create a new set with the entered name
my $response=$window->run;
$window->destroy;
#kill the main if needed
if ($_[1]{killmain}) {
Gtk2->main_quit;
}
if ($pressed ne 'accept') {
return undef;
}
#add the bookmark
$self->{obj}->setTemplate($name, '');
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='setTemplate errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
if (!defined($guiID)) {
return 1;
}
#update the stuff
$self->updateTemplateList( $guiID );
return 1;
}
=head2 manage
Invokes the view window.
$zcrssgtk->manage;
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub manage{
my $self=$_[0];
my $function='manage';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
Gtk2->init;
my $guiID=$self->manageWindow;
$self->{gui}{$guiID}{window}->show;
Gtk2->main;
return 1;
}
=head2 manageVBox
This creates a VBox for the manage GUI and returns the GUI ID of it.
=head3 args hash
=head4 disableQuit
If this is set to true, the quit selection under the misc functions menu will not be present.
my $guiID=$zcrssgtk->manageVBox;
if($zcrssgtk->{error}){
print "Error!\n";
}else{
$window=Gtk2::Window->new;
$window->set_default_size(750, 400);
$window->set_title('ZConf::RSS: manage');
$window->add($zcrssgtk->{gui}{$guiID}{vbox});
}
=cut
sub manageVBox{
my $self=$_[0];
my %args;
if(defined($_[1])){
%args= %{$_[1]};
}
my $function='manageVBox';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
my %gui;
$gui{id}=rand().rand().rand();
#the main box
$gui{vbox}=Gtk2::VBox->new;
$gui{vbox}->show;
$gui{hpaned}=Gtk2::HPaned->new;
$gui{hpaned}->set_position(0);
$gui{hpaned}->show;
$gui{hpaned}->set_position(200);
$gui{vbox}->pack_start($gui{hpaned}, 1, 1, 0);
#puts together the basics for the list box
$gui{listbox}=Gtk2::VBox->new;
$gui{listbox}->show;
$gui{hpaned}->add1($gui{listbox});
#this is the menu bar that goes in the list box
$gui{menubar}=Gtk2::MenuBar->new;
$gui{menubarmenu}=Gtk2::MenuItem->new('_misc functions');
$gui{menubar}->show;
$gui{menubarmenu}->show;
$gui{menubar}->append($gui{menubarmenu});
$gui{listbox}->pack_start($gui{menubar}, 0, 1, 0);
#the window that holdes
$gui{listSW}=Gtk2::ScrolledWindow->new;
$gui{listSW}->show;
$gui{listbox}->pack_start($gui{listSW}, 1, 1, 0);
#puts the feed list stuff together
$gui{templatelist}=Gtk2::SimpleList->new(
'Templates'=>'text',
);
$gui{templatelist}->get_selection->set_mode ('single');
$gui{templatelist}->show;
$gui{templatelist}->signal_connect(row_activated=>sub{
$_[3]{self}->updateTemplateText($_[3]{id});
},
{
self=>$self,
id=>$gui{id},
}
);
$gui{listSW}->add($gui{templatelist});
#puts together the menu
$gui{menu}=Gtk2::Menu->new;
$gui{menu}->show;
#add
$gui{menuAdd}=Gtk2::MenuItem->new('_add');
$gui{menuAdd}->show;
$gui{menuAdd}->signal_connect(activate=>sub{
$_[1]{self}->addTemplateDialog({id=>$_[1]{id}});
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuAdd});
#remove
$gui{menuRemove}=Gtk2::MenuItem->new('_remove');
$gui{menuRemove}->show;
$gui{menuRemove}->signal_connect(activate=>sub{
$_[1]{self}->removeTemplateDialog($_[1]{id});
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuRemove});
#quit
if (!$args{disableQuit}) {
$gui{menuQuit}=Gtk2::MenuItem->new('_quit');
$gui{menuQuit}->show;
$gui{menuQuit}->signal_connect(activate=>sub{
Gtk2->main_quit;
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuQuit});
}
#attaches it
$gui{menubarmenu}->set_submenu($gui{menu});
#puts together the view section
$gui{textVBox}=Gtk2::VBox->new;
$gui{textVBox}->show;
$gui{hpaned}->add2($gui{textVBox});
#adds the hbox that will hold the save/reload button
$gui{buttonHBox}=Gtk2::HBox->new;
$gui{buttonHBox}->show;
$gui{textVBox}->pack_start($gui{buttonHBox}, 0, 1, 0);
#reload button
$gui{reloadTemplate}=Gtk2::Button->new();
$gui{reloadTemplateLabel}=Gtk2::Label->new('Reload Selected Template');
$gui{reloadTemplateLabel}->show;
$gui{reloadTemplate}->add($gui{reloadTemplateLabel});
$gui{reloadTemplate}->show;
$gui{buttonHBox}->pack_start($gui{reloadTemplate}, 0, 1, 0);
$gui{reloadTemplate}->signal_connect(clicked=>sub{
$_[1]{self}->updateTemplateText($_[1]{id});
},
{
id=>$gui{id},
self=>$self,
}
);
#save button
$gui{saveTemplate}=Gtk2::Button->new();
$gui{saveTemplateLabel}=Gtk2::Label->new('Save Selected Template');
$gui{saveTemplateLabel}->show;
$gui{saveTemplate}->add($gui{saveTemplateLabel});
$gui{saveTemplate}->show;
$gui{buttonHBox}->pack_start($gui{saveTemplate}, 0, 1, 0);
$gui{saveTemplate}->signal_connect(clicked=>sub{
my $self=$_[1]{self};
my $guiID=$_[1]{id};
#get the current template name
my @selected=$self->{gui}{ $guiID }{templatelist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $templateName=$self->{templates}[$selected[0]];
if (!defined($templateName)) {
return undef;
}
my $template=$self->{gui}{ $guiID }{buffer}->get_text(
$self->{gui}{ $guiID }{buffer}->get_start_iter,
$self->{gui}{ $guiID }{buffer}->get_end_iter,
1
);
$self->{obj}->setTemplate($templateName, $template);
},
{
id=>$gui{id},
self=>$self,
}
);
#adds the VPaned that will hold the template and the template info
$gui{textVPaned}=Gtk2::VPaned->new;
$gui{textVPaned}->show;
$gui{textVPaned}->set_position(200);
$gui{textVBox}->pack_start($gui{textVPaned}, 1, 1, 1);
#adds the template info text view
$gui{templateInfo}=Gtk2::TextView->new;
$gui{templateInfo}->show;
$gui{templateInfo}->set_editable(0);
$gui{templateInfoSW}=Gtk2::ScrolledWindow->new;
$gui{templateInfoSW}->show;
$gui{templateInfoSW}->add($gui{templateInfo});
$gui{textVPaned}->add1($gui{templateInfoSW});
$gui{templateInfoBuffer}=Gtk2::TextBuffer->new;
$gui{templateInfoBuffer}->set_text(
'TEMPLATE VARIABLES
The templating system used is \'Text::NeatTemplate\'. The varialbes are as below.
CHANNEL
{$ctitle} - This is the title for the channel.
{$cdesc} - This is the description for the channel.
{$cpubdate} - This is the publication date for the channel.
{$ccopyright} - This is the copyright info for the channel.
{$clink} - This is the link for the channel.
{$clang} - This is the language for the channel.
{$cimage} - This is the image for the channel.
ITEM
{$ititle} - This is the title for a item.
{$idesc} - This is the description for a item.
{$idescFTWL} - This is the description for a item that has been has been formated with \'HTML::FormatText::WithLinks\'
{$ipubdate} - This is the date published for a item.
{$icat} - This is the category for a item.
{$iauthor} - This is the author for a item.
{$iguid} - This is the item\'s guid element.
{$ilink} - This is the link for a item.'
);
$gui{templateInfo}->set_buffer($gui{templateInfoBuffer});
#puts together the template text view
$gui{text}=Gtk2::TextView->new;
$gui{text}->show;
$gui{text}->set_editable(1);
$gui{textSW}=Gtk2::ScrolledWindow->new;
$gui{textSW}->show;
$gui{textSW}->add($gui{text});
$gui{textVPaned}->add2($gui{textSW});
$gui{buffer}=Gtk2::TextBuffer->new;
$gui{buffer}->set_text('');
$gui{text}->set_buffer($gui{buffer});
$self->{gui}{ $gui{id} }=\%gui;
#this updates it
$self->updateTemplateList($gui{id});
return $gui{id};
}
=head2 manageWindow
This generates the manage window.
=head3 args hash
=head4 disableQuit
Do not show the quit selection in the menu.
=head4 quitOnClose
If the window is closed, quit the main GTK loop.
=head4 removeOnClose
Removes the GUI upon close.
Gtk2->init;
my $guiID=$zcrssgtk->manageWindow;
if($zcrssgtk->{error}){
print "Error!\n";
}else{
$zcrssgtk->{gui}{$guiID}{window}->show;
$Gtk2->main;
}
=cut
sub manageWindow{
my $self=$_[0];
my %args;
if(defined($_[1])){
%args= %{$_[1]};
}
my $function='manageWindow';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
my $guiID=$self->manageVBox({disableQuit=>$args{disableQuit}});
$self->{gui}{ $guiID }{window}=Gtk2::Window->new;
$self->{gui}{ $guiID }{window}->set_default_size(750, 500);
$self->{gui}{ $guiID }{window}->set_title('ZConf::RSS: manage');
$self->{gui}{ $guiID }{window}->add($self->{gui}{$guiID}{vbox});
#handles the closure stuff
if ($args{quitOnClose}) {
$self->{gui}{ $guiID }{window}->signal_connect(delete_event=>sub{
Gtk2->main_quit;
}
);
}
if ($args{quitOnClose}) {
$self->{gui}{ $guiID }{window}->signal_connect(delete_event=>sub{
delete($self->{gui}{ $guiID });
});
}
return $guiID;
}
=head2 modifyFeedDialog
This is the modifies the currently selected feed.
Only one arguement is required and it is GUI ID.
$zcrssgtk->modifyFeedDialog($guiID);
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub modifyFeedDialog{
my $self=$_[0];
my $guiID=$_[1];
my $function='addFeedDialog';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
if (!defined($guiID)) {
$self->{error}=4;
$self->{errorString}='No GUI ID specified';
warn($self->{module}.' '.$function.':"'.$self->{error}.': '.$self->{errorString});
return undef;
}
#get the feed
my @selected=$self->{gui}{ $guiID }{feedlist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $feed=$self->{feeds}[$selected[0]];
#gets the feed args
my %feedArgs=$self->{obj}->getFeedArgs($feed);
my $window = Gtk2::Dialog->new('ZConf::RSS - Modify the feed "'.$feed.'"?',
undef,
[qw/modal destroy-with-parent/],
'gtk-cancel' => 'cancel',
'gtk-ok' => 'accept',
);
$window->set_position('center-always');
$window->set_response_sensitive ('accept', 0);
$window->set_response_sensitive ('reject', 0);
my $vbox = $window->vbox;
$vbox->set_border_width(5);
#url stuff
my $ubox=Gtk2::HBox->new;
$ubox->show;
my $ulabel=Gtk2::Label->new('URL: ');
$ulabel->show;
$ubox->pack_start($ulabel, 0, 1, 0);
my $uentry = Gtk2::Entry->new();
$uentry->set_editable(1);
$uentry->set_text($feedArgs{feed});
$uentry->show;
$ubox->pack_start($uentry, 1, 1, 0);
$vbox->pack_start($ubox, 0, 0, 1);
#name stuff
my $nhbox=Gtk2::HBox->new;
$nhbox->show;
my $nlabel=Gtk2::Label->new('name: ');
$nlabel->show;
$nhbox->pack_start($nlabel, 0, 1, 0);
my $nentry = Gtk2::Entry->new();
$nentry->set_text($feedArgs{name});
$nhbox->pack_start($nentry, 1, 1, 0);
$nentry->show;
$vbox->pack_start($nhbox, 0, 0, 1);
#top template stuff
my $tthbox=Gtk2::HBox->new;
$tthbox->show;
my $ttlabel=Gtk2::Label->new('top template: ');
$ttlabel->show;
$tthbox->pack_start($ttlabel, 0, 1, 0);
my $ttentry = Gtk2::Entry->new();
$ttentry->set_text($feedArgs{topTemplate});
$tthbox->pack_start($ttentry, 1, 1, 0);
$ttentry->show;
$vbox->pack_start($tthbox, 0, 0, 1);
#item template stuff
my $ithbox=Gtk2::HBox->new;
$ithbox->show;
my $itlabel=Gtk2::Label->new('item template: ');
$itlabel->show;
$ithbox->pack_start($itlabel, 0, 1, 0);
my $itentry = Gtk2::Entry->new();
$itentry->set_text($feedArgs{itemTemplate});
$ithbox->pack_start($itentry, 1, 1, 0);
$itentry->show;
$vbox->pack_start($ithbox, 0, 0, 1);
#bottom template stuff
my $bthbox=Gtk2::HBox->new;
$bthbox->show;
my $btlabel=Gtk2::Label->new('bottom template: ');
$btlabel->show;
$bthbox->pack_start($btlabel, 0, 1, 0);
my $btentry = Gtk2::Entry->new();
$btentry->set_text($feedArgs{bottomTemplate});
$bthbox->pack_start($btentry, 1, 1, 0);
$btentry->show;
$vbox->pack_start($bthbox, 0, 0, 1);
$uentry->signal_connect (changed => sub {
my $text = $uentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$nentry->signal_connect (changed => sub {
my $text = $nentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$ttentry->signal_connect (changed => sub {
my $text = $ttentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$itentry->signal_connect (changed => sub {
my $text = $itentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
$btentry->signal_connect (changed => sub {
my $text = $btentry->get_text;
$window->set_response_sensitive ('accept', $text !~ m/^\s*$/);
$window->set_response_sensitive ('reject', 1);
}
);
my $name;
my $top;
my $item;
my $bottom;
my $pressed;
my $url;
$window->signal_connect(response => sub {
$url=$uentry->get_text;
$name=$nentry->get_text;
$top=$ttentry->get_text;
$item=$itentry->get_text;
$bottom=$btentry->get_text;
$pressed=$_[1];
}
);
#runs the dailog and gets the response
#'cancel' means the user decided not to create a new set
#'accept' means the user wants to create a new set with the entered name
my $response=$window->run;
$window->destroy;
if ($pressed ne 'accept') {
return undef;
}
#remove the old one if we are renaming it
if ($name ne $feedArgs{name}) {
$self->{obj}->delFeed($feed);
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='setFeed errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
}
#add the bookmark
$self->{obj}->setFeed({
feed=>$url,
name=>$name,
topTemplate=>$top,
bottomTemplate=>$bottom,
itemTemplate=>$item,
});
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='setFeed errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
#update the stuff
$self->updateFeedList( $guiID );
return 1;
}
=head2 removeFeedDialog
A dialog for adding a new feed.
One arguement is taken and it is the GUI ID.
$zcrssgtk->removeFeedDialog($guiID);
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub removeFeedDialog{
my $self=$_[0];
my $guiID=$_[1];
my $function='removeFeedDialog';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
if (!defined($guiID)) {
$self->{error}=4;
$self->{errorString}='No GUI ID specified';
warn($self->{module}.' '.$function.':"'.$self->{error}.': '.$self->{errorString});
return undef;
}
my @selected=$self->{gui}{ $guiID }{feedlist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $feed=$self->{feeds}[$selected[0]];
my $window = Gtk2::Dialog->new('ZConf::RSS - Remove the feed "'.$feed.'"?',
undef,
[qw/modal destroy-with-parent/],
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
$window->set_position('center-always');
$window->set_response_sensitive ('accept', 0);
$window->set_response_sensitive ('reject', 0);
my $vbox = $window->vbox;
$vbox->set_border_width(5);
my $label = Gtk2::Label->new_with_mnemonic('Remove the feed "'.$feed.'"?');
$vbox->pack_start($label, 0, 0, 1);
$label->show;
my $pressed;
$window->signal_connect(response => sub {
$pressed=$_[1];
}
);
#runs the dailog and gets the response
#'cancel' means the user decided not to create a new set
#'accept' means the user wants to create a new set with the entered name
my $response=$window->run;
$window->destroy;
if ($pressed ne 'ok') {
return undef;
}
#remove the feed
$self->{obj}->delFeed($feed);
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='delFeed errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
if (!defined($guiID)) {
return 1;
}
#update the stuff
$self->updateFeedList( $guiID );
return 1;
}
=head2 removeTemplateDialog
A dialog for adding a new feed.
One arguement is taken and it is the GUI ID.
$zcrssgtk->removeTemplateDialog($guiID);
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub removeTemplateDialog{
my $self=$_[0];
my $guiID=$_[1];
my $function='removeTemplateDialog';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
if (!defined($guiID)) {
$self->{error}=4;
$self->{errorString}='No GUI ID specified';
warn($self->{module}.' '.$function.':"'.$self->{error}.': '.$self->{errorString});
return undef;
}
my @selected=$self->{gui}{ $guiID }{templatelist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $template=$self->{templates}[$selected[0]];
my $window = Gtk2::Dialog->new('ZConf::RSS - Remove the template "'.$template.'"?',
undef,
[qw/modal destroy-with-parent/],
'gtk-cancel' => 'cancel',
'gtk-ok' => 'ok',
);
$window->set_position('center-always');
$window->set_response_sensitive ('accept', 0);
$window->set_response_sensitive ('reject', 0);
my $vbox = $window->vbox;
$vbox->set_border_width(5);
my $label = Gtk2::Label->new_with_mnemonic('Remove the template "'.$template.'"?');
$vbox->pack_start($label, 0, 0, 1);
$label->show;
my $pressed;
$window->signal_connect(response => sub {
$pressed=$_[1];
}
);
#runs the dailog and gets the response
#'cancel' means the user decided not to create a new set
#'accept' means the user wants to create a new set with the entered name
my $response=$window->run;
$window->destroy;
if ($pressed ne 'ok') {
return undef;
}
#remove the feed
$self->{obj}->delTemplate($template);
if ($self->{obj}->{error}) {
$self->{error}=3;
$self->{errorString}='delTemplate errored. error="'.$self->{obj}->{error}.'" errorstring="'.$self->{errorString}.'"';
warn($self->{module}.' '.$function.':'.$self->{error}.': '.$self->{errorString});
return undef;
}
#update the stuff
$self->updateTemplateList( $guiID );
return 1;
}
=head2 view
Invokes the view window.
$zcrssgtk->view;
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub view{
my $self=$_[0];
my $function='view';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
Gtk2->init;
my $guiID=$self->viewWindow({quitOnClose=>1, removeOnClose=>1});
$self->{gui}{$guiID}{window}->show;
Gtk2->main;
return 1;
}
=head2 viewVBox
This creates a VBox for the view GUI and returns the GUI ID of it.
=head3 args hash
=head4 disableQuit
If this is set to true, the quit selection under the misc functions menu will not be present.
my $guiID=$zcrssgtk->viewVBox;
if($zcrssgtk->{error}){
print "Error!\n";
}else{
$window=Gtk2::Window->new;
$window->set_default_size(750, 400);
$window->set_title('ZConf::RSS: view');
$window->add($zcrssgtk->{gui}{$guiID}{vbox});
}
=cut
sub viewVBox{
my $self=$_[0];
my %args;
if(defined($_[1])){
%args= %{$_[1]};
}
my $function='viewVBox';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
my %gui;
$gui{id}=rand().rand().rand();
#the main box
$gui{vbox}=Gtk2::VBox->new;
$gui{vbox}->show;
$gui{hpaned}=Gtk2::HPaned->new;
$gui{hpaned}->set_position(0);
$gui{hpaned}->show;
$gui{hpaned}->set_position(200);
$gui{vbox}->pack_start($gui{hpaned}, 1, 1, 0);
#puts together the basics for the list box
$gui{listbox}=Gtk2::VBox->new;
$gui{listbox}->show;
$gui{hpaned}->add1($gui{listbox});
#this is the menu bar that goes in the list box
$gui{menubar}=Gtk2::MenuBar->new;
$gui{menubarmenu}=Gtk2::MenuItem->new('_misc functions');
$gui{menubar}->show;
$gui{menubarmenu}->show;
$gui{menubar}->append($gui{menubarmenu});
$gui{listbox}->pack_start($gui{menubar}, 0, 1, 0);
#the window that holdes
$gui{listSW}=Gtk2::ScrolledWindow->new;
$gui{listSW}->show;
$gui{listbox}->pack_start($gui{listSW}, 1, 1, 0);
#puts the feed list stuff together
$gui{feedlist}=Gtk2::SimpleList->new(
'RSS feeds'=>'text',
);
$gui{feedlist}->get_selection->set_mode ('single');
$gui{feedlist}->show;
$gui{feedlist}->signal_connect(row_activated=>sub{
$_[3]{self}->updateFeed($_[3]{id});
},
{
self=>$self,
id=>$gui{id},
}
);
$gui{listSW}->add($gui{feedlist});
#puts together the menu
$gui{menu}=Gtk2::Menu->new;
$gui{menu}->show;
#add
$gui{menuAdd}=Gtk2::MenuItem->new('_add');
$gui{menuAdd}->show;
$gui{menuAdd}->signal_connect(activate=>sub{
$_[1]{self}->addFeedDialog({id=>$_[1]{id}});
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuAdd});
#modify
$gui{menuModify}=Gtk2::MenuItem->new('_modify');
$gui{menuModify}->show;
$gui{menuModify}->signal_connect(activate=>sub{
$_[1]{self}->modifyFeedDialog($_[1]{id});
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuModify});
#remove
$gui{menuRemove}=Gtk2::MenuItem->new('_remove');
$gui{menuRemove}->show;
$gui{menuRemove}->signal_connect(activate=>sub{
$_[1]{self}->removeFeedDialog($_[1]{id});
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuRemove});
#manage templates
$gui{menuManage}=Gtk2::MenuItem->new('manage _templates');
$gui{menuManage}->show;
$gui{menuManage}->signal_connect(activate=>sub{
my $guiID=$_[1]{self}->manageWindow({disableQuit=>1});
$_[1]{self}->{gui}{ $guiID }{window}->show;
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuManage});
#quit
if (!$args{disableQuit}) {
$gui{menuQuit}=Gtk2::MenuItem->new('_quit');
$gui{menuQuit}->show;
$gui{menuQuit}->signal_connect(activate=>sub{
Gtk2->main_quit;
},
{
id=>$gui{id},
self=>$self,
}
);
$gui{menu}->append($gui{menuQuit});
}
#attaches it
$gui{menubarmenu}->set_submenu($gui{menu});
#puts together the view section
$gui{text}=Gtk2::TextView->new;
$gui{text}->show;
$gui{text}->set_editable(0);
$gui{textSW}=Gtk2::ScrolledWindow->new;
$gui{textSW}->show;
$gui{textSW}->add($gui{text});
$gui{hpaned}->add2($gui{textSW});
$gui{buffer}=Gtk2::TextBuffer->new;
$gui{buffer}->set_text('');
$gui{text}->set_buffer($gui{buffer});
$self->{gui}{ $gui{id} }=\%gui;
#this updates it
$self->updateFeedList($gui{id});
return $gui{id};
}
=head2 viewWindow
This generates the view window.
=head3 args hash
=head4 disableQuit
Do not show the quit selection the menu.
=head4 quitOnClose
If the window is closed, quit the main GTK loop.
=head4 removeOnClose
Removes the GUI upon close.
Gtk2->init;
my $guiID=$zcrssgtk->viewWindow;
if($zcrssgtk->{error}){
print "Error!\n";
}else{
$zcrssgtk->{gui}{$guiID}{window}->show;
$Gtk2->main;
}
=cut
sub viewWindow{
my $self=$_[0];
my %args;
if(defined($_[1])){
%args= %{$_[1]};
}
my $function='viewWindow';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
my $guiID=$self->viewVBox({disableQuit=>$args{disableQuit}});
$self->{gui}{ $guiID }{window}=Gtk2::Window->new;
$self->{gui}{ $guiID }{window}->set_default_size(750, 400);
$self->{gui}{ $guiID }{window}->set_title('ZConf::RSS');
$self->{gui}{ $guiID }{window}->add($self->{gui}{$guiID}{vbox});
#handles the closure stuff
if ($args{quitOnClose}) {
$self->{gui}{ $guiID }{window}->signal_connect(delete_event=>sub{
Gtk2->main_quit;
}
);
}
if ($args{quitOnClose}) {
$self->{gui}{ $guiID }{window}->signal_connect(delete_event=>sub{
delete($self->{gui}{ $guiID });
}
);
}
return $guiID;
}
=head2 updateFeed
This updates the updates the feed view.
$zcrssgtk->updateFeed($guiID);
=cut
sub updateFeed{
my $self=$_[0];
my $guiID=$_[1];
my $function='updateFeed';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
my @selected=$self->{gui}{ $guiID }{feedlist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $feed=$self->{feeds}[$selected[0]];
my $feedString=$self->{obj}->getFeedAsTemplatedString($feed);
if (!defined($feedString)) {
return undef;
}
$self->{gui}{ $guiID }{buffer}->set_text($feedString);
$self->{gui}{ $guiID }{text}->set_buffer($self->{gui}{ $guiID }{buffer});
return 1;
}
=head2 updateFeedList
This updates the feed list.
$zcrssgtk->updateFeed($guiID);
=cut
sub updateFeedList{
my $self=$_[0];
my $guiID=$_[1];
my $function='updateFeedList';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
#get the feeds and sort them
my @feeds=$self->{obj}->listFeeds();
@feeds=sort(@feeds);
#make save it for later easy recall
$self->{feeds}=\@feeds;
#puts it together
my @int=0;
@{$self->{gui}{ $guiID }{feedlist}->{data}}=@feeds;
return 1;
}
=head2 updateTemplateList
This updates the template list for a manage window.
One arguement is required and it is a GUI ID for the manage window in question.
$zcrssgtk->updateTemplateList($guiID);
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub updateTemplateList{
my $self=$_[0];
my $guiID=$_[1];
my $function='updateFeedList';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
#get the feeds and sort them
my @templates=$self->{obj}->listTemplates();
@templates=sort(@templates);
#make save it for later easy recall
$self->{templates}=\@templates;
#puts it together
my @int=0;
@{$self->{gui}{ $guiID }{templatelist}->{data}}=@templates;
return 1;
}
=head2 updateTemplateText
This updates template text for the specified GUI ID.
$zcrssgtl->updateTemplateText($guiID);
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub updateTemplateText{
my $self=$_[0];
my $guiID=$_[1];
my @selected=$self->{gui}{ $guiID }{templatelist}->get_selected_indices;
if(!defined($selected[0])){
return undef;
}
my $template=$self->{templates}[$selected[0]];
my $templateText=$self->{obj}->getTemplate($template);
if (!defined($templateText)) {
return undef;
}
$self->{gui}{ $guiID }{buffer}->set_text($templateText);
$self->{gui}{ $guiID }{text}->set_buffer($self->{gui}{ $guiID }{buffer});
}
=head2 dialogs
This returns a array of available dialogs.
my @dialogs=$zcrssgtk->dialogs;
if($zcrssgtk->{error}){
print "Error!\n";
}
=cut
sub dialogs{
my $self=$_[0];
my $function='dialogs';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
return ('addFeed');
}
=head2 windows
This returns a array of available dialogs.
my @windows=$zcrssGui->windows;
if($zcrssGui->{error}){
print "Error!\n";
}
=cut
sub windows{
my $self=$_[0];
my $function='windows';
$self->errorblank;
if ($self->{error}) {
warn($self->{module}.' '.$function.': A permanent error is set. error="'.$self->{error}.'" errorString="'.$self->{errorString}.'"');
return undef;
}
return ('view', 'manage');
}
=head2 errorblank
This blanks the error storage and is only meant for internal usage.
It does the following.
$self->{error}=undef;
$self->{errorString}="";
=cut
#blanks the error flags
sub errorblank{
my $self=$_[0];
if ($self->{perror}) {
warn('ZConf-DevTemplate errorblank: A permanent error is set');
return undef;
}
$self->{error}=undef;
$self->{errorString}="";
return 1;
}
=head1 ERROR CODES
=head2 1
Failed to initiate ZConf::RSS.
=head2 2
Failed to initiate ZConf::GUI.
=head2 3
Adding the new feed failed.
=head2 4
No GUI ID specified.
=head2 5
Backend errored.
=head2 6
Removing the old feed for the purpose of renaming it failed.
=head1 WINDOWS
Please not that unless working directly and specifically with a backend, windows and dialogs
are effectively the same in that they don't return until the window exits, generally.
=head2 add
This adds a new a new feed.
=head2 manage
This allows the RSS feeds to be managed along with the templates.
=head2 view
This allows the RSS feeds to be viewed.
=head1 AUTHOR
Zane C. Bowers, C<< <vvelox at vvelox.net> >>
=head1 BUGS
Please report any bugs or feature requests to C<bug-zconf-devtemplate at rt.cpan.org>, or through
the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=ZConf-RSS-GUI-GTK>. I will be notified, and then you'll
automatically be notified of progress on your bug as I make changes.
=head1 SUPPORT
You can find documentation for this module with the perldoc command.
perldoc ZConf::RSS::GUI
You can also look for information at:
=over 4
=item * RT: CPAN's request tracker
L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=ZConf::RSS::GUI::GTK>
=item * AnnoCPAN: Annotated CPAN documentation
L<http://annocpan.org/dist/ZConf::RSS::GUI::GTK>
=item * CPAN Ratings
L<http://cpanratings.perl.org/d/ZConf::RSS::GUI::GTK>
=item * Search CPAN
L<http://search.cpan.org/dist/ZConf::RSS::GUI::GTK/>
=back
=head1 COPYRIGHT & LICENSE
Copyright 2009 Zane C. Bowers, all rights reserved.
This program is free software; you can redistribute it and/or modify it
under the same terms as Perl itself.
=cut
1; # End of ZConf::RSS::GUI