- D(("display_tree %s %d,%d", cn->path, x, y));
-
- /* An expandable item contains an arrow and a text label. When you press the
- * button it flips its expand state.
- *
- * A non-expandable item has just a text label and no arrow.
- */
- if(!cn->container) {
- BEGIN(new_widgets);
- 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"))) {
- 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",
- G_CALLBACK(clicked_choosenode), cn);
- g_signal_connect(cn->container, "button-press-event",
- G_CALLBACK(clicked_choosenode), cn);
- g_object_ref(cn->container);
- /* Show everything by default */
- gtk_widget_show_all(cn->container);
- MTAG_POP();
- END(new_widgets);
- }
- assert(cn->container);
- /* Set colors */
- BEGIN(colors);
- /* This section turns out to reliably take >50% of the elapsed time when
- * displaying the tree, both when it's largely unexpanded and when it's
- * heavily expanded. */
- if(search_result)
- gtk_widget_modify_bg(cn->container, GTK_STATE_NORMAL, &search_bg);
- else
- gtk_widget_modify_bg(cn->container, GTK_STATE_NORMAL, &layout_bg);
- gtk_widget_modify_bg(cn->container, GTK_STATE_SELECTED, &selected_bg);
- gtk_widget_modify_bg(cn->container, GTK_STATE_PRELIGHT, &selected_bg);
- gtk_widget_modify_fg(cn->label, GTK_STATE_NORMAL, &item_fg);
- gtk_widget_modify_fg(cn->label, GTK_STATE_SELECTED, &selected_fg);
- gtk_widget_modify_fg(cn->label, GTK_STATE_PRELIGHT, &selected_fg);
- END(colors);
- /* Make sure the icon is right */
- BEGIN(markers);
- if(cn->flags & CN_EXPANDABLE)
- gtk_arrow_set(GTK_ARROW(cn->arrow),
- cn->flags & CN_EXPANDED ? GTK_ARROW_DOWN : GTK_ARROW_RIGHT,
- GTK_SHADOW_NONE);
- else if(cn->marker)
- /* Make sure the queued marker is right */
- /* TODO: doesn't always work */
- (queued(cn->path) ? gtk_widget_show : gtk_widget_hide)(cn->marker);
- END(markers);
- /* Put the widget in the right place */
- BEGIN(location);
- if(cn->flags & CN_DISPLAYED)
- gtk_layout_move(GTK_LAYOUT(chooselayout), cn->container, x, 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);
- }
- END(location);
- /* Set the widget's selection status */
- BEGIN(selection);
- if(!(cn->flags & CN_EXPANDABLE))
- display_selection(cn);
- END(selection);
- /* Find the size used so we can get vertical positioning right. */
- gtk_widget_size_request(cn->container, &req);
- d.width = x + req.width;
- d.height = y + req.height;
- cn->ymin = y;
- cn->ymax = d.height;
- if(cn->flags & CN_EXPANDED) {
- /* We'll offset children by the size of the arrow whatever it might be. */
- assert(cn->arrow);
- gtk_widget_size_request(cn->arrow, &req);
- aw = req.width;
- for(n = 0; n < cn->children.nvec; ++n) {
- cd = display_tree(cn->children.vec[n], x + aw, d.height);
- if(cd.width > d.width)
- d.width = cd.width;
- d.height = cd.height;
- }
- } else {
- BEGIN(undisplay);
- for(n = 0; n < cn->children.nvec; ++n)
- undisplay_tree(cn->children.vec[n]);
- END(undisplay);
- }
- if(!(cn->flags & CN_EXPANDABLE)) {
- ++files_visible;
- if(cn->flags & CN_SELECTED)
- ++files_selected;
- }
- /* update the search results array */
- if(search_result)
- searchnodes[search_result - 1] = cn;
- /* report back how much space we used */
- D(("display_tree %s %d,%d total size %dx%d", cn->path, x, y,
- d.width, d.height));
- return d;
-}
-
-/** @brief Remove widgets for newly hidden nodes */
-static void undisplay_tree(struct choosenode *cn) {
- int n;
-
- D(("undisplay_tree %s", cn->path));
- /* Remove this widget from the display */
- if(cn->flags & CN_DISPLAYED) {
- gtk_container_remove(GTK_CONTAINER(chooselayout), cn->container);
- cn->flags ^= CN_DISPLAYED;
- }
- /* Remove children too */
- for(n = 0; n < cn->children.nvec; ++n)
- undisplay_tree(cn->children.vec[n]);
-}
-
-/* Selection --------------------------------------------------------------- */
-
-/** @brief Mark the widget @p cn according to its selection state */
-static void display_selection(struct choosenode *cn) {
- /* Need foreground and background colors */
- gtk_widget_set_state(cn->label, (cn->flags & CN_SELECTED
- ? GTK_STATE_SELECTED : GTK_STATE_NORMAL));
- gtk_widget_set_state(cn->container, (cn->flags & CN_SELECTED
- ? GTK_STATE_SELECTED : GTK_STATE_NORMAL));
-}
-
-/** @brief Set the selection state of a widget
- *
- * Directories can never be selected, we just ignore attempts to do so. */
-static void set_selection(struct choosenode *cn, int selected) {
- unsigned f = selected ? CN_SELECTED : 0;
-
- D(("set_selection %d %s", selected, cn->path));
- if(!(cn->flags & CN_EXPANDABLE) && (cn->flags & CN_SELECTED) != f) {
- cn->flags ^= CN_SELECTED;
- /* Maintain selection count */
- if(selected)
- ++files_selected;
- else
- --files_selected;
- display_selection(cn);
- /* Update main menu sensitivity */
- menu_update(-1);
- }
-}
-
-/** @brief Recursively clear all selection bits from CN down */
-static void clear_selection(struct choosenode *cn) {
- int n;
-
- set_selection(cn, 0);
- for(n = 0; n < cn->children.nvec; ++n)
- clear_selection(cn->children.vec[n]);