summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/Chat/ChatController.cpp10
-rw-r--r--Swift/Controllers/MainController.cpp2
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.cpp12
-rw-r--r--Swift/Controllers/Roster/ContactRosterItem.h4
-rw-r--r--Swift/Controllers/StatusTracker.cpp6
-rw-r--r--Swift/Controllers/StatusTracker.h5
-rw-r--r--Swift/QtUI/ChatList/ChatListDelegate.cpp5
-rw-r--r--Swift/QtUI/ChatList/ChatListRecentItem.h3
-rw-r--r--Swift/QtUI/Roster/DelegateCommons.cpp9
-rw-r--r--Swift/QtUI/Roster/DelegateCommons.h5
-rw-r--r--Swift/QtUI/Roster/RosterDelegate.cpp3
-rw-r--r--Swift/QtUI/Roster/RosterModel.cpp11
-rw-r--r--Swift/QtUI/Roster/RosterModel.h2
-rw-r--r--Swift/QtUI/Swift.qrc1
-rw-r--r--Swift/resources/icons/zzz.pngbin0 -> 850 bytes
-rw-r--r--Swift/resources/icons/zzz.svg166
-rw-r--r--Swiften/Elements/Idle.h37
-rw-r--r--Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp2
-rw-r--r--Swiften/Parser/PayloadParsers/IdleParser.cpp32
-rw-r--r--Swiften/Parser/PayloadParsers/IdleParser.h24
-rw-r--r--Swiften/Parser/PayloadParsers/UnitTest/IdleParserTest.cpp38
-rw-r--r--Swiften/Parser/SConscript1
-rw-r--r--Swiften/SConscript2
-rw-r--r--Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp2
-rw-r--r--Swiften/Serializer/PayloadSerializers/IdleSerializer.h22
-rw-r--r--Swiften/Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp32
26 files changed, 424 insertions, 12 deletions
diff --git a/Swift/Controllers/Chat/ChatController.cpp b/Swift/Controllers/Chat/ChatController.cpp
index edd2e3b..2d2d941 100644
--- a/Swift/Controllers/Chat/ChatController.cpp
+++ b/Swift/Controllers/Chat/ChatController.cpp
@@ -31,6 +31,7 @@
#include <Swift/Controllers/UIEvents/ShowWhiteboardUIEvent.h>
#include <Swiften/Elements/DeliveryReceipt.h>
#include <Swiften/Elements/DeliveryReceiptRequest.h>
+#include <Swiften/Elements/Idle.h>
#include <Swift/Controllers/SettingConstants.h>
#include <Swift/Controllers/Highlighter.h>
#include <Swiften/Base/Log.h>
@@ -62,6 +63,10 @@ ChatController::ChatController(const JID& self, StanzaChannel* stanzaChannel, IQ
startMessage = str(format(QT_TRANSLATE_NOOP("", "Starting chat with %1% - %2%")) % nick % contact.toBare().toString());
theirPresence = contact.isBare() ? presenceOracle->getHighestPriorityPresence(contact.toBare()) : presenceOracle->getLastPresence(contact);
}
+ Idle::ref idle;
+ if (theirPresence && (idle = theirPresence->getPayload<Idle>())) {
+ startMessage += QT_TRANSLATE_NOOP("", ", who has been idle since ") + boost::posix_time::to_simple_string(idle->getSince());
+ }
startMessage += ": " + statusShowTypeToFriendlyName(theirPresence ? theirPresence->getShow() : StatusShow::None);
if (theirPresence && !theirPresence->getStatus().empty()) {
startMessage += " (" + theirPresence->getStatus() + ")";
@@ -335,6 +340,11 @@ std::string ChatController::getStatusChangeString(boost::shared_ptr<Presence> pr
response = QT_TRANSLATE_NOOP("", "%1% is now busy");
}
}
+ Idle::ref idle;
+ if ((idle = presence->getPayload<Idle>())) {
+ response += QT_TRANSLATE_NOOP("", " and has been idle since ") + boost::posix_time::to_simple_string(idle->getSince());
+ }
+
if (!response.empty()) {
response = str(format(response) % nick);
}
diff --git a/Swift/Controllers/MainController.cpp b/Swift/Controllers/MainController.cpp
index 87ec94d..0c9c09c 100644
--- a/Swift/Controllers/MainController.cpp
+++ b/Swift/Controllers/MainController.cpp
@@ -461,7 +461,7 @@ void MainController::handleInputIdleChanged(bool idle) {
}
else {
if (idle) {
- if (statusTracker_->goAutoAway()) {
+ if (statusTracker_->goAutoAway(idleDetector_->getIdleTimeSeconds())) {
if (client_ && client_->isAvailable()) {
sendPresence(statusTracker_->getNextPresence());
}
diff --git a/Swift/Controllers/Roster/ContactRosterItem.cpp b/Swift/Controllers/Roster/ContactRosterItem.cpp
index 5b1b6e0..d2edfe7 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.cpp
+++ b/Swift/Controllers/Roster/ContactRosterItem.cpp
@@ -8,6 +8,9 @@
#include "Swift/Controllers/Roster/GroupRosterItem.h"
#include <Swiften/Base/foreach.h>
+#include <Swiften/Elements/Idle.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
namespace Swift {
@@ -39,6 +42,15 @@ std::string ContactRosterItem::getStatusText() const {
return shownPresence_ ? shownPresence_->getStatus() : "";
}
+std::string ContactRosterItem::getIdleText() const {
+ Idle::ref idle = shownPresence_ ? shownPresence_->getPayload<Idle>() : Idle::ref();
+ if (!idle || idle->getSince().is_not_a_date_time()) {
+ return "";
+ } else {
+ return boost::posix_time::to_simple_string(idle->getSince());
+ }
+}
+
void ContactRosterItem::setAvatarPath(const std::string& path) {
avatarPath_ = path;
onDataChanged();
diff --git a/Swift/Controllers/Roster/ContactRosterItem.h b/Swift/Controllers/Roster/ContactRosterItem.h
index 8389a44..7a2100e 100644
--- a/Swift/Controllers/Roster/ContactRosterItem.h
+++ b/Swift/Controllers/Roster/ContactRosterItem.h
@@ -17,6 +17,7 @@
#include <boost/bind.hpp>
#include "Swiften/Base/boost_bsignals.h"
#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/ptime.hpp>
namespace Swift {
@@ -35,6 +36,7 @@ class ContactRosterItem : public RosterItem {
StatusShow::Type getStatusShow() const;
StatusShow::Type getSimplifiedStatusShow() const;
std::string getStatusText() const;
+ std::string getIdleText() const;
void setAvatarPath(const std::string& path);
const std::string& getAvatarPath() const;
const JID& getJID() const;
@@ -50,9 +52,11 @@ class ContactRosterItem : public RosterItem {
void setSupportedFeatures(const std::set<Feature>& features);
bool supportsFeature(Feature feature) const;
+
private:
JID jid_;
JID displayJID_;
+ boost::posix_time::ptime lastAvailableTime_;
std::string avatarPath_;
std::map<std::string, boost::shared_ptr<Presence> > presences_;
boost::shared_ptr<Presence> offlinePresence_;
diff --git a/Swift/Controllers/StatusTracker.cpp b/Swift/Controllers/StatusTracker.cpp
index 0c88f4d..6766c2e 100644
--- a/Swift/Controllers/StatusTracker.cpp
+++ b/Swift/Controllers/StatusTracker.cpp
@@ -8,6 +8,8 @@
#include <boost/smart_ptr/make_shared.hpp>
+#include <Swiften/Elements/Idle.h>
+
namespace Swift {
StatusTracker::StatusTracker() {
@@ -21,6 +23,7 @@ boost::shared_ptr<Presence> StatusTracker::getNextPresence() {
presence = boost::make_shared<Presence>();
presence->setShow(StatusShow::Away);
presence->setStatus(queuedPresence_->getStatus());
+ presence->addPayload(boost::make_shared<Idle>(isAutoAwaySince_));
} else {
presence = queuedPresence_;
}
@@ -35,11 +38,12 @@ void StatusTracker::setRequestedPresence(boost::shared_ptr<Presence> presence) {
// }
}
-bool StatusTracker::goAutoAway() {
+bool StatusTracker::goAutoAway(const int& seconds) {
if (queuedPresence_->getShow() != StatusShow::Online) {
return false;
}
isAutoAway_ = true;
+ isAutoAwaySince_ = boost::posix_time::second_clock::universal_time() - boost::posix_time::seconds(seconds);
return true;
}
diff --git a/Swift/Controllers/StatusTracker.h b/Swift/Controllers/StatusTracker.h
index 4f4e880..10a5c0c 100644
--- a/Swift/Controllers/StatusTracker.h
+++ b/Swift/Controllers/StatusTracker.h
@@ -10,6 +10,8 @@
#include "Swiften/Elements/Presence.h"
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
namespace Swift {
class StatusTracker {
@@ -17,10 +19,11 @@ namespace Swift {
StatusTracker();
boost::shared_ptr<Presence> getNextPresence();
void setRequestedPresence(boost::shared_ptr<Presence> presence);
- bool goAutoAway();
+ bool goAutoAway(const int& seconds);
bool goAutoUnAway();
private:
boost::shared_ptr<Presence> queuedPresence_;
bool isAutoAway_;
+ boost::posix_time::ptime isAutoAwaySince_;
};
}
diff --git a/Swift/QtUI/ChatList/ChatListDelegate.cpp b/Swift/QtUI/ChatList/ChatListDelegate.cpp
index 5b879df..5b03ac5 100644
--- a/Swift/QtUI/ChatList/ChatListDelegate.cpp
+++ b/Swift/QtUI/ChatList/ChatListDelegate.cpp
@@ -120,7 +120,7 @@ void ChatListDelegate::paintRecent(QPainter* painter, const QStyleOptionViewItem
QString name = item->data(Qt::DisplayRole).toString();
//qDebug() << "Avatar for " << name << " = " << avatarPath;
QString statusText = item->data(ChatListRecentItem::DetailTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, false, item->getChat().unreadCount, compact_);
}
void ChatListDelegate::paintWhiteboard(QPainter* painter, const QStyleOptionViewItem& option, ChatListWhiteboardItem* item) const {
@@ -135,7 +135,8 @@ void ChatListDelegate::paintWhiteboard(QPainter* painter, const QStyleOptionView
QString name = item->data(Qt::DisplayRole).toString();
//qDebug() << "Avatar for " << name << " = " << avatarPath;
QString statusText = item->data(ChatListWhiteboardItem::DetailTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, item->getChat().unreadCount, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, false, item->getChat().unreadCount, compact_);
+
}
}
diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.h b/Swift/QtUI/ChatList/ChatListRecentItem.h
index 4e7bc3e..3f27a68 100644
--- a/Swift/QtUI/ChatList/ChatListRecentItem.h
+++ b/Swift/QtUI/ChatList/ChatListRecentItem.h
@@ -23,7 +23,8 @@ namespace Swift {
DetailTextRole = Qt::UserRole,
AvatarRole = Qt::UserRole + 1,
PresenceIconRole = Qt::UserRole + 2/*,
- StatusShowTypeRole = Qt::UserRole + 3*/
+ StatusShowTypeRole = Qt::UserRole + 3,
+ IdleRole = Qt::UserRole + 4*/
};
ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent);
const ChatListWindow::Chat& getChat() const;
diff --git a/Swift/QtUI/Roster/DelegateCommons.cpp b/Swift/QtUI/Roster/DelegateCommons.cpp
index a575cb0..e7342f3 100644
--- a/Swift/QtUI/Roster/DelegateCommons.cpp
+++ b/Swift/QtUI/Roster/DelegateCommons.cpp
@@ -17,8 +17,8 @@ void DelegateCommons::drawElidedText(QPainter* painter, const QRect& region, con
painter->drawText(region, flags, adjustedText.simplified());
}
-void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const {
- painter->save();
+void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, bool isIdle, int unreadCount, bool compact) const {
+ painter->save();
QRect fullRegion(option.rect);
if ( option.state & QStyle::State_Selected ) {
painter->fillRect(fullRegion, option.palette.highlight());
@@ -29,6 +29,7 @@ void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem
QRect presenceIconRegion(QPoint(farLeftMargin, fullRegion.top()), QSize(presenceIconWidth, fullRegion.height() - verticalMargin));
+ QRect idleIconRegion(QPoint(farLeftMargin, fullRegion.top()), QSize(presenceIconWidth*2, fullRegion.height() - verticalMargin));
int calculatedAvatarSize = presenceIconRegion.height();
//This overlaps the presenceIcon, so must be painted first
QRect avatarRegion(QPoint(presenceIconRegion.right() - presenceIconWidth / 2, presenceIconRegion.top()), QSize(calculatedAvatarSize, calculatedAvatarSize));
@@ -51,6 +52,10 @@ void DelegateCommons::paintContact(QPainter* painter, const QStyleOptionViewItem
//Paint the presence icon over the top of the avatar
presenceIcon.paint(painter, presenceIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ if (isIdle) {
+ idleIcon.paint(painter, idleIconRegion, Qt::AlignBottom | Qt::AlignHCenter);
+ }
+
QFontMetrics nameMetrics(nameFont);
painter->setFont(nameFont);
int extraFontWidth = nameMetrics.width("H");
diff --git a/Swift/QtUI/Roster/DelegateCommons.h b/Swift/QtUI/Roster/DelegateCommons.h
index 8732598..0684410 100644
--- a/Swift/QtUI/Roster/DelegateCommons.h
+++ b/Swift/QtUI/Roster/DelegateCommons.h
@@ -17,7 +17,7 @@
namespace Swift {
class DelegateCommons {
public:
- DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()) {
+ DelegateCommons() : nameFont(QApplication::font()), detailFont(QApplication::font()), idleIcon(QIcon(":/icons/zzz.png")) {
detailFontSizeDrop = nameFont.pointSize() >= 10 ? 2 : 0;
detailFont.setStyle(QFont::StyleItalic);
detailFont.setPointSize(nameFont.pointSize() - detailFontSizeDrop);
@@ -26,7 +26,7 @@ namespace Swift {
static void drawElidedText(QPainter* painter, const QRect& region, const QString& text, int flags = Qt::AlignTop);
QSize contactSizeHint(const QStyleOptionViewItem& option, const QModelIndex& index, bool compact) const;
- void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, int unreadCount, bool compact) const;
+ void paintContact(QPainter* painter, const QStyleOptionViewItem& option, const QColor& nameColor, const QString& avatarPath, const QIcon& presenceIcon, const QString& name, const QString& statusText, bool isIdle, int unreadCount, bool compact) const;
int detailFontSizeDrop;
QFont nameFont;
@@ -38,5 +38,6 @@ namespace Swift {
static const int presenceIconHeight;
static const int presenceIconWidth;
static const int unreadCountSize;
+ QIcon idleIcon;
};
}
diff --git a/Swift/QtUI/Roster/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp
index 5c964ca..aef588c 100644
--- a/Swift/QtUI/Roster/RosterDelegate.cpp
+++ b/Swift/QtUI/Roster/RosterDelegate.cpp
@@ -74,9 +74,10 @@ void RosterDelegate::paintContact(QPainter* painter, const QStyleOptionViewItem&
QIcon presenceIcon = index.data(PresenceIconRole).isValid() && !index.data(PresenceIconRole).value<QIcon>().isNull()
? index.data(PresenceIconRole).value<QIcon>()
: QIcon(":/icons/offline.png");
+ bool isIdle = index.data(IdleRole).isValid() ? index.data(IdleRole).toBool() : false;
QString name = index.data(Qt::DisplayRole).toString();
QString statusText = index.data(StatusTextRole).toString();
- common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, 0, compact_);
+ common_.paintContact(painter, option, nameColor, avatarPath, presenceIcon, name, statusText, isIdle, 0, compact_);
}
}
diff --git a/Swift/QtUI/Roster/RosterModel.cpp b/Swift/QtUI/Roster/RosterModel.cpp
index 1fc20dd..885d04c 100644
--- a/Swift/QtUI/Roster/RosterModel.cpp
+++ b/Swift/QtUI/Roster/RosterModel.cpp
@@ -84,7 +84,8 @@ QVariant RosterModel::data(const QModelIndex& index, int role) const {
case AvatarRole: return getAvatar(item);
case PresenceIconRole: return getPresenceIcon(item);
case ChildCountRole: return getChildCount(item);
- default: return QVariant();
+ case IdleRole: return getIsIdle(item);
+ default: return QVariant();
}
}
@@ -93,6 +94,11 @@ int RosterModel::getChildCount(RosterItem* item) const {
return group ? group->getDisplayedChildren().size() : 0;
}
+bool RosterModel::getIsIdle(RosterItem* item) const {
+ ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item);
+ return contact ? !contact->getIdleText().empty() : false;
+}
+
QColor RosterModel::intToColor(int color) const {
return QColor(
((color & 0xFF0000)>>16),
@@ -131,6 +137,9 @@ QString RosterModel::getToolTip(RosterItem* item) const {
if (!getStatusText(item).isEmpty()) {
tip += ": " + getStatusText(item);
}
+ if (!contact->getIdleText().empty()) {
+ tip += "\n " + tr("Idle since ") + P2QSTRING(contact->getIdleText());
+ }
}
return tip;
}
diff --git a/Swift/QtUI/Roster/RosterModel.h b/Swift/QtUI/Roster/RosterModel.h
index bd34e9c..23d54f8 100644
--- a/Swift/QtUI/Roster/RosterModel.h
+++ b/Swift/QtUI/Roster/RosterModel.h
@@ -18,6 +18,7 @@ namespace Swift {
PresenceIconRole = Qt::UserRole + 2,
StatusShowTypeRole = Qt::UserRole + 3,
ChildCountRole = Qt::UserRole + 4,
+ IdleRole = Qt::UserRole + 5
};
class QtTreeWidget;
@@ -48,6 +49,7 @@ namespace Swift {
QString getStatusText(RosterItem* item) const;
QIcon getPresenceIcon(RosterItem* item) const;
int getChildCount(RosterItem* item) const;
+ bool getIsIdle(RosterItem* item) const;
void reLayout();
Roster* roster_;
QtTreeWidget* view_;
diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc
index 934bd80..f1b3140 100644
--- a/Swift/QtUI/Swift.qrc
+++ b/Swift/QtUI/Swift.qrc
@@ -40,5 +40,6 @@
<file alias="emoticons/wink.png">../resources/emoticons/wink.png</file>
<file alias="icons/star-checked.png">../resources/icons/star-checked2.png</file>
<file alias="icons/star-unchecked.png">../resources/icons/star-unchecked2.png</file>
+ <file alias="icons/zzz.png">../resources/icons/zzz.png</file>
</qresource>
</RCC>
diff --git a/Swift/resources/icons/zzz.png b/Swift/resources/icons/zzz.png
new file mode 100644
index 0000000..706c2f4
--- /dev/null
+++ b/Swift/resources/icons/zzz.png
Binary files differ
diff --git a/Swift/resources/icons/zzz.svg b/Swift/resources/icons/zzz.svg
new file mode 100644
index 0000000..adbd0be
--- /dev/null
+++ b/Swift/resources/icons/zzz.svg
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1024"
+ height="1024"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.48.2 r9819"
+ version="1.0"
+ inkscape:export-filename="/Users/tobias/dev/rep/swift-lastseen/Swift/resources/icons/zzz.png"
+ inkscape:export-xdpi="2.8125"
+ inkscape:export-ydpi="2.8125"
+ sodipodi:docname="Swift_resources_icons_zzz_Before_b244d1f210fa994df40297664f111de31b6bf676.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <linearGradient
+ id="linearGradient3155">
+ <stop
+ style="stop-color:#007600;stop-opacity:1;"
+ offset="0"
+ id="stop3157" />
+ <stop
+ style="stop-color:#97f597;stop-opacity:1;"
+ offset="1"
+ id="stop3159" />
+ </linearGradient>
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 526.18109 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="744.09448 : 526.18109 : 1"
+ inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
+ id="perspective10" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3155"
+ id="linearGradient3185"
+ gradientUnits="userSpaceOnUse"
+ x1="146.36209"
+ y1="457.4635"
+ x2="376.76218"
+ y2="76.855392"
+ gradientTransform="matrix(0.5616546,0,0,0.5616546,113.6799,171.43482)" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.45254834"
+ inkscape:cx="461.16373"
+ inkscape:cy="517.26047"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:window-width="1440"
+ inkscape:window-height="852"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:snap-global="false"
+ inkscape:window-maximized="1">
+ <inkscape:grid
+ type="xygrid"
+ id="grid2988"
+ empspacing="5"
+ visible="true"
+ enabled="true"
+ snapvisiblegridlinesonly="true" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,512)">
+ <path
+ style="fill:url(#linearGradient3185);fill-opacity:1;stroke:#004900;stroke-width:40;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;opacity:0"
+ d="m 400.8403,315.01501 c 0,79.25627 -64.77302,144.32061 -143.37683,143.5802 -78.9121,-0.74331 -143.37683,-64.32393 -143.37683,-143.5802 0,-80.79036 64.23282,-234.091516 143.37683,-234.091516 79.14401,0 143.37683,154.835246 143.37683,234.091516 z"
+ id="path2383"
+ sodipodi:nodetypes="csssc"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-size:145.84281920999998761px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:#ffffff;font-family:Sans;stroke-opacity:1;stroke-width:20;stroke-miterlimit:4;stroke-dasharray:none"
+ x="278.41916"
+ y="192.9375"
+ id="text2990"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan2992"
+ x="278.41916"
+ y="192.9375"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"><tspan
+ style="font-size:290px;font-weight:bold;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;-inkscape-font-specification:Helvetica Bold;font-family:Sans;font-style:normal;font-stretch:normal;font-variant:normal"
+ id="tspan2998"
+ dy="0"
+ dx="0">Z</tspan><tspan
+ style="font-size:350px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"
+ id="tspan2994"
+ dy="-60">Z</tspan><tspan
+ style="font-size:450px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;letter-spacing:-60px;writing-mode:lr-tb;fill:#000000;stroke:#ffffff;stroke-width:20;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;font-family:Sans;-inkscape-font-specification:Helvetica Bold"
+ id="tspan2996"
+ dy="-80">Z</tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="470.72134"
+ y="-22.449778"
+ id="text3000"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3002"
+ x="470.72134"
+ y="-22.449778" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="1020.8854"
+ y="840.59418"
+ id="text3791"
+ sodipodi:linespacing="125%"
+ transform="translate(0,-512)"><tspan
+ sodipodi:role="line"
+ id="tspan3793"
+ x="1020.8854"
+ y="840.59418" /></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="279.52814"
+ y="289.27185"
+ id="text3766"
+ sodipodi:linespacing="125%"
+ transform="translate(0,-512)"><tspan
+ sodipodi:role="line"
+ id="tspan3768"
+ x="279.52814"
+ y="289.27185" /></text>
+ </g>
+</svg>
diff --git a/Swiften/Elements/Idle.h b/Swiften/Elements/Idle.h
new file mode 100644
index 0000000..572eba2
--- /dev/null
+++ b/Swiften/Elements/Idle.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <boost/date_time/posix_time/posix_time_types.hpp>
+
+#include <Swiften/Elements/Payload.h>
+
+namespace Swift {
+
+ class Idle : public Payload {
+ public:
+ typedef boost::shared_ptr<Idle> ref;
+
+ public:
+ Idle() {}
+ Idle(boost::posix_time::ptime since) : since_(since) {
+ }
+
+ void setSince(boost::posix_time::ptime since) {
+ since_ = since;
+ }
+
+ boost::posix_time::ptime getSince() const {
+ return since_;
+ }
+
+ private:
+ boost::posix_time::ptime since_;
+ };
+
+}
diff --git a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
index a40e8f6..1797e45 100644
--- a/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
+++ b/Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.cpp
@@ -67,6 +67,7 @@
#include <Swiften/Parser/PayloadParsers/DeliveryReceiptParserFactory.h>
#include <Swiften/Parser/PayloadParsers/DeliveryReceiptRequestParserFactory.h>
#include <Swiften/Parser/PayloadParsers/WhiteboardParser.h>
+#include <Swiften/Parser/PayloadParsers/IdleParser.h>
using namespace boost;
@@ -128,6 +129,7 @@ FullPayloadParserFactoryCollection::FullPayloadParserFactoryCollection() {
factories_.push_back(boost::make_shared<GenericPayloadParserFactory<WhiteboardParser> >("wb", "http://swift.im/whiteboard"));
factories_.push_back(boost::make_shared<DeliveryReceiptParserFactory>());
factories_.push_back(boost::make_shared<DeliveryReceiptRequestParserFactory>());
+ factories_.push_back(boost::make_shared<GenericPayloadParserFactory<IdleParser> >("idle", "urn:xmpp:idle:1"));
foreach(shared_ptr<PayloadParserFactory> factory, factories_) {
addFactory(factory.get());
diff --git a/Swiften/Parser/PayloadParsers/IdleParser.cpp b/Swiften/Parser/PayloadParsers/IdleParser.cpp
new file mode 100644
index 0000000..51aa34b
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/IdleParser.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <Swiften/Parser/PayloadParsers/IdleParser.h>
+
+#include <Swiften/Base/DateTime.h>
+
+namespace Swift {
+
+IdleParser::IdleParser() : level_(0) {
+}
+
+void IdleParser::handleStartElement(const std::string& /*element*/, const std::string& /*ns*/, const AttributeMap& attributes) {
+ if (level_ == 0) {
+ boost::posix_time::ptime since = stringToDateTime(attributes.getAttribute("since"));
+ getPayloadInternal()->setSince(since);
+ }
+ ++level_;
+}
+
+void IdleParser::handleEndElement(const std::string&, const std::string&) {
+ --level_;
+}
+
+void IdleParser::handleCharacterData(const std::string&) {
+
+}
+
+}
diff --git a/Swiften/Parser/PayloadParsers/IdleParser.h b/Swiften/Parser/PayloadParsers/IdleParser.h
new file mode 100644
index 0000000..38001b2
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/IdleParser.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Elements/Idle.h>
+#include <Swiften/Parser/GenericPayloadParser.h>
+
+namespace Swift {
+ class IdleParser : public GenericPayloadParser<Idle> {
+ public:
+ IdleParser();
+
+ virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes);
+ virtual void handleEndElement(const std::string& element, const std::string&);
+ virtual void handleCharacterData(const std::string& data);
+
+ private:
+ int level_;
+ };
+}
diff --git a/Swiften/Parser/PayloadParsers/UnitTest/IdleParserTest.cpp b/Swiften/Parser/PayloadParsers/UnitTest/IdleParserTest.cpp
new file mode 100644
index 0000000..74da474
--- /dev/null
+++ b/Swiften/Parser/PayloadParsers/UnitTest/IdleParserTest.cpp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <Swiften/Parser/PayloadParsers/UnitTest/PayloadsParserTester.h>
+#include <Swiften/Elements/Presence.h>
+#include <Swiften/Elements/Idle.h>
+#include <Swiften/Base/DateTime.h>
+
+using namespace Swift;
+
+class IdleParserTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IdleParserTest);
+ CPPUNIT_TEST(testParse_XepWhatever_Example1);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testParse_XepWhatever_Example1() {
+ PayloadsParserTester parser;
+ CPPUNIT_ASSERT(parser.parse(
+ "<presence from='juliet@capulet.com/balcony'>\n"
+ "<show>away</show>\n"
+ "<idle xmlns='urn:xmpp:idle:1' since='1969-07-21T02:56:15Z'/>\n"
+ "</presence>\n"
+ ));
+
+ Presence::ref presence = parser.getPayload<Presence>();
+ CPPUNIT_ASSERT(presence);
+ Idle::ref idle = presence->getPayload<Idle>();
+ CPPUNIT_ASSERT(idle);
+ CPPUNIT_ASSERT(stringToDateTime("1969-07-21T02:56:15Z") == idle->getSince());
+ }
+};
diff --git a/Swiften/Parser/SConscript b/Swiften/Parser/SConscript
index 64e9eb9..068cbd7 100644
--- a/Swiften/Parser/SConscript
+++ b/Swiften/Parser/SConscript
@@ -69,6 +69,7 @@ sources = [
"PayloadParsers/NicknameParser.cpp",
"PayloadParsers/ReplaceParser.cpp",
"PayloadParsers/LastParser.cpp",
+ "PayloadParsers/IdleParser.cpp",
"PayloadParsers/S5BProxyRequestParser.cpp",
"PayloadParsers/DeliveryReceiptParser.cpp",
"PayloadParsers/DeliveryReceiptRequestParser.cpp",
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 8e6bd97..a62d344 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -375,6 +375,7 @@ if env["SCONS_STAGE"] == "build" :
File("Parser/PayloadParsers/UnitTest/MUCAdminPayloadParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/MUCUserPayloadParserTest.cpp"),
File("Parser/PayloadParsers/UnitTest/DeliveryReceiptParserTest.cpp"),
+ File("Parser/PayloadParsers/UnitTest/IdleParserTest.cpp"),
File("Parser/UnitTest/BOSHBodyExtractorTest.cpp"),
File("Parser/UnitTest/AttributeMapTest.cpp"),
File("Parser/UnitTest/IQParserTest.cpp"),
@@ -424,6 +425,7 @@ if env["SCONS_STAGE"] == "build" :
File("Serializer/PayloadSerializers/UnitTest/MUCAdminPayloadSerializerTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/JingleSerializersTest.cpp"),
File("Serializer/PayloadSerializers/UnitTest/DeliveryReceiptSerializerTest.cpp"),
+ File("Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp"),
File("Serializer/UnitTest/StreamFeaturesSerializerTest.cpp"),
File("Serializer/UnitTest/AuthSuccessSerializerTest.cpp"),
File("Serializer/UnitTest/AuthChallengeSerializerTest.cpp"),
diff --git a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
index b4822cd..3f45a7c 100644
--- a/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
+++ b/Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.cpp
@@ -51,6 +51,7 @@
#include <Swiften/Serializer/PayloadSerializers/ReplaceSerializer.h>
#include <Swiften/Serializer/PayloadSerializers/LastSerializer.h>
#include <Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.h>
+#include <Swiften/Serializer/PayloadSerializers/IdleSerializer.h>
#include <Swiften/Serializer/PayloadSerializers/StreamInitiationFileInfoSerializer.h>
#include <Swiften/Serializer/PayloadSerializers/JingleContentPayloadSerializer.h>
@@ -110,6 +111,7 @@ FullPayloadSerializerCollection::FullPayloadSerializerCollection() {
serializers_.push_back(new ReplaceSerializer());
serializers_.push_back(new LastSerializer());
serializers_.push_back(new WhiteboardSerializer());
+ serializers_.push_back(new IdleSerializer());
serializers_.push_back(new StreamInitiationFileInfoSerializer());
serializers_.push_back(new JingleContentPayloadSerializer());
diff --git a/Swiften/Serializer/PayloadSerializers/IdleSerializer.h b/Swiften/Serializer/PayloadSerializers/IdleSerializer.h
new file mode 100644
index 0000000..45f9da4
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/IdleSerializer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#pragma once
+
+#include <Swiften/Serializer/GenericPayloadSerializer.h>
+#include <Swiften/Elements/Idle.h>
+#include <Swiften/Base/DateTime.h>
+
+namespace Swift {
+ class IdleSerializer : public GenericPayloadSerializer<Idle> {
+ public:
+ IdleSerializer() : GenericPayloadSerializer<Idle>() {}
+
+ virtual std::string serializePayload(boost::shared_ptr<Idle> idle) const {
+ return "<idle xmlns='urn:xmpp:idle:1' since='" + dateTimeToString(idle->getSince()) + "'/>";
+ }
+ };
+}
diff --git a/Swiften/Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp b/Swiften/Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp
new file mode 100644
index 0000000..9700869
--- /dev/null
+++ b/Swiften/Serializer/PayloadSerializers/UnitTest/IdleSerializerTest.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 Tobias Markmann
+ * Licensed under the simplified BSD license.
+ * See Documentation/Licenses/BSD-simplified.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+
+#include <boost/make_shared.hpp>
+
+#include <Swiften/Serializer/PayloadSerializers/IdleSerializer.h>
+
+using namespace Swift;
+
+class IdleSerializerTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(IdleSerializerTest);
+ CPPUNIT_TEST(testSerialize);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IdleSerializerTest() {}
+
+ void testSerialize() {
+ IdleSerializer testling;
+ Idle::ref idle = boost::make_shared<Idle>(stringToDateTime("1969-07-21T02:56:15Z"));
+
+ CPPUNIT_ASSERT_EQUAL(std::string("<idle xmlns='urn:xmpp:idle:1' since='1969-07-21T02:56:15Z'/>"), testling.serialize(idle));
+ }
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IdleSerializerTest);