=head1 NAME Gtk2::porting - Guide to porting from Gtk-Perl to Gtk2-Perl =head1 SYNOPSIS first change Gtk to Gtk2... then fix what's broken until it works. =head1 DESCRIPTION Gtk-Perl (the project that included the Gtk module) bound gtk+ 1.x, whereas Gtk2-Perl (the project that includes the Gtk2 module) binds gtk+ 2.x. GTK+ itself changed rather substantially from the 1.x series to the 2.x series, resulting in the need to reimplement the Perl bindings completely. Gtk2-Perl uses many precedents from Gtk-Perl in making API decisions (its authors were avid Gtk-Perl users), but retaining complete source compatibility in the face of the changes in the underlying library is something of a lost cause. As an additional wrinkle, Gtk2-Perl attempts to bind only the things that were not deprecated as of gtk+ 2.0.0. Thus, upgrading your Gtk-Perl programs to Gtk2-Perl is going to require some actual porting work. The GNOME 2.0 porting guide on http://developer.gnome.org has some more detailed discussion of porting from gtk+ 1.2 to gtk+ 2.0. See the sections on GLib and GTK+; it's geared towards the C API, but you can refer to L for information on how to translate between C and Perl in your head. The biggest stumbling block for most porting will be the fact that several important widgets have been omitted from Gtk2-Perl because they are deprecated in gtk+-2.0. Old Widget Replacement ------------ ------------------------------------- Gtk::CList Gtk2::TreeView with a Gtk2::ListStore Gtk::CTree Gtk2::TreeView with a Gtk2::TreeStore Gtk::Text Gtk2::TextView and a Gtk2::TextBuffer Gtk::Pixmap Gtk2::Image Gtk::Preview Gtk2::Image Gtk::List Gtk2::TreeView with a Gtk2::ListStore Gtk::Tree Gtk2::TreeView with a Gtk2::ListStore Invariably, they have been replaced with far superior widgets, but the porting effort can be daunting for applications which use Gtk::CList, Gtk::CTree, and Gtk::Text heavily. To ease such porting, I created Gtk2::Deprecated; its documentation explains at length how you shouldn't use it. Find it via google. ;-) I recommend it for putting off the porting from CList to TreeView as long as possible in your porting effort, so you can still see something as you work. =head1 PORTING FROM Gtk-Perl TO Gtk2-Perl =head2 Step 1: Just get it to run. The very first step: you need to C, not C. The -init argument is still supported, but the -locale argument is not necessary (gtk+ handles this automatically). The same goes for C and other modules you may use: I use vim, so my initial step is usually :% s/\/Gtk2/g :% s/\/Gnome2/g Note that the C module does not contain everything -- some things are now in other, separate modules, such as C, C, and C. Pick and choose as you need. (This document really only describes Gtk => Gtk2, so i won't go into detail on the others.) Of course, this is not enough. Now you'll start hitting all the API problems. The section L lists in excruciating detail the sorts of things you need to look out for. Things of note not included there are =over =item o The format of C entries has changed. =item o Several Gtk functions have moved to Glib: Gtk::timeout_add() Glib::Timeout::add() Gtk::timeout_remove() Glib::Source::remove() Gtk::idle_add() Glib::Idle::add() Gtk::idle_remove() Glib::Source::remove() =item o Deriving widgets with signals and properties is completely different. It's mostly for the better. See L and L. =item o ... =back If your program is basically a wrapper around C or C, you will want to use C to allow yourself to get the program up and running so that you can port incrementally to the replacement widgets. I'll be really annoying and say it again -- do not use C to avoid porting your program! The replacement widgets truly are B nicer than their forebears! =head2 Step 2: Take advantage of new features. (Warning: this part usually involves reducing the amount of code in your program while increasing the amount of cool features it provides.) Port from deprecated widgets to good widgets, and remove C. Use Mnemonics everywhere. It's as simple as adding underscores to the strings passed to activatable widget constructors. Use Stock Icons. E.g., C<< $button = Gtk2::Button->new_from_stock ('gtk-ok'); >>. However don't bother using this to create buttons for your dialogs. Use C's response API. (See L for more info.) Use C instead of rolling your own alert, error, and other message-only dialogs. Use Pango markup to add attributes to your text. If your program fiddles with Cs to make bold headings or larger text and things like that, you can now strip all that code out and do something like $label = Gtk2::Label->new; $label->set_markup ('Pango Markup is ' .'REALLY ' .'cool!'); The Pango API reference has a description of this simple html-like markup language. http://developer.gnome.org/doc/API/2.0/pango/PangoMarkupFormat.html If you're the type who hates magic numbers, ask Glib to export the TRUE and FALSE constants. gtk+ 2.4 stuff -- Gtk2::UIManager, Gtk2::FileChooser, Gtk2::ComboBox, Gtk2::Expander, the new Gtk2::Toolbar API, etc, etc. FIXME finish me =head2 Step 3: Beef it up! I leave this step as an exercise for the reader. Use the docs and examples to figure out how to make your user interface better. Convert your user interface to follow the Gnome Human Interface Guidelines. Learn about usability and accessibility and try to make your UI better. Make things simpler, faster, leaner, cooler, more featureful, whatever you like. Join gtk-perl-list gnome org and hang out in #gtk-perl on irc.gnome.org. =head2 Incompatible changes made between Gtk-Perl and Gtk2-Perl (This is adapted from the gtk+ API reference's "Changes from 1.2 to 2.0" Chapter.) GTK+ changed fairly substantially from version 1.2 to 2.0, much more so than from 1.0 to 1.2. Subsequent updates (possibilities are 2.0 to 2.2, 2.2 to 2.4, then to 3.0) will almost certainly be much, much smaller. Nonetheless, most programs written for 1.2 compile against 2.0 with few changes. The bulk of changes listed below are to obscure features or very specialized features, and compatibility interfaces exist whenever possible. =over =item o Gtk::Container::get_toplevels() was removed and replaced with Gtk2::Window::list_toplevels(). =item o The Gtk::Gdk::time* functions have been removed. This functionality has been unused since the main loop was moved into GLib prior to 1.2. =item o Gtk::Paned::set_gutter_size() has been removed, since the small handle tab has been changed to include the entire area previously occupied by the gutter. =item o Gtk::Paned::set_handle_size() has been removed, in favor of a style property, since this is an option that only makes sense for themes to adjust. =item o GDK no longer selects OwnerGrabButtonMask for button presses. This means that the automatic grab that occurs when the user presses a button will have owner_events = FALSE, so all events are redirected to the grab window, even events that would normally go to other windows of the window's owner. =item Type system changes: The GTK+ type system was moved to GLib in 2.0, and the base object is now GObject rather than GtkObject. GLib is wrapped separately by the Glib module (see L), and thus GObject is Glib::Object in Perl. =item Object system changes: GtkObject derives from GObject, so is not the basic object type anymore. This imposes the following source incompatible changes: =over =item - Subclassing of Gtk::Objects was a mess in Gtk-Perl. Subclassing of Glib::Objects is a mess in Gtk2-Perl, but a completely different mess. See L and L for more information. In particular, code that uses GTK_SET_ARG, GTK_GET_ARG, GTK_CLASS_INIT, GTK_OBJECT_INIT, etc, will have to be rewritten to use the corresponding Glib::Object stuff, which is not terribly complicated, but too involved to document here. GtkObject has no klass field anymore, an object's class can be retrieved with the object's coresponding GTK__GET_CLASS (object) macro. GtkObjectClass has no type field anymore, a class's type can be retrived with the GTK_CLASS_TYPE (class) macro. The GtkObject::destroy signal can now be emitted multiple times on an object. ::destroy implementations should check that make sure that they take this into account, by checking to make sure that resources are there before freeing them. =back =item Signal system changes: The GTK+ 2.0 signal system merely proxies the GSignal system now. For future usage, direct use of the GSignal API is recommended, this avoids significant performance hits where GtkArg structures have to be converted into GValues. For language bindings, GSignal+GClosure provide a much more flexible and convenient mechanism to hook into signal emissions or install class default handlers, so the old GtkSignal API for language bindings is not supported anymore. Functions that got removed in the GTK+ signal API: gtk_signal_n_emissions(), gtk_signal_n_emissions_by_name(), gtk_signal_set_funcs(), gtk_signal_handler_pending_by_id(), gtk_signal_add_emission_hook(), gtk_signal_add_emission_hook_full(), gtk_signal_remove_emission_hook(), gtk_signal_query(). Also, the GtkCallbackMarshal argument to gtk_signal_connect_full() is not supported anymore. For many of the removed functions, similar variants are available in the g_signal_* namespace. The GSignal system performs emissions in a slightly different manner than the old GtkSignal code. Signal handlers that are connected to signal "foo" on object "bar" while "foo" is being emitted, will not be called anymore during the emission they were connected within. =item o Inserting and deleting text in Gtk2::Entry though functions such as Gtk2::Entry::insert_text() now leave the cursor at its original position in the text instead of moving it to the location of the insertion/deletion. =item o The 'font' and 'font_set' declarations in RC files are now ignored. There is a new 'font_name' field that holds the string form of a Pango font. =item o A number of types in GDK have become subclasses of Glib::Object. For the most part, this should not break anyone's code. However, it's now possible/encouraged to use Glib::Object features with these GDK types. The converted types are: Gtk2::Gdk::Window, Gtk2::Gdk::Drawable, Gtk2::Gdk::Pixmap, Gtk2::Gdk::Image, Gtk2::Gdk::GC, Gtk2::Gdk::DragContext, Gtk2::Gdk::Colormap. =item o Gtk2::Style and Gtk2::RcStyle are now subclasses of Glib::Object as well. This requires fairly extensive changes to theme engines, but shouldn't affect most other code. =item o Some GtkStyle draw_* methods have been removed (cross, oval, ramp) and others have been added (expander, layout). This will require changes to theme engines. =item o The visual for a widget, and also the default visual is now derived from the colormap for the widget and the default colormap. Gtk::Widget::set_visual(), Gtk::Widget::set_default_visual(), Gtk::Widget::push_visual() and Gtk::Widget::pop_visual() now do nothing in gtk+, and are not included in Gtk2-Perl. It is safe to simply delete all references to these functions. =item o A number of functions in GDK have been renamed for consistency and clarity. Many of these are picked up automatically by inheritance, but the rest require changes to your code: Old Function New Function ------------------------------- ----------------------------------- Gtk::Gdk::Pixmap::draw_pixmap Gtk2::Gdk::Drawable::draw_drawable Gtk::Gdk::Pixmap::draw_bitmap Gtk2::Gdk::Drawable::draw_drawable Gtk::Gdk::Pixmap::get_type Gtk2::Gdk::Window::get_window_type Gtk::Gdk::GC::destroy (no replacement, not needed) Gtk::Gdk::Image::destroy (no replacement, not needed) Gtk::Gdk::Cursor::destroy (no replacement, not needed) Gtk::Gdk::Window::copy_area Gtk2::Gdk::Drawable::draw_drawable Gtk::Gdk::Rgb::get_cmap (replacement get_colormap is not bound) Note that the automatic memory management of Glib::Object removes the need for the ->destroy functions that were bound for many of the old GDK objects; in fact, they weren't really necessary in Gtk-Perl, either. gtk_widget_popup() was removed, it was only usable for GtkWindows, and there the same effect can be achieved by gtk_window_move() and gtk_widget_show(). =item o gdk_pixmap_foreign_new() no longer calls XFreePixmap() on the pixmap when the GdkPixmap is finalized. This change corresponds to the behavior of gdk_window_foreign_new(), and fixes a lot of problems with code where the pixmap wasn't supposed to be freed. If XFreePixmap() is needed, it can be done using the destroy-notification facilities of g_object_set_data(). =item GtkProgress/GtkProgressBar had serious problems in GTK+ 1.2. =over =item - Only 3 or 4 functions are really needed for 95% of progress interfaces; GtkProgress/GtkProgressBar had about 25 functions, and didn't even include these 3 or 4. =item - In activity mode, the API involves setting the adjustment to any random value, just to have the side effect of calling the progress bar update function - the adjustment is totally ignored in activity mode. =item - You set the activity step as a pixel value, which means to set the activity step you basically need to connect to size_allocate. =item - There are ctree_set_expander_style()-functions, to randomly change look-and-feel for no good reason. =item - The split between GtkProgress and GtkProgressBar makes no sense whatsoever. =back This was a big wart on GTK+ and made people waste lots of time, both learning and using the interface. So, we have added what we feel is the correct API, and marked all the rest deprecated. However, the changes are 100% backward-compatible and should break no existing code. The following 5 functions are the new programming interface and you should consider changing your code to use them: $progressbar->pulse (); $progressbar->set_text ($text); $progressbar->set_fraction ($fraction); $progressbar->set_pulse_step ($fraction); $progressbar->set_orientation ($orientation); =item o The GtkNotebookPage structure has been removed from gtk+'s public API, so there are no Gtk::NotebookPage objects in Gtk2-Perl. They were never supposed to be public in the first place, so gtk+ does not apologize. =item o Negative values of the position parameter to Gtk2::Notebook::reorder_child() now cause the page to be appended, not inserted at the beginning. (This gives consistency with Gtk2::Box::reorder_child(), Gtk2::Menu::reorder_child().) =item o GtkMenuPositionFunc (the menu position callback for Gtk2::Menu::popup()) has a new parameter, "push_in", which controls how menus placed outside the screen is handled. If this is set to TRUE and part of the menu is outside the screen then GTK+ pushes it into the visible area. Otherwise the menu is cut of at the end of the visible screen area. =item o Regardless of what happens to the size of the menu, the result is always that the items are placed in the same place as if the menu was placed outside the screen, using menu scrolling if necessary. =item o The "draw" signal and virtual method on GtkWidget have been removed. All drawing should now occur by invalidating a region of the widget (call Gtk2::Gdk::Window::invalidate_rect() or Gtk2::Widget::queue_draw() for example to invalidate a region). GTK+ merges all invalid regions, and sends expose events to the widget in an idle handler for the invalid regions. Gtk::Widget::draw() is deprecated and thus not part of Gtk2. (but still works; it adds the passed-in area to the invalid region and immediately sends expose events for the current invalid region.) Most widgets will work fine if you just delete their "draw" implementation, since they will already have working expose_event implementations. The draw method was rarely called in practice anyway. =item o The Gtk2::Gdk::Event::Expose has a new "region" field. This can be used instead of the "area" field if you want a more exact representation of the area to update. =item o Sending synthetic exposes using Gtk2::Widget::event() is no longer allowed. If you just need an expose call you should use Gtk2::Gdk::Window::invalidate_rect() or Gtk2::Gdk::Window::invalidate_region() instead. For the case of container widgets that need to propagate expose events to NO_WINDOW children you can either use Gtk2::Container::propagate_expose(), or chain to the default container expose handler. =item o The "draw_default" and "draw_focus" methods/signals on GtkWidget are gone; simply draw things in your expose handler. Gtk::Widget::draw_focus() and Gtk::Widget::draw_default() wrapper functions are also gone; just queue a draw on the widget, or the part affected by the focus/default anyway. Also, GtkWidget now has default implementations for focus_in_event and focus_out_event. These set/unset the 'has-focus' flag, and queue a draw. So if your focus in/out handler just does that, you can delete it. =item o GtkText and GtkTree are buggy and broken. GTK+ doesn't recommend using them, and in fact they plan to remove them from future versions, so we didn't bind them in Gtk2-Perl. The recommended alternatives are Gtk2::TextView and Gtk2::TreeView. =item o Gtk::Gdk::ColorContext is gone; you probably weren't using it anyway. Use Gtk2::Gdk::Colormap and the Gtk2::Gdk::Rgb functions instead. =item o Gtk2::MenuBar now draws the container's border_width space outside the frame, not inside the frame. =item o In GTK+ 1.2 (and Gtk-Perl), if an event handler returned TRUE it prevented propagation of that event to parent widgets. That is, the event signal would not be emitted on parent widgets. In GTK+ 2.0 (and Gtk2-Perl), if an event handler returns TRUE, the current signal emission on the current widget is immediately stopped. That is, other callbacks connected to the signal will not be invoked. =item o Gtk2::Toolbar::new() no longer has arguments. This function was broken because the default GtkToolbarStyle (icons, text, both) is now a user preference, which is overridden when you call Gtk2::Toolbar::set_style(). The constructor forced everyone to override the preference, which was undesirable. So to port your app, decide if you want to force the toolbar style or conform to the user's global defaults; if you want to force it, call Gtk2::Toolbar::set_style(). The orientation arg was removed from Gtk2::Toolbar::new() as well, just because it wasn't very useful and we were breaking the function anyway so had an opportunity to lose it. Call Gtk2::Toolbar::set_orientation() to set toolbar orientation. =item o GtkRange/GtkScrollbar/GtkScale were rewritten; this means that most theme engines won't draw them properly, and any custom subclasses of these widgets will need a rewrite (though if you could figure out how to subclass the old version of GtkRange, you have our respect). Also, GtkTroughType is gone. Here are some notable changes: =over =item - stepper_size style property is the height for vertical ranges, width for horizontal; the other dimension matches the trough size. =item - Added the ability to do NeXT-style steppers (and several other styles that don't make any sense). =item - Added min_slider_length, fixed_slider_length properties to GtkScrollbar. =item - Cleaned some private (or at least useless) functions out of gtkscale.h, e.g. gtk_scale_value_width. =item - Moved key bindings from subclasses to GtkScale, even arrow keys, since blind users don't know scale orientation. =item - Changed move_slider action signal to use new GtkScrollType, remove GtkTroughType argument. =item - Digits rounds the values a range will input to the given number of decimals, but will not try to force adjustment values set by other controllers. That is, we no longer modify adjustment->value inside a value_changed handler. =item - Added getters for GtkScale setters. =item - Middle-click begins a slider drag. =back =item o The Gtk::Container's "focus" signal/virtual function and Gtk::Container::focus() call were replaced by Gtk2::Widget's "focus" signal and Gtk2::Widget::child_focus(). The semantics are the same, so you should be able to just replace Gtk::Container::focus() calls with Gtk2::Widget::child_focus() calls. The purpose of this change was to allow non-containers to have focusable elements. Lots of other stuff changed in GtkContainer, but Gtk-Perl didn't bind it, and Gtk2-Perl doesn't bind it yet, so never you worry your pretty little head about it. :-) =item o Gtk::Gdk::Image::get() (or rather its replacement, Gtk2::Gdk::Drawable::get_image()) now handles errors properly by returning undef, whereas previously it would crash. Also, a window being offscreen is no longer considered an error; instead, the area contains undefined contents for the offscreen areas. In most cases, code using Gtk::Gdk::Image::get() should really be ported to Gtk2::Gdk::Pixbuf::get_from_drawable(). =item o gtk_widget_set_usize() has been renamed to gtk_widget_set_size_request(), however the old name still exists unless you define GTK_DISABLE_DEPRECATED. =item o gtk_widget_set_uposition() is deprecated; use gtk_window_move(), gtk_fixed_put(), or gtk_layout_put() instead. =item o gtk_window_set_policy() is deprecated. To get the effect of "allow_shrink", call C<< $window->set_size_request (0, 0) >>. To get the effect of "allow_grow", call C<< $window->set_resizable (TRUE) >>. You didn't want the effect of "auto_shrink", it made no sense. But maybe if you were using it you want to use C<< $window->resize (1, 1) >> to snap a window back to its minimum size (the 1, 1 will be rounded up to the minimum window size). =item o The core GTK+ now takes care of handling mapping, unmapping and realizing the child widgets of containers in Gtk2::Widget::set_parent(). In most cases, this allows container implementations to be simplified by removing the code in add() methods to map and realize children. However, there are a couple of things to watch out for here: =item o If the parent is realized before the add() happens, Gtk2::Widget::set_parent_window() must be called before Gtk2::Widget::set_parent(), since Gtk2::Widget::set_parent() will realize the child. =item o If a container depended on its children not being mapped unless it did so itself (for example, GtkNotebook only mapped the current page), then the new function gtk_widget_set_child_visible() must be called to keep widgets that should not be mapped not mapped. As part of this change, most containers also will no longer need custom implementations of the map() and unmap() virtual functions. The only cases where this is necessary are: =over =item - For !NO_WINDOW widgets, if you create children of $widget->window and don't map them in realize() then you must map them in map(). [ In almost all cases, you can simply map the windows in realize(). ] =item - For NO_WINDOW widgets, if you create windows in your realize() method, you must map then in map() and unmap them in unmap(). =back =item o Gtk::Widget::set_default_style(), Gtk::Widget::push_style(), and Gtk::Widget::pop_style() have been removed, since they did not work properly with themes and there were better alternatives for modifying the appearance of widgets. You should generally use Gtk2::Widget::modify_*() instead. =item o Gtk2::Image::new() now takes no arguments and creates an empty Gtk2::Image widget. To create a Gtk2::Image widget from a Gtk2::Gdk::Image (the least common usage of Gtk2::Gdk::Image), use Gtk2::Image::new_from_image(). =item o GTK_SELECTION_EXTENDED is now deprecated, and neither the GtkList/GtkTree nor the GtkCList/GtkCTree support GTK_SELECTION_EXTENDED anymore. However, the old extended behavior replaces MULTIPLE behavior. =item o The handling of colormaps and widgets has been changed: The default colormap for widgets is now the GdkRGB colormap, not the system default colormap. If you try to use resources created for a widget (e.g., $widget->style) with a window using the system colormap, errors will result on some machines. Gtk2::Widget::push_colormap()/Gtk2::Widget::pop_colormap() only cause the colormap to be explicitly set on toplevel widgets, not on all widgets. The colormap for other widgets (when not set using Gtk2::Widget::set_colormap()), is determined by finding the nearest ancestor with a colormap set on it explicitly, or if that fails, the default colormap. =item o The default selected day for Gtk2::Calendar is now the current day in the month, not the first day in the month. The current month and year were already used. =item o GDK is no longer put into threaded mode automatically when g_thread_init() has been called (g_thread_init() is called automatically by Glib if your bindings have been built with threading enabled). In order to use the global GDK thread mutex with Gtk2::Gdk::Threads::enter() and Gtk2::Gdk::Threads::leave(), you must call Gtk2::Gdk::Threads::init() explicitly. If you aren't using GDK and GTK+ functions from multiple threads, there is no reason to call Gtk2::Gdk::Threads::init(). =item o The Gtk::Preview is deprecated; just use a Gtk2::Image to display a Gtk2::Gdk::Pixbuf, instead. -- The GtkPreviewInfo struct has had its visual and colormap fields removed. Also, gtk_preview_get_cmap() and gtk_preview_get_visual() are deprecated, as GdkRGB works on any colormap and visual. You no longer need to gtk_widget_push_cmap (gtk_preview_get_cmap ()) in your code. =item o The Gtk2::Box, Gtk2::Table, and Gtk2::Alignment widgets now call C<< $widget->set_redraw_on_allocate (FALSE) >> on themselves. If you want to actually draw contents in a widget derived from one of these widgets, you'll probably want to change this in your INIT_INSTANCE() function. =item o A number of widgets are now NO_WINDOW widgets (most importantly Gtk2::Button, but also Gtk2::Range and Gtk2::Notebook). This has a couple of effects: =over =item - If you are deriving from one of these widgets, you need to adapt your code appropriately -- for instance, drawing coordinates start from widget->allocation.x, widget->allocation.y. =item - If you are embedding one of these widgets in a custom widget, you must make sure you call Gtk2::Container::propagate_expose() correctly, as you must for any NO_WINDOW widgets. =back GtkFixed is a little special; it is now created by default as a NO_WINDOW widget, but if you do $fixed->set_has_window (TRUE); after creating a fixed widget, it will create a window and handle it properly. =item o Gtk2::Layout no longer has the xoffset, yoffset fields, which used to store the difference between world and window coordinates for layout->bin_window. These coordinate systems are now always the same. =item o Gtk2::paint_focus(), Gtk2::draw_focus() and Gtk2::Style's draw_focus virtual function have been changed a bit: =over =item - A Gtk2::StateType argument has been added to Gtk2::paint_focus(). =item - The default implementation of Gtk2::Style's draw_focus() virtual function now draws a focus rectangle whose width is determined by Gtk2::Widget's focus-width style property. =item - The rectangle passed in is the bounding box, instead of the rectangle used in the Gtk2::Gdk::draw_rectangle() call, so it is no longer necessary to subtract 1 from the width and height. =back =back =head1 AUTHOR Blame muppet for not writing this sooner. The "Incompatible Changes from 1.2 to 2.0" section is adapted from the GTK+ Reference Manual, with the parts that referred to stuff not included in Gtk-Perl just ripped out. =cut