~larryprice/acolyterm/release-0.1

« back to all changes in this revision

Viewing changes to src/plugin/konsole/TerminalDisplay.cpp

  • Committer: Larry Price
  • Date: 2016-06-15 14:47:59 UTC
  • Revision ID: larry.price@canonical.com-20160615144759-6wopn0gxwgta3x1n
Updating QMLTermWidget and removing unnecessary konsole codebase

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
    This file is part of KTerminal, QML plugin of the Konsole,
3
 
    which is a terminal emulator from KDE.
4
 
 
5
 
    Copyright 2006-2008 by Robert Knight   <robertknight@gmail.com>
6
 
    Copyright 1997,1998 by Lars Doelle     <lars.doelle@on-line.de>
7
 
 
8
 
    Rewritten for QT5/QML by Dmitry Zagnoyko   <hiroshidi@gmail.com>, Copyright (C) 2013
9
 
 
10
 
    This program is free software; you can redistribute it and/or modify
11
 
    it under the terms of the GNU General Public License as published by
12
 
    the Free Software Foundation; either version 2 of the License, or
13
 
    (at your option) any later version.
14
 
 
15
 
    This program is distributed in the hope that it will be useful,
16
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 
    GNU General Public License for more details.
19
 
 
20
 
    You should have received a copy of the GNU General Public License
21
 
    along with this program; if not, write to the Free Software
22
 
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23
 
    02110-1301  USA.
24
 
*/
25
 
 
26
 
// Own
27
 
#include "TerminalDisplay.h"
28
 
 
29
 
// Qt
30
 
#include <QtQuick/QtQuick>
31
 
 
32
 
#include <QGuiApplication>
33
 
#include <QStyleHints>
34
 
#include <QInputMethod>
35
 
 
36
 
#include <QtGui/QPainter>
37
 
#include <QtGui/QPixmap>
38
 
#include <QtGui/QClipboard>
39
 
#include <QtGui/QKeyEvent>
40
 
 
41
 
#include <QtCore/QEvent>
42
 
#include <QtCore/QTime>
43
 
#include <QtCore/QFile>
44
 
#include <QtCore/QTimer>
45
 
 
46
 
#include <QtDebug>
47
 
#include <QUrl>
48
 
 
49
 
// Konsole
50
 
//#include <config-apps.h>
51
 
#include "Filter.h"
52
 
#include "konsole_wcwidth.h"
53
 
#include "ScreenWindow.h"
54
 
#include "ColorScheme.h"
55
 
#include "ColorTables.h"
56
 
#include "TerminalCharacterDecoder.h"
57
 
 
58
 
 
59
 
#ifndef loc
60
 
#define loc(X,Y) ((Y)*_columns+(X))
61
 
#endif
62
 
 
63
 
#define REPCHAR   "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
64
 
                  "abcdefgjijklmnopqrstuvwxyz" \
65
 
                  "0123456789./+@"
66
 
 
67
 
const ColorEntry base_color_table[TABLE_COLORS] =
68
 
// The following are almost IBM standard color codes, with some slight
69
 
// gamma correction for the dim colors to compensate for bright X screens.
70
 
// It contains the 8 ansiterm/xterm colors in 2 intensities.
71
 
{
72
 
  // Fixme: could add faint colors here, also.
73
 
  // normal
74
 
  ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 1), // Dfore, Dback
75
 
  ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xB2,0x18,0x18), 0), // Black, Red
76
 
  ColorEntry(QColor(0x18,0xB2,0x18), 0), ColorEntry( QColor(0xB2,0x68,0x18), 0), // Green, Yellow
77
 
  ColorEntry(QColor(0x18,0x18,0xB2), 0), ColorEntry( QColor(0xB2,0x18,0xB2), 0), // Blue, Magenta
78
 
  ColorEntry(QColor(0x18,0xB2,0xB2), 0), ColorEntry( QColor(0xB2,0xB2,0xB2), 0), // Cyan, White
79
 
  // intensiv
80
 
  ColorEntry(QColor(0x00,0x00,0x00), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 1),
81
 
  ColorEntry(QColor(0x68,0x68,0x68), 0), ColorEntry( QColor(0xFF,0x54,0x54), 0),
82
 
  ColorEntry(QColor(0x54,0xFF,0x54), 0), ColorEntry( QColor(0xFF,0xFF,0x54), 0),
83
 
  ColorEntry(QColor(0x54,0x54,0xFF), 0), ColorEntry( QColor(0xFF,0x54,0xFF), 0),
84
 
  ColorEntry(QColor(0x54,0xFF,0xFF), 0), ColorEntry( QColor(0xFF,0xFF,0xFF), 0)
85
 
};
86
 
 
87
 
// scroll increment used when dragging selection at top/bottom of window.
88
 
 
89
 
// static
90
 
bool KTerminalDisplay::_antialiasText = true;
91
 
bool KTerminalDisplay::HAVE_TRANSPARENCY = true;
92
 
 
93
 
// we use this to force QPainter to display text in LTR mode
94
 
// more information can be found in: http://unicode.org/reports/tr9/
95
 
const QChar LTR_OVERRIDE_CHAR( 0x202D );
96
 
 
97
 
/* ------------------------------------------------------------------------- */
98
 
/*                                                                           */
99
 
/*                                Colors                                     */
100
 
/*                                                                           */
101
 
/* ------------------------------------------------------------------------- */
102
 
 
103
 
/* Note that we use ANSI color order (bgr), while IBMPC color order is (rgb)
104
 
 
105
 
   Code        0       1       2       3       4       5       6       7
106
 
   ----------- ------- ------- ------- ------- ------- ------- ------- -------
107
 
   ANSI  (bgr) Black   Red     Green   Yellow  Blue    Magenta Cyan    White
108
 
   IBMPC (rgb) Black   Blue    Green   Cyan    Red     Magenta Yellow  White
109
 
*/
110
 
 
111
 
 
112
 
 
113
 
/////////////////////////////////////////////////////////////////////////////////////
114
 
/////////////////////////////////////////////////////////////////////////////////////
115
 
///                                   BEGIN
116
 
/////////////////////////////////////////////////////////////////////////////////////
117
 
/////////////////////////////////////////////////////////////////////////////////////
118
 
 
119
 
 
120
 
/* ------------------------------------------------------------------------- */
121
 
/*                                                                           */
122
 
/*                         Constructor / Destructor                          */
123
 
/*                                                                           */
124
 
/* ------------------------------------------------------------------------- */
125
 
KTerminalDisplay::KTerminalDisplay(QQuickItem *parent) :
126
 
    QQuickPaintedItem(parent)
127
 
  ,_screenWindow(0)
128
 
  ,_allowBell(true)
129
 
  ,_fontHeight(1)
130
 
  ,_fontWidth(1)
131
 
  ,_fontAscent(1)
132
 
  ,_boldIntense(true)
133
 
  ,_lines(1)
134
 
  ,_columns(1)
135
 
  ,_usedLines(1)
136
 
  ,_usedColumns(1)
137
 
  ,_contentHeight(1)
138
 
  ,_contentWidth(1)
139
 
  ,_image(0)
140
 
  ,_randomSeed(0)
141
 
  ,_resizing(false)
142
 
  ,_bidiEnabled(false)
143
 
  ,_actSel(0)
144
 
  ,_wordSelectionMode(false)
145
 
  ,_lineSelectionMode(false)
146
 
  ,_preserveLineBreaks(false)
147
 
  ,_columnSelectionMode(false)
148
 
  ,_wordCharacters(":@-./_~")
149
 
  ,_bellMode(NotifyBell) //def: SystemBeepBell)
150
 
  ,_blinking(false)
151
 
  ,_hasBlinker(false)
152
 
  ,_cursorBlinking(false)
153
 
  ,_hasBlinkingCursor(false)
154
 
  ,_allowBlinkingText(true)
155
 
  ,_isFixedSize(false)
156
 
  ,_resizeTimer(0)
157
 
  ,_flowControlWarningEnabled(false)
158
 
  ,_lineSpacing(0)
159
 
  ,_colorsInverted(false)
160
 
  ,_cursorShape(BlockCursor)
161
 
  ,m_session(0)
162
 
  ,m_focusOnClick(true)
163
 
  ,m_showVKBonClick(true)
164
 
  ,m_parent(parent)
165
 
{
166
 
    _blendColor  = qRgba(0,0,0,0xff);
167
 
    m_widgetRect = QRectF(0,0,1,1);
168
 
 
169
 
    m_palette = qApp->palette();
170
 
 
171
 
    m_font = QFont("Monospace",16);
172
 
 
173
 
#ifdef Q_WS_UBUNTU
174
 
#if QT_VERSION >= 0x040700
175
 
    m_font.setStyleStrategy(QFont::ForceIntegerMetrics);
176
 
#else
177
 
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
178
 
#endif
179
 
#endif
180
 
 
181
 
    // The offsets are not yet calculated.
182
 
    // Do not calculate these too often to be more smoothly when resizing
183
 
    // konsole in opaque mode.
184
 
    _topMargin = DEFAULT_TOP_MARGIN;
185
 
    _leftMargin = DEFAULT_LEFT_MARGIN;
186
 
 
187
 
    // setup timers for blinking cursor and text
188
 
    _blinkTimer   = new QTimer(this);
189
 
    connect(_blinkTimer, SIGNAL(timeout()), this, SLOT(blinkEvent()));
190
 
    _blinkCursorTimer   = new QTimer(this);
191
 
    connect(_blinkCursorTimer, SIGNAL(timeout()), this, SLOT(blinkCursorEvent()));
192
 
 
193
 
    //KCursor::setAutoHideCursor( this, true );
194
 
 
195
 
    setColorTable(base_color_table);
196
 
 
197
 
    new AutoScrollHandler(this);
198
 
 
199
 
    setAcceptedMouseButtons(Qt::LeftButton);
200
 
    setFlags(ItemHasContents | ItemAcceptsInputMethod);
201
 
    //installEventFilter(this);
202
 
 
203
 
    m_font.setStyleHint(QFont::TypeWriter);
204
 
 
205
 
    setVTFont(m_font);
206
 
}
207
 
 
208
 
KTerminalDisplay::~KTerminalDisplay()
209
 
{
210
 
    disconnect(_blinkTimer);
211
 
    disconnect(_blinkCursorTimer);
212
 
 
213
 
    delete[] _image;
214
 
}
215
 
 
216
 
void KTerminalDisplay::setSession(KSession * session)
217
 
{
218
 
    if (m_session != session) {
219
 
        m_session = session;
220
 
 
221
 
        connect(this, SIGNAL(copyAvailable(bool)),
222
 
                m_session, SLOT(selectionChanged(bool)));
223
 
        connect(this, SIGNAL(termGetFocus()),
224
 
                m_session, SIGNAL(termGetFocus()));
225
 
        connect(this, SIGNAL(termLostFocus()),
226
 
                m_session, SIGNAL(termLostFocus()));
227
 
        connect(this, SIGNAL(keyPressedSignal(QKeyEvent *)),
228
 
                m_session, SIGNAL(termKeyPressed(QKeyEvent *)));
229
 
 
230
 
        m_session->addView(this);
231
 
 
232
 
        //m_session->changeDir(QDir::currentPath());
233
 
 
234
 
        setRandomSeed(m_session->getRandomSeed());
235
 
 
236
 
        emit changedSession(session);
237
 
    }
238
 
}
239
 
 
240
 
 
241
 
ScreenWindow* KTerminalDisplay::screenWindow() const
242
 
{
243
 
    return _screenWindow;
244
 
}
245
 
 
246
 
void KTerminalDisplay::forcedFocus()
247
 
{
248
 
 
249
 
    bool focused = hasActiveFocus();
250
 
 
251
 
    if (!focused) {
252
 
        forceActiveFocus();
253
 
        focused = hasActiveFocus();
254
 
    }
255
 
 
256
 
}
257
 
 
258
 
