2595 lines
77 KiB
Perl
2595 lines
77 KiB
Perl
package Gtk3;
|
|
$Gtk3::VERSION = '0.038';
|
|
=encoding utf8
|
|
|
|
=head1 NAME
|
|
|
|
Gtk3 - Perl interface to the 3.x series of the gtk+ toolkit
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Gtk3 -init;
|
|
my $window = Gtk3::Window->new ('toplevel');
|
|
my $button = Gtk3::Button->new ('Quit');
|
|
$button->signal_connect (clicked => sub { Gtk3::main_quit });
|
|
$window->add ($button);
|
|
$window->show_all;
|
|
Gtk3::main;
|
|
|
|
=head1 ABSTRACT
|
|
|
|
Perl bindings to the 3.x series of the gtk+ toolkit. This module allows you to
|
|
write graphical user interfaces in a Perlish and object-oriented way, freeing
|
|
you from the casting and memory management in C, yet remaining very close in
|
|
spirit to original API.
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
The C<Gtk3> module allows a Perl developer to use the gtk+ graphical user
|
|
interface library. Find out more about gtk+ at L<http://www.gtk.org>.
|
|
|
|
The gtk+ reference manual is also a handy companion when writing C<Gtk3>
|
|
programs in Perl: L<http://developer.gnome.org/gtk3/stable/>. The Perl
|
|
bindings follow the C API very closely, and the C reference documentation
|
|
should be considered the canonical source. The principles underlying the
|
|
mapping from C to Perl are explained in the documentation of
|
|
L<Glib::Object::Introspection>, on which C<Gtk3> is based.
|
|
|
|
L<Glib::Object::Introspection> also comes with the C<perli11ndoc> program which
|
|
displays the API reference documentation of all installed libraries organized
|
|
in accordance with these principles.
|
|
|
|
=cut
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Carp qw/croak/;
|
|
use Cairo::GObject;
|
|
use Glib::Object::Introspection;
|
|
use Exporter;
|
|
|
|
our @ISA = qw(Exporter);
|
|
|
|
=head2 Wrapped libraries
|
|
|
|
C<Gtk3> automatically sets up the following correspondence between C libraries
|
|
and Perl packages:
|
|
|
|
Library | Package
|
|
--------------+----------
|
|
Gtk-3.0 | Gtk3
|
|
Gdk-3.0 | Gtk3::Gdk
|
|
GdkPixbuf-2.0 | Gtk3::Gdk
|
|
GdkPixdata-2.0| Gtk3::Gdk
|
|
Pango-1.0 | Pango
|
|
|
|
=cut
|
|
|
|
=head2 Import arguments
|
|
|
|
When importing C<Gtk3>, you can pass C<-init> as in C<< use Gtk3 -init; >> to
|
|
have C<Gtk3::init> automatically called. You can also pass a version number to
|
|
require a certain version of C<Gtk3>.
|
|
|
|
=cut
|
|
|
|
my $_GTK_BASENAME = 'Gtk';
|
|
my $_GTK_VERSION = '3.0';
|
|
my $_GTK_PACKAGE = 'Gtk3';
|
|
|
|
my $_GDK_BASENAME = 'Gdk';
|
|
my $_GDK_VERSION = '3.0';
|
|
my $_GDK_PACKAGE = 'Gtk3::Gdk';
|
|
|
|
my $_GDK_PIXBUF_BASENAME = 'GdkPixbuf';
|
|
my $_GDK_PIXBUF_VERSION = '2.0';
|
|
my $_GDK_PIXBUF_PACKAGE = 'Gtk3::Gdk';
|
|
|
|
my $_GDK_PIXDATA_BASENAME = 'GdkPixdata';
|
|
my $_GDK_PIXDATA_VERSION = '2.0';
|
|
my $_GDK_PIXDATA_PACKAGE = 'Gtk3::Gdk';
|
|
|
|
my $_PANGO_BASENAME = 'Pango';
|
|
my $_PANGO_VERSION = '1.0';
|
|
my $_PANGO_PACKAGE = 'Pango';
|
|
|
|
=head2 Customizations and overrides
|
|
|
|
In order to make things more Perlish or to make porting from C<Gtk2> to C<Gtk3>
|
|
easier, C<Gtk3> customizes the API generated by L<Glib::Object::Introspection>
|
|
in a few spots:
|
|
|
|
=over
|
|
|
|
=cut
|
|
|
|
# - Customizations ---------------------------------------------------------- #
|
|
|
|
=item * The array ref normally returned by the following functions is flattened
|
|
into a list:
|
|
|
|
=over
|
|
|
|
=item Gtk3::ActionGroup::list_actions
|
|
|
|
=item Gtk3::Builder::get_objects
|
|
|
|
=item Gtk3::CellLayout::get_cells
|
|
|
|
=item Gtk3::Container::get_children
|
|
|
|
=item Gtk3::SizeGroup::get_widgets
|
|
|
|
=item Gtk3::TreePath::get_indices
|
|
|
|
=item Gtk3::TreeView::get_columns
|
|
|
|
=item Gtk3::UIManager::get_action_groups
|
|
|
|
=item Gtk3::UIManager::get_toplevels
|
|
|
|
=item Gtk3::Window::list_toplevels
|
|
|
|
=item Gtk3::stock_list_ids
|
|
|
|
=item Gtk3::Gdk::Pixbuf::get_formats
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
my @_GTK_FLATTEN_ARRAY_REF_RETURN_FOR = qw/
|
|
Gtk3::ActionGroup::list_actions
|
|
Gtk3::Builder::get_objects
|
|
Gtk3::CellLayout::get_cells
|
|
Gtk3::Container::get_children
|
|
Gtk3::SizeGroup::get_widgets
|
|
Gtk3::TreePath::get_indices
|
|
Gtk3::TreeView::get_columns
|
|
Gtk3::UIManager::get_action_groups
|
|
Gtk3::UIManager::get_toplevels
|
|
Gtk3::Window::list_toplevels
|
|
Gtk3::stock_list_ids
|
|
/;
|
|
|
|
my @_GDK_PIXBUF_FLATTEN_ARRAY_REF_RETURN_FOR = qw/
|
|
Gtk3::Gdk::Pixbuf::get_formats
|
|
/;
|
|
|
|
=item * The following functions normally return a boolean and additional out
|
|
arguments, where the boolean indicates whether the out arguments are valid.
|
|
They are altered such that when the boolean is true, only the additional out
|
|
arguments are returned, and when the boolean is false, an empty list is
|
|
returned.
|
|
|
|
=over
|
|
|
|
=item Gtk3::TextBuffer::get_selection_bounds
|
|
|
|
=item Gtk3::TreeModel::get_iter
|
|
|
|
=item Gtk3::TreeModel::get_iter_first
|
|
|
|
=item Gtk3::TreeModel::get_iter_from_string
|
|
|
|
=item Gtk3::TreeModel::iter_children
|
|
|
|
=item Gtk3::TreeModel::iter_nth_child
|
|
|
|
=item Gtk3::TreeModel::iter_parent
|
|
|
|
=item Gtk3::TreeModelFilter::convert_child_iter_to_iter
|
|
|
|
=item Gtk3::TreeModelSort::convert_child_iter_to_iter
|
|
|
|
=item Gtk3::TreeSelection::get_selected
|
|
|
|
=item Gtk3::TreeView::get_dest_row_at_pos
|
|
|
|
=item Gtk3::TreeView::get_path_at_pos
|
|
|
|
=item Gtk3::TreeView::get_tooltip_context
|
|
|
|
=item Gtk3::TreeView::get_visible_range
|
|
|
|
=item Gtk3::TreeViewColumn::cell_get_position
|
|
|
|
=item Gtk3::stock_lookup
|
|
|
|
=item Gtk3::Gdk::Event::get_axis
|
|
|
|
=item Gtk3::Gdk::Event::get_button
|
|
|
|
=item Gtk3::Gdk::Event::get_click_count
|
|
|
|
=item Gtk3::Gdk::Event::get_coords
|
|
|
|
=item Gtk3::Gdk::Event::get_keycode
|
|
|
|
=item Gtk3::Gdk::Event::get_keyval
|
|
|
|
=item Gtk3::Gdk::Event::get_scroll_direction
|
|
|
|
=item Gtk3::Gdk::Event::get_scroll_deltas
|
|
|
|
=item Gtk3::Gdk::Event::get_state
|
|
|
|
=item Gtk3::Gdk::Event::get_root_coords
|
|
|
|
=item Gtk3::Gdk::Window::get_origin
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
my @_GTK_HANDLE_SENTINEL_BOOLEAN_FOR = qw/
|
|
Gtk3::TextBuffer::get_selection_bounds
|
|
Gtk3::TreeModel::get_iter
|
|
Gtk3::TreeModel::get_iter_first
|
|
Gtk3::TreeModel::get_iter_from_string
|
|
Gtk3::TreeModel::iter_children
|
|
Gtk3::TreeModel::iter_nth_child
|
|
Gtk3::TreeModel::iter_parent
|
|
Gtk3::TreeModelFilter::convert_child_iter_to_iter
|
|
Gtk3::TreeModelSort::convert_child_iter_to_iter
|
|
Gtk3::TreeSelection::get_selected
|
|
Gtk3::TreeView::get_dest_row_at_pos
|
|
Gtk3::TreeView::get_path_at_pos
|
|
Gtk3::TreeView::get_tooltip_context
|
|
Gtk3::TreeView::get_visible_range
|
|
Gtk3::TreeViewColumn::cell_get_position
|
|
Gtk3::stock_lookup
|
|
/;
|
|
|
|
my @_GDK_HANDLE_SENTINEL_BOOLEAN_FOR = qw/
|
|
Gtk3::Gdk::Event::get_axis
|
|
Gtk3::Gdk::Event::get_button
|
|
Gtk3::Gdk::Event::get_click_count
|
|
Gtk3::Gdk::Event::get_coords
|
|
Gtk3::Gdk::Event::get_keycode
|
|
Gtk3::Gdk::Event::get_keyval
|
|
Gtk3::Gdk::Event::get_scroll_direction
|
|
Gtk3::Gdk::Event::get_scroll_deltas
|
|
Gtk3::Gdk::Event::get_state
|
|
Gtk3::Gdk::Event::get_root_coords
|
|
Gtk3::Gdk::Window::get_origin
|
|
/;
|
|
|
|
my @_GTK_USE_GENERIC_SIGNAL_MARSHALLER_FOR = (
|
|
['Gtk3::Editable', 'insert-text'],
|
|
['Gtk3::Dialog', 'response', \&Gtk3::Dialog::_gtk3_perl_response_converter],
|
|
['Gtk3::InfoBar', 'response', \&Gtk3::Dialog::_gtk3_perl_response_converter],
|
|
);
|
|
|
|
=item * Values of type Gtk3::ResponseType are converted to and from nick names
|
|
if possible, while still allowing raw IDs, in the following places:
|
|
|
|
=over
|
|
|
|
=item - For Gtk3::Dialog and Gtk3::InfoBar: the signal C<response> as well as
|
|
the methods C<add_action_widget>, C<add_button>, C<add_buttons>, C<response>,
|
|
C<set_default_response> and C<set_response_sensitive>.
|
|
|
|
=item - For Gtk3::Dialog: the methods C<get_response_for_widget>,
|
|
C<get_widget_for_response>, C<run> and C<set_alternative_button_order>.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
# GtkResponseType: id <-> nick
|
|
my $_GTK_RESPONSE_ID_TO_NICK = sub {
|
|
my ($id) = @_;
|
|
{
|
|
local $@;
|
|
my $nick = eval { Glib::Object::Introspection->convert_enum_to_sv (
|
|
'Gtk3::ResponseType', $id) };
|
|
return $nick if defined $nick;
|
|
}
|
|
return $id;
|
|
};
|
|
my $_GTK_RESPONSE_NICK_TO_ID = sub {
|
|
my ($nick) = @_;
|
|
{
|
|
local $@;
|
|
my $id = eval { Glib::Object::Introspection->convert_sv_to_enum (
|
|
'Gtk3::ResponseType', $nick) };
|
|
return $id if defined $id;
|
|
}
|
|
return $nick;
|
|
};
|
|
|
|
# Converter for GtkDialog's "response" signal.
|
|
sub Gtk3::Dialog::_gtk3_perl_response_converter {
|
|
my ($dialog, $id, $data) = @_;
|
|
return ($dialog, $_GTK_RESPONSE_ID_TO_NICK->($id), $data);
|
|
}
|
|
|
|
=item * Values of type Gtk3::IconSize are converted to and from nick names if
|
|
possible, while still allowing raw IDs, in the following places:
|
|
|
|
=over
|
|
|
|
=item - Gtk3::Image: the constructors new_from_stock, new_from_icon_set,
|
|
new_from_icon_name and new_from_gicon, the getters get_stock, get_icon_set,
|
|
get_icon_name and get_gicon and the setters set_from_stock, set_from_icon_set,
|
|
set_from_icon_name, set_from_gicon.
|
|
|
|
=item - Gtk3::Widget: the method render_icon.
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
# GtkIconSize: id <-> nick
|
|
my $_GTK_ICON_SIZE_ID_TO_NICK = sub {
|
|
my ($id) = @_;
|
|
{
|
|
local $@;
|
|
my $nick = eval { Glib::Object::Introspection->convert_enum_to_sv (
|
|
'Gtk3::IconSize', $id) };
|
|
return $nick if defined $nick;
|
|
}
|
|
{
|
|
my $nick = Gtk3::IconSize::get_name ($id);
|
|
return $nick if defined $nick;
|
|
}
|
|
return $id;
|
|
};
|
|
my $_GTK_ICON_SIZE_NICK_TO_ID = sub {
|
|
my ($nick) = @_;
|
|
{
|
|
local $@;
|
|
my $id = eval { Glib::Object::Introspection->convert_sv_to_enum (
|
|
'Gtk3::IconSize', $nick) };
|
|
return $id if defined $id;
|
|
}
|
|
{
|
|
my $id = Gtk3::IconSize::from_name ($nick);
|
|
return $id if $id;# if it's not zero
|
|
}
|
|
return $nick;
|
|
};
|
|
|
|
=item * The constants C<Gtk3::EVENT_PROPAGATE> and C<Gtk3::EVENT_STOP> can be
|
|
used in handlers for event signals like C<key-press-event> to indicate whether
|
|
or not the event should continue propagating through the widget hierarchy.
|
|
|
|
=cut
|
|
|
|
# Names "STOP" and "PROPAGATE" here are per the GtkWidget event signal
|
|
# descriptions. In some other flavours of signals the jargon is "handled"
|
|
# instead of "stop". "Handled" matches g_signal_accumulator_true_handled(),
|
|
# though that function doesn't rate a mention in the Gtk docs. There's
|
|
# nothing fixed in the idea of "true means cease emission" (whether it's
|
|
# called "stop" or "handled"). You can just as easily have false for cease
|
|
# (the way the underlying GSignalAccumulator func in fact operates). The
|
|
# upshot being don't want to attempt to be too universal with the names
|
|
# here; "EVENT" is meant to hint at the context or signal flavour they're
|
|
# for use with.
|
|
sub Gtk3::EVENT_PROPAGATE() { !1 };
|
|
sub Gtk3::EVENT_STOP() { 1 };
|
|
|
|
=item * The records corresponding to the various Gtk3::Gdk::Event types, like
|
|
C<expose> or C<key-release>, are represented as objects blessed into specific
|
|
Perl packages, like C<Gtk3::Gdk::EventExpose> or C<Gtk3::Gdk::EventKey>, that
|
|
all inherit from C<Gtk3::Gdk::Event>. This allows you to seemlessly access
|
|
type-specific fields as well as common fields, as in C<< $event->window >> or
|
|
C<< $event->keyval >>.
|
|
|
|
=cut
|
|
|
|
my %_GDK_REBLESSERS = (
|
|
'Gtk3::Gdk::Event' => \&Gtk3::Gdk::Event::_rebless,
|
|
);
|
|
|
|
my %_GDK_EVENT_TYPE_TO_PACKAGE = (
|
|
'expose' => 'Expose',
|
|
'motion-notify' => 'Motion',
|
|
'button-press' => 'Button',
|
|
'2button-press' => 'Button',
|
|
'3button-press' => 'Button',
|
|
'button-release' => 'Button',
|
|
'key-press' => 'Key',
|
|
'key-release' => 'Key',
|
|
'enter-notify' => 'Crossing',
|
|
'leave-notify' => 'Crossing',
|
|
'focus-change' => 'Focus',
|
|
'configure' => 'Configure',
|
|
'property-notify' => 'Property',
|
|
'selection-clear' => 'Selection',
|
|
'selection-request' => 'Selection',
|
|
'selection-notify' => 'Selection',
|
|
'proximity-in' => 'Proximity',
|
|
'proximity-out' => 'Proximity',
|
|
'drag-enter' => 'DND',
|
|
'drag-leave' => 'DND',
|
|
'drag-motion' => 'DND',
|
|
'drag-status' => 'DND',
|
|
'drop-start' => 'DND',
|
|
'drop-finished' => 'DND',
|
|
'client-event' => 'Client',
|
|
'visibility-notify' => 'Visibility',
|
|
'no-expose' => 'NoExpose',
|
|
'scroll' => 'Scroll',
|
|
'window-state' => 'WindowState',
|
|
'setting' => 'Setting',
|
|
'owner-change' => 'OwnerChange',
|
|
'grab-broken' => 'GrabBroken',
|
|
'damage' => 'Expose',
|
|
# added in 3.4:
|
|
'touch-begin' => 'Touch',
|
|
'touch-update' => 'Touch',
|
|
'touch-end' => 'Touch',
|
|
'touch-cancel' => 'Touch',
|
|
# added in 3.6:
|
|
'double-button-press' => 'Button',
|
|
'triple-button-press' => 'Button',
|
|
);
|
|
|
|
# Make all of the above sub-types inherit from Gtk3::Gdk::Event.
|
|
{
|
|
no strict qw(refs);
|
|
my %seen;
|
|
foreach (grep { !$seen{$_}++ } values %_GDK_EVENT_TYPE_TO_PACKAGE) {
|
|
push @{'Gtk3::Gdk::Event' . $_ . '::ISA'}, 'Gtk3::Gdk::Event';
|
|
}
|
|
}
|
|
|
|
sub Gtk3::Gdk::Event::_rebless {
|
|
my ($event) = @_;
|
|
my $package = 'Gtk3::Gdk::Event';
|
|
if (exists $_GDK_EVENT_TYPE_TO_PACKAGE{$event->type}) {
|
|
$package .= $_GDK_EVENT_TYPE_TO_PACKAGE{$event->type};
|
|
}
|
|
return bless $event, $package;
|
|
}
|
|
|
|
# - Wiring ------------------------------------------------------------------ #
|
|
|
|
=item * Gtk3::Gdk::Atom has overloads for the C<==> and C<!=> operators that
|
|
check for equality of the underlying atoms.
|
|
|
|
=cut
|
|
|
|
sub import {
|
|
my $class = shift;
|
|
|
|
Glib::Object::Introspection->setup (
|
|
basename => $_GTK_BASENAME,
|
|
version => $_GTK_VERSION,
|
|
package => $_GTK_PACKAGE,
|
|
flatten_array_ref_return_for => \@_GTK_FLATTEN_ARRAY_REF_RETURN_FOR,
|
|
handle_sentinel_boolean_for => \@_GTK_HANDLE_SENTINEL_BOOLEAN_FOR,
|
|
use_generic_signal_marshaller_for => \@_GTK_USE_GENERIC_SIGNAL_MARSHALLER_FOR);
|
|
|
|
Glib::Object::Introspection->setup (
|
|
basename => $_GDK_BASENAME,
|
|
version => $_GDK_VERSION,
|
|
package => $_GDK_PACKAGE,
|
|
handle_sentinel_boolean_for => \@_GDK_HANDLE_SENTINEL_BOOLEAN_FOR,
|
|
reblessers => \%_GDK_REBLESSERS);
|
|
|
|
Glib::Object::Introspection->setup (
|
|
basename => $_GDK_PIXBUF_BASENAME,
|
|
version => $_GDK_PIXBUF_VERSION,
|
|
package => $_GDK_PIXBUF_PACKAGE,
|
|
flatten_array_ref_return_for => \@_GDK_PIXBUF_FLATTEN_ARRAY_REF_RETURN_FOR);
|
|
|
|
# In gdk-pixbuf 2.38.0, the GdkPixdata introspection information was split
|
|
# out into its own file.
|
|
if (Gtk3::Gdk::Pixbuf::CHECK_VERSION (2, 38, 0)) {
|
|
Glib::Object::Introspection->setup (
|
|
basename => $_GDK_PIXDATA_BASENAME,
|
|
version => $_GDK_PIXDATA_VERSION,
|
|
package => $_GDK_PIXDATA_PACKAGE);
|
|
}
|
|
|
|
Glib::Object::Introspection->setup (
|
|
basename => $_PANGO_BASENAME,
|
|
version => $_PANGO_VERSION,
|
|
package => $_PANGO_PACKAGE);
|
|
|
|
Glib::Object::Introspection->_register_boxed_synonym (
|
|
"cairo", "RectangleInt", "gdk_rectangle_get_type");
|
|
|
|
# FIXME: This uses an undocumented interface for overloading to avoid the
|
|
# need for a package declaration.
|
|
Gtk3::Gdk::Atom->overload::OVERLOAD (
|
|
'==' => sub { ${$_[0]} == ${$_[1]} },
|
|
'!=' => sub { ${$_[0]} != ${$_[1]} },
|
|
fallback => 1);
|
|
|
|
my $init = 0;
|
|
my @unknown_args = ($class);
|
|
foreach (@_) {
|
|
if (/^-?init$/) {
|
|
$init = 1;
|
|
} else {
|
|
push @unknown_args, $_;
|
|
}
|
|
}
|
|
|
|
if ($init) {
|
|
Gtk3::init ();
|
|
}
|
|
|
|
# call into Exporter for the unrecognized arguments; handles exporting and
|
|
# version checking
|
|
Gtk3->export_to_level (1, @unknown_args);
|
|
}
|
|
|
|
# - Overrides --------------------------------------------------------------- #
|
|
|
|
=item * For backwards compatibility, the functions C<Gtk3::get_version_info>
|
|
and C<Gtk3::GET_VERSION_INFO> are provided, and the functions
|
|
C<Gtk3::CHECK_VERSION>, C<Gtk3::check_version>, C<Gtk3::init>,
|
|
C<Gtk3::init_check>, C<Gtk3::main>, C<Gtk3::main_level> and C<Gtk3::main_quit>
|
|
can be called as class-static or as normal functions: for example, C<<
|
|
Gtk3->main_quit >> and C<< Gtk3::main_quit >> are both supported.
|
|
Additionally, C<Gtk3::init> and C<Gtk3::init_check> automatically handle
|
|
passing and updating C<@ARGV> as appropriate.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::get_version_info {
|
|
return Gtk3::get_major_version (),
|
|
Gtk3::get_minor_version (),
|
|
Gtk3::get_micro_version ();
|
|
}
|
|
|
|
sub Gtk3::GET_VERSION_INFO {
|
|
return Gtk3->MAJOR_VERSION, Gtk3->MINOR_VERSION, Gtk3->MICRO_VERSION;
|
|
}
|
|
|
|
sub Gtk3::CHECK_VERSION {
|
|
return not defined Gtk3::check_version(@_ == 4 ? @_[1..3] : @_);
|
|
}
|
|
|
|
sub Gtk3::check_version {
|
|
Glib::Object::Introspection->invoke ($_GTK_BASENAME, undef, 'check_version',
|
|
@_ == 4 ? @_[1..3] : @_);
|
|
}
|
|
|
|
sub Gtk3::init {
|
|
my $rest = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, undef, 'init',
|
|
[$0, @ARGV]);
|
|
@ARGV = @{$rest}[1 .. $#$rest]; # remove $0
|
|
return;
|
|
}
|
|
|
|
sub Gtk3::init_check {
|
|
my ($success, $rest) = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, undef, 'init_check',
|
|
[$0, @ARGV]);
|
|
@ARGV = @{$rest}[1 .. $#$rest]; # remove $0
|
|
return $success;
|
|
}
|
|
|
|
sub Gtk3::main {
|
|
# Ignore any arguments passed in.
|
|
Glib::Object::Introspection->invoke ($_GTK_BASENAME, undef, 'main');
|
|
}
|
|
|
|
sub Gtk3::main_level {
|
|
# Ignore any arguments passed in.
|
|
return Glib::Object::Introspection->invoke ($_GTK_BASENAME, undef, 'main_level');
|
|
}
|
|
|
|
sub Gtk3::main_quit {
|
|
# Ignore any arguments passed in.
|
|
Glib::Object::Introspection->invoke ($_GTK_BASENAME, undef, 'main_quit');
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::show_about_dialog> is provided.
|
|
|
|
=cut
|
|
|
|
{
|
|
my $global_about_dialog = undef;
|
|
my $about_dialog_key = '__gtk3_about_dialog';
|
|
|
|
sub Gtk3::show_about_dialog {
|
|
# For backwards-compatibility, optionally accept and discard a class
|
|
# argument.
|
|
my $parent_or_class = shift;
|
|
my $parent = defined $parent_or_class && $parent_or_class eq 'Gtk3'
|
|
? shift
|
|
: $parent_or_class;
|
|
my %props = @_;
|
|
my $dialog = defined $parent
|
|
? $parent->{$about_dialog_key}
|
|
: $global_about_dialog;
|
|
|
|
if (!$dialog) {
|
|
$dialog = Gtk3::AboutDialog->new;
|
|
$dialog->signal_connect (delete_event => sub { $dialog->hide_on_delete });
|
|
$dialog->signal_connect (response => sub { $dialog->hide });
|
|
foreach my $prop (keys %props) {
|
|
$dialog->set ($prop => $props{$prop});
|
|
}
|
|
if ($parent) {
|
|
$dialog->set_modal (Glib::TRUE);
|
|
$dialog->set_transient_for ($parent);
|
|
$dialog->set_destroy_with_parent (Glib::TRUE);
|
|
$parent->{$about_dialog_key} = $dialog;
|
|
} else {
|
|
$global_about_dialog = $dialog;
|
|
}
|
|
}
|
|
|
|
$dialog->present;
|
|
}
|
|
}
|
|
|
|
=item * Perl reimplementations of C<Gtk3::ActionGroup::add_actions>,
|
|
C<add_radio_actions> and C<add_toggle_actions> are provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::ActionGroup::add_actions {
|
|
my ($self, $entries, $user_data) = @_;
|
|
|
|
croak 'actions must be a reference to an array of action entries'
|
|
unless (ref($entries) eq 'ARRAY');
|
|
|
|
croak 'action array is empty'
|
|
unless (@$entries);
|
|
|
|
my $process = sub {
|
|
my ($p) = @_;
|
|
my ($name, $stock_id, $label, $accelerator, $tooltip, $callback);
|
|
|
|
if (ref($p) eq 'ARRAY') {
|
|
$name = $p->[0];
|
|
$stock_id = $p->[1];
|
|
$label = $p->[2];
|
|
$accelerator = $p->[3];
|
|
$tooltip = $p->[4];
|
|
$callback = $p->[5];
|
|
} elsif (ref($p) eq 'HASH') {
|
|
$name = $p->{name};
|
|
$stock_id = $p->{stock_id};
|
|
$label = $p->{label};
|
|
$accelerator = $p->{accelerator};
|
|
$tooltip = $p->{tooltip};
|
|
$callback = $p->{callback};
|
|
} else {
|
|
croak 'action entry must be a reference to a hash or an array';
|
|
}
|
|
|
|
if (defined($label)) {
|
|
$label = $self->translate_string($label);
|
|
}
|
|
if (defined($tooltip)) {
|
|
$tooltip = $self->translate_string($tooltip);
|
|
}
|
|
|
|
my $action = Gtk3::Action->new ($name, $label, $tooltip, $stock_id);
|
|
|
|
if ($callback) {
|
|
$action->signal_connect ('activate', $callback, $user_data);
|
|
}
|
|
$self->add_action_with_accel ($action, $accelerator);
|
|
};
|
|
|
|
for my $e (@$entries) {
|
|
$process->($e);
|
|
}
|
|
}
|
|
|
|
sub Gtk3::ActionGroup::add_toggle_actions {
|
|
my ($self, $entries, $user_data) = @_;
|
|
|
|
croak 'entries must be a reference to an array of toggle action entries'
|
|
unless (ref($entries) eq 'ARRAY');
|
|
|
|
croak 'toggle action array is empty'
|
|
unless (@$entries);
|
|
|
|
my $process = sub {
|
|
my ($p) = @_;
|
|
my ($name, $stock_id, $label, $accelerator, $tooltip,
|
|
$callback, $is_active);
|
|
|
|
if (ref($p) eq 'ARRAY') {
|
|
$name = $p->[0];
|
|
$stock_id = $p->[1];
|
|
$label = $p->[2];
|
|
$accelerator = $p->[3];
|
|
$tooltip = $p->[4];
|
|
$callback = $p->[5];
|
|
$is_active = $p->[6];
|
|
} elsif (ref($p) eq 'HASH') {
|
|
$name = $p->{name};
|
|
$stock_id = $p->{stock_id};
|
|
$label = $p->{label};
|
|
$accelerator = $p->{accelerator};
|
|
$tooltip = $p->{tooltip};
|
|
$callback = $p->{callback};
|
|
$is_active = $p->{is_active};
|
|
} else {
|
|
croak 'action entry must be a hash or an array';
|
|
}
|
|
|
|
if (defined($label)) {
|
|
$label = $self->translate_string($label);
|
|
}
|
|
if (defined($tooltip)) {
|
|
$tooltip = $self->translate_string($tooltip);
|
|
}
|
|
|
|
my $action = Gtk3::ToggleAction->new (
|
|
$name, $label, $tooltip, $stock_id);
|
|
$action->set_active ($is_active) if defined $is_active;
|
|
|
|
if ($callback) {
|
|
$action->signal_connect ('activate', $callback, $user_data);
|
|
}
|
|
|
|
$self->add_action_with_accel ($action, $accelerator);
|
|
};
|
|
|
|
for my $e (@$entries) {
|
|
$process->($e);
|
|
}
|
|
}
|
|
|
|
sub Gtk3::ActionGroup::add_radio_actions {
|
|
my ($self, $entries, $value, $on_change, $user_data) = @_;
|
|
|
|
croak 'radio_action_entries must be a reference to '
|
|
. 'an array of action entries'
|
|
unless (ref($entries) eq 'ARRAY');
|
|
|
|
croak 'radio action array is empty'
|
|
unless (@$entries);
|
|
|
|
my $first_action = undef;
|
|
|
|
my $process = sub {
|
|
my ($group, $p) = @_;
|
|
my ($name, $stock_id, $label, $accelerator, $tooltip, $entry_value);
|
|
|
|
if (ref($p) eq 'ARRAY') {
|
|
$name = $p->[0];
|
|
$stock_id = $p->[1];
|
|
$label = $p->[2];
|
|
$accelerator = $p->[3];
|
|
$tooltip = $p->[4];
|
|
$entry_value = $p->[5];
|
|
} elsif (ref($p) eq 'HASH') {
|
|
$name = $p->{name};
|
|
$stock_id = $p->{stock_id};
|
|
$label = $p->{label};
|
|
$accelerator = $p->{accelerator};
|
|
$tooltip = $p->{tooltip};
|
|
$entry_value = $p->{value};
|
|
} else {
|
|
croak 'radio action entries neither hash nor array';
|
|
}
|
|
|
|
if (defined($label)) {
|
|
$label = $self->translate_string($label);
|
|
}
|
|
if (defined($tooltip)) {
|
|
$tooltip = $self->translate_string($tooltip);
|
|
}
|
|
|
|
my $action = Gtk3::RadioAction->new (
|
|
$name, $label, $tooltip, $stock_id, $entry_value);
|
|
|
|
$action->join_group($group);
|
|
|
|
if ($value == $entry_value) {
|
|
$action->set_active(Glib::TRUE);
|
|
}
|
|
$self->add_action_with_accel($action, $accelerator);
|
|
return $action;
|
|
};
|
|
|
|
for my $e (@$entries) {
|
|
my $group = $process->($first_action, $e);
|
|
if (!$first_action) {
|
|
$first_action = $group;
|
|
}
|
|
}
|
|
|
|
if ($first_action && $on_change) {
|
|
$first_action->signal_connect ('changed', $on_change, $user_data);
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::Builder::add_objects_from_file> and C<add_objects_from_string>
|
|
also accept a list of objects instead of an array ref.
|
|
|
|
=item * C<Gtk3::Builder::add_objects_from_string> and C<add_from_string> don't
|
|
take length arguments, as they are computed automatically.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Builder::add_objects_from_file {
|
|
my ($builder, $filename, @rest) = @_;
|
|
my $ref = _rest_to_ref (\@rest);
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Builder', 'add_objects_from_file',
|
|
$builder, $filename, $ref);
|
|
}
|
|
|
|
sub Gtk3::Builder::add_objects_from_string {
|
|
my ($builder, $string, @rest) = @_;
|
|
my $ref = _rest_to_ref (\@rest);
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Builder', 'add_objects_from_string',
|
|
$builder, $string, -1, $ref); # wants length in bytes
|
|
}
|
|
|
|
sub Gtk3::Builder::add_from_string {
|
|
my ($builder, $string) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Builder', 'add_from_string',
|
|
$builder, $string, -1); # wants length in bytes
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::Builder::connect_signals> is
|
|
provided.
|
|
|
|
=cut
|
|
|
|
# Copied from Gtk2.pm
|
|
sub Gtk3::Builder::connect_signals {
|
|
my $builder = shift;
|
|
my $user_data = shift;
|
|
|
|
my $do_connect = sub {
|
|
my ($object,
|
|
$signal_name,
|
|
$user_data,
|
|
$connect_object,
|
|
$flags,
|
|
$handler) = @_;
|
|
my $func = ($flags & 'after') ? 'signal_connect_after' : 'signal_connect';
|
|
# we get connect_object when we're supposed to call
|
|
# signal_connect_object, which ensures that the data (an object)
|
|
# lives as long as the signal is connected. the bindings take
|
|
# care of that for us in all cases, so we only have signal_connect.
|
|
# if we get a connect_object, just use that instead of user_data.
|
|
$object->$func($signal_name => $handler,
|
|
$connect_object || $user_data);
|
|
};
|
|
|
|
# $builder->connect_signals ($user_data)
|
|
# $builder->connect_signals ($user_data, $package)
|
|
if ($#_ <= 0) {
|
|
my $package = shift;
|
|
$package = caller unless defined $package;
|
|
|
|
$builder->connect_signals_full(sub {
|
|
my ($builder,
|
|
$object,
|
|
$signal_name,
|
|
$handler_name,
|
|
$connect_object,
|
|
$flags) = @_;
|
|
|
|
no strict qw/refs/;
|
|
|
|
my $handler = $handler_name;
|
|
if (ref $package) {
|
|
$handler = sub { $package->$handler_name(@_) };
|
|
} else {
|
|
if ($package && $handler !~ /::/) {
|
|
$handler = $package.'::'.$handler_name;
|
|
}
|
|
}
|
|
|
|
$do_connect->($object, $signal_name, $user_data, $connect_object,
|
|
$flags, $handler);
|
|
});
|
|
}
|
|
|
|
# $builder->connect_signals ($user_data, %handlers)
|
|
else {
|
|
my %handlers = @_;
|
|
|
|
$builder->connect_signals_full(sub {
|
|
my ($builder,
|
|
$object,
|
|
$signal_name,
|
|
$handler_name,
|
|
$connect_object,
|
|
$flags) = @_;
|
|
|
|
return unless exists $handlers{$handler_name};
|
|
|
|
$do_connect->($object, $signal_name, $user_data, $connect_object,
|
|
$flags, $handlers{$handler_name});
|
|
});
|
|
}
|
|
}
|
|
|
|
=item * The default C<new> constructors of Gtk3::Button, Gtk3::CheckButton,
|
|
Gtk3::ColorButton, Gtk3::FontButton and Gtk3::ToggleButton reroute to
|
|
C<new_with_mnemonic> if given an extra argument.
|
|
|
|
=cut
|
|
|
|
{
|
|
no strict 'refs';
|
|
my @button_classes = ([Button => 'new_with_mnemonic'],
|
|
[CheckButton => 'new_with_mnemonic'],
|
|
[ColorButton => 'new_with_color'],
|
|
[FontButton => 'new_with_font'],
|
|
[ToggleButton => 'new_with_mnemonic']);
|
|
foreach my $button_pair (@button_classes) {
|
|
my ($button_class, $button_ctor) = @$button_pair;
|
|
*{'Gtk3::' . $button_class . '::new'} = sub {
|
|
my ($class, $thing) = @_;
|
|
if (defined $thing) {
|
|
return $class->$button_ctor ($thing);
|
|
} else {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $button_class, 'new', @_);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
=item * The default C<new> constructor of Gtk3::CheckMenuItem reroutes to
|
|
C<new_with_mnemonic> if given an extra argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::CheckMenuItem::new {
|
|
my ($class, $mnemonic) = @_;
|
|
if (defined $mnemonic) {
|
|
return $class->new_with_mnemonic ($mnemonic);
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'CheckMenuItem', 'new', @_);
|
|
}
|
|
|
|
=item * The C<length> argument of C<Gtk3::Clipboard::set_text> is optional.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Clipboard::set_text {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Clipboard', 'set_text',
|
|
@_ == 3 ? @_ : (@_[0,1], -1)); # wants length in bytes
|
|
}
|
|
|
|
=item * Perl reimplementations of C<Gtk3::Container::add_with_properties>,
|
|
C<Gtk3::Container::child_get> and C<Gtk3::Container::child_set> are provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Container::add_with_properties {
|
|
my ($container, $widget, @rest) = @_;
|
|
$widget->freeze_child_notify;
|
|
$container->add ($widget);
|
|
if ($widget->get_parent) {
|
|
$container->child_set ($widget, @rest);
|
|
}
|
|
$widget->thaw_child_notify;
|
|
}
|
|
|
|
sub Gtk3::Container::child_get {
|
|
my ($container, $child, @rest) = @_;
|
|
my $properties = _rest_to_ref (\@rest);
|
|
my @values;
|
|
foreach my $property (@$properties) {
|
|
my $pspec = Gtk3::ContainerClass::find_child_property ($container, $property);
|
|
croak "Cannot find type information for property '$property' on $container"
|
|
unless defined $pspec;
|
|
my $value_wrapper = Glib::Object::Introspection::GValueWrapper->new (
|
|
$pspec->get_value_type, undef);
|
|
$container->child_get_property ($child, $property, $value_wrapper);
|
|
push @values, $value_wrapper->get_value;
|
|
}
|
|
return @values[0..$#values];
|
|
}
|
|
|
|
sub Gtk3::Container::child_set {
|
|
my ($container, $child, @rest) = @_;
|
|
my ($properties, $values) = _unpack_keys_and_values (\@rest);
|
|
foreach my $i (0..$#$properties) {
|
|
my $property = $properties->[$i];
|
|
my $value = $values->[$i];
|
|
my $pspec = Gtk3::ContainerClass::find_child_property ($container, $property);
|
|
croak "Cannot find type information for property '$property' on $container"
|
|
unless defined $pspec;
|
|
my $value_wrapper = Glib::Object::Introspection::GValueWrapper->new (
|
|
$pspec->get_value_type, $value);
|
|
$container->child_set_property ($child, $property, $value_wrapper);
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::Container::find_child_property> and
|
|
C<Gtk3::Container::list_child_properties> are forwarded to the corresponding
|
|
functions in C<Gtk3::ContainerClass>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Container::find_child_property {
|
|
return Gtk3::ContainerClass::find_child_property (@_);
|
|
}
|
|
|
|
sub Gtk3::Container::list_child_properties {
|
|
my $ref = Gtk3::ContainerClass::list_child_properties (@_);
|
|
return if not defined $ref;
|
|
return wantarray ? @$ref : $ref->[$#$ref];
|
|
}
|
|
|
|
=item * C<Gtk3::Container::get_focus_chain> returns a list of widgets, or an
|
|
empty list.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Container::get_focus_chain {
|
|
my ($container) = @_;
|
|
my ($is_set, $widgets) = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Container', 'get_focus_chain',
|
|
$container);
|
|
return () unless $is_set;
|
|
return @$widgets;
|
|
}
|
|
|
|
=item * C<Gtk3::Container::set_focus_chain> also accepts a list of widgets.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Container::set_focus_chain {
|
|
my ($container, @rest) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Container', 'set_focus_chain',
|
|
$container, _rest_to_ref (\@rest));
|
|
}
|
|
|
|
=item * C<Gtk3::CssProvider::load_from_data> also accepts a string.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::CssProvider::load_from_data {
|
|
my ($self, $data) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'CssProvider', 'load_from_data',
|
|
$self, _unpack_unless_array_ref ($data));
|
|
}
|
|
|
|
=item * For Gtk3::Dialog and Gtk3::InfoBar, a Perl implementation of
|
|
C<add_buttons> is provided.
|
|
|
|
=cut
|
|
|
|
# Gtk3::Dialog / Gtk3::InfoBar methods due to overlap
|
|
{
|
|
no strict qw(refs);
|
|
foreach my $dialog_package (qw/Dialog InfoBar/) {
|
|
*{'Gtk3::' . $dialog_package . '::add_action_widget'} = sub {
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $dialog_package, 'add_action_widget',
|
|
$_[0], $_[1], $_GTK_RESPONSE_NICK_TO_ID->($_[2]));
|
|
};
|
|
*{'Gtk3::' . $dialog_package . '::add_button'} = sub {
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $dialog_package, 'add_button',
|
|
$_[0], $_[1], $_GTK_RESPONSE_NICK_TO_ID->($_[2]));
|
|
};
|
|
*{'Gtk3::' . $dialog_package . '::add_buttons'} = sub {
|
|
my ($dialog, @rest) = @_;
|
|
for (my $i = 0; $i < @rest; $i += 2) {
|
|
$dialog->add_button ($rest[$i], $rest[$i+1]);
|
|
}
|
|
};
|
|
*{'Gtk3::' . $dialog_package . '::response'} = sub {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $dialog_package, 'response',
|
|
$_[0], $_GTK_RESPONSE_NICK_TO_ID->($_[1]));
|
|
};
|
|
*{'Gtk3::' . $dialog_package . '::set_default_response'} = sub {
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $dialog_package, 'set_default_response',
|
|
$_[0], $_GTK_RESPONSE_NICK_TO_ID->($_[1]));
|
|
};
|
|
*{'Gtk3::' . $dialog_package . '::set_response_sensitive'} = sub {
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $dialog_package, 'set_response_sensitive',
|
|
$_[0], $_GTK_RESPONSE_NICK_TO_ID->($_[1]), $_[2]);
|
|
};
|
|
}
|
|
}
|
|
|
|
sub Gtk3::Dialog::get_response_for_widget {
|
|
my $id = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Dialog', 'get_response_for_widget', @_);
|
|
return $_GTK_RESPONSE_ID_TO_NICK->($id);
|
|
}
|
|
|
|
sub Gtk3::Dialog::get_widget_for_response {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Dialog', 'get_widget_for_response',
|
|
$_[0], $_GTK_RESPONSE_NICK_TO_ID->($_[1]));
|
|
}
|
|
|
|
=item * C<Gtk3::Dialog::new> can optionally be called as C<< Gtk3::Dialog->new
|
|
(TITLE, PARENT, FLAGS, ...) >> where C<...> is a series of button text and
|
|
response id pairs.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Dialog::new {
|
|
my ($class, $title, $parent, $flags, @rest) = @_;
|
|
if (@_ == 1) {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Dialog', 'new', @_);
|
|
} elsif ((@_ < 4) || (@rest % 2)){
|
|
croak ("Usage: Gtk3::Dialog->new ()\n" .
|
|
" or Gtk3::Dialog->new (TITLE, PARENT, FLAGS, ...)\n" .
|
|
" where ... is a series of button text and response id pairs");
|
|
} else {
|
|
my $dialog = Gtk3::Dialog->new;
|
|
defined $title and $dialog->set_title ($title);
|
|
defined $parent and $dialog->set_transient_for ($parent);
|
|
if (! eval { $flags->isa ('Gtk3::DialogFlags'); }) {
|
|
$flags = Gtk3::DialogFlags->new ($flags);
|
|
}
|
|
$flags & 'modal' and $dialog->set_modal (Glib::TRUE);
|
|
$flags & 'destroy-with-parent' and $dialog->set_destroy_with_parent (Glib::TRUE);
|
|
$dialog->add_buttons (@rest);
|
|
return $dialog;
|
|
}
|
|
}
|
|
|
|
=item * A Perl implementation of C<Gtk3::Dialog::new_with_buttons> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Dialog::new_with_buttons {
|
|
&Gtk3::Dialog::new;
|
|
}
|
|
|
|
sub Gtk3::Dialog::run {
|
|
my $id = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Dialog', 'run', @_);
|
|
return $_GTK_RESPONSE_ID_TO_NICK->($id);
|
|
}
|
|
|
|
sub Gtk3::Dialog::set_alternative_button_order {
|
|
my ($dialog, @rest) = @_;
|
|
return unless @rest;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Dialog', 'set_alternative_button_order_from_array',
|
|
$dialog, [map { $_GTK_RESPONSE_NICK_TO_ID->($_) } @rest]);
|
|
}
|
|
|
|
=item * The C<length> argument of C<Gtk3::Editable::insert_text> is optional.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Editable::insert_text {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Editable', 'insert_text',
|
|
@_ == 4 ? @_ : (@_[0,1], -1, $_[2])); # wants length in bytes
|
|
}
|
|
|
|
=item * A Perl implementation of C<Gtk3::FileChooserDialog::new> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::FileChooserDialog::new {
|
|
my ($class, $title, $parent, $action, @varargs) = @_;
|
|
|
|
if (@varargs % 2) {
|
|
croak 'Usage: Gtk3::FileChooserDialog->new' .
|
|
' (title, parent, action, button-text =>' .
|
|
" response-id, ...)\n";
|
|
}
|
|
|
|
my $result = Glib::Object::new (
|
|
$class,
|
|
title => $title,
|
|
action => $action,
|
|
);
|
|
|
|
if ($parent) {
|
|
$result->set_transient_for ($parent);
|
|
}
|
|
|
|
for (my $i = 0; $i < @varargs; $i += 2) {
|
|
$result->add_button ($varargs[$i], $varargs[$i+1]);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
=item * C<Gtk3::HBox::new> uses the defaults homogeneous = FALSE and spacing =
|
|
5.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::HBox::new {
|
|
my ($class, $homogeneous, $spacing) = @_;
|
|
$homogeneous = 0 unless defined $homogeneous;
|
|
$spacing = 5 unless defined $spacing;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'HBox', 'new', $class, $homogeneous, $spacing);
|
|
}
|
|
|
|
# Gtk3::Image
|
|
{
|
|
no strict qw(refs);
|
|
foreach my $ctor (qw/new_from_stock new_from_icon_set new_from_icon_name new_from_gicon/) {
|
|
*{'Gtk3::Image::' . $ctor} = sub {
|
|
my ($class, $thing, $size) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Image', $ctor, $class, $thing,
|
|
$_GTK_ICON_SIZE_NICK_TO_ID->($size));
|
|
}
|
|
}
|
|
foreach my $getter (qw/get_stock get_icon_set get_icon_name get_gicon/) {
|
|
*{'Gtk3::Image::' . $getter} = sub {
|
|
my ($image) = @_;
|
|
my ($thing, $size) = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Image', $getter, $image);
|
|
return ($thing, $_GTK_ICON_SIZE_ID_TO_NICK->($size));
|
|
}
|
|
}
|
|
foreach my $setter (qw/set_from_stock set_from_icon_set set_from_icon_name set_from_gicon/) {
|
|
*{'Gtk3::Image::' . $setter} = sub {
|
|
my ($image, $thing, $size) = @_;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Image', $setter, $image, $thing,
|
|
$_GTK_ICON_SIZE_NICK_TO_ID->($size));
|
|
}
|
|
}
|
|
}
|
|
|
|
=item * The default C<new> constructor of Gtk3::ImageMenuItem reroutes to
|
|
C<new_with_mnemonic> if given an extra argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::ImageMenuItem::new {
|
|
my ($class, $mnemonic) = @_;
|
|
if (defined $mnemonic) {
|
|
return $class->new_with_mnemonic ($mnemonic);
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'ImageMenuItem', 'new', @_);
|
|
}
|
|
|
|
=item * C<Gtk3::InfoBar::new> can optionally be called as C<<
|
|
Gtk3::InfoBar->new (...) >> where C<...> is a series of button text and
|
|
response id pairs.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::InfoBar::new {
|
|
my ($class, @buttons) = @_;
|
|
if (@_ == 1) {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'InfoBar', 'new', @_);
|
|
} elsif (@buttons % 2) {
|
|
croak "Usage: Gtk3::InfoBar->new_with_buttons (button-text => response_id, ...)\n";
|
|
} else {
|
|
my $infobar = Gtk3::InfoBar->new;
|
|
for (my $i = 0; $i < @buttons; $i += 2) {
|
|
$infobar->add_button ($buttons[$i], $buttons[$i+1]);
|
|
}
|
|
return $infobar;
|
|
}
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::InfoBar::new_with_buttons> is
|
|
provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::InfoBar::new_with_buttons {
|
|
&Gtk3::InfoBar::new;
|
|
}
|
|
|
|
=item * The default C<new> constructor of Gtk3::LinkButton reroutes to
|
|
C<new_with_label> if given an extra argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::LinkButton::new {
|
|
my ($class, $uri, $label) = @_;
|
|
if (defined $label) {
|
|
return Gtk3::LinkButton->new_with_label ($uri, $label);
|
|
} else {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'LinkButton', 'new', @_);
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::ListStore::new> also accepts a list of type names.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::ListStore::new {
|
|
return _common_tree_model_new ('ListStore', @_);
|
|
}
|
|
|
|
=item * Gtk3::ListStore has a C<get> method that calls C<Gtk3::TreeModel::get>
|
|
instead of C<Glib::Object::get>.
|
|
|
|
=cut
|
|
|
|
# Reroute 'get' to Gtk3::TreeModel instead of Glib::Object.
|
|
sub Gtk3::ListStore::get {
|
|
return Gtk3::TreeModel::get (@_);
|
|
}
|
|
|
|
=item * C<Gtk3::ListStore::insert_with_values> also accepts a list of C<<
|
|
column => value >> pairs and reroutes to C<insert_with_valuesv>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::ListStore::insert_with_values {
|
|
my ($model, $position, @columns_and_values) = @_;
|
|
my ($columns, $values) = _unpack_keys_and_values (\@columns_and_values);
|
|
if (not defined $columns) {
|
|
croak ("Usage: Gtk3::ListStore::insert_with_values (\$model, \$position, \\\@columns, \\\@values)\n",
|
|
" -or-: Gtk3::ListStore::insert_with_values (\$model, \$position, \$column1 => \$value1, ...)");
|
|
}
|
|
my @wrapped_values = ();
|
|
foreach my $i (0..$#{$columns}) {
|
|
my $column_type = $model->get_column_type ($columns->[$i]);
|
|
push @wrapped_values,
|
|
Glib::Object::Introspection::GValueWrapper->new (
|
|
$column_type, $values->[$i]);
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'ListStore', 'insert_with_valuesv', # FIXME: missing rename-to annotation?
|
|
$model, $position, $columns, \@wrapped_values);
|
|
}
|
|
|
|
=item * C<Gtk3::ListStore::set> also accepts a list of C<< column => value >>
|
|
pairs.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::ListStore::set {
|
|
return _common_tree_model_set ('ListStore', @_);
|
|
}
|
|
|
|
=item * C<Gtk3::Menu::popup> reroutes to C<popup_for_device> for better
|
|
callback handling.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Menu::popup {
|
|
my $self = shift;
|
|
$self->popup_for_device (undef, @_);
|
|
}
|
|
|
|
=item * C<Gtk3::Menu::popup_for_device> allows the given menu position func to
|
|
return only x and y coordinates, defaulting C<push_in> to FALSE.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Menu::popup_for_device {
|
|
my ($menu, $device, $parent_menu_shell, $parent_menu_item, $func, $data, $button, $activate_time) = @_;
|
|
my $real_func = $func ? sub {
|
|
my @stuff = eval { $func->(@_) };
|
|
if ($@) {
|
|
warn "*** menu position callback ignoring error: $@";
|
|
}
|
|
if (@stuff == 3) {
|
|
return (@stuff);
|
|
} elsif (@stuff == 2) {
|
|
return (@stuff, Glib::FALSE); # provide a default for push_in
|
|
} else {
|
|
warn "*** menu position callback must return two integers " .
|
|
"(x, y) or two integers and a boolean (x, y, push_in)";
|
|
return (0, 0, Glib::FALSE);
|
|
}
|
|
} : undef;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Menu', 'popup_for_device',
|
|
$menu, $device, $parent_menu_shell, $parent_menu_item, $real_func, $data, $button, $activate_time);
|
|
}
|
|
|
|
=item * The default C<new> constructor of Gtk3::MenuItem reroutes to
|
|
C<new_with_mnemonic> if given an extra argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::MenuItem::new {
|
|
my ($class, $mnemonic) = @_;
|
|
if (defined $mnemonic) {
|
|
return $class->new_with_mnemonic ($mnemonic);
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'MenuItem', 'new', @_);
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::MessageDialog::new> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::MessageDialog::new {
|
|
my ($class, $parent, $flags, $type, $buttons, $format, @args) = @_;
|
|
my $dialog = Glib::Object::new ($class, message_type => $type,
|
|
buttons => $buttons);
|
|
if (defined $format) {
|
|
# sprintf can handle empty @args
|
|
my $msg = sprintf $format, @args;
|
|
$dialog->set (text => $msg);
|
|
}
|
|
if (defined $parent) {
|
|
$dialog->set_transient_for ($parent);
|
|
}
|
|
if (! eval { $flags->isa ('Gtk3::DialogFlags'); }) {
|
|
$flags = Gtk3::DialogFlags->new ($flags);
|
|
}
|
|
if ($flags & 'modal') {
|
|
$dialog->set_modal (Glib::TRUE);
|
|
}
|
|
if ($flags & 'destroy-with-parent') {
|
|
$dialog->set_destroy_with_parent (Glib::TRUE);
|
|
}
|
|
return $dialog;
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::MessageDialog::new_with_markup> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::MessageDialog::new_with_markup {
|
|
my ($class, $parent, $flags, $type, $buttons, $format, @args) = @_;
|
|
my $dialog = Gtk3::MessageDialog::new ($class, $parent, $flags, $type, $buttons, undef);
|
|
if (defined $format) {
|
|
my $markup = sprintf $format, @args;
|
|
$dialog->set_markup ($markup);
|
|
}
|
|
return $dialog;
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::MessageDialog::format_secondary_text> and
|
|
C<Gtk3::MessageDialog::format_secondary_markup> is provided
|
|
|
|
=cut
|
|
|
|
sub Gtk3::MessageDialog::format_secondary_text {
|
|
my ($dialog, $format, @args) = @_;
|
|
|
|
my $text = sprintf $format, @args;
|
|
$dialog->set ('secondary-text' => $text, 'secondary-use-markup' => 0);
|
|
}
|
|
|
|
sub Gtk3::MessageDialog::format_secondary_markup {
|
|
my ($dialog, $format, @args) = @_;
|
|
|
|
my $text = sprintf $format, @args;
|
|
$dialog->set ('secondary-text' => $text, 'secondary-use-markup' => 1);
|
|
}
|
|
|
|
=item * The group handling in the constructors and accessors of
|
|
Gtk3::RadioAction, Gtk3::RadioButton, Gtk3::RadioMenuItem and
|
|
Gtk3::RadioToolButton is amended to work correctly when given array refs of
|
|
group members or single group members.
|
|
|
|
=cut
|
|
|
|
# Gtk3::RadioAction, Gtk3::RadioButton, Gtk3::RadioMenuItem and
|
|
# Gtk3::RadioToolButton constructors.
|
|
{
|
|
no strict qw(refs);
|
|
|
|
my $group_converter = sub {
|
|
my ($ctor, $group_or_member, $package) = @_;
|
|
local $@;
|
|
# undef => []
|
|
if (!defined $group_or_member) {
|
|
return ($ctor, []);
|
|
}
|
|
# [] => []
|
|
elsif (eval { $#$group_or_member == -1 }) {
|
|
return ($ctor, []);
|
|
}
|
|
# [member1, ...] => member1
|
|
elsif (eval { $#$group_or_member >= 0 }) {
|
|
my $member = $group_or_member->[0];
|
|
if (defined $member) {
|
|
return ($ctor . '_from_widget', $member);
|
|
}
|
|
return ($ctor, []);
|
|
}
|
|
# member => member
|
|
elsif (eval { $group_or_member->isa ('Gtk3::' . $package) }) {
|
|
return ($ctor . '_from_widget', $group_or_member);
|
|
}
|
|
else {
|
|
croak ('Unhandled group or member argument encountered');
|
|
}
|
|
};
|
|
|
|
# Gtk3::RadioAction/Gtk3::RadioButton/Gtk3::RadioMenuItem/Gtk3::RadioToolButton
|
|
foreach my $package (qw/RadioAction RadioButton RadioMenuItem RadioToolButton/) {
|
|
*{'Gtk3::' . $package . '::set_group'} = sub {
|
|
my ($button, $group) = @_;
|
|
my $real_group = $group;
|
|
if (eval { $#$group >= 0 }) {
|
|
$real_group = $group->[0];
|
|
}
|
|
$button->set (group => $real_group);
|
|
};
|
|
}
|
|
|
|
# Gtk3::RadioButton/Gtk3::RadioMenuItem
|
|
foreach my $package (qw/RadioButton RadioMenuItem/) {
|
|
foreach my $ctor (qw/new new_with_label new_with_mnemonic/) {
|
|
# Avoid using the list-based API, as G:O:I does not support the memory
|
|
# ownership semantics. Use the item-based API instead.
|
|
*{'Gtk3::' . $package . '::' . $ctor} = sub {
|
|
my ($class, $group_or_member, @rest) = @_;
|
|
my ($real_ctor, $real_group_or_member) =
|
|
$group_converter->($ctor, $group_or_member, $package);
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $package, $real_ctor,
|
|
$class, $real_group_or_member, @rest);
|
|
};
|
|
|
|
# Work around <https://bugzilla.gnome.org/show_bug.cgi?id=679563>.
|
|
*{'Gtk3::' . $package . '::' . $ctor . '_from_widget'} = sub {
|
|
my ($class, $member, @rest) = @_;
|
|
my $real_ctor = $ctor;
|
|
my $real_group_or_member = $member;
|
|
if (!defined $member) {
|
|
$real_group_or_member = [];
|
|
} else {
|
|
$real_ctor .= '_from_widget';
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $package, $real_ctor,
|
|
$class, $real_group_or_member, @rest);
|
|
};
|
|
}
|
|
}
|
|
|
|
# GtkRadioToolButton
|
|
foreach my $ctor (qw/new new_from_stock/) {
|
|
# Avoid using the list-based API, as G:O:I does not support the memory
|
|
# ownership semantics. Use the item-based API instead.
|
|
*{'Gtk3::RadioToolButton::' . $ctor} = sub {
|
|
my ($class, $group_or_member, @rest) = @_;
|
|
my ($real_ctor, $real_group_or_member) =
|
|
$group_converter->($ctor, $group_or_member, 'RadioToolButton');
|
|
$real_ctor =~ s/_from_stock_from_/_with_stock_from_/; # you gotta be kidding me...
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'RadioToolButton', $real_ctor,
|
|
$class, $real_group_or_member, @rest);
|
|
};
|
|
}
|
|
}
|
|
|
|
=item * Perl reimplementations of C<Gtk3::RecentChooserDialog::new> and
|
|
C<new_for_manager> are provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::RecentChooserDialog::new {
|
|
my ($class, $title, $parent, @buttons) = @_;
|
|
my $dialog = Glib::Object::new ($class, title => $title);
|
|
for (my $i = 0; $i < @buttons; $i += 2) {
|
|
$dialog->add_button ($buttons[$i], $buttons[$i+1]);
|
|
}
|
|
if (defined $parent) {
|
|
$dialog->set_transient_for ($parent);
|
|
}
|
|
return $dialog;
|
|
}
|
|
|
|
sub Gtk3::RecentChooserDialog::new_for_manager {
|
|
my ($class, $title, $parent, $mgr, @buttons) = @_;
|
|
my $dialog = Glib::Object::new ($class, title => $title,
|
|
recent_manager => $mgr);
|
|
for (my $i = 0; $i < @buttons; $i += 2) {
|
|
$dialog->add_button ($buttons[$i], $buttons[$i+1]);
|
|
}
|
|
if (defined $parent) {
|
|
$dialog->set_transient_for ($parent);
|
|
}
|
|
return $dialog;
|
|
}
|
|
|
|
=item * Redirects are provided from C<Gtk3::Stock::[function]> to
|
|
C<Gtk3::stock_[function]> for C<add>, C<add_static>, C<list_ids>, C<lookup> and
|
|
C<set_translate_func>.
|
|
|
|
=cut
|
|
|
|
{
|
|
no strict qw/refs/;
|
|
|
|
my %stock_name_corrections = (
|
|
'Gtk3::Stock::add' => 'Gtk3::stock_add',
|
|
'Gtk3::Stock::add_static' => 'Gtk3::stock_add_static',
|
|
'Gtk3::Stock::list_ids' => 'Gtk3::stock_list_ids',
|
|
'Gtk3::Stock::lookup' => 'Gtk3::stock_lookup',
|
|
'Gtk3::Stock::set_translate_func' => 'Gtk3::stock_set_translate_func',
|
|
);
|
|
|
|
foreach my $new (keys %stock_name_corrections) {
|
|
*{$new} = \&{$stock_name_corrections{$new}};
|
|
}
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::StyleContext::get> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::StyleContext::get {
|
|
my ($context, $state, @properties) = @_;
|
|
my @values = map { $context->get_property ($_, $state) } @properties;
|
|
return @values[0..$#values];
|
|
}
|
|
|
|
=item * An override for C<Gtk3::TargetEntry::new> is provided that
|
|
automatically handles the conversion of the C<flags> argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TargetEntry::new {
|
|
my ($class, $target, $flags, $info) = @_;
|
|
if ($flags !~ /^\d+$/) {
|
|
$flags = Glib::Object::Introspection->convert_sv_to_flags (
|
|
"Gtk3::TargetFlags", $flags)
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TargetEntry', 'new', $class, $target, $flags, $info);
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::TextBuffer::create_tag> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TextBuffer::create_tag {
|
|
my ($buffer, $tag_name, @rest) = @_;
|
|
if (@rest % 2) {
|
|
croak ('Usage: $buffer->create_tag ($tag_name, $property1 => $value1, ...');
|
|
}
|
|
my $tag = Gtk3::TextTag->new ($tag_name);
|
|
my $tag_table = $buffer->get_tag_table;
|
|
$tag_table->add ($tag);
|
|
for (my $i = 0 ; $i < @rest ; $i += 2) {
|
|
$tag->set_property ($rest[$i], $rest[$i+1]);
|
|
}
|
|
return $tag;
|
|
}
|
|
|
|
=item * The C<length> arguments of C<Gtk3::TextBuffer::insert>,
|
|
C<insert_at_cursor>, C<insert_interactive>, C<insert_interactive_at_cursor>,
|
|
C<insert_markup> and C<set_text> are optional.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TextBuffer::insert {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'insert',
|
|
@_ == 4 ? @_ : (@_[0,1,2], -1)); # wants length in bytes
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::insert_at_cursor {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'insert_at_cursor',
|
|
@_ == 3 ? @_ : (@_[0,1], -1)); # wants length in bytes
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::insert_interactive {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'insert_interactive',
|
|
@_ == 5 ? @_ : (@_[0,1,2], -1, $_[3])); # wants length in bytes
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::insert_interactive_at_cursor {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'insert_interactive_at_cursor',
|
|
@_ == 4 ? @_ : (@_[0,1], -1, $_[2])); # wants length in bytes
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::insert_markup {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'insert_markup',
|
|
@_ == 4 ? @_ : (@_[0,1,2], -1)); # wants length in bytes
|
|
}
|
|
|
|
=item * Perl reimplementations of C<Gtk3::TextBuffer::insert_with_tags> and
|
|
C<insert_with_tags_by_name> are provided which do not require a C<length>
|
|
argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TextBuffer::insert_with_tags {
|
|
my ($buffer, $iter, $text, @tags) = @_;
|
|
my $start_offset = $iter->get_offset;
|
|
$buffer->insert ($iter, $text);
|
|
my $start = $buffer->get_iter_at_offset ($start_offset);
|
|
foreach my $tag (@tags) {
|
|
$buffer->apply_tag ($tag, $start, $iter);
|
|
}
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::insert_with_tags_by_name {
|
|
my ($buffer, $iter, $text, @tag_names) = @_;
|
|
my $start_offset = $iter->get_offset;
|
|
$buffer->insert ($iter, $text);
|
|
my $tag_table = $buffer->get_tag_table;
|
|
my $start = $buffer->get_iter_at_offset ($start_offset);
|
|
foreach my $tag_name (@tag_names) {
|
|
my $tag = $tag_table->lookup ($tag_name);
|
|
if (!$tag) {
|
|
warn "no tag with name $tag_name";
|
|
} else {
|
|
$buffer->apply_tag ($tag, $start, $iter);
|
|
}
|
|
}
|
|
}
|
|
|
|
sub Gtk3::TextBuffer::set_text {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TextBuffer', 'set_text',
|
|
@_ == 3 ? @_ : (@_[0,1], -1)); # wants length in bytes
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::TreeModel::get> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeModel::get {
|
|
my ($model, $iter, @columns) = @_;
|
|
if (!@columns) {
|
|
@columns = (0..($model->get_n_columns-1));
|
|
}
|
|
my @values = map { $model->get_value ($iter, $_) } @columns;
|
|
return @values[0..$#values];
|
|
}
|
|
|
|
=item * A redirect is added from C<Gtk3::TreeModelFilter::new> to
|
|
<Gtk3::TreeModel::filter_new> so that Gtk3::TreeModelFilter objects can be
|
|
constructed normally.
|
|
|
|
=cut
|
|
|
|
# Not needed anymore once <https://bugzilla.gnome.org/show_bug.cgi?id=646742>
|
|
# is fixed.
|
|
sub Gtk3::TreeModelFilter::new {
|
|
my ($class, $child_model, $root) = @_;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreeModel', 'filter_new', $child_model, $root);
|
|
}
|
|
|
|
=item * Gtk3::TreeModelFilter has a C<get> method that calls
|
|
C<Gtk3::TreeModel::get> instead of C<Glib::Object::get>.
|
|
|
|
=cut
|
|
|
|
# Reroute 'get' to Gtk3::TreeModel instead of Glib::Object.
|
|
sub Gtk3::TreeModelFilter::get {
|
|
return Gtk3::TreeModel::get (@_);
|
|
}
|
|
|
|
=item * Prior to gtk+ 3.24.14, a redirect is added from
|
|
C<Gtk3::TreeModelSort::new_with_model> to
|
|
<Gtk3::TreeModel::sort_new_with_model> so that Gtk3::TreeModelSort objects can
|
|
be constructed normally.
|
|
|
|
=cut
|
|
|
|
# Not needed anymore once <https://bugzilla.gnome.org/show_bug.cgi?id=646742>
|
|
# is fixed. This never happened, but in gtk+ 3.24.14, the return type
|
|
# annotation was changed: <https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1134>.
|
|
sub Gtk3::TreeModelSort::new_with_model {
|
|
if (Gtk3::CHECK_VERSION (3, 24, 14)) {
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreeModelSort', 'new_with_model', @_);
|
|
} else {
|
|
my ($class, $child_model) = @_;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreeModel', 'sort_new_with_model', $child_model);
|
|
}
|
|
}
|
|
|
|
=item * Gtk3::TreeModelSort has a C<get> method that calls
|
|
C<Gtk3::TreeModel::get> instead of C<Glib::Object::get>.
|
|
|
|
=cut
|
|
|
|
# Reroute 'get' to Gtk3::TreeModel instead of Glib::Object.
|
|
sub Gtk3::TreeModelSort::get {
|
|
return Gtk3::TreeModel::get (@_);
|
|
}
|
|
|
|
=item * C<Gtk3::TreePath::new> redirects to C<new_from_string> if an additional
|
|
argument is given.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreePath::new {
|
|
my ($class, @args) = @_;
|
|
my $method = (@args == 1) ? 'new_from_string' : 'new';
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreePath', $method, @_);
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::TreePath::new_from_indices> is
|
|
provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreePath::new_from_indices {
|
|
my ($class, @indices) = @_;
|
|
my $path = Gtk3::TreePath->new;
|
|
foreach (@indices) {
|
|
$path->append_index ($_);
|
|
}
|
|
return $path;
|
|
}
|
|
|
|
=item * C<Gtk3::TreeStore::new> also accepts a list of type names.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeStore::new {
|
|
return _common_tree_model_new ('TreeStore', @_);
|
|
}
|
|
|
|
=item * Gtk3::TreeStore has a C<get> method that calls C<Gtk3::TreeModel::get>
|
|
instead of C<Glib::Object::get>.
|
|
|
|
=cut
|
|
|
|
# Reroute 'get' to Gtk3::TreeModel instead of Glib::Object.
|
|
sub Gtk3::TreeStore::get {
|
|
return Gtk3::TreeModel::get (@_);
|
|
}
|
|
|
|
=item * C<Gtk3::TreeStore::insert_with_values> also accepts a list of C<<
|
|
column => value >> pairs.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeStore::insert_with_values {
|
|
my ($model, $parent, $position, @columns_and_values) = @_;
|
|
my ($columns, $values) = _unpack_keys_and_values (\@columns_and_values);
|
|
if (not defined $columns) {
|
|
croak ("Usage: Gtk3::TreeStore::insert_with_values (\$model, \$parent, \$position, \\\@columns, \\\@values)\n",
|
|
" -or-: Gtk3::TreeStore::insert_with_values (\$model, \$parent, \$position, \$column1 => \$value1, ...)");
|
|
}
|
|
my @wrapped_values = ();
|
|
foreach my $i (0..$#{$columns}) {
|
|
my $column_type = $model->get_column_type ($columns->[$i]);
|
|
push @wrapped_values,
|
|
Glib::Object::Introspection::GValueWrapper->new (
|
|
$column_type, $values->[$i]);
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreeStore', 'insert_with_values',
|
|
$model, $parent, $position, $columns, \@wrapped_values);
|
|
}
|
|
|
|
=item * C<Gtk3::TreeStore::set> also accepts a list of C<< column => value >>
|
|
pairs.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeStore::set {
|
|
return _common_tree_model_set ('TreeStore', @_);
|
|
}
|
|
|
|
=item * C<Gtk3::TreeView::new> redirects to C<new_with_model> if an additional
|
|
argument is given.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeView::new {
|
|
my ($class, @args) = @_;
|
|
my $method = (@args == 1) ? 'new_with_model' : 'new';
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'TreeView', $method, @_);
|
|
}
|
|
|
|
=item * A Perl reimplementation of
|
|
C<Gtk3::TreeView::insert_column_with_attributes> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeView::insert_column_with_attributes {
|
|
my ($tree_view, $position, $title, $cell, @rest) = @_;
|
|
if (@rest % 2) {
|
|
croak ('Usage: $tree_view->insert_column_with_attributes (position, title, cell_renderer, attr1 => col1, ...)');
|
|
}
|
|
my $column = Gtk3::TreeViewColumn->new;
|
|
my $n = $tree_view->insert_column ($column, $position);
|
|
$column->set_title ($title);
|
|
$column->pack_start ($cell, Glib::TRUE);
|
|
for (my $i = 0; $i < @rest; $i += 2) {
|
|
$column->add_attribute ($cell, $rest[$i], $rest[$i+1]);
|
|
}
|
|
return $n;
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::TreeViewColumn::new_with_attributes>
|
|
is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::TreeViewColumn::new_with_attributes {
|
|
my ($class, $title, $cell, @rest) = @_;
|
|
if (@rest % 2) {
|
|
croak ('Usage: Gtk3::TreeViewColumn->new_with_attributes (title, cell_renderer, attr1 => col1, ...)');
|
|
}
|
|
my $object = $class->new;
|
|
$object->set_title ($title);
|
|
$object->pack_start ($cell, Glib::TRUE);
|
|
for (my $i = 0; $i < @rest; $i += 2) {
|
|
$object->add_attribute ($cell, $rest[$i], $rest[$i+1]);
|
|
}
|
|
return $object;
|
|
}
|
|
|
|
=item * Perl reimplementations of C<Gtk3::TreeViewColumn::set_attributes> and
|
|
C<Gtk3::CellLayout::set_attributes> are provided.
|
|
|
|
=cut
|
|
|
|
# Gtk3::TreeViewColumn::set_attributes and Gtk3::CellLayout::set_attributes
|
|
{
|
|
no strict 'refs';
|
|
foreach my $package (qw/TreeViewColumn CellLayout/) {
|
|
*{'Gtk3::' . $package . '::set_attributes'} = sub {
|
|
my ($object, $cell, @rest) = @_;
|
|
if (@rest % 2) {
|
|
croak ('Usage: $object->set_attributes (cell_renderer, attr1 => col1, ...)');
|
|
}
|
|
$object->clear_attributes ($cell);
|
|
for (my $i = 0; $i < @rest; $i += 2) {
|
|
$object->add_attribute ($cell, $rest[$i], $rest[$i+1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::UIManager::add_ui_from_string> takes no C<length> argument.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::UIManager::add_ui_from_string {
|
|
my ($manager, $string) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'UIManager', 'add_ui_from_string',
|
|
$manager, $string, -1); # wants length in bytes
|
|
}
|
|
|
|
=item * C<Gtk3::VBox::new> uses the defaults homogeneous = FALSE and spacing =
|
|
5.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::VBox::new {
|
|
my ($class, $homogeneous, $spacing) = @_;
|
|
$homogeneous = 0 unless defined $homogeneous;
|
|
$spacing = 5 unless defined $spacing;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'VBox', 'new', $class, $homogeneous, $spacing);
|
|
}
|
|
|
|
=item * C<Gtk3::Widget::add_events> and C<Gtk3::Widget::set_events> also accept
|
|
strings, array references and C<Gtk3::Gdk::EventMask> objects for the C<events>
|
|
parameter.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Widget::add_events {
|
|
my ($widget, $events) = @_;
|
|
eval {
|
|
$events = Glib::Object::Introspection->convert_sv_to_flags (
|
|
'Gtk3::Gdk::EventMask', $events);
|
|
};
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Widget', 'add_events', $widget, $events);
|
|
}
|
|
|
|
sub Gtk3::Widget::set_events {
|
|
my ($widget, $events) = @_;
|
|
eval {
|
|
$events = Glib::Object::Introspection->convert_sv_to_flags (
|
|
'Gtk3::Gdk::EventMask', $events);
|
|
};
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Widget', 'set_events', $widget, $events);
|
|
}
|
|
|
|
=item * C<Gtk3::Widget::get_events> returns a C<Gtk3::Gdk::EventMask> object
|
|
that can also be compared to numeric values with C<< == >> and C<< >= >>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Widget::get_events {
|
|
my ($widget) = @_;
|
|
my $events = Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Widget', 'get_events', $widget);
|
|
return Glib::Object::Introspection->convert_flags_to_sv (
|
|
'Gtk3::Gdk::EventMask', $events);
|
|
}
|
|
|
|
sub Gtk3::Widget::render_icon {
|
|
my ($widget, $stock_id, $size, $detail) = @_;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Widget', 'render_icon', $widget, $stock_id,
|
|
$_GTK_ICON_SIZE_NICK_TO_ID->($size), $detail);
|
|
}
|
|
|
|
=item * C<Gtk3::Widget::find_style_property> and
|
|
C<Gtk3::Widget::list_style_properties> are forwarded to the corresponding
|
|
functions in C<Gtk3::WidgetClass>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Widget::find_style_property {
|
|
return Gtk3::WidgetClass::find_style_property (@_);
|
|
}
|
|
|
|
sub Gtk3::Widget::list_style_properties {
|
|
my $ref = Gtk3::WidgetClass::list_style_properties (@_);
|
|
return if not defined $ref;
|
|
return wantarray ? @$ref : $ref->[$#$ref];
|
|
}
|
|
|
|
=item * A Perl reimplementation of C<Gtk3::Widget::style_get> is provided.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Widget::style_get {
|
|
my ($widget, @rest) = @_;
|
|
my $properties = _rest_to_ref (\@rest);
|
|
my @values;
|
|
foreach my $property (@$properties) {
|
|
my $pspec = Gtk3::WidgetClass::find_style_property ($widget, $property);
|
|
croak "Cannot find type information for property '$property' on $widget"
|
|
unless defined $pspec;
|
|
my $value_wrapper = Glib::Object::Introspection::GValueWrapper->new (
|
|
$pspec->get_value_type, undef);
|
|
$widget->style_get_property ($property, $value_wrapper);
|
|
push @values, $value_wrapper->get_value;
|
|
}
|
|
return @values[0..$#values];
|
|
}
|
|
|
|
=item * C<Gtk3::Window::new> uses the default type = 'toplevel'.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Window::new {
|
|
my ($class, $type) = @_;
|
|
$type = 'toplevel' unless defined $type;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, 'Window', 'new', $class, $type);
|
|
}
|
|
|
|
# --- Gdk ---
|
|
|
|
=item * A constructor C<Gtk3::Gdk::RGBA::new> is provided that can be called as
|
|
C<< Gtk3::Gdk::RGBA->new (r, g, b, a) >>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::RGBA::new {
|
|
my ($class, @rest) = @_;
|
|
# Handle Gtk3::Gdk::RGBA->new (r, g, b, a) specially.
|
|
if (4 == @rest) {
|
|
my %data;
|
|
@data{qw/red green blue alpha/} = @rest;
|
|
return Glib::Boxed::new ($class, \%data);
|
|
}
|
|
# Fall back to Glib::Boxed::new.
|
|
return Glib::Boxed::new ($class, @rest);
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::RGBA::parse> can be called as a function returning a new
|
|
instance (C<< $rgba = Gtk3::Gdk::RGBA::parse ($spec) >>) or as a method (C<<
|
|
$rgba->parse ($spec) >>).
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::RGBA::parse {
|
|
my $have_instance;
|
|
{
|
|
local $@;
|
|
$have_instance = eval { $_[0]->isa ('Gtk3::Gdk::RGBA') };
|
|
}
|
|
# This needs to be switched around if/when
|
|
# <https://bugzilla.gnome.org/show_bug.cgi?id=682125> is fixed.
|
|
if ($have_instance) {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GDK_BASENAME, 'RGBA', 'parse', @_);
|
|
} else {
|
|
my $instance = Gtk3::Gdk::RGBA->new;
|
|
my $success = Glib::Object::Introspection->invoke (
|
|
$_GDK_BASENAME, 'RGBA', 'parse',
|
|
$instance, @_);
|
|
return $success ? $instance : undef;
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::Window::new> optionally computes the C<attr_mask>
|
|
automatically from the given C<attr>.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Window::new {
|
|
my ($class, $parent, $attr, $attr_mask) = @_;
|
|
if (not defined $attr_mask) {
|
|
$attr_mask = Gtk3::Gdk::WindowAttributesType->new ([]);
|
|
if (exists $attr->{title}) { $attr_mask |= 'GDK_WA_TITLE' }
|
|
if (exists $attr->{x}) { $attr_mask |= 'GDK_WA_X' }
|
|
if (exists $attr->{y}) { $attr_mask |= 'GDK_WA_Y' }
|
|
if (exists $attr->{cursor}) { $attr_mask |= 'GDK_WA_CURSOR' }
|
|
if (exists $attr->{visual}) { $attr_mask |= 'GDK_WA_VISUAL' }
|
|
if (exists $attr->{wmclass_name} && exists $attr->{wmclass_class}) { $attr_mask |= 'GDK_WA_WMCLASS' }
|
|
if (exists $attr->{override_redirect}) { $attr_mask |= 'GDK_WA_NOREDIR' }
|
|
if (exists $attr->{type_hint}) { $attr_mask |= 'GDK_WA_TYPE_HINT' }
|
|
if (!Gtk3::CHECK_VERSION (3, 4, 4)) {
|
|
# Before 3.4.4 or 3.5.6, the attribute mask parameter lacked proper
|
|
# annotations, hence we numerify it here. FIXME: This breaks
|
|
# encapsulation.
|
|
$attr_mask = $$attr_mask;
|
|
}
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GDK_BASENAME, 'Window', 'new',
|
|
$class, $parent, $attr, $attr_mask);
|
|
}
|
|
|
|
# --- GdkPixbuf ---
|
|
|
|
sub Gtk3::Gdk::Pixbuf::CHECK_VERSION {
|
|
my ($major, $minor, $micro) = @_;
|
|
return
|
|
(Gtk3::Gdk::PIXBUF_MAJOR () > $major) ||
|
|
(Gtk3::Gdk::PIXBUF_MAJOR () == $major && Gtk3::Gdk::PIXBUF_MINOR () > $minor) ||
|
|
(Gtk3::Gdk::PIXBUF_MAJOR () == $major && Gtk3::Gdk::PIXBUF_MINOR () == $minor && Gtk3::Gdk::PIXBUF_MICRO () >= $micro);
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::Pixbuf::get_pixels> returns a byte string.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Pixbuf::get_pixels {
|
|
my $pixel_aref = Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', 'get_pixels', @_);
|
|
return pack 'C*', @{$pixel_aref};
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::Pixbuf::new_from_data> is reimplemented in terms of
|
|
C<new_from_bytes> (with gdk-pixbuf >= 2.32) or C<new_from_inline> (with
|
|
gtk-pixbuf < 2.32) for correct memory management. No C<destroy_fn> and
|
|
C<destroy_fn_data> arguments are needed.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Pixbuf::new_from_data {
|
|
my ($class, $data, $colorspace, $has_alpha, $bits_per_sample, $width, $height, $rowstride) = @_;
|
|
if (Gtk3::Gdk::Pixbuf::CHECK_VERSION (2, 32, 0)) {
|
|
my $packed_data = ref($data) eq 'ARRAY' ? pack 'C*', @$data : $data;
|
|
return Gtk3::Gdk::Pixbuf->new_from_bytes(
|
|
Glib::Bytes->new($packed_data),
|
|
$colorspace, $has_alpha,
|
|
$bits_per_sample, $width,
|
|
$height, $rowstride);
|
|
} else {
|
|
die 'Only RGB is currently supported' unless $colorspace eq 'rgb';
|
|
die 'Only 8 bits per pixel are currently supported'
|
|
unless $bits_per_sample == 8;
|
|
my $length = Gtk3::Gdk::PIXDATA_HEADER_LENGTH () +
|
|
$rowstride*$height;
|
|
my $type = Gtk3::Gdk::PixdataType->new ([qw/sample_width_8 encoding_raw/]);
|
|
$type |= $has_alpha ? 'color_type_rgba' : 'color_type_rgb';
|
|
my @header_numbers = (0x47646b50,
|
|
$length,
|
|
$$type, # FIXME: This kind of breaks encapsulation.
|
|
$rowstride,
|
|
$width,
|
|
$height);
|
|
# Convert to 8 bit unsigned chars, padding to 32 bit little-endian first.
|
|
my @header = map { unpack ("C*", pack ("N", $_)) } @header_numbers;
|
|
my $inline_data = _unpack_unless_array_ref ($data);
|
|
unshift @$inline_data, @header;
|
|
return Gtk3::Gdk::Pixbuf->new_from_inline ($inline_data);
|
|
}
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::Pixbuf::new_from_inline> does not take a C<copy_pixels>
|
|
argument. It is always set to TRUE for correct memory management.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Pixbuf::new_from_inline {
|
|
my ($class, $data) = @_;
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', 'new_from_inline',
|
|
$class, _unpack_unless_array_ref ($data), Glib::TRUE); # always copy pixels
|
|
}
|
|
|
|
=item * C<Gtk3::Gdk::Pixbuf::new_from_xpm_data> also accepts a list of XPM
|
|
lines.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Pixbuf::new_from_xpm_data {
|
|
my ($class, @rest) = @_;
|
|
my $data = _rest_to_ref (\@rest);
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', 'new_from_xpm_data',
|
|
$class, $data);
|
|
}
|
|
|
|
# Version check for the new annotations described in
|
|
# <https://bugzilla.gnome.org/show_bug.cgi?id=670372>.
|
|
my $_GET_SAVE_VARIANT = sub {
|
|
my ($method) = @_;
|
|
if (Gtk3::Gdk::Pixbuf::CHECK_VERSION (2, 31, 3)) {
|
|
return $method . 'v';
|
|
} elsif (Gtk3::Gdk::Pixbuf::CHECK_VERSION (2, 31, 2)) {
|
|
return $method;
|
|
} else {
|
|
return $method . 'v';
|
|
}
|
|
};
|
|
|
|
=item * C<Gtk3::Gdk::Pixbuf::save>, C<save_to_buffer> and C<save_to_callback>
|
|
also accept C<< key => value >> pairs and invoke the correct C function as
|
|
appropriate for the current gdk-pixbuf version.
|
|
|
|
=cut
|
|
|
|
sub Gtk3::Gdk::Pixbuf::save {
|
|
my ($pixbuf, $filename, $type, @rest) = @_;
|
|
my ($keys, $values) = _unpack_keys_and_values (\@rest);
|
|
if (not defined $keys) {
|
|
croak ("Usage: \$pixbuf->save (\$filename, \$type, \\\@keys, \\\@values)\n",
|
|
" -or-: \$pixbuf->save (\$filename, \$type, \$key1 => \$value1, ...)");
|
|
}
|
|
my $method = $_GET_SAVE_VARIANT->('save');
|
|
Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', $method,
|
|
$pixbuf, $filename, $type, $keys, $values);
|
|
}
|
|
|
|
sub Gtk3::Gdk::Pixbuf::save_to_buffer {
|
|
my ($pixbuf, $type, @rest) = @_;
|
|
my ($keys, $values) = _unpack_keys_and_values (\@rest);
|
|
if (not defined $keys) {
|
|
croak ("Usage: \$pixbuf->save_to_buffer (\$type, \\\@keys, \\\@values)\n",
|
|
" -or-: \$pixbuf->save_to_buffer (\$type, \$key1 => \$value1, ...)");
|
|
}
|
|
my $method = $_GET_SAVE_VARIANT->('save_to_buffer');
|
|
my (undef, $buffer) =
|
|
Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', $method,
|
|
$pixbuf, $type, $keys, $values);
|
|
return $buffer;
|
|
}
|
|
|
|
sub Gtk3::Gdk::Pixbuf::save_to_callback {
|
|
my ($pixbuf, $save_func, $user_data, $type, @rest) = @_;
|
|
my ($keys, $values) = _unpack_keys_and_values (\@rest);
|
|
if (not defined $keys) {
|
|
croak ("Usage: \$pixbuf->save_to_callback (\$save_func, \$user_data, \$type, \\\@keys, \\\@values)\n",
|
|
" -or-: \$pixbuf->save_to_callback (\$save_func, \$user_data, \$type, \$key1 => \$value1, ...)");
|
|
}
|
|
my $method = $_GET_SAVE_VARIANT->('save_to_callback');
|
|
Glib::Object::Introspection->invoke (
|
|
$_GDK_PIXBUF_BASENAME, 'Pixbuf', $method,
|
|
$pixbuf, $save_func, $user_data, $type, $keys, $values);
|
|
}
|
|
|
|
# --- Pango ---
|
|
|
|
=item * The C<length> arguments of C<Pango::Layout::set_text> and C<set_markup>
|
|
are optional.
|
|
|
|
=cut
|
|
|
|
sub Pango::Layout::set_text {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_PANGO_BASENAME, 'Layout', 'set_text',
|
|
@_ == 3 ? @_ : (@_[0,1], -1)); # wants length in bytes
|
|
}
|
|
|
|
sub Pango::Layout::set_markup {
|
|
return Glib::Object::Introspection->invoke (
|
|
$_PANGO_BASENAME, 'Layout', 'set_markup',
|
|
@_ == 3 ? @_ : (@_[0,1], -1)); # wants length in bytes
|
|
}
|
|
|
|
=back
|
|
|
|
=cut
|
|
|
|
# - Fixes ------------------------------------------------------------------- #
|
|
|
|
=head2 Perl compatibility
|
|
|
|
As of 5.20.0, perl does not automatically re-check the locale environment for
|
|
changes. If a function thus changes the locale behind perl's back, problems
|
|
might arise whenever numbers are formatted, for example when checking versions.
|
|
To ensure perl's assumption about the locale are up-to-date, the functions
|
|
C<Gtk3::init>, C<init_check>, C<init_with_args> and C<parse_args> are amended
|
|
to let perl know of any changes.
|
|
|
|
=cut
|
|
|
|
# Compatibility with perl 5.20 and non-dot locales. Wrap all functions that
|
|
# might end up calling setlocale() such that POSIX::setlocale() is also called
|
|
# to ensure perl knows about the current locale. See the discussion in
|
|
# <https://rt.perl.org/Public/Bug/Display.html?id=121930>,
|
|
# <https://rt.perl.org/Public/Bug/Display.html?id=121317>,
|
|
# <https://rt.perl.org/Public/Bug/Display.html?id=120723>.
|
|
if ($^V ge v5.20.0) {
|
|
require POSIX;
|
|
no strict 'refs';
|
|
no warnings 'redefine';
|
|
|
|
my $disable_setlocale = 0;
|
|
*{'Gtk3::disable_setlocale'} = sub {
|
|
$disable_setlocale = 1;
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, undef, 'disable_setlocale', @_);
|
|
};
|
|
|
|
# These two already have overrides.
|
|
foreach my $function (qw/Gtk3::init Gtk3::init_check/) {
|
|
my $orig = \&{$function};
|
|
*{$function} = sub {
|
|
if (!$disable_setlocale) {
|
|
POSIX::setlocale (POSIX::LC_ALL (), '');
|
|
}
|
|
$orig->(@_);
|
|
};
|
|
}
|
|
|
|
foreach my $function (qw/init_with_args parse_args/) {
|
|
*{'Gtk3::' . $function} = sub {
|
|
if (!$disable_setlocale) {
|
|
POSIX::setlocale (POSIX::LC_ALL (), '');
|
|
}
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, undef, $function, @_);
|
|
};
|
|
}
|
|
}
|
|
|
|
# - Helpers ----------------------------------------------------------------- #
|
|
|
|
sub _common_tree_model_new {
|
|
my ($package, $class, @types) = @_;
|
|
my $real_types;
|
|
{
|
|
local $@;
|
|
$real_types = (@types == 1 && eval { @{$types[0]} })
|
|
? $types[0]
|
|
: \@types;
|
|
}
|
|
return Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $package, 'new',
|
|
$class, $real_types);
|
|
}
|
|
|
|
sub _common_tree_model_set {
|
|
my ($package, $model, $iter, @columns_and_values) = @_;
|
|
my ($columns, $values) = _unpack_keys_and_values (\@columns_and_values);
|
|
if (not defined $columns) {
|
|
croak ("Usage: Gtk3::${package}::set (\$model, \$iter, \\\@columns, \\\@values)\n",
|
|
" -or-: Gtk3::${package}::set (\$model, \$iter, \$column1 => \$value1, ...)");
|
|
}
|
|
my @wrapped_values = ();
|
|
foreach my $i (0..$#{$columns}) {
|
|
my $column_type = $model->get_column_type ($columns->[$i]);
|
|
push @wrapped_values,
|
|
Glib::Object::Introspection::GValueWrapper->new (
|
|
$column_type, $values->[$i]);
|
|
}
|
|
Glib::Object::Introspection->invoke (
|
|
$_GTK_BASENAME, $package, 'set',
|
|
$model, $iter, $columns, \@wrapped_values);
|
|
}
|
|
|
|
sub _unpack_keys_and_values {
|
|
my ($keys_and_values) = @_;
|
|
my (@keys, @values);
|
|
my $have_array_refs;
|
|
{
|
|
local $@;
|
|
$have_array_refs =
|
|
@$keys_and_values == 2 && eval { @{$keys_and_values->[0]} };
|
|
}
|
|
if ($have_array_refs) {
|
|
@keys = @{$keys_and_values->[0]};
|
|
@values = @{$keys_and_values->[1]};
|
|
} elsif (@$keys_and_values % 2 == 0) {
|
|
# To preserve the order of the key-value pairs, avoid creating an
|
|
# intermediate hash.
|
|
my @range = 0 .. (@$keys_and_values/2-1);
|
|
@keys = @$keys_and_values[map { 2*$_ } @range];
|
|
@values = @$keys_and_values[map { 2*$_+1 } @range];
|
|
} else {
|
|
return ();
|
|
}
|
|
return (\@keys, \@values);
|
|
}
|
|
|
|
sub _unpack_unless_array_ref {
|
|
my ($data) = @_;
|
|
local $@;
|
|
return eval { @{$data} }
|
|
? $data
|
|
: [unpack 'C*', $data];
|
|
}
|
|
|
|
sub _rest_to_ref {
|
|
my ($rest) = @_;
|
|
local $@;
|
|
if (scalar @$rest == 1 && eval { defined $rest->[0]->[0] }) {
|
|
return $rest->[0];
|
|
} else {
|
|
return $rest;
|
|
}
|
|
}
|
|
|
|
package Gtk3::Gdk::EventMask;
|
|
$Gtk3::Gdk::EventMask::VERSION = '0.038';
|
|
use overload
|
|
'==' => \&eq,
|
|
'>=' => \≥
|
|
use Scalar::Util qw/looks_like_number/;
|
|
|
|
my $_convert_one = sub {
|
|
return Glib::Object::Introspection->convert_flags_to_sv (
|
|
'Gtk3::Gdk::EventMask', $_[0]);
|
|
};
|
|
|
|
my $_convert_two = sub {
|
|
my ($a, $b) = @_;
|
|
if (looks_like_number ($a)) {
|
|
$a = $_convert_one->($a);
|
|
}
|
|
if (looks_like_number ($b)) {
|
|
$b = $_convert_one->($b);
|
|
}
|
|
return ($a, $b);
|
|
};
|
|
|
|
sub eq {
|
|
my ($a, $b, $swap) = @_;
|
|
($a, $b) = $_convert_two->($a, $b);
|
|
return Glib::Flags::eq ($a, $b, $swap);
|
|
}
|
|
|
|
sub ge {
|
|
my ($a, $b, $swap) = @_;
|
|
($a, $b) = $_convert_two->($a, $b);
|
|
return Glib::Flags::ge ($a, $b, $swap);
|
|
}
|
|
|
|
package Gtk3;
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head2 Porting from Gtk2 to Gtk3
|
|
|
|
The majority of the API has not changed, so as a first approximation you can
|
|
run C<< s/Gtk2/Gtk3/ >> on your application. A big exception to this rule is
|
|
APIs that were deprecated in gtk+ 2.x -- these were all removed from gtk+ 3.0
|
|
and thus from L<Gtk3>. The migration guide at
|
|
L<http://developer.gnome.org/gtk3/stable/migrating.html> describes what to use
|
|
instead. Apart from this, here is a list of some other incompatible
|
|
differences between L<Gtk2> and L<Gtk3>:
|
|
|
|
=over
|
|
|
|
=item * The call syntax for class-static methods is now always
|
|
C<< Gtk3::Stock::lookup >> instead of C<< Gtk3::Stock->lookup >>.
|
|
|
|
=item * The %Gtk2::Gdk::Keysyms hash is gone; instead of C<<
|
|
Gtk2::Gdk::Keysyms{XYZ} >>, use C<< Gtk3::Gdk::KEY_XYZ >>.
|
|
|
|
=item * The Gtk2::Pango compatibility wrapper was not carried over; simply use
|
|
the namespace "Pango" everywhere. It gets set up automatically when loading
|
|
L<Gtk3>.
|
|
|
|
=item * The types Gtk3::Allocation and Gtk3::Gdk::Rectangle are now aliases for
|
|
Cairo::RectangleInt, and as such they are represented as plain hashes with
|
|
keys 'width', 'height', 'x' and 'y'.
|
|
|
|
=item * Gtk3::Editable: Callbacks connected to the "insert-text" signal do not
|
|
have as many options anymore as they had in Gtk2. Changes to arguments will
|
|
not be propagated to the next signal handler, and only the updated position can
|
|
and must be returned.
|
|
|
|
=item * Gtk3::Menu: In gtk+ < 3.16, the position callback passed to popup()
|
|
does not receive x and y parameters.
|
|
|
|
=item * Gtk3::RadioAction: The constructor now follows the C API.
|
|
|
|
=item * Gtk3::TreeModel: iter_next() is now a method that is modifying the iter
|
|
directly, instead of returning a new one. rows_reordered() and the
|
|
"rows-reordered" signal are currently unusable.
|
|
|
|
=item * Gtk3::TreeSelection: get_selected_rows() now returns two values: an
|
|
array ref containing the selected paths, and the model. get_user_data() is not
|
|
available currently.
|
|
|
|
=item * Gtk3::TreeSortable: get_sort_column_id() has an additional boolean
|
|
return value.
|
|
|
|
=item * Gtk3::TreeStore, Gtk3::ListStore: reorder() is currently unusable.
|
|
|
|
=item * Gtk3::Widget: grab_add() and grab_remove() are methods now: C<<
|
|
$widget->grab_add >>, C<< $widget->grab_remove >>.
|
|
|
|
=item * Gtk3::Gdk::Atom: The constructor new() is not provided anymore, and the
|
|
class function intern() must now be called as C<< Gtk3::Gdk::Atom::intern
|
|
(name, only_if_exists) >>.
|
|
|
|
=item * Implementations of Gtk3::TreeModel: Gtk3::TreeIter now has a
|
|
constructor called new() expecting C<< key => value >> pairs;
|
|
new_from_arrayref() does not exist anymore. To access the contents of
|
|
Gtk3::TreeIter, use stamp(), user_data(), user_data2() and user_data3();
|
|
to_arrayref() does not exist anymore. GET_ITER(), ITER_CHILDREN(),
|
|
ITER_NTH_CHILD() and ITER_PARENT() must return an additional boolean value.
|
|
ITER_NEXT() must modify the iter and return a boolean rather than return a new
|
|
iter. GET_VALUE() must return the value wrapped with C<<
|
|
Glib::Object::Introspection::GValueWrapper->new >>.
|
|
|
|
=item * Implementations of Gtk3::CellLayout: GET_CELLS() now needs to return an
|
|
array ref instead of a list.
|
|
|
|
=back
|
|
|
|
Note also that Gtk3::CHECK_VERSION will always fail when passed 2.y.z, so if
|
|
you have any existing version checks in your code, you will most likely need to
|
|
remove them.
|
|
|
|
=head1 SEE ALSO
|
|
|
|
=over
|
|
|
|
=item * To discuss Gtk3 and ask questions join gtk-perl-list@gnome.org at
|
|
L<http://mail.gnome.org/mailman/listinfo/gtk-perl-list>.
|
|
|
|
=item * Also have a look at the gtk2-perl website and sourceforge project page,
|
|
L<http://gtk2-perl.sourceforge.net>.
|
|
|
|
=item * L<Glib>
|
|
|
|
=item * L<Glib::Object::Introspection>
|
|
|
|
=back
|
|
|
|
=head1 AUTHORS
|
|
|
|
=over
|
|
|
|
=item Torsten Schönfeld <kaffeetisch@gmx.de>
|
|
|
|
=back
|
|
|
|
=head1 COPYRIGHT AND LICENSE
|
|
|
|
Copyright (C) 2011-2015 by Torsten Schoenfeld <kaffeetisch@gmx.de>
|
|
|
|
This library is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU Library General Public License as published by the Free
|
|
Software Foundation; either version 2.1 of the License, or (at your option) any
|
|
later version.
|
|
|
|
=cut
|