]> Dogcows Code - chaz/tint2/commitdiff
fixed bug : clock resize when date changed, number of desktop changed
authorThierry Lorthiois <lorthiois@bbsoft.fr>
Tue, 10 Feb 2009 23:16:10 +0000 (23:16 +0000)
committerThierry Lorthiois <lorthiois@bbsoft.fr>
Tue, 10 Feb 2009 23:16:10 +0000 (23:16 +0000)
21 files changed:
ChangeLog
src/Makefile
src/clock/clock.c
src/clock/clock.h
src/config.c
src/panel.c
src/panel.h
src/server.c
src/server.h
src/systray/docker.c
src/systray/net.c
src/systray/systraybar.c
src/systray/systraybar.h
src/systray/tint_merge.h
src/taskbar/task.c
src/taskbar/taskbar.c
src/taskbar/taskbar.h
src/tint.c
src/tint2
src/util/area.c
src/util/area.h

index 9cce93f667aa306443e10ac89f8da747403f6b72..1a94a50dcdfa4af7d974f3fcd41298b6f8f00619 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-02-11
+- fixed bug with number of desktop changed
+- fixed clock resize when date changed
+
 2009-02-08
 - backward compatibility with tint-0.6 (convert config file)
   feature freeze until next release. need bug correction.
index 63d4bd24204bd8afe1b8f784a564651f0a2ae6a7..c31fc32c18d43b1e26a7abd8c1374ef8f5d2d987 100644 (file)
@@ -1,8 +1,8 @@
 CFLAGS= -O2
 CC = gcc
-FLAGS=-W -Wall -g `pkg-config --cflags --libs cairo pangocairo x11 xinerama imlib2 glib-2.0`
+FLAGS=-W -g `pkg-config --cflags --libs cairo pangocairo x11 xinerama imlib2 glib-2.0`
 PROGNAME=tint2
-FILES=tint.c server.c panel.c config.c taskbar/task.c taskbar/taskbar.c clock/clock.c systray/systraybar.c systray/docker.c systray/icons.c systray/kde.c systray/net.c systray/xproperty.c util/window.c util/area.c
+FILES=tint.c server.c panel.c config.c taskbar/task.c taskbar/taskbar.c clock/clock.c systray/systraybar.c util/window.c util/area.c
 
 ifndef DESTDIR
        ifndef PREFIX
index 64ac1ba0f7bb9476d8c9e49363a4d3403bdc1039..e7bb32f66699544ca09c60c1a94194bc113343b2 100644 (file)
@@ -37,18 +37,19 @@ struct timeval time_clock;
 int  time_precision;
 PangoFontDescription *time1_font_desc;
 PangoFontDescription *time2_font_desc;
+static char buf_time[40];
+static char buf_date[40];
 
 
 void init_clock(Clock *clock, Area *parent)
 {
    Panel *panel = (Panel *)parent;
-   char buf_time[40];
-   char buf_date[40];
    int time_height, time_height_ink, date_height, date_height_ink;
 
        clock->area.parent = parent;
        clock->area.panel = panel;
-   clock->area.draw_foreground = draw_foreground_clock;
+   clock->area._draw_foreground = draw_foreground_clock;
+   clock->area._resize = resize_clock;
    if (!time1_format) return;
 
    if (strchr(time1_format, 'S') == NULL) time_precision = 60;
@@ -62,7 +63,7 @@ void init_clock(Clock *clock, Area *parent)
 
    clock->area.posy = parent->pix.border.width + parent->paddingy;
    clock->area.height = parent->height - (2 * clock->area.posy);
-   clock->area.width = 0;  // force posx and width detection
+   clock->area.resize = 1;
    clock->area.redraw = 1;
 
    strftime(buf_time, sizeof(buf_time), time1_format, localtime(&time_clock.tv_sec));
@@ -82,12 +83,45 @@ void init_clock(Clock *clock, Area *parent)
 
 
 void draw_foreground_clock (void *obj, cairo_t *c, int active)
+{
+   Clock *clock = obj;
+   PangoLayout *layout;
+
+   //printf("  draw_foreground_clock : %s en (%d, %d)\n", buf_time, clock->area.posx, clock->area.width);
+   layout = pango_cairo_create_layout (c);
+
+   // draw layout
+   pango_layout_set_font_description (layout, time1_font_desc);
+   pango_layout_set_width (layout, clock->area.width * PANGO_SCALE);
+   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
+   pango_layout_set_text (layout, buf_time, strlen(buf_time));
+
+   cairo_set_source_rgba (c, clock->font.color[0], clock->font.color[1], clock->font.color[2], clock->font.alpha);
+
+   pango_cairo_update_layout (c, layout);
+   cairo_move_to (c, 0, clock->time1_posy);
+   pango_cairo_show_layout (c, layout);
+
+   if (time2_format) {
+      pango_layout_set_font_description (layout, time2_font_desc);
+      pango_layout_set_indent(layout, 0);
+      pango_layout_set_text (layout, buf_date, strlen(buf_date));
+      pango_layout_set_width (layout, clock->area.width * PANGO_SCALE);
+
+      pango_cairo_update_layout (c, layout);
+      cairo_move_to (c, 0, clock->time2_posy);
+      pango_cairo_show_layout (c, layout);
+   }
+
+   g_object_unref (layout);
+}
+
+
+void resize_clock (void *obj)
 {
    Area *parent = ((Area*)obj)->parent;
    Clock *clock = obj;
    PangoLayout *layout;
-   char buf_time[40];
-   char buf_date[40];
    int time_width, date_width, new_width;
 
    time_width = date_width = 0;
@@ -95,8 +129,14 @@ void draw_foreground_clock (void *obj, cairo_t *c, int active)
    if (time2_format)
       strftime(buf_date, sizeof(buf_date), time2_format, localtime(&time_clock.tv_sec));
 
-   //printf("  draw_foreground_clock : %s\n", buf_time);
-redraw:
+   //printf("  resize_clock\n");
+   cairo_surface_t *cs;
+   cairo_t *c;
+       Pixmap pmap;
+       pmap = XCreatePixmap (server.dsp, server.root_win, clock->area.width, clock->area.height, server.depth);
+
+   cs = cairo_xlib_surface_create (server.dsp, pmap, server.visual, clock->area.width, clock->area.height);
+   c = cairo_create (cs);
    layout = pango_cairo_create_layout (c);
 
    // check width
@@ -110,44 +150,22 @@ redraw:
       pango_layout_set_text (layout, buf_date, strlen(buf_date));
       pango_layout_get_pixel_size (layout, &date_width, NULL);
    }
+
    if (time_width > date_width) new_width = time_width;
    else new_width = date_width;
    new_width += (2*clock->area.paddingxlr) + (2*clock->area.pix.border.width);
 
-   if (new_width > clock->area.width || (new_width != clock->area.width && date_width > time_width)) {
-      //printf("clock_width %d, new_width %d\n", clock->area.width, new_width);
+   if (new_width > clock->area.width || new_width < (clock->area.width-3)) {
       // resize clock
+      //printf("clock_width %d, new_width %d\n", clock->area.width, new_width);
       clock->area.width = new_width;
       clock->area.posx = parent->width - clock->area.width - parent->paddingxlr - parent->pix.border.width;
-
-      g_object_unref (layout);
-      resize_taskbar(parent);
-      goto redraw;
-   }
-
-   // draw layout
-   pango_layout_set_font_description (layout, time1_font_desc);
-   pango_layout_set_width (layout, clock->area.width * PANGO_SCALE);
-   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
-   pango_layout_set_text (layout, buf_time, strlen(buf_time));
-
-   cairo_set_source_rgba (c, clock->font.color[0], clock->font.color[1], clock->font.color[2], clock->font.alpha);
-
-   pango_cairo_update_layout (c, layout);
-   cairo_move_to (c, 0, clock->time1_posy);
-   pango_cairo_show_layout (c, layout);
-
-   if (time2_format) {
-      pango_layout_set_font_description (layout, time2_font_desc);
-      pango_layout_set_indent(layout, 0);
-      pango_layout_set_text (layout, buf_date, strlen(buf_date));
-      pango_layout_set_width (layout, clock->area.width * PANGO_SCALE);
-
-      pango_cairo_update_layout (c, layout);
-      cairo_move_to (c, 0, clock->time2_posy);
-      pango_cairo_show_layout (c, layout);
+      set_resize(parent);
    }
 
    g_object_unref (layout);
+   cairo_destroy (c);
+   cairo_surface_destroy (cs);
+   XFreePixmap (server.dsp, pmap);
 }
 
index a0747971c6d3e8904be79571bdad5ff235ca6476..613c40d4ed0017dcc8f6d272c96ca84367bb110b 100644 (file)
@@ -37,5 +37,6 @@ void init_clock(Clock *clock, Area *parent);
 
 void draw_foreground_clock (void *obj, cairo_t *c, int active);
 
+void resize_clock (void *obj);
 
 #endif
index 6f7d42a64bb538506cfaba1ca9c465bbacf96322..f4236542e112263189ae6f37b3fb2c15b8b4c23c 100644 (file)
@@ -38,6 +38,7 @@
 #include "server.h"
 #include "task.h"
 #include "taskbar.h"
+#include "systraybar.h"
 #include "clock.h"
 #include "panel.h"
 #include "config.h"
@@ -409,6 +410,14 @@ void add_entry (char *key, char *value)
       memcpy(&panel_config->g_task.area.pix_active.border, &a->pix.border, sizeof(Border));
    }
 
