Enabling multithreaded compilation.
[debian/lxpanel.git] / src / plugins / wnckpager / wnckpager.c
CommitLineData
f8c25730
DB
1/* pager.c -- pager module of lxpanel project
2 *
3 * Copyright (C) 2002-2003 Anatoly Asviyan <aanatoly@users.sf.net>
4 * Joe MacDonald <joe@deserted.net>
5 *
6 * This file is part of lxpanel.
7 *
8 * lxpanel is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
11 * any later version.
12 *
13 * lxpanel is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with sawfish; see the file COPYING. If not, write to
20 * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA.
22 */
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <stdlib.h>
27#include <gtk/gtk.h>
28
29#include <X11/Xlib.h>
30#include <X11/Xutil.h>
31
32#include <gdk-pixbuf/gdk-pixbuf.h>
33#include <glib/gi18n.h>
34#ifndef WNCK_I_KNOW_THIS_IS_UNSTABLE
35#define WNCK_I_KNOW_THIS_IS_UNSTABLE
36#endif
37#include <libwnck/libwnck.h>
38
39#include "panel.h"
40#include "misc.h"
41#include "plugin.h"
42
43#include "dbg.h"
44
45/* managed window: all related info that wm holds about its managed windows */
46typedef struct task {
47 Window win;
48 int x, y;
49 guint w, h;
50 gint refcount;
51 guint stacking;
52 guint desktop;
53 char *name, *iname;
54 int ws;
55 NetWMState nws;
56 NetWMWindowType nwwt;
57 guint focused:1;
58} task;
59
60typedef struct _desk desk;
61typedef struct _pager pager;
62
63#define MAX_DESK_NUM 20
64/* map of a desktop */
65struct _desk {
66 GtkWidget *da;
67 GdkPixmap *pix;
68 int no, dirty, first;
69 gfloat scalew, scaleh;
70 pager *pg;
71};
72
73struct _pager {
74 Plugin* plugin;
75 GtkWidget *box, *eb;
76 desk *desks[MAX_DESK_NUM];
77 guint desknum;
78 guint curdesk;
79 int dw, dh;
80 gfloat scalex, scaley, ratio;
81 Window *wins;
82 int winnum, dirty;
83 GHashTable* htable;
84 task *focusedtask;
85};
86
87
88#define TASK_VISIBLE(tk) \
89 (!( (tk)->nws.hidden || (tk)->nws.skip_pager ))
90//if (t->nws.skip_pager || t->nwwt.desktop /*|| t->nwwt.dock || t->nwwt.splash*/ )
91
92static void pager_rebuild_all(FbEv *ev, pager *pg);
93
94static inline void desk_set_dirty_by_win(pager *p, task *t);
95static inline void desk_set_dirty(desk *d);
96static inline void desk_set_dirty_all(pager *pg);
97/*
98static void desk_clear_pixmap(desk *d);
99static gboolean task_remove_stale(Window *win, task *t, pager *p);
100static gboolean task_remove_all(Window *win, task *t, pager *p);
101*/
102
103
104
105
106/*****************************************************************
107 * Task Management Routines *
108 *****************************************************************/
109
110
111/* tell to remove element with zero refcount */
112static gboolean
113task_remove_stale(Window *win, task *t, pager *p)
114{
115 if (t->refcount-- == 0) {
116 desk_set_dirty_by_win(p, t);
117 if (p->focusedtask == t)
118 p->focusedtask = NULL;
119 DBG("del %x\n", t->win);
120 g_free(t);
121 return TRUE;
122 }
123 return FALSE;
124}
125
126/* tell to remove element with zero refcount */
127static gboolean
128task_remove_all(Window *win, task *t, pager *p)
129{
130 g_free(t);
131 return TRUE;
132}
133
134
135static void
136task_get_sizepos(task *t)
137{
138 Window root, junkwin;
139 int rx, ry;
140 guint dummy;
141 XWindowAttributes win_attributes;
142
143 ENTER;
144 if (!XGetWindowAttributes(GDK_DISPLAY(), t->win, &win_attributes)) {
145 if (!XGetGeometry (GDK_DISPLAY(), t->win, &root, &t->x, &t->y, &t->w, &t->h,
146 &dummy, &dummy)) {
147 t->x = t->y = t->w = t->h = 2;
148 }
149
150 } else {
151 XTranslateCoordinates (GDK_DISPLAY(), t->win, win_attributes.root,
152 -win_attributes.border_width,
153 -win_attributes.border_width,
154 &rx, &ry, &junkwin);
155 t->x = rx;
156 t->y = ry;
157 t->w = win_attributes.width;
158 t->h = win_attributes.height;
159 DBG("win=0x%x WxH=%dx%d\n", t->win,t->w, t->h);
160 }
161 RET();
162}
163
164
165static void
166task_update_pix(task *t, desk *d)
167{
168 int x, y, w, h;
169 GtkWidget *widget;
170 Panel* p;
171
172 ENTER;
173 g_return_if_fail(d->pix != NULL);
174 if (!TASK_VISIBLE(t))
175 RET();;
176
177 p = d->pg->plugin->panel;
178 if (t->desktop < p->desknum &&
179 t->desktop != d->no)
180 RET();
181
182 x = (gfloat)t->x * d->scalew;
183 y = (gfloat)t->y * d->scaleh;
184 w = (gfloat)t->w * d->scalew;
185 //h = (gfloat)t->h * d->scaleh;
186 h = (t->nws.shaded) ? 3 : (gfloat)t->h * d->scaleh;
187 if (w < 3 || h < 3)
188 RET();
189 widget = GTK_WIDGET(d->da);
190 gdk_draw_rectangle (d->pix,
191 (d->pg->focusedtask == t) ?
192 widget->style->bg_gc[GTK_STATE_SELECTED] :
193 widget->style->bg_gc[GTK_STATE_NORMAL],
194 TRUE,
195 x+1, y+1, w-1, h-1);
196 gdk_draw_rectangle (d->pix,
197 (d->pg->focusedtask == t) ?
198 widget->style->fg_gc[GTK_STATE_SELECTED] :
199 widget->style->fg_gc[GTK_STATE_NORMAL],
200 FALSE,
201 x, y, w, h);
202 RET();
203}
204
205
206/*****************************************************************
207 * Desk Functions *
208 *****************************************************************/
209static void
210desk_clear_pixmap(desk *d)
211{
212 GtkWidget *widget;
213
214 ENTER;
215 DBG("d->no=%d\n", d->no);
216 if (!d->pix)
217 RET();
218 widget = GTK_WIDGET(d->da);
219 gdk_draw_rectangle (d->pix,
220 ((d->no == d->pg->curdesk) ?
221 widget->style->dark_gc[GTK_STATE_SELECTED] :
222 widget->style->dark_gc[GTK_STATE_NORMAL]),
223 TRUE,
224 0, 0,
225 widget->allocation.width,
226 widget->allocation.height);
227
228 RET();
229}
230
231
232
233static inline void
234desk_set_dirty(desk *d)
235{
236 ENTER;
237 d->dirty = 1;
238 gtk_widget_queue_draw(d->da);
239 RET();
240}
241
242static inline void
243desk_set_dirty_all(pager *pg)
244{
245 int i;
246 ENTER;
247 for (i = 0; i < pg->desknum; i++)
248 desk_set_dirty(pg->desks[i]);
249 RET();
250}
251
252static inline void
253desk_set_dirty_by_win(pager *p, task *t)
254{
255 ENTER;
256 if (t->nws.skip_pager || t->nwwt.desktop /*|| t->nwwt.dock || t->nwwt.splash*/ )
257 RET();
258 if (t->desktop < p->desknum)
259 desk_set_dirty(p->desks[t->desktop]);
260 else
261 desk_set_dirty_all(p);
262 RET();
263}
264
265/* Redraw the screen from the backing pixmap */
266static gint
267desk_expose_event (GtkWidget *widget, GdkEventExpose *event, desk *d)
268{
269 ENTER;
270 DBG("d->no=%d\n", d->no);
271
272 if (d->dirty) {
273 pager *pg = d->pg;
274 task *t;
275 int j;
276
277 d->dirty = 0;
278 desk_clear_pixmap(d);
279 for (j = 0; j < pg->winnum; j++) {
280 if (!(t = g_hash_table_lookup(pg->htable, &pg->wins[j])))
281 continue;
282 task_update_pix(t, d);
283 }
284 }
285 gdk_draw_drawable(widget->window,
286 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
287 d->pix,
288 event->area.x, event->area.y,
289 event->area.x, event->area.y,
290 event->area.width, event->area.height);
291 RET(FALSE);
292}
293
294/* Upon realize and every resize creates a new backing pixmap of the appropriate size */
295static gint
296desk_configure_event (GtkWidget *widget, GdkEventConfigure *event, desk *d)
297{
298 Panel* p;
299 int w, h;
300 ENTER;
301 DBG("d->no=%d %dx%d\n", d->no, widget->allocation.width, widget->allocation.height);
302 if (d->pix)
303 g_object_unref(d->pix);
304
305 d->pix = gdk_pixmap_new(widget->window,
306 widget->allocation.width,
307 widget->allocation.height,
308 -1);
309
310 d->scalew = (gfloat)widget->allocation.height / (gfloat)gdk_screen_height();
311 d->scaleh = (gfloat)widget->allocation.width / (gfloat)gdk_screen_width();
312 desk_set_dirty(d);
313
314 p = d->pg->plugin->panel;
315 //request best size
316 if (p->orientation != ORIENT_HORIZ) {
317 w = widget->allocation.width;
318 h = (gfloat) w / d->pg->ratio;
319 } else {
320 h = widget->allocation.height;
321 w = (gfloat) h * d->pg->ratio;
322 }
323 DBG("requesting %dx%d\n", w, h);
324 gtk_widget_set_size_request(widget, w, h);
325
326 RET(FALSE);
327}
328
329static gint
330desk_button_press_event(GtkWidget * widget, GdkEventButton * event, desk *d)
331{
332 ENTER;
333 if( event->button == 3 ) { /* right button */
334 GtkMenu* popup =(GtkMenu*) lxpanel_get_panel_menu
335 ( d->pg->plugin->panel, d->pg->plugin, FALSE );
336 gtk_menu_popup( popup, NULL, NULL, NULL, NULL, event->button, event->time );
337 return TRUE;
338 }
339
340 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, d->no, 0, 0, 0, 0);
341 RET(TRUE);
342}
343
344/*
345static gint
346desk_button_release_event(GtkWidget * widget, GdkEventButton * event, desk *d)
347{
348 ENTER;
349 DBG("t=%d\n", d->no);
350 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, d->no, 0, 0, 0, 0);
351 RET(TRUE);
352}
353*/
354
355static gint
356desk_scroll_event (GtkWidget *widget, GdkEventScroll *event, desk *d)
357{
358 int i;
359
360 ENTER;
361 DBG("scroll direction = %d\n", event->direction);
362 i = d->pg->curdesk;
363 if (event->direction == GDK_SCROLL_UP ||event->direction == GDK_SCROLL_LEFT) {
364 i--;
365 if (i < 0)
366 i = d->pg->desknum - 1;
367 } else {
368 i++;
369 if (i >= d->pg->desknum)
370 i = 0;
371 }
372 Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, i, 0, 0, 0, 0);
373 RET(TRUE);
374}
375
376static void
377desk_new(pager *pg, int i)
378{
379 desk *d;
380
381 ENTER;
382 g_assert(i < pg->desknum);
383 d = pg->desks[i] = g_new0(desk, 1);
384 d->pg = pg;
385 d->pix = NULL;
386 d->dirty = 0;
387 d->first = 1;
388 d->no = i;
389
390 d->da = gtk_drawing_area_new();
391 //gtk_widget_set_size_request(GTK_WIDGET(d->da), 10, 10);
392 gtk_box_pack_start(GTK_BOX(pg->box), d->da, TRUE, TRUE, 0);
393 gtk_widget_add_events (d->da, GDK_EXPOSURE_MASK
394 | GDK_BUTTON_PRESS_MASK
395 | GDK_BUTTON_RELEASE_MASK);
396 g_signal_connect (G_OBJECT (d->da), "expose_event",
397 (GCallback) desk_expose_event, (gpointer)d);
398 g_signal_connect (G_OBJECT (d->da), "configure_event",
399 (GCallback) desk_configure_event, (gpointer)d);
400 g_signal_connect (G_OBJECT (d->da), "scroll-event",
401 (GCallback) desk_scroll_event, (gpointer)d);
402 g_signal_connect (G_OBJECT (d->da), "button_press_event",
403 (GCallback) desk_button_press_event, (gpointer)d);
404 //g_signal_connect (G_OBJECT (d->da), "button_release_event",
405 // (GCallback) desk_button_release_event, (gpointer)d);
406 gtk_widget_show(d->da);
407 DBG("before pack\n");
408
409 DBG("after show\n");
410 RET();
411}
412
413static void
414desk_free(pager *pg, int i)
415{
416 desk *d;
417
418 ENTER;
419 d = pg->desks[i];
420 DBG("i=%d d->no=%d d->da=%p d->pix=%p\n",
421 i, d->no, d->da, d->pix);
422 if (d->pix)
423 g_object_unref(d->pix);
424 gtk_widget_destroy(d->da);
425 g_free(d);
426 RET();
427}
428
429
430/*****************************************************************
431 * Netwm/WM Interclient Communication *
432 *****************************************************************/
433
434static void
435do_net_active_window(FbEv *ev, pager *p)
436{
437 Window *fwin;
438 task *t;
439
440 ENTER;
441 fwin = get_xaproperty(GDK_ROOT_WINDOW(), a_NET_ACTIVE_WINDOW, XA_WINDOW, 0);
442 DBG("win=%x\n", fwin ? *fwin : 0);
443 if (fwin) {
444 t = g_hash_table_lookup(p->htable, fwin);
445 if (t != p->focusedtask) {
446 if (p->focusedtask)
447 desk_set_dirty_by_win(p, p->focusedtask);
448 p->focusedtask = t;
449 if (t)
450 desk_set_dirty_by_win(p, t);
451 }
452 XFree(fwin);
453 } else {
454 if (p->focusedtask) {
455 desk_set_dirty_by_win(p, p->focusedtask);
456 p->focusedtask = NULL;
457 }
458 }
459 RET();
460}
461
462static void
463do_net_current_desktop(FbEv *ev, pager *p)
464{
465 ENTER;
466 desk_set_dirty(p->desks[p->curdesk]);
467 p->curdesk = get_net_current_desktop ();
468 if (p->curdesk >= p->desknum)
469 p->curdesk = 0;
470 desk_set_dirty(p->desks[p->curdesk]);
471 RET();
472}
473
474
475static void
476do_net_client_list_stacking(FbEv *ev, pager *p)
477{
478 int i;
479 task *t;
480
481 ENTER;
482 if (p->wins)
483 XFree(p->wins);
484 p->wins = get_xaproperty (GDK_ROOT_WINDOW(), a_NET_CLIENT_LIST_STACKING,
485 XA_WINDOW, &p->winnum);
486 if (!p->wins || !p->winnum)
487 RET();
488
489 /* refresh existing tasks and add new */
490 for (i = 0; i < p->winnum; i++) {
491 if ((t = g_hash_table_lookup(p->htable, &p->wins[i]))) {
492 t->refcount++;
493 if (t->stacking != i) {
494 t->stacking = i;
495 desk_set_dirty_by_win(p, t);
496 }
497 } else {
498 t = g_new0(task, 1);
499 t->refcount++;
500 t->win = p->wins[i];
501 t->ws = get_wm_state (t->win);
502 t->desktop = get_net_wm_desktop(t->win);
503 get_net_wm_state(t->win, &t->nws);
504 get_net_wm_window_type(t->win, &t->nwwt);
505 task_get_sizepos(t);
506 if (!FBPANEL_WIN(t->win))
507 XSelectInput (GDK_DISPLAY(), t->win, PropertyChangeMask | StructureNotifyMask);
508 g_hash_table_insert(p->htable, &t->win, t);
509 DBG("add %x\n", t->win);
510 desk_set_dirty_by_win(p, t);
511 }
512 }
513 /* pass throu hash table and delete stale windows */
514 g_hash_table_foreach_remove(p->htable, (GHRFunc) task_remove_stale, (gpointer)p);
515 RET();
516}
517
518
519/*****************************************************************
520 * Pager Functions *
521 *****************************************************************/
522/*
523static void
524pager_unmapnotify(pager *p, XEvent *ev)
525{
526 Window win = ev->xunmap.window;
527 task *t;
528 if (!(t = g_hash_table_lookup(p->htable, &win)))
529 RET();
530 DBG("pager_unmapnotify: win=0x%x\n", win);
531 RET();
532 t->ws = WithdrawnState;
533 desk_set_dirty_by_win(p, t);
534 RET();
535}
536*/
537static void
538pager_configurenotify(pager *p, XEvent *ev)
539{
540 Window win = ev->xconfigure.window;
541 task *t;
542
543 ENTER;
544
545 if (!(t = g_hash_table_lookup(p->htable, &win)))
546 RET();
547 DBG("win=0x%x\n", win);
548 task_get_sizepos(t);
549 desk_set_dirty_by_win(p, t);
550 RET();
551}
552
553static void
554pager_propertynotify(pager *p, XEvent *ev)
555{
556 Atom at = ev->xproperty.atom;
557 Window win = ev->xproperty.window;
558 task *t;
559
560 ENTER;
561 if ((win == GDK_ROOT_WINDOW()) || !(t = g_hash_table_lookup(p->htable, &win)))
562 RET();
563
564 /* The property is deleted */
565 if( ((XPropertyEvent*)ev)->state == 1 )
566 return;
567
568 DBG("window=0x%x\n", t->win);
569 if (at == a_WM_STATE) {
570 DBG("event=WM_STATE\n");
571 t->ws = get_wm_state (t->win);
572 } else if (at == a_NET_WM_STATE) {
573 DBG("event=NET_WM_STATE\n");
574 get_net_wm_state(t->win, &t->nws);
575 } else if (at == a_NET_WM_DESKTOP) {
576 DBG("event=NET_WM_DESKTOP\n");
577 desk_set_dirty_by_win(p, t); // to clean up desks where this task was
578 t->desktop = get_net_wm_desktop(t->win);
579 } else {
580 RET();
581 }
582 desk_set_dirty_by_win(p, t);
583 RET();
584}
585
586static GdkFilterReturn
587pager_event_filter( XEvent *xev, GdkEvent *event, pager *pg)
588{
589 ENTER;
590 if (xev->type == PropertyNotify )
591 pager_propertynotify(pg, xev);
592 else if (xev->type == ConfigureNotify )
593 pager_configurenotify(pg, xev);
594 RET(GDK_FILTER_CONTINUE);
595}
596
597
598
599
600
601static void
602pager_rebuild_all(FbEv *ev, pager *pg)
603{
5d26221e 604 int desknum, dif, i;
f8c25730
DB
605
606 ENTER;
607 desknum = pg->desknum;
f8c25730
DB
608
609 pg->desknum = get_net_number_of_desktops();
610 if (pg->desknum < 1)
611 pg->desknum = 1;
612 else if (pg->desknum > MAX_DESK_NUM) {
613 pg->desknum = MAX_DESK_NUM;
614 ERR("pager: max number of supported desks is %d\n", MAX_DESK_NUM);
615 }
616 pg->curdesk = get_net_current_desktop();
617 if (pg->curdesk >= pg->desknum)
618 pg->curdesk = 0;
619 DBG("desknum=%d curdesk=%d\n", desknum, curdesk);
620 DBG("pg->desknum=%d pg->curdesk=%d\n", pg->desknum, pg->curdesk);
621 dif = pg->desknum - desknum;
622
623 if (dif == 0)
624 RET();
625
626 if (dif < 0) {
627 /* if desktops were deleted then delete their maps also */
628 for (i = pg->desknum; i < desknum; i++)
629 desk_free(pg, i);
630 } else {
631 for (i = desknum; i < pg->desknum; i++)
632 desk_new(pg, i);
633 }
634 do_net_client_list_stacking(NULL, pg);
635 RET();
636}
637
638
639static int
640pager_wnck_constructor(Plugin *plug, char **fp)
641{
642 pager *pg;
643
644 ENTER;
645 pg = g_new0(pager, 1);
646 g_return_val_if_fail(pg != NULL, 0);
647 plug->priv = pg;
648 pg->plugin = plug;
649
650 plug->pwid = gtk_event_box_new();
651 GTK_WIDGET_SET_FLAGS( plug->pwid, GTK_NO_WINDOW );
652
653 pg->htable = g_hash_table_new (g_int_hash, g_int_equal);
654
655 pg->box = wnck_pager_new(NULL);
656 g_return_val_if_fail(pg->box != NULL, 0);
657 //set orientation
658 wnck_pager_set_orientation (WNCK_PAGER (pg->box),pg->plugin->panel->orientation);
659 wnck_pager_set_n_rows (WNCK_PAGER (pg->box), 1); //pager->rows);
660 wnck_pager_set_display_mode (WNCK_PAGER (pg->box),WNCK_PAGER_DISPLAY_CONTENT);
661 //pager->show_names ? WNCK_PAGER_DISPLAY_NAME : WNCK_PAGER_DISPLAY_CONTENT);
662 //gtk_widget_show (pg->box);
663 //gtk_container_add (GTK_CONTAINER (plugin), pg->box);
664
665 gtk_container_set_border_width (GTK_CONTAINER (pg->box), 2);
666 gtk_widget_show(pg->box);
667
668 gtk_container_set_border_width (GTK_CONTAINER (plug->pwid), 1);
669 gtk_container_add(GTK_CONTAINER(plug->pwid), pg->box);
670 pg->eb = pg->box;
671
672 pg->ratio = (gfloat)gdk_screen_width() / (gfloat)gdk_screen_height();
673 pg->scaley = (gfloat)pg->dh / (gfloat)gdk_screen_height();
674 pg->scalex = (gfloat)pg->dw / (gfloat)gdk_screen_width();
675
676 pager_rebuild_all(fbev, pg);
677 //do_net_current_desktop(fbev, pg);
678 //do_net_client_list_stacking(fbev, pg);
679
680 gdk_window_add_filter(NULL, (GdkFilterFunc)pager_event_filter, pg );
681
682 g_signal_connect (G_OBJECT (fbev), "current_desktop",
683 G_CALLBACK (do_net_current_desktop), (gpointer) pg);
684 g_signal_connect (G_OBJECT (fbev), "active_window",
685 G_CALLBACK (do_net_active_window), (gpointer) pg);
686 g_signal_connect (G_OBJECT (fbev), "number_of_desktops",
687 G_CALLBACK (pager_rebuild_all), (gpointer) pg);
688 g_signal_connect (G_OBJECT (fbev), "client_list_stacking",
689 G_CALLBACK (do_net_client_list_stacking), (gpointer) pg);
690 RET(1);
691}
692
693static void
694pager_destructor(Plugin *p)
695{
696 pager *pg = (pager *)p->priv;
697
698 ENTER;
699 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), do_net_current_desktop, pg);
700 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), do_net_active_window, pg);
701 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), pager_rebuild_all, pg);
702 g_signal_handlers_disconnect_by_func(G_OBJECT (fbev), do_net_client_list_stacking, pg);
703 gdk_window_remove_filter(NULL, (GdkFilterFunc)pager_event_filter, pg);
704 while (--pg->desknum) {
705 desk_free(pg, pg->desknum);
706 }
707 g_hash_table_foreach_remove(pg->htable, (GHRFunc) task_remove_all, (gpointer)pg);
708 g_hash_table_destroy(pg->htable);
709 gtk_widget_destroy(pg->eb);
710 g_free(pg);
711 RET();
712}
713
714
715PluginClass wnckpager_plugin_class = {
716
717 PLUGINCLASS_VERSIONING,
718
719 type : "wnckpager",
720 name : N_("WNCKPager"),
721 version: "1.0",
722 description : N_("WNCKpager plugin"),
723
724 /* FIXME: orientation should be handled!! */
725 constructor : pager_wnck_constructor,
726 destructor : pager_destructor,
727 config : NULL,
728 save : NULL,
729 panel_configuration_changed : NULL
730};