void KTerminalDisplay::setScreenWindow(ScreenWindow* window)
259
 
{
260
 
    // disconnect existing screen window if any
261
 
    if ( _screenWindow )
262
 
    {
263
 
        disconnect( _screenWindow , 0 , this , 0 );
264
 
    }
265
 
 
266
 
    _screenWindow = window;
267
 
 
268
 
    if ( window )
269
 
    {
270
 
 
271
 
// TODO: Determine if this is an issue.
272
 
//#warning "The order here is not specified - does it matter whether updateImage or updateLineProperties comes first?"
273
 
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateLineProperties()) );
274
 
        connect( _screenWindow , SIGNAL(outputChanged()) , this , SLOT(updateImage()) );
275
 
        window->setWindowLines(_lines);
276
 
    }
277
 
}
278
 
 
279
 
const ColorEntry* KTerminalDisplay::colorTable() const
280
 
{
281
 
  return _colorTable;
282
 
}
283
 
void KTerminalDisplay::setBackgroundColor(const QColor& color)
284
 
{
285
 
    _colorTable[DEFAULT_BACK_COLOR].color = color;
286
 
//    QPalette p = m_palette;
287
 
//    p.setColor( backgroundRole(), color );
288
 
//    setPalette( p );
289
 
 
290
 
    update();
291
 
}
292
 
void KTerminalDisplay::setForegroundColor(const QColor& color)
293
 
{
294
 
    _colorTable[DEFAULT_FORE_COLOR].color = color;
295
 
 
296
 
    update();
297
 
}
298
 
void KTerminalDisplay::setColorTable(const ColorEntry table[])
299
 
{
300
 
  for (int i = 0; i < TABLE_COLORS; i++)
301
 
      _colorTable[i] = table[i];
302
 
 
303
 
  setBackgroundColor(_colorTable[DEFAULT_BACK_COLOR].color);
304
 
}
305
 
 
306
 
 
307
 
/* ------------------------------------------------------------------------- */
308
 
/*                                                                           */
309
 
/*                                   Font                                    */
310
 
/*                                                                           */
311
 
/* ------------------------------------------------------------------------- */
312
 
 
313
 
/*
314
 
   The VT100 has 32 special graphical characters. The usual vt100 extended
315
 
   xterm fonts have these at 0x00..0x1f.
316
 
 
317
 
   QT's iso mapping leaves 0x00..0x7f without any changes. But the graphicals
318
 
   come in here as proper unicode characters.
319
 
 
320
 
   We treat non-iso10646 fonts as VT100 extended and do the requiered mapping
321
 
   from unicode to 0x00..0x1f. The remaining translation is then left to the
322
 
   QCodec.
323
 
*/
324
 
 
325
 
static inline bool isLineChar(quint16 c) { return ((c & 0xFF80) == 0x2500);}
326
 
static inline bool isLineCharString(const QString& string)
327
 
{
328
 
        return (string.length() > 0) && (isLineChar(string.at(0).unicode()));
329
 
}
330
 
 
331
 
 
332
 
// assert for i in [0..31] : vt100extended(vt100_graphics[i]) == i.
333
 
 
334
 
unsigned short vt100_graphics[32] =
335
 
{ // 0/8     1/9    2/10    3/11    4/12    5/13    6/14    7/15
336
 
  0x0020, 0x25C6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0,
337
 
  0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c,
338
 
  0xF800, 0xF801, 0x2500, 0xF803, 0xF804, 0x251c, 0x2524, 0x2534,
339
 
  0x252c, 0x2502, 0x2264, 0x2265, 0x03C0, 0x2260, 0x00A3, 0x00b7
340
 
};
341
 
 
342
 
void KTerminalDisplay::fontChange(const QFont&)
343
 
{
344
 
  QFontMetrics fm(m_font);
345
 
  _fontHeight = fm.height() + _lineSpacing;
346
 
 
347
 
  // waba TerminalDisplay 1.123:
348
 
  // "Base character width on widest ASCII character. This prevents too wide
349
 
  //  characters in the presence of double wide (e.g. Japanese) characters."
350
 
  // Get the width from representative normal width characters
351
 
  _fontWidth = (double)fm.width(REPCHAR)/(double)strlen(REPCHAR);
352
 
 
353
 
  _fixedFont = true;
354
 
 
355
 
  int fw = fm.width(REPCHAR[0]);
356
 
  for(unsigned int i=1; i< strlen(REPCHAR); i++)
357
 
  {
358
 
    if (fw != fm.width(REPCHAR[i]))
359
 
    {
360
 
      _fixedFont = false;
361
 
      break;
362
 
    }
363
 
  }
364
 
 
365
 
  if (_fontWidth < 1)
366
 
    _fontWidth=1;
367
 
 
368
 
  _fontAscent = fm.ascent();
369
 
 
370
 
  emit changedFontMetricSignal( _fontHeight, _fontWidth );
371
 
  propagateSize();
372
 
  update();
373
 
}
374
 
 
375
 
void KTerminalDisplay::setVTFont(const QFont& f)
376
 
{
377
 
  QFont font = f;
378
 
 
379
 
#if defined(Q_WS_MAC) || defined(Q_WS_UBUNTU)
380
 
#if QT_VERSION >= 0x040700
381
 
    font.setStyleStrategy(QFont::ForceIntegerMetrics);
382
 
#else
383
 
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
384
 
#endif
385
 
#endif
386
 
 
387
 
  QFontMetrics metrics(font);
388
 
 
389
 
  if ( !QFontInfo(font).fixedPitch() )
390
 
  {
391
 
      qDebug() << "Using an unsupported variable-width font in the terminal.  This may produce display errors.";
392
 
  }
393
 
 
394
 
  if ( metrics.height() < height() && metrics.maxWidth() < width() )
395
 
  {
396
 
    // hint that text should be drawn without anti-aliasing.
397
 
    // depending on the user's font configuration, this may not be respected
398
 
    if (!_antialiasText)
399
 
        font.setStyleStrategy( QFont::NoAntialias );
400
 
 
401
 
    // experimental optimization.  Konsole assumes that the terminal is using a
402
 
    // mono-spaced font, in which case kerning information should have an effect.
403
 
    // Disabling kerning saves some computation when rendering text.
404
 
    font.setKerning(false);
405
 
 
406
 
    //QWidget::setFont(font);
407
 
    m_font = font;
408
 
    fontChange(font);
409
 
  }
410
 
}
411
 
 
412
 
void KTerminalDisplay::setColorScheme(const QString &name)
413
 
{
414
 
    const ColorScheme *cs;
415
 
    // avoid legacy (int) solution
416
 
    if (!availableColorSchemes().contains(name))
417
 
        cs = ColorSchemeManager::instance()->defaultColorScheme();
418
 
    else
419
 
        cs = ColorSchemeManager::instance()->findColorScheme(name);
420
 
 
421
 
    if (! cs)
422
 
    {
423
 
#ifndef QT_NO_DEBUG
424
 
        qDebug() << "Cannot load color scheme: " << name;
425
 
#endif
426
 
        return;
427
 
    }
428
 
 
429
 
    ColorEntry table[TABLE_COLORS];
430
 
    cs->getColorTable(table);
431
 
    setColorTable(table);
432
 
    m_scheme = name;
433
 
}
434
 
 
435
 
QStringList KTerminalDisplay::availableColorSchemes()
436
 
{
437
 
    QStringList ret;
438
 
    foreach (const ColorScheme* cs, ColorSchemeManager::instance()->allColorSchemes())
439
 
        ret.append(cs->name());
440
 
    return ret;
441
 
}
442
 
 
443
 
void KTerminalDisplay::click(qreal x, qreal y)
444
 
{
445
 
    QMouseEvent me(QEvent::MouseButtonPress, QPointF(x,y), Qt::RightButton, Qt::RightButton, Qt::NoModifier);
446
 
    mousePressEvent(&me);
447
 
}
448
 
 
449
 
void KTerminalDisplay::setAutoFocus(bool au)
450
 
{
451
 
    m_focusOnClick = au;
452
 
    emit changedAutoFocus(au);
453
 
}
454
 
 
455
 
void KTerminalDisplay::setAutoVKB(bool au)
456
 
{
457
 
    m_showVKBonClick = au;
458
 
    emit changedAutoVKB(au);
459
 
}
460
 
 
461
 
 
462
 
/* ------------------------------------------------------------------------- */
463
 
/*                                                                           */
464
 
/*                             Display Operations                            */
465
 
/*                                                                           */
466
 
/* ------------------------------------------------------------------------- */
467
 
 
468
 
/**
469
 
 A table for emulating the simple (single width) unicode drawing chars.
470
 
 It represents the 250x - 257x glyphs. If it's zero, we can't use it.
471
 
 if it's not, it's encoded as follows: imagine a 5x5 grid where the points are numbered
472
 
 0 to 24 left to top, top to bottom. Each point is represented by the corresponding bit.
473
 
 
474
 
 Then, the pixels basically have the following interpretation:
475
 
 _|||_
476
 
 -...-
477
 
 -...-
478
 
 -...-
479
 
 _|||_
480
 
 
481
 
where _ = none
482
 
      | = vertical line.
483
 
      - = horizontal line.
484
 
 */
485
 
 
486
 
 
487
 
enum LineEncode
488
 
{
489
 
    TopL  = (1<<1),
490
 
    TopC  = (1<<2),
491
 
    TopR  = (1<<3),
492
 
 
493
 
    LeftT = (1<<5),
494
 
    Int11 = (1<<6),
495
 
    Int12 = (1<<7),
496
 
    Int13 = (1<<8),
497
 
    RightT = (1<<9),
498
 
 
499
 
    LeftC = (1<<10),
500
 
    Int21 = (1<<11),
501
 
    Int22 = (1<<12),
502
 
    Int23 = (1<<13),
503
 
    RightC = (1<<14),
504
 
 
505
 
    LeftB = (1<<15),
506
 
    Int31 = (1<<16),
507
 
    Int32 = (1<<17),
508
 
    Int33 = (1<<18),
509
 
    RightB = (1<<19),
510
 
 
511
 
    BotL  = (1<<21),
512
 
    BotC  = (1<<22),
513
 
    BotR  = (1<<23)
514
 
};
515
 
 
516
 
#include "LineFont.h"
517
 
 
518
 
static void drawLineChar(QPainter* paint, qreal x, qreal y, qreal w, qreal h, uchar code)
519
 
