The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# Copyright 2007, 2008, 2009, 2010, 2011, 2013, 2014 Kevin Ryde

# This file is part of Chart.
#
# Chart 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 3, or (at your option) any later version.
#
# Chart 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 Chart.  If not, see <http://www.gnu.org/licenses/>.

package App::Chart::Gtk2::SymlistRadioMenu;
use 5.010;
use strict;
use warnings;
use Carp;
use Gtk2;

use App::Chart::Gtk2::Symlist;
use App::Chart::Gtk2::SymlistListModel (qw(COL_KEY COL_NAME));

# uncomment this to run the ### lines
#use Smart::Comments;

use Gtk2::Ex::MenuView;
use Glib::Object::Subclass
  'Gtk2::Ex::MenuView',
  signals => { activate => \&_do_activate,
               item_create_or_update => \&_do_item_create_or_update,
             },
  properties => [ Glib::ParamSpec->object
                  ('symlist',
                   'symlist',
                   'App::Chart::Gtk2::Symlist object selected, or undef.',
                   'App::Chart::Gtk2::Symlist',
                   Glib::G_PARAM_READWRITE),
                ];

sub INIT_INSTANCE {
  my ($self) = @_;

  my $model = App::Chart::Gtk2::SymlistListModel->instance;

  # hack for extra constructed lists ...
  #
  my @symlists = grep {$_->isa('App::Chart::Gtk2::Symlist::Join')}
    values %App::Chart::Gtk2::Symlist::instances;
  if (@symlists) {
    @symlists = sort {$a->key cmp $b->key} @symlists;
    require Gtk2::Ex::TreeModelBits;
    my $store = Gtk2::ListStore->new
      (Gtk2::Ex::TreeModelBits::all_column_types ($model));
    my $pos = 0;
    foreach my $symlist (@symlists) {
      ###   extra: COL_KEY.' '.COL_NAME.' '.$symlist->key
      $store->insert_with_values ($pos++,
                                  COL_KEY, $symlist->key,
                                  COL_NAME, $symlist->name);
    }
    require Gtk2::Ex::ListModelConcat;
    $model = Gtk2::Ex::ListModelConcat->new (models => [ $store, $model ]);
  }

  $self->set (model => $model);
}

sub SET_PROPERTY {
  my ($self, $pspec, $newval) = @_;
  my $pname = $pspec->get_name;
  if ($pspec->get_name eq 'symlist') {
    $self->set_symlist ($newval);
  } else {
    $self->{$pname} = $newval;  # per default GET_PROPERTY
  }
}

sub _do_item_create_or_update {
  my ($self, $item, $model, $path, $iter) = @_;
  ### SymlistRadioMenu _do_item_create_or_update: $item
  if (! $item) {
    my $group = ($self->get_children)[0];
    $item = Gtk2::RadioMenuItem->new_with_label($group,'');
  }
  ###   label: $item->get_child
  $item->get_child->set_text ($model->get_value ($iter, COL_NAME));
  return $item;
}

# 'activate' signal handler on items
sub _do_activate {
  my ($self, $item, $model, $path, $iter) = @_;
  if ($item->get_active) {
    my $key = $model->get_value ($iter, COL_KEY);
    $self->{'symlist'} = App::Chart::Gtk2::Symlist->new_from_key ($key);
    $self->notify ('symlist');
  }
}

sub set_symlist {
  my ($self, $symlist) = @_;
  ### SymlistRadioMenu set_symlist: $self->get_name." to ".($symlist||'[none]')
  if (($self->{'symlist'}||0) == ($symlist||0)) { return; }

  my $key = $symlist->key;
  my $model = $self->get('model');
  my $pos = liststore_find_pos
    ($model, sub { my ($model, $path, $iter) = @_;
                   return ($model->get_value($iter, COL_KEY) eq $key); });
  if (! defined $pos) {
    croak "SymlistRadioMenu: no such list for set_symlist: $key";
  }

  $self->{'symlist'} = undef;
  require Glib::Ex::FreezeNotify;
  { my $freezer = Glib::Ex::FreezeNotify->new ($self);
    if (my $item = $self->item_at_indices ($pos)) {
      $item->set_active (1);
    }
    $self->notify ('symlist');
  }
}

sub liststore_find_pos {
  my ($store, $subr) = @_;
  my $pos;
  $store->foreach (sub {
                     my ($store, $path, $iter) = @_;
                     if ($subr->($store, $path, $iter)) {
                       ($pos) = $path->get_indices;
                       return 1; # stop;
                     }
                     return 0; # continue;
                   });
  return $pos;
}

1;
__END__

=for stopwords symlists undef symlist programmatically

=head1 NAME

App::Chart::Gtk2::SymlistRadioMenu -- radio menu of symbol lists

=head1 SYNOPSIS

 use App::Chart::Gtk2::SymlistRadioMenu;
 my $menu = App::Chart::Gtk2::SymlistRadioMenu->new;

=head1 WIDGET HIERARCHY

C<App::Chart::Gtk2::SymlistRadioMenu> is a subclass of C<Gtk2::Ex::MenuView>,

    Gtk2::Widget
      Gtk2::Container
        Gtk2::MenuShell
          Gtk2::Menu
            Gtk2::Ex::MenuView
              App::Chart::Gtk2::SymlistRadioMenu

=head1 DESCRIPTION

A C<App::Chart::Gtk2::SymlistRadioMenu> displays a menu of the available symlists
with C<Gtk2::RadioMenuItem>s, allowing the user to select one of them.  The
currently selected list is in the C<symlist> property.

    +--------------+
    | * Alerts     |
    +--------------+
    |   Favourites |
    +--------------+
    |   All        |
    +--------------+
    |   Historical |
    +--------------+

=head1 FUNCTIONS

=over 4

=item C<< App::Chart::Gtk2::SymlistRadioMenu->new (key=>value,...) >>

Create and return a new C<App::Chart::Gtk2::SymlistRadioMenu> object.  Optional
key/value pairs set initial properties as per C<< Glib::Object->new >>.

=back

=head1 PROPERTIES

=over 4

=item C<symlist> (C<App::Chart::Gtk2::Symlist>, default undef)

The currently selected symlist in the menu radio buttons, or undef for none
selected.  This changes when the user selects a new list, and it can also be
set programmatically.

=back

=head1 SEE ALSO

L<App::Chart::Gtk2::Symlist>, L<Gtk2::Menu>, L<Gtk2::RadioMenuItem>

=cut