GNU Octave  4.4.1
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
QWinTerminalImpl.cpp
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2011-2018 Michael Goffioul
4 
5 This file is part of QConsole.
6 
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this program. If not,
19 see <https://www.gnu.org/licenses/>.
20 
21 */
22 
23 #include <QApplication>
24 #include <QClipboard>
25 #include <QColor>
26 #include <QFont>
27 #include <QGridLayout>
28 #include <QPaintEvent>
29 #include <QPainter>
30 #include <QResizeEvent>
31 #include <QScrollBar>
32 #include <QtDebug>
33 #include <QThread>
34 #include <QTimer>
35 #include <QToolTip>
36 #include <QCursor>
37 #include <QMessageBox>
38 #include <QDragEnterEvent>
39 #include <QDropEvent>
40 #include <QUrl>
41 #include <QMimeData>
42 
43 #include <fcntl.h>
44 #include <io.h>
45 #include <stdio.h>
46 #include <stdarg.h>
47 #define WIN32_LEAN_AND_MEAN
48 #if ! defined (_WIN32_WINNT) && ! defined (NTDDI_VERSION)
49 #define _WIN32_WINNT 0x0500
50 #endif
51 #include <windows.h>
52 #include <versionhelpers.h>
53 #include <cstring>
54 #include <csignal>
55 #include <limits>
56 
57 #include "QWinTerminalImpl.h"
58 #include "QTerminalColors.h"
59 
60 // Uncomment to log activity to LOGFILENAME
61 // #define DEBUG_QCONSOLE
62 #define LOGFILENAME "QConsole.log"
63 // Uncomment to create hidden console window
64 #define HIDDEN_CONSOLE
65 
66 #ifdef _MSC_VER
67 # pragma warning(disable : 4996)
68 #endif
69 
70 //////////////////////////////////////////////////////////////////////////////
71 
72 class QConsoleView : public QWidget
73 {
74 public:
75  QConsoleView (QWinTerminalImpl* parent = 0) : QWidget (parent), q (parent) { }
76  ~QConsoleView (void) { }
77 
78 protected:
79  void paintEvent (QPaintEvent* event) { q->viewPaintEvent (this, event); }
80  void resizeEvent (QResizeEvent* event) { q->viewResizeEvent (this, event); }
81 
82 private:
84 };
85 
86 //////////////////////////////////////////////////////////////////////////////
87 
88 class QConsoleThread : public QThread
89 {
90 public:
91  QConsoleThread (QWinTerminalImpl* console) : QThread (console), q (console) { }
92 
93 protected:
94  void run (void)
95  { q->start (); }
96 
97 private:
99 };
100 
101 //////////////////////////////////////////////////////////////////////////////
102 
103 static QString translateKey (QKeyEvent *ev)
104 {
105  QString esc = "\x1b";
106  QString s;
107 
108  if (ev->key () == Qt::Key_Delete)
109  s = esc + "[C\b";
110  else if (!ev->text ().isEmpty ())
111  s = ev->text ();
112  else
113  {
114 
115  switch (ev->key ())
116  {
117  case Qt::Key_Up:
118  s = esc + "[A";
119  break;
120 
121  case Qt::Key_Down:
122  s = esc + "[B";
123  break;
124 
125  case Qt::Key_Right:
126  s = esc + "[C";
127  break;
128 
129  case Qt::Key_Left:
130  s = esc + "[D";
131  break;
132 
133  case Qt::Key_Home:
134  s = esc + "[H";
135  break;
136 
137  case Qt::Key_End:
138  s = esc + "[F";
139  break;
140 
141  case Qt::Key_Insert:
142  s = esc + "[2~";
143  break;
144 
145  case Qt::Key_PageUp:
146  s = esc + "[5~";
147  break;
148 
149  case Qt::Key_PageDown:
150  s = esc + "[6~";
151  break;
152 
153  case Qt::Key_Escape:
154  s = esc;
155  break;
156 
157  default:
158  break;
159  }
160  }
161 
162  return s;
163 }
164 
166 {
167  friend class QWinTerminalImpl;
168 
169 public:
170 
172  {
176  };
177 
178  QConsolePrivate (QWinTerminalImpl* parent, const QString& cmd = QString ());
179  ~QConsolePrivate (void);
180 
181  void updateConsoleSize (bool sync = false, bool allow_smaller_width = false);
182  void syncConsoleParameters (void);
183  void grabConsoleBuffer (CHAR_INFO* buf = 0);
184  void updateHorizontalScrollBar (void);
185  void updateVerticalScrollBar (void);
186  void setHorizontalScrollValue (int value);
187  void setVerticalScrollValue (int value);
188  void updateConsoleView (bool grab = true);
189  void monitorConsole (void);
190  void startCommand (void);
191  void sendConsoleText (const QString& s);
192  QRect cursorRect (void);
193  void selectAll();
194  void selectWord(const QPoint& cellPos);
195  void selectLine(const QPoint& cellPos);
196 
197  void log (const char* fmt, ...);
198 
199  void closeStandardIO (int fd, DWORD stdHandleId, const char* name);
200  void setupStandardIO (DWORD stdHandleId, int fd, const char* name,
201  const char* devName);
202 
203  QPoint posToCell (const QPoint& pt);
204  QString getSelection (void);
205  void updateSelection (void);
206  void clearSelection (void);
207 
208  QColor backgroundColor (void) const;
209  QColor foregroundColor (void) const;
210  QColor selectionColor (void) const;
211  QColor cursorColor (void) const;
212 
213  void setBackgroundColor (const QColor& color);
214  void setForegroundColor (const QColor& color);
215  void setSelectionColor (const QColor& color);
216  void setCursorColor (bool useForegroundColor, const QColor& color);
217  void setScrollBufferSize (int value);
218 
219  void drawTextBackground (QPainter& p, int cx1, int cy1, int cx2, int cy2,
220  int cw, int ch);
221 
222  void drawSelection (QPainter& p, int cx1, int cy1, int cx2, int cy2,
223  int cw, int ch);
224 
225  void drawCursor (QPainter& p);
226 
227  void drawText (QPainter& p, int cx1, int cy1, int cx2, int cy2,
228  int cw, int ch);
229 
230 private:
232 
233 private:
234  QFont m_font;
235  QString m_command;
238  QString m_title;
239 
240  QSize m_charSize;
244  QPoint m_cursorPos;
249 
253 
256 
257  HANDLE m_stdOut;
259  CHAR_INFO* m_buffer;
260  CHAR_INFO* m_tmpBuffer;
261  HANDLE m_process;
262 
265  QScrollBar* m_verticalScrollBar;
268 
269  // The delay in milliseconds between redrawing blinking text.
270  static const int BLINK_DELAY = 500;
271 };
272 
273 static void maybeSwapPoints (QPoint& begin, QPoint& end)
274 {
275  if (end.y () < begin.y ()
276  || (end.y () == begin.y () && end.x () < begin.x ()))
277  qSwap (begin, end);
278 }
279 
280 //////////////////////////////////////////////////////////////////////////////
281 
283  : q (parent), m_command (cmd), m_auto_scroll (true), m_cursorBlinking (false),
284  m_hasBlinkingCursor (true), m_cursorType (BlockCursor),
285  m_beginSelection (0, 0), m_endSelection (0, 0), m_settingSelection (false),
286  m_process (NULL), m_inWheelEvent (false)
287 {
288  log (NULL);
289 
290  // Possibly detach from any existing console
291  log ("Detaching from existing console (if any)...\n");
292  FreeConsole ();
293  log ("Closing standard IO...\n");
294  closeStandardIO (0, STD_INPUT_HANDLE, "STDIN");
295  closeStandardIO (1, STD_OUTPUT_HANDLE, "STDOUT");
296  closeStandardIO (2, STD_ERROR_HANDLE, "STDERR");
297 
298 #ifdef HIDDEN_CONSOLE
299  HWINSTA hOrigSta, hNewSta;
300 
301  // Create new (hidden) console
302  hOrigSta = GetProcessWindowStation ();
303  hNewSta = CreateWindowStation (NULL, 0, GENERIC_ALL, NULL);
304  log ("Current Windows station: %p.\nNew Windows station: %p.\n", hOrigSta,
305  hNewSta);
306  if (! SetProcessWindowStation (hNewSta))
307  log ("Failed to switch to new Windows station.\n");
308 #endif
309  if (! AllocConsole ())
310  log ("Failed to create new console.\n");
311 #ifdef HIDDEN_CONSOLE
312  if (! SetProcessWindowStation (hOrigSta))
313  log ("Failed to restore original Windows station.\n");
314  if (! CloseWindowStation (hNewSta))
315  log ("Failed to close new Windows station.\n");
316 #endif
317 
318  log ("New (hidden) console created.\n");
319 
320  setupStandardIO (STD_INPUT_HANDLE, 0, "STDIN", "CONIN$");
321  setupStandardIO (STD_OUTPUT_HANDLE, 1, "STDOUT", "CONOUT$");
322  setupStandardIO (STD_ERROR_HANDLE, 2, "STDERR", "CONOUT$");
323 
324  log ("Standard input/output/error set up.\n");
325 
326  *stdin = *(fdopen (0, "rb"));
327  *stdout = *(fdopen (1, "wb"));
328  *stderr = *(fdopen (2, "wb"));
329 
330  log ("POSIX standard streams created.\n");
331 
332  setvbuf (stdin, NULL, _IONBF, 0);
333  setvbuf (stdout, NULL, _IONBF, 0);
334  setvbuf (stderr, NULL, _IONBF, 0);
335 
336  log ("POSIX standard stream buffers adjusted.\n");
337 
338  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
339 
340  log ("Console allocated: hStdOut: %p\n", hStdOut);
341 
342  m_stdOut = hStdOut;
343  m_consoleWindow = GetConsoleWindow ();
344 
345  // In case the console window hasn't been created hidden...
346 #ifdef HIDDEN_CONSOLE
347  ShowWindow (m_consoleWindow, SW_HIDE);
348 #endif
349 
350  CONSOLE_SCREEN_BUFFER_INFO sbi;
351 
352  GetConsoleScreenBufferInfo (hStdOut, &sbi);
353  m_bufferSize = QSize (sbi.dwSize.X, qMax (sbi.dwSize.Y, (SHORT)500));
354  m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
355  sbi.srWindow.Right - sbi.srWindow.Left + 1,
356  sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
357  m_cursorPos = QPoint (sbi.dwCursorPosition.X, sbi.dwCursorPosition.Y);
358 
359  log ("Initial console parameters:\n");
360  log (" buffer size: %d x %d\n", m_bufferSize.width (),
361  m_bufferSize.height ());
362  log (" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
363  m_consoleRect.left (), m_consoleRect.top (),
364  m_consoleRect.right (), m_consoleRect.bottom (),
365  m_consoleRect.width (), m_consoleRect.height ());
366 
367  wchar_t titleBuf[260];
368  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
369  q->setWindowTitle (QString::fromWCharArray (titleBuf));
370 
371  m_font.setFamily ("Lucida Console");
372  m_font.setPointSize (9);
373  m_font.setStyleHint (QFont::TypeWriter);
374 
375  m_buffer = m_tmpBuffer = 0;
376 
377  m_consoleView = new QConsoleView (parent);
378  m_horizontalScrollBar = new QScrollBar (Qt::Horizontal, parent);
379  m_verticalScrollBar = new QScrollBar (Qt::Vertical, parent);
380 
381  QGridLayout* l = new QGridLayout (parent);
382  l->setContentsMargins (0, 0, 0, 0);
383  l->setSpacing (0);
384  l->addWidget (m_consoleView, 0, 0);
385  l->addWidget (m_horizontalScrollBar, 1, 0);
386  l->addWidget (m_verticalScrollBar, 0, 1);
387 
388  if (IsWindows7OrGreater ())
389  {
390  SetConsoleCP (65001);
391  SetConsoleOutputCP (65001);
392  }
393 
394  // Choose 15 (0xF) as index into the Windows console color map for the
395  // background and 0 (0x0) as the index for the foreground. This
396  // selection corresponds to the indices used in the foregroundColor,
397  // setForegroundColor, backgroundColor, and SetBackgroundColor
398  // functions.
399 
400  SetConsoleTextAttribute (m_stdOut, 0xF0);
401 
402  // Defaults.
403  setBackgroundColor (Qt::white);
404  setForegroundColor (Qt::black);
405  setSelectionColor (Qt::lightGray);
406  setCursorColor (false, Qt::darkGray);
407 
408  // FIXME -- should we set the palette?
409  QPalette palette (backgroundColor ());
410  m_consoleView->setPalette (palette);
411 
412  m_consoleView->setAutoFillBackground (true);
413 
414  m_consoleView->setFont (m_font);
415  parent->setFocusPolicy (Qt::StrongFocus);
416  parent->winId ();
417 
420 
421  m_consoleWatcher = new QTimer (parent);
422  m_consoleWatcher->setInterval (10);
423  m_consoleWatcher->setSingleShot (false);
424 
425  m_blinkCursorTimer = new QTimer (parent);
426  QObject::connect (m_blinkCursorTimer, SIGNAL (timeout()),
427  q, SLOT (blinkCursorEvent ()));
428 
429  QObject::connect (m_horizontalScrollBar, SIGNAL (valueChanged (int)),
430  q, SLOT (horizontalScrollValueChanged (int)));
431 
432  QObject::connect (m_verticalScrollBar, SIGNAL (valueChanged (int)),
433  q, SLOT (verticalScrollValueChanged (int)));
434 
435  QObject::connect (m_consoleWatcher, SIGNAL (timeout (void)),
436  q, SLOT (monitorConsole (void)));
437 
438  m_consoleWatcher->start ();
439 
440  if (m_command.isEmpty ())
441  m_consoleThread = 0;
442  else
443  {
445  QObject::connect (m_consoleThread, SIGNAL (finished (void)),
446  q, SIGNAL (terminated (void)));
447  m_consoleThread->start ();
448  }
449 }
450 
451 //////////////////////////////////////////////////////////////////////////////
452 
454 {
455  if (m_consoleThread && m_consoleThread->isRunning () && m_process)
456  {
457  TerminateProcess (m_process, (UINT)-1);
458  m_consoleThread->wait ();
459  }
460  if (m_buffer)
461  delete [] m_buffer;
462  if (m_tmpBuffer)
463  delete [] m_tmpBuffer;
464 }
465 
466 //////////////////////////////////////////////////////////////////////////////
467 
468 void QConsolePrivate::setupStandardIO (DWORD stdHandleId, int targetFd,
469  const char* name, const char* devName)
470 {
471  log ("Opening %s...\n", devName);
472 
473  int fd = open (devName, _O_RDWR | _O_BINARY);
474 
475  if (fd != -1)
476  {
477  if (fd != targetFd)
478  {
479  log ("Opened %s is not at target file descriptor %d, "
480  "duplicating...\n", name, targetFd);
481  if (dup2 (fd, targetFd) == -1)
482  log ("Failed to duplicate file descriptor: errno=%d.\n", errno);
483  if (close (fd) == -1)
484  log ("Failed to close original file descriptor: errno=%d.\n",
485  errno);
486  }
487  else
488  log ("%s opened and assigned to file descriptor %d.\n", devName, fd);
489  if (! SetStdHandle (stdHandleId, (HANDLE) _get_osfhandle (targetFd)))
490  log ("Failed to re-assign %s: error=%08x.\n", name, GetLastError ());
491  }
492  else
493  log ("Failed to open %s: errno=%d.\n", devName, errno);
494 }
495 
496 QPoint QConsolePrivate::posToCell (const QPoint& p)
497 {
498  return QPoint (m_consoleRect.left () + p.x () / m_charSize.width (),
499  m_consoleRect.top () + p.y () / m_charSize.height ());
500 }
501 
503 {
504  QString selection;
505 
506  QPoint begin = m_beginSelection;
507  QPoint end = m_endSelection;
508 
509  maybeSwapPoints (begin, end);
510 
511  if (begin != end)
512  {
513  CHAR_INFO* buf;
514  COORD bufSize, bufCoord;
515  SMALL_RECT bufRect;
516  int nr;
517 
518  nr = end.y () - begin.y () + 1;
519  buf = new CHAR_INFO[m_bufferSize.width () * nr];
520  bufSize.X = m_bufferSize.width ();
521  bufSize.Y = nr;
522  bufCoord.X = 0;
523  bufCoord.Y = 0;
524 
525  bufRect.Left = 0;
526  bufRect.Right = m_bufferSize.width ();
527  bufRect.Top = begin.y ();
528  bufRect.Bottom = end.y ();
529 
530  if (ReadConsoleOutput (m_stdOut, buf, bufSize, bufCoord, &bufRect))
531  {
532  int start_pos = begin.x ();
533  int end_pos = (nr - 1) * m_bufferSize.width () + end.x ();
534  int lastNonSpace = -1;
535 
536  for (int i = start_pos; i <= end_pos; i++)
537  {
538  if (i && (i % m_bufferSize.width ()) == 0)
539  {
540  if (lastNonSpace >= 0)
541  selection.truncate (lastNonSpace);
542  selection.append ('\n');
543  lastNonSpace = selection.length ();
544  }
545 
546  QChar c (buf[i].Char.UnicodeChar);
547  if (c.isNull ())
548  c = QChar (' ');
549 
550  selection.append (c);
551  if (! c.isSpace ())
552  lastNonSpace = selection.length ();
553  }
554 
555  if (lastNonSpace >= 0)
556  selection.truncate (lastNonSpace);
557  }
558  }
559 
560  return selection;
561 }
562 
564 {
565  QPoint begin = m_beginSelection;
566  QPoint end = m_endSelection;
567 
568  maybeSwapPoints (begin, end);
569 
570  begin.rx () = 0;
571  end.rx () = m_consoleRect.width ();
572 
573  m_consoleView->update ();
574 }
575 
577 {
578  m_beginSelection = m_endSelection = QPoint ();
579 
580  m_consoleView->update ();
581 }
582 
584 {
585  return m_colors[15];
586 }
587 
589 {
590  return m_colors[0];
591 }
592 
594 {
595  return m_selectionColor;
596 }
597 
598 QColor QConsolePrivate::cursorColor (void) const
599 {
600  return m_cursorColor.isValid () ? m_cursorColor : foregroundColor ();
601 }
602 
603 void QConsolePrivate::setBackgroundColor (const QColor& color)
604 {
605  m_colors[15] = color;
606 
607  QPalette palette (color);
608  m_consoleView->setPalette (palette);
609 }
610 
611 void QConsolePrivate::setForegroundColor (const QColor& color)
612 {
613  m_colors[0] = color;
614 }
615 
616 void QConsolePrivate::setSelectionColor (const QColor& color)
617 {
618  m_selectionColor = color;
619 }
620 
621 void QConsolePrivate::setCursorColor (bool useForegroundColor,
622  const QColor& color)
623 {
624  m_cursorColor = useForegroundColor ? QColor () : color;
625 }
626 
628 {
629  CONSOLE_SCREEN_BUFFER_INFO sbi;
630  GetConsoleScreenBufferInfo (m_stdOut, &sbi);
631 
632  m_bufferSize = QSize (sbi.dwSize.X, (SHORT)value);
633 
634  updateConsoleSize (true);
635 }
636 
637 void QConsolePrivate::drawTextBackground (QPainter& p, int cx1, int cy1,
638  int cx2, int cy2, int cw, int ch)
639 {
640  p.save ();
641 
642  int ascent = p.fontMetrics ().ascent ();
643  int stride = m_consoleRect.width ();
644  int y = ascent + cy1 * ch;;
645 
646  for (int j = cy1; j <= cy2; j++, y += ch)
647  {
648  int len = 0;
649  bool hasChar = false;
650  int x = cx1 * cw;
651  WORD attr = 0;
652 
653  for (int i = cx1; i <= cx2; i++)
654  {
655  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
656 
657  if ((ci->Attributes & 0x00ff) != attr)
658  {
659  // Character attributes changed
660  if (len != 0)
661  {
662  // String buffer not empty -> draw it
663  if (hasChar || (attr & 0x00f0))
664  {
665  if (attr & 0x00f0)
666  p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
667  }
668 
669  x += (len * cw);
670  len = 0;
671  hasChar = false;
672  }
673  // Update current brush and store current attributes
674  attr = (ci->Attributes & 0x00ff);
675  p.setBrush (m_colors[(attr >> 4) & 0x000f]);
676  }
677 
678  // Append current character to the string buffer
679  len++;
680  if (ci->Char.UnicodeChar != L' ')
681  hasChar = true;
682  }
683 
684  if (len != 0 && (hasChar || (attr & 0x00f0)))
685  {
686  // Line end reached, but string buffer not empty -> draw it
687  // No need to update s or x, they will be reset on the next
688  // for-loop iteration
689 
690  if (attr & 0x00f0)
691  p.fillRect (x, y-ascent, len * cw, ch, p.brush ());
692  }
693  }
694 
695  p.restore ();
696 }
697 
699 {
700  m_beginSelection = QPoint (0,0);
701  m_endSelection = QPoint(m_bufferSize.width (),
702  m_cursorPos.y());
703  updateSelection();
704 }
705 
706 void QConsolePrivate::selectWord (const QPoint & cellpos)
707 {
708  QPoint begin = cellpos;
709  QPoint end = cellpos;
710 
711  int stride = m_consoleRect.width ();
712 
713  int verticalScrollOffset = m_consoleRect.top ();
714  int horizontalScrollOffset = m_consoleRect.left ();
715 
716  // get begin, end in buffer offsets
717  begin.ry () -= verticalScrollOffset;
718  end.ry () -= verticalScrollOffset;
719 
720  begin.rx () -= horizontalScrollOffset;
721  end.rx () -= horizontalScrollOffset;
722 
723  // loog at current clicked on char to determinate ig getting space chunk or nonspace chunk
724  if (QChar(m_buffer[begin.y ()*stride + begin.x ()].Char.UnicodeChar).isSpace () == false)
725  {
726  // from current char, go back and fwd to find start and end of block
727  while(begin.x () > 0 &&
728  QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace() == false)
729  {
730  begin.rx () --;
731  }
732 
733  while(end.x () < m_consoleRect.width () &&
734  QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace() == false)
735  {
736  end.rx () ++;
737  }
738  }
739  else
740  {
741  while(begin.x () > 0 &&
742  QChar(m_buffer[begin.y ()*stride + begin.x () -1].Char.UnicodeChar).isSpace())
743  {
744  begin.rx () --;
745  }
746 
747  while(end.x () < m_consoleRect.width () &&
748  QChar(m_buffer[end.y ()*stride + end.x () +1].Char.UnicodeChar).isSpace ())
749  {
750  end.rx () ++;
751  }
752  }
753 
754  // convert console offsets to absolute cell positions
755  begin.ry () += verticalScrollOffset;
756  end.ry () += verticalScrollOffset;
757 
758  begin.rx () += horizontalScrollOffset;
759  end.rx () += horizontalScrollOffset;
760 
761  m_beginSelection = begin;
762  m_endSelection = end;
763 
764  updateSelection ();
765 }
766 
767 void QConsolePrivate::selectLine (const QPoint & cellpos)
768 {
769  m_beginSelection = QPoint (0, cellpos.y ());
770  m_endSelection = QPoint (m_bufferSize.width ()-1, cellpos.y ());
771  updateSelection ();
772 }
773 
774 
775 void QConsolePrivate::drawSelection (QPainter& p, int cx1, int cy1,
776  int cx2, int cy2, int cw, int ch)
777 {
778  p.save ();
779 
780  QPoint begin = m_beginSelection;
781  QPoint end = m_endSelection;
782 
783  bool haveSelection = (begin != end);
784 
785  if (haveSelection)
786  maybeSwapPoints (begin, end);
787 
788  int verticalScrollOffset = m_consoleRect.top ();
789  int horizontalScrollOffset = m_consoleRect.left ();
790 
791  begin.ry () -= verticalScrollOffset;
792  end.ry () -= verticalScrollOffset;
793 
794  begin.rx () -= horizontalScrollOffset;
795  end.rx () -= horizontalScrollOffset;
796 
797  int ascent = p.fontMetrics ().ascent ();
798  int stride = m_consoleRect.width ();
799 
800  int y = ascent + cy1 * ch;;
801  for (int j = cy1; j <= cy2; j++, y += ch)
802  {
803  int charsThisLine = 0;
804  int len = 0;
805  bool hasChar = false;
806  WORD attr = 0;
807 
808  for (int i = cx1; i <= cx2; i++)
809  {
810  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
811 
812  if ((ci->Attributes & 0x00ff) != attr)
813  {
814  // Character attributes changed
815  if (len != 0)
816  {
817  charsThisLine += len;
818  len = 0;
819  hasChar = false;
820  }
821 
822  // Store current attributes
823  attr = (ci->Attributes & 0x00ff);
824  }
825 
826  // Append current character to the string buffer
827  len++;
828  if (ci->Char.UnicodeChar != L' ')
829  hasChar = true;
830  }
831 
832  if (len != 0 && (hasChar || (attr & 0x00f0)))
833  charsThisLine += len;
834 
835  if (haveSelection && j >= begin.y () && j <= end.y ())
836  {
837  int selectionBegin = j == begin.y () ? begin.x (): 0;
838 
839  int len = ((j == end.y () && end.x () < charsThisLine)
840  ? end.x () - selectionBegin + 1
841  : stride - selectionBegin);
842 
843  p.fillRect (selectionBegin * cw, y-ascent, len * cw, ch,
844  selectionColor ());
845  }
846  }
847 
848  p.restore ();
849 }
850 
852 {
853  if (! m_cursorBlinking)
854  {
855  p.save ();
856 
857  QRect rect = cursorRect ();
858  QColor color = cursorColor ();
859 
860  p.setPen (color);
861 
863  {
864  if (q->hasFocus ())
865  p.fillRect (rect, color);
866  else
867  {
868  // draw the cursor outline, adjusting the area so that
869  // it is draw entirely inside 'rect'
870 
871  int penWidth = qMax (1, p.pen().width());
872 
873  p.drawRect (rect.adjusted (penWidth/2, penWidth/2,
874  - penWidth/2 - penWidth%2,
875  - penWidth/2 - penWidth%2));
876  }
877  }
879  {
880  p.drawLine (rect.left (), rect.bottom (),
881  rect.right (), rect.bottom ());
882  }
884  {
885  p.drawLine (rect.left (), rect.top (),
886  rect.left (), rect.bottom ());
887  }
888 
889  p.restore ();
890  }
891 }
892 
893 void QConsolePrivate::drawText (QPainter& p, int cx1, int cy1,
894  int cx2, int cy2, int cw, int ch)
895 {
896  p.save ();
897 
898  p.setFont (m_font);
899  p.setPen (foregroundColor ());
900 
901  QString s;
902  s.reserve (cx2 - cx1 + 1);
903 
904  int ascent = p.fontMetrics ().ascent ();
905  int stride = m_consoleRect.width ();
906 
907  int y = ascent + cy1 * ch;;
908  for (int j = cy1; j <= cy2; j++, y += ch)
909  {
910  // Reset string buffer and starting X coordinate
911  s.clear ();
912  bool hasChar = false;
913  int x = cx1 * cw;
914  WORD attr = 0;
915 
916  for (int i = cx1; i <= cx2; i++)
917  {
918  CHAR_INFO* ci = &(m_buffer[stride*j+i]);
919 
920  if ((ci->Attributes & 0x00ff) != attr)
921  {
922  // Character attributes changed
923  if (! s.isEmpty ())
924  {
925  // String buffer not empty -> draw it
926  if (hasChar || (attr & 0x00f0))
927  p.drawText (x, y, s);
928 
929  x += (s.length () * cw);
930  s.clear ();
931  hasChar = false;
932  }
933  // Update current pen and store current attributes
934  attr = (ci->Attributes & 0x00ff);
935  p.setPen (m_colors[attr & 0x000f]);
936  }
937 
938  // Append current character to the string buffer
939  s.append (ci->Char.UnicodeChar);
940  if (ci->Char.UnicodeChar != L' ')
941  hasChar = true;
942  }
943 
944  if (! s.isEmpty () && (hasChar || (attr & 0x00f0)))
945  {
946  // Line end reached, but string buffer not empty -> draw it
947  // No need to update s or x, they will be reset on the next
948  // for-loop iteration
949 
950  p.drawText (x, y, s);
951  }
952  }
953 
954  p.restore ();
955 }
956 
957 /////////////////////////////////////////////////////////////////////////////
958 
959 void QConsolePrivate::closeStandardIO (int fd, DWORD stdHandleId,
960  const char* name)
961 {
962  if (close (fd) == -1)
963  log ("Failed to close file descriptor %d: errno=%d.\n", fd, errno);
964  if (! CloseHandle (GetStdHandle (stdHandleId)))
965  log ("Failed to close Win32 %s: error=%08x.\n", name, GetLastError ());
966 }
967 
968 //////////////////////////////////////////////////////////////////////////////
969 
970 void QConsolePrivate::log (const char* fmt, ...)
971 {
972 #ifdef DEBUG_QCONSOLE
973  if (fmt)
974  {
975  va_list l;
976  FILE* flog = fopen (LOGFILENAME, "ab");
977 
978  va_start (l, fmt);
979  vfprintf (flog, fmt, l);
980  va_end (l);
981  fclose (flog);
982  }
983  else
984  {
985  // Special case to re-initialize the log file
986  FILE* flog = fopen (LOGFILENAME, "w");
987  fclose (flog);
988  }
989 #else
990  Q_UNUSED (fmt);
991 #endif
992 }
993 
994 //////////////////////////////////////////////////////////////////////////////
995 
996 void QConsolePrivate::updateConsoleSize (bool sync, bool allow_smaller_width)
997 {
998  QFontMetrics fm = m_consoleView->fontMetrics ();
999  QSize winSize = m_consoleView->size ();
1000 
1001  m_charSize.rwidth () = fm.averageCharWidth ();
1002  m_charSize.rheight () = fm.lineSpacing ();
1003 
1004  m_consoleRect.setWidth (winSize.width () / fm.averageCharWidth ());
1005  m_consoleRect.setHeight (winSize.height () / fm.lineSpacing ());
1006 
1007  // Don't shrink the size of the buffer. That way wide lines won't be
1008  // truncated and will reappear if the window is enlarged again later.
1009 
1010  if (allow_smaller_width || m_consoleRect.width () > m_bufferSize.width ())
1011  m_bufferSize.rwidth () = m_consoleRect.width ();
1012 
1013  if (qMax (m_bufferSize.height (), m_consoleRect.height ())
1014  > m_bufferSize.height ())
1015  m_bufferSize.rheight () = qMax (m_bufferSize.height (),
1016  m_consoleRect.height ());
1017 
1018  // Store the terminal size in the environment. When Octave is
1019  // initialized, we ask the command editor (usually readline) to prefer
1020  // using these values rather than querying the terminal so that the
1021  // buffer size can be larger than the size of the window that the
1022  // command editor will actually use.
1023 
1024  qputenv ("LINES", QByteArray::number (m_consoleRect.height ()));
1025  qputenv ("COLUMNS", QByteArray::number (m_consoleRect.width ()));
1026 
1027  // Force the command line editor (usually readline) to notice the
1028  // change in screen size as soon as possible.
1029 
1030  q->setSize (m_consoleRect.height (), m_consoleRect.width ());
1031 
1032  m_consoleRect.moveLeft (0);
1033  if (m_consoleRect.bottom () >= m_bufferSize.height ())
1034  m_consoleRect.moveTop (m_bufferSize.height () - m_consoleRect.height ());
1035 
1036  log ("Console resized:\n");
1037  log (" widget size: %d x %d\n", winSize.width (), winSize.height ());
1038  log (" buffer size: %d x %d\n", m_bufferSize.width (),
1039  m_bufferSize.height ());
1040  log (" window: (%d, %d) -> (%d, %d) [%d x %d]\n",
1041  m_consoleRect.left (), m_consoleRect.top (),
1042  m_consoleRect.right (), m_consoleRect.bottom (),
1043  m_consoleRect.width (), m_consoleRect.height ());
1044 
1045  if (sync)
1047 
1050 }
1051 
1052 //////////////////////////////////////////////////////////////////////////////
1053 
1055 {
1056  CONSOLE_SCREEN_BUFFER_INFO sbi;
1057  HANDLE hStdOut = m_stdOut;
1058 
1059  GetConsoleScreenBufferInfo (hStdOut, &sbi);
1060 
1061  COORD bs;
1062  SMALL_RECT sr;
1063 
1064  bs.X = sbi.dwSize.X;
1065  bs.Y = m_bufferSize.height ();
1066  sr.Left = sbi.srWindow.Left;
1067  sr.Right = sbi.srWindow.Right;
1068  sr.Top = m_consoleRect.top ();
1069  sr.Bottom = m_consoleRect.bottom ();
1070 
1071  if (bs.Y > sbi.dwSize.Y)
1072  {
1073  SetConsoleScreenBufferSize (hStdOut, bs);
1074  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1075  }
1076  else
1077  {
1078  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1079  SetConsoleScreenBufferSize (hStdOut, bs);
1080  }
1081 
1082  bs.X = m_bufferSize.width ();
1083  sr.Left = m_consoleRect.left ();
1084  sr.Right = m_consoleRect.right ();
1085 
1086  if (bs.X > sbi.dwSize.X)
1087  {
1088  SetConsoleScreenBufferSize (hStdOut, bs);
1089  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1090  }
1091  else
1092  {
1093  SetConsoleWindowInfo (hStdOut, TRUE, &sr);
1094  SetConsoleScreenBufferSize (hStdOut, bs);
1095  }
1096 
1097  log ("Sync'ing console parameters:\n");
1098  log (" buffer size: %d x %d\n", bs.X, bs.Y);
1099  log (" window: (%d, %d) -> (%d, %d)\n",
1100  sr.Left, sr.Top, sr.Right, sr.Bottom);
1101 
1102  if (m_buffer)
1103  delete [] m_buffer;
1104  if (m_tmpBuffer)
1105  delete [] m_tmpBuffer;
1106 
1107  int bufSize = m_consoleRect.width () * m_consoleRect.height ();
1108 
1109  m_buffer = new CHAR_INFO[bufSize];
1110  m_tmpBuffer = new CHAR_INFO[bufSize];
1111 }
1112 
1113 //////////////////////////////////////////////////////////////////////////////
1114 
1116 {
1117  COORD bs, bc;
1118  SMALL_RECT r;
1119 
1120  bs.X = m_consoleRect.width ();
1121  bs.Y = m_consoleRect.height ();
1122  bc.X = 0;
1123  bc.Y = 0;
1124 
1125  r.Left = m_consoleRect.left ();
1126  r.Top = m_consoleRect.top ();
1127  r.Right = m_consoleRect.right ();
1128  r.Bottom = m_consoleRect.bottom ();
1129 
1130  log ("ReadConsoleOutput (%d,%d) -> (%d,%d)\n", r.Left, r.Top, r.Right, r.Bottom);
1131  if (! ReadConsoleOutput (m_stdOut, (buf ? buf : m_buffer), bs, bc, &r))
1132  qCritical ("cannot read console output");
1133 }
1134 
1135 //////////////////////////////////////////////////////////////////////////////
1136 
1138 {
1139  m_horizontalScrollBar->setMinimum (0);
1140  if (m_bufferSize.width () > m_consoleRect.width ())
1141  m_horizontalScrollBar->setMaximum (m_bufferSize.width () - m_consoleRect.width ());
1142  else
1143  m_horizontalScrollBar->setMaximum (0);
1144  m_horizontalScrollBar->setSingleStep (1);
1145  m_horizontalScrollBar->setPageStep (m_consoleRect.width ());
1146  m_horizontalScrollBar->setValue (m_consoleRect.left ());
1147 
1148  log ("Horizontal scrollbar parameters updated: %d/%d/%d/%d\n",
1149  m_horizontalScrollBar->minimum (),
1150  m_horizontalScrollBar->maximum (),
1151  m_horizontalScrollBar->singleStep (),
1152  m_horizontalScrollBar->pageStep ());
1153 }
1154 
1156 {
1157  m_verticalScrollBar->setMinimum (0);
1158  if (m_bufferSize.height () > m_consoleRect.height ())
1159  m_verticalScrollBar->setMaximum (m_bufferSize.height () - m_consoleRect.height ());
1160  else
1161  m_verticalScrollBar->setMaximum (0);
1162  m_verticalScrollBar->setSingleStep (1);
1163  m_verticalScrollBar->setPageStep (m_consoleRect.height ());
1164  m_verticalScrollBar->setValue (m_consoleRect.top ());
1165 
1166  log ("Vertical scrollbar parameters updated: %d/%d/%d/%d\n",
1167  m_verticalScrollBar->minimum (), m_verticalScrollBar->maximum (),
1168  m_verticalScrollBar->singleStep (), m_verticalScrollBar->pageStep ());
1169 }
1170 
1171 //////////////////////////////////////////////////////////////////////////////
1172 
1174 {
1175  if (value == m_consoleRect.left ())
1176  return;
1177 
1178  SMALL_RECT r;
1179  HANDLE hStdOut = m_stdOut;
1180 
1181  if (value + m_consoleRect.width () > m_bufferSize.width ())
1182  value = m_bufferSize.width () - m_consoleRect.width ();
1183 
1184  r.Left = value;
1185  r.Top = m_consoleRect.top ();
1186  r.Right = value + m_consoleRect.width () - 1;
1187  r.Bottom = m_consoleRect.bottom ();
1188 
1189  log ("Scrolling window horizontally: (%d, %d) -> (%d, %d) [%d x %d]\n",
1190  r.Left, r.Top, r.Right, r.Bottom,
1191  r.Right - r.Left + 1, r.Bottom - r.Top + 1);
1192 
1193  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
1194  {
1195  m_consoleRect.moveLeft (value);
1196  updateConsoleView ();
1197  }
1198 }
1199 
1201 {
1202  if (value == m_consoleRect.top ())
1203  return;
1204 
1205  SMALL_RECT r;
1206  HANDLE hStdOut = m_stdOut;
1207 
1208  if (value + m_consoleRect.height () > m_bufferSize.height ())
1209  value = m_bufferSize.height () - m_consoleRect.height ();
1210 
1211  r.Left = m_consoleRect.left ();
1212  r.Top = value;
1213  r.Right = m_consoleRect.right ();
1214  r.Bottom = value + m_consoleRect.height () - 1;
1215 
1216  log ("Scrolling window vertically: (%d, %d) -> (%d, %d) [%d x %d]\n",
1217  r.Left, r.Top, r.Right, r.Bottom,
1218  r.Right - r.Left + 1, r.Bottom - r.Top + 1);
1219 
1220  if (SetConsoleWindowInfo (hStdOut, TRUE, &r))
1221  {
1222  m_consoleRect.moveTop (value);
1223 
1224  CONSOLE_SCREEN_BUFFER_INFO sbi;
1225  if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1226  {
1227  if (sbi.dwCursorPosition.Y > m_consoleRect.bottom ())
1228  m_auto_scroll = false;
1229  else
1230  m_auto_scroll = true;
1231  }
1232 
1233  updateConsoleView ();
1234  }
1235 }
1236 
1237 //////////////////////////////////////////////////////////////////////////////
1238 
1240 {
1241  if (grab)
1242  grabConsoleBuffer ();
1243  m_consoleView->update ();
1244  m_consoleWatcher->start ();
1245 }
1246 
1247 //////////////////////////////////////////////////////////////////////////////
1248 
1250 {
1251  CONSOLE_SCREEN_BUFFER_INFO sbi;
1252  HANDLE hStdOut = GetStdHandle (STD_OUTPUT_HANDLE);
1253 
1254  static wchar_t titleBuf[260];
1255 
1256  GetConsoleTitleW (titleBuf, sizeof (titleBuf));
1257  QString title = QString::fromWCharArray (titleBuf);
1258 
1259  if (title != m_title)
1260  {
1261  q->setWindowTitle (title);
1262  emit q->titleChanged (title);
1263  }
1264 
1265  if (GetConsoleScreenBufferInfo (hStdOut, &sbi))
1266  {
1267  if (m_bufferSize.width () != sbi.dwSize.X
1268  || m_bufferSize.height () != sbi.dwSize.Y)
1269  {
1270  // Buffer size changed
1271  m_bufferSize.rwidth () = sbi.dwSize.X;
1272  m_bufferSize.rheight () = sbi.dwSize.Y;
1275  }
1276 
1277  if (m_cursorPos.x () != sbi.dwCursorPosition.X
1278  || m_cursorPos.y () != sbi.dwCursorPosition.Y)
1279  {
1280  // Cursor position changed
1281  m_consoleView->update
1282  ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
1283  (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
1284  m_charSize.width (), m_charSize.height ());
1285  m_cursorPos.rx () = sbi.dwCursorPosition.X;
1286  m_cursorPos.ry () = sbi.dwCursorPosition.Y;
1287  m_consoleView->update
1288  ((m_cursorPos.x () - sbi.srWindow.Left) * m_charSize.width (),
1289  (m_cursorPos.y () - sbi.srWindow.Top) * m_charSize.height (),
1290  m_charSize.width (), m_charSize.height ());
1291  }
1292 
1293  if (m_consoleRect.left () != sbi.srWindow.Left
1294  || m_consoleRect.right () != sbi.srWindow.Right
1295  || (m_auto_scroll &&
1296  (m_consoleRect.top () != sbi.srWindow.Top
1297  || m_consoleRect.bottom () != sbi.srWindow.Bottom)))
1298  {
1299  // Console window changed
1300  log ("--> Console window changed\n");
1301  m_consoleRect = QRect (sbi.srWindow.Left, sbi.srWindow.Top,
1302  sbi.srWindow.Right - sbi.srWindow.Left + 1,
1303  sbi.srWindow.Bottom - sbi.srWindow.Top + 1);
1306  updateConsoleView ();
1307  return;
1308  }
1309 
1310  if (m_tmpBuffer && m_buffer)
1311  {
1313  if (memcmp (m_tmpBuffer, m_buffer,
1314  sizeof (CHAR_INFO) * m_consoleRect.width () *
1315  m_consoleRect.height ()))
1316  {
1317  // FIXME: compute the area to update based on the
1318  // difference between the 2 buffers.
1319  qSwap (m_buffer, m_tmpBuffer);
1320  updateConsoleView (false);
1321  }
1322  }
1323  }
1324 }
1325 
1326 //////////////////////////////////////////////////////////////////////////////
1327 
1329 {
1330  QString cmd = m_command;
1331 
1332  if (cmd.isEmpty ())
1333  cmd = qgetenv ("COMSPEC").constData ();
1334 
1335  if (! cmd.isEmpty ())
1336  {
1337  STARTUPINFO si;
1338  PROCESS_INFORMATION pi;
1339 
1340  ZeroMemory (&si, sizeof (si));
1341  si.cb = sizeof (si);
1342  ZeroMemory (&pi, sizeof (pi));
1343 
1344  if (CreateProcessW (NULL,
1345  (LPWSTR)cmd.unicode (),
1346  NULL,
1347  NULL,
1348  TRUE,
1349  0,
1350  NULL,
1351  NULL,
1352  &si,
1353  &pi))
1354  {
1355  CloseHandle (pi.hThread);
1356  m_process = pi.hProcess;
1357  WaitForSingleObject (m_process, INFINITE);
1358  CloseHandle (m_process);
1359  m_process = NULL;
1360  }
1361  }
1362 }
1363 
1364 //////////////////////////////////////////////////////////////////////////////
1365 
1367 {
1368  // Send the string in chunks of 512 characters. Each character is
1369  // translated into an equivalent keypress event.
1370 
1371 #define TEXT_CHUNK_SIZE 512
1372 
1373  // clear any selection on inserting text
1374  clearSelection();
1375  // enable auto-scrolling
1376  m_auto_scroll = true;
1377 
1378  int len = s.length ();
1379  INPUT_RECORD events[TEXT_CHUNK_SIZE];
1380  DWORD nEvents = 0, written;
1381  HANDLE hStdIn = GetStdHandle (STD_INPUT_HANDLE);
1382 
1383  ZeroMemory (events, sizeof (events));
1384 
1385  for (int i = 0; i < len; i++)
1386  {
1387  QChar c = s.at (i);
1388 
1389  if (c == L'\r' || c == L'\n')
1390  {
1391  if (c == L'\r' && i < (len - 1) && s.at (i+1) == L'\n')
1392  i++;
1393 
1394  // add new line
1395  events[nEvents].EventType = KEY_EVENT;
1396  events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1397  events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1398  events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1399  VK_RETURN;
1400  events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1401  events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1402  events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1403  nEvents++;
1404 
1405  WriteConsoleInput (hStdIn, events, nEvents, &written);
1406  nEvents = 0;
1407  ZeroMemory (events, sizeof (events));
1408 
1409  }
1410  else
1411  {
1412  events[nEvents].EventType = KEY_EVENT;
1413  events[nEvents].Event.KeyEvent.bKeyDown = TRUE;
1414  events[nEvents].Event.KeyEvent.wRepeatCount = 1;
1415  events[nEvents].Event.KeyEvent.wVirtualKeyCode =
1416  LOBYTE (VkKeyScan (c.unicode ()));
1417  events[nEvents].Event.KeyEvent.wVirtualScanCode = 0;
1418  events[nEvents].Event.KeyEvent.uChar.UnicodeChar = c.unicode ();
1419  events[nEvents].Event.KeyEvent.dwControlKeyState = 0;
1420  nEvents++;
1421  }
1422 
1423  if (nEvents == TEXT_CHUNK_SIZE
1424  || (nEvents > 0 && i == (len - 1)))
1425  {
1426  WriteConsoleInput (hStdIn, events, nEvents, &written);
1427  nEvents = 0;
1428  ZeroMemory (events, sizeof (events));
1429  }
1430  }
1431 }
1432 
1433 QRect
1435 {
1436  int cw = m_charSize.width ();
1437  int ch = m_charSize.height ();
1438 
1439  return QRect ((m_cursorPos.x () - m_consoleRect.x ()) * cw,
1440  (m_cursorPos.y () - m_consoleRect.y ()) * ch,
1441  cw, ch);
1442 }
1443 
1444 //////////////////////////////////////////////////////////////////////////////
1445 
1447  : QTerminal (parent), d (new QConsolePrivate (this)),
1449 {
1450  installEventFilter (this);
1451 
1452  connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
1453  parent, SLOT (set_global_shortcuts (bool)));
1454  connect (this, SIGNAL (set_global_shortcuts_signal (bool)),
1455  this, SLOT (set_global_shortcuts (bool)));
1456 
1457  connect (this, SIGNAL (set_screen_size_signal (int, int)),
1458  parent, SLOT (set_screen_size (int, int)));
1459 
1460  setAcceptDrops (true);
1461 }
1462 
1463 //////////////////////////////////////////////////////////////////////////////
1464 
1465 QWinTerminalImpl::QWinTerminalImpl (const QString& cmd, QWidget* parent)
1466  : QTerminal (parent), d (new QConsolePrivate (this, cmd))
1467 {
1468 }
1469 
1470 //////////////////////////////////////////////////////////////////////////////
1471 
1473 {
1474  delete d;
1475 }
1476 
1477 void QWinTerminalImpl::mouseMoveEvent (QMouseEvent *event)
1478 {
1479  if (d->m_settingSelection)
1480  {
1481  d->m_endSelection = d->posToCell (event->pos ());
1482 
1483  updateSelection ();
1484  }
1485 }
1486 
1487 void QWinTerminalImpl::mousePressEvent (QMouseEvent *event)
1488 {
1489  if (allowTripleClick)
1490  {
1491  mouseTripleClickEvent (event);
1492  }
1493  else if (event->button () == Qt::LeftButton)
1494  {
1495  d->m_settingSelection = true;
1496 
1497  d->m_beginSelection = d->posToCell (event->pos ());
1498  }
1499 }
1500 
1501 void QWinTerminalImpl::mouseReleaseEvent (QMouseEvent *event)
1502 {
1503  if (event->button () == Qt::LeftButton && d->m_settingSelection)
1504  {
1505  d->m_endSelection = d->posToCell (event->pos ());
1506 
1507  updateSelection ();
1508 
1509  d->m_settingSelection = false;
1510  }
1511 }
1512 
1514 {
1515  if (event->button () == Qt::LeftButton)
1516  {
1517  // doubleclick - select word
1518  d->m_settingSelection = false;
1519 
1520  d->selectWord (d->posToCell (event->pos ()));
1521 
1522  allowTripleClick = true;
1523 
1524  QTimer::singleShot (QApplication::doubleClickInterval (),this,
1525  SLOT (tripleClickTimeout ()));
1526 
1527  }
1528 }
1529 
1531 {
1532  if (event->button () == Qt::LeftButton)
1533  {
1534  d->selectLine (d->posToCell (event->pos ()));
1535  }
1536 }
1537 
1539 {
1540  allowTripleClick = false;
1541 }
1542 
1543 //////////////////////////////////////////////////////////////////////////////
1544 
1546 {
1547  d->updateConsoleSize (true);
1548  d->grabConsoleBuffer ();
1549 }
1550 
1551 //////////////////////////////////////////////////////////////////////////////
1552 
1554 {
1555  QPainter p (w);
1556 
1557  int cw = d->m_charSize.width ();
1558  int ch = d->m_charSize.height ();
1559 
1560  QRect updateRect = event->rect ();
1561 
1562  int cx1 = updateRect.left () / cw;
1563  int cy1 = updateRect.top () / ch;
1564  int cx2 = qMin (d->m_consoleRect.width () - 1, updateRect.right () / cw);
1565  int cy2 = qMin (d->m_consoleRect.height () - 1, updateRect.bottom () / ch);
1566 
1567  if (cx1 > d->m_consoleRect.width () - 1
1568  || cy1 > d->m_consoleRect.height () - 1)
1569  return;
1570 
1571  d->drawTextBackground (p, cx1, cy1, cx2, cy2, cw, ch);
1572  d->drawSelection (p, cx1, cy1, cx2, cy2, cw, ch);
1573  d->drawCursor (p);
1574  d->drawText (p, cx1, cy1, cx2, cy2, cw, ch);
1575 }
1576 
1578 {
1579  if (d->m_hasBlinkingCursor)
1581  else
1582  d->m_cursorBlinking = false;
1583 
1584  d->m_consoleView->update (d->cursorRect ());
1585 }
1586 
1588 {
1589  d->m_hasBlinkingCursor = blink;
1590 
1591  setBlinkingCursorState (blink);
1592 }
1593 
1595 {
1596  if (blink && ! d->m_blinkCursorTimer->isActive ())
1597  d->m_blinkCursorTimer->start (d->BLINK_DELAY);
1598 
1599  if (! blink && d->m_blinkCursorTimer->isActive ())
1600  {
1601  d->m_blinkCursorTimer->stop ();
1602 
1603  if (d->m_cursorBlinking)
1604  blinkCursorEvent ();
1605  }
1606 }
1607 
1608 // Reset width of console buffer and terminal window to be the same.
1609 
1611 {
1612  d->updateConsoleSize (true, true);
1613 }
1614 
1615 //////////////////////////////////////////////////////////////////////////////
1616 
1617 void QWinTerminalImpl::wheelEvent (QWheelEvent* event)
1618 {
1619  if (! d->m_inWheelEvent)
1620  {
1621  // Forward to the scrollbar (avoid recursion)
1622  d->m_inWheelEvent = true;
1623  QApplication::sendEvent (d->m_verticalScrollBar, event);
1624  d->m_inWheelEvent = false;
1625  }
1626 }
1627 
1628 //////////////////////////////////////////////////////////////////////////////
1629 
1631 {
1633 }
1634 
1636 {
1638 }
1639 
1640 //////////////////////////////////////////////////////////////////////////////
1641 
1643 {
1644  d->monitorConsole ();
1645 }
1646 
1648 {
1649  d->updateSelection ();
1650 }
1651 
1652 //////////////////////////////////////////////////////////////////////////////
1653 
1654 void QWinTerminalImpl::focusInEvent (QFocusEvent* event)
1655 {
1656  emit set_global_shortcuts_signal (false); // disable some shortcuts
1657 
1658  setBlinkingCursorState (true);
1659 
1660  QWidget::focusInEvent (event);
1661 }
1662 
1663 void QWinTerminalImpl::focusOutEvent (QFocusEvent* event)
1664 {
1665  emit set_global_shortcuts_signal (true); // re-enable shortcuts
1666 
1667  // Force the cursor to be redrawn.
1668  d->m_cursorBlinking = true;
1669 
1670  setBlinkingCursorState (false);
1671 
1672  QWidget::focusOutEvent (event);
1673 }
1674 
1675 bool QWinTerminalImpl::eventFilter (QObject *obj, QEvent * event)
1676 {
1677  // if a keypress, filter out tab keys so that the next/prev tabbing is
1678  // disabled - but we still need to pass along to the console .
1679  if (event->type () == QEvent::KeyPress)
1680  {
1681  QKeyEvent* k = static_cast<QKeyEvent*>(event);
1682  if (k->key () == Qt::Key_Tab)
1683  {
1684  sendText ("\t");
1685  return true;
1686  }
1687  }
1688  return false;
1689 }
1690 
1691 void QWinTerminalImpl::keyPressEvent (QKeyEvent* event)
1692 {
1693  QString s = translateKey (event);
1694  if (!s.isEmpty ())
1695  sendText (s);
1696 
1697  if (d->m_hasBlinkingCursor)
1698  {
1699  d->m_blinkCursorTimer->start (d->BLINK_DELAY);
1700 
1701  if (d->m_cursorBlinking)
1702  blinkCursorEvent ();
1703  }
1704 
1705  QWidget::keyPressEvent (event);
1706 }
1707 
1708 //////////////////////////////////////////////////////////////////////////////
1709 
1711 {
1712  d->startCommand ();
1713 }
1714 
1715 //////////////////////////////////////////////////////////////////////////////
1716 
1717 void QWinTerminalImpl::sendText (const QString& s)
1718 {
1719  d->sendConsoleText (s);
1720 }
1721 
1723 {
1724  switch (type)
1725  {
1726  case UnderlineCursor:
1728  break;
1729 
1730  case BlockCursor:
1732  break;
1733 
1734  case IBeamCursor:
1736  break;
1737  }
1738 
1739  setBlinkingCursor (blinking);
1740 }
1741 
1742 void QWinTerminalImpl::setBackgroundColor (const QColor& color)
1743 {
1744  d->setBackgroundColor (color);
1745 }
1746 
1747 void QWinTerminalImpl::setForegroundColor (const QColor& color)
1748 {
1749  d->setForegroundColor (color);
1750 }
1751 
1752 void QWinTerminalImpl::setSelectionColor (const QColor& color)
1753 {
1754  d->setSelectionColor (color);
1755 }
1756 
1757 void QWinTerminalImpl::setCursorColor (bool useForegroundColor,
1758  const QColor& color)
1759 {
1760  d->setCursorColor (useForegroundColor, color);
1761 }
1762 
1764 {
1766 }
1767 
1768 
1769 //////////////////////////////////////////////////////////////////////////////
1770 
1772 {
1773  d->m_font = f;
1774  d->m_consoleView->setFont (f);
1775  d->updateConsoleSize (true);
1776 }
1777 
1778 //////////////////////////////////////////////////////////////////////////////
1779 
1780 void QWinTerminalImpl::setSize (int columns, int lines)
1781 {
1782  d->log ("emit set_screen_size_signal (%d, %d)\n", columns, lines);
1783 
1784  emit set_screen_size_signal (columns, lines);
1785 }
1786 
1787 //////////////////////////////////////////////////////////////////////////////
1788 
1790 {
1791  if(!hasFocus()) return;
1792 
1793  QClipboard *clipboard = QApplication::clipboard ();
1794 
1795  QString selection = d->getSelection ();
1796 
1797  if (selection.isEmpty ())
1798  {
1799  if (! _extra_interrupt)
1800  terminal_interrupt ();
1801  }
1802  else
1803  {
1804  clipboard->setText (selection);
1805  emit report_status_message (tr ("copied selection to clipboard"));
1806  }
1807 }
1808 
1809 //////////////////////////////////////////////////////////////////////////////
1810 
1812 {
1813  if(!hasFocus()) return;
1814 
1815  QString text = QApplication::clipboard()->text (QClipboard::Clipboard);
1816 
1817  if (! text.isEmpty ())
1818  sendText (text);
1819 }
1820 
1821 //////////////////////////////////////////////////////////////////////////////
1822 
1824 {
1825  if(!hasFocus()) return;
1826 
1827  d->selectAll();
1828 }
1829 
1830 
1831 
1832 //////////////////////////////////////////////////////////////////////////////
1833 
1835 {
1836  QString selection = d->getSelection ();
1837  return selection;
1838 }
1839 
1840 //////////////////////////////////////////////////////////////////////////////
1841 
1842 void QWinTerminalImpl::dragEnterEvent (QDragEnterEvent *event)
1843 {
1844  if (event->mimeData ()->hasUrls ())
1845  {
1846  event->acceptProposedAction();
1847  }
1848 }
1849 
1850 //////////////////////////////////////////////////////////////////////////////
1851 
1852 void QWinTerminalImpl::dropEvent (QDropEvent *event)
1853 {
1854  QString dropText;
1855 
1856  if (event->mimeData ()->hasUrls ())
1857  {
1858  foreach (QUrl url, event->mimeData ()->urls ())
1859  {
1860  if(dropText.length () > 0)
1861  dropText += '\n';
1862  dropText += url.toLocalFile ();
1863  }
1864  sendText (dropText);
1865  }
1866 }
1867 
1868 //////////////////////////////////////////////////////////////////////////////
1869 
1871 {
1872  _extra_interrupt = extra;
1873 }
QScrollBar * m_verticalScrollBar
#define TEXT_CHUNK_SIZE
int dup2(int old_fd, int new_fd)
Definition: oct-syscalls.cc:50
QConsoleView * m_consoleView
void horizontalScrollValueChanged(int value)
void resizeEvent(QResizeEvent *event)
void setScrollBufferSize(int value)
void setBlinkingCursor(bool blink)
QConsoleThread * m_consoleThread
void updateHorizontalScrollBar(void)
void log(const char *fmt,...)
void focusOutEvent(QFocusEvent *)
friend class QConsoleView
void drawSelection(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void setCursorType(CursorType type, bool blinking)
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE * f
for fields that display a single number
Definition: time.cc:441
for large enough k
Definition: lu.cc:617
void selectWord(const QPoint &cellPos)
CHAR_INFO * m_tmpBuffer
void tripleClickTimeout(void)
void terminal_interrupt(void)
Definition: QTerminal.h:116
void updateConsoleView(bool grab=true)
QConsolePrivate(QWinTerminalImpl *parent, const QString &cmd=QString())
void set_global_shortcuts_signal(bool)
void titleChanged(const QString &)
void syncConsoleParameters(void)
void updateSelection(void)
friend class QConsoleThread
void terminated(void)
QWinTerminalImpl * q
nd example oindent opens the file binary numeric values will be read assuming they are stored in IEEE format with the least significant bit and then converted to the native representation Opening a file that is already open simply opens it again and returns a separate file id It is not an error to open a file several though writing to the same file through several different file ids may produce unexpected results The possible values of text mode reading and writing automatically converts linefeeds to the appropriate line end character for the you may append a you must also open the file in binary mode The parameter conversions are currently only supported for and permissions will be set to and then everything is written in a single operation This is very efficient and improves performance c
Definition: file-io.cc:587
s
Definition: file-io.cc:2729
void closeStandardIO(int fd, DWORD stdHandleId, const char *name)
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE * d
QConsoleColors m_colors
void updateConsoleSize(bool sync=false, bool allow_smaller_width=false)
QColor foregroundColor(void) const
QWinTerminalImpl * q
void init_terminal_size(void)
nd deftypefn *std::string name
Definition: sysdep.cc:647
void sendConsoleText(const QString &s)
void grabConsoleBuffer(CHAR_INFO *buf=0)
void setCursorColor(bool useForegoundColor, const QColor &color)
void setSelectionColor(const QColor &color)
OCTAVE_EXPORT octave_value_list or both For fclose
Definition: file-io.cc:676
void report_status_message(const QString &)
void set_global_shortcuts(bool focus_out)
Definition: QTerminal.cc:76
static const int BLINK_DELAY
text(const graphics_handle &mh, const graphics_handle &p)
Definition: graphics.in.h:4541
void verticalScrollValueChanged(int value)
std::complex< double > w(std::complex< double > z, double relerr=0)
QString getSelection(void)
void clearSelection(void)
QConsoleView(QWinTerminalImpl *parent=0)
void setHorizontalScrollValue(int value)
void setForegroundColor(const QColor &color)
double timeout
Definition: graphics.cc:12198
is false
Definition: cellfun.cc:400
void mouseReleaseEvent(QMouseEvent *event)
void keyPressEvent(QKeyEvent *)
static QString translateKey(QKeyEvent *ev)
void setScrollBufferSize(int value)
void sendText(const QString &s)
returns the type of the matrix and caches it for future use Called with more than one the function will not attempt to guess the type if it is still unknown This is useful for debugging purposes The possible matrix types depend on whether the matrix is full or and can be one of the following able sis tem and mark type as unknown tem as the structure of the matrix explicitly gives this(Sparse matrices only) tem code
Definition: matrix_type.cc:120
virtual void start(void)
void paintEvent(QPaintEvent *event)
idx type
Definition: ov.cc:3114
void mouseTripleClickEvent(QMouseEvent *event)
void focusInEvent(QFocusEvent *)
void set_screen_size_signal(int, int)
QPoint posToCell(const QPoint &pt)
void setSize(int columns, int lines)
std::string url
Definition: urlwrite.cc:118
QConsolePrivate * d
void setBackgroundColor(const QColor &color)
void setBackgroundColor(const QColor &color)
void has_extra_interrupt(bool)
void setBlinkingCursorState(bool blink)
void viewPaintEvent(QConsoleView *, QPaintEvent *)
#define INFINITE
Definition: randgamma.cc:90
QColor backgroundColor(void) const
p
Definition: lu.cc:138
QWinTerminalImpl * q
void dropEvent(QDropEvent *event)
void drawTextBackground(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
void setTerminalFont(const QFont &font)
void viewResizeEvent(QConsoleView *, QResizeEvent *)
bool eventFilter(QObject *obj, QEvent *ev)
void setForegroundColor(const QColor &color)
the element is set to zero In other the statement xample y
Definition: data.cc:5264
QWinTerminalImpl(QWidget *parent=0)
void dragEnterEvent(QDragEnterEvent *event)
void mouseMoveEvent(QMouseEvent *event)
#define LOGFILENAME
for i
Definition: data.cc:5264
void drawText(QPainter &p, int cx1, int cy1, int cx2, int cy2, int cw, int ch)
QConsoleThread(QWinTerminalImpl *console)
QColor selectionColor(void) const
void setVerticalScrollValue(int value)
void setSelectionColor(const QColor &color)
void mouseDoubleClickEvent(QMouseEvent *event)
void mousePressEvent(QMouseEvent *event)
static void maybeSwapPoints(QPoint &begin, QPoint &end)
void wheelEvent(QWheelEvent *)
void updateVerticalScrollBar(void)
nd group nd example For each display the value
Definition: sysdep.cc:866
KeyboardCursorType m_cursorType
void drawCursor(QPainter &p)
void selectLine(const QPoint &cellPos)
void setCursorColor(bool useForegroundColor, const QColor &color)
void setupStandardIO(DWORD stdHandleId, int fd, const char *name, const char *devName)
F77_RET_T const F77_REAL const F77_REAL F77_REAL &F77_RET_T const F77_DBLE const F77_DBLE F77_DBLE &F77_RET_T const F77_DBLE F77_DBLE &F77_RET_T const F77_REAL F77_REAL &F77_RET_T const F77_DBLE * x
QScrollBar * m_horizontalScrollBar
static const double pi
Definition: lo-specfun.cc:1996
QColor cursorColor(void) const