chiark / gitweb /
,commit
authorrjk@greenend.org.uk <>
Sun, 29 Jul 2007 14:36:28 +0000 (15:36 +0100)
committerrjk@greenend.org.uk <>
Sun, 29 Jul 2007 14:36:28 +0000 (15:36 +0100)
.bzrignore
disobedience/choose.c
disobedience/control.c
disobedience/disobedience.h
disobedience/misc.c
disobedience/queue.c

index 6a51fabafbc171cfda2be29063e3f79031dfc07a..bffd38553ff6c441483978c68d33cfd5f02cf3d7 100644 (file)
@@ -89,3 +89,4 @@ doc/disorder-speaker.8.html
 core
 vgcore.*
 *.tar.gz
+TAGS
index 71256c8e19f1cff7363f3b9b93e0a1be013631b9..cf22eafa948429a97b71221466d8b6f08b255274 100644 (file)
 
 /* Choose track ------------------------------------------------------------ */
 
+WT(label);
+WT(event_box);
+WT(menu);
+WT(menu_item);
+WT(layout);
+WT(vbox);
+WT(arrow);
+WT(hbox);
+WT(button);
+WT(image);
+WT(entry);
+
 /* We don't use the built-in tree widgets because they require that you know
  * the children of a node on demand, and we have to wait for the server to tell
  * us. */
@@ -197,6 +209,34 @@ static void fill_root_node(struct choosenode *cn) {
   }
 }
 
