File: | libgui/qterminal/libqterminal/unix/Filter.cpp |
Location: | line 217, column 24 |
Description: | Called C++ object pointer is null |
1 | /* | |||
2 | Copyright (C) 2007, 2013 by Robert Knight <robertknight@gmail.com> | |||
3 | ||||
4 | Rewritten for QT4 by e_k <e_k at users.sourceforge.net>, Copyright (C)2008 | |||
5 | ||||
6 | This program is free software; you can redistribute it and/or modify | |||
7 | it under the terms of the GNU General Public License as published by | |||
8 | the Free Software Foundation; either version 2 of the License, or | |||
9 | (at your option) any later version. | |||
10 | ||||
11 | This program is distributed in the hope that it will be useful, | |||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
14 | GNU General Public License for more details. | |||
15 | ||||
16 | You should have received a copy of the GNU General Public License | |||
17 | along with this program; if not, write to the Free Software | |||
18 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |||
19 | 02110-1301 USA. | |||
20 | */ | |||
21 | ||||
22 | // Own | |||
23 | #include "unix/Filter.h" | |||
24 | ||||
25 | // System | |||
26 | #include <iostream> | |||
27 | ||||
28 | // Qt | |||
29 | #include <QAction> | |||
30 | #include <QApplication> | |||
31 | #include <QClipboard> | |||
32 | #include <QtCore/QString> | |||
33 | ||||
34 | #include <QtCore/QSharedData> | |||
35 | #include <QtCore> | |||
36 | ||||
37 | // Konsole | |||
38 | #include "unix/TerminalCharacterDecoder.h" | |||
39 | ||||
40 | FilterChain::~FilterChain() | |||
41 | { | |||
42 | QMutableListIterator<Filter*> iter(*this); | |||
43 | ||||
44 | while ( iter.hasNext() ) | |||
45 | { | |||
46 | Filter* filter = iter.next(); | |||
47 | iter.remove(); | |||
48 | delete filter; | |||
49 | } | |||
50 | } | |||
51 | ||||
52 | void FilterChain::addFilter(Filter* filter) | |||
53 | { | |||
54 | append(filter); | |||
55 | } | |||
56 | void FilterChain::removeFilter(Filter* filter) | |||
57 | { | |||
58 | removeAll(filter); | |||
59 | } | |||
60 | bool FilterChain::containsFilter(Filter* filter) | |||
61 | { | |||
62 | return contains(filter); | |||
63 | } | |||
64 | void FilterChain::reset() | |||
65 | { | |||
66 | QListIterator<Filter*> iter(*this); | |||
67 | while (iter.hasNext()) | |||
68 | iter.next()->reset(); | |||
69 | } | |||
70 | void FilterChain::setBuffer(const QString* buffer , const QList<int>* linePositions) | |||
71 | { | |||
72 | QListIterator<Filter*> iter(*this); | |||
73 | while (iter.hasNext()) | |||
74 | iter.next()->setBuffer(buffer,linePositions); | |||
75 | } | |||
76 | void FilterChain::process() | |||
77 | { | |||
78 | QListIterator<Filter*> iter(*this); | |||
79 | while (iter.hasNext()) | |||
80 | iter.next()->process(); | |||
81 | } | |||
82 | void FilterChain::clear() | |||
83 | { | |||
84 | QList<Filter*>::clear(); | |||
85 | } | |||
86 | Filter::HotSpot* FilterChain::hotSpotAt(int line , int column) const | |||
87 | { | |||
88 | QListIterator<Filter*> iter(*this); | |||
89 | while (iter.hasNext()) | |||
90 | { | |||
91 | Filter* filter = iter.next(); | |||
92 | Filter::HotSpot* spot = filter->hotSpotAt(line,column); | |||
93 | if ( spot != 0 ) | |||
94 | { | |||
95 | return spot; | |||
96 | } | |||
97 | } | |||
98 | ||||
99 | return 0; | |||
100 | } | |||
101 | ||||
102 | QList<Filter::HotSpot*> FilterChain::hotSpots() const | |||
103 | { | |||
104 | QList<Filter::HotSpot*> list; | |||
105 | QListIterator<Filter*> iter(*this); | |||
106 | while (iter.hasNext()) | |||
107 | { | |||
108 | Filter* filter = iter.next(); | |||
109 | list << filter->hotSpots(); | |||
110 | } | |||
111 | return list; | |||
112 | } | |||
113 | ||||
114 | TerminalImageFilterChain::TerminalImageFilterChain() | |||
115 | : _buffer(0) | |||
116 | , _linePositions(0) | |||
117 | { | |||
118 | } | |||
119 | ||||
120 | TerminalImageFilterChain::~TerminalImageFilterChain() | |||
121 | { | |||
122 | delete _buffer; | |||
123 | delete _linePositions; | |||
124 | } | |||
125 | ||||
126 | void TerminalImageFilterChain::setImage(const Character* const image , int lines , int columns, const QVector<LineProperty>& lineProperties) | |||
127 | { | |||
128 | //qDebug("%s %d", __FILE__, __LINE__); | |||
129 | if (empty()) | |||
130 | return; | |||
131 | //qDebug("%s %d", __FILE__, __LINE__); | |||
132 | ||||
133 | // reset all filters and hotspots | |||
134 | reset(); | |||
135 | //qDebug("%s %d", __FILE__, __LINE__); | |||
136 | ||||
137 | PlainTextDecoder decoder; | |||
138 | decoder.setTrailingWhitespace(false); | |||
139 | ||||
140 | //qDebug("%s %d", __FILE__, __LINE__); | |||
141 | // setup new shared buffers for the filters to process on | |||
142 | QString* newBuffer = new QString(); | |||
143 | QList<int>* newLinePositions = new QList<int>(); | |||
144 | setBuffer( newBuffer , newLinePositions ); | |||
145 | ||||
146 | // free the old buffers | |||
147 | delete _buffer; | |||
148 | delete _linePositions; | |||
149 | ||||
150 | _buffer = newBuffer; | |||
151 | _linePositions = newLinePositions; | |||
152 | ||||
153 | QTextStream lineStream(_buffer); | |||
154 | decoder.begin(&lineStream); | |||
155 | ||||
156 | for (int i=0 ; i < lines ; i++) | |||
157 | { | |||
158 | _linePositions->append(_buffer->length()); | |||
159 | decoder.decodeLine(image + i*columns,columns,LINE_DEFAULT); | |||
160 | ||||
161 | // pretend that each line ends with a newline character. | |||
162 | // this prevents a link that occurs at the end of one line | |||
163 | // being treated as part of a link that occurs at the start of the next line | |||
164 | // | |||
165 | // the downside is that links which are spread over more than one line are not | |||
166 | // highlighted. | |||
167 | // | |||
168 | // TODO - Use the "line wrapped" attribute associated with lines in a | |||
169 | // terminal image to avoid adding this imaginary character for wrapped | |||
170 | // lines | |||
171 | if ( !(lineProperties.value(i,LINE_DEFAULT) & LINE_WRAPPED) ) | |||
172 | lineStream << QChar('\n'); | |||
173 | } | |||
174 | decoder.end(); | |||
175 | // qDebug("%s %d", __FILE__, __LINE__); | |||
176 | } | |||
177 | ||||
178 | Filter::Filter() : | |||
179 | _linePositions(0), | |||
180 | _buffer(0) | |||
181 | { | |||
182 | } | |||
183 | ||||
184 | Filter::~Filter() | |||
185 | { | |||
186 | QListIterator<HotSpot*> iter(_hotspotList); | |||
187 | while (iter.hasNext()) | |||
188 | { | |||
189 | delete iter.next(); | |||
190 | } | |||
191 | } | |||
192 | void Filter::reset() | |||
193 | { | |||
194 | _hotspots.clear(); | |||
195 | _hotspotList.clear(); | |||
196 | } | |||
197 | ||||
198 | void Filter::setBuffer(const QString* buffer , const QList<int>* linePositions) | |||
199 | { | |||
200 | _buffer = buffer; | |||
201 | _linePositions = linePositions; | |||
202 | } | |||
203 | ||||
204 | void Filter::getLineColumn(int position , int& startLine , int& startColumn) | |||
205 | { | |||
206 | Q_ASSERT( _linePositions )((!(_linePositions)) ? qt_assert("_linePositions","qterminal/libqterminal/unix/Filter.cpp" ,206) : qt_noop()); | |||
207 | Q_ASSERT( _buffer )((!(_buffer)) ? qt_assert("_buffer","qterminal/libqterminal/unix/Filter.cpp" ,207) : qt_noop()); | |||
208 | ||||
209 | ||||
210 | for (int i = 0 ; i < _linePositions->count() ; i++) | |||
211 | { | |||
212 | //kDebug() << "line position at " << i << " = " << _linePositions[i]; | |||
213 | int nextLine = 0; | |||
214 | ||||
215 | if ( i == _linePositions->count()-1 ) | |||
216 | { | |||
217 | nextLine = _buffer->length() + 1; | |||
| ||||
218 | } | |||
219 | else | |||
220 | { | |||
221 | nextLine = _linePositions->value(i+1); | |||
222 | } | |||
223 | ||||
224 | // kDebug() << "pos - " << position << " line pos(" << i<< ") " << _linePositions->value(i) << | |||
225 | // " next = " << nextLine << " buffer len = " << _buffer->length(); | |||
226 | ||||
227 | if ( _linePositions->value(i) <= position && position < nextLine ) | |||
228 | { | |||
229 | startLine = i; | |||
230 | startColumn = position - _linePositions->value(i); | |||
231 | return; | |||
232 | } | |||
233 | } | |||
234 | } | |||
235 | ||||
236 | ||||
237 | /*void Filter::addLine(const QString& text) | |||
238 | { | |||
239 | _linePositions << _buffer.length(); | |||
240 | _buffer.append(text); | |||
241 | }*/ | |||
242 | ||||
243 | const QString* Filter::buffer() | |||
244 | { | |||
245 | return _buffer; | |||
246 | } | |||
247 | Filter::HotSpot::~HotSpot() | |||
248 | { | |||
249 | } | |||
250 | void Filter::addHotSpot(HotSpot* spot) | |||
251 | { | |||
252 | _hotspotList << spot; | |||
253 | ||||
254 | for (int line = spot->startLine() ; line <= spot->endLine() ; line++) | |||
255 | { | |||
256 | _hotspots.insert(line,spot); | |||
257 | } | |||
258 | } | |||
259 | QList<Filter::HotSpot*> Filter::hotSpots() const | |||
260 | { | |||
261 | return _hotspotList; | |||
262 | } | |||
263 | QList<Filter::HotSpot*> Filter::hotSpotsAtLine(int line) const | |||
264 | { | |||
265 | return _hotspots.values(line); | |||
266 | } | |||
267 | ||||
268 | Filter::HotSpot* Filter::hotSpotAt(int line , int column) const | |||
269 | { | |||
270 | QListIterator<HotSpot*> spotIter(_hotspots.values(line)); | |||
271 | ||||
272 | while (spotIter.hasNext()) | |||
273 | { | |||
274 | HotSpot* spot = spotIter.next(); | |||
275 | ||||
276 | if ( spot->startLine() == line && spot->startColumn() > column ) | |||
277 | continue; | |||
278 | if ( spot->endLine() == line && spot->endColumn() < column ) | |||
279 | continue; | |||
280 | ||||
281 | return spot; | |||
282 | } | |||
283 | ||||
284 | return 0; | |||
285 | } | |||
286 | ||||
287 | Filter::HotSpot::HotSpot(int startLine , int startColumn , int endLine , int endColumn) | |||
288 | : _startLine(startLine) | |||
289 | , _startColumn(startColumn) | |||
290 | , _endLine(endLine) | |||
291 | , _endColumn(endColumn) | |||
292 | , _type(NotSpecified) | |||
293 | { | |||
294 | } | |||
295 | QString Filter::HotSpot::tooltip() const | |||
296 | { | |||
297 | return QString(); | |||
298 | } | |||
299 | QList<QAction*> Filter::HotSpot::actions() | |||
300 | { | |||
301 | return QList<QAction*>(); | |||
302 | } | |||
303 | int Filter::HotSpot::startLine() const | |||
304 | { | |||
305 | return _startLine; | |||
306 | } | |||
307 | int Filter::HotSpot::endLine() const | |||
308 | { | |||
309 | return _endLine; | |||
310 | } | |||
311 | int Filter::HotSpot::startColumn() const | |||
312 | { | |||
313 | return _startColumn; | |||
314 | } | |||
315 | int Filter::HotSpot::endColumn() const | |||
316 | { | |||
317 | return _endColumn; | |||
318 | } | |||
319 | Filter::HotSpot::Type Filter::HotSpot::type() const | |||
320 | { | |||
321 | return _type; | |||
322 | } | |||
323 | void Filter::HotSpot::setType(Type type) | |||
324 | { | |||
325 | _type = type; | |||
326 | } | |||
327 | ||||
328 | RegExpFilter::RegExpFilter() | |||
329 | { | |||
330 | } | |||
331 | ||||
332 | RegExpFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) | |||
333 | : Filter::HotSpot(startLine,startColumn,endLine,endColumn) | |||
334 | { | |||
335 | setType(Marker); | |||
336 | } | |||
337 | ||||
338 | void RegExpFilter::HotSpot::activate(QObject*) | |||
339 | { | |||
340 | } | |||
341 | ||||
342 | void RegExpFilter::HotSpot::setCapturedTexts(const QStringList& texts) | |||
343 | { | |||
344 | _capturedTexts = texts; | |||
345 | } | |||
346 | QStringList RegExpFilter::HotSpot::capturedTexts() const | |||
347 | { | |||
348 | return _capturedTexts; | |||
349 | } | |||
350 | ||||
351 | void RegExpFilter::setRegExp(const QRegExp& regExp) | |||
352 | { | |||
353 | _searchText = regExp; | |||
354 | } | |||
355 | QRegExp RegExpFilter::regExp() const | |||
356 | { | |||
357 | return _searchText; | |||
358 | } | |||
359 | /*void RegExpFilter::reset(int) | |||
360 | { | |||
361 | _buffer = QString(); | |||
362 | }*/ | |||
363 | void RegExpFilter::process() | |||
364 | { | |||
365 | int pos = 0; | |||
366 | const QString* text = buffer(); | |||
367 | ||||
368 | Q_ASSERT( text )((!(text)) ? qt_assert("text","qterminal/libqterminal/unix/Filter.cpp" ,368) : qt_noop()); | |||
369 | ||||
370 | // ignore any regular expressions which match an empty string. | |||
371 | // otherwise the while loop below will run indefinitely | |||
372 | static const QString emptyString(""); | |||
373 | if ( _searchText.exactMatch(emptyString) ) | |||
| ||||
374 | return; | |||
375 | ||||
376 | while(pos >= 0) | |||
377 | { | |||
378 | pos = _searchText.indexIn(*text,pos); | |||
379 | ||||
380 | if ( pos >= 0 ) | |||
381 | { | |||
382 | ||||
383 | int startLine = 0; | |||
384 | int endLine = 0; | |||
385 | int startColumn = 0; | |||
386 | int endColumn = 0; | |||
387 | ||||
388 | ||||
389 | //kDebug() << "pos from " << pos << " to " << pos + _searchText.matchedLength(); | |||
390 | ||||
391 | getLineColumn(pos,startLine,startColumn); | |||
392 | getLineColumn(pos + _searchText.matchedLength(),endLine,endColumn); | |||
393 | ||||
394 | //kDebug() << "start " << startLine << " / " << startColumn; | |||
395 | //kDebug() << "end " << endLine << " / " << endColumn; | |||
396 | ||||
397 | RegExpFilter::HotSpot* spot = newHotSpot(startLine,startColumn, | |||
398 | endLine,endColumn); | |||
399 | spot->setCapturedTexts(_searchText.capturedTexts()); | |||
400 | ||||
401 | addHotSpot( spot ); | |||
402 | pos += _searchText.matchedLength(); | |||
403 | ||||
404 | // if matchedLength == 0, the program will get stuck in an infinite loop | |||
405 | Q_ASSERT( _searchText.matchedLength() > 0 )((!(_searchText.matchedLength() > 0)) ? qt_assert("_searchText.matchedLength() > 0" ,"qterminal/libqterminal/unix/Filter.cpp",405) : qt_noop()); | |||
406 | } | |||
407 | } | |||
408 | } | |||
409 | ||||
410 | RegExpFilter::HotSpot* RegExpFilter::newHotSpot(int startLine,int startColumn, | |||
411 | int endLine,int endColumn) | |||
412 | { | |||
413 | return new RegExpFilter::HotSpot(startLine,startColumn, | |||
414 | endLine,endColumn); | |||
415 | } | |||
416 | RegExpFilter::HotSpot* UrlFilter::newHotSpot(int startLine,int startColumn,int endLine, | |||
417 | int endColumn) | |||
418 | { | |||
419 | return new UrlFilter::HotSpot(startLine,startColumn, | |||
420 | endLine,endColumn); | |||
421 | } | |||
422 | UrlFilter::HotSpot::HotSpot(int startLine,int startColumn,int endLine,int endColumn) | |||
423 | : RegExpFilter::HotSpot(startLine,startColumn,endLine,endColumn) | |||
424 | , _urlObject(new FilterObject(this)) | |||
425 | { | |||
426 | setType(Link); | |||
427 | } | |||
428 | QString UrlFilter::HotSpot::tooltip() const | |||
429 | { | |||
430 | QString url = capturedTexts().first(); | |||
431 | ||||
432 | const UrlType kind = urlType(); | |||
433 | ||||
434 | if ( kind == StandardUrl ) | |||
435 | return QString(); | |||
436 | else if ( kind == Email ) | |||
437 | return QString(); | |||
438 | else | |||
439 | return QString(); | |||
440 | } | |||
441 | UrlFilter::HotSpot::UrlType UrlFilter::HotSpot::urlType() const | |||
442 | { | |||
443 | QString url = capturedTexts().first(); | |||
444 | ||||
445 | if ( FullUrlRegExp.exactMatch(url) ) | |||
446 | return StandardUrl; | |||
447 | else if ( EmailAddressRegExp.exactMatch(url) ) | |||
448 | return Email; | |||
449 | else | |||
450 | return Unknown; | |||
451 | } | |||
452 | ||||
453 | void UrlFilter::HotSpot::activate(QObject* object) | |||
454 | { | |||
455 | QString url = capturedTexts().first(); | |||
456 | ||||
457 | const UrlType kind = urlType(); | |||
458 | ||||
459 | const QString& actionName = object ? object->objectName() : QString(); | |||
460 | ||||
461 | if ( actionName == "copy-action" ) | |||
462 | { | |||
463 | //kDebug() << "Copying url to clipboard:" << url; | |||
464 | ||||
465 | QApplication::clipboard()->setText(url); | |||
466 | return; | |||
467 | } | |||
468 | ||||
469 | if ( !object || actionName == "open-action" ) | |||
470 | { | |||
471 | if ( kind == StandardUrl ) | |||
472 | { | |||
473 | // if the URL path does not include the protocol ( eg. "www.kde.org" ) then | |||
474 | // prepend http:// ( eg. "www.kde.org" --> "http://www.kde.org" ) | |||
475 | if (!url.contains("://")) | |||
476 | { | |||
477 | url.prepend("http://"); | |||
478 | } | |||
479 | } | |||
480 | else if ( kind == Email ) | |||
481 | { | |||
482 | url.prepend("mailto:"); | |||
483 | } | |||
484 | ||||
485 | // new KRun(url,QApplication::activeWindow()); | |||
486 | } | |||
487 | } | |||
488 | ||||
489 | // Note: Altering these regular expressions can have a major effect on the performance of the filters | |||
490 | // used for finding URLs in the text, especially if they are very general and could match very long | |||
491 | // pieces of text. | |||
492 | // Please be careful when altering them. | |||
493 | ||||
494 | //regexp matches: | |||
495 | // full url: | |||
496 | // protocolname:// or www. followed by anything other than whitespaces, <, >, ' or ", and ends before whitespaces, <, >, ', ", ], !, comma and dot | |||
497 | const QRegExp UrlFilter::FullUrlRegExp("(www\\.(?!\\.)|[a-z][a-z0-9+.-]*://)[^\\s<>'\"]+[^!,\\.\\s<>'\"\\]]"); | |||
498 | // email address: | |||
499 | // [word chars, dots or dashes]@[word chars, dots or dashes].[word chars] | |||
500 | const QRegExp UrlFilter::EmailAddressRegExp("\\b(\\w|\\.|-)+@(\\w|\\.|-)+\\.\\w+\\b"); | |||
501 | ||||
502 | // matches full url or email address | |||
503 | const QRegExp UrlFilter::CompleteUrlRegExp('('+FullUrlRegExp.pattern()+'|'+ | |||
504 | EmailAddressRegExp.pattern()+')'); | |||
505 | ||||
506 | UrlFilter::UrlFilter() | |||
507 | { | |||
508 | setRegExp( CompleteUrlRegExp ); | |||
509 | } | |||
510 | UrlFilter::HotSpot::~HotSpot() | |||
511 | { | |||
512 | delete _urlObject; | |||
513 | } | |||
514 | void FilterObject::activated() | |||
515 | { | |||
516 | _filter->activate(sender()); | |||
517 | } | |||
518 | QList<QAction*> UrlFilter::HotSpot::actions() | |||
519 | { | |||
520 | QList<QAction*> list; | |||
521 | ||||
522 | const UrlType kind = urlType(); | |||
523 | ||||
524 | QAction* openAction = new QAction(_urlObject); | |||
525 | QAction* copyAction = new QAction(_urlObject);; | |||
526 | ||||
527 | Q_ASSERT( kind == StandardUrl || kind == Email )((!(kind == StandardUrl || kind == Email)) ? qt_assert("kind == StandardUrl || kind == Email" ,"qterminal/libqterminal/unix/Filter.cpp",527) : qt_noop()); | |||
528 | ||||
529 | if ( kind == StandardUrl ) | |||
530 | { | |||
531 | openAction->setText(("Open Link")); | |||
532 | copyAction->setText(("Copy Link Address")); | |||
533 | } | |||
534 | else if ( kind == Email ) | |||
535 | { | |||
536 | openAction->setText(("Send Email To...")); | |||
537 | copyAction->setText(("Copy Email Address")); | |||
538 | } | |||
539 | ||||
540 | // object names are set here so that the hotspot performs the | |||
541 | // correct action when activated() is called with the triggered | |||
542 | // action passed as a parameter. | |||
543 | openAction->setObjectName("open-action"); | |||
544 | copyAction->setObjectName("copy-action"); | |||
545 | ||||
546 | QObject::connect( openAction , SIGNAL(triggered())qFlagLocation("2""triggered()" "\0" "qterminal/libqterminal/unix/Filter.cpp" ":" "546") , _urlObject , SLOT(activated())qFlagLocation("1""activated()" "\0" "qterminal/libqterminal/unix/Filter.cpp" ":" "546") ); | |||
547 | QObject::connect( copyAction , SIGNAL(triggered())qFlagLocation("2""triggered()" "\0" "qterminal/libqterminal/unix/Filter.cpp" ":" "547") , _urlObject , SLOT(activated())qFlagLocation("1""activated()" "\0" "qterminal/libqterminal/unix/Filter.cpp" ":" "547") ); | |||
548 | ||||
549 | list << openAction; | |||
550 | list << copyAction; | |||
551 | ||||
552 | return list; | |||
553 | } | |||
554 | ||||
555 | //#include "moc_Filter.cpp" |