# Copyright (c) 2011-2014, B.I.Stepanov Institute of Physics, National Academy
# of Sciences of Belarus.
#
# 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., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from PyQt4.QtCore import *
from PyQt4.QtGui import *

__all__ = ['ConsoleWidget']

# *****************************************************************************
class ConsoleWidget(QPlainTextEdit):
    """Widget suitable for displaying of real-time textual output generated by
    an external console application."""

    # ---- Public methods -----------------------------------------------------
    def __init__(self, width = 80, height = 20, parent = None):
        """'width' and 'height' should specify preferred dimensions of the
        output window, in characters."""

        QPlainTextEdit.__init__(self, parent)

        self.setReadOnly(True)
        # Font of the output window has to be monospace. However, there seems
        # to be no way of selecting a generic platform-specific monospace font.
        font = self.font()
        font.setFamily('Courier New')
        self.setFont(font)

        # Display the text as is by default.
        self.setLineWrapMode(QPlainTextEdit.NoWrap)

        self.width = width
        self.height = height

    def appendOutput(self, text):
        """Append a new output string to the end of the widget's current text,
        and make sure that added text is visible on the screen."""

        # Don't use 'appendPlainText' because it adds an extra newline after
        # the added text. Use 'moveCursor' to make sure that 'insertPlainText'
        # will insert at the end of the widget (otherwise, it will insert at
        # the point of the last mouse click, possibly messing everything up).
        self.moveCursor(QTextCursor.End)
        self.insertPlainText(text)
        # Scroll to the recently added text. Note: for 'QPlainTextEdit', this
        # is not the same as scrolling to the bottom of the widget, as this
        # widget seems to always add an extra empty line below the text, and
        # this function won't scroll to it. Indeed, such a behaviour is
        # desirable, because too much whitespace at the bottom won't look fine.
        self.ensureCursorVisible()

    # ---- Private overridden methods -----------------------------------------
    def sizeHint(self):
        # This will modify default size of the widget, so that it could hold a
        # reasonable amount of text out of the box.

        metrics = QFontMetrics(self.font())

        frameWidth = self.frameWidth()
        # Document margin is float, but it has an integer value by default, and
        # that value seems to be an appropriate one.
        margin = int(self.document().documentMargin())

        # Width and height of a widget capable of holding the required number
        # of characters (without scrollbars taken into account). Include only a
        # single margin for height, because top margin is displayed only if the
        # widget is scrolled to its top, and in that case bottom margin won't
        # be necessary.
        width = metrics.width(' ' * self.width) + frameWidth * 2 + margin * 2
        height = metrics.height() * self.height + frameWidth * 2 + margin

        # Include space for the vertical scrollbar, as it is very likely to be
        # shown at some moment. Use 'sizeHint' instead of 'size', as 'size'
        # won't work if the widget is not laid out yet. Don't include space for
        # the horizontal scrollbar by default, as it usually should not appear.
        width += self.verticalScrollBar().sizeHint().width()

        return QSize(width, height)