+static void delete_cn_widgets(struct choosenode *cn) {
+  if(cn->arrow) {
+    DW(arrow);
+    gtk_widget_destroy(cn->arrow);
+    cn->arrow = 0;
+  }
+  if(cn->label) {
+    DW(label);
+    gtk_widget_destroy(cn->label);
+    cn->label = 0;
+  }
+  if(cn->marker) {
+    DW(image);
+    gtk_widget_destroy(cn->marker);
+    cn->marker = 0;
+  }
+  if(cn->hbox) {
+    DW(hbox);
+    gtk_widget_destroy(cn->hbox);
+    cn->hbox = 0;
+  }
+  if(cn->container) {
+    DW(event_box);
+    gtk_widget_destroy(cn->container);
+    cn->container = 0;
+  }
+}
+
 /* Clear all the children of CN */
 static void clear_children(struct choosenode *cn) {
   int n;
@@ -205,15 +245,7 @@ static void clear_children(struct choosenode *cn) {
   /* Recursively clear subtrees */
   for(n = 0; n < cn->children.nvec; ++n) {
     clear_children(cn->children.vec[n]);
-    if(cn->children.vec[n]->container) {
-      if(cn->children.vec[n]->arrow)
-        gtk_widget_destroy(cn->children.vec[n]->arrow);
-      gtk_widget_destroy(cn->children.vec[n]->label);
-      if(cn->children.vec[n]->marker)
-        gtk_widget_destroy(cn->children.vec[n]->marker);
-      gtk_widget_destroy(cn->children.vec[n]->hbox);
-      gtk_widget_destroy(cn->children.vec[n]->container);
-    }
+    delete_cn_widgets(cn->children.vec[n]);
   }
   cn->children.nvec = 0;
 }
@@ -330,6 +362,11 @@ static void contract_node(struct choosenode *cn) {
   cn->flags &= ~CN_EXPANDED;
   /* Clear selection below this node */
   clear_selection(cn);
+  /* Zot children.  We never used to do this but the result would be that over
+   * time you'd end up with the entire tree pulled into memory.  If the server
+   * is over a slow network it will make interactivity slightly worse; if
+   * anyone complains we can make it an option. */
+  clear_children(cn);
   /* We can contract a node immediately. */
   redisplay_tree();
 }
@@ -535,10 +572,7 @@ static void clearsearch_clicked(GtkButton attribute((unused)) *button,
 static void delete_widgets(struct choosenode *cn) {
   int n;
 
-  if(cn->container) {
-    gtk_widget_destroy(cn->container);
-    cn->container = 0;
-  }
+  delete_cn_widgets(cn);
   for(n = 0; n < cn->children.nvec; ++n)
     delete_widgets(cn->children.vec[n]);
   cn->flags &= ~(CN_DISPLAYED|CN_SELECTED);
@@ -590,23 +624,29 @@ static struct displaydata display_tree(struct choosenode *cn, int x, int y) {
    */
   if(!cn->container) {
     /* Widgets need to be created */
+    NW(hbox);
     cn->hbox = gtk_hbox_new(FALSE, 1);
     if(cn->flags & CN_EXPANDABLE) {
+      NW(arrow);
       cn->arrow = gtk_arrow_new(cn->flags & CN_EXPANDED ? GTK_ARROW_DOWN
                                                         : GTK_ARROW_RIGHT,
                                 GTK_SHADOW_NONE);
       cn->marker = 0;
     } else {
       cn->arrow = 0;
-      if((pb = find_image("notes.png")))
+      if((pb = find_image("notes.png"))) {
+        NW(image);
         cn->marker = gtk_image_new_from_pixbuf(pb);
+      }
     }
+    NW(label);
     cn->label = gtk_label_new(cn->display);
     if(cn->arrow)
       gtk_container_add(GTK_CONTAINER(cn->hbox), cn->arrow);
     gtk_container_add(GTK_CONTAINER(cn->hbox), cn->label);
     if(cn->marker)
       gtk_container_add(GTK_CONTAINER(cn->hbox), cn->marker);
+    NW(event_box);
     cn->container = gtk_event_box_new();
     gtk_container_add(GTK_CONTAINER(cn->container), cn->hbox);
     g_signal_connect(cn->container, "button-release-event", 
@@ -942,15 +982,18 @@ GtkWidget *choose_widget(void) {
    */
   
   /* Text entry box for search terms */
+  NW(entry);
   searchentry = gtk_entry_new();
   g_signal_connect(searchentry, "changed", G_CALLBACK(searchentry_changed), 0);
 
   /* Cancel button to clear the search */
+  NW(button);
   clearsearch = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
   g_signal_connect(G_OBJECT(clearsearch), "clicked",
                    G_CALLBACK(clearsearch_clicked), 0);
 
   /* hbox packs the search box and the cancel button together on a line */
+  NW(hbox);
   hbox = gtk_hbox_new(FALSE/*homogeneous*/, 1/*spacing*/);
   gtk_box_pack_start(GTK_BOX(hbox), searchentry,
                      TRUE/*expand*/, TRUE/*fill*/, 0/*padding*/);
@@ -959,15 +1002,18 @@ GtkWidget *choose_widget(void) {
   
   /* chooselayout contains the currently visible subset of the track
    * namespace */
+  NW(layout);
   chooselayout = gtk_layout_new(0, 0);
   root = newnode(0/*parent*/, "<root>", "All files", "",
                  CN_EXPANDABLE, fill_root_node);
   realroot = root;
   expand_node(root);                    /* will call redisplay_tree */
   /* Create the popup menu */
+  NW(menu);
   menu = gtk_menu_new();
   g_signal_connect(menu, "destroy", G_CALLBACK(gtk_widget_destroyed), &menu);
   for(n = 0; n < NMENUITEMS; ++n) {
+    NW(menu_item);
     menuitems[n].w = gtk_menu_item_new_with_label(menuitems[n].name);
     gtk_menu_attach(GTK_MENU(menu), menuitems[n].w, 0, 1, n, n + 1);
   }
@@ -975,6 +1021,7 @@ GtkWidget *choose_widget(void) {
   scrolled = scroll_widget(GTK_WIDGET(chooselayout), "choose");
 
   /* The scrollable layout and the search hbox go together in a vbox */
+  NW(vbox);
   vbox = gtk_vbox_new(FALSE/*homogenous*/, 1/*spacing*/);
   gtk_box_pack_start(GTK_BOX(vbox), hbox,
                      FALSE/*expand*/, FALSE/*fill*/, 0/*padding*/);
index 3b6f2acf612fb9cf85b7792c8a2b44c6ed8badd5..5e2e0d32c8828992b3846b9b13c9706805be321a 100644 (file)
 
 #include "disobedience.h"
 
-/* Forward declartions ----------------------------------------------------- */
+/* Forward declarations ---------------------------------------------------- */
+
+WT(adjustment);
+WT(hscale);
+WT(hbox);
+WT(tooltips);
+WT(button);
+WT(image);
+WT(label);
+WT(vbox);
 
 struct icon;
 
@@ -77,7 +86,7 @@ static struct icon {
 GtkAdjustment *volume_adj, *balance_adj;
 
 /* Create the control bar */
- GtkWidget *control_widget(void) {
+GtkWidget *control_widget(void) {
   GtkWidget *hbox = gtk_hbox_new(FALSE, 1), *vbox;
   GtkWidget *content;
   GdkPixbuf *pb;
@@ -85,31 +94,42 @@ GtkAdjustment *volume_adj, *balance_adj;
   GtkTooltips *tips = gtk_tooltips_new();
   int n;
 
+  NW(hbox);
+  NW(tooltips);
   D(("control_widget"));
   for(n = 0; n < NICONS; ++n) {
+    NW(button);
     icons[n].button = gtk_button_new();
-    if((pb = find_image(icons[n].icon)))
+    if((pb = find_image(icons[n].icon))) {
+      NW(image);
       content = gtk_image_new_from_pixbuf(pb);
-    else
+    } else {
+      NW(label);
       content = gtk_label_new(icons[n].icon);
+    }
     gtk_container_add(GTK_CONTAINER(icons[n].button), content);
     gtk_tooltips_set_tip(tips, icons[n].button, icons[n].tip, "");
     g_signal_connect(G_OBJECT(icons[n].button), "clicked",
                      G_CALLBACK(icons[n].clicked), &icons[n]);
     /* pop the icon in a vbox so it doesn't get vertically stretch if there are
      * taller things in the control bar */
+    NW(vbox);
     vbox = gtk_vbox_new(FALSE, 0);
     gtk_box_pack_start(GTK_BOX(vbox), icons[n].button, TRUE, FALSE, 0);
     gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
   }
   /* create the adjustments for the volume control */
+  NW(adjustment);
   volume_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, 0, goesupto,
                                                  goesupto / 20, goesupto / 20,
                                                  0));
+  NW(adjustment);
   balance_adj = GTK_ADJUSTMENT(gtk_adjustment_new(0, -1, 1,
                                                   0.2, 0.2, 0));
   /* the volume control */
+  NW(hscale);
   v = gtk_hscale_new(volume_adj);
+  NW(hscale);
   b = gtk_hscale_new(balance_adj);
   gtk_scale_set_digits(GTK_SCALE(v), 10);
   gtk_scale_set_digits(GTK_SCALE(b), 10);
index 59cc6e9b22c8678d7acc0e21f59397e9ce6d59b9..3998124d1b044a227064e7f9c99d8ad837797d18 100644 (file)
@@ -167,6 +167,24 @@ GtkWidget *choose_widget(void);
 void choose_update(void);
 /* Called when we think the choose tree might need updating */
 
+/* Widget leakage debugging rubbish ---------------------------------------- */
+
+#if MDEBUG
+#define NW(what) do {                                   \
+  if(++current##what % 100 > max##what) {               \
+    fprintf(stderr, "%s:%d: %d %s\n",                   \
+            __FILE__, __LINE__, current##what, #what);  \
+    max##what = current##what;                          \
+  }                                                     \
+} while(0)
+#define WT(what) static int current##what, max##what
+#define DW(what) (--current##what)
+#else
+#define NW(what) (0)
+#define DW(what) (0)
+#define WT(what) struct neverused
+#endif
+
 #endif /* DISOBEDIENCE_H */
 
 /*
index 4ce3a430e5033b7ee8ca3d9fc2d2f04b957db135..d0caf3b8a5676b1f1b96caec09870a67a6b9a293 100644 (file)
@@ -22,6 +22,8 @@
 
 /* Miscellaneous GTK+ stuff ------------------------------------------------ */
 
+WT(cached_image);
+
 /* Functions */
 
 GtkWidget *scroll_widget(GtkWidget *child,
@@ -68,6 +70,7 @@ GdkPixbuf *find_image(const char *name) {
       error(0, "%s", err->message);
       return 0;
     }
+    NW(cached_image);
     cache_put(&image_cache_type, name,  pb);
   }
   return pb;
index 16cbc4c8a6186708bf5024924b12a3a3d63839b6..f9b9ec8af98d5a8398eb3960a71bb060a55c92ee 100644 (file)
 
 /* Queue management -------------------------------------------------------- */
 
+WT(label);
+WT(event_box);
+WT(menu);
+WT(menu_item);
+WT(layout);
+WT(vbox);
+
 struct queuelike;
 
 static void add_drag_targets(struct queuelike *ql);
@@ -361,6 +368,7 @@ static GtkWidget *column_when(const struct queuelike attribute((unused)) *ql,
     strftime(when, sizeof when, "%H:%M", localtime_r(&t, &tm));
   else
     when[0] = 0;
+  NW(label);
   return gtk_label_new(when);
 }
 
@@ -369,6 +377,7 @@ static GtkWidget *column_who(const struct queuelike attribute((unused)) *ql,
                              const struct queue_entry *q,
                              const char attribute((unused)) *data) {
   D(("column_who"));
+  NW(label);
   return gtk_label_new(q->submitter ? q->submitter : "");
 }
 
@@ -378,6 +387,7 @@ static GtkWidget *column_namepart(const struct queuelike
                                   const struct queue_entry *q,
                                   const char *data) {
   D(("column_namepart"));
+  NW(label);
   return gtk_label_new(namepart(q->track, "display", data));
 }
 
@@ -417,13 +427,17 @@ static GtkWidget *column_length(const struct queuelike attribute((unused)) *ql,
   D(("column_length"));
   if(q == playing_track) {
     assert(!playing_length_label);
+    NW(label);
     playing_length_label = gtk_label_new(text_length(q));
     /* Zot playing_length_label when it is destroyed */
     g_signal_connect(playing_length_label, "destroy",
                      G_CALLBACK(gtk_widget_destroyed), &playing_length_label);
     return playing_length_label;
-  } else
+  } else {
+    NW(label);
     return gtk_label_new(text_length(q));
+  }
+  
 }
 
 /* Apply a new queue contents, transferring the selection from the old value */
@@ -462,6 +476,7 @@ static GtkWidget *wrap_queue_cell(GtkWidget *label,
    * background */
   gtk_misc_set_padding(GTK_MISC(label), HCELLPADDING, VCELLPADDING);
   /* Event box is just to hold a background color */
+  NW(event_box);
   bg = gtk_event_box_new();
   gtk_container_add(GTK_CONTAINER(bg), label);
   if(wp) {
@@ -492,6 +507,7 @@ static GtkWidget *get_queue_cell(struct queuelike *ql,
 /* Add a padding cell to the end of a row */
 static GtkWidget *get_padding_cell(const char *name) {
   D(("get_padding_cell"));
+  NW(label);
   return wrap_queue_cell(gtk_label_new(""), name, 0);
 }
 
@@ -729,6 +745,7 @@ static gboolean queue_drag_motion(GtkWidget attribute((unused)) *widget,
 
   if(!id || q) {
     if(!ql->dragmark) {
+      NW(event_box);
       ql->dragmark = gtk_event_box_new();
       g_signal_connect(ql->dragmark, "destroy",
                        G_CALLBACK(gtk_widget_destroyed), &ql->dragmark);
@@ -765,6 +782,7 @@ static void add_drag_target(struct queuelike *ql, int y, int row,
   GtkWidget *eventbox;
 
   assert(ql->dropzones[row] == 0);
+  NW(event_box);
   eventbox = gtk_event_box_new();
   /* Make the target zone invisible */
   gtk_event_box_set_visible_window(GTK_EVENT_BOX(eventbox), FALSE);
@@ -818,6 +836,7 @@ static void remove_drag_targets(struct queuelike *ql) {
 
   for(row = 0; row < ql->nrows; ++row) {
     if(ql->dropzones[row]) {
+      DW(event_box);
       gtk_widget_destroy(ql->dropzones[row]);
     }
     assert(ql->dropzones[row] == 0);
@@ -843,8 +862,11 @@ static void redisplay_queue(struct queuelike *ql) {
       c;
       c = c->next) {
     /* Destroy both the label and the eventbox */
-    if(GTK_BIN(c->data)->child)
+    if(GTK_BIN(c->data)->child) {
+      DW(label);
       gtk_widget_destroy(GTK_BIN(c->data)->child);
+    }
+    DW(event_box);
     gtk_widget_destroy(GTK_WIDGET(c->data));
   }
   /* Adjust the row count */
@@ -992,7 +1014,9 @@ static GtkWidget *queuelike(struct queuelike *ql,
   ql->mainrowheight = !0;                /* else division by 0 */
   ql->selection = selection_new();
   /* Create the layouts */
+  NW(layout);
   ql->mainlayout = gtk_layout_new(0, 0);
+  NW(layout);
   ql->titlelayout = gtk_layout_new(0, 0);
   /* Scroll the layouts */
   ql->mainscroll = mainscroll = scroll_widget(ql->mainlayout, name);
@@ -1005,6 +1029,7 @@ static GtkWidget *queuelike(struct queuelike *ql,
   g_signal_connect(mainadj, "value-changed", G_CALLBACK(queue_scrolled), titleadj);
   /* Fill the titles and put them anywhere */
   for(col = 0; col < NCOLUMNS; ++col) {
+    NW(label);
     label = gtk_label_new(columns[col].name);
     gtk_misc_set_alignment(GTK_MISC(label), columns[col].xalign, 0);
     ql->titlecells[col] = wrap_queue_cell(label, "row-title", 0);
@@ -1013,14 +1038,17 @@ static GtkWidget *queuelike(struct queuelike *ql,
   ql->titlecells[col] = get_padding_cell("row-title");
   gtk_layout_put(GTK_LAYOUT(ql->titlelayout), ql->titlecells[col], 0, 0);
   /* Pack the lot together in a vbox */
+  NW(vbox);
   vbox = gtk_vbox_new(0, 0);
   gtk_box_pack_start(GTK_BOX(vbox), titlescroll, 0, 0, 0);
   gtk_box_pack_start(GTK_BOX(vbox), mainscroll, 1, 1, 0);
   /* Create the popup menu */
+  NW(menu);
   ql->menu = gtk_menu_new();
   g_signal_connect(ql->menu, "destroy",
                    G_CALLBACK(gtk_widget_destroyed), &ql->menu);
   for(n = 0; menuitems[n].name; ++n) {
+    NW(menu_item);
     menuitems[n].w = gtk_menu_item_new_with_label(menuitems[n].name);
     gtk_menu_attach(GTK_MENU(ql->menu), menuitems[n].w, 0, 1, n, n + 1);
   }