// ------------------------------------------------------------------ // GoldED+ // Copyright (C) 1990-1999 Odinn Sorensen // Copyright (C) 1999-2000 Alexander S. Aganichev // ------------------------------------------------------------------ // 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 the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, // MA 02111-1307 USA // ------------------------------------------------------------------ // $Id$ // ------------------------------------------------------------------ // Main reader and function key dispatcher. // ------------------------------------------------------------------ #include // ------------------------------------------------------------------ // Global data extern bool cmdlinenoscan; extern bool cmdlineexportsoup; GMsg* reader_msg; int reader_gen_confirm = false; int reader_finished; int reader_done = false; int reader_topline; int reader_keyok; int reader_direction; int reader_rcv_noise = false; gkey reader_keycode; gkey reader_lastcode = 0; GMsgHeaderView *HeaderView; GMsgBodyView *BodyView; void TwitDeleteMsg(GMsg* msg); void GoNextMsg(); // ------------------------------------------------------------------ gkey ReaderGetKey() { gkey keycode; do { keycode = getxchtick(); if(keycode == Key_Tick) CheckTick(KK_ReadQuitNow); } while(keycode == Key_Tick); if(keycode < KK_Commands) { keycode = key_tolower(keycode); gkey kk = SearchKey(keycode, ReadKey, ReadKeys); if(kk) keycode = kk; } return keycode; } // ------------------------------------------------------------------ gkey ViewMessage(int istwit) { gkey keycode; int keyok; do { keycode = ReaderGetKey(); keyok = true; switch(keycode) { case KK_ReadMsgLineUp: if(istwit <= TWIT_SHOW) BodyView->LineUp(); break; case KK_ReadMsgLineDown: if(istwit <= TWIT_SHOW) BodyView->LineDown(); break; case KK_ReadMsgPgUp: if(istwit <= TWIT_SHOW) BodyView->PageUp(); break; case KK_ReadMsgPgDn: if(istwit <= TWIT_SHOW) BodyView->PageDown(); break; case KK_ReadMsgContinue: if((istwit > TWIT_SHOW) or not BodyView->Continue()) keyok = false; break; case KK_ReadMsgHome: BodyView->Top(istwit > TWIT_SHOW); istwit = NOT_TWIT; break; case KK_ReadMsgEnd: BodyView->Bottom(); istwit = NOT_TWIT; break; default: keyok = false; } update_statuslines(); } while(keyok); return keycode; } // ------------------------------------------------------------------ void Reader() { int istwit; bool istwitto, istwitsubj; char buf[256], buf2[200]; GMsg* msg = (GMsg*)throw_calloc(1, sizeof(GMsg)); reader_finished = false; reader_msg = msg; // Sort all areas by the given sort order before scanning AL.Sort(); int personalmails = 0; int pmareas = 0; int pmscan = CFG->personalmail & PM_STARTUP; if(not cmdlinenoscan) { if(cmdlinedebughg) LOG.printf("- AreaScan during startup"); AL.AreaScan(SCAN_STARTUP, 0, pmscan, personalmails, pmareas); startupscan_success = true; } CheckSemaphores(); if(cmdlineimportsoup) ImportSOUP(); if(cmdlineexportsoup) ExportSOUP(); AL.Sort(); startecho = *stecho ? AL.AreaEchoToNo(stecho) : -1; inforow = (MAXROW-1)/2; wclear(); // Reset tick values to avoid triggering screenblanker or timeout gkbdtickpressreset(); gkbdtickvaluereset(); if(startecho == -1) { int i = 0; do { AL.SetActiveAreaNo(i++); } while(AA->isseparator()); NewArea(); } else { AL.SetActiveAreaNo(startecho); AA->RandomizeData(); if(AA->Msglistfirst()) kbput(KK_ReadMessageList); } if(not reader_finished) { // Open the editor/reader window HeaderView->width = MAXCOL; HeaderView->height = 6; HeaderView->border_color = C_HEADB; HeaderView->window_color = C_HEADW; HeaderView->title_color = C_HEADT; HeaderView->highlight_color = C_HEADQ; HeaderView->from_color = C_HEADBY; HeaderView->to_color = C_HEADTO; HeaderView->subject_color = C_HEADRE; HeaderView->Create(); BodyView->at_row = 6; BodyView->width = MAXCOL; BodyView->height = MAXROW - 6 - 1; BodyView->border_color = C_READB; BodyView->window_color = C_READW; BodyView->scrollbar_color = C_READPB; BodyView->highlight_color = C_READH; BodyView->Create(); do { AA->isreadpm = false; AA->isreadmark = false; reader_direction = DIR_NEXT; reader_msg->msgno = 0; CFG->tplno = 0; AL.SetActiveAreaId(CurrArea); OrigArea = CurrArea; AA->Open(); AA->RandomizeData(); if(AA->Play().type) AA->Play().Play(); // Jump to next message. Go to personal mail if any. if(AA->PMrk.Tags()) { AA->SetBookmark(AA->lastread()); AA->isreadpm = false; ToggleMarkRead(); } else { if(CFG->switches.get(dispautonext) or (AA->lastread() == 0)) next_msg(DIR_NEXT); AA->SetBookmark(AA->lastread()); } // Main message reader loop do { // Set the default help for this window whelpcat(H_Reader); if(not AA->attr().hex()) reader_topline = 0; if(AA->Msgn.Count() and not AA->lastread()) AA->set_lastread(1); if(LoadMessage(msg, CFG->dispmargin-(int)CFG->switches.get(disppagebar))) { do { HeaderView->Use(AA, msg); HeaderView->Paint(); if(AA->isreadmark or AA->isreadpm) { GTag& tag = AA->isreadpm ? AA->PMrk : AA->Mark; uint mtemp = tag.Find(msg->msgno); sprintf(buf2, "%s [%s%s%s]", LNG->ReadMarked, AA->Viewhidden() ? "H" : "", AA->Viewkludge() ? "K" : "", AA->Twitmode() == TWIT_IGNORE ? "Ti" : AA->Twitmode() == TWIT_SKIP ? "Ts" : AA->Twitmode() == TWIT_BLANK ? "Tb" : AA->Twitmode() == TWIT_KILL ? "Tk" : "" ); sprintf(buf, buf2, mtemp, tag.Count(), tag.Count()-mtemp ); } else { sprintf(buf2, "%s [%s%s%s]", LNG->ReadAll, AA->Viewhidden() ? "H" : "", AA->Viewkludge() ? "K" : "", AA->Twitmode() == TWIT_IGNORE ? "Ti" : AA->Twitmode() == TWIT_SKIP ? "Ts" : AA->Twitmode() == TWIT_BLANK ? "Tb" : AA->Twitmode() == TWIT_KILL ? "Tk" : "" ); sprintf(buf, buf2, AA->lastread(), AA->Msgn.Count(), AA->Msgn.Count()-AA->lastread() ); } update_statusline(buf); reader_done = false; reader_keyok = false; // Do some updating first AA->set_lastread(AA->Msgn.ToReln(msg->msgno)); AA->UpdateAreadata(); // Determine if the message is of the "twit" type istwit = MsgIsTwit(msg, istwitto, istwitsubj); if(istwit) HandleGEvent(EVTT_MSGISTWIT); if((istwit == TWIT_SKIP) or (not CFG->showdeleted and msg->attr.del())) { if(reader_direction == DIR_NEXT) { if(AA->lastread() < AA->Msgn.Count()) { if(gkbd.kbuf == NULL) { switch(reader_lastcode) { case KK_ReadGotoReplies: case KK_ReadGotoReply1st: case KK_ReadGotoReplyNext: if(MsgHasReplies(msg)) { kbput(KK_ReadGotoReplies); } else { SayBibi(); kbput(KK_ReadGotoReplyPrev); } break; default: kbput(KK_ReadGotoNextMsg); } } } } else { if(AA->lastread() > 1) { if(gkbd.kbuf == NULL) { if(reader_lastcode == KK_ReadGotoReplyPrev) { if(AA->Msgn.ToReln(msg->link.to())) kbput(KK_ReadGotoReplyPrev); else { SayBibi(); kbput(KK_ReadGotoReplies); } } else kbput(KK_ReadGotoPrevMsg); } } } } BodyView->Use(AA, msg, reader_topline); if(istwit > TWIT_SHOW) { BodyView->window.clear(); if(istwit == TWIT_SKIP) BodyView->window.prints(1, 0, C_READW, LNG->SkippingTwit); else if(istwit == TWIT_KILL) BodyView->window.prints(1, 0, C_READW, LNG->KillingTwit); else { BodyView->window.prints(1, 0, C_READW, LNG->HidingTwit); } } else { BodyView->Paint(); } if(reader_rcv_noise) { if(CFG->beepyourmail == ALWAYS) HandleGEvent(EVTT_MSGTOYOU); else if(reader_rcv_noise > 1 and CFG->beepyourmail == true) HandleGEvent(EVTT_MSGTOYOU); reader_rcv_noise = false; } else { if(msg->attr.fmu()) HandleGEvent(EVTT_MSGFROMYOU); } if(msg->attr.loc() and CFG->switches.get(beeplocalmsg)) HandleGEvent(EVTT_MSGISLOCAL); if(AA->Msgn.Count() and CFG->switches.get(highlightunread) and (msg->orig_timesread == 0)) AA->UpdateTimesread(msg); switch(istwit) { case TWIT_KILL: TwitDeleteMsg(msg); continue; case TWIT_SKIP: reader_keycode = ReaderGetKey(); break; default: reader_keycode = ViewMessage(istwit); } reader_topline = BodyView->UpperLine(); if(reader_gen_confirm) ConfirmMsg(); reader_gen_confirm = false; reader_lastcode = reader_keycode; switch(reader_keycode) { case KK_ReadMainMenu: { GMenuNavigate MenuNavigate; int finaltag = MenuNavigate.Run(); if((finaltag != W_ESCPRESS) and (finaltag != -1)) kbput((gkey)finaltag); } break; case KK_ReadSoundkill: HandleGEvent(EVTT_STOPVOICE); break; case KK_ReadChangeAttrs: ChangeAttributes(); break; case KK_ReadChangeAka: ChangeAka(); break; case KK_ReadChangeTagline: ChangeTagline(); break; case KK_ReadChangeOrigin: ChangeOrigin(); break; case KK_ReadChangeUsername: ChangeUsername(); break; case KK_ReadChangeTemplate: ChangeTemplate(); break; case KK_ReadChangeXlatImport: ChangeXlatImport(); break; case KK_ReadLookupDest: LookupNode(msg, "", LOOK_DEST); reader_keyok = true; break; case KK_ReadLookupOrig: LookupNode(msg, "", LOOK_ORIG); reader_keyok = true; break; case KK_ReadFidoRenumber: RenumberArea(); break; case KK_ReadDecreaseMargin: DecMargin(); break; case KK_ReadIncreaseMargin: IncMargin(); break; case KK_ReadToggleStyles: ToggleStyles(); break; case KK_ReadToggleHexdump: AA->attr().hexX(); break; case KK_ReadToggleTwits: ToggleTwits(); break; case KK_ReadToggleMarkRead: ToggleMarkRead(); break; case KK_ReadToggleMark: ToggleMark(); break; case KK_ReadToggleBookMark: ToggleBookMark(); break; case KK_ReadGotoBookMark: GotoBookMark(); break; case KK_ReadGotoPrevMsg: GotoPrevMsg(); break; case KK_ReadGotoNextMsg: case KK_ReadMsgContinue: GotoNextMsg(); break; case KK_ReadGotoFirstMsg: GotoFirstMsg(); break; case KK_ReadGotoLastMsg: GotoLastMsg(); break; case KK_ReadGotoReplies: GotoReplies(); break; case KK_ReadGotoReply1st: GotoReply1st(); break; case KK_ReadGotoReplyNext: GotoReplyNext(); break; case KK_ReadGotoReplyPrev: GotoReplyPrev(); break; case KK_ReadGotoNextUnread: GotoNextUnread(); break; case KK_ReadGotoPrevUnread: GotoPrevUnread(); break; case KK_ReadToggleHiddKlud: ToggleHiddKlud(); break; case KK_ReadToggleKludge: ToggleKludge(); break; case KK_ReadToggleHidden: ToggleHidden(); break; case KK_ReadToggleQuote: ToggleQuote(); break; case KK_ReadToggleROT13: ToggleROT13(); break; case KK_ReadTogglePageBar: TogglePageBar(); break; case KK_ReadToggleRealMsgno: ToggleRealMsgno(); break; case Key_0: case Key_1: case Key_2: case Key_3: case Key_4: case Key_5: case Key_6: case Key_7: case Key_8: case Key_9: case KK_ReadGotoMsgno: GotoMsgno(); break; case KK_ReadMessageList: MessageBrowse(); break; case KK_ReadThreadtree: MsgThreadlist(); break; case KK_ReadDosShell: DosShell(); reader_keyok = YES; break; case KK_ReadFindAll: FindAll(msg, reader_topline, reader_keyok); break; case KK_ReadFindHeader: FindHdr(msg, reader_topline, reader_keyok); break; case KK_ReadSearch: AdvancedSearch(msg, reader_topline, reader_keyok); break; case KK_ReadFileRequest: FileRequest(msg); break; case KK_ReadNewMsg: NewMsg(); break; case KK_ReadMoveQuoteMsg: OtherAreaQuoteMsg(); break; case KK_ReadMoveCommentMsg: OtherAreaCommentMsg(); break; case KK_ReadQuoteBuf: QuoteBuf(msg); break; case KK_ReadQuoteMsg: QuoteMsg(); break; case KK_ReadCommentMsg: CommentMsg(); break; case KK_ReadReplyMsg: ReplyMsg(); break; case KK_ReadChangeMsg: ChangeMsg(); break; case KK_ReadCopyMoveForward: CopyMoveForward(); break; case KK_ReadDeleteMsg: AA->DelMsg(); break; case KK_ReadMakeUserlist: MakeUserList(); break; case KK_ReadMakePathreport: MakePathreport(); break; case KK_ReadWriteMsg: WriteMsg(msg); break; case KK_ReadMarkingOptions: MarkingOptions(); break; case KK_ReadGotoPrevArea: PrevArea(); break; case KK_ReadGotoNextArea: NextArea(); break; case KK_ReadQuitNow: QuitNow(); break; case KK_ReadAskExit: ExitAsk(); break; case KK_ReadNewArea: NewArea(); break; case KK_ReadTouchNetscan: TouchNetscan(); reader_keyok = true; break; case KK_ReadTouchSemaphore: TouchSemaphore(); reader_keyok = true; break; case KK_ReadExternUtilMenu: ExternUtilMenu(); break; case KK_ReadAddressbookAdd: { GMenuDomarks MenuDomarks; int source = AA->Mark.Count() ? MenuDomarks.Run(LNG->Addressbook) : WRITE_CURRENT; if(source != WRITE_QUIT) { if(source == WRITE_MARKED) { for(uint n=0; nMark.Count(); n++) { AA->LoadMsg(msg, AA->Mark[n], CFG->dispmargin-(int)CFG->switches.get(disppagebar)); update_addressbook(msg, true, true); } } else // Write current update_addressbook(msg, true, true); } } break; case KK_ReadUserbase: edit_addressbook(msg); break; case KK_ReadUUDecode: UUDecode(msg); break; case KK_ExternUtil01: reader_keyok = ExternUtil(msg, 1); break; case KK_ExternUtil02: reader_keyok = ExternUtil(msg, 2); break; case KK_ExternUtil03: reader_keyok = ExternUtil(msg, 3); break; case KK_ExternUtil04: reader_keyok = ExternUtil(msg, 4); break; case KK_ExternUtil05: reader_keyok = ExternUtil(msg, 5); break; case KK_ExternUtil06: reader_keyok = ExternUtil(msg, 6); break; case KK_ExternUtil07: reader_keyok = ExternUtil(msg, 7); break; case KK_ExternUtil08: reader_keyok = ExternUtil(msg, 8); break; case KK_ExternUtil09: reader_keyok = ExternUtil(msg, 9); break; case KK_ExternUtil10: reader_keyok = ExternUtil(msg, 10); break; case KK_ExternUtil11: reader_keyok = ExternUtil(msg, 11); break; case KK_ExternUtil12: reader_keyok = ExternUtil(msg, 12); break; case KK_ExternUtil13: reader_keyok = ExternUtil(msg, 13); break; case KK_ExternUtil14: reader_keyok = ExternUtil(msg, 14); break; case KK_ExternUtil15: reader_keyok = ExternUtil(msg, 15); break; case KK_ExternUtil16: reader_keyok = ExternUtil(msg, 16); break; case KK_ExternUtil17: reader_keyok = ExternUtil(msg, 17); break; case KK_ExternUtil18: reader_keyok = ExternUtil(msg, 18); break; case KK_ExternUtil19: reader_keyok = ExternUtil(msg, 19); break; case KK_ExternUtil20: reader_keyok = ExternUtil(msg, 20); break; case KK_ExternUtil21: reader_keyok = ExternUtil(msg, 21); break; case KK_ExternUtil22: reader_keyok = ExternUtil(msg, 22); break; case KK_ExternUtil23: reader_keyok = ExternUtil(msg, 23); break; case KK_ExternUtil24: reader_keyok = ExternUtil(msg, 24); break; case KK_AreaShowDel: CFG->showdeleted = not CFG->showdeleted; reader_keyok = true; break; case KK_ReadUndefine: reader_keyok = true; break; default: if(not PlayMacro(reader_keycode, KT_R)) { SayBibi(); reader_keyok = true; } } } while(reader_keyok); } if(gkbd.quitall) break; } while(not reader_done); if(AA->isopen()) AA->Close(); } while(not reader_finished and not gkbd.quitall); HeaderView->Destroy(); BodyView->Destroy(); } ResetMsg(msg); throw_free(msg); HandleGEvent(EVTT_EXIT); } // ------------------------------------------------------------------ // Determine if the message has replies ulong MsgHasReplies(GMsg* msg) { if(msg->link.first()) return msg->link.first(); for(int n=0; nlink.list_max(); n++) if(msg->link.list(n)) return msg->link.list(n); if(msg->link.next()) return msg->link.next(); return 0; } // ------------------------------------------------------------------ // Determine if the message is of the "twit" type int MsgIsTwit(GMsg* msg, bool& istwitto, bool& istwitsubj) { bool istwit = false; istwitto = istwitsubj = false; // Check for twit names vector::iterator tn; for(tn = CFG->twitname.begin(); tn != CFG->twitname.end(); tn++) { if(msg->orig.match(tn->addr)) { if(*tn->name == NUL or strwild(msg->By(), tn->name)) { istwit = true; break; } } if(CFG->switches.get(twitto)) { if(msg->dest.match(tn->addr)) { if(*tn->name == NUL or strwild(msg->To(), tn->name)) { istwitto = true; istwit = true; break; } } } } // Check for twit subjects if(not istwit) { gstrarray::iterator n; for(n = CFG->twitsubj.begin(); n != CFG->twitsubj.end(); n++) { if(striinc(n->c_str(), msg->re)) { istwitsubj = true; istwit = true; break; } } } if(not istwit) return NOT_TWIT; if(AA->Twitmode() == TWIT_BLANK) return TWIT_BLANK; if(AA->Twitmode() == TWIT_KILL) return TWIT_KILL; if(AA->Twitmode() == TWIT_IGNORE) return TWIT_SKIP; if((AA->Twitmode() == TWIT_SKIP) and not msg->attr.tou()) return TWIT_SKIP; return TWIT_SHOW; } // ------------------------------------------------------------------ int LoadMessage(GMsg* msg, int margin) { GFTRK("LoadMessage"); if(AA->LoadMsg(msg, AA->Msgn.CvtReln(AA->lastread()), margin)) { // Mark message as received, if it is for us if(msg->msgno and not AA->attr().hex()) { for(vector::iterator n = CFG->username.begin(); n != CFG->username.end(); n++) { // Check TO: if(msg->dest.match(n->addr)) { if(strieql(n->name, msg->To())) { if(n->addr.net != GFTN_ALL or msg->dest.net == 0 or not AA->isnet()) msg->attr.tou1(); // Set highlight mark else { for(vector::iterator x = CFG->aka.begin(); x != CFG->aka.end(); x++) { if(msg->dest.match(x->addr)) { msg->attr.tou1(); // Set highlight mark break; } } } } } // Check FROM: if(msg->orig.match(n->addr)) { if(strieql(n->name, msg->by)) { if(n->addr.net != GFTN_ALL or msg->orig.net == 0 or not AA->isnet()) msg->attr.fmu1(); // Set highlight mark else { for(vector::iterator x = CFG->aka.begin(); x != CFG->aka.end(); x++) { if(msg->orig.match(x->addr)) { msg->attr.fmu1(); // Set highlight mark break; } } } } } } if(*AA->Internetaddress()) { if(strieql(msg->to, AA->Internetaddress()) or strieql(msg->idest, AA->Internetaddress())) msg->attr.tou1(); } if(msg->attr.tou()) { reader_rcv_noise = 1; if(not msg->attr.rcv()) { // Have we seen it? time_t a = time(NULL); struct tm *tp = gmtime(&a); tp->tm_isdst = -1; time_t b = mktime(tp); msg->received = a + a - b; // Get current date msg->attr.rcv1(); // Mark as received reader_rcv_noise++; } else if(not CFG->switches.get(rcvdisablescfm)) reader_rcv_noise++; } // Touch the msg if it's the first time received if(reader_rcv_noise > 1) msg->attr.upd1(); // Tell SaveHdr to "touch" the time stamp of the *.MSG file // Update the "Times Read" field msg->orig_timesread = msg->timesread++; if(reader_rcv_noise > 1) { GMsg* tmsg = (GMsg*) throw_calloc(1, sizeof(GMsg)); AA->LoadHdr(tmsg, msg->msgno, false); tmsg->attr = msg->attr; tmsg->orig_timesread = msg->orig_timesread; tmsg->received = msg->received; AA->SaveHdr(GMSG_UPDATE, tmsg); throw_free(tmsg); } msg->attr.upd0(); if((reader_rcv_noise > 1) and AA->isnet() and (msg->attr.cfm() or msg->attr.rrq())) reader_gen_confirm = true; } } else { msg->txt = throw_strdup(""); } GFTRK(NULL); return true; } // ------------------------------------------------------------------ uint next_msg(int direction) { uint relno = 0; if(direction == DIR_NEXT) { if(AA->lastread() < AA->Msgn.Count()) { AA->set_lastread(AA->lastread()+1); relno = AA->lastread(); } } else { if(1 < AA->lastread()) { AA->set_lastread(AA->lastread()-1); relno = AA->lastread(); } } return relno; } // ------------------------------------------------------------------ void ToggleMarkRead() { if(AA->PMrk.Tags()) { AA->isreadpm = not AA->isreadpm; if(AA->isreadpm) { bool is_marked = false; if(AA->PMrk.Find(reader_msg->msgno)) is_marked = true; if(not is_marked) NextMarkedmsg(reader_direction, reader_msg); } } else if(AA->Mark.Count()) { AA->isreadmark = not AA->isreadmark; if(AA->isreadmark) { bool is_marked = false; if(AA->Mark.Find(reader_msg->msgno)) is_marked = true; if(not is_marked) NextMarkedmsg(reader_direction, reader_msg); } } else if(AA->isreadmark) AA->isreadmark = false; else reader_keyok = true; } // ------------------------------------------------------------------ void GotoBookMark() { if(AA->bookmark) { ulong temp = reader_msg->msgno; AA->set_lastread(AA->Msgn.ToReln(AA->bookmark)); AA->bookmark = temp; } else { reader_keyok = true; SayBibi(); } } // ------------------------------------------------------------------ void GoNextMsg() { GMenuNewarea MenuNewarea; int mode; if(AA->isreadmark or AA->isreadpm) { if(not NextMarkedmsg(reader_direction, reader_msg)) { if((mode = MenuNewarea.Run()) != 0) { NewArea(mode == 2); return; } reader_keyok = true; } } else if(not next_msg(reader_direction)) { if((mode = MenuNewarea.Run())!=0) { if(AA->Msgn.ToReln(reader_msg->msgno) == 1) if(reader_direction == DIR_PREV) AA->set_lastread(0); NewArea(mode == 2); return; } reader_keyok = true; } } // ------------------------------------------------------------------ void GotoPrevMsg() { reader_direction = DIR_PREV; GoNextMsg(); } // ------------------------------------------------------------------ void GotoNextMsg() { reader_direction = DIR_NEXT; GoNextMsg(); } // ------------------------------------------------------------------ void GotoFirstMsg() { reader_direction = DIR_NEXT; if(AA->isreadpm) AA->set_lastread(AA->PMrk.ToReln(AA->PMrk[0])); else if(AA->isreadmark) AA->set_lastread(AA->Msgn.ToReln(AA->Mark[0])); else AA->set_lastread(AA->Msgn.Count() ? 1 : 0); } // ------------------------------------------------------------------ void GotoLastMsg() { reader_direction = DIR_PREV; if(AA->isreadpm) AA->set_lastread(AA->Msgn.ToReln(AA->PMrk[AA->PMrk.Tags()-1])); else if(AA->isreadmark) AA->set_lastread(AA->Msgn.ToReln(AA->Mark[AA->Mark.Count()-1])); else AA->set_lastread(AA->Msgn.Count()); } // ------------------------------------------------------------------ struct ReplySel { uint reln; char isread; char msgno[12]; Name name; Name addr; char written[25]; }; // ------------------------------------------------------------------ void GotoReplies() { reader_direction = DIR_NEXT; GMsg* msg = reader_msg; const int list_max = msg->link.list_max(); char buf[200]; int replies = 0; int cursorbar = -1; uint maxname = 0; uint maxaddr = 0; uint gotolink = 0; uint maxmsgno = 0; uint maxwritten = 0; GMsg* rmsg = (GMsg*)throw_calloc(1, sizeof(GMsg)); ReplySel* rlist = (ReplySel*)throw_calloc(list_max+3, sizeof(ReplySel)); for(int n=0; nlink.list(n-1) : msg->link.next()) : msg->link.first(); uint reln = AA->Msgn.ToReln(msgn); if(reln) { if(CFG->replylinklist == REPLYLINKLIST_FULL) AA->LoadMsg(rmsg, msgn, CFG->dispmargin-(int)CFG->switches.get(disppagebar)); else AA->LoadHdr(rmsg, msgn); rlist[replies].isread = ((0 == rmsg->timesread) and CFG->switches.get(highlightunread)) ? '\x10' : ' '; if((cursorbar == -1) and (0 == rmsg->timesread) and (CFG->switches.get(highlightunread))) cursorbar = replies; rlist[replies].msgno[0] = (n==list_max+1) ? '*' : '+'; ulong msgno = CFG->switches.get(disprealmsgno) ? msgn : reln; sprintf(rlist[replies].msgno+1, "%lu", msgno); maxmsgno = MaxV(maxmsgno, (uint)strlen(rlist[replies].msgno+1)); strcpy(rlist[replies].name, rmsg->by); maxname = MaxV(maxname, (uint)strlen(rlist[replies].name)); if(not AA->isinternet()) { rmsg->orig.make_string(buf); if(*buf) sprintf(rlist[replies].addr, " (%s) ", buf); } maxaddr = MaxV(maxaddr, (uint)strlen(rlist[replies].addr)); strftimei(rlist[replies].written, CFG->disphdrdateset.len, LNG->DateTimeFmt, gmtime(&rmsg->written)); maxwritten = MaxV(maxwritten, (uint)strlen(rlist[replies].written)); rlist[replies].reln = reln; replies++; if(gotolink == 0) gotolink = reln; } } ResetMsg(rmsg); throw_free(rmsg); int selected = 0; if(replies > 1) { uint maxname2 = MAXCOL-16-maxmsgno-maxaddr-maxwritten; maxname = MinV(maxname, maxname2); char** listr = (char**)throw_calloc(list_max+3, sizeof(char*)); int n; for(n=0; nReplies, TCENTER, C_ASKT); update_statusline(LNG->SelectReply); whelppcat(H_GotoReplies); int pick_max = MinV(replies, (MAXROW-10)); if(cursorbar < 0) cursorbar = 0; selected = wpickstr(6, 0, 6+pick_max+1, -1, W_BASK, C_ASKB, C_ASKW, C_ASKS, listr, cursorbar, title_shadow); whelpop(); gotolink = (selected != -1) ? rlist[selected].reln : 0; for(n=0; nset_lastread(gotolink); else { if(selected != -1) SayBibi(); reader_keyok = true; } } // ------------------------------------------------------------------ void GotoReply1st() { reader_direction = DIR_NEXT; uint gotolink = AA->Msgn.ToReln(reader_msg->link.first()); if(gotolink) AA->set_lastread(gotolink); else { SayBibi(); reader_keyok = true; } } // ------------------------------------------------------------------ void GotoReplyNext() { reader_direction = DIR_NEXT; uint gotolink = AA->Msgn.ToReln(reader_msg->link.next()); if(gotolink) AA->set_lastread(gotolink); else { SayBibi(); reader_keyok = true; } } // ------------------------------------------------------------------ void GotoReplyPrev() { reader_direction = DIR_PREV; uint gotolink = AA->Msgn.ToReln(reader_msg->link.to()); if(gotolink) AA->set_lastread(gotolink); else { SayBibi(); reader_keyok = true; } } // ------------------------------------------------------------------ void GotoPrevUnread() { reader_direction = DIR_PREV; w_info(LNG->Wait); GMsg* msg = (GMsg*)throw_calloc(1, sizeof(GMsg)); bool found = false; int prev = AA->lastread(); while(1 < prev) { prev--; AA->LoadHdr(msg, AA->Msgn.CvtReln(prev), false); if(msg->timesread == 0) { AA->set_lastread(prev); found = true; break; } } ResetMsg(msg); throw_free(msg); if(not found) { SayBibi(); reader_keyok = true; } w_info(NULL); } // ------------------------------------------------------------------ void GotoNextUnread() { w_info(LNG->Wait); reader_direction = DIR_NEXT; GMsg* msg = (GMsg*)throw_calloc(1, sizeof(GMsg)); bool found = false; int count = AA->Msgn.Count(); int next = AA->lastread(); while(next < count) { next++; AA->LoadHdr(msg, AA->Msgn.CvtReln(next)); if(msg->timesread == 0) { AA->set_lastread(next); found = true; break; } } ResetMsg(msg); throw_free(msg); if(not found) { SayBibi(); reader_keyok = true; } w_info(NULL); } // ------------------------------------------------------------------