+   /* Trayer */
+   else if (strcmp (key, "trayer_background_id") == 0) {
+      int id = atoi (value);
+      Area *a = g_slist_nth_data(list_back, id);
+      memcpy(&panel_config->trayer.area.pix.back, &a->pix.back, sizeof(Color));
+      memcpy(&panel_config->trayer.area.pix.border, &a->pix.border, sizeof(Border));
+   }
+
    /* Mouse actions */
    else if (strcmp (key, "mouse_middle") == 0)
       get_action (value, &mouse_middle);
@@ -539,20 +548,18 @@ void config_finish ()
       fprintf(stderr, "tint2 exit : monitor %d not found.\n", panel_config->monitor+1);
       exit(0);
    }
-   else {
-      if (!server.monitor[panel_config->monitor].width || !server.monitor[panel_config->monitor].height)
-         fprintf(stderr, "tint2 error : invalid monitor size.\n");
-   }
 
        // alloc panels
    int i;
    if (panel_config->monitor >= 0) {
+       // just one monitor
           nb_panel = 1;
           panel1 = calloc(nb_panel, sizeof(Panel));
           memcpy(panel1, panel_config, sizeof(Panel));
+          panel1->monitor = panel_config->monitor;
        }
        else {
-       // multi monitor
+       // all monitors
           nb_panel = server.nb_monitor;
           panel1 = calloc(nb_panel, sizeof(Panel));
 
@@ -566,10 +573,14 @@ void config_finish ()
    init_taskbar();
    visible_object();
 
-       task_refresh_tasklist();
-       panel_refresh = 1;
-
        cleanup_config();
+
+   // force the resize (using visible_object() order)
+       for (i=0 ; i < nb_panel ; i++) {
+               //init_systray(&panel1[i].trayer, &panel1[i].area);
+               set_resize(&panel1[i]);
+       }
+       task_refresh_tasklist();
 }
 
 
index 3e2d76e12461ab092b2287c041d34a7d50f1a138..881fdaf8695b19e0903200ae2dd6ab4adfe28502 100644 (file)
@@ -115,6 +115,7 @@ void init_panel()
 
                init_clock(&p->clock, &p->area);
        }
+       panel_refresh = 1;
 }
 
 
@@ -129,10 +130,17 @@ void cleanup_panel()
        for (i=0 ; i < nb_panel ; i++) {
                p = &panel1[i];
 
-          free_area(&p->area);
+               // no free_area(&p->area) because it's the list of visible objects
+               if (p->area.list) {
+                       g_slist_free(p->area.list);
+                       p->area.list = 0;
+               }
+
           free_area(&p->g_task.area);
           free_area(&p->g_taskbar);
        free_area(&p->clock.area);
+               if (p->area.pix.pmap) XFreePixmap(server.dsp, p->area.pix.pmap);
+               if (p->area.pix_active.pmap) XFreePixmap(server.dsp, p->area.pix_active.pmap);
                if (p->root_pmap) XFreePixmap(server.dsp, p->root_pmap);
                if (p->main_win) XDestroyWindow(server.dsp, p->main_win);
        }
