X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/77d95881e7050083d85a67c0942c046f24977adc..8f763f1bcd527d7f2bb27a3bf3677571c8ffd2d9:/disobedience/choose.c diff --git a/disobedience/choose.c b/disobedience/choose.c index af811f0..cc9f3ac 100644 --- a/disobedience/choose.c +++ b/disobedience/choose.c @@ -1,6 +1,6 @@ /* * This file is part of DisOrder - * Copyright (C) 2006 Richard Kettlewell + * Copyright (C) 2006, 2007 Richard Kettlewell * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,6 +22,18 @@ /* 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. */ @@ -36,7 +48,8 @@ struct displaydata { }; /* instantiate the node vector type */ -VECTOR_TYPE(nodevector, struct choosenode *, xrealloc) + +VECTOR_TYPE(nodevector, struct choosenode *, xrealloc); struct choosenode { struct choosenode *parent; /* parent node */ @@ -125,17 +138,24 @@ static void clicked_choosenode(GtkWidget attribute((unused)) *widget, gpointer user_data); static void activate_play(GtkMenuItem *menuitem, gpointer user_data); +#if 0 static void activate_remove(GtkMenuItem *menuitem, gpointer user_data); +#endif static void activate_properties(GtkMenuItem *menuitem, gpointer user_data); static gboolean sensitive_play(struct choosenode *cn); +#if 0 static gboolean sensitive_remove(struct choosenode *cn); +#endif static gboolean sensitive_properties(struct choosenode *cn); static struct menuitem menuitems[] = { - { "Play", activate_play, sensitive_play, 0, 0 }, + { "Play track", activate_play, sensitive_play, 0, 0 }, +#if 0 + /* Not implemented yet */ { "Remove", activate_remove, sensitive_remove, 0, 0 }, - { "Properties", activate_properties, sensitive_properties, 0, 0 }, +#endif + { "Track properties", activate_properties, sensitive_properties, 0, 0 }, }; #define NMENUITEMS (int)(sizeof menuitems / sizeof *menuitems) @@ -197,6 +217,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 +253,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; } @@ -298,7 +338,7 @@ static void fill_directory_node(struct choosenode *cn) { /* TODO: de-dupe against fill_letter_node */ assert(report_label != 0); gtk_label_set_text(GTK_LABEL(report_label), "getting files"); - cn->children.nvec = 0; + clear_children(cn); cbd = xmalloc(sizeof *cbd); cbd->u.choosenode = cn; disorder_eclient_dirs(client, got_dirs, cn->path, 0, cbd); @@ -330,6 +370,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(); } @@ -389,7 +434,7 @@ static void fill_search_node(struct choosenode *cn) { D(("fill_search_node %s", cn->path)); /* We depend on the search results being sorted as by compare_path(). */ - cn->children.nvec = 0; + clear_children(cn); for(n = 0; n < nsearchresults; ++n) { /* We only care about descendants of CN */ if(!is_descendant(cn->path, searchresults[n])) @@ -535,10 +580,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); @@ -555,7 +597,9 @@ static void redisplay_tree(void) { files_selected = 0; files_visible = 0; /* Correct the layout and find out how much space it uses */ + MTAG_PUSH("display_tree"); d = display_tree(root, 0, 0); + MTAG_POP(); /* We must set the total size or scrolling will not work (it wouldn't be hard * for GtkLayout to figure it out for itself but presumably you're supposed * to be able to have widgets off the edge of the layuot.) @@ -589,24 +633,35 @@ static struct displaydata display_tree(struct choosenode *cn, int x, int y) { * A non-expandable item has just a text label and no arrow. */ if(!cn->container) { + MTAG_PUSH("make_widgets_1"); /* 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); + } } + MTAG_POP(); + MTAG_PUSH("make_widgets_2"); + 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); + MTAG_POP(); + MTAG_PUSH("make_widgets_3"); + 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", @@ -618,6 +673,7 @@ static struct displaydata display_tree(struct choosenode *cn, int x, int y) { gtk_widget_set_name(cn->container, "choose"); /* Show everything by default */ gtk_widget_show_all(cn->container); + MTAG_POP(); } assert(cn->container); /* Make sure the icon is right */ @@ -635,6 +691,8 @@ static struct displaydata display_tree(struct choosenode *cn, int x, int y) { else { gtk_layout_put(GTK_LAYOUT(chooselayout), cn->container, x, y); cn->flags |= CN_DISPLAYED; + /* Now chooselayout has a ref to the container */ + g_object_unref(cn->container); } /* Set the widget's selection status */ if(!(cn->flags & CN_EXPANDABLE)) @@ -863,10 +921,12 @@ static void activate_play(GtkMenuItem attribute((unused)) *menuitem, disorder_eclient_play(client, tracks[n], 0, 0); } +#if 0 static void activate_remove(GtkMenuItem attribute((unused)) *menuitem, gpointer attribute((unused)) user_data) { /* TODO remove all selected tracks */ } +#endif static void activate_properties(GtkMenuItem attribute((unused)) *menuitem, gpointer attribute((unused)) user_data) { @@ -877,21 +937,24 @@ static void activate_properties(GtkMenuItem attribute((unused)) *menuitem, } static gboolean sensitive_play(struct choosenode attribute((unused)) *cn) { - return !!files_selected; + return (!!files_selected + && (disorder_eclient_state(client) & DISORDER_CONNECTED)); } +#if 0 static gboolean sensitive_remove(struct choosenode attribute((unused)) *cn) { return FALSE; /* not implemented yet */ } +#endif static gboolean sensitive_properties(struct choosenode attribute((unused)) *cn) { - return !!files_selected; + return !!files_selected && (disorder_eclient_state(client) & DISORDER_CONNECTED); } /* Main menu plumbing ------------------------------------------------------ */ static int choose_properties_sensitive(GtkWidget attribute((unused)) *w) { - return !!files_selected; + return !!files_selected && (disorder_eclient_state(client) & DISORDER_CONNECTED); } static int choose_selectall_sensitive(GtkWidget attribute((unused)) *w) { @@ -942,15 +1005,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 +1025,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*/, "", "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 +1044,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*/);