{
520
 
    //Calculate cell midpoints, end points.
521
 
    qreal cx = x + w/2;
522
 
    qreal cy = y + h/2;
523
 
    qreal ex = x + w - 1;
524
 
    qreal ey = y + h - 1;
525
 
 
526
 
    quint32 toDraw = LineChars[code];
527
 
 
528
 
    //Top _lines:
529
 
    if (toDraw & TopL)
530
 
        paint->drawLine(cx-1, y, cx-1, cy-2);
531
 
    if (toDraw & TopC)
532
 
        paint->drawLine(cx, y, cx, cy-2);
533
 
    if (toDraw & TopR)
534
 
        paint->drawLine(cx+1, y, cx+1, cy-2);
535
 
 
536
 
    //Bot _lines:
537
 
    if (toDraw & BotL)
538
 
        paint->drawLine(cx-1, cy+2, cx-1, ey);
539
 
    if (toDraw & BotC)
540
 
        paint->drawLine(cx, cy+2, cx, ey);
541
 
    if (toDraw & BotR)
542
 
        paint->drawLine(cx+1, cy+2, cx+1, ey);
543
 
 
544
 
    //Left _lines:
545
 
    if (toDraw & LeftT)
546
 
        paint->drawLine(x, cy-1, cx-2, cy-1);
547
 
    if (toDraw & LeftC)
548
 
        paint->drawLine(x, cy, cx-2, cy);
549
 
    if (toDraw & LeftB)
550
 
        paint->drawLine(x, cy+1, cx-2, cy+1);
551
 
 
552
 
    //Right _lines:
553
 
    if (toDraw & RightT)
554
 
        paint->drawLine(cx+2, cy-1, ex, cy-1);
555
 
    if (toDraw & RightC)
556
 
        paint->drawLine(cx+2, cy, ex, cy);
557
 
    if (toDraw & RightB)
558
 
        paint->drawLine(cx+2, cy+1, ex, cy+1);
559
 
 
560
 
    //Intersection points.
561
 
    if (toDraw & Int11)
562
 
        paint->drawPoint(cx-1, cy-1);
563
 
    if (toDraw & Int12)
564
 
        paint->drawPoint(cx, cy-1);
565
 
    if (toDraw & Int13)
566
 
        paint->drawPoint(cx+1, cy-1);
567
 
 
568
 
    if (toDraw & Int21)
569
 
        paint->drawPoint(cx-1, cy);
570
 
    if (toDraw & Int22)
571
 
        paint->drawPoint(cx, cy);
572
 
    if (toDraw & Int23)
573
 
        paint->drawPoint(cx+1, cy);
574
 
 
575
 
    if (toDraw & Int31)
576
 
        paint->drawPoint(cx-1, cy+1);
577
 
    if (toDraw & Int32)
578
 
        paint->drawPoint(cx, cy+1);
579
 
    if (toDraw & Int33)
580
 
        paint->drawPoint(cx+1, cy+1);
581
 
 
582
 
}
583
 
 
584
 
void KTerminalDisplay::setKeyboardCursorShape(KeyboardCursorShape shape)
585
 
{
586
 
    _cursorShape = shape;
587
 
}
588
 
 
589
 
KTerminalDisplay::KeyboardCursorShape KTerminalDisplay::keyboardCursorShape() const
590
 
{
591
 
    return _cursorShape;
592
 
}
593
 
 
594
 
void KTerminalDisplay::setKeyboardCursorColor(bool useForegroundColor, const QColor& color)
595
 
{
596
 
    if (useForegroundColor)
597
 
        _cursorColor = QColor(); // an invalid color means that
598
 
                                 // the foreground color of the
599
 
                                 // current character should
600
 
                                 // be used
601
 
 
602
 
    else
603
 
        _cursorColor = color;
604
 
}
605
 
 
606
 
QColor KTerminalDisplay::keyboardCursorColor() const
607
 
{
608
 
    return _cursorColor;
609
 
}
610
 
 
611
 
void KTerminalDisplay::ShowVKB(bool show)
612
 
{
613
 
    bool focused = hasActiveFocus();
614
 
 
615
 
    if (focused && show && !qGuiApp->inputMethod()->isVisible()) {
616
 
        updateInputMethod(Qt::ImEnabled);
617
 
        qGuiApp->inputMethod()->show();
618
 
    }
619
 
 
620
 
    if (focused && !show && qGuiApp->inputMethod()->isVisible()) {
621
 
        updateInputMethod(Qt::ImEnabled);
622
 
        qGuiApp->inputMethod()->hide();
623
 
    }
624
 
}
625
 
 
626
 
void KTerminalDisplay::setRandomSeed(uint randomSeed) { _randomSeed = randomSeed; }
627
 
uint KTerminalDisplay::randomSeed() const { return _randomSeed; }
628
 
 
629
 
#if 0
630
 
/*!
631
 
    Set XIM Position
632
 
*/
633
 
void TerminalDisplay::setCursorPos(const int curx, const int cury)
634
 
{
635
 
    int xpos, ypos;
636
 
    ypos = _topMargin + _fontHeight*(cury-1) + _fontAscent;
637
 
    xpos = _leftMargin + _fontWidth*curx;
638
 
    //setMicroFocusHint(xpos, ypos, 0, _fontHeight); //### ???
639
 
    // fprintf(stderr, "x/y = %d/%d\txpos/ypos = %d/%d\n", curx, cury, xpos, ypos);
640
 
    _cursorLine = cury;
641
 
    _cursorCol = curx;
642
 
}
643
 
#endif
644
 
 
645
 
// scrolls the image by 'lines', down if lines > 0 or up otherwise.
646
 
//
647
 
// the terminal emulation keeps track of the scrolling of the character
648
 
// image as it receives input, and when the view is updated, it calls scrollImage()
649
 
// with the final scroll amount.  this improves performance because scrolling the
650
 
// display is much cheaper than re-rendering all the text for the
651
 
// part of the image which has moved up or down.
652
 
// Instead only new lines have to be drawn
653
 
void KTerminalDisplay::scrollImage(int lines , const QRect& screenWindowRegion)
654
 
{
655
 
    // constrain the region to the display
656
 
    // the bottom of the region is capped to the number of lines in the display's
657
 
    // internal image - 2, so that the height of 'region' is strictly less
658
 
    // than the height of the internal image.
659
 
    QRect region = screenWindowRegion;
660
 
    region.setBottom( qMin(region.bottom(),this->_lines-2) );
661
 
 
662
 
    // return if there is nothing to do
663
 
    if (    lines == 0
664
 
         || _image == 0
665
 
         || !region.isValid()
666
 
         || (region.top() + abs(lines)) >= region.bottom()
667
 
         || this->_lines <= region.height() ) return;
668
 
 
669
 
 
670
 
    void* firstCharPos = &_image[ region.top() * this->_columns ];
671
 
    void* lastCharPos = &_image[ (region.top() + abs(lines)) * this->_columns ];
672
 
 
673
 
    int top = _topMargin + (region.top() * _fontHeight);
674
 
    Q_UNUSED(top) //BAD IDEA!
675
 
    int linesToMove = region.height() - abs(lines);
676
 
    int bytesToMove = linesToMove *
677
 
                      this->_columns *
678
 
                      sizeof(Character);
679
 
 
680
 
    Q_ASSERT( linesToMove > 0 );
681
 
    Q_ASSERT( bytesToMove > 0 );
682
 
 
683
 
    //scroll internal image
684
 
    if ( lines > 0 )
685
 
    {
686
 
        // check that the memory areas that we are going to move are valid
687
 
        Q_ASSERT( (char*)lastCharPos + bytesToMove <
688
 
                  (char*)(_image + (this->_lines * this->_columns)) );
689
 
 
690
 
        Q_ASSERT( (lines*this->_columns) < _imageSize );
691
 
 
692
 
        //scroll internal image down
693
 
        memmove( firstCharPos , lastCharPos , bytesToMove );
694
 
    }
695
 
    else
696
 
    {
697
 
        // check that the memory areas that we are going to move are valid
698
 
        Q_ASSERT( (char*)firstCharPos + bytesToMove <
699
 
                  (char*)(_image + (this->_lines * this->_columns)) );
700
 
 
701
 
        //scroll internal image up
702
 
        memmove( lastCharPos , firstCharPos , bytesToMove );
703
 
    }
704
 
 
705
 
    // Q_ASSERT(scrollRect.isValid() && !scrollRect.isEmpty());
706
 
 
707
 
    //scroll the display vertically to match internal _image
708
 
    // scroll( 0 , _fontHeight * (-lines) , scrollRect );
709
 
}
710
 
 
711
 
void KTerminalDisplay::updateImage()
712
 
{
713
 
  if ( !_screenWindow )
714
 
      return;
715
 
 
716
 
  // optimization - scroll the existing image where possible and
717
 
  // avoid expensive text drawing for parts of the image that
718
 
  // can simply be moved up or down
719
 
  scrollImage( _screenWindow->scrollCount() ,
720
 
               _screenWindow->scrollRegion() );
721
 
  _screenWindow->resetScrollCount();
722
 
 
723
 
  if (!_image) {
724
 
     // Create _image.
725
 
     // The emitted changedContentSizeSignal also leads to getImage being recreated, so do this first.
726
 
     updateImageSize();
727
 
  }
728
 
 
729
 
  Character* const newimg = _screenWindow->getImage();
730
 
  int lines   = _screenWindow->windowLines();
731
 
  int columns = _screenWindow->windowColumns();
732
 
 
733
 
  Q_ASSERT( this->_usedLines <= this->_lines );
734
 
  Q_ASSERT( this->_usedColumns <= this->_columns );
735
 
 
736
 
  int y,x,len;
737
 
 
738
 
  _hasBlinker = false;
739
 
 
740
 
  CharacterColor cf;       // undefined
741
 
  CharacterColor _clipboard;       // undefined
742
 
  int cr  = -1;   // undefined
743
 
 
744
 
  const int linesToUpdate   = qMin(this->_lines,  qMax(0,lines  ));
745
 
  const int columnsToUpdate = qMin(this->_columns,qMax(0,columns));
746
 
 
747
 
  QChar *disstrU = new QChar[columnsToUpdate];
748
 
  char *dirtyMask = new char[columnsToUpdate+2];
749
 
  QRegion dirtyRegion;
750
 
 
751
 
  // debugging variable, this records the number of lines that are found to
752
 
  // be 'dirty' ( ie. have changed from the old _image to the new _image ) and
753
 
  // which therefore need to be repainted
754
 
  int dirtyLineCount = 0;
755
 
 
756
 
  for (y = 0; y < linesToUpdate; ++y)
757
 
  {
758
 
    const Character*       currentLine = &_image[y*this->_columns];
759
 
    const Character* const newLine = &newimg[y*columns];
760
 
 
761
 
    bool updateLine = false;
762
 
 
763
 
    // The dirty mask indicates which characters need repainting. We also
764
 
    // mark surrounding neighbours dirty, in case the character exceeds
765
 
    // its cell boundaries
766
 
    memset(dirtyMask, 0, columnsToUpdate+2);
767
 
 
768
 
    for( x = 0 ; x < columnsToUpdate ; ++x)
769
 
    {
770
 
        if ( newLine[x] != currentLine[x] )
771
 
        {
772
 
            dirtyMask[x] = true;
773
 
        }
774
 
    }
775
 
 
776
 
    if (!_resizing) // not while _resizing, we're expecting a paintEvent
777
 
    for (x = 0; x < columnsToUpdate; ++x)
778
 
    {
779
 
      _hasBlinker |= (newLine[x].rendition & RE_BLINK);
780
 
 
781
 
      // Start drawing if this character or the next one differs.
782
 
      // We also take the next one into account to handle the situation
783
 
      // where characters exceed their cell width.
784
 
      if (dirtyMask[x])
785
 
      {
786
 
        quint16 c = newLine[x+0].character;
787
 
        if ( !c )
788
 
            continue;
789
 
        int p = 0;
790
 
        disstrU[p++] = c; //fontMap(c);
791
 
        bool lineDraw = isLineChar(c);
792
 
        bool doubleWidth = (x+1 == columnsToUpdate) ? false : (newLine[x+1].character == 0);
793
 
        cr = newLine[x].rendition;
794
 
        _clipboard = newLine[x].backgroundColor;
795
 
        if (newLine[x].foregroundColor != cf) cf = newLine[x].foregroundColor;
796
 
        int lln = columnsToUpdate - x;
797
 
        for (len = 1; len < lln; ++len)
798
 
        {
799
 
            const Character& ch = newLine[x+len];
800
 
 
801
 
            if (!ch.character)
802
 
                continue; // Skip trailing part of multi-col chars.
803
 
 
804
 
            bool nextIsDoubleWidth = (x+len+1 == columnsToUpdate) ? false : (newLine[x+len+1].character == 0);
805
 
 
806
 
            if (  ch.foregroundColor != cf ||
807
 
                  ch.backgroundColor != _clipboard ||
808
 
                  ch.rendition != cr ||
809
 
                  !dirtyMask[x+len] ||
810
 
                  isLineChar(c) != lineDraw ||
811
 
                  nextIsDoubleWidth != doubleWidth )
812
 
            break;
813
 
 
814
 
          disstrU[p++] = c; //fontMap(c);
815
 
        }
816
 
 
817
 
        QString unistr(disstrU, p);
818
 
 
819
 
        bool saveFixedFont = _fixedFont;
820
 
        if (lineDraw)
821
 
           _fixedFont = false;
822
 
        if (doubleWidth)
823
 
           _fixedFont = false;
824
 
 
825
 
        updateLine = true;
826
 
 
827
 
        _fixedFont = saveFixedFont;
828
 
        x += len - 1;
829
 
      }
830
 
 
831
 
    }
832
 
 
833
 
    //both the top and bottom halves of double height _lines must always be redrawn
834
 
    //although both top and bottom halves contain the same characters, only
835
 
    //the top one is actually
836
 
    //drawn.
837
 
    if (_lineProperties.count() > y)
838
 
        updateLine |= (_lineProperties[y] & LINE_DOUBLEHEIGHT);
839
 
 
840
 
    // if the characters on the line are different in the old and the new _image
841
 
    // then this line must be repainted.
842
 
    if (updateLine)
843
 
    {
844
 
        dirtyLineCount++;
845
 
 
846
 
        // add the area occupied by this line to the region which needs to be
847
 
        // repainted
848
 
        QRect dirtyRect = QRect( qRound(_leftMargin),
849
 
                                 qRound(_topMargin + _fontHeight*y) ,
850
 
                                 qRound(_fontWidth * columnsToUpdate) ,
851
 
                                 _fontHeight );
852
 
 
853
 
        dirtyRegion |= dirtyRect;
854
 
    }
855
 
 
856
 
    // replace the line of characters in the old _image with the
857
 
    // current line of the new _image
858
 
    memcpy((void*)currentLine,(const void*)newLine,columnsToUpdate*sizeof(Character));
859
 
  }
860
 
 
861
 
  // if the new _image is smaller than the previous _image, then ensure that the area
862
 
  // outside the new _image is cleared
863
 
  if ( linesToUpdate < _usedLines )
864
 
  {
865
 
    dirtyRegion |= QRect( qRound(_leftMargin),
866
 
                          qRound(_topMargin + _fontHeight*linesToUpdate) ,
867
 
                          qRound(_fontWidth * this->_columns) ,
868
 
                          _fontHeight * (_usedLines-linesToUpdate) );
869
 
  }
870
 
  _usedLines = linesToUpdate;
871
 
 
872
 
  if ( columnsToUpdate < _usedColumns )
873
 
  {
874
 
    dirtyRegion |= QRect( qRound(_leftMargin + columnsToUpdate*_fontWidth) ,
875
 
                          qRound(_topMargin),
876
 
                          qRound(_fontWidth * (_usedColumns-columnsToUpdate)) ,
877
 
                          _fontHeight * this->_lines );
878
 
  }
879
 
  _usedColumns = columnsToUpdate;
880
 
 
881
 
  dirtyRegion |= geometryRound(_inputMethodData.previousPreeditRect);
882
 
 
883
 
  // update the parts of the display which have changed
884
 
  // update(dirtyRegion.boundingRect());
885
 
 
886
 
  // update whole widget
887
 
  update();
888
 
 
889
 
  if ( _hasBlinker && !_blinkTimer->isActive()) _blinkTimer->start( TEXT_BLINK_DELAY );
890
 
  if (!_hasBlinker && _blinkTimer->isActive()) { _blinkTimer->stop(); _blinking = false; }
891
 
  delete[] dirtyMask;
892
 
  delete[] disstrU;
893
 
 
894
 
}
895
 
 
896
 
