diff --git a/retroshare-gui/src/gui/common/Emoticons.cpp b/retroshare-gui/src/gui/common/Emoticons.cpp index 3894c9deb..6bc605168 100644 --- a/retroshare-gui/src/gui/common/Emoticons.cpp +++ b/retroshare-gui/src/gui/common/Emoticons.cpp @@ -15,198 +15,247 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. ****************************************************************/ -#include -#include #include -#include -#include -#include #include +#include +#include +#include +#include #include +#include +#include #include #include + #include "Emoticons.h" #include "util/HandleRichText.h" -static QHash Smileys; -static QVector order; +static QHash, QHash > > Smileys; +static QVector grpOrdered; void Emoticons::load() { - QString sm_codes; - bool internalEmoticons = true; + QString sm_AllLines; + bool internalFiles = true; -#if defined(Q_OS_WIN) - // first try external emoticons - QFile sm_file(QApplication::applicationDirPath() + "/emoticons/emotes.acs"); - if(sm_file.open(QIODevice::ReadOnly)) - { - internalEmoticons = false; - } else { - // then embedded emotions - sm_file.setFileName(":/emojione/emotes.acs"); - if(!sm_file.open(QIODevice::ReadOnly)) - { - std::cout << "error opening ressource file" << std::endl ; - return ; - } - } -#else - QFile sm_file(QString(":/emojione/emotes.acs")); - if(!sm_file.open(QIODevice::ReadOnly)) - { - std::cout << "error opening ressource file" << std::endl ; - return ; - } -#endif + // First try external emoticons + QFile sm_File(QApplication::applicationDirPath() + "/emoticons/emotes.acs"); + if(sm_File.open(QIODevice::ReadOnly)) + internalFiles = false; + else + { + // Then embedded emotions + sm_File.setFileName(":/emojione/emotes.acs"); + if(!sm_File.open(QIODevice::ReadOnly)) + { + std::cout << "error opening ressource file" << std::endl; + return; + } + } - sm_codes = sm_file.readAll(); - sm_file.close(); + sm_AllLines = sm_File.readAll(); + sm_File.close(); - sm_codes.remove("\n"); - sm_codes.remove("\r"); + sm_AllLines.remove("\n"); + sm_AllLines.remove("\r"); - int i = 0; - QString smcode; - QString smfile; - while(i < sm_codes.length() && sm_codes[i] != '{') - { - ++i; - } - while (i < sm_codes.length()-2) - { - smcode = ""; - smfile = ""; + int i = 0 ; + QString smGroup; + QString smCode; + QString smFile; + while(i < sm_AllLines.length() && sm_AllLines[i] != '{') + ++i ;//Ignore text before { - while(sm_codes[i] != '\"') - { - ++i; - } - ++i; - while (sm_codes[i] != '\"') - { - smcode += sm_codes[i]; - ++i; - } - ++i; + while (i < sm_AllLines.length()-2) + { + // Type of lines: + // "Group"|":code:":"emojione/file.png"; + smGroup = ""; + smCode = ""; + smFile = ""; - while(sm_codes[i] != '\"') - { - ++i; - } - ++i; - while(sm_codes[i] != '\"' && sm_codes[i+1] != ';') - { - smfile += sm_codes[i]; - ++i; - } - ++i; - if(!smcode.isEmpty() && !smfile.isEmpty()) { - while (smcode.right(1) == "|") { - smcode.remove(smcode.length() - 1, 1); - } - - if (internalEmoticons) { - Smileys.insert(smcode, ":/"+smfile); - } else { - Smileys.insert(smcode, smfile); - } - order.append(smcode); - } - } + //Reading Groupe (First into "") + while(sm_AllLines[i] != '\"') + ++i; //Ignore text outside "" - // init embedder - RsHtml::initEmoticons(Smileys); + ++i; //'"' + while (sm_AllLines[i] != '\"') + smGroup += sm_AllLines[i++]; //Get group char by char + + ++i; //'"' + + if (sm_AllLines[i] == '|') + { + //File in new format with group + ++i; //'|' + //Reading Code (Second into "") + while(sm_AllLines[i] != '\"') + ++i; //Ignore text outside "" + + ++i; //'"' + while (sm_AllLines[i] != '\"') + smCode += sm_AllLines[i++]; //Get code char by char + + ++i; //'"' + } else { + //Old file without group + ++i; //':' + smCode = smGroup; + smGroup = "NULL"; + } + + //Reading File (Third into "") + while(sm_AllLines[i] != '\"') + ++i; //Ignore text outside "" + + ++i; //'"' + while(sm_AllLines[i] != '\"' && sm_AllLines[i+1] != ';') + smFile += sm_AllLines[i++]; //Get file char by char + + ++i; //'"' + ++i; //';' + + if(!smGroup.isEmpty() && !smCode.isEmpty() && !smFile.isEmpty()) + { + while (smCode.right(1) == "|") + smCode.remove(smCode.length() - 1, 1); + + if (internalFiles) + { + smFile = ":/" + smFile; + if (smGroup.right(4).toLower() == ".png") + smGroup = ":/" + smGroup; + } + + QVector ordered; + if (Smileys.contains(smGroup)) ordered = Smileys[smGroup].first; + ordered.append(smCode); + Smileys[smGroup].first = ordered; + Smileys[smGroup].second.insert(smCode, smFile); + + if (!grpOrdered.contains(smGroup)) grpOrdered.append(smGroup); + } + } + + // init embedder + RsHtml::initEmoticons(Smileys); } void Emoticons::showSmileyWidget(QWidget *parent, QWidget *button, const char *slotAddMethod, bool above) { - QWidget *smWidget = new QWidget(parent, Qt::Popup); + QWidget *smWidget = new QWidget(parent, Qt::Popup) ; + smWidget->setAttribute(Qt::WA_DeleteOnClose) ; + smWidget->setWindowTitle("Emoticons") ; - const int buttonWidth = 26; - const int buttonHeight = 26; + //QTabWidget::setTabBarAutoHide(true) is from QT5.4, no way to hide TabBar before. + bool bOnlyOneGroup = (Smileys.count() == 1); - int rowCount = (int)sqrt((double)Smileys.size()); - int countPerLine = (Smileys.size()/rowCount) + ((Smileys.size() % rowCount) ? 1 : 0); + QTabWidget *smTab = NULL; + if (! bOnlyOneGroup) + { + smTab = new QTabWidget(smWidget); + QGridLayout *smGLayout = new QGridLayout(smWidget); + smGLayout->setContentsMargins(0,0,0,0); + smGLayout->addWidget(smTab); + } - smWidget->setAttribute( Qt::WA_DeleteOnClose); - smWidget->setWindowTitle("Emoticons"); - smWidget->setBaseSize(countPerLine*buttonWidth, rowCount*buttonHeight); + const int buttonWidth = QFontMetricsF(smWidget->font()).height()*2; + const int buttonHeight = QFontMetricsF(smWidget->font()).height()*2; + int maxRowCount = 0; + int maxCountPerLine = 0; - //Warning: this part of code was taken from kadu instant messenger; - // It was EmoticonSelector::alignTo(QWidget* w) function there - // comments are Polish, I dont' know how does it work... - // oblicz pozycj� widgetu do kt�rego r�wnamy - // oblicz rozmiar selektora - QPoint pos = button->mapToGlobal(QPoint(0,0)); - QSize e_size = smWidget->sizeHint(); - // oblicz rozmiar pulpitu - QSize s_size = QApplication::desktop()->size(); - // oblicz dystanse od widgetu do lewego brzegu i do prawego - int l_dist = pos.x(); - int r_dist = s_size.width() - (pos.x() + button->width()); - // oblicz pozycj� w zale�no�ci od tego czy po lewej stronie - // jest wi�cej miejsca czy po prawej - int x; - if (l_dist >= r_dist) - x = pos.x() - e_size.width(); - else - x = pos.x() + button->width(); - // oblicz pozycj� y - centrujemy w pionie - int y = pos.y() + button->height()/2 - e_size.height()/2; - // je�li wychodzi poza doln� kraw�d� to r�wnamy do niej - if (y + e_size.height() > s_size.height()) - y = s_size.height() - e_size.height(); + QVectorIterator grp(grpOrdered); + while(grp.hasNext()) + { + QString groupName = grp.next(); + QHash group = Smileys.value(groupName).second; - if (above) { - y -= rowCount*buttonHeight; - } + QWidget *tabGrpWidget = NULL; + if (! bOnlyOneGroup) + { + tabGrpWidget = new QWidget(smTab); + if (groupName.right(4).toLower() == ".png") + smTab->addTab( tabGrpWidget, QIcon(groupName), ""); + else + smTab->addTab( tabGrpWidget, groupName); + } else { + tabGrpWidget = smWidget; + } - // je�li wychodzi poza g�rn� kraw�d� to r�wnamy do niej - if (y < 0) - y = 0; - // ustawiamy selektor na wyliczonej pozycji - smWidget->move(x, y); + QGridLayout *tabGLayout = new QGridLayout(tabGrpWidget); + tabGLayout->setContentsMargins(0,0,0,0); + tabGLayout->setSpacing(0); - x = 0; - y = 0; + int rowCount = (int)sqrt((double)group.size()); + int countPerLine = (group.size()/rowCount) + ((group.size() % rowCount) ? 1 : 0); + maxRowCount = qMax(maxRowCount, rowCount); + maxCountPerLine = qMax(maxCountPerLine, countPerLine); - QVectorIterator i(order); - while(i.hasNext()) - { - QString key = i.next(); - QPushButton *smButton = new QPushButton("", smWidget); - smButton->setGeometry(x*buttonWidth, y*buttonHeight, buttonWidth, buttonHeight); - smButton->setIconSize(QSize(buttonWidth, buttonHeight)); - smButton->setIcon(QPixmap(Smileys.value(key))); - smButton->setToolTip(key); - smButton->setStyleSheet("QPushButton:hover {border: 3px solid white; border-radius: 2px;}"); - smButton->setFlat(true); - ++x; - if(x >= countPerLine) - { - x = 0; - ++y; - } - QObject::connect(smButton, SIGNAL(clicked()), parent, slotAddMethod); - QObject::connect(smButton, SIGNAL(clicked()), smWidget, SLOT(close())); - } + int lin = 0; + int col = 0; + QVector ordered = Smileys.value(groupName).first; + QVectorIterator it(ordered); + while(it.hasNext()) + { - smWidget->show(); + QString key = it.next(); + QPushButton *button = new QPushButton("", tabGrpWidget); + button->setIconSize(QSize(buttonWidth, buttonHeight)); + button->setFixedSize(QSize(buttonWidth, buttonHeight)); + button->setIcon(QPixmap(group.value(key))); + button->setToolTip(key); + button->setStyleSheet("QPushButton:hover {border: 3px solid white; border-radius: 2px;}"); + button->setFlat(true); + tabGLayout->addWidget(button,col,lin); + ++lin; + if(lin >= countPerLine) + { + lin = 0; + ++col; + } + QObject::connect(button, SIGNAL(clicked()), parent, slotAddMethod); + QObject::connect(button, SIGNAL(clicked()), smWidget, SLOT(close())); + } + + } + + //Get left up pos of button + QPoint butTopLeft = button->mapToGlobal(QPoint(0,0)); + //Get widget's size + QSize sizeWidget = smWidget->sizeHint(); + //Get screen's size + QSize sizeScreen = QApplication::desktop()->size(); + + //Calculate left distance to screen start + int distToScreenLeft = butTopLeft.x(); + //Calculate right distance to screen end + int distToRightScreen = sizeScreen.width() - (butTopLeft.x() + button->width()); + + //Calculate left position + int x; + if (distToScreenLeft >= distToRightScreen) //More distance in left than right in screen + x = butTopLeft.x() - sizeWidget.width(); //Place widget on left of button + else + x = butTopLeft.x() + button->width(); //Place widget on right of button + + //Calculate top position + int y; + if (above) //Widget must be above the button + y = butTopLeft.y() + button->height() - sizeWidget.height(); + else + y = butTopLeft.y() + button->height()/2 - sizeWidget.height()/2; //Centered on button height + + if (y + sizeWidget.height() > sizeScreen.height()) //Widget will be too low + y = sizeScreen.height() - sizeWidget.height(); //Place widget bottom at screen bottom + + if (y < 0) //Widget will be too high + y = 0; //Place widget top at screen top + + smWidget->move(x, y) ; + smWidget->show() ; } - -//void Emoticons::formatText(QString &text) -//{ -// QHashIterator i(Smileys); -// while(i.hasNext()) { -// i.next(); -// foreach (QString code, i.key().split("|")) { -// text.replace(code, ""); -// } -// } -//} diff --git a/retroshare-gui/src/gui/emojione/emotes.acs b/retroshare-gui/src/gui/emojione/emotes.acs index 3d4cadc8f..fd3cfb3db 100644 --- a/retroshare-gui/src/gui/emojione/emotes.acs +++ b/retroshare-gui/src/gui/emojione/emotes.acs @@ -1,201 +1,201 @@ { -":grinning:":"emojione/1F600.png"; -":grin:":"emojione/1F601.png"; -":joy:|:')|:'-)":"emojione/1F602.png"; -":smile:|:-DD|:DD":"emojione/1F604.png"; -":smiley:|:-D|:D":"emojione/1F603.png"; -":sweat_smile:|':)|':-)|'=)|':D|':-D|'=D":"emojione/1F605.png"; -":laughing:|:satisfied":"emojione/1F606.png"; -":innocent:|O:-)|0:-3|0:3|0:-)|0:)":"emojione/1F607.png"; -":smiling_imp:":"emojione/1F608.png"; -":imp:":"emojione/1F47F.png"; -":wink:|;-)|;)":"emojione/1F609.png"; -":blush:":"emojione/1F60A.png"; -":relaxed:":"emojione/263A.png"; -":yum:":"emojione/1F60B.png"; -":relieved:":"emojione/1F60C.png"; -":heart_eyes:":"emojione/1F60D.png"; -":sunglasses:|B-)|B)|8)|8-)|B-D|8-D":"emojione/1F60E.png"; -":smirk:":"emojione/1F60F.png"; -":neutral_face:":"emojione/1F610.png"; -":expressionless:|-_-":"emojione/1F611.png"; -":unamused:":"emojione/1F612.png"; -":sweat:|':(|':-(|'=(":"emojione/1F613.png"; -":pensive:":"emojione/1F614.png"; -":confused:|:-/|:-.|:\|=/|=\|:L|=L":"emojione/1F615.png"; -":confounded:":"emojione/1F616.png"; -":kissing:":"emojione/1F617.png"; -":kissing_heart:|:*|:-*|=*":"emojione/1F618.png"; -":kissing_smiling_eyes:":"emojione/1F619.png"; -":kissing_closed_eyes:":"emojione/1F61A.png"; -":stuck_out_tongue:|:-P|:P|:p|:-p":"emojione/1F61B.png"; -":stuck_out_tongue_winking_eye:":"emojione/1F61C.png"; -":stuck_out_tongue_closed_eyes:":"emojione/1F61D.png"; -":disappointed:|:-(|:(":"emojione/1F61E.png"; -":worried:":"emojione/1F61F.png"; -":angry:|:@":"emojione/1F620.png"; -":rage:":"emojione/1F621.png"; -":cry:|:'(|:'-(|;(|;-(":"emojione/1F622.png"; -":persevere:":"emojione/1F623.png"; -":triumph:":"emojione/1F624.png"; -":disappointed_relieved:":"emojione/1F625.png"; -":frowning:":"emojione/1F626.png"; -":anguished:":"emojione/1F627.png"; -":fearful:":"emojione/1F628.png"; -":weary:":"emojione/1F629.png"; -":sleepy:":"emojione/1F62A.png"; -":tired_face:":"emojione/1F62B.png"; -":grimacing:":"emojione/1F62C.png"; -":sob:":"emojione/1F62D.png"; -":open_mouth:|:-O|:O":"emojione/1F62E.png"; -":hushed:":"emojione/1F62F.png"; -":cold_sweat:":"emojione/1F630.png"; -":scream:":"emojione/1F631.png"; -":astonished:":"emojione/1F632.png"; -":flushed:|=$|:$|:-$":"emojione/1F633.png"; -":sleeping:":"emojione/1F634.png"; -":bulb:":"emojione/1F4A1.png"; -":zzz:":"emojione/1F4A4.png"; -":fire:|:flame:":"emojione/1F525.png"; -":stars:":"emojione/1F320.png"; -":star:":"emojione/2B50.png"; -":dizzy:":"emojione/1F4AB.png"; -":boom:":"emojione/1F4A5.png"; -":anger:":"emojione/1F4A2.png"; -":bomb:":"emojione/1F4A3.png"; -":sweat_drops:":"emojione/1F4A6.png"; -":droplet:":"emojione/1F4A7.png"; -":dash:":"emojione/1F4A8.png"; -":poop:|:shit:|:hankey:|:poo:":"emojione/1F4A9.png"; -":dizzy_face:|#-)|#)|%-)|%)|X)|X-)":"emojione/1F635.png"; -":no_mouth:|:-X|:X:|-#|:#|=X|=x|:x|:-x|=#":"emojione/1F636.png"; -":mask:":"emojione/1F637.png"; -":slight_frown:|:slightly_frowning_face:":"emojione/1F641.png"; -":slight_smile:|:)|:-)|:slightly_smiling_face:":"emojione/1F642.png"; -":smile_cat:|(@)":"emojione/1F638.png"; -":joy_cat:":"emojione/1F639.png"; -":smiley_cat:":"emojione/1F63A.png"; -":heart_eyes_cat:":"emojione/1F63B.png"; -":smirk_cat:":"emojione/1F63C.png"; -":kissing_cat:":"emojione/1F63D.png"; -":pouting_cat:":"emojione/1F63E.png"; -":crying_cat_face:":"emojione/1F63F.png"; -":scream_cat:":"emojione/1F640.png"; -":footprints:":"emojione/1F463.png"; -":bust_in_silhouette:":"emojione/1F464.png"; -":busts_in_silhouette:":"emojione/1F465.png"; -":levitate:|:man_in_business_suit_levitating:":"emojione/1F574.png"; -":spy:|:sleuth_or_spy:":"emojione/1F575.png"; -":baby:":"emojione/1F476.png"; -":boy:":"emojione/1F466.png"; -":girl:":"emojione/1F467.png"; -":man:":"emojione/1F468.png"; -":woman:":"emojione/1F469.png"; -":family:":"emojione/1F46A.png"; -":couple:":"emojione/1F46B.png"; -":dancers:":"emojione/1F46F.png"; -":bride_with_veil:":"emojione/1F470.png"; -":person_with_blond_hair:":"emojione/1F471.png"; -":man_with_gua_pi_mao:":"emojione/1F472.png"; -":man_with_turban:":"emojione/1F473.png"; -":older_man:":"emojione/1F474.png"; -":older_woman:":"emojione/1F475.png"; -":cop:":"emojione/1F46E.png"; -":construction_worker:":"emojione/1F477.png"; -":princess:":"emojione/1F478.png"; -":guardsman:":"emojione/1F482.png"; -":angel:":"emojione/1F47C.png"; -":santa:":"emojione/1F385.png"; -":ghost:":"emojione/1F47B.png"; -":skull:|:skeleton:":"emojione/1F480.png"; -":alien:":"emojione/1F47D.png"; -":space_invader:":"emojione/1F47E.png"; -":japanese_ogre:":"emojione/1F479.png"; -":japanese_goblin:":"emojione/1F47A.png"; -":bow:":"emojione/1F647.png"; -":information_desk_person:":"emojione/1F481.png"; -":raised_hands:":"emojione/1F64C.png"; -":clap:":"emojione/1F44F.png"; -":ear:":"emojione/1F442.png"; -":eye:":"emojione/1F441.png"; -":eyes:":"emojione/1F440.png"; -":nose:":"emojione/1F443.png"; -":lips:":"emojione/1F444.png"; -":lips2:":"emojione/1F5E2.png"; -":kiss:":"emojione/1F48B.png"; -":tongue:":"emojione/1F445.png"; -":nail_care:":"emojione/1F485.png"; -":wave:":"emojione/1F44B.png"; -":thumbsup:|:+1:|(y)|(Y)":"emojione/1F44D.png"; -":thumbsdown:|:-1:|(N)|(n)":"emojione/1F44E.png"; -":muscle:":"emojione/1F4AA.png"; -":middle_finger:|":"emojione/1F595.png"; -":finger_pointing_down:":"emojione/1F597.png"; -":finger_pointing_left:":"emojione/1F598.png"; -":finger_pointing_right:":"emojione/1F599.png"; -":finger_pointing_up:":"emojione/1F59E.png"; -":coffee:|(C)|(c)":"emojione/2615.png"; -":sake:":"emojione/1F376.png"; -":tea:":"emojione/1F375.png"; -":cake:":"emojione/1F370.png"; -":egg:":"emojione/1F373.png"; -":hamburger:":"emojione/1F354.png"; -":doughnut:":"emojione/1F369.png"; -":wine_glass:":"emojione/1F377.png"; -":cocktail:":"emojione/1F378.png"; -":tropical_drink:":"emojione/1F379.png"; -":beer:|(B)|(b)":"emojione/1F37A.png"; -":beers:":"emojione/1F37B.png"; -":watch:":"emojione/231A.png"; -":iphone:":"emojione/1F4F1.png"; -":monkey:":"emojione/1F412.png"; -":see_no_evil:":"emojione/1F648.png"; -":hear_no_evil:":"emojione/1F649.png"; -":speak_no_evil:":"emojione/1F64A.png"; -":turtle:":"emojione/1F422.png"; -":frog:":"emojione/1F438.png"; -":airplane:":"emojione/2708.png"; -":heart:|<3":"emojione/2764.png"; -":broken_heart:|& hash) +void RsHtml::initEmoticons(const QHash, QHash > >& hash) { QString newRE; - for(QHash::const_iterator it = hash.begin(); it != hash.end(); ++it) - foreach(QString smile, it.key().split("|")) { - if (smile.isEmpty()) { - continue; - } - defEmbedImg.smileys.insert(smile, it.value()); - // add space around smileys - newRE += "(?:^|\\s)(" + QRegExp::escape(smile) + ")(?:$|\\s)|"; - // explanations: - // (?:^|\s)(*smiley*)(?:$|\s) - // - // (?:^|\s) Non-capturing group - // 1st Alternative: ^ - // ^ assert position at start of the string - // 2nd Alternative: \s - // \s match any white space character [\r\n\t\f ] - // - // 1st Capturing group (*smiley*) - // *smiley* matches the characters *smiley* literally (case sensitive) - // - // (?:$|\s) Non-capturing group - // 1st Alternative: $ - // $ assert position at end of the string - // 2nd Alternative: \s - // \s match any white space character [\r\n\t\f ] + for(QHash, QHash > >::const_iterator groupit = hash.begin(); groupit != hash.end(); ++groupit) { + QHash group = groupit.value().second; + for(QHash::const_iterator it = group.begin(); it != group.end(); ++it) + foreach(QString smile, it.key().split("|")) { + if (smile.isEmpty()) { + continue; + } + defEmbedImg.smileys.insert(smile, it.value()); + // add space around smileys + newRE += "(?:^|\\s)(" + QRegExp::escape(smile) + ")(?:$|\\s)|"; + // explanations: + // (?:^|\s)(*smiley*)(?:$|\s) + // + // (?:^|\s) Non-capturing group + // 1st Alternative: ^ + // ^ assert position at start of the string + // 2nd Alternative: \s + // \s match any white space character [\r\n\t\f ] + // + // 1st Capturing group (*smiley*) + // *smiley* matches the characters *smiley* literally (case sensitive) + // + // (?:$|\s) Non-capturing group + // 1st Alternative: $ + // $ assert position at end of the string + // 2nd Alternative: \s + // \s match any white space character [\r\n\t\f ] - /* + /* * TODO * a better version is: * (?<=^|\s)(*smile*)(?=$|\s) using the lookbehind/lookahead operator instead of non-capturing groups. * This solves the problem that spaces are matched, too (see workaround in RsHtml::embedHtml) * This is not supported by Qt4! */ - } + } + } + newRE.chop(1); // remove last | defEmbedImg.myREs.append(QRegExp(newRE)); } diff --git a/retroshare-gui/src/util/HandleRichText.h b/retroshare-gui/src/util/HandleRichText.h index 46a25075a..2c1ab791a 100644 --- a/retroshare-gui/src/util/HandleRichText.h +++ b/retroshare-gui/src/util/HandleRichText.h @@ -60,7 +60,7 @@ class RsHtml public: RsHtml(); - static void initEmoticons(const QHash< QString, QString >& hash); + static void initEmoticons(const QHash, QHash > > &hash); QString formatText(QTextDocument *textDocument, const QString &text, ulong flag, const QColor &backgroundColor = Qt::white, qreal desiredContrast = 1.0, int desiredMinimumFontSize = 10); static bool findAnchors(const QString &text, QStringList& urls);