Logo Search packages:      
Sourcecode: qtads version File versions

qtadsgamewindow.h

/* 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.
 */

#ifndef QTADSGAMEWINDOW_H
#define QTADSGAMEWINDOW_H

#include "config.h"

#include <queue>
#include <vector>
#include <qcursor.h>
#include <qtextedit.h>


struct QTadsFormattedString;


/* QTadsGameWindow is a widget based on QTextEdit.  It represents the
 * main output window of QTads and is located below the statusline
 * (which is a different widget).
 *
 * This class is (more or less) just a wrapper around QTextEdit.  It
 * violates "good" object oriented programming in that is uses public
 * inheritance only for implementation purposes.  Yuck!  I need to fix
 * this.  This class *is not* a QTextEdit, although it publicly
 * inherits from it.  The reason is simple; it needs to be treated like
 * a QTextEdit by Qt's layout manager, but not by other code.  I'm
 * lazy, so don't expect me to write a layout manager for this class
 * if I can avoid it.
 *
 * In case you're curious, this is how it *should* inherit:
 *
 *   class QTadsGameWindow: private QTextEdit, public QScrollView
 *
 * Or maybe just:
 *
 *   class QTadsGameWindow: private QTextEdit, public QWidget
 *
 * Maybe it should use virtual inheritance, since QTextEdit is also a
 * QScrollView/QWidget, but I'm not sure (man, I *love* C++!)
 */
class QTadsGameWindow: public QTextEdit {
      Q_OBJECT

  private:
      // These values specify the exact input-mode we are in.
      enum InputMode {
            // We aren't in input-mode.
            None,

            // Return-terminated input.
            NormalInput,

            // Single key-press (interpreted as a raw character).
            RawCharInput,

            // Single key-press (without evaluating it).
            WaitCharInput,

            // Any input-event (with no time-limit).
            Event,

            // Any input-event (with time-limit).
            // FIXME: We don't support this yet.
            TimedEvent,

            // We are waiting for a response to continue scrolling.
            PagePause
      };

      // The input-mode we are currently in.
      InputMode fInputMode;

      // If this is true, it means that the user clicked somewhere
      // outside the input-area while we were in input-mode and the
      // widget switched to read-only mode.  The `fRestorePar' and
      // `fRestoreInd' variables contain the last editing position
      // before the user clicked outside the editing area.
      bool fRestoreInput;

      // Last paragraph position if fRestoreInput is true.
      int fRestorePar;

      // Last character index if fRestoreInput is true.
      int fRestoreInd;

      // The last user-input in NormalInput-mode.
      QString fInput;

      // The last user-input in RawCharInput-mode.
      QKeyEvent* fChar;

      // Paragraph where input began.
      int fInputBeginPar;

      // Index of character inside fInputBeginPar where input began.
      int fInputBeginInd;

      // If true, setContentsPos() doesn't do any scrolling.
      bool fNoScroll;

      // Holds the Y-coordinate of the bottom of the window just
      // after the last input operation.  This value is used to
      // implement page-pausing ("More" prompt).
      int fLastBottomPos;

      // Size of the scrollback buffer in bytes.
      unsigned int fScrollBufferSize;

      // Holds previous user-inputs (for command history).
      std::vector<QString> fInputHistory;

      // Is the user currently editing a previous input?
      bool fInHistory;

      // The entry in the history the user is currently
      // viewing/editing.  Entry 0 is the oldest user input still in
      // the history.
      std::vector<QString>::size_type fHistPos;

      // Accumulator for enable/disable scrolling requests.
      int fScrollAc;

      // If this is true, we won't display a "more" prompt when the
      // text grows larger than the window's capacity.
      bool fNonstopMode;

      // This gets called by our own keyPressEvent() handler and
      // handles the events that should work differently than the
      // default QTextEdit behavior (when we are in input-mode).
      // Everything we don't recognize is simply passed to the
      // QTextEdit::keyPressEvent() handler.
        //
      // This method is called only when our input mode is
      // NormalInput.
      void
      inputKeyPressEvent( QKeyEvent* e );

      // This handles keypress events when we are waiting for a
      // single character input.  It is called by our own
      // keyPressEvent().
      //
      // This method is called only when our input mode is
      // WaitCharInput.
      void
      waitCharKeyPressEvent( QKeyEvent* e );

      // This handles keypress events when we are in RawCharInput
      // mode.  It is called by our own keyPressEvent().
      //
      // This method is called only when our input mode is
      // RawCharInput.
      void
      charKeyPressEvent( QKeyEvent* e );