void KTerminalDisplay::setBlinkingCursor(bool blink)
897
 
{
898
 
  _hasBlinkingCursor=blink;
899
 
 
900
 
  if (blink && !_blinkCursorTimer->isActive())
901
 
      _blinkCursorTimer->start(100); // WARN! HARDCODE
902
 
 
903
 
  if (!blink && _blinkCursorTimer->isActive())
904
 
  {
905
 
    _blinkCursorTimer->stop();
906
 
    if (_cursorBlinking)
907
 
      blinkCursorEvent();
908
 
    else
909
 
      _cursorBlinking = false;
910
 
  }
911
 
}
912
 
 
913
 
void KTerminalDisplay::setBlinkingTextEnabled(bool blink)
914
 
{
915
 
    _allowBlinkingText = blink;
916
 
 
917
 
    if (blink && !_blinkTimer->isActive())
918
 
        _blinkTimer->start(qApp->styleHints()->cursorFlashTime() / 2);
919
 
 
920
 
    if (!blink && _blinkTimer->isActive())
921
 
    {
922
 
        _blinkTimer->stop();
923
 
        _blinking = false;
924
 
    }
925
 
}
926
 
 
927
 
void KTerminalDisplay::focusOutEvent(QFocusEvent*)
928
 
{
929
 
    emit termLostFocus();
930
 
    // trigger a repaint of the cursor so that it is both visible (in case
931
 
    // it was hidden during blinking)
932
 
    // and drawn in a focused out state
933
 
    _cursorBlinking = false;
934
 
    updateCursor();
935
 
 
936
 
    _blinkCursorTimer->stop();
937
 
    if (_blinking)
938
 
        blinkEvent();
939
 
 
940
 
    _blinkTimer->stop();
941
 
 
942
 
    //parent->activeFocus = false;
943
 
    emit activeFocusChanged(true);
944
 
}
945
 
 
946
 
void KTerminalDisplay::focusInEvent(QFocusEvent*)
947
 
{
948
 
    emit termGetFocus();
949
 
    if (_hasBlinkingCursor)
950
 
    {
951
 
        _blinkCursorTimer->start();
952
 
    }
953
 
    updateCursor();
954
 
 
955
 
    if (_hasBlinker)
956
 
        _blinkTimer->start();
957
 
}
958
 
 
959
 
QPoint KTerminalDisplay::cursorPosition() const
960
 
{
961
 
    if (_screenWindow)
962
 
        return _screenWindow->cursorPosition();
963
 
    else
964
 
        return QPoint(0,0);
965
 
}
966
 
 
967
 
void KTerminalDisplay::blinkEvent()
968
 
{
969
 
  if (!_allowBlinkingText) return;
970
 
 
971
 
  _blinking = !_blinking;
972
 
 
973
 
  //TODO:  Optimize to only repaint the areas of the widget
974
 
  // where there is blinking text
975
 
  // rather than repainting the whole widget.
976
 
  update();
977
 
}
978
 
 
979
 
void KTerminalDisplay::updateCursor()
980
 
{
981
 
  QRect cursorRect = imageToWidget( QRect(cursorPosition(),QSize(1,1)) );
982
 
  update(cursorRect);
983
 
}
984
 
 
985
 
void KTerminalDisplay::blinkCursorEvent()
986
 
{
987
 
  _cursorBlinking = !_cursorBlinking;
988
 
  updateCursor();
989
 
}
990
 
 
991
 
/* ------------------------------------------------------------------------- */
992
 
/*                                                                           */
993
 
/*                                  Resizing                                 */
994
 
/*                                                                           */
995
 
/* ------------------------------------------------------------------------- */
996
 
 
997
 
void KTerminalDisplay::propagateSize()
998
 
{
999
 
  if (_image) {
1000
 
     updateImageSize();
1001
 
  }
1002
 
}
1003
 
 
1004
 
void KTerminalDisplay::updateImageSize()
1005
 
{
1006
 
  Character* oldimg = _image;
1007
 
  int oldlin = _lines;
1008
 
  int oldcol = _columns;
1009
 
 
1010
 
  makeImage();
1011
 
 
1012
 
  // copy the old image to reduce flicker
1013
 
  int lines =   qMin(oldlin,_lines);
1014
 
  int columns = qMin(oldcol,_columns);
1015
 
 
1016
 
  if (oldimg)
1017
 
  {
1018
 
    for (int line = 0; line < lines; line++)
1019
 
    {
1020
 
      memcpy((void*)&_image[_columns*line],
1021
 
             (void*)&oldimg[oldcol*line],columns*sizeof(Character));
1022
 
    }
1023
 
    delete[] oldimg;
1024
 
  }
1025
 
 
1026
 
  if (_screenWindow)
1027
 
      _screenWindow->setWindowLines(_lines);
1028
 
 
1029
 
  _resizing = (oldlin!=_lines) || (oldcol!=_columns);
1030
 
 
1031
 
  if ( _resizing )
1032
 
  {
1033
 
    emit changedContentSizeSignal(_contentHeight, _contentWidth); // expose resizeEvent
1034
 
  }
1035
 
 
1036
 
  _resizing = false;
1037
 
}
1038
 
 
1039
 
void KTerminalDisplay::scrollToEnd()
1040
 
{
1041
 
  //_screenWindow->scrollTo( _scrollBar->value() + 1 );
1042
 
  _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
1043
 
}
1044
 
 
1045
 
void KTerminalDisplay::extendSelection( const QPoint& position )
1046
 
{
1047
 
  QPoint pos = position;
1048
 
 
1049
 
  if ( !_screenWindow )
1050
 
      return;
1051
 
 
1052
 
  //if ( !contentsRect().contains(ev->pos()) ) return;
1053
 
 
1054
 
  // we're in the process of moving the mouse with the left button pressed
1055
 
  // the mouse cursor will kept caught within the bounds of the text in
1056
 
  // this widget.
1057
 
 
1058
 
  int linesBeyondWidget = 0;
1059
 
 
1060
 
  QRect textBounds( _leftMargin,
1061
 
                    _topMargin,
1062
 
                   _usedColumns*_fontWidth-1,
1063
 
                   _usedLines*_fontHeight-1);
1064
 
 
1065
 
  // Adjust position within text area bounds.
1066
 
  QPoint oldpos = pos;
1067
 
 
1068
 
  pos.setX( qBound(textBounds.left(),pos.x(),textBounds.right()) );
1069
 
  pos.setY( qBound(textBounds.top(),pos.y(),textBounds.bottom()) );
1070
 
 
1071
 
  if ( oldpos.y() > textBounds.bottom() )
1072
 
  {
1073
 
      linesBeyondWidget = (oldpos.y()-textBounds.bottom()) / _fontHeight;
1074
 
  }
1075
 
  if ( oldpos.y() < textBounds.top() )
1076
 
  {
1077
 
      linesBeyondWidget = (textBounds.top()-oldpos.y()) / _fontHeight;
1078
 
  }
1079
 
 
1080
 
  int charColumn = 0;
1081
 
  int charLine = 0;
1082
 
  getCharacterPosition(pos,charLine,charColumn);
1083
 
 
1084
 
  QPoint here = QPoint(charColumn,charLine); //QPoint((pos.x()-tLx-_leftMargin+(_fontWidth/2))/_fontWidth,(pos.y()-tLy-_topMargin)/_fontHeight);
1085
 
  QPoint ohere;
1086
 
  QPoint _iPntSelCorr = _iPntSel;
1087
 
  _iPntSelCorr.ry() -= 0; //_scrollBar->value();
1088
 
  QPoint _pntSelCorr = _pntSel;
1089
 
  _pntSelCorr.ry() -= 0; //_scrollBar->value();
1090
 
  bool swapping = false;
1091
 
 
1092
 
  if ( _wordSelectionMode )
1093
 
  {
1094
 
    // Extend to word boundaries
1095
 
    int i;
1096
 
    QChar selClass;
1097
 
 
1098
 
    bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
1099
 
       ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) );
1100
 
    bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
1101
 
       ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) );
1102
 
    swapping = left_not_right != old_left_not_right;
