gboolean i7_node_in_thread(I7Node *self, I7Node *endnode) { return (endnode == self) || g_node_is_ancestor(self->gnode, endnode->gnode); }
/* This keeps the directory tree and the map geometry in sync * (expansion state vs. "deployment" value) */ void colexp( GNode *dnode, ColExpMesg mesg ) { static double colexp_time; static int depth = 0; static int max_depth; GNode *node; double wait_time; double pan_time; int wait_count = 0; boolean curnode_is_ancestor, curnode_is_descendant, curnode_is_equal; g_assert( NODE_IS_DIR(dnode) ); if (depth == 0) { #ifdef DEBUG if (mesg != COLEXP_EXPAND_ANY) { /* All ancestor directories must be expanded */ node = dnode->parent; while (NODE_IS_DIR(node)) { g_assert( DIR_NODE_DESC(node)->deployment > (1.0 - EPSILON) ); node = node->parent; } } #endif /* Update ctree and determine maximum recursion depth */ switch (mesg) { case COLEXP_COLLAPSE_RECURSIVE: dirtree_entry_collapse_recursive( dnode ); max_depth = max_expanded_depth( dnode ); break; case COLEXP_EXPAND: dirtree_entry_expand( dnode ); max_depth = 0; break; case COLEXP_EXPAND_ANY: dirtree_entry_expand( dnode ); max_depth = collapsed_depth( dnode ); break; case COLEXP_EXPAND_RECURSIVE: dirtree_entry_expand_recursive( dnode ); /* max_depth will be used as a high-water mark */ max_depth = 0; break; SWITCH_FAIL } /* Make file list appropriately (in)accessible */ filelist_reset_access( ); gui_update( ); /* Collapse/expand time for current visualization mode */ switch (globals.fsv_mode) { case FSV_DISCV: colexp_time = DISCV_COLEXP_TIME; break; case FSV_MAPV: colexp_time = MAPV_COLEXP_TIME; break; case FSV_TREEV: colexp_time = TREEV_COLEXP_TIME; break; SWITCH_FAIL } } morph_break( &DIR_NODE_DESC(dnode)->deployment ); /* Determine time to wait before collapsing/expanding directory */ switch (mesg) { case COLEXP_COLLAPSE_RECURSIVE: wait_count = max_depth - depth; break; case COLEXP_EXPAND_RECURSIVE: case COLEXP_EXPAND: wait_count = depth; break; case COLEXP_EXPAND_ANY: wait_count = max_depth - depth; break; SWITCH_FAIL } if (wait_count > 0) { wait_time = (double)wait_count * colexp_time; morph( &DIR_NODE_DESC(dnode)->deployment, MORPH_LINEAR, DIR_NODE_DESC(dnode)->deployment, wait_time ); } /* Initiate collapse/expand */ switch (mesg) { case COLEXP_COLLAPSE_RECURSIVE: morph_full( &DIR_NODE_DESC(dnode)->deployment, MORPH_QUADRATIC, 0.0, colexp_time, colexp_progress_cb, colexp_progress_cb, dnode ); break; case COLEXP_EXPAND: case COLEXP_EXPAND_ANY: case COLEXP_EXPAND_RECURSIVE: morph_full( &DIR_NODE_DESC(dnode)->deployment, MORPH_INV_QUADRATIC, 1.0, colexp_time, colexp_progress_cb, colexp_progress_cb, dnode ); break; SWITCH_FAIL } /* Recursion */ /* geometry_colexp_initiated( ) is called at differing points below * because (at least in TreeV mode) notification must always * proceed from parent to children, and not the other way around */ switch (mesg) { case COLEXP_EXPAND: /* Initial collapse/expand notify */ geometry_colexp_initiated( dnode ); /* EXPAND does not walk the tree */ break; case COLEXP_EXPAND_ANY: /* Ensure that all parent directories are expanded */ if (NODE_IS_DIR(dnode->parent)) { ++depth; colexp( dnode->parent, COLEXP_EXPAND_ANY ); --depth; } /* Initial collapse/expand notify */ geometry_colexp_initiated( dnode ); break; case COLEXP_COLLAPSE_RECURSIVE: case COLEXP_EXPAND_RECURSIVE: /* Initial collapse/expand notify */ geometry_colexp_initiated( dnode ); /* Perform action on subdirectories */ ++depth; node = dnode->children; while (node != NULL) { if (NODE_IS_DIR(node)) colexp( node, mesg ); else break; node = node->next; } --depth; break; SWITCH_FAIL } if (mesg == COLEXP_EXPAND_RECURSIVE) { /* Update high-water mark */ max_depth = MAX(max_depth, depth); } if (depth == 0) { /* Determine position of current node w.r.t. the * collapsing/expanding directory node */ curnode_is_ancestor = g_node_is_ancestor( globals.current_node, dnode ); curnode_is_equal = globals.current_node == dnode; curnode_is_descendant = g_node_is_ancestor( dnode, globals.current_node ); /* Handle the camera semi-intelligently if it is not under * manual control */ if (!camera->manual_control) { switch (mesg) { case COLEXP_COLLAPSE_RECURSIVE: pan_time = (double)(max_depth + 1) * colexp_time; if (curnode_is_ancestor || curnode_is_equal) camera_look_at_full( globals.current_node, MORPH_LINEAR, pan_time ); else if (curnode_is_descendant) camera_look_at_full( dnode, MORPH_LINEAR, pan_time ); break; case COLEXP_EXPAND: case COLEXP_EXPAND_RECURSIVE: if (curnode_is_ancestor || curnode_is_equal) { pan_time = (double)(max_depth + 1) * colexp_time; camera_look_at_full( globals.current_node, MORPH_LINEAR, pan_time ); } break; case COLEXP_EXPAND_ANY: /* Don't do anything. Something else * should already be doing something * with the camera */ break; SWITCH_FAIL } } /* If, in TreeV mode, the current node is an ancestor of * a collapsing/expanding directory, the scrollbars may * need updating to reflect a new scroll range */ scrollbars_colexp_adjust = FALSE; if (curnode_is_ancestor && (globals.fsv_mode == FSV_TREEV)) scrollbars_colexp_adjust = TRUE; }
gboolean remove_from_tree(GNode *file, gboolean unmount) { int position = g_list_position(lines, FILE(file)->line); gboolean refresh_needed = FALSE; GList *line_ptr, *line_ptr2; GNode *dir_ptr; if (G_NODE_IS_ROOT(file)) { endwin(); clean_up(); printf("The tree root was removed\n"); exit(EXIT_SUCCESS); } if (g_node_is_ancestor(file, NODE(selected_line))) select_file(file); if (FILE(file)->type == directory_type) { close_directory(file); destroy_directory_content_real(file, FALSE); if (unmount) return TRUE; } else if (FILE(file)->type == file_type) { for (dir_ptr = file->parent; !G_NODE_IS_ROOT(dir_ptr); dir_ptr = dir_ptr->parent) { if (FILE(dir_ptr)->open == FALSE) { g_node_unlink(file); return FALSE; } } if (FILE(dir_ptr)->open == FALSE) { g_node_unlink(file); return FALSE; } } g_node_unlink(file); if (g_list_position(lines, first_line) <= position && position <= g_list_position(lines, last_line)) { if (first_line == FILE(file)->line && selected_line == FILE(file)->line) { selected_line = first_line = g_list_previous(first_line); lines = g_list_delete_link(lines, FILE(file)->line); print_lines(first_line, first_line, FALSE); } else if (position < g_list_position(lines, selected_line)) { if (first_line == FILE(file)->line) first_line = g_list_next(first_line); line_ptr = g_list_previous(FILE(file)->line); lines = g_list_delete_link(lines, FILE(file)->line); if ((line_ptr2 = g_list_previous(first_line)) != NULL) { first_line = line_ptr2; print_lines(first_line, line_ptr, FALSE); } else if ((line_ptr2 = g_list_next(last_line)) != NULL) { last_line = line_ptr2; print_lines(line_ptr, last_line, FALSE); } else print_lines(line_ptr, last_line, TRUE); } else { if (FILE(file)->line == selected_line) selected_line = g_list_previous(selected_line); if (last_line == FILE(file)->line) last_line = g_list_previous(last_line); line_ptr = g_list_previous(FILE(file)->line); lines = g_list_delete_link(lines, FILE(file)->line); if ((line_ptr2 = g_list_next(last_line)) != NULL) { last_line = line_ptr2; print_lines(line_ptr, last_line, FALSE); } else print_lines(line_ptr, last_line, TRUE); } refresh_needed = TRUE; } else { if (last_line == g_list_previous(FILE(file)->line)) { lines = g_list_delete_link(lines, FILE(file)->line); print_lines(last_line, last_line, FALSE); refresh_needed = TRUE; } else lines = g_list_delete_link(lines, FILE(file)->line); } free_node_data(file, NULL); g_node_destroy(file); return refresh_needed; }
/* return the reversed thread tree */ GNode *procmsg_get_thread_tree(GSList *mlist) { GNode *root, *parent, *node, *next; GHashTable *msgid_table; GRelation *subject_relation; MsgInfo *msginfo; const gchar *msgid; root = g_node_new(NULL); msgid_table = g_hash_table_new(g_str_hash, g_str_equal); subject_relation = g_relation_new(2); g_relation_index(subject_relation, 0, g_str_hash, g_str_equal); for (; mlist != NULL; mlist = mlist->next) { msginfo = (MsgInfo *)mlist->data; parent = root; if (msginfo->inreplyto) { parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto); if (parent == NULL) { parent = root; } } node = g_node_insert_data_before (parent, parent == root ? parent->children : NULL, msginfo); if ((msgid = msginfo->msgid) && g_hash_table_lookup(msgid_table, msgid) == NULL) g_hash_table_insert(msgid_table, (gchar *)msgid, node); /* CLAWS: add subject to relation (without prefix) */ if (prefs_common.thread_by_subject) { subject_relation_insert(subject_relation, node); } } /* complete the unfinished threads */ for (node = root->children; node != NULL; ) { parent = NULL; next = node->next; msginfo = (MsgInfo *)node->data; if (msginfo->inreplyto) { parent = g_hash_table_lookup(msgid_table, msginfo->inreplyto); /* node should not be the parent, and node should not be an ancestor of parent (circular reference) */ if (parent && parent != node && !g_node_is_ancestor(node, parent)) { g_node_unlink(node); g_node_insert_before (parent, parent->children, node); } } node = next; } if (prefs_common.thread_by_subject) { for (node = root->children; node && node != NULL;) { next = node->next; msginfo = (MsgInfo *) node->data; parent = subject_relation_lookup(subject_relation, msginfo); /* the node may already be threaded by IN-REPLY-TO, so go up * in the tree to find the parent node */ if (parent != NULL) { if (g_node_is_ancestor(node, parent)) parent = NULL; if (parent == node) parent = NULL; } if (parent) { g_node_unlink(node); g_node_append(parent, node); } node = next; } } g_relation_destroy(subject_relation); g_hash_table_destroy(msgid_table); return root; }