Logo Search packages:      
Sourcecode: qtads version File versions

qtadsio.cc

/* Copyright (C) 2003 Nikos Chantziaras.
 *
 * This file is part of the QTads program.  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, 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; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 */

#include "config.h"

#include "qtadsio.h"

#include <queue>

#include <qstring.h>
#include <qbrush.h>
#include <qcolor.h>
#include <qstatusbar.h>
#include <qfiledialog.h>
#include <qmessagebox.h>
#include <qvbox.h>
#include <qaction.h>
#include <qregexp.h>

#include "qtadssettings.h"
#include "qtadsdialog.h"
#include "qtadsmainwindow.h"
#include "qtadsstatusline.h"
#include "qtadsgamewindow.h"
#include "qtadstypes.h"

#include "tio.h"
#include "t3std.h"
#include "vmglob.h"
#include "vmconsol.h"


namespace QTadsIO {

bool gameRunning = false;
bool openNewGame = false;
QString newGameToOpen;
bool restartGame = false;
bool quitApp = false;

// The application's main window.
static QTadsMainWindow* fMainWindow = 0;
// The game status line.
static QTadsStatusLine* fStatusLine = 0;
// The main QTads display area.
static QTadsGameWindow* fGameWindow = 0;
// Current text-format.
static QTadsTextFormat fCurrentFormat;
// We don't display output immediately.
static std::queue<QTadsFormattedString> fBuffer;
// The current content of the statline's left side.
static QString fStatusString;
// The current content of the statline's right side.
static QString fScoreString;
// The current alignment of the main window's text.
static Qt::AlignmentFlags fAlignment;
// User preferences and themes.
static QTadsSettings* fSettings;
// Are we in Tads3-mode?
static bool fTads3Mode = false;


/* Helper function; it's not part of the QTadsIO-interface.
 *
 * Sets the game text's alignment to `flags'.
 */
static inline
void
alignment( Qt::AlignmentFlags flags )
{
      int cursorPar;
      int cursorInd;
      QTadsIO::fGameWindow->getCursorPosition(&cursorPar, &cursorInd);
      QTadsIO::fGameWindow->setUpdatesEnabled(false);
      QTadsIO::fGameWindow->scrolling(false);
      for (int i = 0; i < QTadsIO::fGameWindow->paragraphs(); ++i) {
            QTadsIO::fGameWindow->setCursorPosition(i, 0);
            QTadsIO::fGameWindow->setAlignment(flags);
      }
      QTadsIO::fGameWindow->scrolling(true);
      QTadsIO::fGameWindow->setCursorPosition(cursorPar, cursorInd);
      QTadsIO::fGameWindow->setUpdatesEnabled(true);
}


/* Helper function; it's not part of the QTadsIO-interface.
 *
 * Applies any needed modifications to a string and returns the result.
 * Game text *must* pass through this filter!
 */
static inline
QString
prepareString( const QString& s )
{
      QString res(s);

      if (not QTadsIO::fSettings->currentTheme().curlyQuotes()) {
            // The user has disabled the display of typographical
            // quotes; replace them with regular ASCII quotes.
            res.replace(QRegExp("[\\x2018\\x2019]"), QChar('\''))
                  .replace(QRegExp("[\\x201C\\x201D]"), QChar('"'));
      }

      if (QTadsIO::fSettings->currentTheme().curlyApostrophes()) {
            // The user enabled the "Curly apostrophes" option;
            // Replace every ASCII apostrophe with a typographical
            // one.
            res.replace(QChar('\''), QChar(0x2019));
      }

      if (QTadsIO::fSettings->currentTheme().dashConversion() and not QTadsIO::fTads3Mode) {
            // Convert "--" to an em-dash if the user enabled it
            // and this isn't a Tads 3 game.
            res.replace(QString("--"), QChar(0x2014));
      }

      return res;
}


/* Helper function; it's not part of the QTadsIO-interface.
 *
 * Constructs and prints the statusline's contents; text to the left
 * and score to the right.
 */
static inline
void
createStatusText()
{
      QTadsIO::fStatusLine->leftText(QTadsIO::fStatusString);
      QTadsIO::fStatusLine->rightText(QTadsIO::fScoreString);
}


void
init()
{
      Q_ASSERT(QTadsIO::fMainWindow == 0);
      Q_ASSERT(QTadsIO::fStatusLine == 0);
      Q_ASSERT(QTadsIO::fGameWindow == 0);

      QTadsIO::fMainWindow = new QTadsMainWindow(0, "main window");

      // Let a QVBox object handle the layout-management of the
      // statusline and main window.  Make it the main window's
      // central widget.
      static QVBox* vBox = new QVBox(QTadsIO::fMainWindow);
      QTadsIO::fMainWindow->setCentralWidget(vBox);

      // Create the rest, then load and apply the settings.
      QTadsIO::fStatusLine = new QTadsStatusLine(vBox, "status line");
      QTadsIO::fGameWindow = new QTadsGameWindow(vBox, "main display");
      QTadsIO::fGameWindow->setReadOnly(true);
      QTadsIO::fSettings = new QTadsSettings;
      QTadsIO::fMainWindow->updateThemeList();
      QTadsIO::fMainWindow->updateRecentGamesList();
      QTadsIO::applySettings();

      // Print a dummy string on the statusline in order to force a
      // resize.
      QTadsIO::fStatusLine->leftText(" ");

      QTadsIO::enableCommandActions(false);

      QTadsIO::fGameWindow->connect(QTadsIO::fMainWindow->editCopyAction,
                              SIGNAL(activated()), SLOT(copy()));
      QTadsIO::fGameWindow->connect(QTadsIO::fMainWindow->editPasteAction,
                              SIGNAL(activated()), SLOT(paste()));
      QTadsIO::fGameWindow->connect(QTadsIO::fMainWindow->displayZoomInAction,
                              SIGNAL(activated()), SLOT(zoomIn()));
      QTadsIO::fGameWindow->connect(QTadsIO::fMainWindow->displayZoomOutAction,
                              SIGNAL(activated()), SLOT(zoomOut()));
      QTadsIO::fMainWindow->connect(QTadsIO::fGameWindow, SIGNAL(showHideMenu()),
                              SLOT(showHideMenu()));
}


void
done()
{
      delete QTadsIO::fSettings;
      delete QTadsIO::fMainWindow;
}


void
reset()
{
      // Flush any pending output.
      QTadsIO::flush();
      // Clear the statusline (we print spaces because it should
      // maintain its current height).
      QTadsIO::statusPrint(" ");
      QTadsIO::scorePrint(" ");
      // Clear the game window.
      QTadsIO::clear();
      // Reset highlight attribute.
      QTadsIO::highlight(false);
      // Reset the main window's caption.
      QTadsIO::title("QTads");
}


QTadsMainWindow&
mainWindow()
{
      return *QTadsIO::fMainWindow;
}


QTadsStatusLine&
statusLine()
{
      return *QTadsIO::fStatusLine;
}


QTadsGameWindow&
gameWindow()
{
      return *QTadsIO::fGameWindow;
}


QTadsSettings&
settings()
{
      return *QTadsIO::fSettings;
}


void
applySettings()
{
      if (fGameWindow->scrollBufferSize() !=
          static_cast<unsigned int>(fSettings->scrollBufferSize()))
      {
            fGameWindow->scrollBufferSize(fSettings->scrollBufferSize());
      }

      const QTadsTheme& curTheme = fSettings->currentTheme();

      if (fGameWindow->paper().color() != curTheme.gameBgColor()) {
            fGameWindow->setPaletteBackgroundColor(curTheme.gameBgColor());
      }
      if (fGameWindow->paletteForegroundColor() != curTheme.gameTextColor()) {
            fGameWindow->setPaletteForegroundColor(curTheme.gameTextColor());
      }
      if (fStatusLine->paletteBackgroundColor() != curTheme.statusBgColor()) {
            fStatusLine->setPaletteBackgroundColor(curTheme.statusBgColor());
      }
      if (fStatusLine->paletteForegroundColor() != curTheme.statusTextColor()) {
            fStatusLine->setPaletteForegroundColor(curTheme.statusTextColor());
      }
      if (fGameWindow->font() != curTheme.gameFont()) {
            QTadsIO::fGameWindow->setFont(curTheme.gameFont());
            QTadsIO::fGameWindow->repaint();
            QTadsIO::fGameWindow->ensureCursorVisible();
      }
      if (fStatusLine->font() != curTheme.statusFont()) {
            QTadsIO::fStatusLine->setFont(curTheme.statusFont());
            QTadsIO::fStatusLine->repaint();
      }
      if (fAlignment != curTheme.alignment()) {
            QTadsIO::alignment(curTheme.alignment());
            fAlignment = curTheme.alignment();
      }
      if (fGameWindow->leftMargin() != curTheme.leftMargin()) {
            fGameWindow->setLeftMargin(curTheme.leftMargin());
      }
      if (fGameWindow->rightMargin() != curTheme.rightMargin()) {
            fGameWindow->setRightMargin(curTheme.rightMargin());
      }

      // Doublespacing is handled by the VM.  Always apply it.  Note
      // that the T3 VM doesn't support doublespacing (it's handled
      // by the game itself).
      out_set_doublespace(curTheme.doubleSpace());
}


void
t3Mode( bool yes )
{
      QTadsIO::fTads3Mode = yes;
}


bool
t3Mode()
{
      return QTadsIO::fTads3Mode;
}


void
nonstopMode( bool yes )
{
      QTadsIO::fGameWindow->nonstopMode(yes);
}


void
print( const QString& txt )
{
      const QString& tmp = QTadsIO::prepareString(txt);

      if (QTadsIO::fBuffer.empty()
          or QTadsIO::fBuffer.back().f != QTadsIO::fCurrentFormat)
      {
            QTadsIO::fBuffer.push(QTadsFormattedString(tmp, QTadsIO::fCurrentFormat));
      } else {
            QTadsIO::fBuffer.back().s.append(tmp);
      }
}


void
statusPrint( const QString& txt )
{
      QTadsIO::fStatusString = QTadsIO::prepareString(txt);
      QTadsIO::createStatusText();
}


void
scorePrint( const QString& txt )
{
      QTadsIO::fScoreString = QTadsIO::prepareString(txt);
      QTadsIO::createStatusText();
}


void
sysStatusPrint( const QString& txt )
{
      QTadsIO::fMainWindow->statusBar()->message(txt);
}


void
sysStatusPrint( const QString& txt, int time_ms )
{
      QTadsIO::fMainWindow->statusBar()->message(txt, time_ms);
}


void
clearSysStatus()
{
      QTadsIO::fMainWindow->statusBar()->clear();
}


/* TODO: Provide a way to view previously cleared text.
 */
void
clear()
{
      QTadsIO::fGameWindow->clear();
      // The paragraph-alignment gets cleared by this operation, so
      // set it again.
      QTadsIO::alignment(QTadsIO::fAlignment);
}


void
flush()
{
      QTadsIO::fGameWindow->insert(QTadsIO::fBuffer);

      Q_ASSERT(QTadsIO::fBuffer.empty());
}


bool
highlight()
{
      return QTadsIO::fCurrentFormat.high;
}


void
highlight( bool yes )
{
      if (yes != QTadsIO::fCurrentFormat.high) {
            QTadsIO::fCurrentFormat.high = yes;
      }
}


bool
italics()
{
      return QTadsIO::fCurrentFormat.italics;
}


void
italics( bool yes )
{
      if (yes != QTadsIO::fCurrentFormat.italics) {
            QTadsIO::fCurrentFormat.italics = yes;
      }
}


void
morePrompt()
{
      QTadsIO::flush();
      QTadsIO::fMainWindow->statusBar()->message(QObject::tr("*** More *** (Press a key to continue)"));
      QTadsIO::fGameWindow->waitChar();
      QTadsIO::fMainWindow->statusBar()->clear();
}


QString
getInput()
{
      QTadsIO::flush();
      return QTadsIO::fGameWindow->getInput();
}


void
waitChar()
{
      QTadsIO::flush();
      QTadsIO::fGameWindow->waitChar();
}


QKeyEvent
getRawChar()
{
      QTadsIO::flush();
      return QTadsIO::fGameWindow->getChar();
}


bool
fullScreen()
{
      return QTadsIO::fMainWindow->isFullScreen();
}


void
fullScreen( bool yes )
{
      if (yes and not QTadsIO::fMainWindow->isFullScreen()) {
            QTadsIO::fMainWindow->showFullScreen();
            QTadsIO::fGameWindow->ensureCursorVisible();
      } else if (not yes and QTadsIO::fMainWindow->isFullScreen()) {
            QTadsIO::fMainWindow->showNormal();
            QTadsIO::fGameWindow->ensureCursorVisible();
      }
}


void
title( const QString& str )
{
      QTadsIO::fMainWindow->setCaption(str);
}


void
enterCommand( const QString& cmd )
{
      if (QTadsIO::gameRunning) {
            QTadsIO::fGameWindow->enterCommand(cmd);
      }
}


void
enableCommandActions( bool yes )
{
      if (QTadsIO::gameRunning) {
            // When a game is running, allow the operation.
            QTadsIO::fMainWindow->gameRestoreAction->setEnabled(yes);
            QTadsIO::fMainWindow->gameSaveAction->setEnabled(yes);
            // Enabling the Quit and Restart actions is always possible, but disabling
            // them is only necessary when they would generate a user command.
            if (yes) {
                  QTadsIO::fMainWindow->gameQuitAction->setEnabled(true);
                  QTadsIO::fMainWindow->gameRestartAction->setEnabled(true);
            } else {
                  if (not QTadsIO::fSettings->immediateQuit()) {
                        QTadsIO::fMainWindow->gameQuitAction->setEnabled(false);
                  }
                  if (not QTadsIO::fSettings->immediateRestart()) {
                        QTadsIO::fMainWindow->gameRestartAction->setEnabled(false);
                  }
            }
      } else if (not yes) {
            // Disabling the actions is always possible.
            QTadsIO::fMainWindow->gameRestoreAction->setEnabled(false);
            QTadsIO::fMainWindow->gameSaveAction->setEnabled(false);
            if (not QTadsIO::fSettings->immediateQuit()) {
                  QTadsIO::fMainWindow->gameQuitAction->setEnabled(false);
            }
            if (not QTadsIO::fSettings->immediateQuit()) {
                  QTadsIO::fMainWindow->gameRestartAction->setEnabled(false);
            }
      } else {
            // When no game is running, enable only the Quit and Restart actions.
            QTadsIO::fMainWindow->gameQuitAction->setEnabled(true);
            QTadsIO::fMainWindow->gameRestartAction->setEnabled(true);
      }
}


QString
openFile( const QString& startWith, const QString& filter, const QString& caption )
{
      return QFileDialog::getOpenFileName(startWith, filter, QTadsIO::fMainWindow,
                                  "file open dialog", caption);
}


QString
saveFile( const QString& startWith, const QString& filter, const QString& caption )
{
      return QFileDialog::getSaveFileName(startWith, filter, QTadsIO::fMainWindow,
                                  "file open dialog", caption);
}


int
inputDialog( const QString& txt, const std::vector<QString>& buttons, unsigned int def )
{
      QTadsDialog dlg(txt, QTadsIO::fMainWindow, "tads input dialog", Qt::WStyle_Customize
                  | Qt::WStyle_DialogBorder | Qt::WStyle_Title | Qt::WStyle_SysMenu);

      // I don't know why, but Qt is brain damaged and displays "STRING <2>" instead of
      // "STRING" in the window's caption, so we simply add a space to make the dialog's
      // caption "different" from the main window's one.
      dlg.setCaption(QTadsIO::fMainWindow->caption() + " ");

      --def; // Adapt it to zero-based index.
      for (unsigned int i = 0; i < buttons.size(); ++i) {
            if (def != i) {
                  dlg.addButton(buttons[i]);
            } else {
                  // This is the default button.
                  dlg.addButton(buttons[i], true);
            }
      }
      return dlg.start();
}


void
resizeWindow( int width, int height )
{
      QTadsIO::fMainWindow->resize(width, height);
}


void
moveWindow( int x, int y )
{
      QTadsIO::fMainWindow->move(x, y);
}


bool
quit()
{
      bool willQuit = true;
      if (QTadsIO::fSettings->immediateQuit()) {
            switch (QMessageBox::information(QTadsIO::fMainWindow,
                                     QTadsIO::fMainWindow->caption() + " ",
                                     QObject::tr("The game is still executing. Quit anyway?"),
                                     QObject::tr("&Yes"), QObject::tr("&No"),
                                     QString::null, 0, 1))
            {
              case 0:
                  QTadsIO::gameRunning = false;
                  break;

              default:
                  willQuit = false;
                  break;
            }
      } else {
            QTadsIO::enterCommand("quit");
      }
      return willQuit;
}


// Restart the game.
void
restart()
{
      if (QTadsIO::fSettings->immediateRestart()) {
            switch (QMessageBox::information(QTadsIO::fMainWindow,
                                     QTadsIO::fMainWindow->caption() + " ",
                                     QObject::tr("The game is still executing. Reset the virtual machine?"),
                                     QObject::tr("&Yes"), QObject::tr("&No"),
                                     QString::null, 0, 1))
            {
              case 0:
                  QTadsIO::gameRunning = false;
                  QTadsIO::restartGame = true;
                  break;
            }
      } else {
            QTadsIO::enterCommand("restart");
      }
}

}; // namespace QTadsIO

Generated by  Doxygen 1.6.0   Back to index