@@ -154,8 +162,9 @@ void visual_refresh (Panel *p)
 
    // draw child object
    GSList *l = p->area.list;
-   for (; l ; l = l->next)
+   for (; l ; l = l->next) {
       refresh (l->data);
+       }
 
    XCopyArea(server.dsp, p->root_pmap, p->main_win, server.gc, 0, 0, p->area.width, p->area.height, 0, 0);
 
@@ -244,6 +253,8 @@ void visible_object()
                if (time1_format)
                        panel->area.list = g_slist_append(panel->area.list, &panel->clock);
 
+               //panel->area.list = g_slist_append(panel->area.list, &panel->trayer);
+
                Taskbar *taskbar;
                for (j=0 ; j < panel->nb_desktop ; j++) {
                        taskbar = &panel->taskbar[j];
index 5992f7a803a0c106a5ccfa8c96335f8dac9ce3da..3a4b6dd0a9bca6230ab98bd63a771e021becd00f 100644 (file)
@@ -61,7 +61,7 @@ typedef struct {
    int monitor;
 
    // --------------------------------------------------
-   // task annd taskbar parameter per panel
+   // task and taskbar parameter per panel
        Area g_taskbar;
        Global_task g_task;
 
@@ -77,7 +77,10 @@ typedef struct {
 
    // --------------------------------------------------
    // systray
-   Systraybar systraybar;
+   Systraybar trayer;
+
+       // global taskbar parameter
+       //Area g_systraybar;
 } Panel;
 
 
index 65d1099e3640a3d2e6e9223ce6565a0f13bee6bb..1fccd6e781711876ab372c6aadfe022b9c4b08e8 100644 (file)
@@ -28,6 +28,8 @@
 
 void server_catch_error (Display *d, XErrorEvent *ev){}
 
+static char *name_trayer = 0;
+
 
 void server_init_atoms ()
 {
@@ -68,6 +70,19 @@ void server_init_atoms ()
    server.atom.WM_NAME = XInternAtom(server.dsp, "WM_NAME", False);
    server.atom.__SWM_VROOT = XInternAtom(server.dsp, "__SWM_VROOT", False);
    server.atom._MOTIF_WM_HINTS = XInternAtom(server.dsp, "_MOTIF_WM_HINTS", False);
+
+       // systray protocol
+       name_trayer = g_strdup_printf("_NET_SYSTEM_TRAY_S%d", DefaultScreen(server.dsp));
+       server.atom._NET_SYSTEM_TRAY = XInternAtom(server.dsp, name_trayer, False);
+       server.atom._NET_SYSTEM_TRAY_OPCODE = XInternAtom(server.dsp, "_NET_SYSTEM_TRAY_OPCODE", False);
+       server.atom.MANAGER = XInternAtom(server.dsp, "MANAGER", False);
+       server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA = XInternAtom(server.dsp, "_NET_SYSTEM_TRAY_MESSAGE_DATA", False);
+}
+
+
+void cleanup_server()
+{
+       if (name_trayer) free(name_trayer);
 }
 
 
index b8014e6b3e9aec575bc7feb14777f2c7fb70431f..2d0aacb85047e952e425bf58c941af8c497e9c7a 100644 (file)
@@ -53,6 +53,10 @@ typedef struct Global_atom
        Atom WM_NAME;
        Atom __SWM_VROOT;
        Atom _MOTIF_WM_HINTS;
+       Atom _NET_SYSTEM_TRAY;
+       Atom _NET_SYSTEM_TRAY_OPCODE;
+       Atom MANAGER;
+       Atom _NET_SYSTEM_TRAY_MESSAGE_DATA;
 } Global_atom;
 
 
index 091c62e61c3208f3c209799dd839b528d0609686..a2e61526a6a82f9fdb3e58016fc6d947d58d7e21 100644 (file)
@@ -28,85 +28,6 @@ int icon_size = 24; /* width and height of systray icons */
 static gboolean exit_app = FALSE;
 
 /*
-void parse_cmd_line()
-{
-  int i;
-  gboolean help = FALSE;
-  
-  for (i = 1; i < argc; i++) {
-    if (0 == strcasecmp(argv[i], "-display")) {
-      ++i;
-      if (i < argc) {
-        display_string = argv[i];
-      } else { 
-        g_printerr("-display requires a parameter\n");
-        help = TRUE;
-      }
-    } else if (0 == strcasecmp(argv[i], "-wmaker")) {
-      wmaker = TRUE;
-    } else if (0 == strcasecmp(argv[i], "-vertical")) {
-      horizontal = FALSE;
-    } else if (0 == strcasecmp(argv[i], "-border")) {
-      ++i;
-
-      if (i < argc) {
-        int b = atoi(argv[i]);
-        if (b > 0) {
-          border = b;
-        } else {
-          g_printerr("-border must be a value greater than 0\n");
-          help = TRUE;
-        }
-      } else { 
-        g_printerr("-border requires a parameter\n");
-        help = TRUE;
-      }
-      } else if (0 == strcasecmp(argv[i], "-iconsize")) {
-      ++i;
-      if (i < argc) {
-        int s = atoi(argv[i]);
-        if (s > 0) {
-          icon_size = s;
-        } else {
-          g_printerr("-iconsize must be a value greater than 0\n");
-          help = TRUE;
-        }
-      } else { 
-        g_printerr("-iconsize requires a parameter\n");
-        help = TRUE;
-      }
-    } else {
-      if (argv[i][0] == '-')
-        help = TRUE;
-    }
-
-
-    if (help) {
-      
-      g_print("%s - version %s\n", argv[0], VERSION);
-      g_print("Copyright 2003, Ben Jansens <ben@orodu.net>\n\n");
-      g_print("Usage: %s [OPTIONS]\n\n", argv[0]);
-      g_print("Options:\n");
-      g_print("  -help             Show this help.\n");
-      g_print("  -display DISLPAY  The X display to connect to.\n");
-      g_print("  -border           The width of the border to put around the\n"
-              "                    system tray icons. Defaults to 1.\n");
-      g_print("  -vertical         Line up the icons vertically. Defaults to\n"
-              "                    horizontally.\n");
-      g_print("  -wmaker           WindowMaker mode. This makes docker a\n"
-              "                    fixed size (64x64) to appear nicely in\n"
-              "                    in WindowMaker.\n"
-              "                    Note: In this mode, you have a fixed\n"
-              "                    number of icons that docker can hold.\n");
-      g_print("  -iconsize SIZE    The size (width and height) to display\n"
-              "                    icons as in the system tray. Defaults to\n"
-              "                    24.\n");
-      exit(1);
-    }
-  }
-}
-*/
-
 void create_hint_win()
 {
   XWMHints hints;
@@ -136,7 +57,7 @@ void create_main_window()
   XTextProperty text;
   char *name = "Docker";
 
-  /* the border must be > 0 if not in wmaker mode */
+  // the border must be > 0 if not in wmaker mode
   assert(wmaker || border > 0);
 
   if (!wmaker)
@@ -145,7 +66,7 @@ void create_main_window()
   else
     win = XCreateSimpleWindow(display, root, 0, 0,
                               64, 64, 0, 0, 0);
-    
+
   assert(win);
 
   XStringListToTextProperty(&name, 1, &text);
@@ -154,21 +75,21 @@ void create_main_window()
   hints.flags = StateHint;
   hints.initial_state = WithdrawnState;
   XSetWMHints(display, win, &hints);
-  
+
   create_hint_win();
-  
+
   XSync(display, False);
   XSetWindowBackgroundPixmap(display, win, ParentRelative);
   XClearWindow(display, win);
 }
-
+*/
 
 void reposition_icons()
 {
   int x = border + ((width % icon_size) / 2),
       y = border + ((height % icon_size) / 2);
   GSList *it;
-  
+
   for (it = icons; it != NULL; it = g_slist_next(it)) {
     TrayWindow *traywin = it->data;
     traywin->x = x;
@@ -193,10 +114,10 @@ void fix_geometry()
 {
   GSList *it;
 
-  /* in wmaker mode we're a fixed size */
+  // in wmaker mode we're a fixed size
   if (wmaker) return;
-  
-  /* find the proper width and height */
+
+  //* find the proper width and height
   width = horizontal ? 0 : icon_size;
   height = horizontal ? icon_size : 0;
   for (it = icons; it != NULL; it = g_slist_next(it)) {
@@ -209,100 +130,13 @@ void fix_geometry()
   XResizeWindow(display, win, width + border * 2, height + border * 2);
 }
 
-
-void event_loop()
-{
-  XEvent e;
-  Window cover;
-  GSList *it;
-  
-  while (!exit_app) {
-    while (XPending(display)) {
-      XNextEvent(display, &e);
-
-      switch (e.type)
-      {
-      case PropertyNotify:
-        /* systray window list has changed? */
-        if (e.xproperty.atom == kde_systray_prop) {
-          XSelectInput(display, win, NoEventMask);
-          kde_update_icons();
-          XSelectInput(display, win, StructureNotifyMask);
-
-          while (XCheckTypedEvent(display, PropertyNotify, &e));
-        }
-
-        break;
-
-      case ConfigureNotify:
-        if (e.xany.window != win) {
-          /* find the icon it pertains to and beat it into submission */
-          GSList *it;
-  
-          for (it = icons; it != NULL; it = g_slist_next(it)) {
-            TrayWindow *traywin = it->data;
-            if (traywin->id == e.xany.window) {
-              XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
-                                icon_size, icon_size);
-              break;
-            }
-          }
-          break;
-        }
-        
-        /* briefly cover the entire containing window, which causes it and
-           all of the icons to refresh their windows. finally, they update
-           themselves when the background of the main window's parent changes.
-        */
-        cover = XCreateSimpleWindow(display, win, 0, 0,
-                                    border * 2 + width, border * 2 + height,
-                                    0, 0, 0);
-        XMapWindow(display, cover);
-        XDestroyWindow(display, cover);
-        
-        break;
-
-      case ReparentNotify:
-        if (e.xany.window == win) /* reparented to us */
-          break;
-      case UnmapNotify:
-      case DestroyNotify:
-        for (it = icons; it; it = g_slist_next(it)) {
-          if (((TrayWindow*)it->data)->id == e.xany.window) {
-            icon_remove(it);
-            break;
-          }
-        }
-        break;
-
-      case ClientMessage:
-        if (e.xclient.message_type == net_opcode_atom &&
-            e.xclient.format == 32 &&
-            e.xclient.window == net_sel_win)
-          net_message(&e.xclient);
-        
-      default:
-        break;
-      }
-    }
-    usleep(500000);
-  }
-
-  /* remove/unparent all the icons */
-  while (icons) {
-    /* do the remove here explicitly, cuz the event handler isn't going to
-       happen anymore. */
-    icon_remove(icons);
-  }
-}
-
 /*
 int main(int c, char **v)
 {
   struct sigaction act;
-  
+
   argc = c; argv = v;
-  
+
   act.sa_handler = signal_handler;
   act.sa_flags = 0;
   sigaction(SIGSEGV, &act, NULL);
@@ -325,18 +159,18 @@ int main(int c, char **v)
 
   if (wmaker)
     width = height = 64 - border * 2;
-  
+
   create_main_window();
 
-  // set up to find KDE systray icons, and get any that already exist 
+  // set up to find KDE systray icons, and get any that already exist
   kde_init();
 
   net_init();
 
   // we want to get ConfigureNotify events, and assume our parent's background
-  // has changed when we do, so we need to refresh ourself to match 
+  // has changed when we do, so we need to refresh ourself to match
   XSelectInput(display, win, StructureNotifyMask);
-  
+
   event_loop();
 
   XCloseDisplay(display);
index 9f0c94a0d4cdf5342b8dbc2288fe0cabc433b6ca..85918342918f5615bed9c4f5296a73dc0615ffd6 100644 (file)
@@ -14,7 +14,7 @@ static Atom net_message_data_atom;
 #define SYSTEM_TRAY_REQUEST_DOCK    0
 #define SYSTEM_TRAY_BEGIN_MESSAGE   1
 #define SYSTEM_TRAY_CANCEL_MESSAGE  2
-         
+
 static void net_create_selection_window()
 {
   net_sel_win = XCreateSimpleWindow(display, root, -1, -1, 1, 1, 0, 0, 0);
@@ -28,38 +28,11 @@ static void net_destroy_selection_window()
   net_sel_win = None;
 }
 
-
-void net_init()
+void net_icon_remove(TrayWindow *traywin)
 {
-  char *name;
-  XEvent m;
-
-  name = g_strdup_printf("_NET_SYSTEM_TRAY_S%d", DefaultScreen(display));
-  net_sel_atom = XInternAtom(display, name, False);
-  assert(net_sel_atom);
-  net_opcode_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_OPCODE", False);
-  assert(net_opcode_atom);
-  net_manager_atom = XInternAtom(display, "MANAGER", False);
-  assert(net_manager_atom);
-  net_message_data_atom = XInternAtom(display, "_NET_SYSTEM_TRAY_MESSAGE_DATA",
-                                      False);
-   assert(net_message_data_atom);
-
-  net_create_selection_window();
-  
-  XSetSelectionOwner(display, net_sel_atom, net_sel_win, CurrentTime);
-  if (XGetSelectionOwner(display, net_sel_atom) != net_sel_win)
-    return; /* we don't get the selection */
+  assert(traywin);
 
-  m.type = ClientMessage;
-  m.xclient.message_type = net_manager_atom;
-  m.xclient.format = 32;
-  m.xclient.data.l[0] = CurrentTime;
-  m.xclient.data.l[1] = net_sel_atom;
-  m.xclient.data.l[2] = net_sel_win;
-  m.xclient.data.l[3] = 0;
-  m.xclient.data.l[4] = 0;
-  XSendEvent(display, root, False, StructureNotifyMask, &m);
+  XSelectInput(display, traywin->id, NoEventMask);
 }
 
 
@@ -69,51 +42,3 @@ void net_destroy()
 }
 
 
-void net_message(XClientMessageEvent *e)
-{
-  unsigned long opcode;
-  Window id;
-
-  assert(e);
-
-  opcode = e->data.l[1];
-
-  switch (opcode)
-  {
-  case SYSTEM_TRAY_REQUEST_DOCK: /* dock a new icon */
-    id = e->data.l[2];
-    if (id && icon_add(id, NET))
-      XSelectInput(display, id, StructureNotifyMask);
-    break;
-
-  case SYSTEM_TRAY_BEGIN_MESSAGE:
-    g_printerr("Message From Dockapp\n");
-    id = e->window;
-    break;
-
-  case SYSTEM_TRAY_CANCEL_MESSAGE:
-    g_printerr("Message Cancelled\n");
-    id = e->window;
-    break;
-
-  default:
-    if (opcode == net_message_data_atom) {
-      g_printerr("Text For Message From Dockapp:\n%s\n", e->data.b);
-      id = e->window;
-      break;
-    }
-    
-    /* unknown message type. not in the spec. */
-    g_printerr("Warning: Received unknown client message to System Tray "
-               "selection window.\n");
-    break;
-  }
-}
-
-
-void net_icon_remove(TrayWindow *traywin)
-{
-  assert(traywin);
-  
-  XSelectInput(display, traywin->id, NoEventMask);
-}
index a6b3767ca90e2fed3f4b90754e6f871bb63911fd..0c515e9d726fcf0b3b50e70f1c1d2a56cf84b7bd 100644 (file)
 
 #include "systraybar.h"
 #include "server.h"
-#include "window.h"
+#include "panel.h"
 
+GSList *icons;
 
+/* defined in the systray spec */
+#define SYSTEM_TRAY_REQUEST_DOCK    0
+#define SYSTEM_TRAY_BEGIN_MESSAGE   1
+#define SYSTEM_TRAY_CANCEL_MESSAGE  2
+
+Window net_sel_win;
+
+
+void init_systray(Systraybar *sysbar, Area *parent)
+{
+   Panel *panel = (Panel *)parent;
+
+       sysbar->area.parent = parent;
+       sysbar->area.panel = panel;
+
+   sysbar->area.posy = parent->pix.border.width + parent->paddingy;
+   sysbar->area.height = parent->height - (2 * sysbar->area.posy);
+   sysbar->area.width = 100;
+
+   sysbar->area.posx = panel->area.width - panel->clock.area.width - panel->area.paddingxlr - panel->area.pix.border.width - panel->area.paddingx - sysbar->area.width;
+
+   sysbar->area.redraw = 1;
+
+printf("init_systray");
+
+       net_init();
+}
+
+
+// net_sel_atom == server.atom._NET_SYSTEM_TRAY
+// net_opcode_atom == server.atom._NET_SYSTEM_TRAY_OPCODE
+// net_manager_atom == server.atom.MANAGER
+// net_message_data_atom == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA
 
 int resize_systray (Systraybar *sysbar)
 {
@@ -38,3 +72,254 @@ int resize_systray (Systraybar *sysbar)
 
 
 
+Window win, root;
+int width, height;
+int border;
+int icon_size;
+
+
+void fix_geometry()
+{
+  GSList *it;
+
+  //* find the proper width and height
+  width = 0;
+  height = icon_size;
+  for (it = icons; it != NULL; it = g_slist_next(it)) {
+    width += icon_size;
+  }
+
+  XResizeWindow(server.dsp, win, width + border * 2, height + border * 2);
+}
+
+
+static void net_create_selection_window()
+{
+  net_sel_win = XCreateSimpleWindow(server.dsp, root, -1, -1, 1, 1, 0, 0, 0);
+}
+
+
+gboolean error;
+int window_error_handler(Display *d, XErrorEvent *e)
+{
+  d=d;e=e;
+  if (e->error_code == BadWindow) {
+    error = TRUE;
+  } else {
+    //g_printerr("X ERROR NOT BAD WINDOW!\n");
+    abort();
+  }
+  return 0;
+}
+
+
+gboolean icon_swallow(TrayWindow *traywin)
+{
+  XErrorHandler old;
+
+  error = FALSE;
+  old = XSetErrorHandler(window_error_handler);
+  XReparentWindow(server.dsp, traywin->id, win, 0, 0);
+  XSync(server.dsp, False);
+  XSetErrorHandler(old);
+
+  return !error;
+}
+
+
+// The traywin must have its id and type set.
+gboolean icon_add(Window id)
+{
+  TrayWindow *traywin;
+
+  traywin = g_new0(TrayWindow, 1);
+  traywin->id = id;
+
+  if (!icon_swallow(traywin)) {
+    g_free(traywin);
+    return FALSE;
+  }
+
+  // find the positon for the systray app window
+       int count = g_slist_length(icons);
+       traywin->x = border + ((width % icon_size) / 2) +
+       (count % (width / icon_size)) * icon_size;
+       traywin->y = border + ((height % icon_size) / 2) +
+       (count / (height / icon_size)) * icon_size;
+
+  // add the new icon to the list
+  icons = g_slist_append(icons, traywin);
+
+  // watch for the icon trying to resize itself!
+  XSelectInput(server.dsp, traywin->id, StructureNotifyMask);
+
+  // position and size the icon window
+  XMoveResizeWindow(server.dsp, traywin->id, traywin->x, traywin->y, icon_size, icon_size);
+
+  // resize our window so that the new window can fit in it
+  fix_geometry();
+
+  // flush before clearing, otherwise the clear isn't effective.
+  XFlush(server.dsp);
+  // make sure the new child will get the right stuff in its background
+  // for ParentRelative.
+  XClearWindow(server.dsp, win);
+
+  // show the window
+  XMapRaised(server.dsp, traywin->id);
+
+  return TRUE;
+}
+
+
+void net_init()
+{
+       // init systray protocol
+   net_sel_win = XCreateSimpleWindow(server.dsp, server.root_win, -1, -1, 1, 1, 0, 0, 0);
+
+       XSetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY, net_sel_win, CurrentTime);
+       if (XGetSelectionOwner(server.dsp, server.atom._NET_SYSTEM_TRAY) != net_sel_win) {
+               fprintf(stderr, "tint error : can't get trayer selection");
+               return;
+       }
+
+       XEvent m;
+       m.type = ClientMessage;
+       m.xclient.message_type = server.atom.MANAGER;
+       m.xclient.format = 32;
+       m.xclient.data.l[0] = CurrentTime;
+       m.xclient.data.l[1] = server.atom._NET_SYSTEM_TRAY;
+       m.xclient.data.l[2] = net_sel_win;
+       m.xclient.data.l[3] = 0;
+       m.xclient.data.l[4] = 0;
+       XSendEvent(server.dsp, server.root_win, False, StructureNotifyMask, &m);
+}
+
+
+void net_message(XClientMessageEvent *e)
+{
+  unsigned long opcode;
+  Window id;
+
+  opcode = e->data.l[1];
+
+  switch (opcode)
+  {
+  case SYSTEM_TRAY_REQUEST_DOCK: /* dock a new icon */
+    id = e->data.l[2];
+    if (id && icon_add(id))
+      XSelectInput(server.dsp, id, StructureNotifyMask);
+    break;
+
+  case SYSTEM_TRAY_BEGIN_MESSAGE:
+    //g_printerr("Message From Dockapp\n");
+    id = e->window;
+    break;
+
+  case SYSTEM_TRAY_CANCEL_MESSAGE:
+    //g_printerr("Message Cancelled\n");
+    id = e->window;
+    break;
+
+  default:
+    if (opcode == server.atom._NET_SYSTEM_TRAY_MESSAGE_DATA) {
+      //g_printerr("Text For Message From Dockapp:\n%s\n", e->data.b);
+      id = e->window;
+      break;
+    }
+
+    /* unknown message type. not in the spec. */
+    //g_printerr("Warning: Received unknown client message to System Tray selection window.\n");
+    break;
+  }
+}
+
+
+/*
+void event_loop()
+{
+  XEvent e;
+  Window cover;
+  GSList *it;
+
+  while (!exit_app) {
+    while (XPending(server.dsp)) {
+      XNextEvent(display, &e);
+
+      switch (e.type)
+      {
+      case PropertyNotify:
+        // systray window list has changed?
+        if (e.xproperty.atom == kde_systray_prop) {
+          XSelectInput(display, win, NoEventMask);
+          kde_update_icons();
+          XSelectInput(display, win, StructureNotifyMask);
+
+          while (XCheckTypedEvent(display, PropertyNotify, &e));
+        }
+
+        break;
+
+      case ConfigureNotify:
+        if (e.xany.window != win) {
+          // find the icon it pertains to and beat it into submission
+          GSList *it;
+
+          for (it = icons; it != NULL; it = g_slist_next(it)) {
+            TrayWindow *traywin = it->data;
+            if (traywin->id == e.xany.window) {
+              XMoveResizeWindow(display, traywin->id, traywin->x, traywin->y,
+                                icon_size, icon_size);
+              break;
+            }
+          }
+          break;
+        }
+
+        // briefly cover the entire containing window, which causes it and
+        // all of the icons to refresh their windows. finally, they update
+        // themselves when the background of the main window's parent changes.
+
+        cover = XCreateSimpleWindow(display, win, 0, 0,
+                                    border * 2 + width, border * 2 + height,
+                                    0, 0, 0);
+        XMapWindow(display, cover);
+        XDestroyWindow(display, cover);
+
+        break;
+
+      case ReparentNotify:
+        if (e.xany.window == win) // reparented to us
+          break;
+      case UnmapNotify:
+      case DestroyNotify:
+        for (it = icons; it; it = g_slist_next(it)) {
+          if (((TrayWindow*)it->data)->id == e.xany.window) {
+            icon_remove(it);
+            break;
+          }
+        }
+        break;
+
+      case ClientMessage:
+        if (e.xclient.message_type == net_opcode_atom &&
+            e.xclient.format == 32 &&
+            e.xclient.window == net_sel_win)
+          net_message(&e.xclient);
+
+      default:
+        break;
+      }
+    }
+    usleep(500000);
+  }
+
+  // remove/unparent all the icons
+  while (icons) {
+    // do the remove here explicitly, cuz the event handler isn't going to
+    // happen anymore.
+    icon_remove(icons);
+  }
+}
+*/
+
index 7fd8c9e5ef525ca6c737cdd749cadc165e4cf8e4..761bda6746dd02aeb2595c0d8fa82091041d9b02 100644 (file)
@@ -18,11 +18,15 @@ typedef struct {
 } Systraybar;
 
 
-// --------------------------------------------------
-// global taskbar parameter
-Area g_systraybar;
+typedef struct
+{
+  Window id;
+  int x, y;
+} TrayWindow;
 
 
+void init_systray(Systraybar *sysbar, Area *parent);
+
 // return 1 if task_width changed
 int resize_systray (Systraybar *sysbar);
 
index 475ebace4c7675bc5718f1f8a8efa4d84eaa3179..2cfbff55d56adc9d4675eeb8dd7501c8a46bb9c9 100644 (file)
@@ -8,7 +8,7 @@
 
 #ifndef TINT_MERGE_H
 #define TINT_MERGE_H
-
+/*
 #define display server.dsp
 #define root server.root_win
 
index 839f6f56d6dfd81b420b0718291adbd7c49f1991..2ec873f76778ed886ae1996d23bb0365ed1daebf 100644 (file)
@@ -91,6 +91,7 @@ void remove_task (Task *tsk)
    if (!tsk) return;
 
    Window win = tsk->win;
+   int desktop = tsk->desktop;
 
        // free title and icon just for the first task
        // even with task_on_all_desktop and with task_on_all_panel
@@ -106,8 +107,7 @@ void remove_task (Task *tsk)
    Taskbar *tskbar;
        for (i=0 ; i < nb_panel ; i++) {
                for (j=0 ; j < panel1[i].nb_desktop ; j++) {
-                       if (tsk->desktop != ALLDESKTOP && tsk->desktop != j) continue;
-                       //if (!panel1[i].taskbar) continue;
+                       if (desktop != ALLDESKTOP && desktop != j) continue;
 
                        GSList *l0;
                        tskbar = &panel1[i].taskbar[j];
index 204491243b67d364be100f63565b77dc7794b695..45d3b3d7844bdf020d5132311a66d9eb112a6c19 100644 (file)
@@ -43,12 +43,13 @@ void init_taskbar()
                panel = &panel1[i];
 
                // taskbar
+               panel->g_taskbar._resize = resize_taskbar;
                panel->g_taskbar.posy = panel->area.pix.border.width + panel->area.paddingy;
                panel->g_taskbar.height = panel->area.height - (2 * panel->g_taskbar.posy);
                panel->g_taskbar.redraw = 1;
 
                // task
-               panel->g_task.area.draw_foreground = draw_foreground_task;
+               panel->g_task.area._draw_foreground = draw_foreground_task;
                panel->g_task.area.posy = panel->g_taskbar.posy + panel->g_taskbar.pix.border.width + panel->g_taskbar.paddingy;
                panel->g_task.area.height = panel->area.height - (2 * panel->g_task.area.posy);
                panel->g_task.area.use_active = 1;
@@ -84,10 +85,7 @@ void init_taskbar()
                        memcpy(&tskbar->area, &panel->g_taskbar, sizeof(Area));
                        tskbar->desktop = j;
                }
-
-          resize_taskbar(panel);
        }
-
 }
 
 
@@ -115,8 +113,10 @@ void cleanup_taskbar()
 
        for (i=0 ; i < nb_panel ; i++) {
                panel = &panel1[i];
-               free(panel->taskbar);
-               panel->taskbar = 0;
+               if (panel->taskbar) {
+                       free(panel->taskbar);
+                       panel->taskbar = 0;
+               }
        }
 }
 
@@ -174,7 +174,7 @@ void task_refresh_tasklist ()
                                        if (tsk->win == win[k]) break;
                                }
                                // careful : remove_task change l0->next
-                               if (tsk->win != win[k]) remove_task (tsk);
+                               if (k == num_results) remove_task (tsk);
                        }
                }
    }
@@ -198,6 +198,7 @@ int resize_tasks (Taskbar *taskbar)
 
    // new task width for 'desktop'
    task_count = g_slist_length(taskbar->area.list);
+
    if (!task_count) pixel_width = panel->g_task.maximum_width;
    else {
       taskbar_width = taskbar->area.width - (2 * panel->g_taskbar.pix.border.width) - (2 * panel->g_taskbar.paddingxlr);
@@ -239,9 +240,9 @@ int resize_tasks (Taskbar *taskbar)
 
 
 // initialise taskbar posx and width
-void resize_taskbar(void *p)
+void resize_taskbar(void *obj)
 {
-   Panel *panel = p;
+   Panel *panel = ((Area*)obj)->panel;
    int taskbar_width, modulo_width, taskbar_on_screen;
 
    if (panel_mode == MULTI_DESKTOP) taskbar_on_screen = panel->nb_desktop;
@@ -250,6 +251,8 @@ void resize_taskbar(void *p)
    taskbar_width = panel->area.width - (2 * panel->area.paddingxlr) - (2 * panel->area.pix.border.width);
    if (time1_format)
       taskbar_width -= (panel->clock.area.width + panel->area.paddingx);
+   //taskbar_width -= (panel->trayer.area.width + panel->area.paddingx);
+
    taskbar_width = (taskbar_width - ((taskbar_on_screen-1) * panel->area.paddingx)) / taskbar_on_screen;
 
    if (taskbar_on_screen > 1)
@@ -272,6 +275,7 @@ void resize_taskbar(void *p)
          modulo--;
       }
 
+               set_redraw (&panel->taskbar[i].area);
       resize_tasks(&panel->taskbar[i]);
    }
 }
index 881eab2df33d97bc5f49f37be2cb1d6a2d6c22be..807e2856dd529c777358802aed331ad2c7dbc2b1 100644 (file)
@@ -34,7 +34,7 @@ void task_refresh_tasklist ();
 // return 1 if task_width changed
 int resize_tasks (Taskbar *tskbar);
 
-void resize_taskbar(void *panel);
+void resize_taskbar(void *obj);
 
 
 #endif
index 10ccb3638672d9b366994d9907cb726bb4ce71a8..b84ee185c1ee7879c2eed94d86935e5f905ebb23 100644 (file)
 #include "config.h"
 #include "task.h"
 #include "taskbar.h"
+#include "systraybar.h"
 #include "panel.h"
-#include "docker.h"
-#include "net.h"
-#include "kde.h"
 
 
 void signal_handler(int sig)
@@ -74,14 +72,6 @@ void init ()
 
    XSetErrorHandler ((XErrorHandler) server_catch_error);
 
-   // init systray
-   //display = server.dsp;
-   //root = RootWindow(display, DefaultScreen(display));
-   //create_main_window();
-   //kde_init();
-   //net_init();
-   //printf("ici 4\n");
-
    imlib_context_set_display (server.dsp);
    imlib_context_set_visual (server.visual);
    imlib_context_set_colormap (DefaultColormap (server.dsp, server.screen));
@@ -138,7 +128,7 @@ void event_button_press (XEvent *e)
 
    if (panel_mode != MULTI_DESKTOP) {
       // drag and drop disabled
-      //XLowerWindow (server.dsp, panel.main_win);
+      XLowerWindow (server.dsp, panel->main_win);
       return;
    }
 
@@ -163,7 +153,7 @@ void event_button_press (XEvent *e)
       }
    }
 
-   //XLowerWindow (server.dsp, panel.main_win);
+   XLowerWindow (server.dsp, panel->main_win);
 }
 
 
@@ -202,7 +192,7 @@ void event_button_release (XEvent *e)
    }
 
    // TODO: check better solution to keep window below
-   //XLowerWindow (server.dsp, panel.main_win);
+   XLowerWindow (server.dsp, panel->main_win);
    task_drag = 0;
    return;
 
@@ -238,7 +228,7 @@ suite:
    }
 
    // to keep window below
-   //XLowerWindow (server.dsp, panel.main_win);
+   XLowerWindow (server.dsp, panel->main_win);
 }
 
 
@@ -259,6 +249,9 @@ void event_property_notify (Window win, Atom at)
                        cleanup_taskbar();
                        init_taskbar();
                        visible_object();
+                       for (i=0 ; i < nb_panel ; i++) {
+                               set_resize(&panel1[i]);
+                       }
                        task_refresh_tasklist();
                        panel_refresh = 1;
       }
@@ -429,6 +422,7 @@ void event_timer()
        int i;
        for (i=0 ; i < nb_panel ; i++) {
           panel1[i].clock.area.redraw = 1;
+          panel1[i].clock.area.resize = 1;
        }
    panel_refresh = 1;
 }
@@ -459,11 +453,6 @@ load_config:
    }
    config_finish();
 
-   // BUG: refresh(clock) is needed here, but 'on the paper' it's not necessary.
-       for (i=0 ; i < nb_panel ; i++) {
-               refresh(&panel1[i].clock.area);
-       }
-
    x11_fd = ConnectionNumber(server.dsp);
    XSync(server.dsp, False);
 
@@ -500,7 +489,6 @@ load_config:
                   break;
 
                case PropertyNotify:
-                  //printf("PropertyNotify %lx\n", e.xproperty.window);
                   event_property_notify (e.xproperty.window, e.xproperty.atom);
                   break;
 
@@ -509,6 +497,25 @@ load_config:
                      goto load_config;
                   else
                      event_configure_notify (e.xconfigure.window);
+                  break;
+
+                                       case UnmapNotify:
+                                       case DestroyNotify:
+                                       /*
+                                               GSList *it;
+                                               for (it = icons; it; it = g_slist_next(it)) {
+                                                       if (((TrayWindow*)it->data)->id == e.xany.window) {
+                                                               icon_remove(it);
+                                                               break;
+                                                       }
+                                               }*/
+                                       break;
+
+                                       case ClientMessage:
+                                               break;
+                                               if (e.xclient.message_type == server.atom._NET_SYSTEM_TRAY_OPCODE && e.xclient.format == 32)
+                                               // &&   e.xclient.window == net_sel_win)
+                                                       net_message(&e.xclient);
                   break;
             }
          }
