2010-12-08 14:37:24 +00:00
|
|
|
/**
|
|
|
|
* customtext.cpp
|
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
|
|
*
|
|
|
|
* Custom text edit objects
|
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2014-02-05 11:42:17 +00:00
|
|
|
* Copyright (C) 2010-2014 Null Team
|
2010-12-08 14:37:24 +00:00
|
|
|
*
|
2013-08-06 13:38:10 +00:00
|
|
|
* This software is distributed under multiple licenses;
|
|
|
|
* see the COPYING file in the main directory for licensing
|
|
|
|
* information for this specific distribution.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
2010-12-08 14:37:24 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-08-06 13:38:10 +00:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2010-12-08 14:37:24 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "customtext.h"
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
namespace { // anonymous
|
|
|
|
|
|
|
|
// The factory
|
|
|
|
class CustomTextFactory : public UIFactory
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline CustomTextFactory(const char* name = "CustomFactory")
|
|
|
|
: UIFactory(name)
|
|
|
|
{ m_types.append(new String("CustomTextEdit")); }
|
|
|
|
virtual void* create(const String& type, const char* name, NamedList* params = 0);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Scroll an area to the end if has a vertical scroll bar
|
|
|
|
class ScrollToEnd
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
inline ScrollToEnd(QAbstractScrollArea* area)
|
|
|
|
: m_area(area)
|
|
|
|
{}
|
|
|
|
inline ~ScrollToEnd() {
|
|
|
|
QScrollBar* bar = m_area ? m_area->verticalScrollBar() : 0;
|
|
|
|
if (bar)
|
|
|
|
bar->setSliderPosition(bar->maximum());
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
QAbstractScrollArea* m_area;
|
|
|
|
};
|
|
|
|
|
|
|
|
static CustomTextFactory s_factory;
|
|
|
|
// Global list of URL handlers
|
|
|
|
static NamedList s_urlHandlers("");
|
|
|
|
|
|
|
|
|
|
|
|
// Check if a char is a word break one (including NULL)
|
|
|
|
static inline bool isWordBreak(char c)
|
|
|
|
{
|
|
|
|
return (c == ' ' || c == '\t' || c == '\r' || c == '\n' || !c);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if a char should be ignored from URL end (including NULL)
|
|
|
|
static inline bool isIgnoreUrlEnd(char c)
|
|
|
|
{
|
|
|
|
return (c == '.' || c == ';' || c == ':' || c == '?' || c == '!');
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move a cursor at document start/end.
|
|
|
|
// Adjust position by 'blocks' count
|
|
|
|
// Select if required and blocks is not 0
|
|
|
|
static void moveCursor(QTextCursor& c, bool atStart, int blocks = 0,
|
|
|
|
bool select = false)
|
|
|
|
{
|
|
|
|
c.movePosition(!atStart ? QTextCursor::End : QTextCursor::Start);
|
|
|
|
if (!blocks)
|
|
|
|
return;
|
|
|
|
c.movePosition(!atStart ? QTextCursor::PreviousBlock : QTextCursor::NextBlock,
|
|
|
|
select ? QTextCursor::KeepAnchor : QTextCursor::MoveAnchor,
|
|
|
|
blocks > 0 ? blocks : -blocks);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* CustomTextFormat
|
|
|
|
*/
|
|
|
|
// Constructor. Build a Block type
|
|
|
|
CustomTextFormat::CustomTextFormat(const String& id, const char* color, const char* bgcolor)
|
|
|
|
: NamedString(id),
|
|
|
|
m_type(Block), m_blockFormat(0), m_charFormat(0)
|
|
|
|
{
|
|
|
|
m_blockFormat = new QTextBlockFormat;
|
|
|
|
if (bgcolor)
|
|
|
|
m_blockFormat->setBackground(QColor(bgcolor));
|
|
|
|
m_charFormat = new QTextCharFormat;
|
|
|
|
if (color)
|
|
|
|
m_charFormat->setForeground(QColor(color));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor. Build a Html/Plain type
|
|
|
|
CustomTextFormat::CustomTextFormat(const String& id, const char* value, bool html)
|
|
|
|
: NamedString(id,value),
|
|
|
|
m_type(html ? Html : Plain), m_blockFormat(0), m_charFormat(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CustomTextFormat::~CustomTextFormat()
|
|
|
|
{
|
|
|
|
if (m_blockFormat)
|
|
|
|
delete m_blockFormat;
|
|
|
|
if (m_charFormat)
|
|
|
|
delete m_charFormat;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add/insert text into an edit widget
|
|
|
|
int CustomTextFormat::insertText(QTextEdit* edit, const String& text, bool atStart, int blocks)
|
|
|
|
{
|
|
|
|
QTextDocument* doc = edit ? edit->document() : 0;
|
|
|
|
if (!doc)
|
|
|
|
return 0;
|
|
|
|
QTextCursor c(doc);
|
|
|
|
moveCursor(c,atStart,blocks,false);
|
|
|
|
int oldBlocks = doc->blockCount();
|
|
|
|
c.insertBlock();
|
|
|
|
c.movePosition(QTextCursor::PreviousBlock,QTextCursor::MoveAnchor);
|
|
|
|
// Insert text
|
|
|
|
if (type() == Html)
|
|
|
|
c.insertHtml(QtClient::setUtf8(text));
|
|
|
|
else {
|
|
|
|
if (m_blockFormat)
|
|
|
|
c.setBlockFormat(*m_blockFormat);
|
|
|
|
if (m_charFormat)
|
|
|
|
c.setCharFormat(*m_charFormat);
|
|
|
|
c.insertText(QtClient::setUtf8(text));
|
|
|
|
}
|
|
|
|
return doc->blockCount() - oldBlocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set text from value. Replace text parameters if not empty
|
|
|
|
void CustomTextFormat::buildText(String& text, const NamedList* params,
|
2010-12-16 10:51:47 +00:00
|
|
|
CustomTextEdit* owner, bool lineBrBefore)
|
2010-12-08 14:37:24 +00:00
|
|
|
{
|
|
|
|
if (null())
|
|
|
|
return;
|
2010-12-16 10:51:47 +00:00
|
|
|
if (lineBrBefore)
|
|
|
|
text = ((type() == Html) ? "<br>" : "\r\n");
|
|
|
|
text << *this;
|
2010-12-08 14:37:24 +00:00
|
|
|
NamedList dummy("");
|
|
|
|
const NamedList* repl = &dummy;
|
|
|
|
if (params) {
|
|
|
|
// Escape or replace HTML markups.
|
|
|
|
// Make a copy of the input list if we are going to change it
|
|
|
|
if (type() == Html) {
|
|
|
|
dummy = *params;
|
|
|
|
unsigned int n = dummy.length();
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
String* s = dummy.getParam(i);
|
|
|
|
if (!TelEngine::null(s)) {
|
2011-04-27 12:09:19 +00:00
|
|
|
Client::plain2html(*s);
|
2010-12-08 14:37:24 +00:00
|
|
|
if (owner)
|
|
|
|
owner->replace(*s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
repl = params;
|
|
|
|
}
|
|
|
|
repl->replaceParams(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-16 10:51:47 +00:00
|
|
|
/*
|
|
|
|
* TextFragmentList
|
|
|
|
*/
|
|
|
|
// Restore this list in the document
|
|
|
|
void TextFragmentList::restore(QTextDocument* doc)
|
|
|
|
{
|
|
|
|
if (doc) {
|
|
|
|
for (int i = 0; i < m_list.size(); i++) {
|
|
|
|
QTextCursor c(doc);
|
|
|
|
c.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,
|
|
|
|
m_list[i].m_docPos);
|
|
|
|
c.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor,
|
|
|
|
m_list[i].toPlainText().length());
|
|
|
|
c.removeSelectedText();
|
|
|
|
c.insertHtml(m_list[i].toHtml());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_list.clear();
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2010-12-08 14:37:24 +00:00
|
|
|
/*
|
|
|
|
* CustomTextEdit
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
CustomTextEdit::CustomTextEdit(const char* name, const NamedList& params, QWidget* parent)
|
|
|
|
: QtCustomWidget(name,parent),
|
|
|
|
m_edit(0),
|
|
|
|
m_debug(false),
|
|
|
|
m_items(""),
|
|
|
|
m_defItem(String::empty(),"",false),
|
|
|
|
m_followUrl(true),
|
|
|
|
m_urlHandlers(""),
|
|
|
|
m_tempItemCount(0),
|
2010-12-16 10:51:47 +00:00
|
|
|
m_tempItemReplace(true),
|
|
|
|
m_lastFoundPos(-1)
|
2010-12-08 14:37:24 +00:00
|
|
|
{
|
|
|
|
// Build properties
|
|
|
|
QtClient::buildProps(this,params["buildprops"]);
|
|
|
|
m_edit = new QTextBrowser(this);
|
|
|
|
m_edit->setObjectName(params.getValue("textedit_name",this->name() + "_textedit"));
|
|
|
|
m_edit->setOpenLinks(false);
|
|
|
|
m_edit->setOpenExternalLinks(false);
|
|
|
|
m_edit->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
|
|
|
|
QtClient::setWidget(this,m_edit);
|
2010-12-16 10:51:47 +00:00
|
|
|
m_searchFoundFormat.setBackground(QBrush(QColor("darkgreen")));
|
|
|
|
m_searchFoundFormat.setForeground(QBrush(QColor("white")));
|
2010-12-08 14:37:24 +00:00
|
|
|
m_debug = params.getBoolValue("_yate_debug_widget");
|
|
|
|
if (m_debug) {
|
|
|
|
m_items.addParam(new CustomTextFormat(String(-1),"white")); // Output() or client set status
|
|
|
|
m_items.addParam(new CustomTextFormat(String(0),"yellow","red")); // DebugFail - blinking yellow on red
|
|
|
|
m_items.addParam(new CustomTextFormat(String(1),"yellow","red")); // Unnamed - yellow on red
|
2017-08-01 13:08:23 +00:00
|
|
|
m_items.addParam(new CustomTextFormat(String(2),"white","red")); // DebugCrit - white on red
|
2010-12-08 14:37:24 +00:00
|
|
|
m_items.addParam(new CustomTextFormat(String(3),"lightgrey","red")); // DebugConf - gray on red
|
|
|
|
m_items.addParam(new CustomTextFormat(String(4),"red")); // DebugStub - red on black
|
|
|
|
m_items.addParam(new CustomTextFormat(String(5),"orangered")); // DebugWarn - light red on black
|
|
|
|
m_items.addParam(new CustomTextFormat(String(6),"yellow")); // DebugMild - yellow on black
|
2017-08-01 13:08:23 +00:00
|
|
|
m_items.addParam(new CustomTextFormat(String(7),"lightgreen")); // DebugNote - light green on black
|
|
|
|
m_items.addParam(new CustomTextFormat(String(8),"white")); // DebugCall - white on black
|
2010-12-08 14:37:24 +00:00
|
|
|
m_items.addParam(new CustomTextFormat(String(9),"cyan")); // DebugInfo - light cyan on black
|
|
|
|
m_items.addParam(new CustomTextFormat(String(10),"teal")); // DebugAll - cyan on black
|
|
|
|
}
|
|
|
|
setParams(params);
|
|
|
|
// Connect signals
|
|
|
|
QtClient::connectObjects(m_edit,SIGNAL(anchorClicked(const QUrl&)),this,SLOT(urlTrigerred(const QUrl&)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set parameters
|
|
|
|
bool CustomTextEdit::setParams(const NamedList& params)
|
|
|
|
{
|
|
|
|
static const String s_setRichItem = "set_richtext_item";
|
|
|
|
static const String s_setPlainItem = "set_plaintext_item";
|
2010-12-16 10:51:47 +00:00
|
|
|
static const String s_search = "search";
|
2010-12-08 14:37:24 +00:00
|
|
|
unsigned int n = params.length();
|
|
|
|
bool ok = true;
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = params.getParam(i);
|
|
|
|
if (!(ns && ns->name()))
|
|
|
|
continue;
|
|
|
|
if (ns->name() == s_setRichItem)
|
|
|
|
setItem(*ns,true);
|
|
|
|
else if (ns->name() == s_setPlainItem)
|
|
|
|
setItem(*ns,false);
|
2010-12-16 10:51:47 +00:00
|
|
|
else if (ns->name() == s_search)
|
|
|
|
ok = setSearchHighlight(ns->toBoolean(),YOBJECT(NamedList,ns)) && ok;
|
2010-12-08 14:37:24 +00:00
|
|
|
else {
|
|
|
|
// Prefixed parameters
|
|
|
|
String tmp(ns->name());
|
|
|
|
if (tmp.startSkip("set_url_handler:",false)) {
|
|
|
|
// Set handler from prefix[{scheme}]=formatting_template
|
|
|
|
if (!tmp)
|
|
|
|
continue;
|
|
|
|
if (!m_urlHandlers.c_str())
|
|
|
|
m_urlHandlers.assign(s_urlHandlers.c_str());
|
|
|
|
// Check for optional scheme
|
|
|
|
int pos = tmp.find('{');
|
|
|
|
if (pos <= 0 || tmp[tmp.length() - 1] != '}')
|
|
|
|
m_urlHandlers.setParam(new CustomTextEditUrl(tmp,*ns));
|
|
|
|
else
|
|
|
|
m_urlHandlers.setParam(new CustomTextEditUrl(tmp.substr(0,pos),*ns,
|
|
|
|
tmp.substr(pos + 1,tmp.length() - pos - 2)));
|
|
|
|
}
|
|
|
|
else if (tmp.startSkip("property:",false)) {
|
|
|
|
QObject* target = m_edit;
|
|
|
|
if (tmp.startSkip(name() + ":",false))
|
|
|
|
target = this;
|
|
|
|
if (!QtClient::setProperty(target,tmp,*ns))
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append or insert text lines to this widget
|
|
|
|
bool CustomTextEdit::addLines(const NamedList& lines, unsigned int max, bool atStart)
|
|
|
|
{
|
|
|
|
unsigned int n = lines.length();
|
|
|
|
if (!n)
|
|
|
|
return true;
|
|
|
|
ScrollToEnd scroll(m_edit);
|
|
|
|
// Remove the temporary item(s)
|
|
|
|
if (m_tempItemCount && m_tempItemReplace) {
|
|
|
|
removeBlocks(m_tempItemCount);
|
|
|
|
m_tempItemCount = 0;
|
|
|
|
}
|
|
|
|
if (!m_debug) {
|
|
|
|
String text;
|
|
|
|
CustomTextFormat* last = 0;
|
|
|
|
// Line format: item=
|
|
|
|
// Each parameter may contain an optional list of parameters to be replaced in item
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = lines.getParam(i);
|
|
|
|
if (!ns)
|
|
|
|
continue;
|
|
|
|
CustomTextFormat* crt = find(ns->name());
|
|
|
|
if (!crt)
|
|
|
|
crt = &m_defItem;
|
|
|
|
if (last && last->type() != crt->type() && text) {
|
|
|
|
// Format changed: insert text now and reset it
|
|
|
|
insert(*last,text,atStart);
|
|
|
|
text.clear();
|
|
|
|
}
|
|
|
|
last = crt;
|
|
|
|
if (last != &m_defItem) {
|
|
|
|
String tmp;
|
2010-12-16 10:51:47 +00:00
|
|
|
last->buildText(tmp,YOBJECT(NamedList,ns),this,!text.null());
|
2010-12-08 14:37:24 +00:00
|
|
|
text << tmp;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
text << ns->name();
|
|
|
|
}
|
|
|
|
if (last && text)
|
|
|
|
insert(*last,text,atStart);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Handle 'max'
|
|
|
|
QTextDocument* doc = m_edit->document();
|
|
|
|
if (doc)
|
|
|
|
doc->setMaximumBlockCount((int)max);
|
|
|
|
// Line format: text=debuglevel
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = lines.getParam(i);
|
|
|
|
if (!ns)
|
|
|
|
continue;
|
|
|
|
CustomTextFormat* f = find(*ns);
|
|
|
|
// Use default output if not found
|
|
|
|
if (!f)
|
|
|
|
f = find(String("-1"));
|
|
|
|
if (f) {
|
|
|
|
// Ignore CR, LF or CR/LF at text end: we are adding a block
|
|
|
|
unsigned int n = 0;
|
|
|
|
if (ns->name().endsWith("\r\n"))
|
|
|
|
n = 2;
|
|
|
|
else if (ns->name().length()) {
|
|
|
|
int pos = ns->name().length() - 1;
|
|
|
|
if (ns->name()[pos] == '\r' || ns->name()[pos] == '\n')
|
|
|
|
n = 1;
|
|
|
|
}
|
|
|
|
if (n)
|
|
|
|
insert(*f,ns->name().substr(0,ns->name().length() - n),atStart);
|
|
|
|
else
|
|
|
|
insert(*f,ns->name(),atStart);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the displayed text of this widget
|
|
|
|
bool CustomTextEdit::setText(const String& text, bool richText)
|
|
|
|
{
|
|
|
|
ScrollToEnd scroll(m_edit);
|
|
|
|
m_edit->clear();
|
|
|
|
if (richText)
|
|
|
|
m_edit->insertHtml(QtClient::setUtf8(text));
|
|
|
|
else
|
|
|
|
m_edit->insertPlainText(QtClient::setUtf8(text));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the displayed text of this widget
|
|
|
|
bool CustomTextEdit::getText(String& text, bool richText)
|
|
|
|
{
|
|
|
|
if (richText)
|
|
|
|
QtClient::getUtf8(text,m_edit->toHtml());
|
|
|
|
else
|
|
|
|
QtClient::getUtf8(text,m_edit->toPlainText());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add/change/clear a pre-formatted item (item must be name[:[value])
|
|
|
|
void CustomTextEdit::setItem(const String& value, bool html)
|
|
|
|
{
|
|
|
|
if (!value)
|
|
|
|
return;
|
|
|
|
int pos = value.find(':');
|
|
|
|
if (pos > 0 && pos != (int)value.length() - 1) {
|
|
|
|
String id = value.substr(0,pos);
|
|
|
|
String val = value.substr(pos + 1);
|
|
|
|
CustomTextFormat* f = find(id);
|
|
|
|
// Remove existing if format changes
|
|
|
|
if (f && ((html && f->type() != CustomTextFormat::Html) ||
|
|
|
|
(!html && f->type() == CustomTextFormat::Plain))) {
|
|
|
|
m_items.clearParam(f);
|
|
|
|
f = 0;
|
|
|
|
}
|
|
|
|
if (!f)
|
|
|
|
m_items.addParam(new CustomTextFormat(id,val,html));
|
|
|
|
else
|
|
|
|
f->assign(val);
|
|
|
|
}
|
|
|
|
else if (pos < 0)
|
|
|
|
m_items.clearParam(value);
|
|
|
|
else if (pos > 0)
|
|
|
|
m_items.clearParam(value.substr(0,pos));
|
|
|
|
}
|
|
|
|
|
2010-12-16 10:51:47 +00:00
|
|
|
// Set/reset text highlight
|
|
|
|
bool CustomTextEdit::setSearchHighlight(bool on, NamedList* params)
|
|
|
|
{
|
|
|
|
if (!on) {
|
|
|
|
m_lastFoundPos = -1;
|
|
|
|
if (params && params->getBoolValue("reset",true))
|
|
|
|
m_searchFound.restore(m_edit->document());
|
|
|
|
else
|
|
|
|
m_searchFound.m_list.clear();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!params)
|
|
|
|
return false;
|
|
|
|
QTextDocument* doc = m_edit->document();
|
|
|
|
if (!doc)
|
|
|
|
return false;
|
|
|
|
QString find = QtClient::setUtf8(params->getValue("find"));
|
|
|
|
if (!find.length())
|
|
|
|
return false;
|
|
|
|
Qt::CaseSensitivity cs = params->getBoolValue("matchcase") ?
|
|
|
|
Qt::CaseSensitive : Qt::CaseInsensitive;
|
|
|
|
bool found = false;
|
|
|
|
QString text = doc->toPlainText();
|
|
|
|
if (params->getBoolValue("all")) {
|
|
|
|
m_lastFoundPos = -1;
|
|
|
|
m_searchFound.restore(doc);
|
|
|
|
int pos = -1;
|
|
|
|
do {
|
|
|
|
pos = text.indexOf(find,pos + 1,cs);
|
|
|
|
if (pos >= 0)
|
|
|
|
handleFound(pos,find.length());
|
|
|
|
}
|
|
|
|
while (pos >= 0);
|
|
|
|
if (m_searchFound.m_list.size()) {
|
|
|
|
found = true;
|
|
|
|
ensureCharVisible(m_searchFound.m_list[0].m_docPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (params->getBoolValue("next"))
|
|
|
|
m_lastFoundPos = text.indexOf(find,m_lastFoundPos >= 0 ? m_lastFoundPos + 1 : 0,cs);
|
|
|
|
else if (m_lastFoundPos < 0)
|
|
|
|
m_lastFoundPos = text.lastIndexOf(find,-1,cs);
|
|
|
|
else if (m_lastFoundPos)
|
|
|
|
m_lastFoundPos = text.lastIndexOf(find,m_lastFoundPos - 1,cs);
|
|
|
|
if (m_lastFoundPos >= 0) {
|
|
|
|
found = true;
|
|
|
|
m_searchFound.restore(doc);
|
|
|
|
handleFound(m_lastFoundPos,find.length());
|
|
|
|
ensureCharVisible(m_lastFoundPos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the character at a given position is visible
|
|
|
|
void CustomTextEdit::ensureCharVisible(int pos)
|
|
|
|
{
|
|
|
|
QTextCursor show(m_edit->document());
|
|
|
|
show.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,pos);
|
|
|
|
m_edit->setTextCursor(show);
|
|
|
|
m_edit->ensureCursorVisible();
|
|
|
|
}
|
|
|
|
|
2010-12-08 14:37:24 +00:00
|
|
|
// Replace string sequences with formatted text
|
|
|
|
void CustomTextEdit::replace(String& text)
|
|
|
|
{
|
|
|
|
if (!text)
|
|
|
|
return;
|
|
|
|
// Replace URLs ?
|
|
|
|
if (m_followUrl) {
|
|
|
|
const NamedList& urls = m_urlHandlers.c_str() ? m_urlHandlers : s_urlHandlers;
|
|
|
|
unsigned int n = urls.length();
|
|
|
|
for (int start = 0; start < (int)text.length();) {
|
|
|
|
int len = 1;
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
const CustomTextEditUrl* ns = static_cast<CustomTextEditUrl*>(urls.getParam(i));
|
|
|
|
// Parameter name is the URL prefix
|
|
|
|
if (!(ns && ns->name()))
|
|
|
|
continue;
|
|
|
|
if (ns->name().length() >= text.length() - start)
|
|
|
|
continue;
|
|
|
|
// Get html template from parameter value or list name
|
|
|
|
const char* templ = *ns ? ns->c_str() : urls.c_str();
|
|
|
|
if (TelEngine::null(templ))
|
|
|
|
continue;
|
|
|
|
// Check for prefix match
|
|
|
|
if (::strncmp(text.c_str() + start,ns->name().c_str(),ns->name().length()))
|
|
|
|
continue;
|
|
|
|
// Detect url end
|
|
|
|
int end = start + (int)ns->name().length();
|
|
|
|
while (!isWordBreak(text[end]))
|
|
|
|
end++;
|
|
|
|
// Go back 1 char if the last one should be ignored
|
|
|
|
if ((end > start + (int)ns->name().length()) && isIgnoreUrlEnd(text[end - 1]))
|
|
|
|
end--;
|
|
|
|
len = end - start;
|
|
|
|
// Replace the URL if have something after prefix
|
|
|
|
if (len <= (int)ns->name().length()) {
|
|
|
|
len++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Check if we have a scheme to prepend for this one
|
|
|
|
String url = text.substr(start,len);
|
|
|
|
NamedList p("");
|
|
|
|
p.addParam("url-display",url);
|
|
|
|
p.addParam("url",ns->m_scheme ? (ns->m_scheme + url) : url);
|
|
|
|
String u = templ;
|
|
|
|
p.replaceParams(u);
|
|
|
|
text = text.substr(0,start) + u + text.substr(end);
|
|
|
|
len = (int)u.length();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
start += len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Insert text using a given format. Update temporary item length if appropriate
|
|
|
|
void CustomTextEdit::insert(CustomTextFormat& fmt, const String& text, bool atStart)
|
|
|
|
{
|
|
|
|
int n = fmt.insertText(m_edit,text,atStart,m_tempItemReplace ? 0 : m_tempItemCount);
|
|
|
|
if (m_tempItemName != fmt.toString()) {
|
|
|
|
// Reset counter if temporary item was replaced
|
|
|
|
if (m_tempItemReplace)
|
|
|
|
m_tempItemCount = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_tempItemCount = !atStart ? n : -n;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove blocks from edit widget
|
|
|
|
void CustomTextEdit::removeBlocks(int blocks)
|
|
|
|
{
|
|
|
|
if (!blocks)
|
|
|
|
return;
|
|
|
|
QTextDocument* doc = m_edit->document();
|
|
|
|
if (!doc)
|
|
|
|
return;
|
|
|
|
QTextCursor c(doc);
|
|
|
|
moveCursor(c,blocks < 0,blocks,true);
|
|
|
|
c.removeSelectedText();
|
|
|
|
}
|
|
|
|
|
|
|
|
// URL clicked notification
|
|
|
|
void CustomTextEdit::urlTrigerred(const QUrl& url)
|
|
|
|
{
|
|
|
|
if (!(m_followUrl && Client::valid()))
|
|
|
|
return;
|
|
|
|
String tmp;
|
|
|
|
QtClient::getUtf8(tmp,url.toString());
|
|
|
|
XDebug(ClientDriver::self(),DebugAll,"CustomTextEdit(%s)::urlTrigerred(%s)",
|
|
|
|
name().c_str(),tmp.c_str());
|
|
|
|
Client::self()->openUrl(tmp);
|
|
|
|
}
|
|
|
|
|
2010-12-16 10:51:47 +00:00
|
|
|
// Handle found item. Add data to found items. Set formatting
|
|
|
|
void CustomTextEdit::handleFound(int pos, int len)
|
|
|
|
{
|
|
|
|
QTextCursor c(m_edit->document());
|
|
|
|
c.movePosition(QTextCursor::NextCharacter,QTextCursor::MoveAnchor,pos);
|
|
|
|
c.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor,len);
|
|
|
|
m_searchFound.add(c);
|
|
|
|
QString sel = c.selectedText();
|
|
|
|
c.removeSelectedText();
|
|
|
|
c.insertText(sel,m_searchFoundFormat);
|
|
|
|
}
|
|
|
|
|
2010-12-08 14:37:24 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* CustomTextFactory
|
|
|
|
*/
|
|
|
|
// Build objects
|
|
|
|
void* CustomTextFactory::create(const String& type, const char* name, NamedList* params)
|
|
|
|
{
|
|
|
|
// Init URL handlers
|
|
|
|
if (!s_urlHandlers.c_str()) {
|
|
|
|
s_urlHandlers.assign("<a href=\"${url}\"><span style=\"text-decoration: underline; color:#0000ff;\">${url-display}</span></a>");
|
|
|
|
s_urlHandlers.addParam(new CustomTextEditUrl("http://"));
|
|
|
|
s_urlHandlers.addParam(new CustomTextEditUrl("https://"));
|
|
|
|
s_urlHandlers.addParam(new CustomTextEditUrl("www.","","http://"));
|
|
|
|
}
|
|
|
|
if (!params)
|
|
|
|
return 0;
|
|
|
|
QWidget* parentWidget = 0;
|
|
|
|
String* wndname = params->getParam("parentwindow");
|
|
|
|
if (!TelEngine::null(wndname)) {
|
|
|
|
String* wName = params->getParam("parentwidget");
|
|
|
|
QtWindow* wnd = static_cast<QtWindow*>(Client::self()->getWindow(*wndname));
|
|
|
|
if (wnd && !TelEngine::null(wName))
|
|
|
|
parentWidget = qFindChild<QWidget*>(wnd,QtClient::setUtf8(*wName));
|
|
|
|
}
|
|
|
|
if (type == "CustomTextEdit")
|
|
|
|
return new CustomTextEdit(name,*params,parentWidget);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
}; // anonymous namespace
|
|
|
|
|
|
|
|
#include "customtext.moc"
|
|
|
|
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|