% trn emulation for slrn... % THINGS TO DO: % Any chance of emulating trn's in-article-view thread tree display? % up and down arrow do the wrong thing wrt threads marked as read % (ie they skip them and they should not.) % Enter in article-mode scrolls down one line, but at the end of % an article it will scroll the article off screen and not go to % next article until the screen has entirely cleared. % ? better done as mod to slrn proper? % thread kill and mark kill one article, not the whole thread. % We don't support '.' to toggle kill-mark at all. % We want a show-unread equivalent. % total_unread would be unnecessary if you could use %u in art_status_line % (feature request forwarded to slrn-user list) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Group Mode Emulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % TODO if necessary %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Article Mode/Thread Selector Emulation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Use a trn-like header display format, eg: %d Allan Nathanson 1 >Help: Filesystem Overflow on OS4.2(intel) % That's <#arts in thread> % This must be set as we use it to return to thread display from article mode variable trn_header_display_format="%F%-5S %20r %t%s"; set_header_display_format(0, trn_header_display_format); variable article_count= 0; variable total_unread = 42; define header_first_unread() { % Go to first unread article in group. % Return 1 if OK, 0 if no unread articles in group. call("header_bob"); % back to top of header list if (get_header_flags() & HEADER_READ) { % Try to find some unread article, then. return header_next_unread(); } % OK, first article was unread. return 1; } define trn_set_total_unread() { % Set the total_unread count accurately. % Expensive, so don't do it often. variable this_article = extract_article_header ("Message-Id"); total_unread = 0; if (header_first_unread()) { do { total_unread++; } while (header_next_unread()); } % Return to article we were viewing. () = locate_header_by_msgid (this_article,0); } define trn_update_count() { % This would be much easier if the article status line supported %u; % as it is we have to calculate it ourselves (total_unread) if (article_count == -1) { set_string_variable("art_status_line", sprintf("(%%P) (%d more)", total_unread)); % set_header_display_format(0, sprintf("(%d more)", total_unread)); } else { set_string_variable("art_status_line", sprintf("(%%P) (%d + %d more)", article_count-1, total_unread)); % set_header_display_format(0, sprintf("(%d + %d more)", article_count-1, total_unread)); } } % Like header_next_unread, but it will wrap around to first % unread article in a group if there is no unread article % after the current one. define header_some_unread() { !if (header_next_unread()) { return header_first_unread(); } % If we get here we found something, so return success. return 1; } define trn_exit_article_mode() { % Exit article viewing mode back to the thread selector call ("hide_article"); % Make sure thread just read doesn't show up in thread list call ("art_xpunge"); set_header_display_format(0, trn_header_display_format); } define trn_exit_thread_mode() { % Back to group selection. Reset format as we don't always % pass through thread mode on our way from article to group mode. set_header_display_format(0, trn_header_display_format); call("quit"); % If we read all the articles in this group, remove it from the % list displayed. if (group_unread() == 0) hide_current_group(); } define trn_enter_thread_mode() { % Switch to thread mode set_header_display_format(0, trn_header_display_format); } define trn_enter_article_mode() { % Start viewing the current article, full screen call ("article_pagedn"); !if (is_article_window_zoomed()) { call ("zoom_article_window"); } % This avoids having a distracting single-line thread selector at the bottom... set_header_display_format(0, " "); trn_set_total_unread(); trn_update_count(); } % Try to get something closer to trn's thread selector/article view distinction % This is borrowed from the tin emulation example in Debian's slrn package. define trn_space_key_cmd () { if (is_article_visible ()) { % Article is visible: page down if possible. % Skip to next article if possible. % Skip to next thread only if we started out % via space rather than return. variable next_pgdn_act = get_next_art_pgdn_action(); if (article_count != -1 and next_pgdn_act != 0) { % If we are about to move out of this article, % and we're in view-one-thread mode, note that % we've read an article and see if we should % go back to the thread selector display. article_count--; total_unread--; if (article_count == 0) { % Skip to next article, if any. If none, drop out % to group selector !if (header_some_unread()) { trn_exit_thread_mode(); return; } % Back to thread selector trn_exit_article_mode(); return; } } if (next_pgdn_act == 2) { % We would leave this group altogether and go on to the next. % (Notice that this case comes after the end-of-subthread % case so we correctly handle reading just the last thread in % the group.) % In this case, we want to drop back to the group selector trn_exit_thread_mode(); return; } % OK to proceed to next article. trn_update_count(); call ("article_pagedn"); return; } % Article not visible, so in thread mode. % If we can page down through thread list, do so. variable pgdn_failed = 0; ERROR_BLOCK { pgdn_failed++; _clear_error(); } call("header_page_down"); !if (pgdn_failed) return; % Otherwise % go to first unread article, make it visible, zoom to full % screen and note that we started via space (read all threads). call ("header_bob"); article_count = -1; trn_enter_article_mode(); } define trn_view_one_thread () { if (is_article_visible ()) { % article already on screen, just scroll down a line call ("article_linedn"); return; } % Make a note of how many articles are in this thread % and available to be viewed via space. article_count = thread_size(); % Article not visible, make it so and zoom it. trn_enter_article_mode(); } define trn_quit_article_or_group () { % In trn, q quits viewing an article (back to the threadselector). % It also quits from threadselector to group. if (is_article_visible ()) { % If we're not at the bottom of the article, don't count it as read. if (get_next_art_pgdn_action() == 0) { set_header_flags(get_header_flags() & ~HEADER_READ); } trn_exit_article_mode(); } else { trn_exit_thread_mode(); } } define trn_up_arrow () { % Up-arrow should move up one article in the thread selector % but scroll up one line in article mode. if (is_article_visible()) { call("article_lineup"); } else { % Move into previous thread, then collapse-uncollapse % to move to first article in that thread. header_up(1); collapse_thread(); uncollapse_thread(); } } define trn_down_arrow () { % Similarly for down if (is_article_visible()) { % This isn't quite trn-like, but don't scroll down % if page-down wouldn't show any more text. if (get_next_art_pgdn_action() == 0) { call("article_linedn"); } } else { % Move down into next thread. % Note check that we moved the right distance; % we want down-arrow on last thread in group to do nothing. variable hoped_count = thread_size(); variable down_count = header_down(hoped_count); if (down_count != hoped_count) { header_up(down_count); } } } define trn_next_article() { % Shift-N should go to next article even if it's read if (is_article_visible()) { if (header_down(1) == 1) { trn_enter_article_mode(); } } } define trn_previous_article() { % Shift-P should go to next article even if it's read. % In the thread selector we make this do a post. if (is_article_visible()) { if (header_up(1) == 1) { trn_enter_article_mode(); } } else { call("post"); } } define trn_left_arrow () { % like pageup on thread display, like prev-article on article display if (is_article_visible()) trn_previous_article(); else call("header_page_up"); } define trn_right_arrow () { % like pagedown on thread display, like next-article on article display if (is_article_visible()) trn_next_article(); else call("header_page_down"); } define trn_page_up () { if (is_article_visible()) { % No need to check for page-up to prev article because it doesn't % happen; only page-down is magical. call("article_page_up"); } else { call("header_page_up"); } } define trn_page_down () { if (is_article_visible()) { if (get_next_art_pgdn_action() == 0) call("article_page_down"); } else { call("header_page_down"); } } definekey ("trn_view_one_thread", "\r", "article"); definekey ("trn_space_key_cmd", " ", "article"); definekey ("trn_quit_article_or_group", "q", "article"); % slrn standard bindings use both these, so we do. definekey ("trn_up_arrow", "\eOA", "article"); definekey ("trn_up_arrow", "\e[A", "article"); definekey ("trn_down_arrow", "\e[B", "article"); definekey ("trn_down_arrow", "\eOB", "article"); definekey("trn_left_arrow", "\eOD", "article"); definekey("trn_left_arrow", "\e[D", "article"); definekey("trn_right_arrow", "\eOC", "article"); definekey("trn_right_arrow", "\e[C", "article"); definekey("trn_page_down", "\e[6~", "article"); definekey("trn_page_down", "\e[G", "article"); % (FreeBSD keycode) definekey("trn_page_up", "\e[5~", "article"); definekey("trn_page_up", "\e[I", "article"); % (FreeBSD keycode) % Turn on verbose headers... definekey ("toggle_headers", "v", "article"); definekey ("goto_last_read", "-", "article"); % k and ',' mark the current thread as killed. definekey ("delete", "k", "article"); definekey ("delete", ",", "article"); % m unmarks the current thread. definekey ("undelete", "m", "article"); % '.' is supposed to toggle the selection between % 'marked' and 'unmarked'. '.' on a marked-for-delete article % puts it in the 'marked' state. % Don't treat F as 'forward'; instead use it as followup % (happens automatically as f is bound to followup) undefinekey ("F", "article"); definekey ("followup", "F", "article"); definekey ("trn_next_article", "N", "article"); definekey ("trn_previous_article", "P", "article"); % Not quite right, but close enough definekey ("header_bob", "^", "article"); definekey ("header_eob", "$", "article");