RetroShare/retroshare-gui/src/gui/chat/ChatStyle.cpp
2011-03-12 21:09:53 +00:00

516 lines
15 KiB
C++

/****************************************************************
*
* RetroShare is distributed under the following license:
*
* Copyright (C) 2006, Thunder
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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,
* Boston, MA 02110-1301, USA.
****************************************************************/
/* Directory tree for stylesheets:
Install version: <RetroShare config dir>/stylesheets
Portable version: <Application dir>/stylesheets
stylesheets
+history
|-Style1
|-Style2
+private
|-Style1
|-Style2
+public
|-Style1
|-Style2
Files and dirs of the style:
info.xml - description
incoming.htm - incoming messages
outgoing.htm - outgoing messages
hincoming.htm - incoming history messages
houtgoing.htm - outgoing history messages
ooutgoing.htm - outgoing offline messages (private chat)
main.css - stylesheet
variants - directory with variants (optional)
+- *.css - Stylesheets for the variants
Example:
info.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE RetroShare_StyleInfo>
<RetroShare_Style version="1.0">
<style>
<name>Name</name>
<description>Description</description>
</style>
<author>
<name>Author</name>
<email>E-Mail</email>
</author>
</RetroShare_Style>
incoming.htm
<style type="text/css">
%css-style%
</style>
<span class='incomingTime'>%time%</span>
<span class='incomingName'><strong>%name%</strong></span>
%message%
outgoing.htm
<style type="text/css">
%css-style%
</style>
<span class='outgoingTime'>%time%</span>
<span class='outgoingName'><strong>%name%</strong></span>
%message%
main.css
.incomingTime {
color:#C00000;
}
.incomingName{
color:#2D84C9;
}
.outgoingTime {
color:#C00000;
}
.outgoingName{
color:#2D84C9;
}
:
more definitions for history and offline messages
See standard styles in retroshare-gui/src/gui/qss/chat/
*/
#include <QApplication>
#include "ChatStyle.h"
#include "gui/settings/rsharesettings.h"
#include "gui/notifyqt.h"
#include "gui/common/Emoticons.h"
#include <retroshare/rsinit.h>
enum enumGetStyle
{
GETSTYLE_INCOMING,
GETSTYLE_OUTGOING,
GETSTYLE_HINCOMING,
GETSTYLE_HOUTGOING,
GETSTYLE_OOUTGOING
};
/* Default constructor */
ChatStyle::ChatStyle() : QObject()
{
m_styleType = TYPE_UNKNOWN;
connect(NotifyQt::getInstance(), SIGNAL(chatStyleChanged(int)), SLOT(styleChanged(int)));
}
/* Destructor. */
ChatStyle::~ChatStyle()
{
}
void ChatStyle::styleChanged(int styleType)
{
if (m_styleType == styleType) {
setStyleFromSettings(m_styleType);
}
}
bool ChatStyle::setStylePath(const QString &stylePath, const QString &styleVariant)
{
m_styleType = TYPE_UNKNOWN;
m_styleDir.setPath(QApplication::applicationDirPath());
if (m_styleDir.cd(stylePath) == false) {
m_styleDir = QDir("");
m_styleVariant.clear();
return false;
}
m_styleVariant = styleVariant;
return true;
}
bool ChatStyle::setStyleFromSettings(enumStyleType styleType)
{
QString stylePath;
QString styleVariant;
switch (styleType) {
case TYPE_PUBLIC:
Settings->getPublicChatStyle(stylePath, styleVariant);
break;
case TYPE_PRIVATE:
Settings->getPrivateChatStyle(stylePath, styleVariant);
break;
case TYPE_HISTORY:
Settings->getHistoryChatStyle(stylePath, styleVariant);
break;
case TYPE_UNKNOWN:
return false;
}
bool result = setStylePath(stylePath, styleVariant);
m_styleType = styleType;
// reset cache
for (int i = 0; i < FORMATMSG_COUNT; i++) {
m_style[i].clear();
}
return result;
}
static QString getStyle(const QDir &styleDir, const QString &styleVariant, enumGetStyle type)
{
QString style;
if (styleDir == QDir("")) {
return "";
}
QFile fileHtml;
switch (type) {
case GETSTYLE_INCOMING:
fileHtml.setFileName(QFileInfo(styleDir, "incoming.htm").absoluteFilePath());
break;
case GETSTYLE_OUTGOING:
fileHtml.setFileName(QFileInfo(styleDir, "outgoing.htm").absoluteFilePath());
break;
case GETSTYLE_HINCOMING:
fileHtml.setFileName(QFileInfo(styleDir, "hincoming.htm").absoluteFilePath());
break;
case GETSTYLE_HOUTGOING:
fileHtml.setFileName(QFileInfo(styleDir, "houtgoing.htm").absoluteFilePath());
break;
case GETSTYLE_OOUTGOING:
fileHtml.setFileName(QFileInfo(styleDir, "ooutgoing.htm").absoluteFilePath());
break;
default:
return "";
}
if (fileHtml.open(QIODevice::ReadOnly)) {
style = fileHtml.readAll();
fileHtml.close();
QFile fileCss(QFileInfo(styleDir, "main.css").absoluteFilePath());
QString css;
if (fileCss.open(QIODevice::ReadOnly)) {
css = fileCss.readAll();
fileCss.close();
}
if (styleVariant.isEmpty() == false) {
QFile fileCssVariant(QFileInfo(styleDir, "variants/" + styleVariant + ".css").absoluteFilePath());
QString cssVariant;
if (fileCssVariant.open(QIODevice::ReadOnly)) {
cssVariant = fileCssVariant.readAll();
fileCssVariant.close();
css += "\n" + cssVariant;
}
}
style.replace("%css-style%", css);
}
return style;
}
QString ChatStyle::formatMessage(enumFormatMessage type, const QString &name, const QDateTime &timestamp, const QString &message, unsigned int flag)
{
QString style = m_style[type];
if (style.isEmpty()) {
switch (type) {
case FORMATMSG_INCOMING:
style = getStyle(m_styleDir, m_styleVariant, GETSTYLE_INCOMING);
break;
case FORMATMSG_OUTGOING:
style = getStyle(m_styleDir, m_styleVariant, GETSTYLE_OUTGOING);
break;
case FORMATMSG_HINCOMING:
style = getStyle(m_styleDir, m_styleVariant, GETSTYLE_HINCOMING);
break;
case FORMATMSG_HOUTGOING:
style = getStyle(m_styleDir, m_styleVariant, GETSTYLE_HOUTGOING);
break;
case FORMATMSG_OOUTGOING:
style = getStyle(m_styleDir, m_styleVariant, GETSTYLE_OOUTGOING);
break;
}
if (style.isEmpty()) {
// default style
style = "<table width='100%'><tr><td><b>%name%</b></td><td width='130' align='right'>%time%</td></tr></table><table width='100%'><tr><td>%message%</td></tr></table>";
}
m_style[type] = style;
}
unsigned int formatFlag = 0;
if (flag & CHAT_FORMATMSG_EMBED_SMILEYS) {
formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS;
}
if (flag & CHAT_FORMATMSG_EMBED_LINKS) {
formatFlag |= RSHTML_FORMATTEXT_EMBED_LINKS;
}
QString msg = RsHtml::formatText(message, formatFlag);
// //replace http://, https:// and www. with <a href> links
// QRegExp rx("(retroshare://[^ <>]*)|(https?://[^ <>]*)|(www\\.[^ <>]*)");
// int count = 0;
// int pos = 100; //ignore the first 100 char because of the standard DTD ref
// while ( (pos = rx.indexIn(message, pos)) != -1 ) {
// //we need to look ahead to see if it's already a well formed link
// if (message.mid(pos - 6, 6) != "href=\"" && message.mid(pos - 6, 6) != "href='" && message.mid(pos - 6, 6) != "ttp://" ) {
// QString tempMessg = message.left(pos) + "<a href=\"" + rx.cap(count) + "\">" + rx.cap(count) + "</a>" + message.mid(pos + rx.matchedLength(), -1);
// message = tempMessg;
// }
// pos += rx.matchedLength() + 15;
// count ++;
// }
// {
// QHashIterator<QString, QString> i(smileys);
// while(i.hasNext())
// {
// i.next();
// foreach(QString code, i.key().split("|"))
// message.replace(code, "<img src=\"" + i.value() + "\" />");
// }
// }
QString formatMsg = style.replace("%name%", name)
.replace("%date%", timestamp.date().toString("dd.MM.yyyy"))
.replace("%time%", timestamp.time().toString("hh:mm:ss"))
.replace("%message%", msg);
return formatMsg;
}
static bool getStyleInfo(QString stylePath, QString stylePathRelative, ChatStyleInfo &info)
{
// Initialize info
info = ChatStyleInfo();
QFileInfo file(stylePath, "info.xml");
QFile xmlFile(file.filePath());
if (xmlFile.open(QIODevice::ReadOnly) == false) {
// No info file found
return false;
}
QXmlStreamReader reader;
reader.setDevice(&xmlFile);
while (reader.atEnd() == false) {
reader.readNext();
if (reader.isStartElement()) {
if (reader.name() == "RetroShare_Style") {
if (reader.attributes().value("version") == "1.0") {
info.stylePath = stylePathRelative;
continue;
}
// Not the right format of the xml file;
return false ;
}
if (info.stylePath.isEmpty()) {
continue;
}
if (reader.name() == "style") {
// read style information
while (reader.atEnd() == false) {
reader.readNext();
if (reader.isEndElement()) {
if (reader.name() == "style") {
break;
}
continue;
}
if (reader.isStartElement()) {
if (reader.name() == "name") {
info.styleName = reader.readElementText();
continue;
}
if (reader.name() == "description") {
info.styleDescription = reader.readElementText();
continue;
}
// ingore all other entries
}
}
continue;
}
if (reader.name() == "author") {
// read author information
while (reader.atEnd() == false) {
reader.readNext();
if (reader.isEndElement()) {
if (reader.name() == "author") {
break;
}
continue;
}
if (reader.isStartElement()) {
if (reader.name() == "name") {
info.authorName = reader.readElementText();
continue;
}
if (reader.name() == "email") {
info.authorEmail = reader.readElementText();
continue;
}
// ingore all other entries
}
}
continue;
}
// ingore all other entries
}
}
if (reader.hasError()) {
return false;
}
if (info.stylePath.isEmpty()) {
return false;
}
return true;
}
static QString getBaseDir()
{
// application path
std::string configDir = RsInit::RsConfigDirectory();
QString baseDir = QString::fromStdString(configDir);
#ifdef WIN32
if (RsInit::isPortable ()) {
// application dir for portable version
baseDir = QApplication::applicationDirPath();
}
#endif
return baseDir;
}
/*static*/ bool ChatStyle::getAvailableStyles(enumStyleType styleType, QList<ChatStyleInfo> &styles)
{
styles.clear();
// base dir
QDir baseDir(getBaseDir());
ChatStyleInfo standardInfo;
QString stylePath;
switch (styleType) {
case TYPE_PUBLIC:
if (getStyleInfo(":/qss/chat/public", ":/qss/chat/public", standardInfo)) {
standardInfo.styleDescription = tr("Standard style for group chat");
styles.append(standardInfo);
}
stylePath = "stylesheets/public";
break;
case TYPE_PRIVATE:
if (getStyleInfo(":/qss/chat/private", ":/qss/chat/private", standardInfo)) {
standardInfo.styleDescription = tr("Standard style for private chat");
styles.append(standardInfo);
}
stylePath = "stylesheets/private";
break;
case TYPE_HISTORY:
if (getStyleInfo(":/qss/chat/history", ":/qss/chat/history", standardInfo)) {
standardInfo.styleDescription = tr("Standard style for history");
styles.append(standardInfo);
}
stylePath = "stylesheets/history";
break;
case TYPE_UNKNOWN:
default:
return false;
}
QDir dir(baseDir);
if (dir.cd(stylePath) == false) {
// no user styles available
return true;
}
// get all style directories
QFileInfoList dirList = dir.entryInfoList(QDir::AllDirs | QDir::NoDotAndDotDot, QDir::Name);
// iterate style directories and get info
for (QFileInfoList::iterator dir = dirList.begin(); dir != dirList.end(); dir++) {
ChatStyleInfo info;
if (getStyleInfo(dir->absoluteFilePath(), baseDir.relativeFilePath(dir->absoluteFilePath()), info)) {
styles.append(info);
}
}
return true;
}
/*static*/ bool ChatStyle::getAvailableVariants(const QString &stylePath, QStringList &variants)
{
variants.clear();
if (stylePath.isEmpty()) {
return false;
}
// application path
QDir dir(QApplication::applicationDirPath());
if (dir.cd(stylePath) == false) {
// style not found
return false;
}
if (dir.cd("variants") == false) {
// no variants available
return true;
}
// get all variants
QStringList filters;
filters.append("*.css");
QFileInfoList fileList = dir.entryInfoList(filters, QDir::Files, QDir::Name);
// iterate variants
for (QFileInfoList::iterator file = fileList.begin(); file != fileList.end(); file++) {
variants.append(file->baseName());
}
return true;
}