yarssr-0.2.2/0002755000175000017500000000000010216711763013246 5ustar leedoleedo00000000000000yarssr-0.2.2/Makefile0000644000175000017500000000225010216711763014703 0ustar leedoleedo00000000000000PREFIX=/usr/local BINDIR=$(PREFIX)/bin LIBDIR=$(PREFIX)/lib/yarssr DATADIR=$(PREFIX)/share LOCALEDIR=$(DATADIR)/locale LC_CATEGORY=LC_MESSAGES all: yarssr yarssr: @mkdir -p build perl -ne 's!\@PREFIX\@!$(PREFIX)!g ; s!\@LIBDIR\@!$(LIBDIR)!g ; print' < src/yarssr > build/yarssr mkdir -p build/locale/en/$(LC_CATEGORY) msgfmt -o build/locale/en/$(LC_CATEGORY)/yarssr.mo src/po/en.po install: mkdir -p $(DESTDIR)/$(BINDIR) \ $(DESTDIR)/$(DATADIR) \ $(DESTDIR)/$(LIBDIR) \ $(DESTDIR)/$(LOCALEDIR)/en/$(LC_CATEGORY) @echo Copying lib files to $(DESTDIR)/$(DATADIR): @cp -Rp lib/* $(DESTDIR)/$(LIBDIR)/ @echo Copying share files to $(DESTDIR)/$(DATADIR): @cp -Rp share/* $(DESTDIR)/$(DATADIR)/ find $(DESTDIR)/$(DATADIR) -type f -exec chmod 644 "{}" \; find $(DESTDIR)/$(LIBDIR) -type f -exec chmod 644 "{}" \; install -m 0644 build/locale/en/$(LC_CATEGORY)/yarssr.mo $(DESTDIR)/$(LOCALEDIR)/en/$(LC_CATEGORY)/ install -m 0755 build/yarssr $(DESTDIR)/$(BINDIR) clean: rm -rf build cleancvs: find . -name CVS -type d -exec rm -rf '{}' \; uninstall: rm -rf $(BINDIR)/yarssr \ $(LIBDIR) \ $(DATADIR)/yarssr .PHONY: all yarssr clean install uninstall yarssr-0.2.2/ChangeLog0000644000175000017500000000416110216711763015020 0ustar leedoleedo000000000000000.1.3 - UI fixes - Fixed browser configuration 0.1.4 - Now uses Gnome::VFS instead of LWP for retrieving (James Curbo) feeds. - Menu doesn't create blank spots if there aren't enough feed items to fill it. - Timer is properly created after the config is read. 0.1.5 - Feeds now display site icon, if available. - Option to use Gnome-default browser. - Better feed error checking. - Now uses proper dialogs for feed errors. 0.1.7 - New preferences interface. - Menu now indicates if an article has been read or not. - Config is saved at more approriate times, to avoide lost changes. - Gnome-default browser setting is saved properly. 0.1.8 - Added Gnome Session support. - Changed icon from check mark to a red dot. - launch_url() now forks the browser before it updates. the menu. This results in the browser launching much faster. - Icons added for 'Update' menu items, as well for Prefs and Quit. - Browser command now accepts switches to the browser, and %s can be used to pass the URL to the browser as well. (Dan Lenski) - List of feeds in the Preferences is now sortable. 0.1.9 - No more empty child processes left behind by browser fork. 0.2.0 - Saves a feeds state, so feeds stay marked as read between sessions. - Marks new articles with a solid dot and unread articles with a hollow dot. - Menu marks feeds with new articles with bold text. - Feed properties are editable - Able to enable/disable all feeds by clicking the column header - Support for Atom feeds added. - Tray icon notifies you if there are new items in the menu - Tray icon has a tooltip with number of new items - Right-clicking the icon brings up a seperate menu with preferences, about, and quit. Left-click brings up a menu with the feeds. 0.2.1 - Feed state stored more often and more consistantly - Error icon shown in menu if feed fails to download or parse - Online and offline mode added 0.2.2 - A few Online/Offline mode fixeds - Username/Password option added to feeds - OPML importing added - Fixed bug that was leaving zombie processes after browsers were closed - il8n support added yarssr-0.2.2/INSTALL0000644000175000017500000000030410216711763014272 0ustar leedoleedo00000000000000To build yarssr simply run make and to install yarssr run make install This will install in /usr/local/bin by default. Use PREFIX to install elsewhere. e.g. PREFIX=/usr && make install yarssr-0.2.2/README0000644000175000017500000000110710216711763014123 0ustar leedoleedo00000000000000Yarssr, a RSS reader for the GNOME Tray Author: Lee Aylward http://yarssr.sf.net Perl Module requirements: XML::RSS Gtk2 (http://gtk2-perl.sf.net) Gtk2::GladeXML Gtk2::TrayIcon Gnome2::VFS Gnome2 >= 0.94 Check INSTALL file for installation directions. Just run 'yarssr'. Wait while it retrieves the RSS feeds, and then displays them in the tray. Preferences allows you to add feeds and adjust the interval time. If you have problems upgrading from an old version of Yarssr, it is recommended that you delete your ~/.yarssr directory. Improvements welcome. yarssr-0.2.2/TODO0000644000175000017500000000035010216711763013732 0ustar leedoleedo00000000000000TODO: - Make menu work while updating - Individual update intervals for feeds - Add translations Long term: - Clean the feed/items object interfaces - Add different parsing and downloading backends - Document what each module does yarssr-0.2.2/lib/0002755000175000017500000000000010216712035014005 5ustar leedoleedo00000000000000yarssr-0.2.2/lib/Yarssr/0002755000175000017500000000000010216712042015266 5ustar leedoleedo00000000000000yarssr-0.2.2/lib/Yarssr/Config.pm0000644000175000017500000001461110216711763017043 0ustar leedoleedo00000000000000package Yarssr::Config; use Yarssr; use Yarssr::Feed; use Data::Dumper; use warnings; use strict; our $configdir = $ENV{HOME}.'/.yarssr/'; our $icondir = $configdir.'icons/'; my $statedir = $configdir.'state/'; my $config = $configdir.'config'; my $options; my $timer; sub init { my $class = shift; $options = load_config(); if ($options->{'startonline'}) { $timer = Glib::Timeout->add($options->{'interval'} * 60_000, sub{ Yarssr->download_all; 1; }); } } sub load_config { Yarssr->log_debug(_("Loading previous session")); my $return = { browser => 'mozilla', interval => 60, usegnome => 0, maxfeeds => 8, online => 1, startonline => 1, }; if (! -e $configdir) { mkdir $configdir or warn "Failed to make config directory: $!\n"; } if (! -e $icondir) { mkdir $icondir or warn "Failed to make icons directory: $!\n"; } if (! -e $statedir) { mkdir $statedir or warn "Failed to make state directory: $!\n"; } if (-e $config) { open (CONFIG,"<",$config) or warn "Failed to open config file for reading: $!\n"; while() { chomp; if (/^feed=(.*);(.*);(\d)(?:;(.*):(.*))?/) { my $feed = Yarssr::Feed->new( url => $1, title => $2, enabled => $3, username => $4, password => $5, ); Yarssr->add_feed($feed); #load_state($feed); } elsif (/^interval=(\d+)/) { $return->{interval} = $1; } elsif (/^maxfeeds=(\d+)/) { $return->{maxfeeds} = $1; } elsif (/^browser=(.*)/) { $return->{browser} = $1; } elsif (/^usegnome=(\d)/) { $return->{usegnome} = $1; } elsif (/^startonline=(\d)/) { $return->{startonline} = $1; $return->{online} = $return->{startonline}; } } close(CONFIG) } Yarssr->log_debug(_("Successfully loaded config")); return $return; } sub write_config { Yarssr->log_debug(_("Writing config")); open (CONFIG,">",$config) or warn "Failed to open config file for writing: $!\n"; print CONFIG "interval=".$options->{'interval'}."\n"; print CONFIG "maxfeeds=".$options->{'maxfeeds'}."\n"; print CONFIG "browser=".$options->{'browser'}."\n"; print CONFIG "usegnome=".$options->{'usegnome'}."\n"; print CONFIG "startonline=".$options->{'startonline'}."\n"; for my $feed (Yarssr->get_feeds_array) { print CONFIG "feed=".$feed->get_url.";".$feed->get_title. ";".$feed->get_enabled.";".$feed->get_username.":". $feed->get_password."\n"; } close(CONFIG); } sub write_states { for (Yarssr->get_feeds_array) { write_state(undef,$_); } } sub write_state { my (undef,$feed) = @_; if (! -e $statedir) { mkdir $statedir or warn "Failed to make statefile directory: $!\n"; } Yarssr->log_debug(_("Writing state for {feed}",feed => $feed->get_title)); my $rss = new XML::RSS (version => '1.0'); $rss->channel( title => $feed->get_title, link => $feed->get_url, ); my $count = 0; for my $item ($feed->get_items_array) { # Limit number of items per feed to save last if $count++ >= 100; my $status = 1; $status = 2 if ( $item->get_status > 1); $rss->add_item( title => $item->get_title, link => $item->get_url, dc => { description => "read: ".$status, }, ); } open RSS,">:utf8",$statedir.$feed->get_title.".xml"; print RSS $rss->as_string or die $!; close RSS; return 0; } sub load_initial_state { for (Yarssr->get_feeds_array) { load_state($_); } Yarssr->log_debug(_("Successfully loaded previous session")); } sub load_state { my $feed = shift; my $file = $statedir.$feed->get_title.".xml"; if (-e $file) { Yarssr->log_debug(_("Loading state for {feed}",feed => $feed->get_title)); my $rss = new XML::RSS; eval { $rss->parsefile($file) }; return if $@; for (@{$rss->{'items'}}) { $_->{dc}{description} =~ /read: (\d)$/; my $read = $1; my $item = Yarssr::Item->new( title => $_->{'title'}, url => $_->{'link'}, parent => $feed, ); $item->set_status($read); $feed->add_item($item); } } } sub set_maxfeeds { my $class = shift; my $maxfeeds = shift; if ($maxfeeds != $options->{'maxfeeds'}) { $options->{'maxfeeds'} = $maxfeeds; } } sub set_interval { my $class = shift; my $interval = shift; Yarssr->log_debug(_("Updating interval timer")); $options->{'interval'} = $interval; if ($options->{online}) { Glib::Source->remove($timer) if $timer; $timer = Glib::Timeout->add($interval * 60_000, sub{ Yarssr->download_all; Yarssr::GUI->redraw_menu; 1; }); } } sub set_browser { my $class = shift; $options->{'browser'} = shift; } sub set_usegnome { my $class = shift; $options->{'usegnome'} = shift; } sub process { my $class = shift; my ($new_interval,$new_maxfeeds,$new_browser, $new_usegnome,$newfeedlist,$online) = @_; my $rebuild = 0; $options->{'browser'} = $new_browser; $options->{'usegnome'} = $new_usegnome; if ($online) { $options->{'startonline'} = 1; } else { $options->{'startonline'} = 0; } if ($new_interval != $options->{'interval'}) { set_interval(undef,$new_interval); } for my $url (keys %{$newfeedlist}) { my $feed; # If this feed doesn't exists add it unless ($feed = Yarssr->get_feed_by_url($url)) { $feed = Yarssr::Feed->new( url => $url, title => $newfeedlist->{$url}[0], enabled => 0, username => $newfeedlist->{$url}[2], password => $newfeedlist->{$url}[3], ); Yarssr->add_feed($feed); } unless ($feed->get_enabled == $newfeedlist->{$url}[1]) { $feed->toggle_enabled if $feed->get_enabled != 3; $feed->update if $feed->get_enabled and $options->{'online'}; $rebuild = 1; } } for my $feed (Yarssr->get_feeds_array) { unless (exists $newfeedlist->{$feed->get_url}) { Yarssr->remove_feed($feed); $rebuild = 1; } } $rebuild = 1 if ($options->{'maxfeeds'} != $new_maxfeeds); $options->{'maxfeeds'} = $new_maxfeeds; return $rebuild; } sub quit { write_config(); write_states(); } no strict; foreach my $field (qw(browser usegnome interval maxfeeds online startonline)) { *{"get_$field"} = sub { return $options->{$field}; }; } sub set_online { my $class = shift; my $bool = shift; $options->{online} = $bool; if ($bool) { Yarssr->log_debug(_("Online mode")); Yarssr->download_all; set_interval(undef,$options->{interval}); } else { Yarssr->log_debug(_("Offline mode")); if ($timer) { Glib::Source->remove($timer); $timer = undef; } } } 1; yarssr-0.2.2/lib/Yarssr/Feed.pm0000644000175000017500000000657410216711763016512 0ustar leedoleedo00000000000000package Yarssr::Feed; use Yarssr::Fetcher; use Yarssr::FeedIcon; use constant TRUE=>1; sub new { my $class = shift; my %settings = @_; my $self = {%settings}; $self->{'items'} = []; $self->{'newitems'} = 0; $self->{'siteurl'} = ''; $self->{'menu'} = undef; # attribute to keep track of status of the # feed. # # 0 = no error # 1 = downloading error # 2 = parsing error $self->{'error'} = 0; bless $self, $class; $self->{'icon'} = Yarssr::FeedIcon->new($self); return $self; } sub get_items_array { my $self = shift; return @{$self->{'items'}}; } sub add_item { my $self = shift; my $item = shift; ref $item eq 'Yarssr::Item' or die; push @{$self->{'items'}}, $item; #reverse(sort {$a->get_date <=> $b->get_date} @{$self->{'items'}}); } sub unshift_item { my $self = shift; my $item = shift; ref $item eq 'Yarssr::Item' or die; unshift @{$self->{'items'}}, $item; #reverse(sort {$a->get_date <=> $b->get_date} @{$self->{'items'}}); } foreach my $field (qw(title date url interval menu username password)) { *{"get_$field"} = sub { my $self = shift; defined $self->{$field} ? return $self->{$field} : return ""; }; *{"set_$field"} = sub { my $self = shift; $self->{$field} = shift; return 1; }; } sub get_icon { my $self = shift; return $self->{'icon'}->get_pixbuf; } sub update_icon { my $self = shift; $self->{'icon'}->update; } sub enable { my $self = shift; $self->{'enabled'} = 1; return 1; } sub disable { my $self = shift; $self->{'enabled'} = 0; return 1; } sub enable_and_flag { my $self = shift; $self->{'enabled'} = 3; return 1; } sub toggle_enabled { my $self = shift; if ($self->{'enabled'}) { $self->disable; } else { $self->enable; } } sub new_menu { my $self = shift; $self->{'menu'} = undef; $self->{'menu'} = Gtk2::Menu->new; } sub check_url { my $self = shift; my $url = shift; for (@{$self->{'items'}}) { return 1 if $_->get_url eq $url; } return 0; } sub get_enabled { my $self = shift; return $self->{'enabled'}; } sub update { my $self = shift; my @items; # Set new items as unread for ($self->get_items_array) { $_->set_status(2) if $_->get_status > 2; } $self->reset_newitems(); $self->enable if ($self->get_enabled == 3); my $content = Yarssr::Fetcher->fetch_feed($self); # If download is successful if ($content) { $self->{status} = 0; unless (@items = Yarssr::Parser->parse($self,$content)) { $self->{status} = 2; } for my $item (reverse @items) { Yarssr::GUI->gui_update; unless ($self->get_item_by_url($item->get_url)) { $self->unshift_item($item); $item->set_parent($self); } } return 1; } # If download fails $self->{status} = 1; return 0; } sub get_status { my $self = shift; return $self->{status}; } sub get_item_by_url { my $self = shift; my $url = shift; for (@{$self->{'items'}}) { return $_ if $_->get_url eq $url; } return 0; } sub add_newitem { my $self = shift; return ++$self->{'newitems'}; } sub subtract_newitem { my $self = shift; return --$self->{'newitems'}; } sub get_newitems { my $self = shift; return $self->{'newitems'}; } sub reset_newitems { my $self = shift; $self->{'newitems'} = 0; } sub clear_newitems { my $self = shift; for ($self->get_items_array) { $_->set_status(2) if $_->get_status > 2; } } 1; yarssr-0.2.2/lib/Yarssr/FeedIcon.pm0000644000175000017500000000236510216711763017315 0ustar leedoleedo00000000000000package Yarssr::FeedIcon; use Yarssr::Fetcher; sub new { my $class = shift; my $feed = shift; my $icondir = $Yarssr::Config::icondir; my $self = { iconfile => $icondir.$feed->get_title.".ico", url => $feed->get_url, }; bless $self,$class; # If we can't load an icon from a file, then try to download one # and attempt to load from the file again unless ($self->load_icon()) { $self->update; $self->load_icon; } return $self; } sub get_pixbuf { my $self = shift; return $self->{'pixbuf'}; } sub update { my $self = shift; my ($content,$type) = Yarssr::Fetcher->fetch_icon($self->{'url'}); open(ICO,'>',$self->{'iconfile'}) or warn "Could not open icon file: $self->{'iconfile'}\n"; if ($type ne 'text/html' and $content) { print ICO $content; close(ICO); } else { print ICO ""; close(ICO); } } sub load_icon { my $self = shift; if (! -e $self->{'iconfile'}) { $self->{'pixbuf'} = 0; return 0; } my $pixbuf = $self->{'pixbuf'}; eval { $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file($self->{'iconfile'}); $pixbuf = $pixbuf->scale_simple(16,16,'bilinear') if ($pixbuf->get_height != 16); }; $self->{'pixbuf'} = $pixbuf; return 1; } 1; yarssr-0.2.2/lib/Yarssr/Fetcher.pm0000644000175000017500000000327310216711763017220 0ustar leedoleedo00000000000000package Yarssr::Fetcher; use Yarssr::Parser; use Gnome2::VFS (-init); use Gtk2; use constant TRUE=>1,FALSE=>0; sub fetch_feed { my ($pkg,$feed) = @_; ref $feed eq 'Yarssr::Feed' or die; my $login = [ $feed->get_username,$feed->get_password ]; Yarssr->log_debug("Downloading ".$feed->get_title); my ($content,$error) = _download($feed->get_url,$login); if ($content) { return $content; } else { Yarssr->log_debug("failed ($error)"); return 0; } } sub fetch_icon { my ($pkg,$url) = @_; caller eq 'Yarssr::FeedIcon' or die; my $uri = Gnome2::VFS::URI->new($url); if ($uri->get_host_name) { $url = 'http://'.$uri->get_host_name.'/favicon.ico'; my ($content,$type) = _download($url); return ($content,$type); } return (0,0); } sub fetch_opml { my ($pkg,$url) = @_; caller eq 'Yarssr::GUI' or die; Yarssr->log_debug("Importing OPML from $url"); my ($content,$type) = _download($url); return ($content,$type); } sub _download { my ($url,$login) = @_; caller eq __PACKAGE__ or die; my ($result, $handle, $info); my $uri = Gnome2::VFS::URI->new($url); if ($login->[0] and $login->[1]) { $uri->set_user_name($login->[0]); $uri->set_password($login->[1]); } ($result, $handle) = $uri->open('read'); return (0,$result) unless ($result eq 'ok'); my $bytes_per_iteration = 1024; my $content = ''; while ($result eq 'ok') { my ($tmp_buffer); ($result, undef, $tmp_buffer) = $handle->read($bytes_per_iteration); if ($result eq 'ok') { $content .= $tmp_buffer; } else { last; } } ($result,$info) = $handle->get_file_info('default'); my $type = $info->get_mime_type; $result = $handle->close(); return $content,$type; } 1; yarssr-0.2.2/lib/Yarssr/GUI.pm0000644000175000017500000004675210216711763016275 0ustar leedoleedo00000000000000package Yarssr::GUI; use Gtk2; use Gtk2::GladeXML; use Gtk2::SimpleList; use Gtk2::TrayIcon; use Gnome2; use Yarssr::Config; use Yarssr::Parser; use Yarssr::Fetcher; use Data::Dumper; use POSIX ":sys_wait_h"; use constant TRUE=>1,FALSE=>0; my $prefs_window; my $import_dialog; my $add_dialog; my $prop_dialog; my $gld; my $menu; my $pref_menu; my $menu_x; my $menu_y; my $icon; my $eventbox; my $tooltips; my $treeview; my $paper_grey_pixbuf; my $paper_red_pixbuf; my $paper_green_pixbuf; my $dot_full_red_pixbuf; my $dot_hollow_red_pixbuf; my $blank_pixbuf; my $tray_image; sub init { my $class = shift; Gtk2->init; my $imagedir = $Yarssr::PREFIX."/share/yarssr/pixmaps/"; $paper_grey_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."paper_grey.xpm"); $paper_red_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."paper_red.xpm"); $paper_green_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."paper_green.xpm"); $dot_full_red_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."full_red.xpm"); $dot_hollow_red_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."hollow_red.xpm"); $blank_pixbuf = Gtk2::Gdk::Pixbuf->new_from_file( $imagedir."blank.xpm"); $tray_image = Gtk2::Image->new_from_pixbuf( $paper_grey_pixbuf); $tooltips = Gtk2::Tooltips->new; # Create tray inon my $tray = Gtk2::TrayIcon->new("rss"); $eventbox = Gtk2::EventBox->new; $eventbox->add($tray_image); set_icon_active(); $tray->add($eventbox); $tray->show_all; Gtk2->main_iteration while Gtk2->events_pending; $gld = Gtk2::GladeXML->new($Yarssr::PREFIX.'/share/yarssr/yarssr.glade'); $gld->signal_autoconnect_from_package('main'); $prefs_window = $gld->get_widget('window_prefs'); $prefs_window->signal_connect('delete-event'=>\&delete_event); $import_dialog = $gld->get_widget('import_dialog'); $import_dialog->set_transient_for($prefs_window); $import_dialog->signal_connect('delete-event'=>\&delete_event); $feedinfo_dialog = $gld->get_widget('feed_dialog'); $feedinfo_dialog->set_transient_for($prefs_window); $feedinfo_dialog->signal_connect('delete-event'=>\&delete_event); $treeview = Gtk2::TreeView->new; $treeview->set_rules_hint(1); my $icon = Gtk2::TreeViewColumn->new_with_attributes( '',Gtk2::CellRendererPixbuf->new,pixbuf => 0); my $title = Gtk2::TreeViewColumn->new_with_attributes( _("Name"),Gtk2::CellRendererText->new,text => 1); my $toggle = Gtk2::CellRendererToggle->new; $toggle->set_data( column => 2); $toggle->signal_connect(toggled => \&item_toggled); my $enabled = Gtk2::TreeViewColumn->new_with_attributes( _("Enabled"),$toggle,active => 2); $enabled->set_clickable(1); $enabled->signal_connect('clicked',\&enabled_header_clicked); my $text = Gtk2::CellRendererText->new; #$text->set_fixed_size(150,-1); my $location = Gtk2::TreeViewColumn->new_with_attributes( _("Address"),$text,text => 3); for (($icon,$title,$enabled,$location)) { $treeview->append_column($_); } my $scrolledwindow = $gld->get_widget('scrolledwindow_feeds'); $scrolledwindow->add($treeview); create_prefs_menu(); Gtk2->main; }; sub delete_event { $_[0]->hide; feedinfo_dialog_clear(); return 1; } sub set_icon_active { $eventbox->signal_handlers_disconnect_by_func(\&handle_button_press); $eventbox->signal_connect("button-press-event", \&ignore_button_press); $tray_image->set_from_pixbuf($paper_red_pixbuf); set_tooltip(_("updating...")); gui_update(); } sub set_icon_inactive { $eventbox->signal_handlers_disconnect_by_func(\&ignore_button_press); $eventbox->signal_connect("button-press-event", \&handle_button_press); my $newitems = Yarssr->get_total_newitems; if ($newitems) { $tray_image->set_from_pixbuf($paper_green_pixbuf); } else { $tray_image->set_from_pixbuf($paper_grey_pixbuf); } set_tooltip($newitems." new links since last update"); gui_update(); } sub launch_url { my $url = shift; if (Yarssr::Config->get_usegnome) { Gnome2::URL->show($url); } else { if ($child = fork) { Glib::Idle->add( sub { my $kid = waitpid($child,WNOHANG); $kid > 0 ? return 0 : return 1; } ); } else { my $b = Yarssr::Config->get_browser; $b .= " \"$url\"" unless $b =~ s/\%s/"$url"/; exec($b) or warn "unable to launch browser\n"; exit; } } } sub prefs_show { my $liststore = Gtk2::ListStore->new( "Gtk2::Gdk::Pixbuf", #icon "Glib::String", #title "Glib::Boolean",#enabled "Glib::String", #url "Glib::String", #username "Glib::String", #password ); $treeview->set_model($liststore); for (Yarssr->get_feeds_array) { my $pixbuf = undef; if ($_->get_status) { my $invis = Gtk2::Invisible->new; $pixbuf = $invis->render_icon('gtk-dialog-warning','menu') } my $iter = $liststore->append; $liststore->set($iter, 0 => $pixbuf, 1 => $_->get_title, 2 => $_->get_enabled, 3 => $_->get_url, 4 => defined $_->get_username ? $_->get_username : 0, 5 => defined $_->get_password ? $_->get_password : 0, ); } $gld->get_widget('interval_entry')->set_text(Yarssr::Config->get_interval); $gld->get_widget('headings_entry')->set_text(Yarssr::Config->get_maxfeeds); $gld->get_widget('browser_entry')->set_text(Yarssr::Config->get_browser); $gld->get_widget('browser_entry')->set_sensitive( !Yarssr::Config->get_usegnome); $gld->get_widget('use_default_browser_checkbox')->set_active( Yarssr::Config->get_usegnome); $gld->get_widget('start_online_checkbutton')->set_active( Yarssr::Config->get_startonline); $gld->get_widget('pref_ok_button')->grab_focus; $prefs_window->show_all; } sub item_toggled { my ($cell,$path_str) = @_; my $liststore = $treeview->get_model; my $path = Gtk2::TreePath->new_from_string($path_str); my $column = $cell->get_data('column'); my $iter = $liststore->get_iter($path); my $toggled_item = $liststore->get($iter,$column); $toggled_item ^= 1; $liststore->set($iter,$column,$toggled_item); } sub on_pref_cancel_button_clicked { $treeview->set_model(undef); $prefs_window->hide; $feedinfo_dialog->hide; } sub on_pref_ok_button_clicked { my $liststore = $treeview->get_model; $gld->get_widget('pref_ok_button')->signal_handlers_disconnect_by_func( \&on_pref_ok_button_clicked); $gld->get_widget('pref_cancel_button')->signal_handlers_disconnect_by_func( \&on_pref_cancel_button_clicked); $prefs_window->hide; Gtk2->main_iteration while Gtk2->events_pending; my $interval = $gld->get_widget('interval_entry')->get_text; my $maxfeeds = $gld->get_widget('headings_entry')->get_text; my $browser = $gld->get_widget('browser_entry')->get_text; my $online = $gld->get_widget('start_online_checkbutton')->get_active; my $usegnome; if ($gld->get_widget('use_default_browser_checkbox')->get_active) { $usegnome = 1; } else { $usegnome = 0; } my $newfeedlist; $liststore->foreach( sub { my ($model,$path,$iter) = @_; my (undef,$title,$enabled,$url,$user,$pass) = $liststore->get($iter); $newfeedlist->{$url} = [ $title,$enabled,$user,$pass ]; return 0; }); set_icon_active(); redraw_menu() if Yarssr::Config->process( $interval,$maxfeeds,$browser,$usegnome,$newfeedlist,$online); Yarssr::Config->write_config; set_icon_inactive(); $treeview->set_model(undef); } sub on_use_default_browser_checkbox_toggled { my ($widget, $window) = @_; my $browser_entry = $gld->get_widget('browser_entry'); if ($widget->get_active) { $browser_entry->set_sensitive(0); } else { $browser_entry->set_sensitive(1); } } sub on_add_button_clicked { my $ok_button = $gld->get_widget('feedinfo_ok_button'); $ok_button->signal_handlers_disconnect_by_func(\&properties_change); $ok_button->signal_connect('clicked',\&feedinfo_add); $feedinfo_dialog->show_all; } sub on_remove_button_clicked { my @rows = $treeview->get_selection->get_selected_rows; my $liststore = $treeview->get_model; for (reverse @rows) { my $iter = $liststore->get_iter($_); $liststore->remove($iter); } } sub feedinfo_add { my $title = $gld->get_widget('feedinfo_name'); my $url = $gld->get_widget('feedinfo_address'); my $username = $gld->get_widget('feedinfo_username'); my $password = $gld->get_widget('feedinfo_password'); my $ok_button = $gld->get_widget('feedinfo_ok_button'); my $liststore = $treeview->get_model; $ok_button->signal_handlers_disconnect_by_func(\&feedinfo_add); unless (Yarssr->get_feed_by_url($url->get_text) or Yarssr->get_feed_by_title($title->get_text)) { my $iter = $liststore->append; $liststore->set($iter, 0 => undef, 1 => $title->get_text, 2 => 1, 3 => $url->get_text, 4 => $username->get_text, 5 => $password->get_text, ); } feedinfo_dialog_clear(); $feedinfo_dialog->hide; } sub feedinfo_dialog_clear { $gld->get_widget('feedinfo_name')->set_text(''); $gld->get_widget('feedinfo_address')->set_text(''); $gld->get_widget('feedinfo_username')->set_text(''); $gld->get_widget('feedinfo_password')->set_text(''); $gld->get_widget('feedinfo_options')->set_expanded(0); } sub on_feedinfo_cancel_button_clicked { feedinfo_dialog_clear(); $feedinfo_dialog->hide; } sub redraw_menu { my $class = shift; Yarssr->log_debug(_("Rebuilding menu")); for my $feed (Yarssr->get_feeds_array) { Gtk2->main_iteration while Gtk2->events_pending; create_feed_menu($feed) if $feed->get_enabled; } create_root_menu(); set_icon_inactive(); } sub create_root_menu { $menu = Gtk2::Menu->new; my $refresh = Gtk2::ImageMenuItem->new_from_stock('gtk-refresh'); my $clear_new = Gtk2::ImageMenuItem->new(_("_Unmark new")); $clear_new->set_image(Gtk2::Image->new_from_stock('gtk-clear','menu')); $refresh->signal_connect('activate',sub { $menu->popdown; Gtk2->main_iteration while Gtk2->events_pending; Yarssr->download_all; }); $clear_new->signal_connect('activate',sub { Yarssr->clear_newitems; redraw_menu(); }); $menu->append($refresh); $menu->append($clear_new); $menu->append(Gtk2::SeparatorMenuItem->new); for my $feed (Yarssr->get_feeds_array) { if ($feed->get_enabled) { my $title = $feed->get_title; my $menuitem = Gtk2::ImageMenuItem->new($title); $menuitem->child->set_markup("$title") if $feed->get_newitems; if (defined $feed->get_status and $feed->get_status > 0) { my $image = Gtk2::Image->new_from_stock('gtk-dialog-warning','menu'); $menuitem->set_image($image); } elsif (my $icon = $feed->get_icon) { my $image = Gtk2::Image->new_from_pixbuf($icon); $menuitem->set_image($image); } $menuitem->set_submenu($feed->get_menu); $menu->append($menuitem); } } $menu->show_all; return $menu; } sub create_prefs_menu { $pref_menu = Gtk2::Menu->new; my $quit = Gtk2::ImageMenuItem->new_from_stock('gtk-quit'); my $prefs = Gtk2::ImageMenuItem->new_from_stock('gtk-preferences'); my $about = Gtk2::ImageMenuItem->new(_("_About")); my $online; my $imageroot = $Yarssr::PREFIX."/share/yarssr/pixmaps/"; if (Yarssr::Config->get_online) { $online = Gtk2::ImageMenuItem->new(_("Go _Offline")); my $image = Gtk2::Image->new_from_file($imageroot."disconnect.png"); $online->set_image($image); $online->signal_connect('activate', sub { Yarssr::Config->set_online(0); create_prefs_menu(); }); } else { $online = Gtk2::ImageMenuItem->new(_("Go _Online")); my $image = Gtk2::Image->new_from_file($imageroot."connect.png"); $online->set_image($image); $online->signal_connect('activate', sub { Yarssr::Config->set_online(1); create_prefs_menu(); }); } $about->set_image(Gtk2::Image->new_from_stock('gnome-stock-about','menu')); $about->signal_connect('activate',\&on_about_button_clicked); $prefs->signal_connect('activate',\&prefs_show); $quit->signal_connect('activate',sub { Yarssr::Config->write_config; Yarssr::Config->write_states; exit;}); $pref_menu->append($online); $pref_menu->append($prefs); $pref_menu->append($about); $pref_menu->append($quit); $pref_menu->show_all; return $prefs_menu; } sub create_feed_menu { my $feed = shift; $feed->new_menu; my $feedcounter = 0; my $image; $feed->reset_newitems; foreach my $item ($feed->get_items_array) { Gtk2->main_iteration while Gtk2->events_pending; last if $feedcounter == Yarssr::Config->get_maxfeeds; $feedcounter++; # Clean up the title for the menu my $title; $title = $item->get_title or $title = '-no title-'; $title =~ s/\n//g; $title =~ s/(?:\s+|\t+)/ /g; my @title = split /(.{42}.+?)\s/,$title; shift @title if $title[0] eq ''; $title = shift @title; foreach (@title) { next if $_ eq ''; $title .= "\n"; $title .= $_; } my $menuitem = Gtk2::ImageMenuItem->new($title); my $status = $item->get_status; if ($status == 4 or $status == 3) { $image = Gtk2::Image->new_from_pixbuf( $dot_full_red_pixbuf); $menuitem->set_image($image); $item->set_status(3); $feed->add_newitem; } elsif ($status == 2) { $image = Gtk2::Image->new_from_pixbuf( $dot_hollow_red_pixbuf); $menuitem->set_image($image); } else { $image = Gtk2::Image->new_from_pixbuf( $blank_pixbuf); $item->set_status(1); $menuitem->set_image($image); } $menuitem->signal_connect('activate',sub { menuitem_clicked($menuitem,$item); }); $feed->get_menu->append($menuitem); } $feed->get_menu->append(Gtk2::SeparatorMenuItem->new); my $update = Gtk2::ImageMenuItem->new(_("Update this feed")); $update->set_image(Gtk2::Image->new_from_stock('gtk-refresh','menu')); $update->signal_connect('activate',sub{ set_icon_active(); Yarssr->download_feed($feed); redraw_menu(); set_icon_inactive(); }); $feed->get_menu->append($update); $feed->get_menu->show_all; } sub menuitem_clicked { my $menuitem = shift; my $item = shift; launch_url($item->get_url); my $feed = $item->get_parent; my $status = $item->get_status; unless ($status == 1) { Glib::Idle->add(sub { Yarssr::Config->write_state($feed); }); } my $newitems; if ($status > 2) { $newitesms = $feed->subtract_newitem(); } $item->set_status(1); redraw_menu() unless $newitems; set_tooltip(Yarssr->get_total_newitems." new links since last update"); my $image = Gtk2::Image->new_from_pixbuf($blank_pixbuf); $menuitem->set_image($image); } sub get_menu { return $menu->{menu}; } sub ignore_button_press { Yarssr->log_debug("Menu is disabled while updating"); return 1; } sub handle_button_press { my $widget = shift; my $event = shift; $menu_x = $event->x_root - $event->x; $menu_y = $event->y_root - $event->y; if ($event->button == 1) { $menu->popup(undef,undef,\&position_menu,0,$event->button,$event->time) } else { $pref_menu->popup(undef,undef,\&position_menu,0,$event->button,$event->time); } } sub position_menu { # Shamlessly stolen from Muine :-) my $x = $menu_x; my $y = $menu_y; my $monitor = $menu->get_screen->get_monitor_at_point($x,$y); my $rect = $menu->get_screen->get_monitor_geometry($monitor); my $space_above = $y - $rect->y; my $space_below = $rect->y + $rect->height - $y; my $requisition = $menu->size_request(); if ($requisition->height <= $space_above || $requisition->height <= $space_below) { if ($requisition->height <= $space_below) { $y = $y + $eventbox->allocation->height; } else { $y = $y - $requisition->height; } } elsif ($requisition->height > $space_below and $requisition->height > $space_above) { if ($space_below >= $space_above) { $y = $rect->y + $rect->height - $requisition->height; } else { $y = $rect->y; } } else { $y = $rect->y; } return ($x,$y,1); } sub gui_update { Gtk2->main_iteration while Gtk2->events_pending; } sub on_about_button_clicked { my $logo = $paper_grey_pixbuf->scale_simple(64,64,'tiles'); my $author = "$Yarssr::AUTHOR\n\n Patches from:\n"; $author .= "\t$_\n" for @Yarssr::COAUTHORS; $author .= "\n$_" for @Yarssr::TESTERS; my $about = Gnome2::About->new( $Yarssr::NAME,$Yarssr::VERSION,$Yarssr::LICENSE, $Yarssr::URL,$author,undef,undef,$logo); $about->show; } sub on_properties_button_clicked { my @paths = $treeview->get_selection->get_selected_rows; return unless @paths; my $liststore = $treeview->get_model; my $iter = $liststore->get_iter($paths[0]); my @row = $liststore->get($iter); $gld->get_widget('feedinfo_name')->set_text($row[1]); $gld->get_widget('feedinfo_address')->set_text($row[3]); $gld->get_widget('feedinfo_username')->set_text($row[4]); $gld->get_widget('feedinfo_password')->set_text($row[5]); my $ok_button = $gld->get_widget('feedinfo_ok_button'); $ok_button->signal_handlers_disconnect_by_func(\&feedinfo_add); $ok_button->signal_connect('clicked', \&properties_change,$paths[0]); $feedinfo_dialog->show_all; } sub properties_change { my (undef,$path) = @_; my $liststore = $treeview->get_model; my $iter = $liststore->get_iter($path); my @row = $liststore->get($iter); my $feed = Yarssr->get_feed_by_url($row[3]); my $ok_button = $gld->get_widget('feedinfo_ok_button'); $ok_button->signal_handlers_disconnect_by_func(\&properties_change); $feedinfo_dialog->hide; my $new_title = $gld->get_widget('feedinfo_name')->get_text; my $new_url = $gld->get_widget('feedinfo_address')->get_text; my $new_username = $gld->get_widget('feedinfo_username')->get_text; my $new_password = $gld->get_widget('feedinfo_password')->get_text; $liststore->set($iter, 1 => $new_title, 3 => $new_url, 4 => $new_username, 5 => $new_password, ); # Need to figure out some way to apply this only when # the user clicks Ok. if ($feed) { $feed->set_title($new_title); $feed->set_url($new_url); $feed->set_username($new_username); $feed->set_password($new_password); $feed->enable_and_flag if $feed->get_enabled; } feedinfo_dialog_clear(); } sub enabled_header_clicked { my $liststore = $treeview->get_model; my $total = $liststore->iter_n_children; my $enabled = 0; $liststore->foreach(sub{$enabled+=$_[0]->get($_[2], 2);0;}); my $bool = 1; $bool = 0 if $total == $enabled; $liststore->foreach(sub{$_[0]->set($_[2],2,$bool);0;}); } sub set_tooltip { my $text = shift; $tooltips->set_tip($eventbox,$text); } sub quit { Gtk2->main_quit; } sub on_import_button_clicked { my $widget = shift; $import_dialog->show_all; } sub on_import_ok_button_clicked { my $widget = shift; my $url = $gld->get_widget('import_url_entry')->get_text; my ($content,$type) = Yarssr::Fetcher->fetch_opml($url); my $feeds = Yarssr::Parser->parse_opml($content); my $model = $treeview->get_model; for ( @{ $feeds }) { my $iter = $model->append; $model->set($iter, 0 => undef, 1 => $_->{title}, 2 => 1, 3 => $_->{url}, ); } close_import_dialog(); } sub close_import_dialog { $gld->get_widget('import_url_entry')->set_text(''); $import_dialog->hide; } sub on_import_path_button_clicked { my $chooser = Gtk2::FileChooserDialog->new("OPML File",$import_dialog,'open', 'gtk-cancel' => 'cancel', 'gtk-ok' => 'ok', ); my $filter = Gtk2::FileFilter->new; $filter->add_pattern('*.xml'); $filter->add_pattern('*.rdf'); $filter->add_pattern('*.opml'); $filter->set_name('OPML Files'); my $filter2 = Gtk2::FileFilter->new; $filter2->add_pattern('*.*'); $filter2->set_name('All Files'); $chooser->add_filter($filter); $chooser->add_filter($filter2); if ('ok' eq $chooser->run) { my $uri = $chooser->get_uri; $gld->get_widget('import_url_entry')->set_text($uri); } $chooser->destroy; } 1; yarssr-0.2.2/lib/Yarssr/Item.pm0000644000175000017500000000101010216711763016521 0ustar leedoleedo00000000000000package Yarssr::Item; sub new { my $class = shift; my (%options) = @_; my $self = { %options }; # Item status used to determine which icon # to use when creating the menu # 4 = not added to the menu yet # 3 = new # 2 = unread # 1 = read $self->{'status'} = 4; bless $self,$class; } foreach my $field (qw(title url status parent)) { *{"get_$field"} = sub { my $self = shift; return $self->{$field}; }; *{"set_$field"} = sub { my $self = shift; $self->{$field} = shift; return 1; }; } 1; yarssr-0.2.2/lib/Yarssr/Parser.pm0000644000175000017500000000466710216711763017104 0ustar leedoleedo00000000000000package Yarssr::Parser; use Data::Dumper; use Yarssr::Item; use Yarssr::Feed; use XML::Parser; use XML::RSS; $XML::RSS::AUTO_ADD = 1; sub parse { push @XML::Parser::Expat::Encoding_Path, $Yarssr::PREFIX."/share/yarssr/encfiles"; my (undef,$feed,$content) = @_; Yarssr->log_debug("Parsing ".$feed->get_title); my $parser = new XML::Parser(Style => Tree); my $parsetree = eval{ $parser->parse($content) }; if ($@) { Yarssr->log_debug($@); return; } if ($parsetree->[0] eq "rss" or $parsetree->[0] eq "rdf" or $parsetree->[0] eq "rdf:RDF") { return parse_rss($feed,$content); } elsif ($parsetree->[0] eq "feed") { return parse_atom($feed,$parsetree); } } sub parse_rss { my ($feed,$content) = @_; my @items; my $parser = new XML::RSS; eval { $parser->parse($content); }; if ($@) { Yarssr->log_debug($@); return; } else { for my $count (0 .. $#{$parser->{'items'}}) { my $item = ${$parser->{'items'}}[$count]; my $link = $item->{'link'}; $link = $item->{'guid'} unless $link; # Fix amperstands $link =~ s/&/&/g; my $article = Yarssr::Item->new( url => $link, title => $item->{'title'}, ); push @items, $article; } } return @items; } sub parse_atom { my ($feed,$tree) = @_; my @items; for (my $i = 0;$i < $#{$tree->[1]};$i++) { if ($tree->[1][$i] eq "entry") { my $item = $tree->[1][++$i]; my ($title,$link); for (my $j=0;$j < $#{$item};$j++) { if ($item->[$j] eq "title") { $title = $item->[++$j][$#{$item->[$j]}]; $title =~ s/^\s*(.*)\s*$/$1/; } elsif ($item->[$j] eq "link" and $item->[++$j][0]{'rel'} eq "alternate") { $link = $item->[$j][0]{'href'}; } } if ($title and $link) { my $article = Yarssr::Item->new( title => $title, url => $link, ); push @items,$article; } } } return @items; } sub parse_opml { my ($class,$content) = @_; my @feeds; my $parser = new XML::Parser(Style => Tree); my $tree = eval{ $parser->parse($content) }; if ($@) { Yarssr->log_debug($@); return; } for (my $i = 0;$i < $#{$tree->[1]};$i++) { if ($tree->[1][$i] eq "body") { my $body = $tree->[1][++$i]; for (my $j = 0;$j < $#{$body};$j++) { if ($body->[$j] eq "outline") { my $item = $body->[++$j]; my $feed = { title => $item->[0]{text}, url => $item->[0]{xmlUrl}, }; push @feeds, $feed; } } } } return \@feeds; } 1; yarssr-0.2.2/lib/Yarssr.pm0000644000175000017500000000603610216711763015640 0ustar leedoleedo00000000000000package Yarssr; use Gnome2; use Gtk2; use Yarssr::GUI; use Yarssr::Config; use Locale::gettext; use POSIX qw/setlocale/; use base 'Exporter'; use vars qw( $LIBDIR $PREFIX $NAME $VERSION $AUTHOR @CO_AUTHORS $URL $LICENSE); our $NAME = 'yarssr'; our $VERSION = '0.2.2'; our $LICENSE = 'GNU General Public License (GPL)'; our $URL = 'http://yarssr.sf.net'; our $AUTHOR = "Lee Aylward"; our @COAUTHORS = ( "James Curbo","Dan Leski" ); our @TESTERS = ( "Thanks to Joachim Breitner for testing\n". "and maintaining the Debian package"); our $debug = 0; our @EXPORT_OK = qw(_); my $feeds = (); $0 = $NAME; # il8n stuff my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG}); setlocale(LC_ALL, $locale); bindtextdomain(lc($NAME), sprintf('%s/share/locale', $PREFIX)); textdomain(lc($NAME)); sub init { # Wait 2 seconds before loading config and begining downloads Gnome2::Program->init($0,$VERSION); Glib::Timeout->add(1000,\&initial_launch); Yarssr::Config->init; Yarssr::GUI->init; } sub quit { Yarssr::Config->quit; Yarssr::GUI->quit; } sub log_debug { return unless $debug; my ($sec,$min,$hour,undef) = localtime; my $time = sprintf("%02d:%02d:%02d",$hour,$min,$sec); print STDERR "[$time] $_[1]\n" if -t; } sub initial_launch { Yarssr::Config->load_initial_state; Glib::Timeout->add(300, sub { 1 }); if (Yarssr::Config->get_startonline) { download_all(); } else { Yarssr::GUI->redraw_menu; } return 0; } sub add_feed { my (undef,$feed) = @_; ref $feed eq 'Yarssr::Feed' or die; return 0 if (Yarssr->get_feed_by_url($feed->get_url) and Yarssr->get_feed_by_title($feed->get_title)); push @feeds,$feed; @feeds = sort { lc $a->get_title cmp lc $b->get_title} @feeds; return 1; } sub get_feeds_array { return @feeds; } sub download_feed { my (undef,$feed) = @_; $feed->update; } sub download_all { Yarssr::GUI->set_icon_active; for my $feed (@feeds) { Yarssr::GUI->gui_update; $feed->update if $feed->get_enabled; } Yarssr::GUI->redraw_menu; Yarssr::Config->write_states; return 1; } sub get_feed_by_url { my (undef,$url) = @_; for (@feeds) { return $_ if $_->get_url eq $url; } return 0; } sub get_feed_by_title { my (undef,$title) = @_; for (@feeds) { return $_ if $_->get_title eq $title; } return 0; } sub remove_feed { my (undef,$feed) = @_; die unless ref $feed eq 'Yarssr::Feed'; for (0 .. $#feeds) { if ($feeds[$_]->get_title eq $feed->get_title) { splice @feeds,$_,1; $feed = undef; last; } } } sub get_total_newitems { my $newitems = 0; for (@feeds) { $newitems += $_->get_newitems; } return $newitems; } sub newitems_exist { for (@feeds) { return 1 if $_->get_newitems; } return 1; } sub clear_newitems { for (@feeds) { $_->clear_newitems; $_->reset_newitems; } } sub _ { my $str = shift; my %params = @_; my $translated = gettext($str); if (scalar(keys(%params)) > 0) { foreach my $key (keys %params) { $translated =~ s/\{$key\}/$params{$key}/g; } } return $translated; } 1; yarssr-0.2.2/share/0002755000175000017500000000000010216711763014350 5ustar leedoleedo00000000000000yarssr-0.2.2/share/yarssr/0002755000175000017500000000000010216711763015673 5ustar leedoleedo00000000000000yarssr-0.2.2/share/yarssr/encfiles/0002755000175000017500000000000010216712441017455 5ustar leedoleedo00000000000000yarssr-0.2.2/share/yarssr/encfiles/iso-8859-15.enc0000644000175000017500000000206010216712351021570 0ustar leedoleedo00000000000000ISO-8859-15  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ `a}~RSxyarssr-0.2.2/share/yarssr/yarssr.glade0000644000175000017500000011146010216711763020215 0ustar leedoleedo00000000000000 6 Yarssr - Preferences GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER False 500 550 True False True False False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST True True False 6 True False 0 True True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC GTK_SHADOW_IN GTK_CORNER_TOP_LEFT 0 True True True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_ICONS True True True Properties gtk-properties True True False False True True Delete gtk-delete True True False False True True True True True False False True New gtk-new True True False False True True Import OPML True gtk-copy True True False False True 0 False False 0 True True True 3 3 False 6 6 True False 0 True Update Interval (min) : False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False 0 1 0 1 fill fill True False 0 True Headings Displayed : False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False 0 1 1 2 fill fill True False 0 True Browser Command : (%s for URL) False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False 0 1 2 3 fill fill True False 0 True True True True 0 True * False 6 0 False True 1 2 1 2 fill fill True True Gnome Default True GTK_RELIEF_NORMAL True False False True 2 3 2 3 True True True True 0 True * False 1 2 2 3 fill True False 6 True False 0 True True True True 0 True * False 6 0 False False 0 False True True True Start Online True GTK_RELIEF_NORMAL True False False True 0 False False 1 2 0 1 fill fill 0 False False True GTK_BUTTONBOX_END 6 True True True gtk-cancel True GTK_RELIEF_NORMAL True True True True True True gtk-ok True GTK_RELIEF_NORMAL True 6 False False 300 Feed Info GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT False 300 False False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_NORTH_WEST True True True False 0 True GTK_BUTTONBOX_END True True True gtk-cancel True GTK_RELIEF_NORMAL True -6 True True True True gtk-ok True GTK_RELIEF_NORMAL True -5 0 False True GTK_PACK_END 6 True 2 2 False 6 6 True True True True 0 True * False 1 2 1 2 True Address: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 1 2 fill True True True True True True True 0 True * True 1 2 0 1 True Name: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 0 1 fill 0 False False True True False 0 6 True 2 2 False 6 6 True True True True 0 True * False 1 2 1 2 True Password: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 1 2 fill True True True True True True True 0 True * True 1 2 0 1 True Username: False False GTK_JUSTIFY_LEFT False False 0 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 1 0 1 fill True Options False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 label_item 0 True True Import OPML GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER_ON_PARENT False True False True False False GDK_WINDOW_TYPE_HINT_DIALOG GDK_GRAVITY_NORTH_WEST True True True False 0 True GTK_BUTTONBOX_END True True True gtk-cancel True GTK_RELIEF_NORMAL True -6 True True True gtk-ok True GTK_RELIEF_NORMAL True -5 0 False True GTK_PACK_END 6 True False 6 True Path or URL False False GTK_JUSTIFY_LEFT False False 0.5 0.5 0 0 PANGO_ELLIPSIZE_NONE -1 False 0 0 False False True True True True 0 True * False 0 True True True True GTK_RELIEF_NONE True True gtk-open 4 0.5 0.5 0 0 0 False False 0 False False yarssr-0.2.2/share/yarssr/pixmaps/0002755000175000017500000000000010216711763017354 5ustar leedoleedo00000000000000yarssr-0.2.2/share/yarssr/pixmaps/blank.xpm0000644000175000017500000000060210216711763021165 0ustar leedoleedo00000000000000/* XPM */ static char * blank_xpm[] = { "16 16 1 1", " c None", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "}; yarssr-0.2.2/share/yarssr/pixmaps/connect.png0000644000175000017500000000116310216711763021512 0ustar leedoleedo00000000000000PNG  IHDR $bKGD pHYs  tIME&toҀIDAT8ݓOhQ uVb)AHPE!EBԓEC<"5TX+Ds f>_V7=d=TCMz o!!À Bh-BhKh;~ŎvDl~5_@z''ZsJuvt]޶@\J^Z*ӓd}|lhgoΫ]r)*D;"+ܙSH2\/95KNO6#|bKo3gk8'o@ȤSf]\vh5wf58zwvb 3x?=Rxmč}9󧠏!SsT*k<A>{cALM$wl{JGmÇNݻhN͑7 P^<\VsH[cqbH}82@wɫ[?KF`v&)!%B>Lv[ *G5?tq]*$`mEIENDB`yarssr-0.2.2/share/yarssr/pixmaps/full_red.xpm0000644000175000017500000000164410216711763021701 0ustar leedoleedo00000000000000/* XPM */ static char * blank_xpm[] = { "16 16 28 1", " c None", ". c #A70505", "+ c #AA0606", "@ c #AE0808", "# c #AF0808", "$ c #B50B0B", "% c #B60B0B", "& c #B90C0C", "* c #BB0E0E", "= c #BC0E0E", "- c #BE1010", "; c #BF1010", "> c #C01111", ", c #C11212", "' c #C31414", ") c #C41515", "! c #C51616", "~ c #C61818", "{ c #C91C1C", "] c #CA1D1D", "^ c #CB1E1E", "/ c #CF2626", "( c #D12828", "_ c #D73535", ": c #D83838", "< c #DC4040", "[ c #DF4A4A", "} c #E86B6B", " ", " ", " ", " ", " ", " '!)> ", " )(<:~= ", " )_}[]* ", " ,^/{,$ ", " &;-*%+ ", " ##@. ", " ", " ", " ", " ", " "}; yarssr-0.2.2/share/yarssr/pixmaps/hollow_red.xpm0000644000175000017500000000077310216711763022245 0ustar leedoleedo00000000000000/* XPM */ static char * hollow_xpm[] = { "16 16 9 1", " c None", ". c #761010", "+ c #771111", "@ c #7E1919", "# c #781212", "$ c #740E0E", "% c #710C0C", "& c #6C0C0C", "* c #6E0C0C", " ", " ", " ", " ", " ", " .+.. ", " .@ #$ ", " . $ ", " . % ", " $$ %& ", " ***& ", " ", " ", " ", " ", " "}; yarssr-0.2.2/share/yarssr/pixmaps/paper_green.xpm0000644000175000017500000000206610216711763022373 0ustar leedoleedo00000000000000/* XPM */ static char * paper_green_xpm[] = { "26 24 20 1", " c None", ". c #136600", "+ c #A2E193", "@ c #000000", "# c #072600", "$ c #082C00", "% c #0E4A00", "& c #2E8E18", "* c #1C8105", "= c #1B8004", "- c #146C00", "; c #105400", "> c #0F5200", ", c #A0E193", "' c #0B3C00", ") c #0A3E00", "! c #0E4800", "~ c #82D66E", "{ c #010800", "] c #51BF3B", " ", " ", " ........................ ", " .++++++++++++++++++++++. ", " .++@+@++@@@++@@@++@@@++. ", " .++@+@++@+@++@+@++@+@++. ", " .++@@@++@@@++@@+++@@+++. ", " .+++@+++@+@++@+@++@+@++. ", " .+++@+++@+@++@+@++@+@++. ", " .+@@@@@@@@@@@@@@@@@@@@+. ", " #++++++++++++++++++++++# ", " #++++++++++++++++++++++# ", " #+$$$$$$$$$$+%%%%%%%%%+# ", " #++++++++++++%&&&&&&&%+# ", " #+$$$$$$$$$$+%******=%+# ", " #++++++++++++%-------%+# ", " #+$$$$$$$$$$+$;;>;>;;%+# ", " #,,,,,,,,,,,,$'')''''!,# ", " #~%%%%%%%%%%~$$$$$$$$%~# ", " #{]]]]]]]]]]]]]]]]]]]]{# ", " {{{{{{{{{{{{{{{{{{{{{{ ", " ", " ", " "}; yarssr-0.2.2/share/yarssr/pixmaps/paper_grey.xpm0000644000175000017500000000253410216711763022241 0ustar leedoleedo00000000000000/* XPM */ static char * blank_xpm[] = { "26 24 30 1", " c None", ". c #808080", "+ c #F4F4E9", "@ c #000000", "# c #515151", "$ c #555551", "% c #6F6F6A", "& c #A3A39F", "* c #939390", "= c #929390", "- c #848381", "; c #848480", "> c #838381", ", c #838480", "' c #838380", ") c #747471", "! c #737471", "~ c #747371", "{ c #747470", "] c #F3F3E8", "^ c #646461", "/ c #646561", "( c #656561", "_ c #656460", ": c #646460", "< c #656461", "[ c #6B6B66", "} c #E3E3D9", "| c #252525", "1 c #C8C8BF", " ", " ", " ........................ ", " .++++++++++++++++++++++. ", " .++@+@++@@@++@@@++@@@++. ", " .++@+@++@+@++@+@++@+@++. ", " .++@@@++@@@++@@+++@@+++. ", " .+++@+++@+@++@+@++@+@++. ", " .+++@+++@+@++@+@++@+@++. ", " .+@@@@@@@@@@@@@@@@@@@@+. ", " #++++++++++++++++++++++# ", " #++++++++++++++++++++++# ", " #+$$$$$$$$$$+%%%%%%%%%+# ", " #++++++++++++%&&&&&&&%+# ", " #+$$$$$$$$$$+%******=%+# ", " #++++++++++++%-;>,'>'%+# ", " #+$$$$$$$$$$+$)!~{~){%+# ", " #]]]]]]]]]]]]$^/(_:<:[]# ", " #}%%%%%%%%%%}$$$$$$$$%}# ", " #|11111111111111111111|# ", " |||||||||||||||||||||| ", " ", " ", " "}; yarssr-0.2.2/share/yarssr/pixmaps/paper_red.xpm0000644000175000017500000000225010216711763022040 0ustar leedoleedo00000000000000/* XPM */ static char * blank_xpm[] = { "26 24 21 1", " c None", ". c #984848", "+ c #F1D6D6", "@ c #000000", "# c #5C3434", "$ c #360000", "% c #5D0000", "& c #A12A2A", "* c #911313", "= c #901212", "- c #820202", "; c #650000", "> c #640000", ", c #F0D3D3", "' c #490000", ") c #4B0000", "! c #4C0000", "~ c #580000", "{ c #E0A9A9", "] c #1B0909", "^ c #C56B6B", " ", " ", " ........................ ", " .++++++++++++++++++++++. ", " .++@+@++@@@++@@@++@@@++. ", " .++@+@++@+@++@+@++@+@++. ", " #++@@@++@@@++@@+++@@+++. ", " #+++@+++@+@++@+@++@+@++. ", " #+++@+++@+@++@+@++@+@++# ", " #+@@@@@@@@@@@@@@@@@@@@+# ", " #++++++++++++++++++++++# ", " #++++++++++++++++++++++# ", " #+$$$$$$$$$$+%%%%%%%%%+# ", " #++++++++++++%&&&&&&&%+# ", " #+$$$$$$$$$$+%******=%+# ", " #++++++++++++%-------%+# ", " #+$$$$$$$$$$+$;;>;>;;%+# ", " #,,,,,,,,,,,,$')!)')'~,# ", " #{%%%%%%%%%%{$$$$$$$$%{# ", " #]^^^^^^^^^^^^^^^^^^^^]# ", " ]]]]]]]]]]]]]]]]]]]]]] ", " ", " ", " "}; yarssr-0.2.2/src/0002755000175000017500000000000010216711763014035 5ustar leedoleedo00000000000000yarssr-0.2.2/src/po/0002755000175000017500000000000010216711763014453 5ustar leedoleedo00000000000000yarssr-0.2.2/src/po/en.po0000644000175000017500000000322310216711763015413 0ustar leedoleedo00000000000000# English translation of yarssr. # Lee Aylward , 2005. # # msgid "" msgstr "" "Project-Id-Version: yarssr 0.2.2\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2005-03-01 22:42-0500\n" "PO-Revision-Date: 2005-03-01 22:43-0500\n" "Last-Translator: Lee Aylward \n" "Language-Team: English\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: " #, fuzzy msgid "Loading state for {feed}" msgstr "Donging {feed}" msgid "Downloading {feed}" msgstr "" msgid "Writing state for {feed}" msgstr "" # debug messages msgid "Loading previous session" msgstr "" msgid "Successfully loaded previous session" msgstr "" msgid "Successfully loaded config" msgstr "" msgid "Rebuilding menu" msgstr "" msgid "Parsing" msgstr "" msgid "Writing config" msgstr "" # Prefs menu msgid "Go _Online" msgstr "" msgid "Go _Offline" msgstr "" msgid "_About" msgstr "" # Feeds menu msgid "updating..." msgstr "" msgid "_Unmark new" msgstr "" msgid "Update this feed" msgstr "" # Prefs window msgid "Name" msgstr "" msgid "Enabled" msgstr "" msgid "Address" msgstr "" msgid "Properties" msgstr "" msgid "Delete" msgstr "" msgid "New" msgstr "" msgid "Import OPML" msgstr "" msgid "Update Interval (min)" msgstr "" msgid "Start Online" msgstr "" msgid "Headings Displayed" msgstr "" msgid "Browser Command:\n (%s for URL)" msgstr "" msgid "Gnome Default" msgstr "" msgid "Cancel" msgstr "" msgid "OK" msgstr "" msgid "Name:" msgstr "" msgid "Address:" msgstr "" msgid "Options" msgstr "" msgid "Username:" msgstr "" msgid "Password:" msgstr "" msgid "Path or URL" msgstr "" yarssr-0.2.2/src/yarssr0000755000175000017500000000147610216711763015314 0ustar leedoleedo00000000000000#!/usr/bin/perl # # yarssr - Yet Another RSS Reader # Copyright (C) 2004 Lee Aylward # http://yarssr.sourceforge.net # Licensed under the GPL # use strict; use lib (-e '@LIBDIR@' ? '@LIBDIR@' : $ENV{PWD}.'/lib/'); use Yarssr; use Getopt::Long; my $result = GetOptions ("debug" => \$Yarssr::debug, "help" => \&show_help, "version" => \&show_version); $SIG{TERM} = \&Yarssr::quit; $SIG{INT} = \&Yarssr::quit; $Yarssr::PREFIX = (-e '@PREFIX@' ? '@PREFIX@' : $ENV{PWD}); $Yarssr::LIBDIR = (-e '@LIBDIR@' ? '@LIBDIR@' : $ENV{PWD}.'/lib'); Yarssr->init; sub show_help { print <