PageRenderTime 38ms CodeModel.GetById 11ms RepoModel.GetById 1ms app.codeStats 0ms

/plugins/message_window/splitterwin.cpp

http://saje.googlecode.com/
C++ | 310 lines | 218 code | 51 blank | 41 comment | 32 complexity | 231402f8ce187d391b0007087f194f68 MD5 | raw file
Possible License(s): LGPL-2.1
  1. #include "splitterwin.h"
  2. #include <QDateTime>
  3. #include <QSettings>
  4. #include <QWebFrame>
  5. #include <QDesktopServices>
  6. #include <QDebug>
  7. #include <QUrl>
  8. #include <QApplication>
  9. #define MAX_MESSAGES 500
  10. #define RX_DOMAIN "(?:\\w+\\.)+(?:co(?:m)?|org|net|gov|biz|info|travel|ous|[a-z]{2})"
  11. #define RX_PROTOS "(?:http(?:s)?://|ftp://|mailto:)?"
  12. #define RX_PORT "(?:\\:\\d{1,5})?"
  13. #define RX_EMAIL "\\w+@" RX_DOMAIN
  14. #define RX_OTHER RX_DOMAIN RX_PORT "(?:[/\\?]\\S+)?"
  15. #define LP "\\b(" RX_PROTOS ")(" RX_EMAIL "|" RX_OTHER ")\\b"
  16. SplitterWin::SplitterWin(Contact *c, EventsI *ei, QWidget *parent)
  17. : QSplitter(parent), contact(c), events_i(ei),
  18. showDate(true), showTime(true), showNick(true), chatState(CS_INACTIVE), contactChatState(CS_INACTIVE), sendChatState(true)
  19. {
  20. ui.setupUi(this);
  21. setAttribute(Qt::WA_QuitOnClose, false);
  22. update_title();
  23. //ui.edMsgLog->setFont(QFont("tahoma", 12));
  24. connect(ui.widget, SIGNAL(msgSend(const QString &)), this, SLOT(msgSend(const QString &)));
  25. QSettings settings;
  26. restoreGeometry(settings.value("MessageWindow/geometry/" + contact->account->proto->name() + ":" + contact->account->account_id + ":" + contact->contact_id).toByteArray());
  27. ui.edMsgLog->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
  28. connect(ui.edMsgLog, SIGNAL(linkClicked(const QUrl &)), this, SLOT(openLink(const QUrl &)));
  29. connect(ui.edMsgLog, SIGNAL(loadFinished(bool)), this, SLOT(scrollLogToBottom()));
  30. connect(&pauseTimer, SIGNAL(timeout()), this, SLOT(paused()));
  31. connect(&inactiveTimer, SIGNAL(timeout()), this, SLOT(inactive()));
  32. pauseTimer.setInterval(5000);
  33. inactiveTimer.setInterval(30000);
  34. connect(ui.widget, SIGNAL(textChanged()), this, SLOT(composing()));
  35. update_log();
  36. }
  37. SplitterWin::~SplitterWin() {
  38. QSettings settings;
  39. settings.setValue("MessageWindow/geometry/" + contact->account->proto->name() + ":" + contact->account->account_id + ":" + contact->contact_id, saveGeometry());
  40. }
  41. void SplitterWin::addEvents(QList<Message> &events) {
  42. foreach(Message m, events) {
  43. addToLog(m);
  44. }
  45. update_log();
  46. }
  47. void SplitterWin::hideEvent(QHideEvent *e) {
  48. gone();
  49. QSplitter::hideEvent(e);
  50. }
  51. void SplitterWin::showEvent(QShowEvent *e) {
  52. QSplitter::showEvent(e);
  53. }
  54. void SplitterWin::closeEvent(QCloseEvent *e) {
  55. QSplitter::closeEvent(e);
  56. emit closed(contact);
  57. }
  58. void SplitterWin::setSendChatState(bool f) {
  59. sendChatState = f;
  60. }
  61. void SplitterWin::setContactChatState(ChatStateType state) {
  62. if(state != contactChatState) {
  63. contactChatState = state;
  64. update_log();
  65. }
  66. }
  67. void SplitterWin::setUserChatState(ChatStateType state) {
  68. if(state != chatState) {
  69. chatState = state;
  70. if(sendChatState) {
  71. ChatState cs(contact, chatState, false, this);
  72. events_i->fire_event(cs);
  73. }
  74. update_log();
  75. }
  76. }
  77. void SplitterWin::update_title() {
  78. if(getNick() != contact->contact_id)
  79. setWindowTitle(getNick() + " (" + contact->contact_id + ")");
  80. else
  81. setWindowTitle(contact->contact_id);
  82. }
  83. void SplitterWin::openLink(const QUrl &url) {
  84. QDesktopServices::openUrl(url);
  85. }
  86. QString SplitterWin::format_text(Message &m) {
  87. QString nick = (m.type == EventsI::ET_INCOMING ? Qt::escape(getNick()) : Qt::escape(m.contact->account->nick));
  88. QString msg = m.text;
  89. if(msg.startsWith("/me "))
  90. msg.replace(0, 4, "* " + nick + " ");
  91. QString text = "<div class='message'>";
  92. text += "<span class='info'>";
  93. text += timestamp(m.timestamp);
  94. text += "<span class='nick'>" + nick + " </span>";
  95. text += "<span class='separator'>: </span></span>";
  96. text += "<span class='text'>" + msg + "</span>";
  97. text += "</div>";
  98. return text;
  99. }
  100. QString SplitterWin::getContent() {
  101. QString ret;
  102. bool first = true, incomming, last_incomming = false;
  103. foreach(Message item, content) {
  104. incomming = (item.type == EventsI::ET_INCOMING);
  105. if(first || incomming != last_incomming) {
  106. if(first)
  107. ret += QString("<div class='") + (incomming ? "incomming" : "outgoing") + "'>";
  108. else
  109. ret += QString("</div>\n<div class='") + (incomming ? "incomming" : "outgoing") + "'>";
  110. }
  111. ret += format_text(item);
  112. first = false;
  113. last_incomming = incomming;
  114. }
  115. if(!first) ret += "</div>\n";
  116. switch(contactChatState) {
  117. case CS_INACTIVE:
  118. ret += "<div class='chat_state' id='inactive'><span class='nick'>" + getNick() + "</span><span class='state_text'> is inactive</span></div>";
  119. break;
  120. case CS_ACTIVE:
  121. ret += "<div class='chat_state' id='active'><span class='nick'>" + getNick() + "</span><span class='state_text'> is active</span></div>";
  122. break;
  123. case CS_COMPOSING:
  124. ret += "<div class='chat_state' id='composing'><span class='nick'>" + getNick() + "</span><span class='state_text'> is typing</span></div>";
  125. break;
  126. case CS_PAUSED:
  127. ret += "<div class='chat_state' id='paused'><span class='nick'>" + getNick() + "</span><span class='state_text'> has entered text</span></div>";
  128. break;
  129. case CS_GONE:
  130. ret += "<div class='chat_state' id='gone'><span class='nick'>" + getNick() + "</span><span class='state_text'> is gone</span></div>";
  131. break;
  132. }
  133. return ret;
  134. }
  135. void SplitterWin::update_log() {
  136. QString page = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
  137. page += "<html xmlns='http://www.w3.org/1999/xhtml'>\n<head><title>Saje Message Log</title>"
  138. "<style type='text/css'>" + style + "</style></head>\n"
  139. + "<body class='messagelog'>\n" + getContent() + "</body>\n</html>";
  140. //qDebug() << "message log content:" << page;
  141. ui.edMsgLog->setContent(page.toUtf8(), "application/xhtml+xml", QUrl::fromLocalFile(QApplication::applicationFilePath()));
  142. }
  143. void SplitterWin::scrollLogToBottom() {
  144. ui.edMsgLog->page()->mainFrame()->setScrollBarValue(Qt::Vertical, ui.edMsgLog->page()->mainFrame()->scrollBarMaximum(Qt::Vertical));
  145. }
  146. QString SplitterWin::getNick() {
  147. if(contact->has_property("handle")) return contact->get_property("handle").toString();
  148. if(contact->has_property("nick")) return contact->get_property("nick").toString();
  149. if(contact->has_property("name")) return contact->get_property("name").toString();
  150. return contact->contact_id;
  151. }
  152. void SplitterWin::setLogStyleSheet(const QString &styleSheet) {
  153. //ui.edMsgLog->document()->setDefaultStyleSheet(styleSheet);
  154. //ui.edMsgLog->setHtml(ui.edMsgLog->toHtml());
  155. style = styleSheet;
  156. update_log();
  157. }
  158. QString SplitterWin::timestamp(QDateTime &dt) {
  159. QString dateClass = "date";
  160. if(dt.date() == QDate::currentDate())
  161. dateClass = "date_today";
  162. QString ret = "<span class='timestamp'>";
  163. ret += "<span class='" + dateClass + "'>" + Qt::escape(dt.date().toString(Qt::SystemLocaleShortDate)) + " </span>";
  164. ret += "<span class='time'>" + Qt::escape(dt.time().toString(Qt::SystemLocaleShortDate)) + "</span>";
  165. ret += " </span>";
  166. return ret;
  167. }
  168. /*
  169. void SplitterWin::linkUrls(QString &str) {
  170. //dispMsg.replace(QRegExp(LP), "<a href='http://\\2'>\\1</a>");
  171. QRegExp rx(LP), rx_email("^" RX_EMAIL);
  172. int pos = 0, len;
  173. QString scheme, after;
  174. bool valid;
  175. while ((pos = rx.indexIn(str, pos)) != -1) {
  176. len = rx.matchedLength();
  177. //rx.cap(0) is whole match, rx.cap(1) is url scheme, rx.cap(2) is the rest
  178. scheme = rx.cap(1);
  179. valid = true;
  180. if(scheme.isEmpty()) {
  181. if(rx_email.indexIn(rx.cap(2)) != -1)
  182. scheme = "mailto:";
  183. else
  184. scheme = "http://";
  185. } else
  186. if((scheme == "mailto:" && rx_email.indexIn(rx.cap(2)) == -1)
  187. || (scheme != "mailto:" && rx_email.indexIn(rx.cap(2)) != -1))
  188. {
  189. valid = false;
  190. }
  191. if(valid) {
  192. after = "<a href='" + scheme + rx.cap(2) + "'>" + rx.cap(0) + "</a>";
  193. str.replace(pos, len, after);
  194. len = after.length();
  195. }
  196. pos += len;
  197. }
  198. }
  199. */
  200. void SplitterWin::addToLog(Message &m) {
  201. int i = content.size();
  202. if(i > 0) {
  203. while(i - 1 > 0 && content[i - 1].timestamp > m.timestamp) i--;
  204. content.insert(i, m);
  205. } else
  206. content << m;
  207. while(content.size() > MAX_MESSAGES)
  208. content.removeFirst();
  209. }
  210. void SplitterWin::msgEvent(Message &m) {
  211. addToLog(m);
  212. if(m.type == EventsI::ET_INCOMING) {
  213. if(contactChatState != CS_ACTIVE)
  214. setContactChatState(CS_ACTIVE);
  215. else
  216. update_log();
  217. } else if(m.type == EventsI::ET_OUTGOING) {
  218. if(chatState != CS_ACTIVE)
  219. setUserChatState(CS_ACTIVE);
  220. else
  221. update_log();
  222. }
  223. }
  224. void SplitterWin::msgSend(const QString &msg) {
  225. MessageSendReq m(contact, msg, 0, this);
  226. events_i->fire_event(m);
  227. }
  228. ////////////////////////
  229. void SplitterWin::active() {
  230. inactiveTimer.stop();
  231. pauseTimer.stop();
  232. setUserChatState(CS_ACTIVE);
  233. inactiveTimer.start();
  234. }
  235. void SplitterWin::composing() {
  236. if(ui.widget->okToSend()) {
  237. pauseTimer.stop();
  238. inactiveTimer.stop();
  239. setUserChatState(CS_COMPOSING);
  240. pauseTimer.start();
  241. inactiveTimer.start();
  242. } else
  243. active();
  244. }
  245. void SplitterWin::paused() {
  246. setUserChatState(CS_PAUSED);
  247. }
  248. void SplitterWin::inactive() {
  249. setUserChatState(CS_INACTIVE);
  250. }
  251. void SplitterWin::gone() {
  252. pauseTimer.stop();
  253. inactiveTimer.stop();
  254. setUserChatState(CS_GONE);
  255. }