%PDF- %PDF-
Direktori : /usr/share/perl5/ |
Current File : //usr/share/perl5/Gtk3.pm |
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