1103
 
 
1104
 
    // Find left (left_not_right ? from here : from start)
1105
 
    QPoint left = left_not_right ? here : _iPntSelCorr;
1106
 
    i = loc(left.x(),left.y());
1107
 
    if (i>=0 && i<=_imageSize) {
1108
 
      selClass = charClass(_image[i].character);
1109
 
      while ( ((left.x()>0) || (left.y()>0 && (_lineProperties[left.y()-1] & LINE_WRAPPED) ))
1110
 
                      && charClass(_image[i-1].character) == selClass )
1111
 
      { i--; if (left.x()>0) left.rx()--; else {left.rx()=_usedColumns-1; left.ry()--;} }
1112
 
    }
1113
 
 
1114
 
    // Find left (left_not_right ? from start : from here)
1115
 
    QPoint right = left_not_right ? _iPntSelCorr : here;
1116
 
    i = loc(right.x(),right.y());
1117
 
    if (i>=0 && i<=_imageSize) {
1118
 
      selClass = charClass(_image[i].character);
1119
 
      while( ((right.x()<_usedColumns-1) || (right.y()<_usedLines-1 && (_lineProperties[right.y()] & LINE_WRAPPED) ))
1120
 
                      && charClass(_image[i+1].character) == selClass )
1121
 
      { i++; if (right.x()<_usedColumns-1) right.rx()++; else {right.rx()=0; right.ry()++; } }
1122
 
    }
1123
 
 
1124
 
    // Pick which is start (ohere) and which is extension (here)
1125
 
    if ( left_not_right )
1126
 
    {
1127
 
      here = left; ohere = right;
1128
 
    }
1129
 
    else
1130
 
    {
1131
 
      here = right; ohere = left;
1132
 
    }
1133
 
    ohere.rx()++;
1134
 
  }
1135
 
 
1136
 
  if ( _lineSelectionMode )
1137
 
  {
1138
 
    // Extend to complete line
1139
 
    bool above_not_below = ( here.y() < _iPntSelCorr.y() );
1140
 
 
1141
 
    QPoint above = above_not_below ? here : _iPntSelCorr;
1142
 
    QPoint below = above_not_below ? _iPntSelCorr : here;
1143
 
 
1144
 
    while (above.y()>0 && (_lineProperties[above.y()-1] & LINE_WRAPPED) )
1145
 
      above.ry()--;
1146
 
    while (below.y()<_usedLines-1 && (_lineProperties[below.y()] & LINE_WRAPPED) )
1147
 
      below.ry()++;
1148
 
 
1149
 
    above.setX(0);
1150
 
    below.setX(_usedColumns-1);
1151
 
 
1152
 
    // Pick which is start (ohere) and which is extension (here)
1153
 
    if ( above_not_below )
1154
 
    {
1155
 
      here = above; ohere = below;
1156
 
    }
1157
 
    else
1158
 
    {
1159
 
      here = below; ohere = above;
1160
 
    }
1161
 
 
1162
 
    QPoint newSelBegin = QPoint( ohere.x(), ohere.y() );
1163
 
    swapping = !(_tripleSelBegin==newSelBegin);
1164
 
    _tripleSelBegin = newSelBegin;
1165
 
 
1166
 
    ohere.rx()++;
1167
 
  }
1168
 
 
1169
 
  int offset = 0;
1170
 
  if ( !_wordSelectionMode && !_lineSelectionMode )
1171
 
  {
1172
 
    int i;
1173
 
    QChar selClass;
1174
 
 
1175
 
    bool left_not_right = ( here.y() < _iPntSelCorr.y() ||
1176
 
       ( here.y() == _iPntSelCorr.y() && here.x() < _iPntSelCorr.x() ) );
1177
 
    bool old_left_not_right = ( _pntSelCorr.y() < _iPntSelCorr.y() ||
1178
 
       ( _pntSelCorr.y() == _iPntSelCorr.y() && _pntSelCorr.x() < _iPntSelCorr.x() ) );
1179
 
    swapping = left_not_right != old_left_not_right;
1180
 
 
1181
 
    // Find left (left_not_right ? from here : from start)
1182
 
    QPoint left = left_not_right ? here : _iPntSelCorr;
1183
 
 
1184
 
    // Find left (left_not_right ? from start : from here)
1185
 
    QPoint right = left_not_right ? _iPntSelCorr : here;
1186
 
    if ( right.x() > 0 && !_columnSelectionMode )
1187
 
    {
1188
 
      i = loc(right.x(),right.y());
1189
 
      if (i>=0 && i<=_imageSize) {
1190
 
        selClass = charClass(_image[i-1].character);
1191
 
       /* if (selClass == ' ')
1192
 
        {
1193
 
          while ( right.x() < _usedColumns-1 && charClass(_image[i+1].character) == selClass && (right.y()<_usedLines-1) &&
1194
 
                          !(_lineProperties[right.y()] & LINE_WRAPPED))
1195
 
          { i++; right.rx()++; }
1196
 
          if (right.x() < _usedColumns-1)
1197
 
            right = left_not_right ? _iPntSelCorr : here;
1198
 
          else
1199
 
            right.rx()++;  // will be balanced later because of offset=-1;
1200
 
        }*/
1201
 
      }
1202
 
    }
1203
 
 
1204
 
    // Pick which is start (ohere) and which is extension (here)
1205
 
    if ( left_not_right )
1206
 
    {
1207
 
      here = left; ohere = right; offset = 0;
1208
 
    }
1209
 
    else
1210
 
    {
1211
 
      here = right; ohere = left; offset = -1;
1212
 
    }
1213
 
  }
1214
 
 
1215
 
  //if ((here == _pntSelCorr) && (scroll == _scrollBar->value())) return; // not moved
1216
 
 
1217
 
  if (here == ohere) return; // It's not left, it's not right.
1218
 
 
1219
 
  if ( _actSel < 2 || swapping )
1220
 
  {
1221
 
    if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
1222
 
    {
1223
 
        _screenWindow->setSelectionStart( ohere.x() , ohere.y() , true );
1224
 
    }
1225
 
    else
1226
 
    {
1227
 
        _screenWindow->setSelectionStart( ohere.x()-1-offset , ohere.y() , false );
1228
 
    }
1229
 
 
1230
 
  }
1231
 
 
1232
 
  _actSel = 2; // within selection
1233
 
  _pntSel = here;
1234
 
  _pntSel.ry() += 0; //_scrollBar->value();
1235
 
 
1236
 
  if ( _columnSelectionMode && !_lineSelectionMode && !_wordSelectionMode )
1237
 
  {
1238
 
     _screenWindow->setSelectionEnd( here.x() , here.y() );
1239
 
  }
1240
 
  else
1241
 
  {
1242
 
     _screenWindow->setSelectionEnd( here.x()+offset , here.y() );
1243
 
  }
1244
 
 
1245
 
    Q_UNUSED(linesBeyondWidget)
1246
 
 
1247
 
}
1248
 
 
1249
 
void KTerminalDisplay::updateLineProperties()
1250
 
{
1251
 
    if ( !_screenWindow )
1252
 
        return;
1253
 
 
1254
 
    _lineProperties = _screenWindow->getLineProperties();
1255
 
}
1256
 
 
1257
 
QChar KTerminalDisplay::charClass(QChar qch) const
1258
 
{
1259
 
    if ( qch.isSpace() ) return ' ';
1260
 
 
1261
 
    if ( qch.isLetterOrNumber() || _wordCharacters.contains(qch, Qt::CaseInsensitive ) )
1262
 
    return 'a';
1263
 
 
1264
 
    return qch;
1265
 
}
1266
 
 
1267
 
void KTerminalDisplay::setWordCharacters(const QString& wc)
1268
 
{
1269
 
    _wordCharacters = wc;
1270
 
}
1271
 
 
1272
 
/* ------------------------------------------------------------------------- */
1273
 
/*                                                                           */
1274
 
/*                               Clipboard                                   */
1275
 
/*                                                                           */
1276
 
/* ------------------------------------------------------------------------- */
1277
 
 
1278
 
#undef KeyPress
1279
 
 
1280
 
void KTerminalDisplay::emitSelection(bool useXselection,bool appendReturn)
1281
 
{
1282
 
  if ( !_screenWindow )
1283
 
      return;
1284
 
 
1285
 
  // Paste Clipboard by simulating keypress events
1286
 
  QString text = QGuiApplication::clipboard()->text(useXselection ? QClipboard::Selection :
1287
 
                                                                 QClipboard::Clipboard);
1288
 
  if(appendReturn)
1289
 
    text.append("\r");
1290
 
  if ( ! text.isEmpty() )
1291
 
  {
1292
 
    text.replace('\n', '\r');
1293
 
    QKeyEvent e(QEvent::KeyPress, 0, Qt::NoModifier, text);
1294
 
    emit keyPressedSignal(&e); // expose as a big fat keypress event
1295
 
 
1296
 
    _screenWindow->clearSelection();
1297
 
  }
1298
 
}
1299
 
 
1300
 
void KTerminalDisplay::setSelection(const QString& t)
1301
 
{
1302
 
  QGuiApplication::clipboard()->setText(t, QClipboard::Selection);
1303
 
}
1304
 
 
1305
 
void KTerminalDisplay::copyClipboard()
1306
 
{
1307
 
  if ( !_screenWindow )
1308
 
      return;
1309
 
 
1310
 
  QString text = _screenWindow->selectedText(_preserveLineBreaks);
1311
 
  if (!text.isEmpty())
1312
 
    QGuiApplication::clipboard()->setText(text);
1313
 
}
1314
 
 
1315
 
void KTerminalDisplay::pasteClipboard()
1316
 
{
1317
 
  emitSelection(false,false);
1318
 
}
1319
 
 
1320
 
void KTerminalDisplay::pasteSelection()
1321
 
{
1322
 
  emitSelection(true,false);
1323
 
}
1324
 
 
1325
 
/* ------------------------------------------------------------------------- */
1326
 
/*                                                                           */
1327
 
/*                                Keyboard                                   */
1328
 
/*                                                                           */
1329
 
/* ------------------------------------------------------------------------- */
1330
 
 
1331
 
void KTerminalDisplay::setFlowControlWarningEnabled( bool enable )
1332
 
{
1333
 
    _flowControlWarningEnabled = enable;
1334
 
 
1335
 
    // if the dialog is currently visible and the flow control warning has
1336
 
    // been disabled then hide the dialog
1337
 
//    if (!enable)
1338
 
//        outputSuspended(false);
1339
 
}
1340
 
 
1341
 
void KTerminalDisplay::inputMethodEvent( QInputMethodEvent* event )
1342
 
{
1343
 
    QKeyEvent keyEvent(QEvent::KeyPress,0,Qt::NoModifier,event->commitString());
1344
 
    emit keyPressedSignal(&keyEvent);
1345
 
 
1346
 
    _inputMethodData.preeditString = event->preeditString();
1347
 
    QRect updRect = geometryRound(preeditRect() | _inputMethodData.previousPreeditRect);
1348
 
    update(updRect);
1349
 
 
1350
 
    event->accept();
1351
 
}
1352
 
 
1353
 
void KTerminalDisplay::inputMethodQuery(QInputMethodQueryEvent *event)
1354
 
{
1355
 
    event->setValue(Qt::ImEnabled, true);
1356
 
    event->setValue(Qt::ImHints, Qt::ImhNoPredictiveText);
1357
 
    event->accept();
1358
 
}
1359
 
 
1360
 
QVariant KTerminalDisplay::inputMethodQuery(Qt::InputMethodQuery query) const
1361
 