      // Handles cursor actions when we are in read-only mode.
      //
      // TODO: This does not do anything yet.  At some point, the
      // user will be able to scroll up and down using the cursor
      // keys when we are in read-only mode.
      void
      readOnlyMoveCursor( CursorAction action );

      // Waits for the user to respond while in PagePause-mode.  If
      // PagePause-mode is terminated while this function is still
      // executing (for example through a slot called by a signal),
      // then this function returns even if the user didn't respond
      // at all.  In other words, set fInputMode to something other
      // than PagePause, and the function returns immediately.
      void
      pagePause();

      // Restores the cursor position (if needed, like after the user
      // clicked outside the input-area while in input-mode).  If
      // there's nothing to restore, this method does nothing.
      void
      restoreCursorPos();

  protected:
      // We override this to provide our own handling of keypress
      // events.  This handler is called automaticly by Qt.  This
      // method calls the various *KeyPressEvent() routines from our
      // private section above (according to the current input-mode).
      virtual void
      keyPressEvent( QKeyEvent* e );

      // Qt calls this handler when the user tries to bring up the
      // context-menu (by right-clicking on the window or pressing
      // the "Application" key on Win95 keyboards, for example).
      // `pos' contains the coordinates where the menu should appear.
      // We use this to offer a menu containing things like
      // "Show/Hide Scrollbar" and similar.
      virtual QPopupMenu*
      createPopupMenu( const QPoint& pos );

      // Qt calls this handler when the mouse is being moved while
      // it's inside out contents-area.  We override it to make the
      // mouse cursor visible again when the user moves it and it was
      // previously invisible.
      virtual void
      contentsMouseMoveEvent( QMouseEvent* e );

      // Qt calls this handler when the window is being resized.  We
      // override it to remove the "More" prompt when the window is
      // resized and there's no need for the prompt anymore.
      //
      // TODO: Not implemented yet.
      virtual void
      resizeEvent( QResizeEvent* );

      // This gets called by Qt when we lose the focus.  We override
      // it to make the cursor visible (if it's currently invisible)
      // when this happens.
      virtual void
      focusOutEvent( QFocusEvent* );

  public:
      QTadsGameWindow( QWidget* parent = 0, const char* name = 0 );

      // Get a return-terminated input from the user.  As a
      // side-effect, some actions (the "Save" and "Restore" buttons,
      // for example) will be enabled while input-mode is active and
      // will be grayed-out again when the input is terminated.
      //
      // TODO: Enabling/disabling actions shouldn't be done here.
      QString
      getInput();

      // Get a single character without evaluating it.
      void
      waitChar();

      // Get a single character and return it as a QKeyEvent.
      QKeyEvent
      getChar();

      // Enables/disables nonstop-mode.  In nonstop-mode, we suppress
      // "more" prompts.
      void
      nonstopMode( bool on );

      // Enables/disables scrolling.  Note that this method uses a
      // buffer; if you call scrolling(false) twice, you have to call
      // scrolling(true) *twice* to enable scrolling again.  This
      // makes it safe to call this method without having to check if
      // scrolling is already disabled.
      void
      scrolling( bool on );

      // Returns true if scrolling is enabled, false otherwise.
      bool
      scrolling();

      // Returns the size of the scrollback buffer (in bytes).
      unsigned int
      scrollBufferSize();

      // Sets the size of the scrollback buffer (in bytes).  The size
      // specified is always approximated; it is used as a hint.
      void
      scrollBufferSize( unsigned int size );

      // Make these public (from protected).
      using QTextEdit::leftMargin;
      using QTextEdit::rightMargin;
      using QTextEdit::topMargin;
      using QTextEdit::bottomMargin;

      // Instead of making setMargins() public, we provide these four
      // methods.
      //
      // NOTE: The reason we prepend "set" in front of the names, is
      // that old compilers (like pre-3.0 GCC) don't allow us to
      // overload the above methods (because we changed their
      // visibility from protected to public).
      void
      setLeftMargin( int m );

      void
      setRightMargin( int m );

      void
      setTopMargin( int m );

      void
      setBottomMargin( int m );

  signals:
      // The main window's menubar should be hidden/shown whenever
      // this signal is emitted.  If it was visible, hide it; if not,
      // show it.
      void showHideMenu();

