The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# instance per-screen ?
# instance per-display and move to screen ?
#
# searching through list_toplevels picks up non-instance created windows
# too, maybe flag those created this way ...
#
# isa() in search might rightly or wrongly pick up subclasses


# Copyright 2009, 2010 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::Ex::ToplevelSingleton;
use 5.008;
use strict;
use warnings;
use List::Util;
use App::Chart::Gtk2::Ex::ToplevelBits;

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

sub import {
  my ($class, %options) = @_;
  if (%options) {
    no strict 'refs';
    ${"${class}::_instance_options"} = \%options;
  }
}

# ClassName->has_instance ()
sub has_instance {
  my $class = shift;
  return $class->has_instance_for_screen (undef, @_);
}
# ClassName->has_instance_for_screen ($screen)
sub has_instance_for_screen {
  my ($class, $screen) = @_;
  $screen = App::Chart::Gtk2::Ex::ToplevelBits::_screen($screen);

  # searching like this isn't fast, but it allows a toplevel to be moved to
  # a different screen
  return List::Util::first { ($_->isa($class) && $_->get_screen == $screen) }
    Gtk2::Window->list_toplevels;
}

# ClassName->instance (args...)
sub instance {
  my $class = shift;
  return $class->instance_for_screen (undef, @_);
}

# ClassName->instance_for_screen ($screen, args...)
sub instance_for_screen {
  my ($class, $screen) = (shift, shift);

  return $class->has_instance_for_screen($screen)
    || do {
      ### ToplevelSingleton instance create
      $class->new_instance (screen => App::Chart::Gtk2::Ex::ToplevelBits::_screen($screen),
                            @_);
    };
}

# default instance constructor
sub new_instance {
  my ($class) = @_;
  my $self = Glib::Object::new (@_);
  my $options = do { no strict 'refs';
                     ${"${class}::_instance_options"} };
  if ($options->{'hide_on_delete'}) {
    $self->signal_connect (delete_event => \&Gtk2::Widget::hide_on_delete);
  }
  return $self;
}

1;
__END__


# # ClassName->has_instance_for_display ($display)
# sub has_instance_for_display {
#   my ($class, $display) = @_;
#   $display = _display($display);
# 
#   # searching like this isn't fast, but it allows a toplevel to be moved to
#   # a different display
#   return List::Util::first { ($_->isa($class) && $_->get_display == $display) }
#     Gtk2::Window->list_toplevels;
# }
# # ClassName->instance_for_display ($display, args...)
# sub instance_for_display {
#   my ($class, $display) = (shift, shift);
# 
#   return ($class->has_instance_for_display($display)
#           || $class->instance_for_screen($display, @_));
# }
# sub _display {
#   my ($obj) = @_;
#   if (! defined $obj) {
#     return Gtk2::Gdk::Screen->get_default->get_display;
#   }
#   if ($obj->can('get_display')) {
#     $obj = $obj->get_display
#       || croak "No display for target $obj";
#   }
#   return $obj;
# }

#  return ($display->{(__PACKAGE__)}->{$class} ||= do {

#  $instance->signal_connect (destroy => \&_do_destroy, $class);
# sub _do_destroy {
#   my ($instance, $class) = @_;
#   my $display = $instance->get_display;
#   if (($display->{(__PACKAGE__)}->{$class}||0) == $instance) {
#     delete $display->{(__PACKAGE__)}->{$class};
#   }
# }

# use Class::Singleton 1.04;  # 1.04 for has_instance()
# use base 'Class::Singleton';
# 
# sub instance {
#   my $class = shift;
#   return $class->has_instance
#     || do {
#       my $instance = $class->SUPER::instance;
#       $instance->signal_connect (destroy => \&_do_destroy, $ivar);
#       $instance
#     };
# }
# 
# sub _do_destroy {
#   my ($instance, $ivar) = @_;
#   if (($instance->has_instance || 0) == $instance) {
#     undef $ivar;
#   }
# }

=for stopwords toplevel multi

=head1 NAME

App::Chart::Gtk2::Ex::ToplevelSingleton -- single instance of toplevel window

=for test_synopsis my ($toplevel)

=head1 SYNOPSIS

 package MyToplevel;
 use Gtk2;
 use Glib::Object::Subclass 'Gtk2::Window';
 use base 'App::Chart::Gtk2::Ex::ToplevelSingleton';

 use App::Chart::Gtk2::Ex::ToplevelSingleton hide_on_delete => 1;

 $toplevel = MyToplevel->instance;
 
 if (MyToplevel->has_instance) { do_something() }

=head1 DESCRIPTION

This package is designed as a multi-inheritance mix-in for subclasses of
C<Gtk2::Window> which normally want only a single window instance, to be
used throughout a program.

After adding C<ToplevelSingleton> to your C<@ISA>,
C<< MyToplevel->instance >> returns a single shared instance of a
C<MyToplevel> window.

=head1 SEE ALSO

L<Class::Singleton>

=cut