1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514
|
=pod
=for comment
$Id: perlpanel-applet-howto.pod,v 1.29 2004/11/04 16:52:17 jodrell Exp $
=head1 DESCRIPTION
This document explains how to write applets for the PerlPanel.
=head1 EXAMPLE
# declare our package:
package PerlPanel::Applet::HelloWorld;
# constructor:
sub new {
my $self = {};
$self->{package} = shift;
bless($self, $self->{package});
return $self;
}
# build the widget:
sub configure {
my $self = shift;
$self->{widget} = Gtk2::Button->new('Click Me!');
$self->widget->signal_connect(
'clicked',
sub {
PerlPanel::alert(
'Hello World!'
);
}
);
$self->widget->show_all;
}
# return the widget;
sub widget {
return $_[0]->{widget};
}
# return the expand (1 or 0) for packing:
sub expand {
return 0;
}
# return the fill (1 or 0) for packing:
sub fill {
return 0;
}
# return 'start' or 'end':
sub end {
return 'start';
}
sub get_default_config {
my $hashref = {
name => 'value',
'foo' => 'bar',
'list' => [1, 2, 3, 4, 5],
};
return $hashref;
}
1;
=head1 INTRODUCTION
It is very easy to write applets for the PerlPanel. Applets are simple
Perl objects, and are allowed to do anything that a normal Gtk2-Perl
program might do.
The package name for the applet must be of the form
PerlPanel::Applet::AppletName
and should have the filename
AppletName.pm
In order to work properly with the PerlPanel, they need to have an
interface the PerlPanel can understand.
When it loads a module, PerlPanel will create a new instance of the
applet. It will then call various methods of the object in order to work
out what to do with it.
These methods are explained below.
=head1 APPLET TYPES
Since Version 0.7.0, PerlPanel has supported two applet types: I<single>
and I<multi>. I<Single> applets are just like applets developed for
older versions of PerlPanel - if more than one instance of the applet
was on the panel, then those instances would all use the same set of
configuration data.
I<Multi> applets are different. A multi applet can appear several times
on the panel, and each instance can have its own configuration settings.
=head2 CREATING A MULTI APPLET
The first thing you have to do is create a package variable called
C<$MULTI>:
package PerlPanel::Applet::FooBar;
use vars qw($MULTI);
$MULTI = 1;
The value of C<$MULTI> must be a true value (1 is the simplest).
When PerlPanel loads the applet, it checks the value of this variable
and if it's true, then it supplies an B<ID> argument to the applet
constructor. See below for details.
Finally, when you are querying for a configuration hashref, you must
supply the ID mentioned above in your call to
C<PerlPanel::get_config()>. See below for more details.
=head1 REQUIRED METHODS
$applet->new
The constructor. The arguments supplied to the constructor vary,
depending on whether the applet has been defined as a I<multi> applet or
not.
If the C<$MULTI> variable has not been set, then C<@_> will look like this:
@_ = ($package);
where C<$package> is a scalar containing the package name of the applet,
eg C<PerlPanel::Applet:FooBar>. If the C<$MULTI> variable is true, then
C<@_> will look like this:
@_ = ($package, $id);
where C<$id> is a scalar containing the ID of the instance. You must
store this ID and use it when you request config data.
$applet->configure
This stage is intended for the loading of config data, creation of
widgets, etc.
$applet->widget
This method should return the Gtk widget that is to be displayed on the
panel. It can be any kind of Gtk widget apart from a window.
$applet->expand
$applet->fill
The panel uses a horizontal packing box to contain the applet widgets.
These two methods return integer values for the 'expand' and 'fill'
arguments to C<pack_start()>.
$applet->get_default_config
This method returns a reference to a hash containing the default
configuration data. This data is imported into the user's config file
and is saved to disk and can then be modified by the user as needed. It
is only called once - the first time the applet is loaded, after which
the panel will use the data from the user's config file.
If this methods returns C<undef> then the panel will assume there is no
configuration data.
NB: The C<end()> function is now deprecated and does not need to be used
in new applets.
=head1 HISTORICAL NOTE
The following sections describe the variables and functions that PerlPanel
provides that make applet writing easier. Prior to version 0.4.0, the
functions below were accessed using the form
$PerlPanel::OBJECT_REF->function_name
As of version 0.4.0, the syntax is
PerlPanel::function_name
Compatibility with the old syntax was removed in version 0.7.0.
=head1 USEFUL VARIABLES
You can access the following variables in your code that give you access
to the internal guts of the panel:
$PerlPanel::OBJECT_REF
This is a reference to the main panel object. A number of methods are
available for your use, they're explained below.
@PerlPanel::APPLET_DIRS
This contains directory paths which PerlPanel will use to search for
applets. It will at least contain C<$PREFIX/lib/perlpanel/PerlPanel/Applet>
and C<$HOME/.perlpanel/applets>.
=head1 USEFUL FUNCTIONS
PerlPanel::get_config($appletname, [$id])
This function returns a reference containing the configuration data for
the applet. The structure of the data will be that defined by the
C<get_default_config> function.
If you supply the second C<$id> argument, then the function will return
the appropriate config data for the instance of the applet defined by
C<$id>.
PerlPanel::tips()
This returns a C<Gtk2::Tooltips> object ready for use. In versions prior to
0.4.0, this was accessed using C<$PerlPanel::TOOLTIPS_REF>.
PerlPanel::icon()
This returns a GdkPixbuf object containing the PerlPanel icon. You
should use this pixbuf to set the icon for any windows you create, eg:
$dialog->set_icon(PerlPanel::icon);
You don't need to resize the pixbuf, this is done when the application
(a window manager, or task list) requires it.
PerlPanel::icon_size()
The size of icons on the panel, in pixels. You should use this to scale
things so that everything fits nicely together.
PerlPanel::screen_width
PerlPanel::screen_height
These two methods return the width and height (in pixels) of the default
display. PerlPanel attempts to work this out if the system's Gtk+ is
recent enough (ie later than 2.2.0), otherwise it will take them from the
output of the C<xdpyinfo(1)> program.
PerlPanel::position
This returns the panel's physical position on-screen, either C<'top'> or
C<'bottom'>. This is useful for when you want to show popup menus from
your applets (for an example, see the BBMenu applet), and need to know
the position of the panel.
PerlPanel::save_config
This tells the panel to save its configuration data to the resource file.
PerlPanel::shutdown
This tells the panel to save its config file to disk and exit.
PerlPanel::request_string($message, $callback)
This is a clone of the request_string method from the Gnome libs, so
that PerlPanel isn't dependent on Gnome being installed. It prompts the
user for a string using C<$message>, and executes C<$callback> when the
user presses 'Ok' or hits the Enter key. The callback's C<$_[0]> will
contain the supplied string.
PerlPanel::request_password($message, $callback)
As above, except the entry widget will not show the entered characters.
PerlPanel::question($message, $ok_callback,
$cancel_callback)
This prompts the user to answer a Yes/No type question. C<$ok_callback>
is executed when the user hits 'Ok'. Working out what happens when the
user hits 'Cancel' is left as an exercise for the reader.
PerlPanel::error($message, $ok_callback)
This pops up a dialog with an error icon and the given message.
C<$ok_callback> is executed if the user hits the 'Ok' button.
PerlPanel::warning($message, $ok_callback)
This pops up a dialog with a warning icon (less severe than an error)
and the given message. C<$ok_callback> is executed if the user hits the
'Ok' button.
PerlPanel::notify($message, $ok_callback)
This pops up a dialog with a information icon and the given message.
C<$ok_callback> is executed if the user hits the 'Ok' button.
PerlPanel::get_widget_position($widget)
This returns two numbers corresponding to the position on-screen of the
top-left corner of $widget. This means that C<$widget> must be visible on
screen.
PerlPanel::get_mouse_pointer
This returns two numbers corresponding to the position on-screen of the
mouse pointer.
PerlPanel::exec_wait($cmd, $callback)
This function allows you to execute a command and wait for it to finish,
without interrupting the Gtk main loop, and without inducing unneeded
CPU load with a custom C<while()> loop. The command contained in C<$cmd>
is opened as a filehandle - so calling applications which detach from
STDOUT are not recommended. C<exec_wait> creates a Glib handler and
waits for C<eof> from the handle. Then it executes the function in
C<$callback>.
This function exists mainly because the 'IconBar' applet needs a way to
wait for the desktop entry editor to close.
PerlPanel::load_glade($name);
This returns a C<Gtk2::GladeXML> object. PerlPanel scans two directories
looking for a file named C<$name.glade>:
$HOME/.local/share/perlpanel/glade
$PREFIX/share/perlpanel/glade
or returns undef if it can't find anything. This is a keyboard-plastic
saving utility only, and doesn't do anything else.
PerlPanel::has_application_menu
Returns a true value if an application launcher menu (BBMenu for example) is
present in the user's applet list.
PerlPanel::has_action_menu
Returns a true value if an ActionMenu applet is in the user's applet list.
PerlPanel::has_pager
Returns a true value if an Pager applet is in the user's applet list.
PerlPanel::lookup_icon($icon);
This is a convenience function that wraps Gtk2::IconTheme. The C<$icon>
argument is the name of a program or similar. This function will return
a filename or C<undef> if unsuccessful.
PerlPanel::remove_applet($appletname, $id);
This method is only useful to multi applets. It tells PerlPanel to remove
the C<$id> instance of the C<$appletname> applet from the panel. This is
useful for when you want to provide a "remove" option in a context menu.
PerlPanel::launch($command, $notification);
This command provides a wrapper to the C<system()> function, with additional
support for the Startup Notification specification. When C<$notification> is
defined, then the user is given visible feedback that the application is being
launched. Once the application has started (and identified itself to the
panel), or a certain period of time has elapsed, the feedback is cancelled.
=head1 INTERNATIONALISATION SUPPORT
PerlPanel has support for foreign languages, using the C<Locale::gettext>
module. PerlPanel provides a special function for retrieving a translation
of a string:
$translated = _($original, %params);
C<$original> is the original, presumably English, string. Any occurances of
the keys of C<%params> are replaced with their values, for example:
$translated = _(
"there are {number} {type} {object}",
number => 6,
type => 'red',
object => 'apples'
);
Applet authors are encouraged to wrap all the strings they use in their
applets in C<_()>.
=head1 WRITING MENU APPLETS
PerlPanel provides an easy-to-use base menu class for creating menu applets.
Consult L<PerlPanel::MenuBase> for more information.
=head1 MANAGING TIMEOUTS
The use of Glib timeouts (as described in L<Glib::MainLoop>) requires careful
consideration for PerlPanel. Applet objects and widgets may be created and
destroyed many times during the lifetime of the PerlPanel process. If these
applets make use of a timeout, and that timeout is not properly managed, then
over time a large amount of CPU time may be used up by timeouts that were
created by applets that are no longer in use.
PerlPanel has a system to track timeouts created by applets, and to remove them
when the panel is reloaded, or when a particular applet is removed from the
panel. Instead of using C<Glib::Timeout-E<gt>add($msec, $callback)> to set up your
applet, use the following function:
$id = PerlPanel::add_timeout($msec, $callback);
The arguments and return values for this function are identical to those of
C<Glib::Timeout-E<gt>add()>. When the panel is reloaded, all the applets are
removed and new ones are created, so the panel will automatically clean up
these timeouts.
When you want to cancel a timeout, use this function:
PerlPanel::remove_timeout($id);
When the code in C<$callback> is executed, it will receive a B<reference> to
a scalar containing the ID as the first member of C<@_>. So if you want to
cancel the timeout from within the callback, you can call write like this:
PerlPanel::remove_timeout(${shift()});
=head1 PACKAGING YOUR APPLET
To create an applet package that a user can install without root access,
you need to create a gzipped tar archive, with the name
C<AppletName-$Version.tar.gz>, that has the following layout:
/
/applet.info
/applets
/applets/AppletName.pm
/share
/share/icons
/share/icons/hicolor
/share/icons/hicolor/48x48
/share/icons/hicolor/48x48/apps
/share/icons/hicolor/48x48/apps/perlpanel-applet-appletname.png
/share/icons/hicolor/48x48/apps/perlpanel-applet-appletname-action-specific-icon.png
/share/perlpanel
/share/perlpanel/glade
/share/perlpanel/glade/appletname.glade
The C<applet.info> file should contain a single line of the form:
AppletName:A short description of what your applet does.:Category
This line is appended to the user's applet.registry, so they see a
descriptive entry in the Add Applet dialog. The first field must match the
C<AppletName> part of the tarball's name.
The C<$Version> part of the file name should contain only digits and
periods. Valid version strings include: C<1.00> (Perl style), C<1.0.0>
(Kernel style), and so on.
The .pm file which contains your applet should go into C<applets/>. The
installer will look for a file called C<AppletName.pm>, where C<AppletName>
is taken from the C<AppletName> part of the tarball's name. PerlPanel will
place this file into C<$HOME/.perlpanel/applets>.
The files inside the C<share/> subdirectory are installed into
C<$HOME/.local/share>. This is a directory defined by the
Freedesktop.org base directory specification as the place in which user
specific data files should be stored. You can use this subdirectory to
install Glade files (which can be accessed using C<load_glade()> - see
above), your applet's icon, and any supporting icons you may need. Using
this path means that calls to C<lookup_icon> will work out the same as
if the icons were in C<$PREFIX/share/icons>, and can also be themed.
For example, if your applet controls a media player, and you want to include icons for the Previous, Forward, Play and Pause buttons, you can include these icons like this:
/share/icons/hicolor/48x48/apps/perlpanel-applet-mediaplayer-previous.png
/share/icons/hicolor/48x48/apps/perlpanel-applet-mediaplayer-next.png
/share/icons/hicolor/48x48/apps/perlpanel-applet-mediaplayer-play.png
/share/icons/hicolor/48x48/apps/perlpanel-applet-mediaplayer-pause.png
/share/icons/hicolor/48x48/apps/perlpanel-applet-mediaplayer-stop.png
And then in your applet code, you can retrieve these icons by using this:
my $pbf = PerlPanel::lookup_icon('mediaplayer-previous', PerlPanel::icon_size);
=head1 FURTHER CONSIDERATIONS
Please try to keep the external dependencies of your applet to a minimum.
Remember that your applet may get installed on systems that have a very
different set of applications installed on them. And never, I<ever> hard-code
paths to files you depend on.
For example, this is very bad:
my $executable = '/usr/bin/executable';
If the user doesn't have the C<executable> program, they may install it from
source, in which case it will probably end up in C</usr/local/bin>, and your
applet will break for no reason.
A much better solution is to do this:
chomp(my $executable = `which executable 2>/dev/null`);
if (!-x $executable) {
PerlPanel::warning(_('Cannot find the {program} program!', program => 'executable'));
}
This will allow C<executable> to be anywhere in the user's path, and will alert
the user if there was a problem.
Another area where this can cause problems is the use of external shared files,
such as graphics and icons. Wherever possible, package shared files in your
tarball as shown in L<PACKAGING YOUR APPLET>.
=head1 AUTHOR
Gavin Brown E<lt>gavin.brown@uk.comE<gt>.
=cut
|