@@ -525,8 +532,10 @@ load_config:
       }
 
       if (panel_refresh) {
-                       for (i=0 ; i < nb_panel ; i++)
+                       for (i=0 ; i < nb_panel ; i++) {
                 visual_refresh(&panel1[i]);
+                       }
+
                        XFlush (server.dsp);
                        panel_refresh = 0;
                }
index e7365a76026be2f733848278b37193f616a8181f..91ceff45118cd99719456736918be3151c88fa91 100755 (executable)
Binary files a/src/tint2 and b/src/tint2 differ
index bd6cf95f282f0fdde5ee4efa9bc758e94d463cc6..7606679b3446a65914a3a6a0d83ed33e8d7a3903 100644 (file)
 
 void refresh (Area *a)
 {
+   if (a->resize) {
+       // resize can generate a redraw
+          if (a->_resize)
+       a->_resize(a);
+      a->resize = 0;
+       }
+
    if (a->redraw) {
       //printf("draw pix\n");
       draw(a, 0);
@@ -61,6 +68,16 @@ void set_redraw (Area *a)
 }
 
 
+void set_resize (Area *a)
+{
+   a->resize = 1;
+
+   GSList *l;
+   for (l = a->list ; l ; l = l->next)
+      set_resize(l->data);
+}
+
+
 void draw (Area *a, int active)
 {
    Pixmap *pmap = (active == 0) ? (&a->pix.pmap) : (&a->pix_active.pmap);
@@ -80,8 +97,8 @@ void draw (Area *a, int active)
 
    draw_background (a, c, active);
 
-   if (a->draw_foreground)
-      a->draw_foreground(a, c, active);
+   if (a->_draw_foreground)
+      a->_draw_foreground(a, c, active);
 
    cairo_destroy (c);
    cairo_surface_destroy (cs);
@@ -180,6 +197,8 @@ void free_area (Area *a)
       g_slist_free(a->list);
       a->list = 0;
    }
+   if (a->pix.pmap) XFreePixmap (server.dsp, a->pix.pmap);
+   if (a->pix_active.pmap) XFreePixmap (server.dsp, a->pix_active.pmap);
 }
 
 
index b03b2d1f1a2f857b16aef487d7b3d03b70ff0dd3..708c03333a9193749dc0f4d5860644e075ed0646 100644 (file)
@@ -60,8 +60,6 @@ typedef struct
 } Pmap;
 
 
-// TODO: isoler 'draw' de 'refresh'
-// TODO: isoler les donnĂ©es locales des donnĂ©es communes aux freres
 typedef struct {
    // absolute coordinate in panel
    int posx, posy;
@@ -72,6 +70,8 @@ typedef struct {
    // list of child : Area object
    GSList *list;
 
+       // need compute position and width
+       int resize;
    // need redraw Pixmap
    int redraw;
    int use_active, is_active;
@@ -84,9 +84,10 @@ typedef struct {
    void *panel;
 
    // each object can overwrite following function
-   void (*draw_foreground)(void *obj, cairo_t *c, int active);
-   void (*add_child)(void *obj);
-   int (*remove_child)(void *obj);
+   void (*_draw_foreground)(void *obj, cairo_t *c, int active);
+   void (*_resize)(void *obj);
+   void (*_add_child)(void *obj);
+   int (*_remove_child)(void *obj);
 } Area;
 
 
This page took 0.06107 seconds and 4 git commands to generate.