ncurses.cpp

Go to the documentation of this file.
00001 /*****************************************************************************
00002  * Copyright (C) 2005 Eliot
00003  * Authors: Olivier Teuliere  <ipkiss@via.ecp.fr>
00004  *
00005  * This program is free software; you can redistribute it and/or modify
00006  * it under the terms of the GNU General Public License as published by
00007  * the Free Software Foundation; either version 2 of the License, or
00008  * (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software
00017  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  *****************************************************************************/
00019 
00020 #include "config.h"
00021 #if ENABLE_NLS
00022 #   include <libintl.h>
00023 #   define _(String) gettext(String)
00024 #else
00025 #   define _(String) String
00026 #endif
00027 
00028 #include <ctype.h>
00029 #include <fstream>
00030 
00031 #include "ncurses.h"
00032 #include "dic.h"
00033 #include "dic_search.h"
00034 #include "game_factory.h"
00035 #include "training.h"
00036 #include "duplicate.h"
00037 #include "freegame.h"
00038 #include "player.h"
00039 
00040 using namespace std;
00041 
00042 
00043 CursesIntf::CursesIntf(WINDOW *win, Game& iGame)
00044     : m_win(win), m_game(&iGame), m_state(DEFAULT), m_dying(false),
00045     m_boxStart(0), m_boxLines(0), m_boxLinesData(0), m_boxY(0),
00046     m_showDots(false)
00047 {
00048 }
00049 
00050 
00051 CursesIntf::~CursesIntf()
00052 {
00053     GameFactory::Instance()->releaseGame(*m_game);
00054 }
00055 
00056 
00057 void CursesIntf::drawBox(WINDOW *win, int y, int x, int h, int w,
00058                          const string& iTitle)
00059 {
00060     if (w > 3 && h > 2)
00061     {
00062         int i_len = iTitle.size();
00063 
00064         if (i_len > w - 2) i_len = w - 2;
00065 
00066         mvwaddch(win, y, x,    ACS_ULCORNER);
00067         mvwhline(win, y, x+1,  ACS_HLINE, ( w-i_len-2)/2);
00068         mvwprintw(win,y, x+1+(w-i_len-2)/2, "%s", iTitle.c_str());
00069         mvwhline(win, y, x+(w-i_len)/2+i_len,
00070                  ACS_HLINE, w - 1 - ((w-i_len)/2+i_len));
00071         mvwaddch(win, y, x+w-1,ACS_URCORNER);
00072 
00073         mvwvline(win, y+1, x,     ACS_VLINE, h-2);
00074         mvwvline(win, y+1, x+w-1, ACS_VLINE, h-2);
00075 
00076         mvwaddch(win, y+h-1, x,     ACS_LLCORNER);
00077         mvwhline(win, y+h-1, x+1,   ACS_HLINE, w - 2);
00078         mvwaddch(win, y+h-1, x+w-1, ACS_LRCORNER);
00079     }
00080 }
00081 
00082 
00083 void CursesIntf::clearRect(WINDOW *win, int y, int x, int h, int w)
00084 {
00085     for (int i = 0; i < h; i++)
00086     {
00087         mvwhline(win, y + i, x, ' ', w);
00088     }
00089 }
00090 
00091 
00092 void CursesIntf::boxPrint(WINDOW *win, int y, int x, const char *fmt, ...)
00093 {
00094     if (y < m_boxStart || y - m_boxStart >= m_boxLines)
00095         return;
00096 
00097     va_list vl_args;
00098     char *buf = NULL;
00099     va_start(vl_args, fmt);
00100     vasprintf(&buf, fmt, vl_args);
00101     va_end(vl_args);
00102 
00103     if (buf == NULL)
00104     {
00105         return;
00106     }
00107     mvwprintw(win, m_boxY + y - m_boxStart, x, "%s", buf);
00108 }
00109 
00110 
00111 void CursesIntf::drawStatus(WINDOW *win, int y, int x,
00112                             const string& iMessage, bool error)
00113 {
00114     if (error)
00115         wattron(win, COLOR_PAIR(COLOR_YELLOW));
00116     mvwprintw(win, y, x, iMessage.c_str());
00117     whline(win, ' ', COLS - x - 1 - iMessage.size());
00118     if (error)
00119         wattron(win, COLOR_PAIR(COLOR_WHITE));
00120 }
00121 
00122 
00123 void CursesIntf::drawBoard(WINDOW *win, int y, int x) const
00124 {
00125     // Box around the board
00126     drawBox(win, y + 1, x + 3, 17, 47, "");
00127 
00128     // Print the coordinates
00129     for (int i = 0; i < 15; i++)
00130     {
00131         mvwaddch(win, y + i + 2, x + 1,  'A' + i);
00132         mvwaddch(win, y + i + 2, x + 51, 'A' + i);
00133         mvwprintw(win, y,      x + 3 * i + 5, "%d", i + 1);
00134         mvwprintw(win, y + 18, x + 3 * i + 5, "%d", i + 1);
00135     }
00136 
00137     // The board itself
00138     for (int row = 1; row < 16; row++)
00139     {
00140         for (int col = 1; col < 16; col++)
00141         {
00142             // Handle colors
00143             int wm = m_game->getBoard().getWordMultiplier(row, col);
00144             int lm = m_game->getBoard().getLetterMultiplier(row, col);
00145             if (wm == 3)
00146                 wattron(win, COLOR_PAIR(COLOR_RED));
00147             else if (wm == 2)
00148                 wattron(win, COLOR_PAIR(COLOR_MAGENTA));
00149             else if (lm == 3)
00150                 wattron(win, COLOR_PAIR(COLOR_BLUE));
00151             else if (lm == 2)
00152                 wattron(win, COLOR_PAIR(COLOR_CYAN));
00153             else
00154                 wattron(win, COLOR_PAIR(COLOR_WHITE));
00155 
00156             // Empty square
00157             mvwprintw(win, y + row + 1, x + 3 * col + 1, "   ");
00158 
00159             // Now add the letter
00160             char c = m_game->getBoard().getChar(row, col);
00161             if (c)
00162             {
00163                 if (islower(c))
00164                     mvwaddch(win, y + row + 1, x + 3 * col + 2,
00165                              c | A_BOLD | COLOR_PAIR(COLOR_GREEN));
00166                 else
00167                     mvwaddch(win, y + row + 1, x + 3 * col + 2, c);
00168             }
00169             else
00170             {
00171                 // Empty square... should we display a dot?
00172                 if (m_showDots)
00173                     mvwaddch(win, y + row + 1, x + 3 * col + 2, '.');
00174             }
00175         }
00176     }
00177     wattron(win, COLOR_PAIR(COLOR_WHITE));
00178 }
00179 
00180 
00181 void CursesIntf::drawScoresRacks(WINDOW *win, int y, int x) const
00182 {
00183     drawBox(win, y, x, m_game->getNPlayers() + 2, 25, _(" Scores "));
00184     for (int i = 0; i < m_game->getNPlayers(); i++)
00185     {
00186         if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
00187             attron(A_BOLD);
00188         mvwprintw(win, y + i + 1, x + 2,
00189                   _("Player %d: %d"), i, m_game->getPlayer(i).getPoints());
00190         if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
00191             attroff(A_BOLD);
00192     }
00193 
00194     // Distance between the 2 boxes
00195     int yOff = m_game->getNPlayers() + 3;
00196 
00197     drawBox(win, y + yOff, x, m_game->getNPlayers() + 2, 25, _(" Racks "));
00198     for (int i = 0; i < m_game->getNPlayers(); i++)
00199     {
00200         if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
00201             attron(A_BOLD);
00202         mvwprintw(win, y + yOff + i + 1, x + 2,
00203                   _("Player %d: %s"), i, m_game->getPlayerRack(i).c_str());
00204         if (m_game->getMode() != Game::kTRAINING && i == m_game->currPlayer())
00205             attroff(A_BOLD);
00206         // Force to refresh the whole rack
00207         whline(win, ' ', 7 - m_game->getPlayerRack(i).size());
00208     }
00209 
00210     // Display a message when the search is complete
00211     if (m_game->getMode() == Game::kTRAINING &&
00212         static_cast<Training*>(m_game)->getHistory().getSize())
00213     {
00214         mvwprintw(win, y + 2*yOff - 1, x + 2, _("Search complete"));
00215     }
00216     else
00217         mvwhline(win, y + 2*yOff - 1, x + 2, ' ', strlen(_("Search complete")));
00218 }
00219 
00220 
00221 void CursesIntf::drawResults(WINDOW *win, int y, int x)
00222 {
00223     if (m_game->getMode() != Game::kTRAINING)
00224         return;
00225     Training *tr_game = static_cast<Training*>(m_game);
00226 
00227     int h = 17;
00228     drawBox(win, y, x, h, 25, _(" Search results "));
00229     m_boxY = y + 1;
00230     m_boxLines = h - 2;
00231     m_boxLinesData = tr_game->getHistory().getSize();
00232 
00233     int i;
00234     const Results& res = tr_game->getResults();
00235     for (i = m_boxStart; i < res.size() &&
00236                          i < m_boxStart + m_boxLines; i++)
00237     {
00238         const Round &r = res.get(i);
00239         string coord = r.getCoord().toString();
00240         boxPrint(win, i, x + 1, "%3d %s%s %3s",
00241                  r.getPoints(),
00242                  r.getWord().c_str(),
00243                  string(h - 3 - r.getWordLen(), ' ').c_str(),
00244                  coord.c_str());
00245     }
00246     // Complete the list with empty lines, to avoid trails
00247     for (; i < m_boxStart + m_boxLines; i++)
00248     {
00249         boxPrint(win, i, x + 1, string(23, ' ').c_str());
00250     }
00251 }
00252 
00253 
00254 void CursesIntf::drawHistory(WINDOW *win, int y, int x)
00255 {
00256     // To allow pseudo-scrolling, without leaving trails
00257     clear();
00258 
00259     drawBox(win, y, x, LINES - y, COLS - x, _(" History of the game "));
00260     m_boxY = y + 1;
00261     m_boxLines = LINES - y - 2;
00262     m_boxLinesData = m_game->getHistory().getSize();
00263 
00264     // Heading
00265     boxPrint(win, m_boxStart, x + 2,
00266              _(" N |   RACK   |    SOLUTION     | REF | PTS | P | BONUS"));
00267     mvwhline(win, y + 2, x + 2, ACS_HLINE, 55);
00268 
00269     int i;
00270     for (i = m_boxStart + 0; i < m_game->getHistory().getSize() &&
00271                          i < m_boxStart + m_boxLines; i++)
00272     {
00273         const Turn& t = m_game->getHistory().getTurn(i);
00274         const Round& r = t.getRound();
00275         string word = r.getWord();
00276         string coord = r.getCoord().toString();
00277         boxPrint(win, i + 2, x + 2,
00278                  "%2d   %8s   %s%s   %3s   %3d   %1d   %c",
00279                  i + 1, t.getPlayedRack().toString().c_str(), word.c_str(),
00280                  string(15 - word.size(), ' ').c_str(),
00281                  coord.c_str(), r.getPoints(),
00282                  t.getPlayer(), r.getBonus() ? '*' : ' ');
00283     }
00284     mvwvline(win, y + 1, x + 5,  ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00285     mvwvline(win, y + 1, x + 16, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00286     mvwvline(win, y + 1, x + 34, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00287     mvwvline(win, y + 1, x + 40, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00288     mvwvline(win, y + 1, x + 46, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00289     mvwvline(win, y + 1, x + 50, ACS_VLINE, min(i + 2 - m_boxStart, m_boxLines));
00290 }
00291 
00292 
00293 void CursesIntf::drawHelp(WINDOW *win, int y, int x)
00294 {
00295     // To allow pseudo-scrolling, without leaving trails
00296     clear();
00297 
00298     drawBox(win, y, x, LINES - y, COLS - x, _(" Help "));
00299     m_boxY = y + 1;
00300     m_boxLines = LINES - y - 2;
00301 
00302     int n = 0;
00303     boxPrint(win, n++, x + 2, _("[Global]"));
00304     boxPrint(win, n++, x + 2, _("   h, H, ?          Show/hide help box"));
00305     boxPrint(win, n++, x + 2, _("   y, Y             Show/hide history of the game"));
00306     boxPrint(win, n++, x + 2, _("   e, E             Show/hide dots on empty squares of the board"));
00307     boxPrint(win, n++, x + 2, _("   d, D             Check the existence of a word in the dictionary"));
00308     boxPrint(win, n++, x + 2, _("   j, J             Play a word"));
00309     boxPrint(win, n++, x + 2, _("   s, S             Save the game"));
00310     boxPrint(win, n++, x + 2, _("   l, L             Load a game"));
00311     boxPrint(win, n++, x + 2, _("   q, Q             Quit"));
00312     boxPrint(win, n++, x + 2, "");
00313 
00314     boxPrint(win, n++, x + 2, _("[Training mode]"));
00315     boxPrint(win, n++, x + 2, _("   *                Take a random rack"));
00316     boxPrint(win, n++, x + 2, _("   +                Complete the current rack randomly"));
00317     boxPrint(win, n++, x + 2, _("   t, T             Set the rack manually"));
00318     boxPrint(win, n++, x + 2, _("   c, C             Compute all the possible words"));
00319     boxPrint(win, n++, x + 2, _("   r, R             Show/hide search results"));
00320     boxPrint(win, n++, x + 2, "");
00321 
00322     boxPrint(win, n++, x + 2, _("[Duplicate mode]"));
00323     boxPrint(win, n++, x + 2, _("   n, N             Switch to the next human player"));
00324     boxPrint(win, n++, x + 2, "");
00325 
00326     boxPrint(win, n++, x + 2, _("[Free game mode]"));
00327     boxPrint(win, n++, x + 2, _("   p, P             Pass your turn (with or without changing letters)"));
00328     boxPrint(win, n++, x + 2, "");
00329 
00330     boxPrint(win, n++, x + 2, _("[Miscellaneous]"));
00331     boxPrint(win, n++, x + 2, _("   <up>, <down>     Navigate in a box line by line"));
00332     boxPrint(win, n++, x + 2, _("   <pgup>, <pgdown> Navigate in a box page by page"));
00333     boxPrint(win, n++, x + 2, _("   Ctrl-l           Refresh the screen"));
00334 
00335     m_boxLinesData = n;
00336 }
00337 
00338 
00339 void CursesIntf::playWord(WINDOW *win, int y, int x)
00340 {
00341     drawBox(win, y, x, 4, 32, _(" Play a word "));
00342     mvwprintw(win, y + 1, x + 2, _("Played word:"));
00343     mvwprintw(win, y + 2, x + 2, _("Coordinates:"));
00344     wrefresh(win);
00345 
00346     // TRANSLATORS: Align the : when translating "Played word:" and
00347     // "Coordinates:". For example:
00348     // Pl. word   :
00349     // Coordinates:
00350     int l1 = strlen(_("Played word:"));
00351     int l2 = strlen(_("Coordinates:"));
00352     int xOff;
00353     if (l1 > l2)
00354         xOff = l1 + 3;
00355     else
00356         xOff = l2 + 3;
00357 
00358     string word, coord;
00359     if (readString(win, y + 1, x + xOff, 15, word) &&
00360         readString(win, y + 2, x + xOff, 3, coord))
00361     {
00362         int res = m_game->play(coord, word);
00363         if (res)
00364         {
00365             drawStatus(win, LINES - 1, 0, _("Incorrect or misplaced word"));
00366         }
00367     }
00368     m_state = DEFAULT;
00369     clearRect(win, y, x, 4, 32);
00370 }
00371 
00372 
00373 void CursesIntf::checkWord(WINDOW *win, int y, int x)
00374 {
00375     drawBox(win, y, x, 4, 32, _(" Dictionary "));
00376     mvwprintw(win, y + 1, x + 2, _("Enter the word to check:"));
00377     wrefresh(win);
00378 
00379     string word;
00380     if (readString(win, y + 2, x + 2, 15, word))
00381     {
00382         int res = Dic_search_word(m_game->getDic(), word.c_str());
00383         char s[100];
00384         if (res)
00385             snprintf(s, 100, _("The word '%s' exists"), word.c_str());
00386         else
00387             snprintf(s, 100, _("The word '%s' does not exist"), word.c_str());
00388         drawStatus(win, LINES - 1, 0, s);
00389     }
00390     m_state = DEFAULT;
00391     clearRect(win, y, x, 4, 32);
00392 }
00393 
00394 
00395 void CursesIntf::saveGame(WINDOW *win, int y, int x)
00396 {
00397     drawBox(win, y, x, 4, 32, _(" Save the game "));
00398     mvwprintw(win, y + 1, x + 2, _("Enter the file name:"));
00399     wrefresh(win);
00400 
00401     string filename;
00402     if (readString(win, y + 2, x + 2, 28, filename, kFILENAME))
00403     {
00404         ofstream fout(filename.c_str());
00405         char s[100];
00406         if (fout.rdstate() == ios::failbit)
00407         {
00408             snprintf(s, 100, _("Cannot open file %s for writing"),
00409                      filename.c_str());
00410         }
00411         else
00412         {
00413             m_game->save(fout);
00414             fout.close();
00415             snprintf(s, 100, _("Game saved in %s"), filename.c_str());
00416         }
00417         drawStatus(win, LINES - 1, 0, s);
00418     }
00419     m_state = DEFAULT;
00420     clearRect(win, y, x, 4, 32);
00421 }
00422 
00423 
00424 void CursesIntf::loadGame(WINDOW *win, int y, int x)
00425 {
00426     drawBox(win, y, x, 4, 32, _(" Load a game "));
00427     mvwprintw(win, y + 1, x + 2, _("Enter the file name:"));
00428     wrefresh(win);
00429 
00430     string filename;
00431     if (readString(win, y + 2, x + 2, 28, filename, kFILENAME))
00432     {
00433         char s[100];
00434         FILE *fin;
00435         if ((fin = fopen(filename.c_str(), "r")) == NULL)
00436         {
00437             snprintf(s, 100, _("Cannot open file %s for reading"),
00438                      filename.c_str());
00439         }
00440         else
00441         {
00442             Game *loaded = Game::load(fin, m_game->getDic());
00443             if (loaded == NULL)
00444             {
00445                 snprintf(s, 100, _("Invalid saved game"));
00446             }
00447             else
00448             {
00449                 snprintf(s, 100, _("Game loaded"));
00450                 GameFactory::Instance()->releaseGame(*m_game);
00451                 m_game = loaded;
00452             }
00453             fclose(fin);
00454         }
00455         drawStatus(win, LINES - 1, 0, s);
00456     }
00457     m_state = DEFAULT;
00458     clearRect(win, y, x, 4, 32);
00459 }
00460 
00461 
00462 void CursesIntf::passTurn(WINDOW *win, int y, int x, FreeGame &iGame)
00463 {
00464     drawBox(win, y, x, 4, 32, _(" Pass your turn "));
00465     mvwprintw(win, y + 1, x + 2, _("Enter the letters to change:"));
00466     wrefresh(win);
00467 
00468     string letters;
00469     if (readString(win, y + 2, x + 2, 7, letters))
00470     {
00471         int res = iGame.pass(letters, m_game->currPlayer());
00472         if (res)
00473         {
00474             drawStatus(win, LINES - 1, 0, _("Cannot pass the turn"));
00475         }
00476     }
00477     m_state = DEFAULT;
00478     clearRect(win, y, x, 4, 32);
00479 }
00480 
00481 
00482 void CursesIntf::setRack(WINDOW *win, int y, int x, Training &iGame)
00483 {
00484     drawBox(win, y, x, 4, 32, _(" Set rack "));
00485     mvwprintw(win, y + 1, x + 2, _("Enter the new letters:"));
00486     wrefresh(win);
00487 
00488     string letters;
00489     if (readString(win, y + 2, x + 2, 7, letters, kJOKER))
00490     {
00491         iGame.setRackManual(false, letters);
00492     }
00493     m_state = DEFAULT;
00494     clearRect(win, y, x, 4, 32);
00495 }
00496 
00497 
00498 bool CursesIntf::readString(WINDOW *win, int y, int x, int n, string &oString,
00499                             unsigned int flag)
00500 {
00501     int c;
00502     wmove(win, y, x);
00503     curs_set(1);
00504     while ((c = getch()) != 0)
00505     {
00506         if (c == 0x1b )  // Esc
00507         {
00508             curs_set(0);
00509             return false;
00510         }
00511         else if (c == KEY_ENTER || c == 0xD)
00512         {
00513             curs_set(0);
00514             return true;
00515         }
00516         else if (c == 0x0c)  // Ctrl-L
00517         {
00518 //             clear();
00519             redraw(win);
00520             wmove(win, y, x);
00521         }
00522         else if (c == KEY_BACKSPACE && oString.size() > 0)
00523         {
00524             x--;
00525             mvwprintw(win, y, x, " ");
00526             wmove(win, y, x);
00527             oString.erase(oString.size() - 1);
00528         }
00529         else if (isalnum(c) && oString.size() < (unsigned int)n)
00530         {
00531             mvwprintw(win, y, x, "%c", c);
00532             x++;
00533             oString += (char)c;
00534         }
00535         else
00536         {
00537             if (flag & kJOKER && c == '?')
00538             {
00539                 mvwprintw(win, y, x, "%c", c);
00540                 x++;
00541                 oString += (char)c;
00542             }
00543             if (flag & kFILENAME)
00544             {
00545                 if (c == '/' || c == '.' || c == '-' || c == '_' || c == ' ')
00546                 {
00547                     mvwprintw(win, y, x, "%c", c);
00548                     x++;
00549                     oString += (char)c;
00550                 }
00551             }
00552         }
00553 //         else
00554 //             mvwprintw(win, 0, 0, "%3d", c);
00555     }
00556     curs_set(0);
00557     return 0;
00558 }
00559 
00560 
00561 int CursesIntf::handleKeyForGame(int iKey, Training &iGame)
00562 {
00563     switch (iKey)
00564     {
00565         case '*':
00566             iGame.setRackRandom(0, false, Game::RACK_ALL);
00567             return 1;
00568 
00569         case '+':
00570             iGame.setRackRandom(0, false, Game::RACK_NEW);
00571             return 1;
00572 
00573         case 't':
00574         case 'T':
00575             setRack(m_win, 22, 10, iGame);
00576             return 1;
00577 
00578         case 'c':
00579         case 'C':
00580             iGame.search();
00581             return 1;
00582 
00583         default:
00584             return 2;
00585     }
00586 }
00587 
00588 
00589 int CursesIntf::handleKeyForGame(int iKey, Duplicate &iGame)
00590 {
00591     switch (iKey)
00592     {
00593         case 'n':
00594         case 'N':
00595             iGame.nextHumanPlayer();
00596             return 1;
00597 
00598         default:
00599             return 2;
00600     }
00601 }
00602 
00603 
00604 int CursesIntf::handleKeyForGame(int iKey, FreeGame &iGame)
00605 {
00606     switch (iKey)
00607     {
00608         case 'p':
00609         case 'P':
00610             passTurn(m_win, 22, 10, iGame);
00611             return 1;
00612 
00613         default:
00614             return 2;
00615     }
00616 }
00617 
00618 
00619 int CursesIntf::handleKey(int iKey)
00620 {
00621     if (m_state == DEFAULT)
00622     {
00623         int res;
00624         if (m_game->getMode() == Game::kTRAINING)
00625         {
00626             res = handleKeyForGame(iKey, (Training&)*m_game);
00627         }
00628         else if (m_game->getMode() == Game::kDUPLICATE)
00629         {
00630             res = handleKeyForGame(iKey, (Duplicate&)*m_game);
00631         }
00632         else
00633         {
00634             res = handleKeyForGame(iKey, (FreeGame&)*m_game);
00635         }
00636 
00637         if (res != 2)
00638             return res;
00639     }
00640     else // m_state is in {HELP, RESULTS, HISTORY}
00641     {
00642         switch (iKey)
00643         {
00644             case KEY_HOME:
00645                 if (m_boxLinesData <= m_boxLines && m_boxStart > 0)
00646                     return 0;
00647                 m_boxStart = 0;
00648                 return 1;
00649             case KEY_END:
00650                 if (m_boxLinesData <= m_boxLines &&
00651                     m_boxStart < m_boxLinesData - 1)
00652                     return 0;
00653                 m_boxStart = m_boxLinesData - 1;
00654                 return 1;
00655             case KEY_UP:
00656                 if (m_boxLinesData <= m_boxLines || m_boxStart <= 0)
00657                     return 0;
00658                 m_boxStart--;
00659                 return 1;
00660             case KEY_DOWN:
00661                 if (m_boxLinesData <= m_boxLines ||
00662                     m_boxStart >= m_boxLinesData - 1)
00663                     return 0;
00664                 m_boxStart++;
00665                 return 1;
00666             case KEY_PPAGE:
00667                 if (m_boxLinesData <= m_boxLines)
00668                     return 0;
00669                 m_boxStart -= m_boxLines;
00670                 if (m_boxStart < 0)
00671                     m_boxStart = 0;
00672                 return 1;
00673             case KEY_NPAGE:
00674                 if (m_boxLinesData <= m_boxLines)
00675                     return 0;
00676                 m_boxStart += m_boxLines;
00677                 if (m_boxStart > m_boxLinesData - 1)
00678                     m_boxStart = m_boxLinesData - 1;
00679                 return 1;
00680         }
00681     }
00682 
00683     switch (iKey)
00684     {
00685         // Toggle help
00686         case 'h':
00687         case 'H':
00688         case '?':
00689             if (m_state == HELP)
00690                 m_state = DEFAULT;
00691             else
00692                 m_state = HELP;
00693             m_boxStart = 0;
00694             clear();
00695             return 1;
00696 
00697         // Toggle history
00698         case 'y':
00699         case 'Y':
00700             if (m_state == HISTORY)
00701                 m_state = DEFAULT;
00702             else
00703                 m_state = HISTORY;
00704             m_boxStart = 0;
00705             clear();
00706             return 1;
00707 
00708         // Toggle results (training mode only)
00709         case 'r':
00710         case 'R':
00711             if (m_game->getMode() != Game::kTRAINING)
00712                 return 0;
00713             if (m_state == RESULTS)
00714                 m_state = DEFAULT;
00715             else
00716                 m_state = RESULTS;
00717             m_boxStart = 0;
00718             clear();
00719             return 1;
00720 
00721         // Toggle dots display
00722         case 'e':
00723         case 'E':
00724             m_showDots = !m_showDots;
00725             return 1;
00726 
00727         // Check a word in the dictionary
00728         case 'd':
00729         case 'D':
00730             if (m_state != DEFAULT)
00731                 return 0;
00732             checkWord(m_win, 22, 10);
00733             return 1;
00734 
00735         // Play a word
00736         case 'j':
00737         case 'J':
00738             if (m_state != DEFAULT)
00739                 return 0;
00740             playWord(m_win, 22, 10);
00741             return 1;
00742 
00743         // Ctrl-L should clear and redraw the screen
00744         case 0x0c:
00745             clear();
00746             return 1;
00747 
00748         case 'l':
00749         case 'L':
00750             if (m_state != DEFAULT)
00751                 return 0;
00752             loadGame(m_win, 22, 10);
00753             return 1;
00754 
00755         case 's':
00756         case 'S':
00757             if (m_state != DEFAULT)
00758                 return 0;
00759             saveGame(m_win, 22, 10);
00760             return 1;
00761 
00762         // Quit
00763         case 'q':
00764         case 'Q':
00765         case 0x1b: // Esc
00766             m_dying = true;
00767             return 0;
00768 
00769         default:
00770             return 0;
00771     }
00772 }
00773 
00774 
00775 void CursesIntf::redraw(WINDOW *win)
00776 {
00777     if (m_state == DEFAULT)
00778     {
00779         drawScoresRacks(win, 3, 54);
00780         drawBoard(win, 2, 0);
00781     }
00782     else if (m_state == RESULTS)
00783     {
00784         drawResults(win, 3, 54);
00785         drawBoard(win, 2, 0);
00786     }
00787     else if (m_state == HELP)
00788     {
00789         drawHelp(win, 1, 0);
00790     }
00791     else if (m_state == HISTORY)
00792     {
00793         drawHistory(win, 1, 0);
00794     }
00795 
00796     // Title
00797     attron(A_REVERSE);
00798     string mode;
00799     if (m_game->getMode() == Game::kTRAINING)
00800         mode = _("Training mode");
00801     else if (m_game->getMode() == Game::kFREEGAME)
00802         mode = _("Free game mode");
00803     else if (m_game->getMode() == Game::kDUPLICATE)
00804         mode = _("Duplicate mode");
00805     string variant = "";
00806     if (m_game->getVariant() == Game::kJOKER)
00807         variant = string(" - ") + _("Joker game");
00808     string title = "Eliot (" + mode + variant + ") " + _("[h for help]");
00809     mvwprintw(win, 0, 0, title.c_str());
00810     whline(win, ' ', COLS - title.size());
00811     attroff(A_REVERSE);
00812 
00813     wrefresh(win);
00814 }
00815 
00816 
00817 int main(int argc, char ** argv)
00818 {
00819 #ifdef HAVE_SETLOCALE
00820     // Set locale via LC_ALL
00821     setlocale(LC_ALL, "");
00822 #endif
00823 
00824 #if ENABLE_NLS
00825     // Set the message domain
00826     bindtextdomain(PACKAGE, LOCALEDIR);
00827     textdomain(PACKAGE);
00828 #endif
00829 
00830     srand(time(NULL));
00831 
00832     Game *game = GameFactory::Instance()->createFromCmdLine(argc, argv);
00833     if (game == NULL)
00834         return 1;
00835 
00836     game->start();
00837 
00838     // Initialize the ncurses library
00839     WINDOW *wBoard = initscr();
00840     keypad(wBoard, true);
00841     // Take input chars one at a time
00842     cbreak();
00843     // Do not do NL -> NL/CR
00844     nonl();
00845     // Hide the cursor
00846     curs_set(0);
00847 
00848     if (has_colors())
00849     {
00850         start_color();
00851 
00852         // Simple color assignment
00853         init_pair(COLOR_BLACK, COLOR_BLACK, COLOR_BLACK);
00854         init_pair(COLOR_GREEN, COLOR_GREEN, COLOR_BLACK);
00855         init_pair(COLOR_WHITE, COLOR_WHITE, COLOR_BLACK);
00856         init_pair(COLOR_YELLOW, COLOR_YELLOW, COLOR_BLACK);
00857 
00858         init_pair(COLOR_BLUE, COLOR_BLACK, COLOR_BLUE);
00859         init_pair(COLOR_CYAN, COLOR_BLACK, COLOR_CYAN);
00860         init_pair(COLOR_MAGENTA, COLOR_BLACK, COLOR_MAGENTA);
00861         init_pair(COLOR_RED, COLOR_BLACK, COLOR_RED);
00862     }
00863 
00864     // Do not echo
00865     noecho();
00866 
00867     // mainIntf will take care of destroying game for us.
00868     CursesIntf mainIntf(wBoard, *game);
00869     mainIntf.redraw(wBoard);
00870 
00871     while (!mainIntf.isDying())
00872     {
00873         int c = getch();
00874         if (mainIntf.handleKey(c) == 1)
00875         {
00876             mainIntf.redraw(wBoard);
00877         }
00878     }
00879 
00880     delwin(wBoard);
00881 
00882     // Exit the ncurses library
00883     endwin();
00884 
00885     GameFactory::Destroy();
00886 
00887     return 0;
00888 }

Generated on Thu Dec 29 02:01:15 2005 for Eliot by  doxygen 1.4.5