{
1362
 
    const QPoint cursorPos = _screenWindow ? _screenWindow->cursorPosition() : QPoint(0,0);
1363
 
    switch ( query )
1364
 
    {
1365
 
        case Qt::ImEnabled:
1366
 
            return (bool)(flags() & ItemAcceptsInputMethod);
1367
 
            break;
1368
 
        case Qt::ImMicroFocus:
1369
 
            return imageToWidget(QRect(cursorPos.x(),cursorPos.y(),1,1));
1370
 
            break;
1371
 
        case Qt::ImFont:
1372
 
            return m_font;
1373
 
            break;
1374
 
        case Qt::ImCursorPosition:
1375
 
            // return the cursor position within the current line
1376
 
            return cursorPos.x();
1377
 
            break;
1378
 
        case Qt::ImSurroundingText:
1379
 
            {
1380
 
                // return the text from the current line
1381
 
                QString lineText;
1382
 
                QTextStream stream(&lineText);
1383
 
                PlainTextDecoder decoder;
1384
 
                decoder.begin(&stream);
1385
 
                decoder.decodeLine(&_image[loc(0,cursorPos.y())],_usedColumns,_lineProperties[cursorPos.y()]);
1386
 
                decoder.end();
1387
 
                return lineText;
1388
 
            }
1389
 
            break;
1390
 
        case Qt::ImCurrentSelection:
1391
 
            return QString();
1392
 
            break;
1393
 
        default:
1394
 
            break;
1395
 
    }
1396
 
 
1397
 
    return QVariant();
1398
 
}
1399
 
 
1400
 
bool KTerminalDisplay::handleShortcutOverrideEvent(QKeyEvent* keyEvent)
1401
 
{
1402
 
    int modifiers = keyEvent->modifiers();
1403
 
 
1404
 
    //  When a possible shortcut combination is pressed,
1405
 
    //  emit the overrideShortcutCheck() signal to allow the host
1406
 
    //  to decide whether the terminal should override it or not.
1407
 
    if (modifiers != Qt::NoModifier)
1408
 
    {
1409
 
        int modifierCount = 0;
1410
 
        unsigned int currentModifier = Qt::ShiftModifier;
1411
 
 
1412
 
        while (currentModifier <= Qt::KeypadModifier)
1413
 
        {
1414
 
            if (modifiers & currentModifier)
1415
 
                modifierCount++;
1416
 
            currentModifier <<= 1;
1417
 
        }
1418
 
        if (modifierCount < 2)
1419
 
        {
1420
 
            bool override = false;
1421
 
            emit overrideShortcutCheck(keyEvent,override);
1422
 
            if (override)
1423
 
            {
1424
 
                keyEvent->accept();
1425
 
                return true;
1426
 
            }
1427
 
        }
1428
 
    }
1429
 
 
1430
 
    // Override any of the following shortcuts because
1431
 
    // they are needed by the terminal
1432
 
    int keyCode = keyEvent->key() | modifiers;
1433
 
    switch ( keyCode )
1434
 
    {
1435
 
      // list is taken from the QLineEdit::event() code
1436
 
      case Qt::Key_Tab:
1437
 
      case Qt::Key_Delete:
1438
 
      case Qt::Key_Home:
1439
 
      case Qt::Key_End:
1440
 
      case Qt::Key_Backspace:
1441
 
      case Qt::Key_Left:
1442
 
      case Qt::Key_Right:
1443
 
        keyEvent->accept();
1444
 
        return true;
1445
 
    }
1446
 
    return false;
1447
 
}
1448
 
 
1449
 
bool KTerminalDisplay::event(QEvent* event)
1450
 
{
1451
 
  bool eventHandled = false;
1452
 
  switch (event->type())
1453
 
  {
1454
 
    case QEvent::ShortcutOverride:
1455
 
        eventHandled = handleShortcutOverrideEvent((QKeyEvent*)event);
1456
 
        break;
1457
 
    case QEvent::PaletteChange:
1458
 
    case QEvent::ApplicationPaletteChange:
1459
 
        break;
1460
 
    case QEvent::InputMethod:
1461
 
        inputMethodEvent(static_cast<QInputMethodEvent *>(event));
1462
 
        break;
1463
 
    case QEvent::InputMethodQuery:
1464
 
        inputMethodQuery(static_cast<QInputMethodQueryEvent *>(event));
1465
 
        break;
1466
 
    default:
1467
 
        eventHandled = QQuickPaintedItem::event(event);
1468
 
        break;
1469
 
  }
1470
 
  return eventHandled; //parent->event(event);
1471
 
}
1472
 
 
1473
 
void KTerminalDisplay::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1474
 
{
1475
 
    if (newGeometry != oldGeometry) {
1476
 
        m_widgetRect = newGeometry;
1477
 
        propagateSize();
1478
 
        update();
1479
 
    }
1480
 
 
1481
 
    QQuickPaintedItem::geometryChanged(newGeometry,oldGeometry);
1482
 
}
1483
 
 
1484
 
QRect KTerminalDisplay::geometryRound(const QRectF &r) const
1485
 
{
1486
 
    QRect rect;
1487
 
 
1488
 
    rect.setTop(qRound(r.top()));
1489
 
    rect.setBottom(qRound(r.bottom()));
1490
 
    rect.setLeft(qRound(r.left()));
1491
 
    rect.setRight(qRound(r.right()));
1492
 
 
1493
 
    return rect;
1494
 
}
1495
 
 
1496
 
void KTerminalDisplay::mousePressEvent(QMouseEvent *ev)
1497
 
{
1498
 
    if (m_focusOnClick) forcedFocus();
1499
 
    if (m_showVKBonClick) ShowVKB(true);
1500
 
 
1501
 
    emit clicked();
1502
 
    QQuickPaintedItem::mouseMoveEvent(ev);
1503
 
}
1504
 
 
1505
 
void KTerminalDisplay::setBellMode(int mode)
1506
 
{
1507
 
  _bellMode=mode;
1508
 
}
1509
 
 
1510
 
void KTerminalDisplay::enableBell()
1511
 
{
1512
 
    _allowBell = true;
1513
 
}
1514
 
 
1515
 
void KTerminalDisplay::bell(const QString& message)
1516
 
{
1517
 
  Q_UNUSED(message)
1518
 
 
1519
 
  if (_bellMode==NoBell) return;
1520
 
 
1521
 
  //limit the rate at which bells can occur
1522
 
  //...mainly for sound effects where rapid bells in sequence
1523
 
  //produce a horrible noise
1524
 
  if ( _allowBell )
1525
 
  {
1526
 
    _allowBell = false;
1527
 
    QTimer::singleShot(500,this,SLOT(enableBell()));
1528
 
 
1529
 
    if (_bellMode==SystemBeepBell)
1530
 
    {
1531
 
        // NO BEEP !
1532
 
        //QGuiApplication::beep();
1533
 
        int a = 0;
1534
 
        Q_UNUSED(a)
1535
 
    }
1536
 
    else if (_bellMode==NotifyBell)
1537
 
    {
1538
 
        //KNotification::event("BellVisible", message,QPixmap(),this);
1539
 
        // TODO/FIXME: qt4 notifications?
1540
 
    }
1541
 
    else if (_bellMode==VisualBell)
1542
 
    {
1543
 
        swapColorTable();
1544
 
        QTimer::singleShot(200,this,SLOT(swapColorTable()));
1545
 
    }
1546
 
  }
1547
 
}
1548
 
 
1549
 
void KTerminalDisplay::selectionChanged()
1550
 
{
1551
 
    emit copyAvailable(_screenWindow->selectedText(false).isEmpty() == false);
1552
 
}
1553
 
 
1554
 
void KTerminalDisplay::swapColorTable()
1555
 
{
1556
 
  ColorEntry color = _colorTable[1];
1557
 
  _colorTable[1]=_colorTable[0];
1558
 
  _colorTable[0]= color;
1559
 
  _colorsInverted = !_colorsInverted;
1560
 
  update();
1561
 
}
1562
 
 
1563
 
void KTerminalDisplay::clearImage()
1564
 
{
1565
 
  // We initialize _image[_imageSize] too. See makeImage()
1566
 
  for (int i = 0; i <= _imageSize; i++)
1567
 
  {
1568
 
    _image[i].character = ' ';
1569
 
    _image[i].foregroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
1570
 
                                               DEFAULT_FORE_COLOR);
1571
 
    _image[i].backgroundColor = CharacterColor(COLOR_SPACE_DEFAULT,
1572
 
                                               DEFAULT_BACK_COLOR);
1573
 
    _image[i].rendition = DEFAULT_RENDITION;
1574
 
  }
1575
 
}
1576
 
 
1577
 
void KTerminalDisplay::calcGeometry()
1578
 
{
1579
 
    _leftMargin    = DEFAULT_LEFT_MARGIN;
1580
 
    _contentWidth  = width() - 2 * DEFAULT_LEFT_MARGIN;
1581
 
 
1582
 
    _topMargin     = DEFAULT_TOP_MARGIN;
1583
 
    _contentHeight = height() - 2 * DEFAULT_TOP_MARGIN + /* mysterious */ 1;
1584
 
 
1585
 
    // ensure that display is always at least one column wide
1586
 
    _columns     = qMax(1, qFloor(_contentWidth / (double)_fontWidth));
1587
 
    _usedColumns = qMin(_usedColumns,_columns);
1588
 
 
1589
 
    // ensure that display is always at least one line high
1590
 
    _lines     = qMax(1, qFloor(_contentHeight / (double)_fontHeight));
1591
 
    _usedLines = qMin(_usedLines,_lines);
1592
 
}
1593
 
 
1594
 
void KTerminalDisplay::makeImage()
1595
 
{
1596
 
  calcGeometry();
1597
 
 
1598
 
  // confirm that array will be of non-zero size, since the painting code
1599
 
  // assumes a non-zero array length
1600
 
  Q_ASSERT( _lines > 0 && _columns > 0 );
1601
 
  Q_ASSERT( _usedLines <= _lines && _usedColumns <= _columns );
1602
 
 
1603
 
  _imageSize=_lines*_columns;
1604
 
 
1605
 
  // We over-commit one character so that we can be more relaxed in dealing with
1606
 
  // certain boundary conditions: _image[_imageSize] is a valid but unused position
1607
 
  _image = new Character[_imageSize+1];
1608
 
 
1609
 
  clearImage();
1610
 
}
1611
 
 
1612
 
uint KTerminalDisplay::lineSpacing() const
1613
 
{
1614
 
  return _lineSpacing;
1615
 
}
1616
 
 
1617
 
void KTerminalDisplay::setLineSpacing(uint i)
1618
 
{
1619
 
  _lineSpacing = i;
1620
 
  setVTFont(m_font); // Trigger an update.
1621
 
}
1622
 
 
1623
 
 
1624
 
 
1625
 
/////////////////////////////////////////////////////////////////////////////////////
1626
 
/////////////////////////////////////////////////////////////////////////////////////
1627
 
///                                    PAINT
1628
 
/////////////////////////////////////////////////////////////////////////////////////
1629
 
/////////////////////////////////////////////////////////////////////////////////////
1630
 
 
1631
 
QRect KTerminalDisplay::imageToWidget(const QRect& imageArea) const
1632
 
{
1633
 
    QRect result;
1634
 
    result.setLeft(   _leftMargin + _fontWidth * imageArea.left() );
1635
 
    result.setTop(    _topMargin  + _fontHeight * imageArea.top() );
1636
 
    result.setWidth(  _fontWidth  * imageArea.width() );
1637
 
    result.setHeight( _fontHeight * imageArea.height() );
1638
 
 
1639
 
    return result;
1640
 
}
1641
 
 
1642
 
void KTerminalDisplay::paint(QPainter *painter)
1643
 
