home *** CD-ROM | disk | FTP | other *** search
- /*
- * MultiMail offline mail reader
- * Interface, ShadowedWin, ListWindow
-
- Copyright (c) 1996 Kolossvary Tamas <thomas@tvnet.hu>
- Copyright (c) 1997 John Zero <john@graphisoft.hu>
- Copyright (c) 1999 William McBrine <wmcbrine@clark.net>
-
- Distributed under the GNU General Public License.
- For details, see the file COPYING in the parent directory. */
-
- #include "interfac.h"
-
- #ifdef XCURSES
- const char *XCursesProgramName = MM_NAME;
- #endif
-
- Interface::Interface()
- {
- isoConsole = mm.resourceObject->getInt(Charset);
- lynxNav = mm.resourceObject->getInt(UseLynxNav);
- searchItem = 0;
- #ifdef SIGWINCH
- resized = false;
- # ifndef XCURSES
- signal(SIGWINCH, sigwinchHandler);
- # endif
- #endif
- commandline = abortNow = dontSetAsRead = false;
- unsaved_reply = any_read = false;
- state = nostate;
- }
-
- void Interface::init()
- {
- colorlist.Init();
- taglines.Init();
- addresses.Init();
- alive();
- screen_init();
- }
-
- void Interface::main()
- {
- if (commandline) {
- pktstatus pktret = mm.selectPacket(cmdpktname);
- if (pktret == PKT_OK)
- newpacket();
- else
- fatalError(pkterrmsg(pktret));
- } else
- newstate(packetlist);
- KeyHandle();
- }
-
- Interface::~Interface()
- {
- delete screen;
- touchwin(stdscr);
- refresh();
- leaveok(stdscr, FALSE);
- echo();
- endwin();
- #ifdef __PDCURSES__
- # ifdef XCURSES
- XCursesExit();
- # else
- PDC_set_cursor_mode(curs_start, curs_end);
- # endif
- #endif
- }
-
- void Interface::init_colors()
- {
- for (int back = COLOR_BLACK; back <= (COLOR_WHITE); back++)
- for (int fore = COLOR_BLACK; fore <= (COLOR_WHITE); fore++)
- init_pair((fore << 3) + back, fore, back);
-
- // Color pair 0 cannot be used (argh!), so swap:
-
- init_pair(((COLOR_WHITE) << 3) + (COLOR_WHITE),
- COLOR_BLACK, COLOR_BLACK);
-
- // Of course, now I can't do white-on-white (grey). :-(
- }
-
- void Interface::alive()
- {
- #if defined (__PDCURSES__) && !defined (XCURSES)
- // Preserve the startup cursor mode:
-
- int curs_mode = PDC_get_cursor_mode();
- curs_start = (curs_mode & 0xff00) >> 8;
- curs_end = curs_mode & 0xff;
- #endif
- initscr();
- refresh();
- start_color();
- init_colors();
- cbreak();
- noecho();
- nonl();
-
- //mousemask(BUTTON1_CLICKED, 0);
- //mouse_set(BUTTON1_PRESSED);
-
- #if defined (__PDCURSES__) && defined (__RSXNT__)
- /* With the RSXNT port, the keyboard check after printing each
- line makes output very slow, unless we disable line break
- optimization.
- */
- typeahead(-1);
- #endif
- }
-
- void Interface::screen_init()
- {
- // Make a new background window, fill it with ACS_BOARD characters:
-
- screen = new Win(LINES, COLS, 0, C_SBACK | ACS_BOARD);
-
- if ((COLS < 60) || (LINES < 20))
- fatalError("A screen at least 60x20 is required");
-
- // Border and title:
-
- char tmp[80];
- sprintf(tmp, MM_TOPHEADER, MM_NAME, MM_MAJOR, MM_MINOR);
- screen->boxtitle(C_SBORDER, tmp, emph(C_SBACK));
-
- // Help window area:
-
- screen->attrib(C_SSEPBOTT);
- screen->horizline(LINES - 5, COLS - 2);
- screen->delay_update();
- }
-
- const char *Interface::pkterrmsg(pktstatus pktret)
- {
- return (pktret == PKT_UNFOUND) ? "Could not open packet" :
- ((pktret == UNCOMP_FAIL) ? "Could not uncompress packet" :
- "Packet type not recognized");
- }
-
- int Interface::WarningWindow(const char *warning, const char **selectors,
- int items)
- {
- static const char *yesno[] = { "Yes", "No" };
- const char **p;
-
- int x, y, z, itemlen, c, curitem, def_val = 0;
- bool result = false;
-
- if (!selectors)
- selectors = yesno; // default options
-
- // Calculate the window width and maximum item width:
-
- for (p = selectors, itemlen = 0, curitem = 0; curitem < items;
- curitem++, p++) {
- z = strlen(*p);
- if (z > itemlen)
- itemlen = z;
- }
- itemlen += 3;
- x = itemlen * (items + 1);
- z = strlen(warning);
- if ((z + 4) > x)
- x = z + 4;
-
- ShadowedWin warn(7, x, (LINES >> 1) - 4, C_WTEXT);
- warn.put(2, (x - z) >> 1, warning);
-
- y = x / (items + 1);
-
- // Display each item:
-
- for (p = selectors, curitem = 0; curitem < items; curitem++) {
- x = (curitem + 1) * y - (strlen(*p) >> 1);
-
- warn.attrib(C_WTEXT);
- warn.put(4, x + 1, *p + 1);
-
- warn.attrib(C_WTEXTHI);
- warn.put(4, x, **p++);
- }
-
- // Main loop -- highlight selected item and process keystrokes:
-
- while (!result) {
- for (p = selectors, curitem = 0; curitem < items;
- curitem++, p++) {
- z = strlen(*p);
- x = (curitem + 1) * y - (z >> 1);
- warn.put(4, x - 1, (def_val == curitem) ? '[' : ' ');
- warn.put(4, x + z, (def_val == curitem) ? ']' : ' ');
- }
- warn.cursor_on(); // For SIGWINCH
- warn.update();
- warn.cursor_off();
-
- c = warn.inkey();
-
- for (p = selectors, curitem = 0; (curitem < items) &&
- !result; curitem++, p++)
- if ((c == tolower(**p)) || (c == toupper(**p))) {
- def_val = curitem;
- result = true;
- }
-
- if (!result)
- switch (c) {
- case KEY_LEFT:
- if (!def_val)
- def_val = items;
- def_val--;
- break;
- case KEY_RIGHT:
- case 9:
- if (++def_val == items)
- def_val = 0;
- break;
- case MM_ENTER:
- result = true;
- }
- }
- return (items - 1) - def_val; // the order is inverted
- }
-
- int Interface::savePrompt(const char *prompt1, char *filename)
- {
- ShadowedWin question(5, 60, (LINES >> 1) - 3, C_LLSAVEBORD);
- question.attrib(C_LLSAVETEXT);
- question.put(1, 2, prompt1);
- question.put(2, 2, "<");
- question.put(2, 57, ">");
- question.put(3, 38, "<ESC> to cancel");
-
- return question.getstring(2, 3, filename, 54, C_LLSAVETEXT,
- C_LLSAVEGET);
- }
-
- void Interface::nonFatalError(const char *warn)
- {
- static const char *ok[] = { "OK" };
-
- WarningWindow(warn, ok, 1);
- redraw();
- }
-
- statetype Interface::active()
- {
- return state;
- }
-
- statetype Interface::prevactive()
- {
- return prevstate;
- }
-
- void Interface::oldstate(statetype n)
- {
- if (n != nostate)
- helpwindow.Delete();
-
- switch (n) {
- case packetlist:
- packets.Delete();
- break;
- case arealist:
- areas.Delete();
- break;
- case letterlist:
- letters.Delete();
- break;
- case littlearealist:
- areas.Select();
- case address:
- case tagwin:
- currList->Delete();
- oldstate(prevstate);
- break;
- case letter_help:
- case letter:
- letterwindow.Delete();
- break;
- case ansi_help:
- case ansiwin:
- ansiwindow.Delete();
- default:;
- }
- }
-
- void Interface::helpreset(statetype oldst)
- {
- screen->wtouch();
- if ((oldst != state) && (prevstate != state))
- helpwindow.baseReset();
- }
-
- void Interface::newstate(statetype n)
- {
- statetype oldst = state;
- state = n;
-
- switch (n) {
- case packetlist:
- helpreset(oldst);
- currList = &packets;
- packets.MakeActive();
- break;
- case arealist:
- helpreset(oldst);
- currList = &areas;
- areas.MakeActive();
- break;
- case letterlist:
- helpreset(oldst);
- currList = &letters;
- letters.MakeActive();
- break;
- case letter:
- letterwindow.MakeActive(oldst == letterlist);
- break;
- case letter_help:
- letterwindow.MakeActive(false);
- break;
- case littlearealist:
- newstate(prevstate);
- state = littlearealist;
- currList = &littleareas;
- littleareas.MakeActive();
- break;
- case address:
- newstate(prevstate);
- state = address;
- currList = &addresses;
- addresses.MakeActive(addrparm);
- break;
- case tagwin:
- newstate(prevstate);
- state = tagwin;
- currList = &taglines;
- taglines.MakeActive();
- break;
- case ansi_help:
- case ansiwin:
- ansiwindow.MakeActive();
- default:;
- }
-
- helpwindow.MakeActive();
- }
-
- void Interface::changestate(statetype n)
- {
- oldstate(state);
- newstate(n);
- }
-
- void Interface::redraw()
- {
- changestate(state);
- }
-
- void Interface::newpacket()
- {
- static const char *keepers[] = {"Save", "Kill"};
- unsaved_reply = any_read = false;
-
- if (mm.checkForReplies() &&
- !WarningWindow("Existing replies found:", keepers))
- mm.deleteReplies();
- mm.openReply();
-
- mm.areaList = new area_list(&mm);
- if (mm.getOffConfig()) {
- mm.areaList->relist();
- mm.areaList->relist();
- }
- areas.FirstUnread();
- changestate(arealist);
-
- bulletins = mm.getBulletins();
- if (bulletins)
- if (WarningWindow("View bulletins?")) {
- file_header **a = bulletins;
- while (a && *a) {
- switch (ansiFile(*a)) {
- case 1:
- a++;
- break;
- case -1:
- if (a != bulletins)
- a--;
- break;
- default:
- a = 0;
- }
- }
- } else
- changestate(arealist);
-
- newFiles = mm.getFileList();
- if (newFiles)
- if (WarningWindow("View new files list?"))
- ansiFile(newFiles, "New files");
- else
- changestate(arealist);
- }
-
- bool Interface::select()
- {
- pktstatus pktret;
-
- switch (state) {
- case packetlist:
- pktret = packets.OpenPacket();
- if (pktret == PKT_OK)
- newpacket();
- else
- if (pktret != NEW_DIR)
- nonFatalError(pkterrmsg(pktret));
- break;
- case arealist:
- areas.Select();
- if (mm.areaList->getNoOfLetters() > 0) {
- mm.areaList->getLetterList();
- letters.FirstUnread();
- changestate(letterlist);
- }
- break;
- case letterlist:
- letters.Select();
- changestate(letter);
- break;
- case letter:
- letterwindow.Next();
- break;
- case littlearealist:
- case address:
- currList->KeyHandle('\n');
- case tagwin:
- Key = '\n';
- case ansiwin:
- case ansi_help:
- case letter_help:
- return back();
- default:;
- }
- return false;
- }
-
- bool Interface::back()
- {
- switch (state) {
- case packetlist:
- if ((Key == KEY_LEFT) && !packets.back())
- return false;
- if (abortNow || WarningWindow("Do you really want to quit?")) {
- oldstate(state);
- return true;
- } else
- redraw();
- break;
- case arealist:
- if (any_read) {
- if (mm.resourceObject->get(AutoSaveRead) ||
- WarningWindow("Save lastread pointers?"))
- save_read();
- }
- if (unsaved_reply)
- if (mm.resourceObject->get(AutoSaveReplies) ||
- WarningWindow(
- "The REPLY area has changed. Save changes?"))
- create_reply_packet();
- mm.Delete();
- if (abortNow || commandline) {
- oldstate(state);
- return true;
- } else
- changestate(packetlist);
- break;
- case letterlist:
- delete mm.letterList;
- case letter:
- case letter_help:
- case ansi_help:
- changestate((statetype)(state - 1));
- if (abortNow)
- return back();
- break;
- case littlearealist:
- case address:
- case tagwin:
- case ansiwin:
- changestate(prevstate);
- return abortNow ? back() : true;
- default:;
- }
- return false;
- }
-
- #ifdef SIGWINCH
- void Interface::sigwinch()
- {
- oldstate(state);
-
- delete screen;
- # ifdef XCURSES
- resize_term(0, 0);
- # else
- endwin();
- initscr();
- refresh();
- # endif
- screen_init();
- newstate(state);
- resized = false;
- }
-
- void Interface::setResized()
- {
- resized = true;
- }
- #endif
-
- void Interface::kill_letter()
- {
- if (WarningWindow(
- "Are you sure you want to delete this letter?")) {
-
- mm.areaList->killLetter(mm.letterList->getMsgNum());
- setUnsaved();
-
- changestate(letterlist);
- if (!mm.areaList->getNoOfLetters())
- back();
- } else
- redraw();
- }
-
- void Interface::create_reply_packet()
- {
- static int lines = mm.resourceObject->getInt(MaxLines);
- if (lines)
- letterwindow.SplitAll(lines);
- if (mm.makeReply())
- unsaved_reply = false;
- else {
- redraw();
- nonFatalError("Warning: Unable to create reply packet!");
- }
- }
-
- void Interface::save_read()
- {
- if (mm.saveRead())
- any_read = false;
- else {
- redraw();
- nonFatalError("Could not save lastread pointers");
- }
- }
-
- void Interface::addressbook(bool NoEnter)
- {
- prevstate = state;
- addrparm = NoEnter;
- changestate(address);
- KeyHandle();
- }
-
- bool Interface::Tagwin()
- {
- prevstate = state;
- changestate(tagwin);
- KeyHandle();
- switch (Key) {
- case MM_ENTER:
- return true;
- }
- return false;
- }
-
- int Interface::ansiLoop(const char *source, const char *title)
- {
- ansiwindow.set(source, title);
- return ansiCommon();
- }
-
- int Interface::ansiFile(file_header *f, const char *title)
- {
- ansiwindow.setFile(f, title);
- return ansiCommon();
- }
-
- int Interface::ansiCommon()
- {
- prevstate = state;
- changestate(ansiwin);
- KeyHandle();
- switch (Key) {
- case KEY_LEFT:
- if (lynxNav)
- return 0;
- case MM_MINUS:
- return -1;
- case KEY_RIGHT:
- case MM_ENTER:
- case MM_PLUS:
- return 1;
- }
- return 0;
- }
-
- int Interface::areaMenu()
- {
- prevstate = state;
- changestate(littlearealist);
- KeyHandle();
- return littleareas.getArea();
- }
-
- void Interface::setUnsaved()
- {
- unsaved_reply = true;
- if (mm.resourceObject->get(AutoSaveReplies))
- create_reply_packet();
- }
-
- void Interface::setUnsavedNoAuto()
- {
- unsaved_reply = true;
- }
-
- void Interface::setAnyRead()
- {
- any_read = true;
- }
-
- bool Interface::dontRead()
- {
- return dontSetAsRead;
- }
-
- void Interface::searchNext()
- {
- if (searchItem) {
- // We should only continue if the search was started in an
- // appropriate state with respect to the current state.
-
- bool stateok = false;
-
- switch (state) {
- case letter:
- case letterlist:
- case arealist:
- stateok = ((int) state >= (int) searchstate);
- break;
- default:
- stateok = (state == searchstate);
- }
-
- if (stateok) {
- searchret result = False;
- dontSetAsRead = true;
-
- bool restorepos = (s_oldpos == -1);
-
- ShadowedWin searchsay(3, 31, (LINES >> 1) - 2, C_WTEXT);
- searchsay.put(1, 2, "Searching (ESC to abort)...");
- searchsay.update();
-
- switch (state) {
- case letter:
- if (restorepos) {
- s_oldpos = letterwindow.getPos();
- letterwindow.setPos(-1);
- }
- result = letterwindow.search(searchItem);
- if ((result != True) && restorepos)
- letterwindow.setPos(s_oldpos);
- break;
- case ansiwin:
- if (restorepos) {
- s_oldpos = ansiwindow.getPos();
- ansiwindow.setPos(-1);
- }
- result = ansiwindow.search(searchItem);
- if ((result != True) && restorepos)
- ansiwindow.setPos(s_oldpos);
- break;
- default:
- if (restorepos) {
- s_oldpos = currList->getActive();
- currList->setActive(-1);
- }
- result = currList->search(searchItem,
- searchmode);
- if ((result != True) && restorepos)
- currList->setActive(s_oldpos);
- }
-
- dontSetAsRead = false;
-
- if (result == False)
- if (state == searchstate) {
- redraw();
- nonFatalError("No more matches");
- } else {
- if (state == letterlist)
- delete mm.letterList;
- else
- if (state == arealist)
- mm.Delete();
- changestate((statetype)
- ((int) state - 1));
- searchNext();
- }
- else
- if (result == Abort) {
- if (state == searchstate)
- redraw();
- else
- while (state != searchstate)
- changestate((statetype)
- ((int) state - 1));
- nonFatalError("Search aborted");
- } else
- redraw();
-
- }
- }
- }
-
- void Interface::searchSet()
- {
- static const char *searchopts[] = { "Pkt list", "Area list",
- "Headers", "Full text", "Quit" };
- static char item[80];
-
- bool inPackets = (state == packetlist);
- bool hasAreas = (state == arealist) || inPackets;
- if (hasAreas || (state == letterlist)) {
- searchmode = WarningWindow("Search which?", searchopts +
- !hasAreas + !inPackets, 3 + hasAreas + inPackets);
-
- if (!searchmode) {
- searchItem = 0;
- return;
- }
- }
- if (!savePrompt("Search for:", item))
- *item = '\0';
-
- searchItem = *item ? item : 0;
- searchstate = state;
- s_oldpos = -1;
- }
-
- void Interface::setKey(int newkey)
- {
- ungetch(newkey);
- }
-
- bool Interface::fromCommandLine(const char *pktname)
- {
- char *s = strchr(pktname, '/');
- if (!s)
- s = strchr(pktname, '\\');
- if (!s) {
- s = new char[strlen(pktname) + 2];
- sprintf(s, "./%s", pktname);
- } else
- s = strdup(pktname);
- cmdpktname = s;
- commandline = true;
- main();
- delete[] s;
- state = nostate;
- return !abortNow;
- }
-
- void Interface::KeyHandle() // Main loop
- {
- bool end = false;
-
- while (!(end || abortNow)) {
- #ifdef SIGWINCH
- if (resized)
- sigwinch();
- #endif
- doupdate();
- Key = screen->inkey();
- #ifdef XCURSES
- resized = is_termresized();
- #endif
-
- if (((state == letter_help) || (state == ansi_help))
- #ifdef SIGWINCH
- && !resized
- #endif
- ) {
- abortNow = (Key == 24);
- back();
- } else {
- if ((Key >= 'a') && (Key <= 'z'))
- Key = toupper(Key);
-
- switch (Key) {
- //case KEY_MOUSE:
- // beep();
- // break;
- case 24: // ^X
- abortNow = true;
- case 'Q':
- case 27: // escape
- case KEY_BACKSPACE:
- end = back();
- break;
- case MM_ENTER:
- end = select();
- break;
- #if defined (__MSDOS__) || defined (__EMX__)
- case 26: // ^Z
- shellout();
- break;
- #endif
- case MM_SLASH:
- searchSet();
- redraw();
- case '.':
- case '>':
- searchNext();
- break;
- case 'A':
- switch (state) {
- case packetlist:
- case arealist:
- case letterlist:
- case letter:
- addressbook(true);
- break;
- case address:
- case tagwin:
- currList->KeyHandle(Key);
- default:;
- }
- break;
- case 20: // ^T
- switch (state) {
- case packetlist:
- case arealist:
- case letterlist:
- case letter:
- Tagwin();
- default:;
- }
- break;
- case 'C':
- isoConsole = !isoConsole;
- redraw();
- break;
- case 'O':
- switch (state) {
- case packetlist:
- case arealist:
- case letterlist:
- helpwindow.baseNext();
- break;
- case letter:
- letterwindow.KeyHandle(Key);
- default:;
- }
- break;
- case '!':
- case KEY_F(2):
- switch (state) {
- case arealist:
- case letterlist:
- case letter:
- if (!mm.checkForReplies() ||
- WarningWindow(
- "This will overwrite the existing reply packet. Continue?"))
- create_reply_packet();
- redraw();
- default:;
- }
- break;
- default:
- switch (state) {
- case letter:
- letterwindow.KeyHandle(Key);
- break;
- case ansiwin:
- switch (Key) {
- case KEY_RIGHT:
- if (lynxNav)
- break;
- case KEY_LEFT:
- case MM_PLUS:
- case MM_MINUS:
- end = back();
- break;
- default:
- ansiwindow.KeyHandle(Key);
- }
- break;
- default:
- end = currList->KeyHandle(Key);
- }
- }
- }
- }
- }
-