summaryrefslogtreecommitdiffstats
path: root/konversation/src/multilinetextedit.cpp
blob: 60c14581216533365a00eed52e7c2361b22c7420 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/*
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
*/

/*
  Copyright (C) 2006 Dario Abatianni <eisfuchs@tigress.com>
*/

#include "multilinetextedit.h"

#include <tqpainter.h>
#include <tqregexp.h>

#include <kdebug.h>


MultilineTextEdit::MultilineTextEdit(TQWidget* parent,const char* name) : TQTextEdit(parent,name)
{
  // make sure, our whitespace highlighting gets called whenever needed
  connect(this,TQ_SIGNAL(textChanged()),this,TQ_SLOT(drawWhitespaces()));
  connect(this,TQ_SIGNAL(cursorPositionChanged(int,int)),this,TQ_SLOT(cursorChanged(int,int)));
}

MultilineTextEdit::~MultilineTextEdit()
{
}

void MultilineTextEdit::drawContents(TQPainter* p,int clipx,int clipy,int clipw,int cliph)
{
  // redraw text
  TQTextEdit::drawContents(p,clipx,clipy,clipw,cliph);
  // overlay whitespace markup
  drawWhitespaces();
}

void MultilineTextEdit::drawWhitespaces()
{
  // prepare a rectangle to store the width of the whitespace found
  TQRect space;
  // get the painter for the text area
  TQPainter pa(viewport());

  // get a sane color
  TQColor col=colorGroup().link();
  // and a brush of the same color
  TQBrush fillBrush(col);
  // use it for line drawing
  pa.setPen(col);
  // and for filling
  pa.setBrush(fillBrush);

  // prepare the carriage return coordinates array
  TQPointArray cr(4);
  // and the tabulator arrow coordinate array
  TQPointArray tab(7);

  // whitespace expression
  TQRegExp regex("\\s");

  // line buffer
  TQString line;

  int x,y,pos,paragraph;
  // start looking in every paragraph
  for(paragraph=0;paragraph<paragraphs();paragraph++)
  {
    // get paragraph text
    line=text(paragraph);
    // start looking for whitespaces from the beginning
    pos=0;
    while((pos=line.find(regex,pos))!=-1)
    {
      // whitespace found is not the carriage return at the end of the line?
      if(pos<((int)line.length()-1))
      {
        // get whitespace rectangle
        space=mapToView(paragraph,pos);
        // extract x/y coordinates
        x=space.width()/2-1+space.x();
        y=space.height()/2-1+space.y();

        // if it was a regular blank ...
        if(regex.cap(0)==" ")
        {
          // dras a simple small square
          pa.drawRect(x-1,y,2,2);
        }
        // if it was a tabulator
        else if(regex.cap(0)=="\t")
        {
          // calculate arrow points and draw them filled
          tab.putPoints(0,7, x-5,y-1, x,y-1, x,y-3, x+3,y, x,y+3, x,y+1, x-5,y+1);
          pa.drawPolygon(tab);
        }
      }
      // go to next position and resume looking for more whitespaces
      pos++;
    } // while

    // end of line, get carriage return position
    space=mapToView(paragraph,line.length()-1);
    // extract x/y positions
    x=space.width()/2-1+space.x();
    y=space.height()/2-1+space.y();
    // calculate carriage return triangle coordinates and draw them filled
    cr.putPoints(0,4, x,y, x,y+1, x+4, y+5, x+4, y-4);
    pa.drawPolygon(cr);
  } // for
}

void MultilineTextEdit::cursorChanged(int /* p */ ,int /* i */)
{
  // update markup, since cursor destroys it
  drawWhitespaces();
}

// code below from kbabel and adapted by me (Eisfuchs). Thanks, Guys!

TQRect MultilineTextEdit::mapToView(int para,int index)
{
    if( para < 0 || para > paragraphs() ||
        index < 0 || index > paragraphLength(para) )
            return TQRect(); //invalid rectangle

    const TQFontMetrics& fm = fontMetrics();
    const TQString& paratext = text(para);

    // Find index of the first character on the same line as parameter
    // 'index' using binary search. Very fast, even for long texts.
    int linestart = 0;
    int indexline = lineOfChar( para, index );
    if ( indexline > 0 )
    {
        int min = 0, max = index;
        int i = (min + max)/2;
        int iline = lineOfChar( para, i );
        while ( iline != indexline-1 ||
                lineOfChar( para, i+1 ) != indexline )
        {
            Q_ASSERT( min != max && min != i && max != i );
            if ( iline < indexline )
                min = i;
            else
                max = i;
            i = (min + max)/2;
            iline = lineOfChar( para, i );
        }
        linestart = i+1;
    }
    Q_ASSERT( linestart >= 0 );

    int linewidth = fm.size(ExpandTabs, paratext.mid( linestart, index-linestart )).width();
    int linewidth2 = fm.size(ExpandTabs, paratext.mid( linestart, index-linestart+1 )).width();

    // FIXME as soon as it's possible to ask real margins from TQTextEdit:
    const int left_margin = 4;
    // const int top_margin = 4;

    TQPainter painter( viewport());
    const TQRect& linerect = paragraphRect(para);

    return TQRect(
        contentsToViewport( TQPoint(
            left_margin + linerect.left() + linewidth ,
            /*top_margin + */linerect.top() + indexline * fm.lineSpacing() + fm.leading())),
        TQSize(
            linewidth2-linewidth,
            fm.lineSpacing()
        ));
}

#include "multilinetextedit.moc"