{
1644
 
    //contentsBoundingRect()
1645
 
    QRectF rect = m_widgetRect;
1646
 
    drawBackground(painter, rect, _colorTable[DEFAULT_BACK_COLOR].color, true /* use opacity setting */);
1647
 
 
1648
 
    /////////////////////////////////////
1649
 
    ///           !!!WARN!!!
1650
 
    ///   THIS FUNCTION REQUIRE PATCH
1651
 
    /////////////////////////////////////
1652
 
    drawContents(painter, rect);
1653
 
    /////////////////////////////////////
1654
 
    /////////////////////////////////////
1655
 
 
1656
 
    drawInputMethodPreeditString(painter, preeditRect());
1657
 
    //paintFilters(painter);
1658
 
}
1659
 
 
1660
 
void KTerminalDisplay::drawContents(QPainter  *paint, QRectF &rect)
1661
 
{
1662
 
  //Q_UNUSED(rect)
1663
 
  int left_   = ceil(rect.left());
1664
 
  int top_    = ceil(rect.top());
1665
 
  int right_  = ceil(rect.right());
1666
 
  int bottom_ = ceil(rect.bottom());
1667
 
 
1668
 
  int lux = qMin(_usedColumns-1, qMax(0, qRound((left_   + _leftMargin ) / _fontWidth  )));
1669
 
  int luy = qMin(_usedLines-1,   qMax(0, qRound((top_    + _topMargin  ) / _fontHeight )));
1670
 
  int rlx = qMin(_usedColumns-1, qMax(0, qRound((right_  - _leftMargin ) / _fontWidth  )));
1671
 
  int rly = qMin(_usedLines-1,   qMax(0, qRound((bottom_ - _topMargin  ) / _fontHeight )));
1672
 
 
1673
 
  // prevent zero size buffer
1674
 
  if (_usedColumns<=1) return;
1675
 
 
1676
 
  const int bufferSize = _usedColumns;
1677
 
  QString unistr;
1678
 
  unistr.reserve(bufferSize);
1679
 
  for (int y = luy; y <= rly; y++)
1680
 
  {
1681
 
    quint16 c = _image[loc(lux,y)].character;
1682
 
    int x = lux;
1683
 
    if(!c && x)
1684
 
      x--; // Search for start of multi-column character
1685
 
    for (; x <= rlx; x++)
1686
 
    {
1687
 
      int len = 1;
1688
 
      int p = 0;
1689
 
 
1690
 
      // reset our buffer to the maximal size
1691
 
      unistr.resize(bufferSize);
1692
 
      QChar *disstrU = unistr.data();
1693
 
 
1694
 
      // is this a single character or a sequence of characters ?
1695
 
      if ( _image[loc(x,y)].rendition & RE_EXTENDED_CHAR )
1696
 
      {
1697
 
        // sequence of characters
1698
 
        ushort extendedCharLength = 0;
1699
 
        ushort* chars = ExtendedCharTable::instance
1700
 
                            .lookupExtendedChar(_image[loc(x,y)].charSequence,extendedCharLength);
1701
 
        for ( int index = 0 ; index < extendedCharLength ; index++ )
1702
 
        {
1703
 
            Q_ASSERT( p < bufferSize );
1704
 
            disstrU[p++] = chars[index];
1705
 
        }
1706
 
      }
1707
 
      else
1708
 
      {
1709
 
        // single character
1710
 
        c = _image[loc(x,y)].character;
1711
 
        if (c)
1712
 
        {
1713
 
             Q_ASSERT( p < bufferSize );
1714
 
             disstrU[p++] = c; //fontMap(c);
1715
 
        }
1716
 
      }
1717
 
 
1718
 
      bool lineDraw = isLineChar(c);
1719
 
      bool doubleWidth = (_image[ qMin(loc(x,y)+1,_imageSize) ].character == 0);
1720
 
      CharacterColor currentForeground = _image[loc(x,y)].foregroundColor;
1721
 
      CharacterColor currentBackground = _image[loc(x,y)].backgroundColor;
1722
 
      quint8 currentRendition = _image[loc(x,y)].rendition;
1723
 
 
1724
 
      while (x+len <= rlx &&
1725
 
             _image[loc(x+len,y)].foregroundColor == currentForeground &&
1726
 
             _image[loc(x+len,y)].backgroundColor == currentBackground &&
1727
 
             _image[loc(x+len,y)].rendition == currentRendition &&
1728
 
             (_image[ qMin(loc(x+len,y)+1,_imageSize) ].character == 0) == doubleWidth &&
1729
 
             isLineChar( c = _image[loc(x+len,y)].character) == lineDraw) // Assignment!
1730
 
      {
1731
 
        if (c)
1732
 
          disstrU[p++] = c; //fontMap(c);
1733
 
        if (doubleWidth) // assert((_image[loc(x+len,y)+1].character == 0)), see above if condition
1734
 
          len++; // Skip trailing part of multi-column character
1735
 
        len++;
1736
 
      }
1737
 
      if ((x+len < _usedColumns) && (!_image[loc(x+len,y)].character))
1738
 
        len++; // Adjust for trailing part of multi-column character
1739
 
 
1740
 
         bool save__fixedFont = _fixedFont;
1741
 
 
1742
 
         if (lineDraw)
1743
 
            _fixedFont = false;
1744
 
         if (doubleWidth)
1745
 
            _fixedFont = false;
1746
 
         unistr.resize(p);
1747
 
 
1748
 
         // Create a text scaling matrix for double width and double height lines.
1749
 
         QMatrix textScale;
1750
 
 
1751
 
         if (y < _lineProperties.size())
1752
 
         {
1753
 
            if (_lineProperties[y] & LINE_DOUBLEWIDTH)
1754
 
                textScale.scale(2,1);
1755
 
 
1756
 
            if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1757
 
                textScale.scale(1,2);
1758
 
         }
1759
 
 
1760
 
         //Apply text scaling matrix.
1761
 
         paint->setWorldMatrix(textScale, true);
1762
 
 
1763
 
         //calculate the area in which the text will be drawn
1764
 
         QRectF textArea = QRectF( _leftMargin + _fontWidth*x , _topMargin + _fontHeight*y , _fontWidth*len , _fontHeight);
1765
 
 
1766
 
         //move the calculated area to take account of scaling applied to the painter.
1767
 
         //the position of the area from the origin (0,0) is scaled
1768
 
         //by the opposite of whatever
1769
 
         //transformation has been applied to the painter.  this ensures that
1770
 
         //painting does actually start from textArea.topLeft()
1771
 
         //(instead of textArea.topLeft() * painter-scale)
1772
 
         textArea.moveTopLeft( textScale.inverted().map(textArea.topLeft()) );
1773
 
 
1774
 
         //paint text fragment
1775
 
         drawTextFragment(  paint,
1776
 
                            textArea,
1777
 
                            unistr,
1778
 
                            &_image[loc(x,y)] ); //,
1779
 
                            //0,
1780
 
                            //!_isPrinting );
1781
 
 
1782
 
         _fixedFont = save__fixedFont;
1783
 
 
1784
 
         //reset back to single-width, single-height _lines
1785
 
         paint->setWorldMatrix(textScale.inverted(), true);
1786
 
 
1787
 
         if (y < _lineProperties.size()-1)
1788
 
         {
1789
 
            //double-height _lines are represented by two adjacent _lines
1790
 
            //containing the same characters
1791
 
            //both _lines will have the LINE_DOUBLEHEIGHT attribute.
1792
 
            //If the current line has the LINE_DOUBLEHEIGHT attribute,
1793
 
            //we can therefore skip the next line
1794
 
            if (_lineProperties[y] & LINE_DOUBLEHEIGHT)
1795
 
                y++;
1796
 
         }
1797
 
 
1798
 
        x += len - 1;
1799
 
    }
1800
 
  }
1801
 
}
1802
 
 
1803
 
 
1804
 
void KTerminalDisplay::drawLineCharString( QPainter* painter, qreal x, qreal y, const QString& str,
1805
 
                                           const Character* attributes)
1806
 
{
1807
 
        const QPen& currentPen = painter->pen();
1808
 
 
1809
 
        if ( (attributes->rendition & RE_BOLD) && _boldIntense )
1810
 
        {
1811
 
            QPen boldPen(currentPen);
1812
 
            boldPen.setWidth(3);
1813
 
            painter->setPen( boldPen );
1814
 
        }
1815
 
 
1816
 
        for (int i=0 ; i < str.length(); i++)
1817
 
        {
1818
 
            uchar code = str[i].cell();
1819
 
            if (LineChars[code])
1820
 
                drawLineChar(painter, x + (_fontWidth*i), y, _fontWidth, _fontHeight, code);
1821
 
        }
1822
 
 
1823
 
        painter->setPen( currentPen );
1824
 
}
1825
 
 
1826
 
void KTerminalDisplay::drawBackground(QPainter* painter, const QRectF &rect, const QColor& backgroundColor, bool useOpacitySetting )
1827
 
{
1828
 
        // the area of the widget showing the contents of the terminal display is drawn
1829
 
        // using the background color from the color scheme set with setColorTable()
1830
 
 
1831
 
        QRectF contentsRect = rect;
1832
 
 
1833
 
        if ( HAVE_TRANSPARENCY && qAlpha(_blendColor) < 0xff && useOpacitySetting )
1834
 
        {
1835
 
            QColor color(backgroundColor);
1836
 
            color.setAlpha(qAlpha(_blendColor));
1837
 
 
1838
 
            painter->save();
1839
 
            painter->setCompositionMode(QPainter::CompositionMode_Source);
1840
 
            painter->fillRect(contentsRect, color);
1841
 
            painter->restore();
1842
 
        }
1843
 
        else
1844
 
            painter->fillRect(contentsRect, backgroundColor);
1845
 
 
1846
 
}
1847
 
 
1848
 
void KTerminalDisplay::drawCursor(QPainter* painter,
1849
 
                                 const QRectF& rect,
1850
 
                                 const QColor& foregroundColor,
1851
 
                                 const QColor& /*backgroundColor*/,
1852
 
                                 bool& invertCharacterColor)
1853
 
{
1854
 
    QRectF cursorRect = rect;
1855
 
    cursorRect.setHeight(_fontHeight - _lineSpacing - 1);
1856
 
 
1857
 
    if (!_cursorBlinking)
1858
 
    {
1859
 
       if ( _cursorColor.isValid() )
1860
 
           painter->setPen(_cursorColor);
1861
 
       else
1862
 
           painter->setPen(foregroundColor);
1863
 
 
1864
 
       if ( _cursorShape == BlockCursor )
1865
 
       {
1866
 
            // draw the cursor outline, adjusting the area so that
1867
 
            // it is draw entirely inside 'rect'
1868
 
            int penWidth = qMax(1,painter->pen().width());
1869
 
 
1870
 
            painter->drawRect(cursorRect.adjusted( penWidth/2,
1871
 
                                                   penWidth/2,
1872
 
                                                 - penWidth/2 - penWidth%2,
1873
 
                                                 - penWidth/2 - penWidth%2));
1874
 
            if ( hasFocus() )
1875
 
            {
1876
 
                painter->fillRect(cursorRect, _cursorColor.isValid() ? _cursorColor : foregroundColor);
1877
 
 
1878
 
                if ( !_cursorColor.isValid() )
1879
 
                {
1880
 
                    // invert the colour used to draw the text to ensure that the character at
1881
 
                    // the cursor position is readable
1882
 
                    invertCharacterColor = true;
1883
 
                }
1884
 
            }
1885
 
       }
1886
 
       else if ( _cursorShape == UnderlineCursor )
1887
 
            painter->drawLine(cursorRect.left(),
1888
 
                              cursorRect.bottom(),
1889
 
                              cursorRect.right(),
1890
 
                              cursorRect.bottom());
1891
 
       else if ( _cursorShape == IBeamCursor )
1892
 
            painter->drawLine(cursorRect.left(),
1893
 
                              cursorRect.top(),
1894
 
                              cursorRect.left(),
1895
 
                              cursorRect.bottom());
1896
 
 
1897
 
    }
1898
 
}
1899
 
 
1900
 
