changeset 27092:55301e06b238 jwe-new-qterminal

experimental qterminal
author John W. Eaton <jwe@octave.org>
date Tue, 14 May 2019 15:26:56 +0000
parents 4092ffc1e43c
children
files libgui/qterminal/libqterminal/QGenericTerminalColors.cpp libgui/qterminal/libqterminal/QGenericTerminalColors.h libgui/qterminal/libqterminal/QGenericTerminalImpl.cpp libgui/qterminal/libqterminal/QGenericTerminalImpl.h libgui/qterminal/libqterminal/QTerminal.cc libgui/qterminal/module.mk
diffstat 6 files changed, 1770 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/qterminal/libqterminal/QGenericTerminalColors.cpp	Tue May 14 15:26:56 2019 +0000
@@ -0,0 +1,46 @@
+/*
+
+Copyright (C) 2011-2019 Michael Goffioul
+
+This file is part of QConsole.
+
+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 3 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,
+see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include "QGenericTerminalColors.h"
+
+//////////////////////////////////////////////////////////////////////////////
+
+QConsoleColors::QConsoleColors (void)
+    : QMap<int, QColor> ()
+{
+  (*this)[0]  = Qt::black;
+  (*this)[1]  = Qt::darkBlue;
+  (*this)[2]  = Qt::darkGreen;
+  (*this)[3]  = Qt::darkCyan;
+  (*this)[4]  = Qt::darkRed;
+  (*this)[5]  = Qt::darkMagenta;
+  (*this)[6]  = Qt::darkYellow;
+  (*this)[7]  = Qt::lightGray;
+  (*this)[8]  = Qt::darkGray;
+  (*this)[9]  = Qt::blue;
+  (*this)[10] = Qt::green;
+  (*this)[11] = Qt::cyan;
+  (*this)[12] = Qt::red;
+  (*this)[13] = Qt::magenta;
+  (*this)[14] = Qt::yellow;
+  (*this)[15] = Qt::white;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/qterminal/libqterminal/QGenericTerminalColors.h	Tue May 14 15:26:56 2019 +0000
@@ -0,0 +1,39 @@
+/*
+
+Copyright (C) 2011-2019 Michael Goffioul
+
+This file is part of QConsole.
+
+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 3 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,
+see <https://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef __QConsoleColors_h__
+#define __QConsoleColors_h__ 1
+
+#include <QColor>
+#include <QMap>
+
+//////////////////////////////////////////////////////////////////////////////
+
+class QConsoleColors : public QMap<int, QColor>
+{
+public:
+  QConsoleColors (void);
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // __QConsoleColors_h__
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/qterminal/libqterminal/QGenericTerminalImpl.cpp	Tue May 14 15:26:56 2019 +0000
@@ -0,0 +1,1549 @@
+/*
+
+Copyright (C) 2011-2019 Michael Goffioul
+
+This file is part of QConsole.
+
+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 3 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,
+see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <QApplication>
+#include <QClipboard>
+#include <QColor>
+#include <QFont>
+#include <QGridLayout>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QResizeEvent>
+#include <QScrollBar>
+#include <QtDebug>
+#include <QThread>
+#include <QTimer>
+#include <QToolTip>
+#include <QCursor>
+#include <QMessageBox>
+#include <QDragEnterEvent>
+#include <QDropEvent>
+#include <QUrl>
+#include <QMimeData>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <cstring>
+#include <csignal>
+
+#include "QGenericTerminalImpl.h"
+#include "QGenericTerminalColors.h"
+
+// Uncomment to log activity to LOGFILENAME
+// #define DEBUG_QCONSOLE
+#define LOGFILENAME "QConsole.log"
+
+typedef void CHAR_INFO;
+
+//////////////////////////////////////////////////////////////////////////////
+
+class QConsoleView : public QWidget
+{
+public:
+  QConsoleView (QGenericTerminalImpl* parent = 0) : QWidget (parent), q (parent) { }
+  ~QConsoleView (void) { }
+
+protected:
+  void paintEvent (QPaintEvent* event) { q->viewPaintEvent (this, event); }
+  void resizeEvent (QResizeEvent* event) { q->viewResizeEvent (this, event); }
+
+private:
+  QGenericTerminalImpl* q;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+class QConsoleThread : public QThread
+{
+public:
+  QConsoleThread (QGenericTerminalImpl* console) : QThread (console), q (console) { }
+
+protected:
+  void run (void)
+    { q->start (); }
+
+private:
+  QGenericTerminalImpl* q;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static QString translateKey (QKeyEvent *ev)
+{
+  QString esc = "\x1b";
+  QString s;
+
+  if (ev->key () == Qt::Key_Delete)
+    s = esc + "[C\b";
+  else if (!ev->text ().isEmpty ())
+    s = ev->text ();
+  else
+    {
+
+      switch (ev->key ())
+        {
+        case Qt::Key_Up:
+          s = esc + "[A";
+          break;
+
+        case Qt::Key_Down:
+          s = esc + "[B";
+          break;
+
+        case Qt::Key_Right:
+          s = esc + "[C";
+          break;
+
+        case Qt::Key_Left:
+          s = esc + "[D";
+          break;
+
+        case Qt::Key_Home:
+          s = esc + "[H";
+          break;
+
+        case Qt::Key_End:
+          s = esc + "[F";
+          break;
+
+        case Qt::Key_Insert:
+          s = esc + "[2~";
+          break;
+
+        case Qt::Key_PageUp:
+          s = esc + "[5~";
+          break;
+
+        case Qt::Key_PageDown:
+          s = esc + "[6~";
+          break;
+
+        case Qt::Key_Escape:
+          s = esc;
+          break;
+
+        default:
+          break;
+        }
+    }
+
+  return s;
+}
+
+class QConsolePrivate
+{
+  friend class QGenericTerminalImpl;
+
+public:
+
+  enum KeyboardCursorType
+    {
+      BlockCursor,
+      UnderlineCursor,
+      IBeamCursor
+    };
+
+  QConsolePrivate (QGenericTerminalImpl* parent, const QString& cmd = QString ());
+  ~QConsolePrivate (void);
+
+  void updateConsoleSize (bool sync = false, bool allow_smaller_width = false);
+  void updateHorizontalScrollBar (void);
+  void updateVerticalScrollBar (void);
+  void setHorizontalScrollValue (int value);
+  void setVerticalScrollValue (int value);
+  void updateConsoleView (bool grab = true);
+  void sendConsoleText (const QString& s);
+  QRect cursorRect (void);
+  void selectAll();
+  void selectWord(const QPoint& cellPos);
+  void selectLine(const QPoint& cellPos);
+
+  void log (const char* fmt, ...);
+
+  QPoint posToCell (const QPoint& pt);
+  QString getSelection (void);
+  void updateSelection (void);
+  void clearSelection (void);
+
+  QColor backgroundColor (void) const;
+  QColor foregroundColor (void) const;
+  QColor selectionColor (void) const;
+  QColor cursorColor (void) const;
+
+  void setBackgroundColor (const QColor& color);
+  void setForegroundColor (const QColor& color);
+  void setSelectionColor (const QColor& color);
+  void setCursorColor (bool useForegroundColor, const QColor& color);
+  void setScrollBufferSize (int value);
+
+  void drawTextBackground (QPainter& p, int cx1, int cy1, int cx2, int cy2,
+                           int cw, int ch);
+
+  void drawSelection (QPainter& p, int cx1, int cy1, int cx2, int cy2,
+                      int cw, int ch);
+
+  void drawCursor (QPainter& p);
+
+  void drawText (QPainter& p, int cx1, int cy1, int cx2, int cy2,
+                 int cw, int ch);
+
+private:
+  QGenericTerminalImpl* q;
+
+private:
+  QFont m_font;
+  QString m_command;
+  QConsoleColors m_colors;
+  bool m_inWheelEvent;
+  QString m_title;
+
+  QSize m_charSize;
+  QSize m_bufferSize;
+  QRect m_consoleRect;
+  bool m_auto_scroll;
+  QPoint m_cursorPos;
+  bool m_cursorBlinking;
+  bool m_hasBlinkingCursor;
+  QTimer *m_blinkCursorTimer;
+  KeyboardCursorType m_cursorType;
+
+  QPoint m_beginSelection;
+  QPoint m_endSelection;
+  bool m_settingSelection;
+
+  QColor m_selectionColor;
+  QColor m_cursorColor;
+
+  //XX CHAR_INFO* m_buffer;
+  //XX CHAR_INFO* m_tmpBuffer;
+
+  QConsoleView* m_consoleView;
+  QScrollBar* m_horizontalScrollBar;
+  QScrollBar* m_verticalScrollBar;
+
+  // The delay in milliseconds between redrawing blinking text.
+  static const int BLINK_DELAY = 500;
+};
+
+static void maybeSwapPoints (QPoint& begin, QPoint& end)
+{
+  if (end.y () < begin.y ()
+      || (end.y () == begin.y () && end.x () < begin.x ()))
+    qSwap (begin, end);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QConsolePrivate::QConsolePrivate (QGenericTerminalImpl* parent, const QString& cmd)
+  : q (parent), m_command (cmd), m_auto_scroll (true), m_cursorBlinking (false),
+    m_hasBlinkingCursor (true), m_cursorType (BlockCursor),
+    m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
+    m_inWheelEvent (false)
+{
+  log (NULL);
+
+  // XXX FIXME XXX
+  m_bufferSize = QSize (100, 500);
+  m_consoleRect = QRect (0, 0, 100, 500);
+  m_cursorPos = QPoint (0, 0);
+
+  log ("Initial console parameters:\n");
+  log ("  buffer size: %d x %d\n", m_bufferSize.width (),
+       m_bufferSize.height ());
+  log ("  window: (%d, %d) -> (%d, %d) [%d x %d]\n",
+       m_consoleRect.left (), m_consoleRect.top (),
+       m_consoleRect.right (), m_consoleRect.bottom (),
+       m_consoleRect.width (), m_consoleRect.height ());
+
+#if 0
+
+  wchar_t titleBuf[260];
+  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
+  q->setWindowTitle (QString::fromWCharArray (titleBuf));
+
+#endif
+
+  m_font.setFamily ("Lucida Console");
+  m_font.setPointSize (9);
+  m_font.setStyleHint (QFont::TypeWriter);
+
+#if 0
+
+  m_buffer = m_tmpBuffer = 0;
+
+#endif
+
+  m_consoleView = new QConsoleView (parent);
+  m_horizontalScrollBar = new QScrollBar (Qt::Horizontal, parent);
+  m_verticalScrollBar = new QScrollBar (Qt::Vertical, parent);
+
+  QGridLayout* l = new QGridLayout (parent);
+  l->setContentsMargins (0, 0, 0, 0);
+  l->setSpacing (0);
+  l->addWidget (m_consoleView, 0, 0);
+  l->addWidget (m_horizontalScrollBar, 1, 0);
+  l->addWidget (m_verticalScrollBar, 0, 1);
+
+  // Defaults.
+  setBackgroundColor (Qt::white);
+  setForegroundColor (Qt::black);
+  setSelectionColor (Qt::lightGray);
+  setCursorColor (false, Qt::darkGray);
+
+  // FIXME -- should we set the palette?
+  QPalette palette (backgroundColor ());
+  m_consoleView->setPalette (palette);
+
+  m_consoleView->setAutoFillBackground (true);
+
+  m_consoleView->setFont (m_font);
+  parent->setFocusPolicy (Qt::StrongFocus);
+  parent->winId ();
+
+  updateHorizontalScrollBar ();
+  updateVerticalScrollBar ();
+
+  m_blinkCursorTimer = new QTimer (parent);
+  QObject::connect (m_blinkCursorTimer, SIGNAL (timeout()),
+                    q, SLOT (blinkCursorEvent ()));
+
+  QObject::connect (m_horizontalScrollBar, SIGNAL (valueChanged (int)),
+                    q, SLOT (horizontalScrollValueChanged (int)));
+
+  QObject::connect (m_verticalScrollBar, SIGNAL (valueChanged (int)),
+                    q, SLOT (verticalScrollValueChanged (int)));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QConsolePrivate::~QConsolePrivate (void)
+{
+#if 0
+
+  delete [] m_buffer;
+  delete [] m_tmpBuffer;
+
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QPoint QConsolePrivate::posToCell (const QPoint& p)
+{
+  return QPoint (m_consoleRect.left () + p.x () / m_charSize.width (),
+                 m_consoleRect.top () + p.y () / m_charSize.height ());
+}
+
+QString QConsolePrivate::getSelection (void)
+{
+  QString selection;
+
+  QPoint begin = m_beginSelection;
+  QPoint end = m_endSelection;
+
+  maybeSwapPoints (begin, end);
+
+  if (begin != end)
+    {
+#if 0
+      CHAR_INFO* buf;
+      COORD bufSize, bufCoord;
+      SMALL_RECT bufRect;
+      int nr;
+
+      nr = end.y () - begin.y () + 1;
+      buf =  new CHAR_INFO[m_bufferSize.width () * nr];
+      bufSize.X = m_bufferSize.width ();
+      bufSize.Y = nr;
+      bufCoord.X = 0;
+      bufCoord.Y = 0;
+
+      bufRect.Left = 0;
+      bufRect.Right = m_bufferSize.width ();
+      bufRect.Top = begin.y ();
+      bufRect.Bottom = end.y ();
+
+      if (ReadConsoleOutput (m_stdOut, buf, bufSize, bufCoord, &bufRect))
+        {
+          int start_pos = begin.x ();
+          int end_pos = (nr - 1) * m_bufferSize.width () + end.x ();
+          int lastNonSpace = -1;
+
+          for (int i = start_pos; i <= end_pos; i++)
+            {
+              if (i && (i % m_bufferSize.width ()) == 0)
+                {
+                  if (lastNonSpace >= 0)
+                    selection.truncate (lastNonSpace);
+                  selection.append ('\n');
+                  lastNonSpace = selection.length ();
+                }
+
+              QChar c (buf[i].Char.UnicodeChar);
+              if (c.isNull ())
+                c = QChar (' ');
+
+              selection.append (c);
+              if (! c.isSpace ())
+                lastNonSpace = selection.length ();
+            }
+
+          if (lastNonSpace >= 0)
+            selection.truncate (lastNonSpace);
+        }
+#endif
+    }
+
+  return selection;
+}
+
+void QConsolePrivate::updateSelection (void)
+{
+  QPoint begin = m_beginSelection;
+  QPoint end = m_endSelection;
+
+  maybeSwapPoints (begin, end);
+
+  begin.rx () = 0;
+  end.rx () = m_consoleRect.width ();
+
+  m_consoleView->update ();
+}
+
+void QConsolePrivate::clearSelection (void)
+{
+  m_beginSelection = m_endSelection = QPoint ();
+
+  m_consoleView->update ();
+}
+
+QColor QConsolePrivate::backgroundColor (void) const
+{
+  return m_colors[15];
+}
+
+QColor QConsolePrivate::foregroundColor (void) const
+{
+  return m_colors[0];
+}
+
+QColor QConsolePrivate::selectionColor (void) const
+{
+  return m_selectionColor;
+}
+
+QColor QConsolePrivate::cursorColor (void) const
+{
+  return m_cursorColor.isValid () ? m_cursorColor : foregroundColor ();
+}
+
+void QConsolePrivate::setBackgroundColor (const QColor& color)
+{
+  m_colors[15] = color;
+
+  QPalette palette (color);
+  m_consoleView->setPalette (palette);
+}
+
+void QConsolePrivate::setForegroundColor (const QColor& color)
+{
+  m_colors[0] = color;
+}
+
+void QConsolePrivate::setSelectionColor (const QColor& color)
+{
+  m_selectionColor = color;
+}
+
+void QConsolePrivate::setCursorColor (bool useForegroundColor,
+                                      const QColor& color)
+{
+  m_cursorColor = useForegroundColor ? QColor () : color;
+}
+
+void QConsolePrivate::setScrollBufferSize (int value)
+{
+#if 0
+
+  CONSOLE_SCREEN_BUFFER_INFO sbi;
+  GetConsoleScreenBufferInfo (m_stdOut, &sbi);
+
+  m_bufferSize = QSize (sbi.dwSize.X, (SHORT)value);
+
+  updateConsoleSize (true);
+
+#endif
+}
+
+void QConsolePrivate::drawTextBackground (QPainter& p, int cx1, int cy1,
+                                          int cx2, int cy2, int cw, int ch)
+{
+  p.save ();
+
+#if 0
+
+  int ascent = p.fontMetrics ().ascent ();
+  int stride = m_consoleRect.width ();
+  int y = ascent + cy1 * ch;;
+
+  for (int j = cy1; j <= cy2; j++, y += ch)
+    {
+      int len = 0;
+      bool hasChar = false;
+      int x = cx1 * cw;
+      WORD attr = 0;
+
+      for (int i = cx1; i <= cx2; i++)
+        {
+          CHAR_INFO* ci = &(m_buffer[stride*j+i]);
+
+          if ((ci->Attributes & 0x00ff) != attr)
+            {
+              // Character attributes changed
+              if (len != 0)
+                {
+                  // String buffer not empty -> draw it
+                  if (hasChar || (attr & 0x00f0))
+                    {
+                      if (attr & 0x00f0)
+                        p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
+                    }
+
+                  x += (len * cw);
+                  len = 0;
+                  hasChar = false;
+                }
+              // Update current brush and store current attributes
+              attr = (ci->Attributes & 0x00ff);
+              p.setBrush (m_colors[(attr >> 4) & 0x000f]);
+            }
+
+          // Append current character to the string buffer
+          len++;
+          if (ci->Char.UnicodeChar != L' ')
+            hasChar = true;
+        }
+
+      if (len != 0 && (hasChar || (attr & 0x00f0)))
+        {
+          // Line end reached, but string buffer not empty -> draw it
+          // No need to update s or x, they will be reset on the next
+          // for-loop iteration
+
+          if (attr & 0x00f0)
+            p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
+        }
+    }
+#endif
+
+  p.restore ();
+}
+
+void QConsolePrivate::selectAll()
+{
+  m_beginSelection = QPoint (0,0);
+  m_endSelection = QPoint(m_bufferSize.width (),
+                          m_cursorPos.y());
+  updateSelection();
+}
+
+void QConsolePrivate::selectWord (const QPoint & cellpos)
+{
+  QPoint begin = cellpos;
+  QPoint end = cellpos;
+
+  int stride = m_consoleRect.width ();
+
+  int verticalScrollOffset = m_consoleRect.top ();
+  int horizontalScrollOffset = m_consoleRect.left ();
+
+  // get begin, end in buffer offsets
+  begin.ry () -= verticalScrollOffset;
+  end.ry () -= verticalScrollOffset;
+
+  begin.rx () -= horizontalScrollOffset;
+  end.rx () -= horizontalScrollOffset;
+
+#if 0
+
+  // loog at current clicked on char to determinate ig getting space chunk or nonspace chunk
+  if (QChar(m_buffer[begin.y ()*stride + begin.x ()].Char.UnicodeChar).isSpace () == false)
+  {
+    // from current char, go back and fwd to find start and end of block
+    while(begin.x () > 0 &&
+          QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace() == false)
+    {
+        begin.rx () --;
+    }
+
+    while(end.x () < m_consoleRect.width () &&
+          QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace() == false)
+    {
+      end.rx () ++;
+    }
+  }
+  else
+  {
+    while(begin.x () > 0 &&
+          QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace())
+    {
+      begin.rx () --;
+    }
+
+    while(end.x () < m_consoleRect.width () &&
+          QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace ())
+    {
+      end.rx () ++;
+    }
+  }
+
+  // convert console  offsets to absolute cell positions
+  begin.ry () += verticalScrollOffset;
+  end.ry () += verticalScrollOffset;
+
+  begin.rx () += horizontalScrollOffset;
+  end.rx () += horizontalScrollOffset;
+
+  m_beginSelection = begin;
+  m_endSelection = end;
+
+  updateSelection ();
+
+#endif
+}
+
+void QConsolePrivate::selectLine (const QPoint & cellpos)
+{
+#if 0
+
+  m_beginSelection = QPoint (0, cellpos.y ());
+  m_endSelection = QPoint (m_bufferSize.width ()-1, cellpos.y ());
+  updateSelection ();
+
+#endif
+}
+
+
+void QConsolePrivate::drawSelection (QPainter& p, int cx1, int cy1,
+                                     int cx2, int cy2, int cw, int ch)
+{
+  p.save ();
+
+#if 0
+
+  QPoint begin = m_beginSelection;
+  QPoint end = m_endSelection;
+
+  bool haveSelection = (begin != end);
+
+  if (haveSelection)
+    maybeSwapPoints (begin, end);
+
+  int verticalScrollOffset = m_consoleRect.top ();
+  int horizontalScrollOffset = m_consoleRect.left ();
+
+  begin.ry () -= verticalScrollOffset;
+  end.ry () -= verticalScrollOffset;
+
+  begin.rx () -= horizontalScrollOffset;
+  end.rx () -= horizontalScrollOffset;
+
+  int ascent = p.fontMetrics ().ascent ();
+  int stride = m_consoleRect.width ();
+
+  int y = ascent + cy1 * ch;;
+  for (int j = cy1; j <= cy2; j++, y += ch)
+    {
+      int charsThisLine = 0;
+      int len = 0;
+      bool hasChar = false;
+      WORD attr = 0;
+
+      for (int i = cx1; i <= cx2; i++)
+        {
+          CHAR_INFO* ci = &(m_buffer[stride*j+i]);
+
+          if ((ci->Attributes & 0x00ff) != attr)
+            {
+              // Character attributes changed
+              if (len != 0)
+                {
+                  charsThisLine += len;
+                  len = 0;
+                  hasChar = false;
+                }
+
+              // Store current attributes
+              attr = (ci->Attributes & 0x00ff);
+            }
+
+          // Append current character to the string buffer
+          len++;
+          if (ci->Char.UnicodeChar != L' ')
+            hasChar = true;
+        }
+
+      if (len != 0 && (hasChar || (attr & 0x00f0)))
+        charsThisLine += len;
+
+      if (haveSelection && j >= begin.y () && j <= end.y ())
+        {
+          int selectionBegin = j == begin.y () ? begin.x (): 0;
+
+          int len = ((j == end.y () && end.x () < charsThisLine)
+                     ? end.x () - selectionBegin + 1
+                     : stride - selectionBegin);
+
+          p.fillRect (selectionBegin * cw, y-ascent, len * cw, ch,
+                      selectionColor ());
+        }
+    }
+#endif
+
+  p.restore ();
+}
+
+void QConsolePrivate::drawCursor (QPainter& p)
+{
+  if (! m_cursorBlinking)
+    {
+      p.save ();
+
+      QRect rect = cursorRect ();
+      QColor color = cursorColor ();
+
+      p.setPen (color);
+
+      if (m_cursorType == QConsolePrivate::BlockCursor)
+        {
+          if (q->hasFocus ())
+            p.fillRect (rect, color);
+          else
+            {
+              // draw the cursor outline, adjusting the area so that
+              // it is draw entirely inside 'rect'
+
+              int penWidth = qMax (1, p.pen().width());
+
+              p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
+                                         - penWidth/2 - penWidth%2,
+                                         - penWidth/2 - penWidth%2));
+            }
+        }
+      else if (m_cursorType == QConsolePrivate::UnderlineCursor)
+        {
+          p.drawLine (rect.left (), rect.bottom (),
+                      rect.right (), rect.bottom ());
+        }
+      else if (m_cursorType == QConsolePrivate::IBeamCursor)
+        {
+          p.drawLine (rect.left (), rect.top (),
+                      rect.left (), rect.bottom ());
+        }
+
+      p.restore ();
+    }
+}
+
+void QConsolePrivate::drawText (QPainter& p, int cx1, int cy1,
+                                int cx2, int cy2, int cw, int ch)
+{
+  p.save ();
+
+  p.setFont (m_font);
+  p.setPen (foregroundColor ());
+
+  QString s;
+  s.reserve (cx2 - cx1 + 1);
+
+  int ascent = p.fontMetrics ().ascent ();
+  int stride = m_consoleRect.width ();
+
+#if 0
+
+  int y = ascent + cy1 * ch;;
+  for (int j = cy1; j <= cy2; j++, y += ch)
+    {
+      // Reset string buffer and starting X coordinate
+      s.clear ();
+      bool hasChar = false;
+      int x = cx1 * cw;
+      WORD attr = 0;
+
+      for (int i = cx1; i <= cx2; i++)
+        {
+          CHAR_INFO* ci = &(m_buffer[stride*j+i]);
+
+          if ((ci->Attributes & 0x00ff) != attr)
+            {
+              // Character attributes changed
+              if (! s.isEmpty ())
+                {
+                  // String buffer not empty -> draw it
+                  if (hasChar || (attr & 0x00f0))
+                    p.drawText (x, y, s);
+
+                  x += (s.length () * cw);
+                  s.clear ();
+                  hasChar = false;
+                }
+              // Update current pen and store current attributes
+              attr = (ci->Attributes & 0x00ff);
+              p.setPen (m_colors[attr & 0x000f]);
+            }
+
+          // Append current character to the string buffer
+          s.append (ci->Char.UnicodeChar);
+          if (ci->Char.UnicodeChar != L' ')
+            hasChar = true;
+        }
+
+      if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
+        {
+          // Line end reached, but string buffer not empty -> draw it
+          // No need to update s or x, they will be reset on the next
+          // for-loop iteration
+
+          p.drawText (x, y, s);
+        }
+    }
+
+#endif
+
+  p.restore ();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::log (const char* fmt, ...)
+{
+#ifdef DEBUG_QCONSOLE
+  if (fmt)
+    {
+      va_list l;
+      FILE* flog = fopen (LOGFILENAME, "ab");
+
+      va_start (l, fmt);
+      vfprintf (flog, fmt, l);
+      va_end (l);
+      fclose (flog);
+    }
+  else
+    {
+      // Special case to re-initialize the log file
+      FILE* flog = fopen (LOGFILENAME, "w");
+      fclose (flog);
+    }
+#else
+  Q_UNUSED (fmt);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::updateConsoleSize (bool sync, bool allow_smaller_width)
+{
+#if 0
+
+  QFontMetrics fm = m_consoleView->fontMetrics ();
+  QSize winSize = m_consoleView->size ();
+
+  m_charSize.rwidth () = fm.averageCharWidth ();
+  m_charSize.rheight () = fm.lineSpacing ();
+
+  m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
+  m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
+
+  // Don't shrink the size of the buffer.  That way wide lines won't be
+  // truncated and will reappear if the window is enlarged again later.
+
+  if (allow_smaller_width || m_consoleRect.width () > m_bufferSize.width ())
+    m_bufferSize.rwidth () = m_consoleRect.width ();
+
+  if (qMax (m_bufferSize.height (), m_consoleRect.height ())
+      > m_bufferSize.height ())
+    m_bufferSize.rheight () = qMax (m_bufferSize.height (),
+                                    m_consoleRect.height ());
+
+  // Store the terminal size in the environment.  When Octave is
+  // initialized, we ask the command editor (usually readline) to prefer
+  // using these values rather than querying the terminal so that the
+  // buffer size can be larger than the size of the window that the
+  // command editor will actually use.
+
+  qputenv ("LINES", QByteArray::number (m_consoleRect.height ()));
+  qputenv ("COLUMNS", QByteArray::number (m_consoleRect.width ()));
+
+  // Force the command line editor (usually readline) to notice the
+  // change in screen size as soon as possible.
+
+  q->setSize (m_consoleRect.height (), m_consoleRect.width ());
+
+  m_consoleRect.moveLeft (0);
+  if (m_consoleRect.bottom () >= m_bufferSize.height ())
+    m_consoleRect.moveTop (m_bufferSize.height () - m_consoleRect.height ());
+
+  log ("Console resized:\n");
+  log ("  widget size: %d x %d\n", winSize.width (), winSize.height ());
+  log ("  buffer size: %d x %d\n", m_bufferSize.width (),
+       m_bufferSize.height ());
+  log ("  window: (%d, %d) -> (%d, %d) [%d x %d]\n",
+       m_consoleRect.left (), m_consoleRect.top (),
+       m_consoleRect.right (), m_consoleRect.bottom (),
+       m_consoleRect.width (), m_consoleRect.height ());
+
+  if (sync)
+    syncConsoleParameters ();
+
+  updateHorizontalScrollBar ();
+  updateVerticalScrollBar ();
+
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::updateHorizontalScrollBar (void)
+{
+  m_horizontalScrollBar->setMinimum (0);
+  if (m_bufferSize.width () > m_consoleRect.width ())
+    m_horizontalScrollBar->setMaximum (m_bufferSize.width () - m_consoleRect.width ());
+  else
+    m_horizontalScrollBar->setMaximum (0);
+  m_horizontalScrollBar->setSingleStep (1);
+  m_horizontalScrollBar->setPageStep (m_consoleRect.width ());
+  m_horizontalScrollBar->setValue (m_consoleRect.left ());
+
+  log ("Horizontal scrollbar parameters updated: %d/%d/%d/%d\n",
+       m_horizontalScrollBar->minimum (),
+       m_horizontalScrollBar->maximum (),
+       m_horizontalScrollBar->singleStep (),
+       m_horizontalScrollBar->pageStep ());
+}
+
+void QConsolePrivate::updateVerticalScrollBar (void)
+{
+  m_verticalScrollBar->setMinimum (0);
+  if (m_bufferSize.height () > m_consoleRect.height ())
+    m_verticalScrollBar->setMaximum (m_bufferSize.height () - m_consoleRect.height ());
+  else
+    m_verticalScrollBar->setMaximum (0);
+  m_verticalScrollBar->setSingleStep (1);
+  m_verticalScrollBar->setPageStep (m_consoleRect.height ());
+  m_verticalScrollBar->setValue (m_consoleRect.top ());
+
+  log ("Vertical scrollbar parameters updated: %d/%d/%d/%d\n",
+       m_verticalScrollBar->minimum (), m_verticalScrollBar->maximum (),
+       m_verticalScrollBar->singleStep (), m_verticalScrollBar->pageStep ());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::setHorizontalScrollValue (int value)
+{
+  if (value == m_consoleRect.left ())
+    return;
+
+#if 0
+
+  SMALL_RECT r;
+  HANDLE hStdOut = m_stdOut;
+
+  if (value + m_consoleRect.width () > m_bufferSize.width ())
+    value = m_bufferSize.width () - m_consoleRect.width ();
+
+  r.Left = value;
+  r.Top = m_consoleRect.top ();
+  r.Right = value + m_consoleRect.width () - 1;
+  r.Bottom = m_consoleRect.bottom ();
+
+  log ("Scrolling window horizontally: (%d, %d) -> (%d, %d) [%d x %d]\n",
+       r.Left, r.Top, r.Right, r.Bottom,
+       r.Right - r.Left + 1, r.Bottom - r.Top + 1);
+
+  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
+    {
+      m_consoleRect.moveLeft (value);
+      updateConsoleView ();
+    }
+
+#endif
+}
+
+void QConsolePrivate::setVerticalScrollValue (int value)
+{
+  if (value == m_consoleRect.top ())
+    return;
+
+#if 0
+
+  SMALL_RECT r;
+  HANDLE hStdOut = m_stdOut;
+
+  if (value + m_consoleRect.height () > m_bufferSize.height ())
+    value = m_bufferSize.height () - m_consoleRect.height ();
+
+  r.Left = m_consoleRect.left ();
+  r.Top = value;
+  r.Right = m_consoleRect.right ();
+  r.Bottom = value + m_consoleRect.height () - 1;
+
+  log ("Scrolling window vertically: (%d, %d) -> (%d, %d) [%d x %d]\n",
+       r.Left, r.Top, r.Right, r.Bottom,
+       r.Right - r.Left + 1, r.Bottom - r.Top + 1);
+
+  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
+    {
+      m_consoleRect.moveTop (value);
+
+      CONSOLE_SCREEN_BUFFER_INFO sbi;
+      if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
+        {
+          if (sbi.dwCursorPosition.Y > m_consoleRect.bottom ())
+            m_auto_scroll = false;
+          else
+            m_auto_scroll = true;
+        }
+
+      updateConsoleView ();
+    }
+
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::updateConsoleView (bool grab)
+{
+  m_consoleView->update ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QConsolePrivate::sendConsoleText (const QString& s)
+{
+  // Send the string in chunks of 512 characters. Each character is
+  // translated into an equivalent keypress event.
+
+#define TEXT_CHUNK_SIZE 512
+
+  // clear any selection on inserting text
+  clearSelection();
+  // enable auto-scrolling
+  m_auto_scroll = true;
+
+  int len = s.length ();
+
+#if 0
+
+  INPUT_RECORD events[TEXT_CHUNK_SIZE];
+  DWORD nEvents = 0, written;
+  HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
+
+  ZeroMemory (events, sizeof (events));
+
+  for (int i = 0; i < len; i++)
+    {
+      QChar c = s.at (i);
+
+      if (c == L'\r' || c == L'\n')
+        {
+          if (c == L'\r' && i < (len - 1) && s.at (i+1) == L'\n')
+            i++;
+
+          // add new line
+          events[nEvents].EventType                        = KEY_EVENT;
+          events[nEvents].Event.KeyEvent.bKeyDown          = TRUE;
+          events[nEvents].Event.KeyEvent.wRepeatCount      = 1;
+          events[nEvents].Event.KeyEvent.wVirtualKeyCode   =
+            VK_RETURN;
+          events[nEvents].Event.KeyEvent.wVirtualScanCode  = 0;
+          events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
+          events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
+          nEvents++;
+
+          WriteConsoleInput (hStdIn, events, nEvents, &written);
+          nEvents = 0;
+          ZeroMemory (events, sizeof (events));
+
+        }
+      else
+        {
+          events[nEvents].EventType                        = KEY_EVENT;
+          events[nEvents].Event.KeyEvent.bKeyDown          = TRUE;
+          events[nEvents].Event.KeyEvent.wRepeatCount      = 1;
+          events[nEvents].Event.KeyEvent.wVirtualKeyCode   =
+            LOBYTE (VkKeyScan (c.unicode ()));
+          events[nEvents].Event.KeyEvent.wVirtualScanCode  = 0;
+          events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
+          events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
+          nEvents++;
+        }
+
+      if (nEvents == TEXT_CHUNK_SIZE
+          || (nEvents > 0 && i == (len - 1)))
+        {
+          WriteConsoleInput (hStdIn, events, nEvents, &written);
+          nEvents = 0;
+          ZeroMemory (events, sizeof (events));
+        }
+    }
+
+#endif
+}
+
+QRect
+QConsolePrivate::cursorRect (void)
+{
+  int cw = m_charSize.width ();
+  int ch = m_charSize.height ();
+
+  return QRect ((m_cursorPos.x () - m_consoleRect.x ()) * cw,
+                (m_cursorPos.y () - m_consoleRect.y ()) * ch,
+                cw, ch);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QGenericTerminalImpl::QGenericTerminalImpl (QWidget* parent)
+    : QTerminal (parent), d (new QConsolePrivate (this)),
+      allowTripleClick (false)
+{
+    installEventFilter (this);
+
+    connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
+           parent, SLOT (set_global_shortcuts (bool)));
+    connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
+             this, SLOT (set_global_shortcuts (bool)));
+
+    connect (this, SIGNAL (set_screen_size_signal (int, int)),
+             parent, SLOT (set_screen_size (int, int)));
+
+    setAcceptDrops (true);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QGenericTerminalImpl::QGenericTerminalImpl (const QString& cmd, QWidget* parent)
+    : QTerminal (parent), d (new QConsolePrivate (this, cmd))
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+QGenericTerminalImpl::~QGenericTerminalImpl (void)
+{
+  delete d;
+}
+
+void QGenericTerminalImpl::mouseMoveEvent (QMouseEvent *event)
+{
+  if (d->m_settingSelection)
+    {
+      d->m_endSelection = d->posToCell (event->pos ());
+
+      updateSelection ();
+    }
+}
+
+void QGenericTerminalImpl::mousePressEvent (QMouseEvent *event)
+{
+  if (allowTripleClick)
+    {
+      mouseTripleClickEvent (event);
+    }
+  else if (event->button () == Qt::LeftButton)
+    {
+      d->m_settingSelection = true;
+
+      d->m_beginSelection = d->posToCell (event->pos ());
+    }
+}
+
+void QGenericTerminalImpl::mouseReleaseEvent (QMouseEvent *event)
+{
+  if (event->button () == Qt::LeftButton && d->m_settingSelection)
+    {
+      d->m_endSelection = d->posToCell (event->pos ());
+
+      updateSelection ();
+
+      d->m_settingSelection = false;
+    }
+}
+
+void QGenericTerminalImpl::mouseDoubleClickEvent (QMouseEvent *event)
+{
+  if (event->button () == Qt::LeftButton)
+    {
+      // doubleclick - select word
+      d->m_settingSelection = false;
+
+      d->selectWord (d->posToCell (event->pos ()));
+
+      allowTripleClick = true;
+
+      QTimer::singleShot (QApplication::doubleClickInterval (),this,
+                     SLOT (tripleClickTimeout ()));
+
+    }
+}
+
+void QGenericTerminalImpl::mouseTripleClickEvent (QMouseEvent *event)
+{
+  if (event->button () == Qt::LeftButton)
+    {
+      d->selectLine (d->posToCell (event->pos ()));
+    }
+}
+
+void QGenericTerminalImpl::tripleClickTimeout ()
+{
+  allowTripleClick = false;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::viewResizeEvent (QConsoleView*, QResizeEvent*)
+{
+  d->updateConsoleSize (true);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::viewPaintEvent (QConsoleView* w, QPaintEvent* event)
+{
+  QPainter p (w);
+
+  int cw = d->m_charSize.width ();
+  int ch = d->m_charSize.height ();
+
+  QRect updateRect = event->rect ();
+
+  int cx1 = updateRect.left () / cw;
+  int cy1 = updateRect.top () / ch;
+  int cx2 = qMin (d->m_consoleRect.width () - 1, updateRect.right () / cw);
+  int cy2 = qMin (d->m_consoleRect.height () - 1, updateRect.bottom () / ch);
+
+  if (cx1 > d->m_consoleRect.width () - 1
+      || cy1 > d->m_consoleRect.height () - 1)
+    return;
+
+  d->drawTextBackground (p, cx1, cy1, cx2, cy2, cw, ch);
+  d->drawSelection (p, cx1, cy1, cx2, cy2, cw, ch);
+  d->drawCursor (p);
+  d->drawText (p, cx1, cy1, cx2, cy2, cw, ch);
+}
+
+void QGenericTerminalImpl::blinkCursorEvent (void)
+{
+  if (d->m_hasBlinkingCursor)
+    d->m_cursorBlinking = ! d->m_cursorBlinking;
+  else
+    d->m_cursorBlinking = false;
+
+  d->m_consoleView->update (d->cursorRect ());
+}
+
+void QGenericTerminalImpl::setBlinkingCursor (bool blink)
+{
+  d->m_hasBlinkingCursor = blink;
+
+  setBlinkingCursorState (blink);
+}
+
+void QGenericTerminalImpl::setBlinkingCursorState (bool blink)
+{
+  if (blink && ! d->m_blinkCursorTimer->isActive ())
+    d->m_blinkCursorTimer->start (d->BLINK_DELAY);
+
+  if (! blink && d->m_blinkCursorTimer->isActive ())
+    {
+      d->m_blinkCursorTimer->stop ();
+
+      if (d->m_cursorBlinking)
+        blinkCursorEvent ();
+    }
+}
+
+// Reset width of console buffer and terminal window to be the same.
+
+void QGenericTerminalImpl::init_terminal_size (void)
+{
+  d->updateConsoleSize (true, true);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::wheelEvent (QWheelEvent* event)
+{
+  if (! d->m_inWheelEvent)
+    {
+      // Forward to the scrollbar (avoid recursion)
+      d->m_inWheelEvent = true;
+      QApplication::sendEvent (d->m_verticalScrollBar, event);
+      d->m_inWheelEvent = false;
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::horizontalScrollValueChanged (int value)
+{
+  d->setHorizontalScrollValue (value);
+}
+
+void QGenericTerminalImpl::verticalScrollValueChanged (int value)
+{
+  d->setVerticalScrollValue (value);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::updateSelection (void)
+{
+  d->updateSelection ();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::focusInEvent (QFocusEvent* event)
+{
+  emit set_global_shortcuts_signal (false);   // disable some shortcuts
+
+  setBlinkingCursorState (true);
+
+  QWidget::focusInEvent (event);
+}
+
+void QGenericTerminalImpl::focusOutEvent (QFocusEvent* event)
+{
+  emit set_global_shortcuts_signal (true);    // re-enable shortcuts
+
+  // Force the cursor to be redrawn.
+  d->m_cursorBlinking = true;
+
+  setBlinkingCursorState (false);
+
+  QWidget::focusOutEvent (event);
+}
+
+bool QGenericTerminalImpl::eventFilter (QObject *obj, QEvent * event)
+{
+  // if a keypress, filter out tab keys so that the next/prev tabbing is
+  // disabled - but we still need to pass along to the console .
+  if (event->type () == QEvent::KeyPress)
+  {
+    QKeyEvent* k = static_cast<QKeyEvent*>(event);
+    if (k->key () == Qt::Key_Tab)
+    {
+      sendText ("\t");
+      return true;
+    }
+  }
+  return false;
+}
+
+void QGenericTerminalImpl::keyPressEvent (QKeyEvent* event)
+{
+  QString s = translateKey (event);
+  if (!s.isEmpty ())
+    sendText (s);
+
+  if (d->m_hasBlinkingCursor)
+    {
+      d->m_blinkCursorTimer->start (d->BLINK_DELAY);
+
+      if (d->m_cursorBlinking)
+        blinkCursorEvent ();
+    }
+
+  QWidget::keyPressEvent (event);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::start (void)
+{
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::sendText (const QString& s)
+{
+  d->sendConsoleText (s);
+}
+
+void QGenericTerminalImpl::setCursorType (CursorType type, bool blinking)
+{
+  switch (type)
+    {
+    case UnderlineCursor:
+      d->m_cursorType = QConsolePrivate::UnderlineCursor;
+      break;
+
+    case BlockCursor:
+      d->m_cursorType = QConsolePrivate::BlockCursor;
+      break;
+
+    case IBeamCursor:
+      d->m_cursorType = QConsolePrivate::IBeamCursor;
+      break;
+    }
+
+  setBlinkingCursor (blinking);
+}
+
+void QGenericTerminalImpl::setBackgroundColor (const QColor& color)
+{
+  d->setBackgroundColor (color);
+}
+
+void QGenericTerminalImpl::setForegroundColor (const QColor& color)
+{
+  d->setForegroundColor (color);
+}
+
+void QGenericTerminalImpl::setSelectionColor (const QColor& color)
+{
+  d->setSelectionColor (color);
+}
+
+void QGenericTerminalImpl::setCursorColor (bool useForegroundColor,
+                                       const QColor& color)
+{
+  d->setCursorColor (useForegroundColor, color);
+}
+
+void QGenericTerminalImpl::setScrollBufferSize(int value)
+{
+  d->setScrollBufferSize (value);
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::setTerminalFont (const QFont& f)
+{
+  d->m_font = f;
+  d->m_consoleView->setFont (f);
+  d->updateConsoleSize (true);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::setSize (int columns, int lines)
+{
+  d->log ("emit set_screen_size_signal (%d, %d)\n", columns, lines);
+
+  emit set_screen_size_signal (columns, lines);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::copyClipboard ()
+{
+  if(!hasFocus()) return;
+
+  QClipboard *clipboard = QApplication::clipboard ();
+
+  QString selection = d->getSelection ();
+
+  if (selection.isEmpty ())
+    {
+      if (! _extra_interrupt)
+        terminal_interrupt ();
+    }
+  else
+    {
+      clipboard->setText (selection);
+      emit report_status_message (tr ("copied selection to clipboard"));
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::pasteClipboard (void)
+{
+  if(!hasFocus()) return;
+
+  QString text = QApplication::clipboard()->text (QClipboard::Clipboard);
+
+  if (! text.isEmpty ())
+    sendText (text);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::selectAll (void)
+{
+  if(!hasFocus()) return;
+
+  d->selectAll();
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+QString QGenericTerminalImpl::selectedText ()
+{
+  QString selection = d->getSelection ();
+  return selection;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::dragEnterEvent (QDragEnterEvent *event)
+{
+   if (event->mimeData ()->hasUrls ())
+     {
+       event->acceptProposedAction();
+     }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::dropEvent (QDropEvent *event)
+{
+  QString dropText;
+
+  if (event->mimeData ()->hasUrls ())
+    {
+      foreach (QUrl url, event->mimeData ()->urls ())
+        {
+          if(dropText.length () > 0)
+            dropText += '\n';
+          dropText  += url.toLocalFile ();
+        }
+      sendText (dropText);
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void QGenericTerminalImpl::has_extra_interrupt (bool extra)
+{
+  _extra_interrupt = extra;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libgui/qterminal/libqterminal/QGenericTerminalImpl.h	Tue May 14 15:26:56 2019 +0000
@@ -0,0 +1,119 @@
+/*
+
+Copyright (C) 2011-2019 Michael Goffioul
+
+This file is part of QConsole.
+
+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 3 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,
+see <https://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef __QConsole_h__
+#define __QConsole_h__ 1
+
+#include <QWidget>
+#include "QTerminal.h"
+class QFocusEvent;
+class QKeyEvent;
+class QPainter;
+class QPaintEvent;
+class QResizeEvent;
+class QWheelEvent;
+class QPoint;
+class QDragEnterEvent;
+class QDropEvent;
+
+class QConsolePrivate;
+class QConsoleThread;
+class QConsoleView;
+
+//////////////////////////////////////////////////////////////////////////////
+
+class QGenericTerminalImpl : public QTerminal
+{
+  Q_OBJECT
+  friend class QConsolePrivate;
+  friend class QConsoleThread;
+  friend class QConsoleView;
+
+public:
+  QGenericTerminalImpl (QWidget* parent = 0);
+  QGenericTerminalImpl (const QString& cmd, QWidget* parent = 0);
+  ~QGenericTerminalImpl (void);
+
+  void setTerminalFont (const QFont& font);
+  void setSize (int columns, int lines);
+  void sendText (const QString& s);
+  void setCursorType (CursorType type, bool blinking);
+
+  void setBackgroundColor (const QColor& color);
+  void setForegroundColor (const QColor& color);
+  void setSelectionColor (const QColor& color);
+  void setCursorColor (bool useForegoundColor, const QColor& color);
+  void setScrollBufferSize(int value);
+
+  QString selectedText ();
+
+  void has_extra_interrupt (bool);
+
+public slots:
+  void copyClipboard (void);
+  void pasteClipboard (void);
+  void selectAll (void);
+  void blinkCursorEvent (void);
+  void init_terminal_size (void);
+
+signals:
+  void terminated (void);
+  void titleChanged (const QString&);
+  void set_global_shortcuts_signal (bool);
+  void set_screen_size_signal (int, int);
+
+protected:
+  void viewPaintEvent (QConsoleView*, QPaintEvent*);
+  void setBlinkingCursor (bool blink);
+  void setBlinkingCursorState (bool blink);
+  void viewResizeEvent (QConsoleView*, QResizeEvent*);
+  void wheelEvent (QWheelEvent*);
+  void focusInEvent (QFocusEvent*);
+  void focusOutEvent (QFocusEvent*);
+  void keyPressEvent (QKeyEvent*);
+  virtual void start (void);
+  void mouseMoveEvent (QMouseEvent *event);
+  void mousePressEvent (QMouseEvent *event);
+  void mouseReleaseEvent (QMouseEvent *event);
+  void mouseDoubleClickEvent (QMouseEvent* event);
+  void mouseTripleClickEvent (QMouseEvent* event);
+
+  bool eventFilter(QObject *obj, QEvent *ev);
+
+  void dragEnterEvent(QDragEnterEvent *event);
+  void dropEvent(QDropEvent *event);
+
+private slots:
+  void horizontalScrollValueChanged (int value);
+  void verticalScrollValueChanged (int value);
+  void updateSelection (void);
+  void tripleClickTimeout (void);
+
+private:
+  QConsolePrivate* d;
+  bool allowTripleClick;
+  bool _extra_interrupt;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#endif // __QConsole_h__
--- a/libgui/qterminal/libqterminal/QTerminal.cc	Fri May 10 22:15:37 2019 +0200
+++ b/libgui/qterminal/libqterminal/QTerminal.cc	Tue May 14 15:26:56 2019 +0000
@@ -24,7 +24,11 @@
 #include "QTerminal.h"
 #include "gui-preferences.h"
 
-#if defined (Q_OS_WIN32)
+#define OCTAVE_USE_EXPERIMENTAL_GENERIC_TERMINAL 1
+
+#if defined (OCTAVE_USE_EXPERIMENTAL_GENERIC_TERMINAL)
+# include "QGenericTerminalImpl.h"
+#elif defined (Q_OS_WIN32)
 # include "win32/QWinTerminalImpl.h"
 #else
 # include "unix/QUnixTerminalImpl.h"
@@ -33,7 +37,9 @@
 QTerminal *
 QTerminal::create (QWidget *xparent)
 {
-#if defined (Q_OS_WIN32)
+#if defined (OCTAVE_USE_EXPERIMENTAL_GENERIC_TERMINAL)
+  return new QGenericTerminalImpl (xparent);
+#elif defined (Q_OS_WIN32)
   return new QWinTerminalImpl (xparent);
 #else
   return new QUnixTerminalImpl (xparent);
--- a/libgui/qterminal/module.mk	Fri May 10 22:15:37 2019 +0200
+++ b/libgui/qterminal/module.mk	Tue May 14 15:26:56 2019 +0000
@@ -1,5 +1,7 @@
 noinst_HEADERS += \
   %reldir%/libqterminal/QTerminal.h \
+  %reldir%/libqterminal/QGenericTerminalImpl.h \
+  %reldir%/libqterminal/QGenericTerminalColors.h \
   %reldir%/libqterminal/win32/QTerminalColors.h \
   %reldir%/libqterminal/win32/QWinTerminalImpl.h \
   %reldir%/libqterminal/unix/BlockArray.h \
@@ -112,6 +114,13 @@
 
 endif
 
+%canon_reldir%_libqterminal_la_SOURCES += \
+  %reldir%/libqterminal/QGenericTerminalColors.cpp \
+  %reldir%/libqterminal/QGenericTerminalImpl.cpp
+
+%canon_reldir%_libqterminal_la_MOC += \
+  %reldir%/libqterminal/moc-QGenericTerminalImpl.cc
+
 noinst_LTLIBRARIES += %reldir%/libqterminal.la
 
 libgui_CLEANFILES += $(%canon_reldir%_libqterminal_la_MOC)