  public slots:
      // This overloads QTextEdit::insert(const QString&, uint).  It
      // behaves the same, except that it provides page-pauses ("more
      // prompts").  The funky type of the `text' argument is
      // intended to help the caller implement a FIFO-buffer that
      // stores the text's appearance together with the text.  If
      // that buffer is to be flushed, this method should be called.
      // When this method returns, the `text' buffer will be empty
      // (so make sure you make a copy of it if you want to preserve
      // its contents).
      //
      // The `insFlags' parameter has the same meaning as in the
      // overloaded method, and it defaults to the or'ed combination
      // of CheckNewLines and RemoveSelected.
      void
      insert( std::queue<QTadsFormattedString>& text,
              uint insFlags = QTextEdit::CheckNewLines | QTextEdit::RemoveSelected );

      // We override this since we want to be able to disable
      // scrolling.
      virtual void
      setContentsPos( int x, int y );

      // We override moveCursor() and doKeyboardAction() because we
      // don't allow the cursor to be moved out of the editing area
      // (or else the user would be able to delete game-text).
      virtual void
      moveCursor( CursorAction action, bool select );

      virtual void
      doKeyboardAction( KeyboardAction action );

      // We override this because we need to recalculate some things
      // related to page pauses when the text is cleared.
      virtual void
      clear();

      // We override this because pasting text should only work in
      // NormalInput mode.
      //
      // TODO: Implement pasting of text that contains newlines.
      virtual void
      paste();

      // Enters the command `cmd' as if the user typed it in
      // (including an emulated [Return] keypress).
      void
      enterCommand( const QString& cmd );

  private slots:
      // Prevents the user from manipulating game text.  We connect
      // QTextEdit's clicked(int,int) signal to this slot, so we can
      // switch to read-only mode when the user clicks outside the
      // input-area when in input-mode.  If the click is inside the
      // input area, read-only mode is terminated so that the cursor
      // is visible again, unless there is a selection extending
      // outside the input area.
      void
      clickHandler( int para, int pos );

      // Ends PagePause-mode when the user manually scrolls to the
      // bottom of the contents.  We connect our
      // verticalScrollBar()'s valueChanged(int) signal to this slot,
      // so it gets called whenever the user moves the scrollbar.
      void
      vScrollBarHandler( int value );

      // Makes the mouse-cursor invisible.  If this is already the
      // case, then nothing happens.
      void
      hideMouseCursor();

      // Makes the mouse-cursor visible.  If this is already the
      // case, then nothing happens.
      void
      showMouseCursor();

      // If the scrollbar is visible, hide it; if not, show it.
      void
      showHideScrollBar();
};


inline void
QTadsGameWindow::nonstopMode( bool on )
{
      this->fNonstopMode = on;
}


inline bool
QTadsGameWindow::scrolling()
{
      return not this->fNoScroll;
}


inline unsigned int
QTadsGameWindow::scrollBufferSize()
{
      return this->fScrollBufferSize;
}


inline void
QTadsGameWindow::scrollBufferSize( unsigned int size )
{
      this->fScrollBufferSize = size;
}


inline void
QTadsGameWindow::setLeftMargin( int m )
{
      this->setMargins(m, this->topMargin(), this->rightMargin(), this->bottomMargin());
}


inline void
QTadsGameWindow::setRightMargin( int m )
{
      this->setMargins(this->leftMargin(), this->topMargin(), m, this->bottomMargin());
}

inline void
QTadsGameWindow::setTopMargin( int m )
{
      this->setMargins(this->leftMargin(), m, this->rightMargin(), this->bottomMargin());
}


inline void
QTadsGameWindow::setBottomMargin( int m )
{
      this->setMargins(this->leftMargin(), this->topMargin(), this->rightMargin(), m);
}


inline void
QTadsGameWindow::clear()
{
      QTextEdit::clear();
      // Since the contents are gone, reset the last visible bottom position.
      this->fLastBottomPos = this->contentsHeight();
}


inline void
QTadsGameWindow::hideMouseCursor()
{
      // Hide the cursor if it's inside the viewport and not already
      // hidden.
      if (this->viewport()->hasMouse()
          and this->viewport()->cursor().shape() != Qt::BlankCursor)
      {
            this->viewport()->setCursor(Qt::BlankCursor);
      }
}


inline void
QTadsGameWindow::showMouseCursor()
{
      // Make the cursor visible, no matter where it is located.
      if (this->viewport()->cursor().shape() == Qt::BlankCursor) {
            this->viewport()->unsetCursor();
            Q_ASSERT(this->viewport()->cursor().shape() != Qt::BlankCursor);
      }
}


inline void
QTadsGameWindow::showHideScrollBar()
{
      if (this->vScrollBarMode() == AlwaysOn) {
            this->setVScrollBarMode(AlwaysOff);
      } else {
            this->setVScrollBarMode(AlwaysOn);
      }
}

#endif // QTADSGAMEWINDOW_H

Generated by  Doxygen 1.6.0   Back to index