# Copyright 2010, 2011, 2012, 2017 Kevin Ryde

# This file is part of Glib-Ex-ConnectProperties.
#
# Glib-Ex-ConnectProperties 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.
#
# Glib-Ex-ConnectProperties 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 Glib-Ex-ConnectProperties.  If not, see <http://www.gnu.org/licenses/>.


package Glib::Ex::ConnectProperties::Element::widget;
use 5.008;
use strict;
use warnings;
use Carp;
use Glib;
use Gtk2;
use base 'Glib::Ex::ConnectProperties::Element';

our $VERSION = 20;


# For reference, among the various "-set" and "-changed" signals,
#
#    parent-set   - already a "parent" property
#
# Other possibilities:
#     widget#screen-nodefault      only when has-screen
#     
#     widget#mapped
#     widget-flags#mapped
#         change on map-event, unmap-event, or unmap action?
#
#     widget-style#pname         style-set prop readable
#     widget-style#fg.normal     writable modify-fg
#     widget-style#property.foo
#     widget-style-property#pname     style-set prop readable
#

my %pspecs = (direction => Glib::ParamSpec->enum ('direction',
                                                  'direction',
                                                  '', # blurb
                                                  'Gtk2::TextDirection',
                                                  'none', # default, unused
                                                  Glib::G_PARAM_READWRITE),
              state => Glib::ParamSpec->enum ('state',
                                              'state',
                                              '', # blurb
                                              'Gtk2::StateType',
                                              'normal', # default, unused
                                              Glib::G_PARAM_READWRITE),
              toplevel => Glib::ParamSpec->object ('toplevel',
                                                   'toplevel',
                                                   '', # blurb
                                                   'Gtk2::Widget',
                                                   'readable'),
             );
my $pspec_screen_writable;
if (Gtk2::Widget->can('get_screen')) {
  # get_screen() new in Gtk 2.2
  $pspecs{'screen'} = Glib::ParamSpec->object ('screen',
                                               'screen',
                                               '', # blurb
                                               'Gtk2::Gdk::Screen',
                                               'readable');
  $pspec_screen_writable = Glib::ParamSpec->object ('screen',
                                                    'screen',
                                                    '', # blurb
                                                    'Gtk2::Gdk::Screen',
                                                    Glib::G_PARAM_READWRITE);
}
if (Gtk2::Widget->can('has_screen')) {
  # has_screen() new in Gtk 2.2
  $pspecs{'has-screen'} = Glib::ParamSpec->boolean ('has-screen',
                                                    'has-screen',
                                                    '', # blurb
                                                    0, # default
                                                    'readable');
}
sub find_property {
  my ($self) = @_;
  my $pname = $self->{'pname'};
  if ($pname eq 'screen' && $self->{'object'}->can('set_screen')) {
    return $pspec_screen_writable;
  }
  return $pspecs{$pname};
}

my %read_signal = ('has-screen' => 'screen-changed',
                   toplevel     => 'hierarchy-changed');
sub read_signal {
  my ($self) = @_;
  my $pname = $self->{'pname'};
  return ($read_signal{$pname} || "$pname-changed");
}

my %get_method = ('has-screen' => 'has_screen',
                  toplevel     => \&_widget_get_toplevel,
                  (Gtk2::Widget->can('get_state') # new in Gtk 2.18
                   ? ()
                   : (state => 'state')),  # otherwise field directly
                 );

sub get_value {
  my ($self) = @_;
  my $pname = $self->{'pname'};
  my $get_method = $get_method{$pname} || "get_$pname";
  return $self->{'object'}->$get_method;
}
sub set_value {
  my ($self, $newval) = @_;
  my $set_method = "set_$self->{'pname'}";
  return $self->{'object'}->$set_method ($newval);
}

sub _widget_get_toplevel {
  my ($widget) = @_;
  my $toplevel;
  return (($toplevel = $widget->get_toplevel) && $toplevel->flags & 'toplevel'
          ? $toplevel
          : undef);
}

1;
__END__

=for stopwords Glib-Ex-ConnectProperties ConnectProperties ltr rtl toplevel Gtk prelight Ryde

=head1 NAME

Glib::Ex::ConnectProperties::Element::widget -- various widget attributes

=for test_synopsis my ($widget,$another);

=head1 SYNOPSIS

 Glib::Ex::ConnectProperties->new([$widget, 'widget#direction'],
                                  [$another, 'something']);

=head1 DESCRIPTION

This element class implements ConnectProperties access to the following
attributes of a L<Gtk2::Widget>,

    widget#direction      Gtk2::TextDirection enum, ltr or rtl
    widget#screen         Gtk2::Gdk::Screen
    widget#has-screen     boolean, read-only
    widget#state          Gtk2::StateType enum
    widget#toplevel       Gtk2::Window or undef, read-only

These things are not available as widget properties as such (though perhaps
they could have been) but instead have get/set methods and report changes
with specific signals.

=over

=item *

C<widget#direction> is the "ltr" or "rtl" text direction, per
C<get_direction()> and C<set_direction()> methods.

If "none" is set then C<get_direction()> gives back "ltr" or "rtl" following
the global default.  Storing "none" with ConnectProperties probably won't
work very well, except to a forced C<write_only> target so that it's not
read back.

=item *

C<widget#screen> uses the C<get_screen()> method.  This means it will give
the default screen until the widget is added to a toplevel C<Gtk2::Window>
or similar to determine the screen.

C<widget#screen> is read-only for most widgets, but is writable for anything
with a C<set_screen()> such as C<Gtk2::Menu>.  There's a plain C<screen>
property on C<Gtk2::Window> so it doesn't need this special
C<widget#screen>, but other widgets benefit.

C<Gtk2::Gdk::Screen> is new in Gtk 2.2 and C<widget#screen> and
C<widget#has-screen> are not available in Gtk 2.0.x.

=item *

C<widget#state> is the C<state()> / C<set_state()> condition, such as
"normal" or "prelight".

Note that storing "insensitive" doesn't work very well, since a subsequent
setting back to "normal" doesn't turn the sensitive flag back on.  Perhaps
this will change in the future, so as to actually enforce the desired new
state.

=item *

C<widget#toplevel> is the widget ancestor with C<toplevel> flag set, or
C<undef> if none.  This is C<get_toplevel()> plus its recommended
C<< $parent->toplevel() >> flag check.  The C<hierarchy-changed> signal
indicates a change to the toplevel.

    Glib::Ex::ConnectProperties->new
      ([$toolitem, 'widget#toplevel'],
       [$dialog,   'transient-for']);

The toplevel is normally a C<Gtk2::Window> or subclass, but in principle
could be another class.

=back

=head1 SEE ALSO

L<Glib::Ex::ConnectProperties>,
L<Glib::Ex::ConnectProperties::Element::widget_allocation>,
L<Gtk2::Widget>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/glib-ex-connectproperties/index.html>

=head1 LICENSE

Copyright 2010, 2011, 2012, 2017 Kevin Ryde

Glib-Ex-ConnectProperties 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.

Glib-Ex-ConnectProperties 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
Glib-Ex-ConnectProperties.  If not, see L<http://www.gnu.org/licenses/>.

=cut
