home *** CD-ROM | disk | FTP | other *** search
- /*
- * Project : tin - a threaded Netnews reader
- * Module : thread.c
- * Author : I.Lea
- * Created : 01-04-91
- * Updated : 11-05-92
- * Notes :
- * Copyright : (c) Copyright 1991-92 by Iain Lea
- * You may freely copy or redistribute this software,
- * so long as there is no profit made from its use, sale
- * trade or reproduction. You may not change this copy-
- * right notice, and it must be included in any copy made
- */
-
- #include "tin.h"
-
- extern int index_point;
- int threaded_on_subject;
- static int top_thread = 0;
- static int thread_index_point = 0;
- static int thread_basenote = 0;
- static int thread_respnum = 0;
- static int first_thread_on_screen = 0;
- static int last_thread_on_screen = 0;
-
-
- /*
- * show current thread. If threaded on Subject: show
- * <respnum> <name> <respnum> <name>
- * If threaded on Archive-name: show
- * <respnum> <subject> <name>
- */
-
- int show_thread (respnum, group, group_path)
- int respnum;
- char *group;
- char *group_path;
- {
- int ret_code = TRUE;
- #ifndef INDEX_DAEMON
- int ch;
- int i, index, n;
- int scroll_lines;
-
- thread_respnum = respnum;
- thread_basenote = which_thread (thread_respnum);
- top_thread = num_of_responses (thread_basenote) + 1;
-
- if (top_thread <= 0) {
- info_message (txt_no_resps_in_thread);
- return FALSE;
- }
-
- if (arts[thread_respnum].archive != (char *) 0) {
- threaded_on_subject = FALSE;
- } else {
- threaded_on_subject = TRUE;
- }
-
- thread_index_point = top_thread;
- if (space_mode) {
- if (i = new_responses (thread_basenote)) {
- for (n=0, i = base[thread_basenote]; i >= 0 ;
- i = arts[i].thread, n++) {
- if (arts[i].unread == ART_UNREAD) {
- thread_index_point = n;
- break;
- }
- }
- }
- }
-
- if (thread_index_point < 0) {
- thread_index_point = 0;
- }
-
- show_thread_page ();
-
- while (TRUE) {
- ch = (char) ReadCh();
-
- if (ch >= '0' && ch <= '9') { /* 0 goes to basenote */
- prompt_thread_num (ch);
- } else switch (ch) {
- case ESC: /* common arrow keys */
- switch (get_arrow_key ()) {
- case KEYMAP_UP:
- goto thread_up;
-
- case KEYMAP_DOWN:
- goto thread_down;
-
- case KEYMAP_PAGE_UP:
- goto thread_page_up;
-
- case KEYMAP_PAGE_DOWN:
- goto thread_page_down;
-
- case KEYMAP_HOME:
- if (thread_index_point != 0) {
- if (0 < first_thread_on_screen) {
- #ifndef USE_CLEARSCREEN
- erase_thread_arrow ();
- #endif
- thread_index_point = 0;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = 0;
- draw_thread_arrow ();
- }
- }
- break;
-
- case KEYMAP_END:
- goto end_of_thread;
- }
- break;
-
- case '$': /* show last page of threads */
- end_of_thread:
- if (thread_index_point < top_thread - 1) {
- if (top_thread > last_thread_on_screen) {
- #ifndef USE_CLEARSCREEN
- erase_thread_arrow ();
- #endif
- thread_index_point = top_thread - 1;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = top_thread - 1;
- draw_thread_arrow ();
- }
- }
- break;
-
- case '\r':
- case '\n': /* read current article within thread */
- n = choose_response (thread_basenote, thread_index_point);
- n = show_page (n, &thread_index_point, group, group_path);
- if (n == thread_basenote) {
- show_thread_page ();
- } else {
- index_point = n;
- goto thread_done;
- }
- break;
-
- case '\t':
- space_mode = TRUE;
- if (thread_index_point == 0) {
- n = thread_respnum;
- } else {
- n = choose_response (thread_basenote, thread_index_point);
- }
- index = thread_index_point;
- for (i = n ; i != -1 ; i = arts[i].thread) {
- if (arts[i].unread == ART_UNREAD) {
- n = show_page (i, &thread_index_point, group, group_path);
- break;
- }
- index++;
- }
- if (n == thread_basenote) {
- show_thread_page ();
- } else {
- index_point = n;
- goto thread_done;
- }
- break;
-
- case ' ': /* page down */
- case ctrl('D'): /* vi style */
- case ctrl('V'): /* emacs style */
- thread_page_down:
- if (thread_index_point + 1 == top_thread) {
- #ifdef NO_LOOP_AROUND
- break;
- #else
- if (0 < first_thread_on_screen) {
- # ifndef USE_CLEARSCREEN
- erase_thread_arrow ();
- # endif
- thread_index_point = 0;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = 0;
- draw_thread_arrow ();
- }
- break;
- #endif
- }
- erase_thread_arrow ();
- scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
- thread_index_point = ((thread_index_point + scroll_lines) /
- scroll_lines) * scroll_lines;
- if (thread_index_point >= top_thread) {
- thread_index_point = (top_thread / scroll_lines) * scroll_lines;
- if (thread_index_point < top_thread - 1) {
- thread_index_point = top_thread - 1;
- }
- }
- if (thread_index_point < first_thread_on_screen ||
- thread_index_point >= last_thread_on_screen) {
- show_thread_page ();
- } else {
- draw_thread_arrow ();
- }
- break;
-
- case ctrl('L'): /* redraw screen */
- case ctrl('R'):
- case ctrl('W'):
- #ifndef USE_CLEARSCREEN
- ClearScreen ();
- #endif
- show_thread_page ();
- break;
-
- case ctrl('N'):
- case 'j': /* line down */
- thread_down:
- if (thread_index_point + 1 >= top_thread) {
- #ifdef NO_LOOP_AROUND
- break;
- #else
- if (0 < first_thread_on_screen) {
- thread_index_point = 0;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = 0;
- draw_thread_arrow ();
- }
- break;
- #endif
- }
- if (thread_index_point + 1 >= last_thread_on_screen) {
- #ifndef USE_CLEARSCREEN
- erase_thread_arrow ();
- #endif
- thread_index_point++;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point++;
- draw_thread_arrow ();
- }
- break;
-
- case ctrl('P'):
- case 'k': /* line up */
- thread_up:
- if (thread_index_point == 0) {
- #ifdef NO_LOOP_AROUND
- break;
- #else
- if (top_thread > last_thread_on_screen) {
- thread_index_point = top_thread - 1;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = top_thread - 1;
- draw_thread_arrow ();
- }
- break;
- #endif
- }
- if (thread_index_point <= first_thread_on_screen) {
- thread_index_point--;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point--;
- draw_thread_arrow ();
- }
- break;
-
- case ctrl('U'): /* page up */
- case 'b':
- thread_page_up:
- if (thread_index_point == 0) {
- #ifdef NO_LOOP_AROUND
- break;
- #else
- if (top_thread > last_thread_on_screen) {
- thread_index_point = top_thread - 1;
- show_thread_page ();
- } else {
- erase_thread_arrow ();
- thread_index_point = top_thread - 1;
- draw_thread_arrow ();
- }
- break;
- #endif
- }
- #ifndef USE_CLEARSCREEN
- clear_message ();
- #endif
- erase_thread_arrow ();
- scroll_lines = (full_page_scroll ? NOTESLINES : NOTESLINES / 2);
- if ((n = thread_index_point % scroll_lines) > 0) {
- thread_index_point = thread_index_point - n;
- } else {
- thread_index_point = ((thread_index_point - scroll_lines) / scroll_lines) * scroll_lines;
- }
- if (thread_index_point < 0) {
- thread_index_point = 0;
- }
- if (thread_index_point < first_thread_on_screen
- || thread_index_point >= last_thread_on_screen)
- show_thread_page ();
- else
- draw_thread_arrow ();
- break;
-
- case 'B': /* bug/gripe/comment mailed to author */
- mail_bug_report ();
- #ifndef USE_CLEARSCREEN
- ClearScreen ();
- #endif
- show_thread_page ();
- break;
-
- case 'c': /* catchup thread but ask for confirmation */
- case 'K': /* mark thread as read immediately */
- if (ch == 'c') {
- if (confirm_action && !prompt_yn (LINES, txt_mark_thread_read, 'y')) {
- break;
- }
- }
- for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) {
- arts[i].unread = ART_READ;
- }
- goto thread_done;
- break;
-
- case 'd': /* toggle display of subject & subj/author */
- if (! threaded_on_subject) {
- toggle_subject_from ();
- show_thread_page ();
- }
- break;
-
- case 'h': /* help */
- show_info_page (HELP_INFO, help_thread, txt_thread_com);
- show_thread_page ();
- break;
-
- case 'I': /* toggle inverse video */
- toggle_inverse_video ();
- show_thread_page ();
- break;
-
- case 'q': /* return to previous level */
- goto thread_done;
-
- case 'Q': /* quit */
- ret_code = -2;
- goto thread_done;
-
- case 'T': /* tag/untag art for mailing/piping/printing/saving */
- if (thread_index_point == 0) {
- n = thread_respnum;
- } else {
- n = choose_response (thread_basenote, thread_index_point);
- }
- if (n < 0)
- break;
-
- if (arts[n].tagged) {
- arts[n].tagged = 0;
- info_message (txt_untagged_art);
- } else {
- arts[n].tagged = ++num_of_tagged_arts;
- info_message (txt_tagged_art);
- }
- show_thread_page ();
- goto thread_down;
- break;
-
- case 'v': /* version */
- info_message (cvers);
- break;
-
- case 'z': /* mark article as unread */
- if (thread_index_point == 0) {
- n = thread_respnum;
- } else {
- n = choose_response (thread_basenote, thread_index_point);
- }
- if (n >= 0) {
- arts[n].unread = ART_UNREAD;
- show_thread_page ();
- info_message (txt_art_marked_as_unread);
- }
- break;
-
- case 'Z': /* mark thread as unread */
- for (i = (int) base[thread_basenote] ; i != -1 ; i = arts[i].thread) {
- arts[i].unread = ART_UNREAD;
- }
- show_thread_page ();
- info_message (txt_thread_marked_as_unread);
- break;
-
- default:
- info_message (txt_bad_command);
- }
- }
-
- thread_done:
- clear_note_area ();
-
- #endif /* INDEX_DAEMON */
-
- return (ret_code);
- }
-
-
- void show_thread_page ()
- {
- #ifndef INDEX_DAEMON
-
- extern int index_point;
- char new_resps[8];
- char from[LEN];
- int i, j;
- int len_from;
- int len_subj = 0;
- int off_subj = 0;
- int off_both = 0;
- static int index = 0;
-
- set_signals_thread ();
-
- ClearScreen ();
-
- if (threaded_on_subject) {
- sprintf (msg, "Thread (%.*s)", COLS-23, arts[thread_respnum].subject);
- } else {
- sprintf (msg, "List Thread (%d of %d)", index_point+1, top_base);
- }
- show_title (msg);
-
- MoveCursor (INDEX_TOP, 0);
- if (thread_index_point > top_thread - 1) {
- thread_index_point = top_thread - 1;
- }
-
- if (NOTESLINES <= 0) {
- first_thread_on_screen = 0;
- } else {
- first_thread_on_screen = (thread_index_point / NOTESLINES) * NOTESLINES;
- if (first_thread_on_screen < 0) {
- first_thread_on_screen = 0;
- }
- }
-
- last_thread_on_screen = first_thread_on_screen + NOTESLINES;
-
- if (last_thread_on_screen >= top_thread) {
- last_thread_on_screen = top_thread;
- first_thread_on_screen = (top_thread / NOTESLINES) * NOTESLINES;
-
- if (first_thread_on_screen == last_thread_on_screen ||
- first_thread_on_screen < 0) {
- if (first_thread_on_screen < 0) {
- first_thread_on_screen = 0;
- } else {
- first_thread_on_screen = last_thread_on_screen - NOTESLINES;
- }
- }
- }
-
- if (top_thread == 0) {
- first_thread_on_screen = 0;
- last_thread_on_screen = 0;
- }
-
- index = choose_response (thread_basenote, first_thread_on_screen);
- assert(first_thread_on_screen != 0 || index == thread_respnum);
-
- if (! draw_arrow_mark) {
- off_subj = 2;
- off_both = 5;
- }
-
- if (threaded_on_subject) {
- len_from = max_subj+max_from+off_both;
- } else {
- if (show_author != SHOW_FROM_NONE) {
- len_from = max_from;
- len_subj = max_subj+off_subj;
- } else {
- len_from = 0;
- len_subj = max_from+max_subj+off_subj;
- }
- }
-
- for (j=0, i = first_thread_on_screen; j < NOTESLINES && i < last_thread_on_screen; i++, j++) {
- if (arts[index].tagged) {
- sprintf (new_resps, "%3d", arts[index].tagged);
- } else if (arts[index].unread == ART_UNREAD) {
- if (arts[index].hot == 0)
- sprintf (new_resps, " %c", UNREAD_ART_MARK);
- else
- sprintf (new_resps, " %c", HOT_ART_MARK);
- } else if (arts[index].unread == ART_WILL_RETURN) {
- sprintf (new_resps, " %c", RETURN_ART_MARK);
- } else {
- strcpy (new_resps, " ");
- }
-
- if (threaded_on_subject) {
- get_author (TRUE, index, from);
- sprintf (screen[j].col, " %4d%3s %-*.*s\r\n",
- i, new_resps, len_from, len_from, from);
- } else {
- if (show_author != SHOW_FROM_NONE) {
- get_author (TRUE, index, from);
- }
- sprintf (screen[j].col, " %4d%3s %-*.*s %-*.*s\r\n",
- i, new_resps, len_subj, len_subj,
- arts[index].subject, len_from, len_from, from);
- }
-
- fputs (screen[j].col, stdout);
-
- if ((index = next_response (index)) == -1) {
- break;
- }
- }
-
- #ifndef USE_CLEARSCREEN
- CleartoEOS ();
- #endif
-
- if (last_thread_on_screen == top_thread) {
- info_message (txt_end_of_thread);
- }
-
- draw_thread_arrow ();
-
- #endif /* INDEX_DAEMON */
- }
-
-
- void draw_thread_arrow ()
- {
- draw_arrow (INDEX_TOP + (thread_index_point-first_thread_on_screen));
- }
-
-
- void erase_thread_arrow ()
- {
- erase_arrow (INDEX_TOP + (thread_index_point-first_thread_on_screen));
- }
-
-
- int prompt_thread_num (ch)
- char ch;
- {
- int num;
-
- clear_message ();
-
- if ((num = prompt_num (ch, txt_read_art)) == -1) {
- clear_message ();
- return FALSE;
- }
-
- if (num >= top_thread)
- num = top_thread - 1;
-
- if (num >= first_thread_on_screen
- && num < last_thread_on_screen) {
- erase_thread_arrow ();
- thread_index_point = num;
- draw_thread_arrow ();
- } else {
- #ifndef USE_CLEARSCREEN
- erase_thread_arrow ();
- #endif
- thread_index_point = num;
- show_thread_page ();
- }
- return TRUE;
- }
-
- /*
- * Return the number of unread articles there are within a thread
- */
-
- int new_responses (thread)
- int thread;
- {
- int i;
- int sum = 0;
-
- for (i = (int) base[thread]; i >= 0; i = arts[i].thread) {
- if (arts[i].unread) {
- sum++;
- }
- }
-
- return sum;
- }
-
- /*
- * Which base note (an index into base[]) does a respnum
- * (an index into arts[]) corresponsd to?
- *
- * In other words, base[] points to an entry in arts[] which is
- * the head of a thread, linked with arts[].thread. For any q: arts[q],
- * find i such that base[i]->arts[n]->arts[o]->...->arts[q]
- *
- * Note that which_thread() can return -1 if in show_read_only mode and
- * the article of interest has been read as well as all other articles in
- * the thread, thus resulting in no base[] entry for it.
- */
-
- int which_thread (n)
- int n;
- {
- register int i, j;
-
- for (i = 0; i < top_base; i++) {
- for (j = (int) base[i] ; j >= 0 ; j = arts[j].thread) {
- if (j == n) {
- return i;
- }
- }
- }
-
- sprintf (msg, "%d", n);
- error_message (txt_cannot_find_base_art, msg);
- return -1;
- }
-
- /*
- * Find how deep in a thread a response is. Start counting at zero
- */
-
- int which_response (n)
- int n;
- {
- int i, j;
- int num = 0;
-
- i = which_thread (n);
- assert(i >= 0);
-
- for (j = (int) base[i]; j != -1; j = arts[j].thread)
- if (j == n)
- break;
- else
- num++;
-
- return num;
- }
-
- /*
- * Given an index into base[], find the number of responses for
- * that basenote
- */
-
- int num_of_responses (n)
- int n;
- {
- int i;
- int oldi = -3;
- int sum = 0;
-
- assert (n < top_base);
-
- for (i = (int) base[n]; i != -1; i = arts[i].thread) {
- assert (i != -2);
- assert (i != oldi);
- oldi = i;
- sum++;
- }
-
- return sum - 1;
- }
-
- /*
- * Given an index into base[], return relevant statistics
- */
-
- int stat_thread (n, sbuf)
- int n;
- struct art_stat_t *sbuf;
- {
- int i;
-
- sbuf->total = 0;
- sbuf->unread = 0;
- sbuf->seen = 0;
- sbuf->hot_total = 0;
- sbuf->hot_unread= 0;
- sbuf->hot_seen = 0;
-
- for (i = (int) base[n]; i != -1; i = arts[i].thread) {
- ++sbuf->total;
- if (arts[i].unread == ART_UNREAD)
- ++sbuf->unread;
- else if (arts[i].unread == ART_WILL_RETURN)
- ++sbuf->seen;
-
- if (arts[i].hot) {
- ++sbuf->hot_total;
- if (arts[i].unread == ART_UNREAD)
- ++sbuf->hot_unread;
- else if (arts[i].unread == ART_WILL_RETURN)
- ++sbuf->hot_seen;
- }
-
- #if 0
- if (arts[i].killed) {
- ++sbuf->killed;
- }
- #endif
- }
-
-
- if (sbuf->hot_unread)
- sbuf->art_mark = HOT_ART_MARK;
- else if (sbuf->unread)
- sbuf->art_mark = UNREAD_ART_MARK;
- else if (sbuf->seen)
- sbuf->art_mark = RETURN_ART_MARK;
- else
- sbuf->art_mark = READ_ART_MARK;
-
- return(sbuf->total);
- }
-
-
- /*
- * Find the next response. Go to the next basenote if there
- * are no more responses in this thread
- */
-
- int next_response (n)
- int n;
- {
- int i;
-
- if (arts[n].thread >= 0)
- return arts[n].thread;
-
- i = which_thread (n) + 1;
-
- if (i >= top_base)
- return -1;
-
- return (int) base[i];
- }
-
- /*
- * Given a respnum (index into arts[]), find the respnum of the
- * next basenote
- */
-
- int next_thread (n)
- int n;
- {
- int i;
-
- i = which_thread (n) + 1;
- if (i >= top_base)
- return -1;
-
- return (int) base[i];
- }
-
- /*
- * Find the previous response. Go to the last response in the previous
- * thread if we go past the beginning of this thread.
- */
-
- int prev_response (n)
- int n;
- {
- int resp;
- int i;
-
- resp = which_response (n);
-
- if (resp > 0)
- return choose_response (which_thread (n), resp-1);
-
- i = which_thread (n) - 1;
-
- if (i < 0)
- return -1;
-
- return choose_response (i, num_of_responses (i));
- }
-
- /*
- * return response number n from thread i
- */
-
- int choose_response (i, n)
- int i;
- int n;
- {
- int j;
-
- j = (int) base[i];
-
- while (n-- && arts[j].thread >= 0) {
- j = arts[j].thread;
- }
-
- return j;
- }
-
- /*
- * Find the next unread response in this group
- */
-
- int next_unread (n)
- int n;
- {
- while (n >= 0) {
- if (arts[n].unread == ART_UNREAD) {
- return n;
- }
- n = next_response (n);
- }
-
- return -1;
- }
-
-
- /*
- * Find the previous unread response in this thread
- */
-
- int prev_unread (n)
- int n;
- {
- while (n >= 0) {
- if (arts[n].unread == ART_UNREAD) {
- return n;
- }
- n = prev_response (n);
- }
-
- return -1;
- }
-