void KTerminalDisplay::drawCharacters(QPainter* painter,
1901
 
                                     const QRectF& rect,
1902
 
                                     const QString& text,
1903
 
                                     const Character* style,
1904
 
                                     bool invertCharacterColor)
1905
 
{
1906
 
    // don't draw text which is currently blinking
1907
 
    if ( _blinking && (style->rendition & RE_BLINK) )
1908
 
            return;
1909
 
 
1910
 
    // setup bold and underline
1911
 
    bool useBold;
1912
 
    ColorEntry::FontWeight weight = style->fontWeight(_colorTable);
1913
 
    if (weight == ColorEntry::UseCurrentFormat)
1914
 
        useBold = ((style->rendition & RE_BOLD) && _boldIntense) || m_font.bold();
1915
 
    else
1916
 
        useBold = (weight == ColorEntry::Bold) ? true : false;
1917
 
    bool useUnderline = style->rendition & RE_UNDERLINE || m_font.underline();
1918
 
 
1919
 
    QFont font  = m_font;
1920
 
    QFont font_ = painter->font();
1921
 
    if (    font.bold() != useBold
1922
 
         || font.underline() != useUnderline )
1923
 
    {
1924
 
       font.setBold(useBold);
1925
 
       font.setUnderline(useUnderline);
1926
 
    }
1927
 
 
1928
 
#ifdef Q_WS_UBUNTU
1929
 
#if QT_VERSION >= 0x040700
1930
 
    font.setStyleStrategy(QFont::ForceIntegerMetrics);
1931
 
#else
1932
 
#warning "Correct handling of the QFont metrics requited Qt>=4.7"
1933
 
#endif
1934
 
#endif
1935
 
 
1936
 
    painter->setFont(font);
1937
 
 
1938
 
    // setup pen
1939
 
    const CharacterColor& textColor = ( invertCharacterColor ? style->backgroundColor : style->foregroundColor );
1940
 
    const QColor color = textColor.color(_colorTable);
1941
 
    QPen pen = painter->pen();
1942
 
    if ( pen.color() != color )
1943
 
    {
1944
 
        pen.setColor(color);
1945
 
        painter->setPen(color);
1946
 
    }
1947
 
 
1948
 
    // draw text
1949
 
    if ( isLineCharString(text) )
1950
 
        drawLineCharString(painter,rect.x(),rect.y(),text,style);
1951
 
    else
1952
 
    {
1953
 
        if (_bidiEnabled)
1954
 
            painter->drawText(rect,text);
1955
 
        else
1956
 
            painter->drawText(rect,LTR_OVERRIDE_CHAR+text);
1957
 
    }
1958
 
 
1959
 
    painter->setFont(font_);
1960
 
}
1961
 
 
1962
 
void KTerminalDisplay::drawTextFragment(QPainter* painter ,
1963
 
                                       const QRectF& rect,
1964
 
                                       const QString& text,
1965
 
                                       const Character* style)
1966
 
{
1967
 
    painter->save();
1968
 
 
1969
 
    // setup painter
1970
 
    const QColor foregroundColor = style->foregroundColor.color(_colorTable);
1971
 
    const QColor backgroundColor = style->backgroundColor.color(_colorTable);
1972
 
 
1973
 
    // draw background if different from the display's background color
1974
 
    if ( backgroundColor != m_palette.background().color() )
1975
 
        drawBackground(painter, rect, backgroundColor, false /* do not use transparency */);
1976
 
 
1977
 
    // draw cursor shape if the current character is the cursor
1978
 
    // this may alter the foreground and background colors
1979
 
    bool invertCharacterColor = false;
1980
 
    if ( style->rendition & RE_CURSOR )
1981
 
        drawCursor(painter, rect, foregroundColor,backgroundColor,invertCharacterColor);
1982
 
 
1983
 
    // draw text
1984
 
    drawCharacters(painter, rect, text, style, invertCharacterColor);
1985
 
 
1986
 
    painter->restore();
1987
 
}
1988
 
 
1989
 
void KTerminalDisplay::getCharacterPosition(const QPointF &widgetPoint, int& line, int& column) const
1990
 
{
1991
 
    //contentsBoundingRect()
1992
 
    QRectF rect = m_widgetRect;
1993
 
 
1994
 
    column = qRound((widgetPoint.x() + _fontWidth/2 - rect.left()-_leftMargin) / _fontWidth);
1995
 
    line   = qRound((widgetPoint.y() - rect.top()-_topMargin) / _fontHeight);
1996
 
 
1997
 
    if ( line < 0 )
1998
 
        line = 0;
1999
 
    if ( column < 0 )
2000
 
        column = 0;
2001
 
 
2002
 
    if ( line >= _usedLines )
2003
 
        line = _usedLines-1;
2004
 
 
2005
 
    // the column value returned can be equal to _usedColumns, which
2006
 
    // is the position just after the last character displayed in a line.
2007
 
    //
2008
 
    // this is required so that the user can select characters in the right-most
2009
 
    // column (or left-most for right-to-left input)
2010
 
    if ( column > _usedColumns )
2011
 
        column = _usedColumns;
2012
 
}
2013
 
 
2014
 
QRectF KTerminalDisplay::preeditRect() const
2015
 
{
2016
 
    const int preeditLength = string_width(_inputMethodData.preeditString);
2017
 
 
2018
 
    if ( preeditLength == 0 )
2019
 
        return QRectF();
2020
 
 
2021
 
    return QRectF(_leftMargin + _fontWidth*cursorPosition().x(),
2022
 
                  _topMargin  + _fontHeight*cursorPosition().y(),
2023
 
                  _fontWidth*preeditLength,
2024
 
                  _fontHeight);
2025
 
}
2026
 
 
2027
 
void KTerminalDisplay::drawInputMethodPreeditString(QPainter *painter , const QRectF &rect)
2028
 
{
2029
 
    if ( _inputMethodData.preeditString.isEmpty() )
2030
 
        return;
2031
 
 
2032
 
    const QPoint cursorPos = cursorPosition();
2033
 
 
2034
 
    bool invertColors = false;
2035
 
    const QColor background = _colorTable[DEFAULT_BACK_COLOR].color;
2036
 
    const QColor foreground = _colorTable[DEFAULT_FORE_COLOR].color;
2037
 
    const Character* style = &_image[loc(cursorPos.x(),cursorPos.y())];
2038
 
 
2039
 
    drawBackground(painter, rect, background,true);
2040
 
    drawCursor(painter, rect, foreground,background,invertColors);
2041
 
    drawCharacters(painter, rect,_inputMethodData.preeditString,style,invertColors);
2042
 
 
2043
 
    _inputMethodData.previousPreeditRect = rect;
2044
 
}
2045
 
 
2046
 
 
2047
 
 
2048
 
void KTerminalDisplay::keyPressEvent(QKeyEvent *event)
2049
 
{
2050
 
    bool emitKeyPressSignal = true;
2051
 
 
2052
 
    // Keyboard-based navigation
2053
 
    if ( event->modifiers() == Qt::ShiftModifier )
2054
 
    {
2055
 
        bool update = true;
2056
 
 
2057
 
        if ( event->key() == Qt::Key_PageUp )
2058
 
        {
2059
 
            _screenWindow->scrollBy( ScreenWindow::ScrollPages , -1 );
2060
 
        }
2061
 
        else if ( event->key() == Qt::Key_PageDown )
2062
 
        {
2063
 
            _screenWindow->scrollBy( ScreenWindow::ScrollPages , 1 );
2064
 
        }
2065
 
        else if ( event->key() == Qt::Key_Up )
2066
 
        {
2067
 
            _screenWindow->scrollBy( ScreenWindow::ScrollLines , -1 );
2068
 
        }
2069
 
        else if ( event->key() == Qt::Key_Down )
2070
 
        {
2071
 
            _screenWindow->scrollBy( ScreenWindow::ScrollLines , 1 );
2072
 
        }
2073
 
        else
2074
 
            update = false;
2075
 
 
2076
 
        if ( update )
2077
 
        {
2078
 
            _screenWindow->setTrackOutput( _screenWindow->atEndOfOutput() );
2079
 
 
2080
 
            updateLineProperties();
2081
 
            updateImage();
2082
 
 
2083
 
            // do not send key press to terminal
2084
 
            emitKeyPressSignal = false;
2085
 
        }
2086
 
    }
2087
 
 
2088
 
    _actSel=0; // Key stroke implies a screen update, so TerminalDisplay won't
2089
 
              // know where the current selection is.
2090
 
 
2091
 
    if (_hasBlinkingCursor)
2092
 
    {
2093
 
      _blinkCursorTimer->start(100); //WARN! HARDCODE
2094
 
      if (_cursorBlinking)
2095
 
        blinkCursorEvent();
2096
 
      else
2097
 
        _cursorBlinking = false;
2098
 
    }
2099
 
 
2100
 
    if ( emitKeyPressSignal )
2101
 
        emit keyPressedSignal(event);
2102
 
 
2103
 
    event->accept();
2104
 
}
2105
 
 
2106
 
 
2107
 
/////////////////////////////////////////////////////////////////////////////////////
2108
 
/////////////////////////////////////////////////////////////////////////////////////
2109
 
///                                   HELPER
2110
 
/////////////////////////////////////////////////////////////////////////////////////
2111
 
/////////////////////////////////////////////////////////////////////////////////////
2112
 
 
2113
 
AutoScrollHandler::AutoScrollHandler(QQuickItem* parent)
2114
 
: QObject(parent)
2115
 
, _timerId(0)
2116
 
{
2117
 
    parent->installEventFilter(this);
2118
 
}
2119
 
void AutoScrollHandler::timerEvent(QTimerEvent* event)
2120
 
{
2121
 
    if (event->timerId() != _timerId)
2122
 
        return;
2123
 
 
2124
 
    QMouseEvent mouseEvent(    QEvent::MouseMove,
2125
 
                              widget()->mapFromScene(QCursor::pos()),
2126
 
                              Qt::NoButton,
2127
 
                              Qt::LeftButton,
2128
 
                              Qt::NoModifier);
2129
 
 
2130
 
    QGuiApplication::sendEvent(widget(),&mouseEvent);
2131
 
}
2132
 
bool AutoScrollHandler::eventFilter(QObject* watched,QEvent* event)
2133
 
{
2134
 
    Q_ASSERT( watched == parent() );
2135
 
    Q_UNUSED( watched );
2136
 
 
2137
 
    QMouseEvent* mouseEvent = (QMouseEvent*)event;
2138
 
    switch (event->type())
2139
 
    {
2140
 
        case QEvent::MouseMove:
2141
 
        {
2142
 
            bool mouseInWidget = false; //widget()->rect().contains(mouseEvent->pos());
2143
 
 
2144
 
            if (mouseInWidget)
2145
 
            {
2146
 
                if (_timerId)
2147
 
                    killTimer(_timerId);
2148
 
                _timerId = 0;
2149
 
            }
2150
 
            else
2151
 
            {
2152
 
                if (!_timerId && (mouseEvent->buttons() & Qt::LeftButton))
2153
 
                    _timerId = startTimer(100);
2154
 
            }
2155
 
                break;
2156
 
        }
2157
 
        case QEvent::MouseButtonRelease:
2158
 
            if (_timerId && (mouseEvent->buttons() & ~Qt::LeftButton))
2159
 
            {
2160
 
                killTimer(_timerId);
2161
 
                _timerId = 0;
2162
 
            }
2163
 
        break;
2164
 
        default:
2165
 
        break;
2166
 
    };
2167
 
 
2168
 
    return false;
2169
 
}
2170
 
 
2171
 
//#include "TerminalDisplay.moc"