diff options
Diffstat (limited to 'Swift/QtUI')
146 files changed, 6403 insertions, 524 deletions
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/ChatListGroupItem.h b/Swift/QtUI/ChatList/ChatListGroupItem.h index a1e479f..17defea 100644 --- a/Swift/QtUI/ChatList/ChatListGroupItem.h +++ b/Swift/QtUI/ChatList/ChatListGroupItem.h @@ -13,14 +13,14 @@ namespace Swift { class ChatListGroupItem : public ChatListItem { public: - ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {}; - void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}}; - void remove(int index) {items_.removeAt(index);}; - int rowCount() {return items_.size();}; - ChatListItem* item(int i) {return items_[i];}; - int row(ChatListItem* item) {return items_.indexOf(item);}; - QVariant data(int role) const {return (role == Qt::DisplayRole) ? name_ : QVariant();}; - void clear() {items_.clear();}; + ChatListGroupItem(const QString& name, ChatListGroupItem* parent, bool sorted = true) : ChatListItem(parent), name_(name), sorted_(sorted) {} + void addItem(ChatListItem* item) {items_.push_back(item); if (sorted_) {qStableSort(items_.begin(), items_.end(), pointerItemLessThan);}} + void remove(int index) {items_.removeAt(index);} + int rowCount() {return items_.size();} + ChatListItem* item(int i) {return items_[i];} + int row(ChatListItem* item) {return items_.indexOf(item);} + QVariant data(int role) const {return (role == Qt::DisplayRole) ? name_ : QVariant();} + void clear() {items_.clear();} private: static bool pointerItemLessThan(const ChatListItem* first, const ChatListItem* second) { QString myName = first->data(Qt::DisplayRole).toString().toLower(); diff --git a/Swift/QtUI/ChatList/ChatListItem.h b/Swift/QtUI/ChatList/ChatListItem.h index e7be614..28c0f9c 100644 --- a/Swift/QtUI/ChatList/ChatListItem.h +++ b/Swift/QtUI/ChatList/ChatListItem.h @@ -13,10 +13,10 @@ namespace Swift { class ChatListGroupItem; class ChatListItem { public: - ChatListItem(ChatListGroupItem* parent) {parent_ = parent;}; + ChatListItem(ChatListGroupItem* parent) {parent_ = parent;} virtual ~ChatListItem() {} - ChatListGroupItem* parent() {return parent_;}; + ChatListGroupItem* parent() {return parent_;} virtual QVariant data(int role) const = 0; private: diff --git a/Swift/QtUI/ChatList/ChatListRecentItem.cpp b/Swift/QtUI/ChatList/ChatListRecentItem.cpp index 6c9807f..5a4d1e1 100644 --- a/Swift/QtUI/ChatList/ChatListRecentItem.cpp +++ b/Swift/QtUI/ChatList/ChatListRecentItem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Kevin Smith + * Copyright (c) 2011-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -7,6 +7,7 @@ #include <Swift/QtUI/ChatList/ChatListRecentItem.h> #include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/Base/Path.h> namespace Swift { ChatListRecentItem::ChatListRecentItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { @@ -25,7 +26,7 @@ QVariant ChatListRecentItem::data(int role) const { case Qt::BackgroundColorRole: return backgroundColor_; case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); case StatusTextRole: return statusText_;*/ - case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str())); + case AvatarRole: return QVariant(P2QSTRING(pathToString(chat_.avatarPath))); case PresenceIconRole: return getPresenceIcon(); default: return QVariant(); } 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/ChatList/ChatListWhiteboardItem.cpp b/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp index 41648b6..6791aa5 100644 --- a/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp +++ b/Swift/QtUI/ChatList/ChatListWhiteboardItem.cpp @@ -4,9 +4,16 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2013 Remko Tronçon + * Licensed under the GNU General Public License. + * See the COPYING file for more information. + */ + #include <Swift/QtUI/ChatList/ChatListWhiteboardItem.h> #include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/Base/Path.h> namespace Swift { ChatListWhiteboardItem::ChatListWhiteboardItem(const ChatListWindow::Chat& chat, ChatListGroupItem* parent) : ChatListItem(parent), chat_(chat) { @@ -25,7 +32,7 @@ namespace Swift { case Qt::BackgroundColorRole: return backgroundColor_; case Qt::ToolTipRole: return isContact() ? toolTipString() : QVariant(); case StatusTextRole: return statusText_;*/ - case AvatarRole: return QVariant(QString(chat_.avatarPath.string().c_str())); + case AvatarRole: return QVariant(P2QSTRING(pathToString(chat_.avatarPath))); case PresenceIconRole: return getPresenceIcon(); default: return QVariant(); } diff --git a/Swift/QtUI/ChatSnippet.cpp b/Swift/QtUI/ChatSnippet.cpp index ab31d29..3436531 100644 --- a/Swift/QtUI/ChatSnippet.cpp +++ b/Swift/QtUI/ChatSnippet.cpp @@ -1,12 +1,14 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ +#include <Swift/QtUI/ChatSnippet.h> + #include <QFile> -#include "ChatSnippet.h" +#include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { @@ -39,4 +41,58 @@ QString ChatSnippet::wrapResizable(const QString& text) { return "<span class='swift_resizable'>" + text + "</span>"; } -}; +QString ChatSnippet::directionToCSS(Direction direction) { + return direction == RTL ? QString("rtl") : QString("ltr"); +} + +ChatSnippet::Direction ChatSnippet::getDirection(const ChatWindow::ChatMessage& message) { + boost::shared_ptr<ChatWindow::ChatTextMessagePart> textPart; + std::string text = ""; + foreach (boost::shared_ptr<ChatWindow::ChatMessagePart> part, message.getParts()) { + if ((textPart = boost::dynamic_pointer_cast<ChatWindow::ChatTextMessagePart>(part))) { + text = textPart->text; + break; + } + } + return getDirection(text); +} + +ChatSnippet::Direction ChatSnippet::getDirection(const std::string& message) { + return getDirection(P2QSTRING(message)); +} + +ChatSnippet::Direction ChatSnippet::getDirection(const QString& message) { + /* + for (int i = 0; i < message.size(); ++i) { + switch (message.at(i).direction()) { + case QChar::DirL: + case QChar::DirLRE: + case QChar::DirLRO: + return ChatSnippet::LTR; + case QChar::DirR: + case QChar::DirAL: + case QChar::DirRLE: + case QChar::DirRLO: + return ChatSnippet::RTL; + case QChar::DirEN: + case QChar::DirES: + case QChar::DirET: + case QChar::DirAN: + case QChar::DirCS: + case QChar::DirB: + case QChar::DirWS: + case QChar::DirON: + case QChar::DirS: + case QChar::DirPDF: + case QChar::DirNSM: + case QChar::DirBN: + break; + } + } + return ChatSnippet::LTR; + */ + return message.isRightToLeft() ? ChatSnippet::RTL : ChatSnippet::LTR; +} + + +} diff --git a/Swift/QtUI/ChatSnippet.h b/Swift/QtUI/ChatSnippet.h index 78e0b88..f60d486 100644 --- a/Swift/QtUI/ChatSnippet.h +++ b/Swift/QtUI/ChatSnippet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -10,11 +10,20 @@ #include <QString> #include <QDateTime> -#include "QtChatTheme.h" + +#include <Swiften/Base/foreach.h> +#include <Swift/Controllers/UIInterfaces/ChatWindow.h> +#include <Swift/QtUI/QtChatTheme.h> + namespace Swift { class ChatSnippet { public: + enum Direction { + RTL, + LTR + }; + ChatSnippet(bool appendToPrevious); virtual ~ChatSnippet(); @@ -42,7 +51,13 @@ namespace Swift { static QString timeToEscapedString(const QDateTime& time); + static Direction getDirection(const std::string& message); + static Direction getDirection(const ChatWindow::ChatMessage& message); + static Direction getDirection(const QString& message); + protected: + static QString directionToCSS(Direction direction); + QString wrapResizable(const QString& text); void setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet> continuationFallback) { continuationFallback_ = continuationFallback; diff --git a/Swift/QtUI/EventViewer/EventDelegate.cpp b/Swift/QtUI/EventViewer/EventDelegate.cpp index 9ecdd34..c0904b3 100644 --- a/Swift/QtUI/EventViewer/EventDelegate.cpp +++ b/Swift/QtUI/EventViewer/EventDelegate.cpp @@ -25,12 +25,13 @@ QSize EventDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIn return QStyledItemDelegate::sizeHint(option, index); } switch (getEventType(item->getEvent())) { - case MessageEventType: return messageDelegate_.sizeHint(option, item); - case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item); - case ErrorEventType: return errorDelegate_.sizeHint(option, item); - case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item); - default: return QStyledItemDelegate::sizeHint(option, index); + case MessageEventType: return messageDelegate_.sizeHint(option, item); + case SubscriptionEventType: return subscriptionDelegate_.sizeHint(option, item); + case ErrorEventType: return errorDelegate_.sizeHint(option, item); + case MUCInviteEventType: return mucInviteDelegate_.sizeHint(option, item); } + assert(false); + return QSize(); } void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { @@ -40,23 +41,30 @@ void EventDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, return; } switch (getEventType(item->getEvent())) { - case MessageEventType: messageDelegate_.paint(painter, option, item);break; - case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break; - case ErrorEventType: errorDelegate_.paint(painter, option, item);break; - case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break; - default: QStyledItemDelegate::paint(painter, option, index); + case MessageEventType: messageDelegate_.paint(painter, option, item);break; + case SubscriptionEventType: subscriptionDelegate_.paint(painter, option, item);break; + case ErrorEventType: errorDelegate_.paint(painter, option, item);break; + case MUCInviteEventType: mucInviteDelegate_.paint(painter, option, item);break; } } EventType EventDelegate::getEventType(boost::shared_ptr<StanzaEvent> event) const { boost::shared_ptr<MessageEvent> messageEvent = boost::dynamic_pointer_cast<MessageEvent>(event); - if (messageEvent) return MessageEventType; + if (messageEvent) { + return MessageEventType; + } boost::shared_ptr<SubscriptionRequestEvent> subscriptionEvent = boost::dynamic_pointer_cast<SubscriptionRequestEvent>(event); - if (subscriptionEvent) return SubscriptionEventType; + if (subscriptionEvent) { + return SubscriptionEventType; + } boost::shared_ptr<ErrorEvent> errorEvent = boost::dynamic_pointer_cast<ErrorEvent>(event); - if (errorEvent) return ErrorEventType; + if (errorEvent) { + return ErrorEventType; + } boost::shared_ptr<MUCInviteEvent> mucInviteEvent = boost::dynamic_pointer_cast<MUCInviteEvent>(event); - if (mucInviteEvent) return MUCInviteEventType; + if (mucInviteEvent) { + return MUCInviteEventType; + } //I don't know what this is. assert(false); return MessageEventType; diff --git a/Swift/QtUI/EventViewer/QtEvent.cpp b/Swift/QtUI/EventViewer/QtEvent.cpp index 3c6f16c..c3ff944 100644 --- a/Swift/QtUI/EventViewer/QtEvent.cpp +++ b/Swift/QtUI/EventViewer/QtEvent.cpp @@ -7,6 +7,7 @@ #include "Swift/QtUI/EventViewer/QtEvent.h" #include <QDateTime> +#include <QColor> #include "Swift/Controllers/XMPPEvents/MessageEvent.h" #include "Swift/Controllers/XMPPEvents/ErrorEvent.h" @@ -25,8 +26,8 @@ QVariant QtEvent::data(int role) { switch (role) { case Qt::ToolTipRole: return QVariant(text()).toString() + "\n" + B2QDATE(event_->getTime()).toString(); case Qt::DisplayRole: return QVariant(text()); - case Qt::TextColorRole: return active_ ? Qt::black : Qt::darkGray; - case Qt::BackgroundColorRole: return active_ ? Qt::white : Qt::lightGray; + case Qt::TextColorRole: return QColor(active_ ? Qt::black : Qt::darkGray); + case Qt::BackgroundColorRole: return QColor(active_ ? Qt::white : Qt::lightGray); case SenderRole: return QVariant(sender()); /*case StatusTextRole: return statusText_; case AvatarRole: return avatar_; diff --git a/Swift/QtUI/EventViewer/QtEvent.h b/Swift/QtUI/EventViewer/QtEvent.h index f5e3dee..11efd60 100644 --- a/Swift/QtUI/EventViewer/QtEvent.h +++ b/Swift/QtUI/EventViewer/QtEvent.h @@ -17,7 +17,7 @@ namespace Swift { public: QtEvent(boost::shared_ptr<StanzaEvent> event, bool active); QVariant data(int role); - boost::shared_ptr<StanzaEvent> getEvent() { return event_; }; + boost::shared_ptr<StanzaEvent> getEvent() { return event_; } enum EventRoles { SenderRole = Qt::UserRole diff --git a/Swift/QtUI/FreeDesktopNotifier.cpp b/Swift/QtUI/FreeDesktopNotifier.cpp index 2393340..1f1ccda 100644 --- a/Swift/QtUI/FreeDesktopNotifier.cpp +++ b/Swift/QtUI/FreeDesktopNotifier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -13,6 +13,9 @@ #include <QStringList> #include <QtDBus/QtDBus> #include <algorithm> +#include <Swiften/Base/Path.h> + +#include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { @@ -43,7 +46,7 @@ void FreeDesktopNotifier::showMessage(Type type, const std::string& subject, con hints["x-canonical-append"] = QString("allowed"); msg << applicationName.c_str(); msg << quint32(0); // ID of previous notification to replace - msg << imageScaler.getScaledImage(picture, 48).string().c_str(); // Icon to display + msg << P2QSTRING(pathToString(imageScaler.getScaledImage(picture, 48))); // Icon to display msg << subject.c_str(); // Summary / Header of the message to display msg << body; // Body of the message to display msg << actions; // Actions from which the user may choose diff --git a/Swift/QtUI/FreeDesktopNotifier.h b/Swift/QtUI/FreeDesktopNotifier.h index 4ba02b4..6da7621 100644 --- a/Swift/QtUI/FreeDesktopNotifier.h +++ b/Swift/QtUI/FreeDesktopNotifier.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010 -2012 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -15,9 +15,8 @@ namespace Swift { FreeDesktopNotifier(const std::string& name); virtual void showMessage(Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function<void()> callback); - virtual void purgeCallbacks() { -#warning FIXME implement. - }; + virtual void purgeCallbacks() {} + private: std::string applicationName; QtCachedImageScaler imageScaler; diff --git a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp index 2fa24c2..7bd16e3 100644 --- a/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp +++ b/Swift/QtUI/MUCSearch/QtMUCSearchWindow.cpp @@ -23,7 +23,7 @@ namespace Swift { QtMUCSearchWindow::QtMUCSearchWindow() { ui_.setupUi(this); -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif setModal(true); diff --git a/Swift/QtUI/MessageSnippet.cpp b/Swift/QtUI/MessageSnippet.cpp index 47aa9f8..28c44c4 100644 --- a/Swift/QtUI/MessageSnippet.cpp +++ b/Swift/QtUI/MessageSnippet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -11,9 +11,9 @@ namespace Swift { -MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id) : ChatSnippet(appendToPrevious) { +MessageSnippet::MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id, Direction direction) : ChatSnippet(appendToPrevious) { if (appendToPrevious) { - setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new MessageSnippet(message, sender, time, iconURI, isIncoming, false, theme, id))); + setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new MessageSnippet(message, sender, time, iconURI, isIncoming, false, theme, id, direction))); } if (isIncoming) { if (appendToPrevious) { @@ -32,12 +32,13 @@ MessageSnippet::MessageSnippet(const QString& message, const QString& sender, co } } + content_.replace("%direction%", directionToCSS(direction)); content_.replace("%message%", wrapResizable("<span class='swift_message'>" + escape(message) + "</span><span class='swift_ack'></span><span class='swift_receipt'></span>")); content_.replace("%wrapped_sender%", wrapResizable(escape(sender))); content_.replace("%sender%", escape(sender)); content_.replace("%time%", wrapResizable("<span class='swift_time'>" + timeToEscapedString(time) + "</span>")); content_.replace("%userIconPath%", escape(iconURI)); - content_ = "<div id='" + id + "'>" + content_ + "</div>"; + content_ = QString("<div id='%1'>%2</div>").arg(id).arg(content_); content_ = "<span class='date" + time.date().toString(Qt::ISODate) + "'>" + content_ + "</span>"; } diff --git a/Swift/QtUI/MessageSnippet.h b/Swift/QtUI/MessageSnippet.h index c7425e9..8186d19 100644 --- a/Swift/QtUI/MessageSnippet.h +++ b/Swift/QtUI/MessageSnippet.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -15,7 +15,7 @@ class QDateTime; namespace Swift { class MessageSnippet : public ChatSnippet { public: - MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id); + MessageSnippet(const QString& message, const QString& sender, const QDateTime& time, const QString& iconURI, bool isIncoming, bool appendToPrevious, QtChatTheme* theme, const QString& id, Direction direction); virtual ~MessageSnippet(); const QString& getContent() const { return content_; @@ -23,7 +23,7 @@ namespace Swift { QString getContinuationElementID() const { return "insert"; - }; + } private: QString content_; diff --git a/Swift/QtUI/QtAboutWidget.cpp b/Swift/QtUI/QtAboutWidget.cpp index acdc61e..c00acf7 100644 --- a/Swift/QtUI/QtAboutWidget.cpp +++ b/Swift/QtUI/QtAboutWidget.cpp @@ -19,7 +19,7 @@ namespace Swift { QtAboutWidget::QtAboutWidget() : QDialog() { -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC setWindowTitle(QString(tr("About %1")).arg("Swift")); #endif setWindowIcon(QIcon(":/logo-icon-16.png")); diff --git a/Swift/QtUI/QtAffiliationEditor.cpp b/Swift/QtUI/QtAffiliationEditor.cpp index ed03c23..0896b92 100644 --- a/Swift/QtUI/QtAffiliationEditor.cpp +++ b/Swift/QtUI/QtAffiliationEditor.cpp @@ -76,4 +76,4 @@ MUCOccupant::Affiliation QtAffiliationEditor::affiliationFromIndex(int affiliati } } -}
\ No newline at end of file +} diff --git a/Swift/QtUI/QtAffiliationEditor.h b/Swift/QtUI/QtAffiliationEditor.h index 913b2cc..96536eb 100644 --- a/Swift/QtUI/QtAffiliationEditor.h +++ b/Swift/QtUI/QtAffiliationEditor.h @@ -34,4 +34,4 @@ namespace Swift { std::map<MUCOccupant::Affiliation, std::vector<JID> > affiliations_; std::vector<ChangePair> changes_; }; -}
\ No newline at end of file +} diff --git a/Swift/QtUI/QtAvatarWidget.cpp b/Swift/QtUI/QtAvatarWidget.cpp index f0bdf3c..015c2da 100644 --- a/Swift/QtUI/QtAvatarWidget.cpp +++ b/Swift/QtUI/QtAvatarWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011 Remko Tronçon + * Copyright (c) 2011-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -19,6 +19,7 @@ #include <QPainter> #include <QtSwiftUtil.h> +#include <Swiften/Base/Path.h> namespace Swift { @@ -68,6 +69,9 @@ void QtAvatarWidget::setAvatar(const ByteArray& data, const std::string& type) { } void QtAvatarWidget::mousePressEvent(QMouseEvent* event) { + if (!editable) { + return; + } QMenu menu; QAction* selectPicture = new QAction(tr("Select picture ..."), this); @@ -81,7 +85,7 @@ void QtAvatarWidget::mousePressEvent(QMouseEvent* event) { QString fileName = QFileDialog::getOpenFileName(this, tr("Select picture"), "", tr("Image Files (*.png *.jpg *.jpeg *.gif)")); if (!fileName.isEmpty()) { ByteArray data; - readByteArrayFromFile(data, Q2PSTRING(fileName)); + readByteArrayFromFile(data, stringToPath(Q2PSTRING(fileName))); QBuffer buffer; buffer.setData(reinterpret_cast<const char*>(vecptr(data)), data.size()); diff --git a/Swift/QtUI/QtAvatarWidget.h b/Swift/QtUI/QtAvatarWidget.h index 8830d82..f4ac4cf 100644 --- a/Swift/QtUI/QtAvatarWidget.h +++ b/Swift/QtUI/QtAvatarWidget.h @@ -15,6 +15,7 @@ class QLabel; namespace Swift { class QtAvatarWidget : public QWidget { Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable) public: QtAvatarWidget(QWidget* parent); @@ -28,9 +29,18 @@ namespace Swift { return type; } + void setEditable(bool b) { + editable = b; + } + + bool isEditable() const { + return editable; + } + void mousePressEvent(QMouseEvent* event); private: + bool editable; ByteArray data; std::string type; QLabel* label; diff --git a/Swift/QtUI/QtBlockListEditorWindow.cpp b/Swift/QtUI/QtBlockListEditorWindow.cpp new file mode 100644 index 0000000..63e8d1f --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QtBlockListEditorWindow.h> +#include <ui_QtBlockListEditorWindow.h> + +#include <boost/bind.hpp> + +#include <QLineEdit> +#include <QMovie> +#include <QShortcut> +#include <QStyledItemDelegate> +#include <QValidator> + +#include <Swift/QtUI/QtUtilities.h> +#include <Swiften/Client/ClientBlockListManager.h> +#include <Swiften/Base/foreach.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swiften/JID/JID.h> + +namespace Swift { + +class QtJIDValidator : public QValidator { + public: + QtJIDValidator(QObject* parent) : QValidator(parent) {} + virtual ~QtJIDValidator() {} + virtual QValidator::State validate(QString& input, int&) const { + return JID(Q2PSTRING(input)).isValid() ? Acceptable : Intermediate; + } +}; + +class QtJIDValidatedItemDelegate : public QItemDelegate { + public: + QtJIDValidatedItemDelegate(QObject* parent) : QItemDelegate(parent) {} + virtual ~QtJIDValidatedItemDelegate() {} + + virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem&, const QModelIndex&) const { + QLineEdit *editor = new QLineEdit(parent); + editor->setValidator(new QtJIDValidator(editor)); + return editor; + } + + void setEditorData(QWidget *editor, const QModelIndex &index) const { + QString value = index.model()->data(index, Qt::EditRole).toString(); + + QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); + lineEdit->setText(value); + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); + QString currentValue = lineEdit->text(); + int pos = 0; + if (lineEdit->validator()->validate(currentValue, pos) == QValidator::Acceptable) { + model->setData(index, lineEdit->text(), Qt::EditRole); + } else { + model->setData(index, QString(), Qt::EditRole); + } + } + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const { + editor->setGeometry(option.rect); + } +}; + +QtBlockListEditorWindow::QtBlockListEditorWindow() : QWidget(), ui(new Ui::QtBlockListEditorWindow) { + ui->setupUi(this); + new QShortcut(QKeySequence::Close, this, SLOT(close())); + ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); + + itemDelegate = new QtRemovableItemDelegate(style()); + + connect(ui->savePushButton, SIGNAL(clicked()), SLOT(applyChanges())); + + ui->blockListTreeWidget->setColumnCount(2); + ui->blockListTreeWidget->header()->setStretchLastSection(false); + int closeIconWidth = ui->blockListTreeWidget->fontMetrics().height(); + ui->blockListTreeWidget->header()->resizeSection(1, closeIconWidth); + +#if QT_VERSION >= 0x050000 + ui->blockListTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); +#else + ui->blockListTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch); +#endif + + ui->blockListTreeWidget->setHeaderHidden(true); + ui->blockListTreeWidget->setRootIsDecorated(false); + ui->blockListTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked); + ui->blockListTreeWidget->setItemDelegateForColumn(0, new QtJIDValidatedItemDelegate(this)); + ui->blockListTreeWidget->setItemDelegateForColumn(1, itemDelegate); + connect(ui->blockListTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int))); + + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->blockListTreeWidget->addTopLevelItem(item); +} + +QtBlockListEditorWindow::~QtBlockListEditorWindow() { +} + +void QtBlockListEditorWindow::show() { + QWidget::show(); + QWidget::activateWindow(); +} + +void QtBlockListEditorWindow::handleItemChanged(QTreeWidgetItem *, int) { + bool hasEmptyRow = false; + QList<QTreeWidgetItem*> rows = ui->blockListTreeWidget->findItems("", Qt::MatchFixedString); + foreach(QTreeWidgetItem* row, rows) { + if (row->text(0).isEmpty()) { + hasEmptyRow = true; + } + } + + if (!hasEmptyRow) { + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->blockListTreeWidget->addTopLevelItem(item); + } +} + +void QtBlockListEditorWindow::applyChanges() { + onSetNewBlockList(getCurrentBlockList()); +} + +void Swift::QtBlockListEditorWindow::setCurrentBlockList(const std::vector<JID> &blockedJIDs) { + ui->blockListTreeWidget->clear(); + + foreach(const JID& jid, blockedJIDs) { + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(jid.toString())) << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + ui->blockListTreeWidget->addTopLevelItem(item); + } + handleItemChanged(0,0); +} + +void Swift::QtBlockListEditorWindow::setBusy(bool isBusy) { + if (isBusy) { + ui->throbberLabel->movie()->start(); + ui->throbberLabel->show(); + } else { + ui->throbberLabel->movie()->stop(); + ui->throbberLabel->hide(); + } +} + +std::vector<JID> Swift::QtBlockListEditorWindow::getCurrentBlockList() const { + std::vector<JID> futureBlockedJIDs; + + for(int i=0; i < ui->blockListTreeWidget->topLevelItemCount(); ++i) { + QTreeWidgetItem* row = ui->blockListTreeWidget->topLevelItem(i); + if (!row->text(0).isEmpty()) { + futureBlockedJIDs.push_back(JID(Q2PSTRING(row->text(0)))); + } + } + return futureBlockedJIDs; +} + +} diff --git a/Swift/QtUI/QtBlockListEditorWindow.h b/Swift/QtUI/QtBlockListEditorWindow.h new file mode 100644 index 0000000..4b124a3 --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/BlockListEditorWidget.h> +#include <Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h> + +#include <QWidget> +#include <QTreeWidgetItem> + +namespace Ui { + class QtBlockListEditorWindow; +} + +namespace Swift { + +class QtBlockListEditorWindow : public QWidget, public BlockListEditorWidget { + Q_OBJECT + + public: + QtBlockListEditorWindow(); + virtual ~QtBlockListEditorWindow(); + + virtual void show(); + virtual void setCurrentBlockList(const std::vector<JID>& blockedJIDs); + virtual void setBusy(bool isBusy); + virtual std::vector<JID> getCurrentBlockList() const; + + private slots: + void handleItemChanged(QTreeWidgetItem*, int); + void applyChanges(); + + private: + Ui::QtBlockListEditorWindow* ui; + QtRemovableItemDelegate* itemDelegate; +}; + +} diff --git a/Swift/QtUI/QtBlockListEditorWindow.ui b/Swift/QtUI/QtBlockListEditorWindow.ui new file mode 100644 index 0000000..f71bbae --- /dev/null +++ b/Swift/QtUI/QtBlockListEditorWindow.ui @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtBlockListEditorWindow</class> + <widget class="QWidget" name="QtBlockListEditorWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>348</width> + <height>262</height> + </rect> + </property> + <property name="windowTitle"> + <string>Edit Block List</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="spacing"> + <number>5</number> + </property> + <property name="margin"> + <number>5</number> + </property> + <item> + <widget class="QTreeWidget" name="blockListTreeWidget"> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="errorLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="throbberLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::NoTextInteraction</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="savePushButton"> + <property name="text"> + <string>Save</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtCachedImageScaler.cpp b/Swift/QtUI/QtCachedImageScaler.cpp index 7307577..45375e7 100644 --- a/Swift/QtUI/QtCachedImageScaler.cpp +++ b/Swift/QtUI/QtCachedImageScaler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -8,6 +8,8 @@ #include <QImage> #include <boost/lexical_cast.hpp> +#include <Swiften/Base/Path.h> +#include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { @@ -15,16 +17,18 @@ QtCachedImageScaler::QtCachedImageScaler() { } boost::filesystem::path QtCachedImageScaler::getScaledImage(const boost::filesystem::path& imagePath, int size) { - boost::filesystem::path scaledImagePath(imagePath.string() + "." + boost::lexical_cast<std::string>(size)); + boost::filesystem::path scaledImagePath(imagePath); + std::string suffix = "." + boost::lexical_cast<std::string>(size); + scaledImagePath = stringToPath(pathToString(scaledImagePath) + suffix); if (!boost::filesystem::exists(scaledImagePath)) { - QImage image(imagePath.string().c_str()); + QImage image(P2QSTRING(pathToString(imagePath))); if (!image.isNull()) { if (image.width() > size || image.height() > size) { QImage scaledImage = image.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - scaledImage.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG"); + scaledImage.save(P2QSTRING(pathToString(scaledImagePath)), "PNG"); } else { - image.save(QString::fromUtf8(scaledImagePath.string().c_str()), "PNG"); + image.save(P2QSTRING(pathToString(scaledImagePath)), "PNG"); } } else { diff --git a/Swift/QtUI/QtChatTabs.cpp b/Swift/QtUI/QtChatTabs.cpp index d3a5676..a119043 100644 --- a/Swift/QtUI/QtChatTabs.cpp +++ b/Swift/QtUI/QtChatTabs.cpp @@ -22,8 +22,8 @@ #include <qdebug.h> namespace Swift { -QtChatTabs::QtChatTabs() : QWidget() { -#ifndef Q_WS_MAC +QtChatTabs::QtChatTabs(bool singleWindow) : QWidget(), singleWindow_(singleWindow) { +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/logo-chat-16.png")); #else setAttribute(Qt::WA_ShowWithoutActivating); @@ -46,7 +46,6 @@ QtChatTabs::QtChatTabs() : QWidget() { layout->setContentsMargins(0, 3, 0, 0); layout->addWidget(tabs_); setLayout(layout); - //resize(400, 300); } void QtChatTabs::closeEvent(QCloseEvent* event) { @@ -114,7 +113,13 @@ void QtChatTabs::handleTabClosing() { if (widget && ((index = tabs_->indexOf(widget)) >= 0)) { tabs_->removeTab(index); if (tabs_->count() == 0) { - hide(); + if (!singleWindow_) { + hide(); + } + else { + setWindowTitle(""); + onTitleChanged(""); + } } else { handleTabTitleUpdated(tabs_->currentWidget()); @@ -237,7 +242,7 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) { switch (tabbable->getWidgetAlertState()) { case QtTabbable::WaitingActivity : tabTextColor = QColor(217, 20, 43); break; case QtTabbable::ImpendingActivity : tabTextColor = QColor(27, 171, 32); break; - default : tabTextColor = QColor(); + case QtTabbable::NoActivity : tabTextColor = QColor(); break; } tabs_->tabBar()->setTabTextColor(index, tabTextColor); @@ -251,7 +256,9 @@ void QtChatTabs::handleTabTitleUpdated(QWidget* widget) { std::string current(Q2PSTRING(qobject_cast<QtTabbable*>(tabs_->currentWidget())->windowTitle())); ChatMessageSummarizer summary; - setWindowTitle(summary.getSummary(current, unreads).c_str()); + QString title = summary.getSummary(current, unreads).c_str(); + setWindowTitle(title); + emit onTitleChanged(title); } void QtChatTabs::flash() { @@ -270,7 +277,7 @@ void QtChatTabs::moveEvent(QMoveEvent*) { void QtChatTabs::checkForFirstShow() { if (!isVisible()) { -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC showMinimized(); #else /* https://bugreports.qt-project.org/browse/QTBUG-19194 diff --git a/Swift/QtUI/QtChatTabs.h b/Swift/QtUI/QtChatTabs.h index 233c574..f9cd685 100644 --- a/Swift/QtUI/QtChatTabs.h +++ b/Swift/QtUI/QtChatTabs.h @@ -17,12 +17,13 @@ namespace Swift { class QtChatTabs : public QWidget { Q_OBJECT public: - QtChatTabs(); + QtChatTabs(bool singleWindow); void addTab(QtTabbable* tab); void minimise(); QtTabbable* getCurrentTab(); signals: void geometryChanged(); + void onTitleChanged(const QString& title); protected slots: void closeEvent(QCloseEvent* event); @@ -44,6 +45,7 @@ namespace Swift { private: void checkForFirstShow(); QtTabWidget* tabs_; + bool singleWindow_; }; } diff --git a/Swift/QtUI/QtChatTheme.h b/Swift/QtUI/QtChatTheme.h index c6b02a0..f72a48b 100644 --- a/Swift/QtUI/QtChatTheme.h +++ b/Swift/QtUI/QtChatTheme.h @@ -13,20 +13,20 @@ namespace Swift { class QtChatTheme { public: QtChatTheme(const QString& themePath); - QString getHeader() const {return fileContents_[Header];}; - QString getFooter() const {return fileContents_[Footer];}; - QString getContent() const {return fileContents_[Content];}; - QString getStatus() const {return fileContents_[Status];}; - QString getTopic() const {return fileContents_[Topic];}; - QString getFileTransferRequest() const {return fileContents_[FileTransferRequest];}; - QString getIncomingContent() const {return fileContents_[IncomingContent];}; - QString getIncomingNextContent() const {return fileContents_[IncomingNextContent];}; - QString getIncomingContext() const {return fileContents_[IncomingContext];}; - QString getIncomingNextContext() const {return fileContents_[IncomingNextContext];}; - QString getOutgoingContent() const {return fileContents_[OutgoingContent];}; - QString getOutgoingNextContent() const {return fileContents_[OutgoingNextContent];}; - QString getOutgoingContext() const {return fileContents_[OutgoingContext];}; - QString getOutgoingNextContext() const {return fileContents_[OutgoingNextContext];}; + QString getHeader() const {return fileContents_[Header];} + QString getFooter() const {return fileContents_[Footer];} + QString getContent() const {return fileContents_[Content];} + QString getStatus() const {return fileContents_[Status];} + QString getTopic() const {return fileContents_[Topic];} + QString getFileTransferRequest() const {return fileContents_[FileTransferRequest];} + QString getIncomingContent() const {return fileContents_[IncomingContent];} + QString getIncomingNextContent() const {return fileContents_[IncomingNextContent];} + QString getIncomingContext() const {return fileContents_[IncomingContext];} + QString getIncomingNextContext() const {return fileContents_[IncomingNextContext];} + QString getOutgoingContent() const {return fileContents_[OutgoingContent];} + QString getOutgoingNextContent() const {return fileContents_[OutgoingNextContent];} + QString getOutgoingContext() const {return fileContents_[OutgoingContext];} + QString getOutgoingNextContext() const {return fileContents_[OutgoingNextContext];} QString getTemplate() const {return fileContents_[Template];} QString getMainCSS() const {return fileContents_[MainCSS];} QString getBase() const; diff --git a/Swift/QtUI/QtChatView.cpp b/Swift/QtUI/QtChatView.cpp index 81820a3..2536d39 100644 --- a/Swift/QtUI/QtChatView.cpp +++ b/Swift/QtUI/QtChatView.cpp @@ -41,7 +41,7 @@ QtChatView::QtChatView(QtChatTheme* theme, QWidget* parent, bool disableAutoScro connect(webView_, SIGNAL(clearRequested()), SLOT(handleClearRequested())); connect(webView_, SIGNAL(fontGrowRequested()), SLOT(increaseFontSize())); connect(webView_, SIGNAL(fontShrinkRequested()), SLOT(decreaseFontSize())); -#ifdef Q_WS_X11 +#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) /* To give a border on Linux, where it looks bad without */ QStackedWidget* stack = new QStackedWidget(this); stack->addWidget(webView_); @@ -128,7 +128,7 @@ void QtChatView::addMessageTop(boost::shared_ptr<ChatSnippet> snippet) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = firstElement_.findAll("span.swift_resizable"); - foreach (QWebElement span, spans) { + Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } @@ -142,6 +142,7 @@ QWebElement QtChatView::snippetToDOM(boost::shared_ptr<ChatSnippet> snippet) { } void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) { + //qDebug() << snippet->getContent(); rememberScrolledToBottom(); bool insert = snippet->getAppendToPrevious(); QWebElement continuationElement = lastElement_.findFirst("#insert"); @@ -160,10 +161,12 @@ void QtChatView::addToDOM(boost::shared_ptr<ChatSnippet> snippet) { double size = 1.0 + 0.2 * fontSizeSteps_; QString sizeString(QString().setNum(size, 'g', 3) + "em"); const QWebElementCollection spans = lastElement_.findAll("span.swift_resizable"); - foreach (QWebElement span, spans) { + Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } } + //qDebug() << "-----------------"; + //qDebug() << webPage_->mainFrame()->toHtml(); } void QtChatView::addLastSeenLine() { @@ -226,13 +229,13 @@ void QtChatView::replaceMessage(const QString& newMessage, const QString& id, co void QtChatView::showEmoticons(bool show) { { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_image"); - foreach (QWebElement span, spans) { + Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("display", show ? "inline" : "none"); } } { const QWebElementCollection spans = document_.findAll("span.swift_emoticon_text"); - foreach (QWebElement span, spans) { + Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("display", show ? "none" : "inline"); } } @@ -321,7 +324,7 @@ void QtChatView::resizeFont(int fontSizeSteps) { QString sizeString(QString().setNum(size, 'g', 3) + "em"); //qDebug() << "Setting to " << sizeString; const QWebElementCollection spans = document_.findAll("span.swift_resizable"); - foreach (QWebElement span, spans) { + Q_FOREACH (QWebElement span, spans) { span.setStyleProperty("font-size", sizeString); } webView_->setFontSizeIsMinimal(size == 1.0); @@ -365,9 +368,9 @@ void QtChatView::resetView() { connect(webPage_->mainFrame(), SIGNAL(contentsSizeChanged(const QSize&)), this, SLOT(handleFrameSizeChanged()), Qt::UniqueConnection); } -QWebElement findElementWithID(QWebElement document, QString elementName, QString id) { +static QWebElement findElementWithID(QWebElement document, QString elementName, QString id) { QWebElementCollection elements = document.findAll(elementName); - foreach(QWebElement element, elements) { + Q_FOREACH(QWebElement element, elements) { if (element.attribute("id") == id) { return element; } diff --git a/Swift/QtUI/QtChatWindow.cpp b/Swift/QtUI/QtChatWindow.cpp index 28549f8..d81de61 100644 --- a/Swift/QtUI/QtChatWindow.cpp +++ b/Swift/QtUI/QtChatWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -25,14 +25,17 @@ #include <Swift/Controllers/UIEvents/SendFileUIEvent.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include "QtChatWindowJSBridge.h" +#include "QtUtilities.h" #include <boost/cstdint.hpp> #include <boost/format.hpp> +#include <boost/smart_ptr/make_shared.hpp> #include <boost/lexical_cast.hpp> #include <QLabel> #include <qdebug.h> #include <QMessageBox> +#include <QMimeData> #include <QInputDialog> #include <QApplication> #include <QBoxLayout> @@ -45,6 +48,7 @@ #include <QTextEdit> #include <QTime> #include <QUrl> +#include <QToolButton> #include <QPushButton> #include <QFileDialog> #include <QMenu> @@ -64,7 +68,7 @@ const QString QtChatWindow::ButtonFileTransferAcceptRequest = QString("filetrans const QString QtChatWindow::ButtonMUCInvite = QString("mucinvite"); -QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), emoticons_(emoticons) { +QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings) : QtTabbable(), contact_(contact), previousMessageWasSelf_(false), previousMessageKind_(PreviosuMessageWasNone), eventStream_(eventStream), blockingState_(BlockingUnsupported) { settings_ = settings; unreadCount_ = 0; idCounter_ = 0; @@ -103,21 +107,18 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt alertLabel_->setStyleSheet(alertStyleSheet_); alertWidget_->hide(); - QBoxLayout* subjectLayout = new QBoxLayout(QBoxLayout::LeftToRight); + subjectLayout_ = new QBoxLayout(QBoxLayout::LeftToRight); subject_ = new QLineEdit(this); - subjectLayout->addWidget(subject_); + subjectLayout_->addWidget(subject_); setSubject(""); subject_->setReadOnly(true); - actionButton_ = new QPushButton(this); + QPushButton* actionButton_ = new QPushButton(this); actionButton_->setIcon(QIcon(":/icons/actions.png")); connect(actionButton_, SIGNAL(clicked()), this, SLOT(handleActionButtonClicked())); - subjectLayout->addWidget(actionButton_); - subject_->hide(); - actionButton_->hide(); - layout->addLayout(subjectLayout); + layout->addLayout(subjectLayout_); logRosterSplitter_ = new QSplitter(this); logRosterSplitter_->setAutoFillBackground(true); @@ -150,13 +151,19 @@ QtChatWindow::QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventSt QHBoxLayout* inputBarLayout = new QHBoxLayout(); inputBarLayout->setContentsMargins(0,0,0,0); inputBarLayout->setSpacing(2); - input_ = new QtTextEdit(this); + input_ = new QtTextEdit(settings_, this); input_->setAcceptRichText(false); inputBarLayout->addWidget(midBar_); inputBarLayout->addWidget(input_); correctingLabel_ = new QLabel(tr("Correcting"), this); inputBarLayout->addWidget(correctingLabel_); correctingLabel_->hide(); + + // using an extra layout to work around Qt margin glitches on OS X + QHBoxLayout* actionLayout = new QHBoxLayout(); + actionLayout->addWidget(actionButton_); + + inputBarLayout->addLayout(actionLayout); layout->addLayout(inputBarLayout); inputClearing_ = false; @@ -215,6 +222,15 @@ bool QtChatWindow::appendToPreviousCheck(QtChatWindow::PreviousMessageKind messa return previousMessageKind_ == messageKind && ((senderIsSelf && previousMessageWasSelf_) || (!senderIsSelf && !previousMessageWasSelf_&& previousSenderName_ == P2QSTRING(senderName))); } +ChatSnippet::Direction QtChatWindow::getActualDirection(const ChatMessage& message, Direction direction) { + if (direction == DefaultDirection) { + return QCoreApplication::translate("QApplication", "QT_LAYOUT_DIRECTION") == "RTL" ? ChatSnippet::RTL : ChatSnippet::LTR; + } + else { + return ChatSnippet::getDirection(message); + } +} + void QtChatWindow::handleFontResized(int fontSizeSteps) { messageLog_->resizeFont(fontSizeSteps); } @@ -413,7 +429,6 @@ void QtChatWindow::convertToMUC() { setAcceptDrops(false); treeWidget_->show(); subject_->show(); - actionButton_->show(); } void QtChatWindow::qAppFocusChanged(QWidget* /*old*/, QWidget* /*now*/) { @@ -481,28 +496,95 @@ void QtChatWindow::updateTitleWithUnreadCount() { emit titleUpdated(); } -std::string QtChatWindow::addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) { - return addMessage(linkimoticonify(P2QSTRING(message)), senderName, senderIsSelf, label, avatarPath, "", time); +std::string QtChatWindow::addMessage( + const ChatMessage& message, + const std::string& senderName, + bool senderIsSelf, + boost::shared_ptr<SecurityLabel> label, + const std::string& avatarPath, + const boost::posix_time::ptime& time, + const HighlightAction& highlight) { + return addMessage(chatMessageToHTML(message), senderName, senderIsSelf, label, avatarPath, "", time, highlight, ChatSnippet::getDirection(message)); +} + +QString QtChatWindow::chatMessageToHTML(const ChatMessage& message) { + QString result; + foreach (boost::shared_ptr<ChatMessagePart> part, message.getParts()) { + boost::shared_ptr<ChatTextMessagePart> textPart; + boost::shared_ptr<ChatURIMessagePart> uriPart; + boost::shared_ptr<ChatEmoticonMessagePart> emoticonPart; + boost::shared_ptr<ChatHighlightingMessagePart> highlightPart; + + if ((textPart = boost::dynamic_pointer_cast<ChatTextMessagePart>(part))) { + QString text = QtUtilities::htmlEscape(P2QSTRING(textPart->text)); + text.replace("\n","<br/>"); + result += text; + continue; + } + if ((uriPart = boost::dynamic_pointer_cast<ChatURIMessagePart>(part))) { + QString uri = QtUtilities::htmlEscape(P2QSTRING(uriPart->target)); + result += "<a href='" + uri + "' >" + uri + "</a>"; + continue; + } + if ((emoticonPart = boost::dynamic_pointer_cast<ChatEmoticonMessagePart>(part))) { + QString textStyle = showEmoticons_ ? "style='display:none'" : ""; + QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; + result += "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + P2QSTRING(emoticonPart->imagePath) + "'/></span><span class='swift_emoticon_text' " + textStyle + ">" + QtUtilities::htmlEscape(P2QSTRING(emoticonPart->alternativeText)) + "</span>"; + continue; + } + if ((highlightPart = boost::dynamic_pointer_cast<ChatHighlightingMessagePart>(part))) { + //FIXME: Maybe do something here. Anything, really. + continue; + } + + } + return result; +} + +/*QString QtChatWindow::linkimoticonify(const std::string& message) const { + return linkimoticonify(P2QSTRING(message)); } QString QtChatWindow::linkimoticonify(const QString& message) const { QString messageHTML(message); - messageHTML = Qt::escape(messageHTML); + messageHTML = QtUtilities::htmlEscape(messageHTML); QMapIterator<QString, QString> it(emoticons_); - QString textStyle = showEmoticons_ ? "style='display:none'" : ""; - QString imageStyle = showEmoticons_ ? "" : "style='display:none'"; + if (messageHTML.length() < 500) { while (it.hasNext()) { it.next(); - messageHTML.replace(it.key(), "<span class='swift_emoticon_image' " + imageStyle + "><img src='" + it.value() + "'/></span><span class='swift_emoticon_text' " + textStyle + ">"+it.key() + "</span>"); + messageHTML.replace(it.key(), ); } messageHTML = P2QSTRING(Linkify::linkify(Q2PSTRING(messageHTML))); } messageHTML.replace("\n","<br/>"); return messageHTML; +}*/ + +QString QtChatWindow::getHighlightSpanStart(const HighlightAction& highlight) { + QString color = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextColor())); + QString background = QtUtilities::htmlEscape(P2QSTRING(highlight.getTextBackground())); + if (color.isEmpty()) { + color = "black"; + } + if (background.isEmpty()) { + background = "yellow"; + } + + return QString("<span style=\"color: %1; background: %2\">").arg(color).arg(background); } -std::string QtChatWindow::addMessage(const QString &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time) { +std::string QtChatWindow::addMessage( + const QString& message, + const std::string& senderName, + bool senderIsSelf, + boost::shared_ptr<SecurityLabel> label, + const std::string& avatarPath, + const QString& style, + const boost::posix_time::ptime& time, + const HighlightAction& highlight, + ChatSnippet::Direction direction) { + if (isWidgetSelected()) { onAllMessagesRead(); } @@ -510,13 +592,15 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string & QString htmlString; if (label) { - htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(Qt::escape(P2QSTRING(label->getForegroundColor()))).arg(Qt::escape(P2QSTRING(label->getBackgroundColor()))); - htmlString += QString("%1</span> ").arg(Qt::escape(P2QSTRING(label->getDisplayMarking()))); + htmlString = QString("<span style=\"border: thin dashed grey; padding-left: .5em; padding-right: .5em; color: %1; background-color: %2; font-size: 90%; margin-right: .5em; \" class='swift_label'>").arg(QtUtilities::htmlEscape(P2QSTRING(label->getForegroundColor()))).arg(QtUtilities::htmlEscape(P2QSTRING(label->getBackgroundColor()))); + htmlString += QString("%1</span> ").arg(QtUtilities::htmlEscape(P2QSTRING(label->getDisplayMarking()))); } - QString messageHTML(message); + QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; QString styleSpanEnd = style == "" ? "" : "</span>"; - htmlString += "<span class='swift_inner_message'>" + styleSpanStart + messageHTML + styleSpanEnd + "</span>" ; + QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; + QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; + htmlString += "<span class='swift_inner_message'>" + styleSpanStart + highlightSpanStart + message + highlightSpanEnd + styleSpanEnd + "</span>" ; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMessage, senderName, senderIsSelf); if (lastLineTracker_.getShouldMoveLastLine()) { @@ -527,7 +611,7 @@ std::string QtChatWindow::addMessage(const QString &message, const std::string & } QString qAvatarPath = scaledAvatarPath.isEmpty() ? "qrc:/icons/avatar.png" : QUrl::fromLocalFile(scaledAvatarPath).toEncoded(); std::string id = "id" + boost::lexical_cast<std::string>(idCounter_++); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); + messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(time), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), direction)); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); @@ -572,10 +656,11 @@ int QtChatWindow::getCount() { return unreadCount_; } -std::string QtChatWindow::addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time) { - return addMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time); +std::string QtChatWindow::addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight) { + return addMessage(" *" + chatMessageToHTML(message) + "*", senderName, senderIsSelf, label, avatarPath, "font-style:italic ", time, highlight, ChatSnippet::getDirection(message)); } +// FIXME: Move this to a different file std::string formatSize(const boost::uintmax_t bytes) { static const char *siPrefix[] = {"k", "M", "G", "T", "P", "E", "Z", "Y", NULL}; int power = 0; @@ -587,11 +672,11 @@ std::string formatSize(const boost::uintmax_t bytes) { return str( boost::format("%.1lf %sB") % engBytes % (power > 0 ? siPrefix[power-1] : "") ); } -QString encodeButtonArgument(const QString& str) { - return Qt::escape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); +static QString encodeButtonArgument(const QString& str) { + return QtUtilities::htmlEscape(P2QSTRING(Base64::encode(createByteArray(Q2PSTRING(str))))); } -QString decodeButtonArgument(const QString& str) { +static QString decodeButtonArgument(const QString& str) { return P2QSTRING(byteArrayToString(Base64::decode(Q2PSTRING(str)))); } @@ -606,11 +691,13 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se SWIFT_LOG(debug) << "addFileTransfer" << std::endl; QString ft_id = QString("ft%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); + QString actionText; QString htmlString; QString formattedFileSize = P2QSTRING(formatSize(sizeInBytes)); if (senderIsSelf) { // outgoing - htmlString = tr("Send file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + + actionText = tr("Send file"); + htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + "<div id='" + ft_id + "'>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Set Description"), ButtonFileTransferSetDescription, ft_id) + @@ -618,7 +705,8 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se "</div>"; } else { // incoming - htmlString = tr("Receiving file") + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + + actionText = tr("Receiving file"); + htmlString = actionText + ": " + P2QSTRING(filename) + " ( " + formattedFileSize + ") <br/>" + "<div id='" + ft_id + "'>" + buildChatWindowButton(tr("Cancel"), ButtonFileTransferCancel, ft_id) + buildChatWindowButton(tr("Accept"), ButtonFileTransferAcceptRequest, ft_id, P2QSTRING(filename)) + @@ -636,7 +724,7 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "ftmessage" + boost::lexical_cast<std::string>(idCounter_++); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); + messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText))); previousMessageWasSelf_ = senderIsSelf; previousSenderName_ = P2QSTRING(senderName); @@ -645,22 +733,25 @@ std::string QtChatWindow::addFileTransfer(const std::string& senderName, bool se } void QtChatWindow::setFileTransferProgress(std::string id, const int percentageDone) { - messageLog_->setFileTransferProgress(QString::fromStdString(id), percentageDone); + messageLog_->setFileTransferProgress(P2QSTRING(id), percentageDone); } void QtChatWindow::setFileTransferStatus(std::string id, const FileTransferState state, const std::string& msg) { - messageLog_->setFileTransferStatus(QString::fromStdString(id), state, QString::fromStdString(msg)); + messageLog_->setFileTransferStatus(P2QSTRING(id), state, P2QSTRING(msg)); } std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { QString wb_id = QString("wb%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); QString htmlString; + QString actionText; if (senderIsSelf) { - htmlString = "<div id='" + wb_id + "'>" + tr("Starting whiteboard chat") + "<br />"+ + actionText = tr("Starting whiteboard chat"); + htmlString = "<div id='" + wb_id + "'>" + actionText + "<br />"+ buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + "</div>"; } else { - htmlString = "<div id='" + wb_id + "'>" + tr("%1 would like to start a whiteboard chat").arg(Qt::escape(contact_)) + ": <br/>" + + actionText = tr("%1 would like to start a whiteboard chat"); + htmlString = "<div id='" + wb_id + "'>" + actionText.arg(QtUtilities::htmlEscape(contact_)) + ": <br/>" + buildChatWindowButton(tr("Cancel"), ButtonWhiteboardSessionCancel, wb_id) + buildChatWindowButton(tr("Accept"), ButtonWhiteboardSessionAcceptRequest, wb_id) + "</div>"; @@ -674,7 +765,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { } QString qAvatarPath = "qrc:/icons/avatar.png"; std::string id = "wbmessage" + boost::lexical_cast<std::string>(idCounter_++); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id)))); + messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(contact_), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, false, theme_, P2QSTRING(id), ChatSnippet::getDirection(actionText))); previousMessageWasSelf_ = false; previousSenderName_ = contact_; @@ -682,7 +773,7 @@ std::string QtChatWindow::addWhiteboardRequest(bool senderIsSelf) { } void QtChatWindow::setWhiteboardSessionStatus(std::string id, const ChatWindow::WhiteboardSessionState state) { - messageLog_->setWhiteboardSessionStatus(QString::fromStdString(id), state); + messageLog_->setWhiteboardSessionStatus(P2QSTRING(id), state); } void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, QString encodedArgument2, QString encodedArgument3) { @@ -719,12 +810,12 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, } else if (id.startsWith(ButtonWhiteboardSessionAcceptRequest)) { QString id = arg1; - messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardAccepted); + messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardAccepted); onWhiteboardSessionAccept(); } else if (id.startsWith(ButtonWhiteboardSessionCancel)) { QString id = arg1; - messageLog_->setWhiteboardSessionStatus(QString::fromStdString(Q2PSTRING(id)), ChatWindow::WhiteboardTerminated); + messageLog_->setWhiteboardSessionStatus(id, ChatWindow::WhiteboardTerminated); onWhiteboardSessionCancel(); } else if (id.startsWith(ButtonWhiteboardShowWindow)) { @@ -744,40 +835,39 @@ void QtChatWindow::handleHTMLButtonClicked(QString id, QString encodedArgument1, } } -void QtChatWindow::addErrorMessage(const std::string& errorMessage) { +void QtChatWindow::addErrorMessage(const ChatMessage& errorMessage) { if (isWidgetSelected()) { onAllMessagesRead(); } - QString errorMessageHTML(Qt::escape(P2QSTRING(errorMessage))); - errorMessageHTML.replace("\n","<br/>"); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_))); + QString errorMessageHTML(chatMessageToHTML(errorMessage)); + + messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>("<span class=\"error\">" + errorMessageHTML + "</span>", QDateTime::currentDateTime(), false, theme_, ChatSnippet::getDirection(errorMessage))); previousMessageWasSelf_ = false; previousMessageKind_ = PreviousMessageWasSystem; } -void QtChatWindow::addSystemMessage(const std::string& message) { +void QtChatWindow::addSystemMessage(const ChatMessage& message, Direction direction) { if (isWidgetSelected()) { onAllMessagesRead(); } - QString messageHTML(P2QSTRING(message)); - messageHTML = linkimoticonify(messageHTML); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); + QString messageHTML = chatMessageToHTML(message); + messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction))); previousMessageKind_ = PreviousMessageWasSystem; } -void QtChatWindow::replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { - replaceMessage(" *" + linkimoticonify(P2QSTRING(message)) + "*", id, time, "font-style:italic "); +void QtChatWindow::replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { + replaceMessage(" *" + chatMessageToHTML(message) + "*", id, time, "font-style:italic ", highlight); } -void QtChatWindow::replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time) { - replaceMessage(linkimoticonify(P2QSTRING(message)), id, time, ""); +void QtChatWindow::replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight) { + replaceMessage(chatMessageToHTML(message), id, time, "", highlight); } -void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style) { +void QtChatWindow::replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style, const HighlightAction& highlight) { if (!id.empty()) { if (isWidgetSelected()) { onAllMessagesRead(); @@ -787,7 +877,9 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id, QString styleSpanStart = style == "" ? "" : "<span style=\"" + style + "\">"; QString styleSpanEnd = style == "" ? "" : "</span>"; - messageHTML = styleSpanStart + messageHTML + styleSpanEnd; + QString highlightSpanStart = highlight.highlightText() ? getHighlightSpanStart(highlight) : ""; + QString highlightSpanEnd = highlight.highlightText() ? "</span>" : ""; + messageHTML = styleSpanStart + highlightSpanStart + messageHTML + highlightSpanEnd + styleSpanEnd; messageLog_->replaceMessage(messageHTML, P2QSTRING(id), B2QDATE(time)); } @@ -796,14 +888,13 @@ void QtChatWindow::replaceMessage(const QString& message, const std::string& id, } } -void QtChatWindow::addPresenceMessage(const std::string& message) { +void QtChatWindow::addPresenceMessage(const ChatMessage& message, Direction direction) { if (isWidgetSelected()) { onAllMessagesRead(); } - QString messageHTML(P2QSTRING(message)); - messageHTML = linkimoticonify(messageHTML); - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(messageHTML, QDateTime::currentDateTime(), false, theme_))); + QString messageHTML = chatMessageToHTML(message); + messageLog_->addMessageBottom(boost::make_shared<SystemMessageSnippet>(messageHTML, QDateTime::currentDateTime(), false, theme_, getActualDirection(message, direction))); previousMessageKind_ = PreviousMessageWasPresence; } @@ -874,12 +965,15 @@ void QtChatWindow::dropEvent(QDropEvent *event) { if (event->mimeData()->urls().size() == 1) { onSendFileRequest(Q2PSTRING(event->mimeData()->urls().at(0).toLocalFile())); } else { - addSystemMessage("Sending of multiple files at once isn't supported at this time."); + std::string messageText(Q2PSTRING(tr("Sending of multiple files at once isn't supported at this time."))); + ChatMessage message; + message.append(boost::make_shared<ChatTextMessagePart>(messageText)); + addSystemMessage(message, DefaultDirection); } } -void QtChatWindow::replaceLastMessage(const std::string& message) { - messageLog_->replaceLastMessage(linkimoticonify(P2QSTRING(message))); +void QtChatWindow::replaceLastMessage(const ChatMessage& message) { + messageLog_->replaceLastMessage(chatMessageToHTML(message)); } void QtChatWindow::setAvailableOccupantActions(const std::vector<OccupantAction>& actions) { @@ -901,15 +995,26 @@ void QtChatWindow::handleActionButtonClicked() { QAction* destroy = NULL; QAction* invite = NULL; - foreach(ChatWindow::RoomAction availableAction, availableRoomActions_) - { - switch(availableAction) + QAction* block = NULL; + QAction* unblock = NULL; + + if (availableRoomActions_.empty()) { + if (blockingState_ == IsBlocked) { + unblock = contextMenu.addAction(tr("Unblock")); + } else if (blockingState_ == IsUnblocked) { + block = contextMenu.addAction(tr("Block")); + } + } else { + foreach(ChatWindow::RoomAction availableAction, availableRoomActions_) { - case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; - case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; - case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; - case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; - case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; + switch(availableAction) + { + case ChatWindow::ChangeSubject: changeSubject = contextMenu.addAction(tr("Change subject…")); break; + case ChatWindow::Configure: configure = contextMenu.addAction(tr("Configure room…")); break; + case ChatWindow::Affiliations: affiliations = contextMenu.addAction(tr("Edit affiliations…")); break; + case ChatWindow::Destroy: destroy = contextMenu.addAction(tr("Destroy room")); break; + case ChatWindow::Invite: invite = contextMenu.addAction(tr("Invite person to this room…")); break; + } } } @@ -949,6 +1054,12 @@ void QtChatWindow::handleActionButtonClicked() { else if (result == invite) { onInvitePersonToThisMUCRequest(); } + else if (result == block) { + onBlockUserRequest(); + } + else if (result == unblock) { + onUnblockUserRequest(); + } } void QtChatWindow::handleAffiliationEditorAccepted() { @@ -960,11 +1071,14 @@ void QtChatWindow::setAffiliations(MUCOccupant::Affiliation affiliation, const s affiliationEditor_->setAffiliations(affiliation, jids); } -void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction> &actions) -{ +void QtChatWindow::setAvailableRoomActions(const std::vector<RoomAction>& actions) { availableRoomActions_ = actions; } +void QtChatWindow::setBlockingState(BlockingState state) { + blockingState_ = state; +} + void QtChatWindow::showRoomConfigurationForm(Form::ref form) { if (mucConfigurationWindow_) { delete mucConfigurationWindow_.data(); @@ -979,18 +1093,20 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji onAllMessagesRead(); } - QString htmlString = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + " <br/>"; + QString message = QObject::tr("You've been invited to enter the %1 room.").arg(P2QSTRING(jid.toString())) + "\n"; + QString htmlString = message; if (!reason.empty()) { - htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "<br/>"; + htmlString += QObject::tr("Reason: %1").arg(P2QSTRING(reason)) + "\n"; } if (!direct) { - htmlString += QObject::tr("This person may not have really sent this invitation!") + "<br/>"; + htmlString += QObject::tr("This person may not have really sent this invitation!") + "\n"; } + htmlString = chatMessageToHTML(ChatMessage(Q2PSTRING(htmlString))); - QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); + QString id = QString(ButtonMUCInvite + "%1").arg(P2QSTRING(boost::lexical_cast<std::string>(idCounter_++))); htmlString += "<div id='" + id + "'>" + - buildChatWindowButton(tr("Accept Invite"), ButtonMUCInvite, Qt::escape(P2QSTRING(jid.toString())), Qt::escape(P2QSTRING(password)), id) + + buildChatWindowButton(chatMessageToHTML(ChatMessage(Q2PSTRING((tr("Accept Invite"))))), ButtonMUCInvite, QtUtilities::htmlEscape(P2QSTRING(jid.toString())), QtUtilities::htmlEscape(P2QSTRING(password)), id) + "</div>"; bool appendToPrevious = appendToPreviousCheck(PreviousMessageWasMUCInvite, senderName, false); @@ -1002,7 +1118,7 @@ void QtChatWindow::addMUCInvitation(const std::string& senderName, const JID& ji } QString qAvatarPath = "qrc:/icons/avatar.png"; - messageLog_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(htmlString, Qt::escape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id))); + messageLog_->addMessageBottom(boost::make_shared<MessageSnippet>(htmlString, QtUtilities::htmlEscape(P2QSTRING(senderName)), B2QDATE(boost::posix_time::second_clock::local_time()), qAvatarPath, false, appendToPrevious, theme_, id, ChatSnippet::getDirection(message))); previousMessageWasSelf_ = false; previousSenderName_ = P2QSTRING(senderName); previousMessageKind_ = PreviousMessageWasMUCInvite; diff --git a/Swift/QtUI/QtChatWindow.h b/Swift/QtUI/QtChatWindow.h index 3416b42..a29edad 100644 --- a/Swift/QtUI/QtChatWindow.h +++ b/Swift/QtUI/QtChatWindow.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -10,6 +10,7 @@ #include <Swift/QtUI/QtMUCConfigurationWindow.h> #include <Swift/QtUI/QtAffiliationEditor.h> #include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/ChatSnippet.h> #include <QtTabbable.h> @@ -37,6 +38,9 @@ namespace Swift { class QtChatWindowJSBridge; class SettingsProvider; + // FIXME: Move this to a different file + std::string formatSize(const boost::uintmax_t bytes); + class LabelModel : public QAbstractListModel { Q_OBJECT public: @@ -83,15 +87,17 @@ namespace Swift { static const QString ButtonMUCInvite; public: - QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings, QMap<QString, QString> emoticons); + QtChatWindow(const QString &contact, QtChatTheme* theme, UIEventStream* eventStream, SettingsProvider* settings); ~QtChatWindow(); - std::string addMessage(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time); - std::string addAction(const std::string &message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time); - void addSystemMessage(const std::string& message); - void addPresenceMessage(const std::string& message); - void addErrorMessage(const std::string& errorMessage); - void replaceMessage(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); - void replaceWithAction(const std::string& message, const std::string& id, const boost::posix_time::ptime& time); + std::string addMessage(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); + std::string addAction(const ChatMessage& message, const std::string &senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const boost::posix_time::ptime& time, const HighlightAction& highlight); + + void addSystemMessage(const ChatMessage& message, Direction direction); + void addPresenceMessage(const ChatMessage& message, Direction direction); + void addErrorMessage(const ChatMessage& message); + + void replaceMessage(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); + void replaceWithAction(const ChatMessage& message, const std::string& id, const boost::posix_time::ptime& time, const HighlightAction& highlight); // File transfer related stuff std::string addFileTransfer(const std::string& senderName, bool senderIsSelf, const std::string& filename, const boost::uintmax_t sizeInBytes); void setFileTransferProgress(std::string id, const int percentageDone); @@ -116,7 +122,7 @@ namespace Swift { void setRosterModel(Roster* roster); void setTabComplete(TabComplete* completer); int getCount(); - void replaceLastMessage(const std::string& message); + void replaceLastMessage(const ChatMessage& message); void setAckState(const std::string& id, AckState state); // message receipts @@ -129,7 +135,8 @@ namespace Swift { void showRoomConfigurationForm(Form::ref); void addMUCInvitation(const std::string& senderName, const JID& jid, const std::string& reason, const std::string& password, bool direct = true); void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&); - void setAvailableRoomActions(const std::vector<RoomAction> &actions); + void setAvailableRoomActions(const std::vector<RoomAction>& actions); + void setBlockingState(BlockingState state); InviteToChatWindow* createInviteToChatWindow(); @@ -189,11 +196,27 @@ namespace Swift { void beginCorrection(); void cancelCorrection(); void handleSettingChanged(const std::string& setting); - std::string addMessage(const QString& message, const std::string& senderName, bool senderIsSelf, boost::shared_ptr<SecurityLabel> label, const std::string& avatarPath, const QString& style, const boost::posix_time::ptime& time); - void replaceMessage(const QString& message, const std::string& id, const boost::posix_time::ptime& time, const QString& style); + std::string addMessage( + const QString& message, + const std::string& senderName, + bool senderIsSelf, + boost::shared_ptr<SecurityLabel> label, + const std::string& avatarPath, + const QString& style, + const boost::posix_time::ptime& time, + const HighlightAction& highlight, + ChatSnippet::Direction direction); + void replaceMessage( + const QString& message, + const std::string& id, + const boost::posix_time::ptime& time, + const QString& style, + const HighlightAction& highlight); void handleOccupantSelectionChanged(RosterItem* item); bool appendToPreviousCheck(PreviousMessageKind messageKind, const std::string& senderName, bool senderIsSelf) const; - QString linkimoticonify(const QString& message) const; + static ChatSnippet::Direction getActualDirection(const ChatMessage& message, Direction direction); + QString chatMessageToHTML(const ChatMessage& message); + QString getHighlightSpanStart(const HighlightAction& highlight); int unreadCount_; bool contactIsTyping_; @@ -205,6 +228,7 @@ namespace Swift { QtChatTheme* theme_; QtTextEdit* input_; QWidget* midBar_; + QBoxLayout* subjectLayout_; QComboBox* labelsWidget_; QtOccupantListWidget* treeWidget_; QLabel* correctingLabel_; @@ -213,7 +237,6 @@ namespace Swift { QPushButton* alertButton_; TabComplete* completer_; QLineEdit* subject_; - QPushButton* actionButton_; bool isCorrection_; bool previousMessageWasSelf_; PreviousMessageKind previousMessageKind_; @@ -232,9 +255,9 @@ namespace Swift { int idCounter_; SettingsProvider* settings_; std::vector<ChatWindow::RoomAction> availableRoomActions_; - QMap<QString, QString> emoticons_; bool showEmoticons_; QPalette defaultLabelsPalette_; LabelModel* labelModel_; + BlockingState blockingState_; }; } diff --git a/Swift/QtUI/QtChatWindowFactory.cpp b/Swift/QtUI/QtChatWindowFactory.cpp index 5f91ff8..78c04c9 100644 --- a/Swift/QtUI/QtChatWindowFactory.cpp +++ b/Swift/QtUI/QtChatWindowFactory.cpp @@ -4,23 +4,24 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "QtChatWindowFactory.h" +#include <Swift/QtUI/QtChatWindowFactory.h> #include <QDesktopWidget> - -#include "QtChatTabs.h" -#include "QtChatWindow.h" -#include "QtSwiftUtil.h" -#include "QtChatTheme.h" #include <qdebug.h> +#include <Swift/QtUI/QtChatTabs.h> +#include <Swift/QtUI/QtChatWindow.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtChatTheme.h> +#include <Swift/QtUI/QtSingleWindow.h> + namespace Swift { static const QString SPLITTER_STATE = "mucSplitterState"; static const QString CHAT_TABS_GEOMETRY = "chatTabsGeometry"; -QtChatWindowFactory::QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap<QString, QString> emoticons) : themePath_(themePath), emoticons_(emoticons) { +QtChatWindowFactory::QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath) : themePath_(themePath) { qtOnlySettings_ = qtSettings; settings_ = settings; tabs_ = tabs; @@ -49,7 +50,7 @@ ChatWindow* QtChatWindowFactory::createChatWindow(const JID &contact,UIEventStre } } - QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_, emoticons_); + QtChatWindow *chatWindow = new QtChatWindow(P2QSTRING(contact.toString()), theme_, eventStream, settings_); connect(chatWindow, SIGNAL(splitterMoved()), this, SLOT(handleSplitterMoved())); connect(this, SIGNAL(changeSplitterState(QByteArray)), chatWindow, SLOT(handleChangeSplitterState(QByteArray))); diff --git a/Swift/QtUI/QtChatWindowFactory.h b/Swift/QtUI/QtChatWindowFactory.h index 4f59961..63da514 100644 --- a/Swift/QtUI/QtChatWindowFactory.h +++ b/Swift/QtUI/QtChatWindowFactory.h @@ -6,21 +6,24 @@ #pragma once -#include "Swift/Controllers/UIInterfaces/ChatWindowFactory.h" -#include "Swiften/JID/JID.h" -#include "QtSettingsProvider.h" +#include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <QObject> #include <QSplitter> + +#include <Swiften/JID/JID.h> +#include <Swift/QtUI/QtSettingsProvider.h> + namespace Swift { class QtChatTabs; class QtChatTheme; class UIEventStream; class QtUIPreferences; + class QtSingleWindow; class QtChatWindowFactory : public QObject, public ChatWindowFactory { Q_OBJECT public: - QtChatWindowFactory(QSplitter* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath, QMap<QString, QString> emoticons); + QtChatWindowFactory(QtSingleWindow* splitter, SettingsProvider* settings, QtSettingsProvider* qtSettings, QtChatTabs* tabs, const QString& themePath); ~QtChatWindowFactory(); ChatWindow* createChatWindow(const JID &contact, UIEventStream* eventStream); signals: @@ -34,7 +37,6 @@ namespace Swift { QtSettingsProvider* qtOnlySettings_; QtChatTabs* tabs_; QtChatTheme* theme_; - QMap<QString, QString> emoticons_; }; } diff --git a/Swift/QtUI/QtColorToolButton.cpp b/Swift/QtUI/QtColorToolButton.cpp new file mode 100644 index 0000000..1d379a3 --- /dev/null +++ b/Swift/QtUI/QtColorToolButton.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QColorDialog> +#include <QPainter> + +#include <Swift/QtUI/QtColorToolButton.h> + +namespace Swift { + +QtColorToolButton::QtColorToolButton(QWidget* parent) : + QToolButton(parent) +{ + connect(this, SIGNAL(clicked()), SLOT(onClicked())); + setColorIcon(Qt::transparent); +} + +void QtColorToolButton::setColor(const QColor& color) +{ + if (color.isValid() != color_.isValid() || (color.isValid() && color != color_)) { + color_ = color; + setColorIcon(color_); + emit colorChanged(color_); + } +} + +void QtColorToolButton::onClicked() +{ + QColor c = QColorDialog::getColor(color_, this); + if (c.isValid()) { + setColor(c); + } +} + +void QtColorToolButton::setColorIcon(const QColor& color) +{ + QPixmap pix(iconSize()); + pix.fill(color.isValid() ? color : Qt::transparent); + setIcon(pix); +} + +} diff --git a/Swift/QtUI/QtColorToolButton.h b/Swift/QtUI/QtColorToolButton.h new file mode 100644 index 0000000..33d195d --- /dev/null +++ b/Swift/QtUI/QtColorToolButton.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QToolButton> + +namespace Swift { + + class QtColorToolButton : public QToolButton { + Q_OBJECT + Q_PROPERTY(QColor color READ getColor WRITE setColor NOTIFY colorChanged) + public: + explicit QtColorToolButton(QWidget* parent = NULL); + void setColor(const QColor& color); + const QColor& getColor() const { return color_; } + + signals: + void colorChanged(const QColor&); + + private slots: + void onClicked(); + + private: + void setColorIcon(const QColor& color); + QColor color_; + }; + +} diff --git a/Swift/QtUI/QtFileTransferListItemModel.cpp b/Swift/QtUI/QtFileTransferListItemModel.cpp index cf1de07..00afacb 100644 --- a/Swift/QtUI/QtFileTransferListItemModel.cpp +++ b/Swift/QtUI/QtFileTransferListItemModel.cpp @@ -9,14 +9,15 @@ #include <boost/bind.hpp> #include <boost/cstdint.hpp> +#include "QtChatWindow.h" // for formatSize + #include <Swiften/Base/boost_bsignals.h> #include <Swift/Controllers/FileTransfer/FileTransferController.h> #include <Swift/Controllers/FileTransfer/FileTransferOverview.h> +#include "QtSwiftUtil.h" namespace Swift { -extern std::string formatSize(const boost::uintmax_t bytes); - QtFileTransferListItemModel::QtFileTransferListItemModel(QObject *parent) : QAbstractItemModel(parent), fileTransferOverview(0) { } @@ -65,11 +66,14 @@ QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) c return controller->isIncoming() ? QVariant(QObject::tr("Incoming")) : QVariant(QObject::tr("Outgoing")); } if (index.column() == OtherParty) { - return QVariant(QString::fromStdString(controller->getOtherParty().toString())); + return QVariant(P2QSTRING(controller->getOtherParty().toString())); } if (index.column() == State) { FileTransfer::State state = controller->getState(); - switch(state.state) { + switch(state.type) { + case FileTransfer::State::Initial: + assert(false); + return QVariant(""); case FileTransfer::State::WaitingForStart: return QVariant(QObject::tr("Waiting for start")); case FileTransfer::State::WaitingForAccept: @@ -91,7 +95,7 @@ QVariant QtFileTransferListItemModel::data(const QModelIndex &index, int role) c return QVariant(QString::number(controller->getProgress())); } if (index.column() == OverallSize) { - return QVariant(QString::fromStdString(formatSize((controller->getSize())))); + return QVariant(P2QSTRING(formatSize((controller->getSize())))); } return QVariant(); } @@ -105,7 +109,7 @@ int QtFileTransferListItemModel::rowCount(const QModelIndex& /* parent */) const } QModelIndex QtFileTransferListItemModel::index(int row, int column, const QModelIndex& /* parent */) const { - return createIndex(row, column, 0); + return createIndex(row, column, (void*) 0); } } diff --git a/Swift/QtUI/QtFileTransferListItemModel.h b/Swift/QtUI/QtFileTransferListItemModel.h index 1d892a5..28f13f8 100644 --- a/Swift/QtUI/QtFileTransferListItemModel.h +++ b/Swift/QtUI/QtFileTransferListItemModel.h @@ -34,7 +34,7 @@ private: State, Progress, OverallSize, - NoOfColumns, + NoOfColumns }; private: diff --git a/Swift/QtUI/QtFormResultItemModel.cpp b/Swift/QtUI/QtFormResultItemModel.cpp index 5461f05..b052334 100644 --- a/Swift/QtUI/QtFormResultItemModel.cpp +++ b/Swift/QtUI/QtFormResultItemModel.cpp @@ -35,7 +35,7 @@ QVariant QtFormResultItemModel::headerData(int section, Qt::Orientation /*orient if (!formResult_) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); if (static_cast<size_t>(section) >= formResult_->getReportedFields().size()) return QVariant(); - return QVariant(QString::fromStdString(formResult_->getReportedFields().at(section)->getLabel())); + return QVariant(P2QSTRING(formResult_->getReportedFields().at(section)->getLabel())); } int QtFormResultItemModel::rowCount(const QModelIndex &/*parent*/) const { diff --git a/Swift/QtUI/QtHighlightEditorWidget.cpp b/Swift/QtUI/QtHighlightEditorWidget.cpp new file mode 100644 index 0000000..7ff094e --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <cassert> + +#include <Swift/QtUI/QtHighlightEditorWidget.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> + +namespace Swift { + +QtHighlightEditorWidget::QtHighlightEditorWidget(QWidget* parent) + : QWidget(parent) +{ + ui_.setupUi(this); + + itemModel_ = new QtHighlightRulesItemModel(this); + ui_.treeView->setModel(itemModel_); + ui_.ruleWidget->setModel(itemModel_); + + for (int i = 0; i < QtHighlightRulesItemModel::NumberOfColumns; ++i) { + switch (i) { + case QtHighlightRulesItemModel::ApplyTo: + case QtHighlightRulesItemModel::Sender: + case QtHighlightRulesItemModel::Keyword: + case QtHighlightRulesItemModel::Action: + ui_.treeView->showColumn(i); + break; + default: + ui_.treeView->hideColumn(i); + break; + } + } + + setHighlightManager(NULL); // setup buttons for empty rules list + + connect(ui_.treeView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)), SLOT(onCurrentRowChanged(QModelIndex))); + + connect(ui_.newButton, SIGNAL(clicked()), SLOT(onNewButtonClicked())); + connect(ui_.deleteButton, SIGNAL(clicked()), SLOT(onDeleteButtonClicked())); + + connect(ui_.moveUpButton, SIGNAL(clicked()), SLOT(onMoveUpButtonClicked())); + connect(ui_.moveDownButton, SIGNAL(clicked()), SLOT(onMoveDownButtonClicked())); + + connect(ui_.closeButton, SIGNAL(clicked()), SLOT(close())); + + setWindowTitle(tr("Highlight Rules")); +} + +QtHighlightEditorWidget::~QtHighlightEditorWidget() +{ +} + +void QtHighlightEditorWidget::show() +{ + if (itemModel_->rowCount(QModelIndex())) { + selectRow(0); + } + QWidget::show(); + QWidget::activateWindow(); +} + +void QtHighlightEditorWidget::setHighlightManager(HighlightManager* highlightManager) +{ + itemModel_->setHighlightManager(highlightManager); + ui_.newButton->setEnabled(highlightManager != NULL); + + ui_.ruleWidget->setEnabled(false); + ui_.deleteButton->setEnabled(false); + ui_.moveUpButton->setEnabled(false); + ui_.moveDownButton->setEnabled(false); +} + +void QtHighlightEditorWidget::closeEvent(QCloseEvent* event) { + ui_.ruleWidget->save(); + event->accept(); +} + +void QtHighlightEditorWidget::onNewButtonClicked() +{ + int row = getSelectedRow() + 1; + itemModel_->insertRow(row, QModelIndex()); + selectRow(row); +} + +void QtHighlightEditorWidget::onDeleteButtonClicked() +{ + int row = getSelectedRow(); + assert(row >= 0); + + itemModel_->removeRow(row, QModelIndex()); + if (row == itemModel_->rowCount(QModelIndex())) { + --row; + } + selectRow(row); +} + +void QtHighlightEditorWidget::onMoveUpButtonClicked() +{ + int row = getSelectedRow(); + assert(row > 0); + + ui_.ruleWidget->save(); + ui_.ruleWidget->setActiveIndex(QModelIndex()); + itemModel_->swapRows(row, row - 1); + selectRow(row - 1); +} + +void QtHighlightEditorWidget::onMoveDownButtonClicked() +{ + int row = getSelectedRow(); + assert(row < itemModel_->rowCount(QModelIndex()) - 1); + + ui_.ruleWidget->save(); + ui_.ruleWidget->setActiveIndex(QModelIndex()); + if (itemModel_->swapRows(row, row + 1)) { + selectRow(row + 1); + } +} + +void QtHighlightEditorWidget::onCurrentRowChanged(const QModelIndex& index) +{ + ui_.ruleWidget->save(); + ui_.ruleWidget->setActiveIndex(index); + + ui_.ruleWidget->setEnabled(index.isValid()); + + ui_.deleteButton->setEnabled(index.isValid()); + + ui_.moveUpButton->setEnabled(index.isValid() && index.row() != 0); + ui_.moveDownButton->setEnabled(index.isValid() && index.row() != itemModel_->rowCount(QModelIndex()) - 1); +} + +void QtHighlightEditorWidget::selectRow(int row) +{ + QModelIndex index = itemModel_->index(row, 0, QModelIndex()); + ui_.treeView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); +} + +/** Return index of selected row or -1 if none is selected */ +int QtHighlightEditorWidget::getSelectedRow() const +{ + QModelIndexList rows = ui_.treeView->selectionModel()->selectedRows(); + return rows.isEmpty() ? -1 : rows[0].row(); +} + +} diff --git a/Swift/QtUI/QtHighlightEditorWidget.h b/Swift/QtUI/QtHighlightEditorWidget.h new file mode 100644 index 0000000..1293c87 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swift/Controllers/UIInterfaces/HighlightEditorWidget.h> +#include <Swift/QtUI/ui_QtHighlightEditorWidget.h> + +namespace Swift { + + class QtHighlightRulesItemModel; + + class QtHighlightEditorWidget : public QWidget, public HighlightEditorWidget { + Q_OBJECT + + public: + QtHighlightEditorWidget(QWidget* parent = NULL); + virtual ~QtHighlightEditorWidget(); + + void show(); + + void setHighlightManager(HighlightManager* highlightManager); + + private slots: + void onNewButtonClicked(); + void onDeleteButtonClicked(); + void onMoveUpButtonClicked(); + void onMoveDownButtonClicked(); + void onCurrentRowChanged(const QModelIndex&); + + private: + virtual void closeEvent(QCloseEvent* event); + + void selectRow(int row); + int getSelectedRow() const; + + Ui::QtHighlightEditorWidget ui_; + QtHighlightRulesItemModel* itemModel_; + }; + +} diff --git a/Swift/QtUI/QtHighlightEditorWidget.ui b/Swift/QtUI/QtHighlightEditorWidget.ui new file mode 100644 index 0000000..0f39168 --- /dev/null +++ b/Swift/QtUI/QtHighlightEditorWidget.ui @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtHighlightEditorWidget</class> + <widget class="QWidget" name="QtHighlightEditorWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>419</width> + <height>373</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Incoming messages are checked against the following rules. First rule that matches will be executed.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTreeView" name="treeView"> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Swift::QtHighlightRuleWidget" name="ruleWidget" native="true"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QPushButton" name="newButton"> + <property name="text"> + <string>New</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteButton"> + <property name="text"> + <string>Delete</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="moveUpButton"> + <property name="text"> + <string>Move up</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="moveDownButton"> + <property name="text"> + <string>Move down</string> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="closeButton"> + <property name="text"> + <string>Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtHighlightRuleWidget</class> + <extends>QWidget</extends> + <header>QtHighlightRuleWidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtHighlightRuleWidget.cpp b/Swift/QtUI/QtHighlightRuleWidget.cpp new file mode 100644 index 0000000..9c0df5e --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <QDataWidgetMapper> +#include <QStringListModel> +#include <QFileDialog> + +#include <Swift/QtUI/QtHighlightRuleWidget.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> + +namespace Swift { + +QtHighlightRuleWidget::QtHighlightRuleWidget(QWidget* parent) + : QWidget(parent) +{ + ui_.setupUi(this); + + QStringList applyToItems; + for (int i = 0; i < QtHighlightRulesItemModel::ApplyToEOL; ++i) { + applyToItems << QtHighlightRulesItemModel::getApplyToString(i); + } + QStringListModel * applyToModel = new QStringListModel(applyToItems, this); + ui_.applyTo->setModel(applyToModel); + + connect(ui_.highlightText, SIGNAL(toggled(bool)), SLOT(onHighlightTextToggled(bool))); + connect(ui_.customColors, SIGNAL(toggled(bool)), SLOT(onCustomColorsToggled(bool))); + connect(ui_.playSound, SIGNAL(toggled(bool)), SLOT(onPlaySoundToggled(bool))); + connect(ui_.customSound, SIGNAL(toggled(bool)), SLOT(onCustomSoundToggled(bool))); + connect(ui_.soundFileButton, SIGNAL(clicked()), SLOT(onSoundFileButtonClicked())); + + mapper_ = new QDataWidgetMapper(this); + hasValidIndex_ = false; + model_ = NULL; +} + +QtHighlightRuleWidget::~QtHighlightRuleWidget() +{ +} + +/** Widget does not gain ownership over the model */ +void QtHighlightRuleWidget::setModel(QtHighlightRulesItemModel* model) +{ + model_ = model; + mapper_->setModel(model_); +} + +void QtHighlightRuleWidget::setActiveIndex(const QModelIndex& index) +{ + if (index.isValid()) { + if (!hasValidIndex_) { + mapper_->addMapping(ui_.applyTo, QtHighlightRulesItemModel::ApplyTo, "currentIndex"); + mapper_->addMapping(ui_.senders, QtHighlightRulesItemModel::Sender, "plainText"); + mapper_->addMapping(ui_.keywords, QtHighlightRulesItemModel::Keyword, "plainText"); + mapper_->addMapping(ui_.nickIsKeyword, QtHighlightRulesItemModel::NickIsKeyword); + mapper_->addMapping(ui_.matchCase, QtHighlightRulesItemModel::MatchCase); + mapper_->addMapping(ui_.matchWholeWords, QtHighlightRulesItemModel::MatchWholeWords); + mapper_->addMapping(ui_.highlightText, QtHighlightRulesItemModel::HighlightText); + mapper_->addMapping(ui_.foreground, QtHighlightRulesItemModel::TextColor, "color"); + mapper_->addMapping(ui_.background, QtHighlightRulesItemModel::TextBackground, "color"); + mapper_->addMapping(ui_.playSound, QtHighlightRulesItemModel::PlaySound); + mapper_->addMapping(ui_.soundFile, QtHighlightRulesItemModel::SoundFile); + } + mapper_->setCurrentModelIndex(index); + ui_.customColors->setChecked(ui_.foreground->getColor().isValid() || ui_.background->getColor().isValid()); + ui_.customSound->setChecked(!ui_.soundFile->text().isEmpty()); + ui_.applyTo->focusWidget(); + } else { + if (hasValidIndex_) { + mapper_->clearMapping(); + } + } + + hasValidIndex_ = index.isValid(); +} + +void QtHighlightRuleWidget::onCustomColorsToggled(bool enabled) +{ + if (!enabled) { + ui_.foreground->setColor(QColor()); + ui_.background->setColor(QColor()); + } + ui_.foreground->setEnabled(enabled); + ui_.background->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onCustomSoundToggled(bool enabled) +{ + if (enabled) { + if (ui_.soundFile->text().isEmpty()) { + onSoundFileButtonClicked(); + } + } else { + ui_.soundFile->clear(); + } + ui_.soundFile->setEnabled(enabled); + ui_.soundFileButton->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onSoundFileButtonClicked() +{ + QString s = QFileDialog::getOpenFileName(this, tr("Choose sound file"), QString(), tr("Sound files (*.wav)")); + if (!s.isEmpty()) { + ui_.soundFile->setText(s); + } +} + +void QtHighlightRuleWidget::onHighlightTextToggled(bool enabled) +{ + ui_.customColors->setEnabled(enabled); +} + +void QtHighlightRuleWidget::onPlaySoundToggled(bool enabled) +{ + ui_.customSound->setEnabled(enabled); +} + +void QtHighlightRuleWidget::save() +{ + if (hasValidIndex_) { + mapper_->submit(); + } +} + +void QtHighlightRuleWidget::revert() +{ + if (hasValidIndex_) { + mapper_->revert(); + } +} + +} diff --git a/Swift/QtUI/QtHighlightRuleWidget.h b/Swift/QtUI/QtHighlightRuleWidget.h new file mode 100644 index 0000000..8a59a14 --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <QModelIndex> + +#include <Swift/QtUI/ui_QtHighlightRuleWidget.h> + +class QDataWidgetMapper; + +namespace Swift { + + class QtHighlightRulesItemModel; + + class QtHighlightRuleWidget : public QWidget + { + Q_OBJECT + + public: + explicit QtHighlightRuleWidget(QWidget* parent = NULL); + ~QtHighlightRuleWidget(); + + void setModel(QtHighlightRulesItemModel* model); + + public slots: + void setActiveIndex(const QModelIndex&); + void save(); + void revert(); + + private slots: + void onHighlightTextToggled(bool); + void onCustomColorsToggled(bool); + void onPlaySoundToggled(bool); + void onCustomSoundToggled(bool); + void onSoundFileButtonClicked(); + + private: + QDataWidgetMapper * mapper_; + QtHighlightRulesItemModel * model_; + bool hasValidIndex_; + Ui::QtHighlightRuleWidget ui_; + }; + +} diff --git a/Swift/QtUI/QtHighlightRuleWidget.ui b/Swift/QtUI/QtHighlightRuleWidget.ui new file mode 100644 index 0000000..9c465f9 --- /dev/null +++ b/Swift/QtUI/QtHighlightRuleWidget.ui @@ -0,0 +1,260 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtHighlightRuleWidget</class> + <widget class="QWidget" name="QtHighlightRuleWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>361</width> + <height>524</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Rule conditions</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0" colspan="2"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Choose when this rule should be applied. +If you want to provide more than one sender or keyword, input them in separate lines.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>&Apply to:</string> + </property> + <property name="buddy"> + <cstring>applyTo</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="applyTo"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>&Senders:</string> + </property> + <property name="buddy"> + <cstring>senders</cstring> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QPlainTextEdit" name="senders"/> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>&Keywords:</string> + </property> + <property name="buddy"> + <cstring>keywords</cstring> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QPlainTextEdit" name="keywords"/> + </item> + <item row="5" column="1"> + <widget class="QCheckBox" name="nickIsKeyword"> + <property name="text"> + <string>Treat &nick as a keyword (in MUC)</string> + </property> + </widget> + </item> + <item row="6" column="1"> + <widget class="QCheckBox" name="matchWholeWords"> + <property name="text"> + <string>Match whole &words</string> + </property> + </widget> + </item> + <item row="7" column="1"> + <widget class="QCheckBox" name="matchCase"> + <property name="text"> + <string>Match &case</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Actions</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="highlightText"> + <property name="text"> + <string>&Highlight text</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>28</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="customColors"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Custom c&olors:</string> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtColorToolButton" name="foreground"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Foreground</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtColorToolButton" name="background"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>&Background</string> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="playSound"> + <property name="text"> + <string>&Play sound</string> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>28</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="customSound"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Custom soun&d:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="soundFile"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="soundFileButton"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>101</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtColorToolButton</class> + <extends>QToolButton</extends> + <header>QtColorToolButton.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtHighlightRulesItemModel.cpp b/Swift/QtUI/QtHighlightRulesItemModel.cpp new file mode 100644 index 0000000..4efa712 --- /dev/null +++ b/Swift/QtUI/QtHighlightRulesItemModel.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include <boost/algorithm/string.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/numeric/conversion/cast.hpp> + +#include <QStringList> +#include <QColor> + +#include <Swift/Controllers/HighlightManager.h> +#include <Swift/QtUI/QtHighlightRulesItemModel.h> +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtHighlightRulesItemModel::QtHighlightRulesItemModel(QObject* parent) : QAbstractItemModel(parent), highlightManager_(NULL) +{ +} + +void QtHighlightRulesItemModel::setHighlightManager(HighlightManager* highlightManager) +{ + emit layoutAboutToBeChanged(); + highlightManager_ = highlightManager; + emit layoutChanged(); +} + +QVariant QtHighlightRulesItemModel::headerData(int section, Qt::Orientation /* orientation */, int role) const +{ + if (role == Qt::DisplayRole) { + switch (section) { + case ApplyTo: return QVariant(tr("Apply to")); + case Sender: return QVariant(tr("Sender")); + case Keyword: return QVariant(tr("Keyword")); + case Action: return QVariant(tr("Action")); + case NickIsKeyword: return QVariant(tr("Nick Is Keyword")); + case MatchCase: return QVariant(tr("Match Case")); + case MatchWholeWords: return QVariant(tr("Match Whole Words")); + case HighlightText: return QVariant(tr("Highlight Text")); + case TextColor: return QVariant(tr("Text Color")); + case TextBackground: return QVariant(tr("Text Background")); + case PlaySound: return QVariant(tr("Play Sounds")); + case SoundFile: return QVariant(tr("Sound File")); + } + } + + return QVariant(); +} + +int QtHighlightRulesItemModel::columnCount(const QModelIndex& /* parent */) const +{ + return NumberOfColumns; +} + +QVariant QtHighlightRulesItemModel::data(const QModelIndex &index, int role) const +{ + if (index.isValid() && highlightManager_ && (role == Qt::DisplayRole || role == Qt::EditRole)) { + + const char* separator = (role == Qt::DisplayRole) ? " ; " : "\n"; + + if (boost::numeric_cast<std::vector<std::string>::size_type>(index.row()) < highlightManager_->getRules().size()) { + const HighlightRule& r = highlightManager_->getRules()[index.row()]; + switch (index.column()) { + case ApplyTo: { + int applyTo = 0; + if (r.getMatchChat() && r.getMatchMUC()) { + applyTo = 1; + } else if (r.getMatchChat()) { + applyTo = 2; + } else if (r.getMatchMUC()) { + applyTo = 3; + } + + if (role == Qt::DisplayRole) { + return QVariant(getApplyToString(applyTo)); + } else { + return QVariant(applyTo); + } + } + case Sender: { + std::string s = boost::join(r.getSenders(), separator); + return QVariant(P2QSTRING(s)); + } + case Keyword: { + std::string s = boost::join(r.getKeywords(), separator); + QString qs(P2QSTRING(s)); + if (role == Qt::DisplayRole && r.getNickIsKeyword()) { + qs = tr("<nick>") + (qs.isEmpty() ? "" : separator + qs); + } + return QVariant(qs); + } + case Action: { + std::vector<std::string> v; + const HighlightAction & action = r.getAction(); + if (action.highlightText()) { + v.push_back(Q2PSTRING(tr("Highlight text"))); + } + if (action.playSound()) { + v.push_back(Q2PSTRING(tr("Play sound"))); + } + std::string s = boost::join(v, separator); + return QVariant(P2QSTRING(s)); + } + case NickIsKeyword: { + return QVariant(r.getNickIsKeyword()); + } + case MatchCase: { + return QVariant(r.getMatchCase()); + } + case MatchWholeWords: { + return QVariant(r.getMatchWholeWords()); + } + case HighlightText: { + return QVariant(r.getAction().highlightText()); + } + case TextColor: { + return QVariant(QColor(P2QSTRING(r.getAction().getTextColor()))); + } + case TextBackground: { + return QVariant(QColor(P2QSTRING(r.getAction().getTextBackground()))); + } + case PlaySound: { + return QVariant(r.getAction().playSound()); + } + case SoundFile: { + return QVariant(P2QSTRING(r.getAction().getSoundFile())); + } + } + } + } + return QVariant(); +} + +bool QtHighlightRulesItemModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.isValid() && highlightManager_ && role == Qt::EditRole) { + if (boost::numeric_cast<std::vector<std::string>::size_type>(index.row()) < highlightManager_->getRules().size()) { + HighlightRule r = highlightManager_->getRule(index.row()); + std::vector<int> changedColumns; + switch (index.column()) { + case ApplyTo: { + bool ok = false; + int applyTo = value.toInt(&ok); + if (!ok) { + return false; + } + r.setMatchChat(applyTo == ApplyToAll || applyTo == ApplyToChat); + r.setMatchMUC(applyTo == ApplyToAll || applyTo == ApplyToMUC); + break; + } + case Sender: + case Keyword: { + std::string s = Q2PSTRING(value.toString()); + std::vector<std::string> v; + boost::split(v, s, boost::is_any_of("\n")); + v.erase(std::remove_if(v.begin(), v.end(), boost::lambda::_1 == ""), v.end()); + if (index.column() == Sender) { + r.setSenders(v); + } else { + r.setKeywords(v); + } + break; + } + case NickIsKeyword: { + r.setNickIsKeyword(value.toBool()); + changedColumns.push_back(Keyword); // "<nick>" + break; + } + case MatchCase: { + r.setMatchCase(value.toBool()); + break; + } + case MatchWholeWords: { + r.setMatchWholeWords(value.toBool()); + break; + } + case HighlightText: { + r.getAction().setHighlightText(value.toBool()); + changedColumns.push_back(Action); + break; + } + case TextColor: { + QColor c = value.value<QColor>(); + r.getAction().setTextColor(c.isValid() ? Q2PSTRING(c.name()) : ""); + break; + } + case TextBackground: { + QColor c = value.value<QColor>(); + r.getAction().setTextBackground(c.isValid() ? Q2PSTRING(c.name()) : ""); + break; + } + case PlaySound: { + r.getAction().setPlaySound(value.toBool()); + changedColumns.push_back(Action); + break; + } + case SoundFile: { + r.getAction().setSoundFile(Q2PSTRING(value.toString())); + break; + } + } + + highlightManager_->setRule(index.row(), r); + emit dataChanged(index, index); + foreach (int column, changedColumns) { + QModelIndex i = createIndex(index.row(), column, (void*) 0); + emit dataChanged(i, i); + } + } + } + + return false; +} + +QModelIndex QtHighlightRulesItemModel::parent(const QModelIndex& /* child */) const +{ + return QModelIndex(); +} + +int QtHighlightRulesItemModel::rowCount(const QModelIndex& /* parent */) const +{ + return highlightManager_ ? highlightManager_->getRules().size() : 0; +} + +QModelIndex QtHighlightRulesItemModel::index(int row, int column, const QModelIndex& /* parent */) const +{ + return createIndex(row, column, (void*) 0); +} + +bool QtHighlightRulesItemModel::insertRows(int row, int count, const QModelIndex& /* parent */) +{ + if (highlightManager_) { + beginInsertRows(QModelIndex(), row, row + count); + while (count--) { + highlightManager_->insertRule(row, HighlightRule()); + } + endInsertRows(); + return true; + } + return false; +} + +bool QtHighlightRulesItemModel::removeRows(int row, int count, const QModelIndex& /* parent */) +{ + if (highlightManager_) { + beginRemoveRows(QModelIndex(), row, row + count); + while (count--) { + highlightManager_->removeRule(row); + } + endRemoveRows(); + return true; + } + return false; +} + +bool QtHighlightRulesItemModel::swapRows(int row1, int row2) +{ + if (highlightManager_) { + assert(row1 >= 0 && row2 >= 0 && boost::numeric_cast<std::vector<std::string>::size_type>(row1) < highlightManager_->getRules().size() && boost::numeric_cast<std::vector<std::string>::size_type>(row2) < highlightManager_->getRules().size()); + HighlightRule r = highlightManager_->getRule(row1); + highlightManager_->setRule(row1, highlightManager_->getRule(row2)); + highlightManager_->setRule(row2, r); + emit dataChanged(index(row1, 0, QModelIndex()), index(row1, 0, QModelIndex())); + emit dataChanged(index(row2, 0, QModelIndex()), index(row2, 0, QModelIndex())); + return true; + } + return false; +} + +QString QtHighlightRulesItemModel::getApplyToString(int applyTo) +{ + switch (applyTo) { + case ApplyToNone: return tr("None"); + case ApplyToAll: return tr("Chat or MUC"); + case ApplyToChat: return tr("Chat"); + case ApplyToMUC: return tr("MUC"); + default: return ""; + } +} + +} diff --git a/Swift/QtUI/QtHighlightRulesItemModel.h b/Swift/QtUI/QtHighlightRulesItemModel.h new file mode 100644 index 0000000..ac85628 --- /dev/null +++ b/Swift/QtUI/QtHighlightRulesItemModel.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 Maciej Niedzielski + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QAbstractItemModel> + +namespace Swift { + + class HighlightManager; + + class QtHighlightRulesItemModel : public QAbstractItemModel { + Q_OBJECT + + public: + QtHighlightRulesItemModel(QObject* parent = NULL); + + void setHighlightManager(HighlightManager* highlightManager); + + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int columnCount(const QModelIndex& parent) const; + QVariant data(const QModelIndex& index, int role) const; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + QModelIndex parent(const QModelIndex& child) const; + int rowCount(const QModelIndex& parent) const; + QModelIndex index(int row, int column, const QModelIndex& parent) const; + bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex()); + bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex()); + bool swapRows(int row1, int row2); + + static QString getApplyToString(int); + + enum Columns { + ApplyTo = 0, + Sender, + Keyword, + Action, + NickIsKeyword, + MatchCase, + MatchWholeWords, + HighlightText, + TextColor, + TextBackground, + PlaySound, + SoundFile, + NumberOfColumns // end of list marker + }; + + enum ApplyToValues { + ApplyToNone = 0, + ApplyToAll, + ApplyToChat, + ApplyToMUC, + ApplyToEOL // end of list marker + }; + + private: + HighlightManager* highlightManager_; + }; + +} diff --git a/Swift/QtUI/QtHistoryWindow.cpp b/Swift/QtUI/QtHistoryWindow.cpp index e54bd51..6f22b76 100644 --- a/Swift/QtUI/QtHistoryWindow.cpp +++ b/Swift/QtUI/QtHistoryWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012 Catalin Badea + * Copyright (c) 2012-2013 Catalin Badea * Licensed under the simplified BSD license. * See Documentation/Licenses/BSD-simplified.txt for more information. */ @@ -13,6 +13,7 @@ #include <string> #include <boost/shared_ptr.hpp> +#include <boost/smart_ptr/make_shared.hpp> #include <QTime> #include <QUrl> @@ -20,9 +21,12 @@ #include <QTextDocument> #include <QDateTime> #include <Swift/QtUI/QtScaledAvatarCache.h> +#include <Swift/QtUI/ChatSnippet.h> #include <QLineEdit> +#include "QtUtilities.h" #include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> #include <boost/date_time/gregorian/gregorian.hpp> namespace Swift { @@ -107,7 +111,7 @@ void QtHistoryWindow::addMessage(const std::string &message, const std::string & QString scaledAvatarPath = QtScaledAvatarCache(32).getScaledAvatarPath(avatarPath.c_str()); QString messageHTML(P2QSTRING(message)); - messageHTML = Qt::escape(messageHTML); + messageHTML = QtUtilities::htmlEscape(messageHTML); QString searchTerm = ui_.searchBox_->lineEdit()->text(); if (searchTerm.length()) { messageHTML.replace(searchTerm, "<span style='background-color: yellow'>" + searchTerm + "</span>"); @@ -124,14 +128,14 @@ void QtHistoryWindow::addMessage(const std::string &message, const std::string & if (addAtTheTop) { bool appendToPrevious = ((senderIsSelf && previousTopMessageWasSelf_) || (!senderIsSelf && !previousTopMessageWasSelf_&& previousTopSenderName_ == P2QSTRING(senderName))); - conversation_->addMessageTop(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); + conversation_->addMessageTop(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, QtUtilities::htmlEscape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(message)))); previousTopMessageWasSelf_ = senderIsSelf; previousTopSenderName_ = P2QSTRING(senderName); } else { bool appendToPrevious = ((senderIsSelf && previousBottomMessageWasSelf_) || (!senderIsSelf && !previousBottomMessageWasSelf_&& previousBottomSenderName_ == P2QSTRING(senderName))); - conversation_->addMessageBottom(boost::shared_ptr<ChatSnippet>(new MessageSnippet(messageHTML, Qt::escape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id)))); + conversation_->addMessageBottom(boost::make_shared<MessageSnippet>(messageHTML, QtUtilities::htmlEscape(P2QSTRING(senderName)), qTime, qAvatarPath, senderIsSelf, appendToPrevious, theme_, P2QSTRING(id), ChatSnippet::getDirection(message))); previousBottomMessageWasSelf_ = senderIsSelf; previousBottomSenderName_ = P2QSTRING(senderName); } @@ -183,7 +187,7 @@ void QtHistoryWindow::handleScrollReachedTop() { int year, month, day; QDate firstDate = *dates_.begin(); firstDate.getDate(&year, &month, &day); - onScrollReachedTop(boost::gregorian::date(year, month, day)); + onScrollReachedTop(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day))); } void QtHistoryWindow::handleScrollReachedBottom() { @@ -194,7 +198,7 @@ void QtHistoryWindow::handleScrollReachedBottom() { int year, month, day; QDate lastDate = *dates_.rbegin(); lastDate.getDate(&year, &month, &day); - onScrollReachedBottom(boost::gregorian::date(year, month, day)); + onScrollReachedBottom(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day))); } void QtHistoryWindow::handleReturnPressed() { @@ -205,7 +209,7 @@ void QtHistoryWindow::handleCalendarClicked(const QDate& date) { int year, month, day; QDate tempDate = date; // getDate discards const qualifier tempDate.getDate(&year, &month, &day); - onCalendarClicked(boost::gregorian::date(year, month, day)); + onCalendarClicked(boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day))); } void QtHistoryWindow::setDate(const boost::gregorian::date& date) { @@ -242,7 +246,7 @@ boost::gregorian::date QtHistoryWindow::getLastVisibleDate() { int year, month, day; lastDate.getDate(&year, &month, &day); - return boost::gregorian::date(year, month, day); + return boost::gregorian::date(boost::numeric_cast<unsigned short>(year), boost::numeric_cast<unsigned short>(month), boost::numeric_cast<unsigned short>(day)); } return boost::gregorian::date(boost::gregorian::not_a_date_time); } diff --git a/Swift/QtUI/QtJoinMUCWindow.ui b/Swift/QtUI/QtJoinMUCWindow.ui index 5a69292..9225f6f 100644 --- a/Swift/QtUI/QtJoinMUCWindow.ui +++ b/Swift/QtUI/QtJoinMUCWindow.ui @@ -43,9 +43,6 @@ </property> </widget> </item> - <item row="1" column="1" colspan="2"> - <widget class="QLineEdit" name="nickName"/> - </item> <item row="0" column="1"> <widget class="QLineEdit" name="room"> <property name="text"> @@ -63,6 +60,9 @@ <item row="2" column="1"> <widget class="QLineEdit" name="password"/> </item> + <item row="1" column="1" colspan="2"> + <widget class="QLineEdit" name="nickName"/> + </item> </layout> </item> <item> diff --git a/Swift/QtUI/QtLoginWindow.cpp b/Swift/QtUI/QtLoginWindow.cpp index c27edfb..188e55f 100644 --- a/Swift/QtUI/QtLoginWindow.cpp +++ b/Swift/QtUI/QtLoginWindow.cpp @@ -30,6 +30,7 @@ #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIEvents/RequestXMLConsoleUIEvent.h> #include <Swift/Controllers/UIEvents/RequestFileTransferListUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestHighlightEditorUIEvent.h> #include <Swift/Controllers/Settings/SettingsProvider.h> #include <Swift/Controllers/SettingConstants.h> #include <Swift/QtUI/QtUISettingConstants.h> @@ -54,7 +55,7 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* set uiEventStream_ = uiEventStream; setWindowTitle("Swift"); -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif QtUtilities::setX11Resource(this, "Main"); @@ -190,6 +191,10 @@ QtLoginWindow::QtLoginWindow(UIEventStream* uiEventStream, SettingsProvider* set generalMenu_->addAction(fileTransferOverviewAction_); #endif + highlightEditorAction_ = new QAction(tr("&Edit Highlight Rules"), this); + connect(highlightEditorAction_, SIGNAL(triggered()), SLOT(handleShowHighlightEditor())); + generalMenu_->addAction(highlightEditorAction_); + toggleSoundsAction_ = new QAction(tr("&Play Sounds"), this); toggleSoundsAction_->setCheckable(true); toggleSoundsAction_->setChecked(settings_->getSetting(SettingConstants::PLAY_SOUNDS)); @@ -438,6 +443,10 @@ void QtLoginWindow::handleShowFileTransferOverview() { uiEventStream_->send(boost::make_shared<RequestFileTransferListUIEvent>()); } +void QtLoginWindow::handleShowHighlightEditor() { + uiEventStream_->send(boost::make_shared<RequestHighlightEditorUIEvent>()); +} + void QtLoginWindow::handleToggleSounds(bool enabled) { settings_->storeSetting(SettingConstants::PLAY_SOUNDS, enabled); } diff --git a/Swift/QtUI/QtLoginWindow.h b/Swift/QtUI/QtLoginWindow.h index c1966d8..7415fbf 100644 --- a/Swift/QtUI/QtLoginWindow.h +++ b/Swift/QtUI/QtLoginWindow.h @@ -62,6 +62,7 @@ namespace Swift { void handleQuit(); void handleShowXMLConsole(); void handleShowFileTransferOverview(); + void handleShowHighlightEditor(); void handleToggleSounds(bool enabled); void handleToggleNotifications(bool enabled); void handleAbout(); @@ -103,6 +104,7 @@ namespace Swift { SettingsProvider* settings_; QAction* xmlConsoleAction_; QAction* fileTransferOverviewAction_; + QAction* highlightEditorAction_; TimerFactory* timerFactory_; ClientOptions currentOptions_; }; diff --git a/Swift/QtUI/QtMainWindow.cpp b/Swift/QtUI/QtMainWindow.cpp index 5d50c1e..572b06f 100644 --- a/Swift/QtUI/QtMainWindow.cpp +++ b/Swift/QtUI/QtMainWindow.cpp @@ -33,6 +33,7 @@ #include <Swift/Controllers/UIEvents/RequestProfileEditorUIEvent.h> #include <Swift/Controllers/UIEvents/JoinMUCUIEvent.h> #include <Swift/Controllers/UIEvents/RequestAdHocUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestBlockListDialogUIEvent.h> #include <Swift/QtUI/QtUISettingConstants.h> #include <Swift/Controllers/SettingConstants.h> #include <Swiften/Base/Platform.h> @@ -47,14 +48,14 @@ namespace Swift { -QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist) : QWidget(), MainWindow(false), loginMenus_(loginMenus) { +QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist) : QWidget(), MainWindow(false), loginMenus_(loginMenus) { uiEventStream_ = uiEventStream; settings_ = settings; setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding)); QBoxLayout *mainLayout = new QBoxLayout(QBoxLayout::TopToBottom, this); mainLayout->setContentsMargins(0,0,0,0); mainLayout->setSpacing(0); - meView_ = new QtRosterHeader(settings, this); + meView_ = new QtRosterHeader(settings, statusCache, this); mainLayout->addWidget(meView_); connect(meView_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleStatusChanged(StatusShow::Type, const QString&))); connect(meView_, SIGNAL(onEditProfileRequest()), this, SLOT(handleEditProfileRequest())); @@ -95,6 +96,14 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr QMenu* viewMenu = new QMenu(tr("&View"), this); menus_.push_back(viewMenu); + + compactRosterAction_ = new QAction(tr("&Compact Roster"), this); + compactRosterAction_->setCheckable(true); + compactRosterAction_->setChecked(false); + connect(compactRosterAction_, SIGNAL(toggled(bool)), SLOT(handleCompactRosterToggled(bool))); + viewMenu->addAction(compactRosterAction_); + handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); + showOfflineAction_ = new QAction(tr("&Show offline contacts"), this); showOfflineAction_->setCheckable(true); showOfflineAction_->setChecked(false); @@ -130,6 +139,10 @@ QtMainWindow::QtMainWindow(SettingsProvider* settings, UIEventStream* uiEventStr connect(viewLogsAction, SIGNAL(triggered()), SLOT(handleViewLogsAction())); actionsMenu->addAction(viewLogsAction); #endif + openBlockingListEditor_ = new QAction(tr("Edit &Blocking List…"), this); + connect(openBlockingListEditor_, SIGNAL(triggered()), SLOT(handleEditBlockingList())); + actionsMenu->addAction(openBlockingListEditor_); + openBlockingListEditor_->setVisible(false); addUserAction_ = new QAction(tr("&Add Contact…"), this); connect(addUserAction_, SIGNAL(triggered(bool)), this, SLOT(handleAddUserActionTriggered(bool))); actionsMenu->addAction(addUserAction_); @@ -182,6 +195,10 @@ void QtMainWindow::handleShowCertificateInfo() { onShowCertificateRequest(); } +void QtMainWindow::handleEditBlockingList() { + uiEventStream_->send(boost::make_shared<RequestBlockListDialogUIEvent>()); +} + QtEventWindow* QtMainWindow::getEventWindow() { return eventWindow_; } @@ -261,6 +278,14 @@ void QtMainWindow::handleSettingChanged(const std::string& settingPath) { if (settingPath == SettingConstants::REQUEST_DELIVERYRECEIPTS.getKey()) { toggleRequestDeliveryReceipts_->setChecked(settings_->getSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS)); } + if (settingPath == QtUISettingConstants::COMPACT_ROSTER.getKey()) { + handleCompactRosterToggled(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); + } +} + +void QtMainWindow::handleCompactRosterToggled(bool state) { + settings_->storeSetting(QtUISettingConstants::COMPACT_ROSTER, state); + compactRosterAction_->setChecked(settings_->getSetting(QtUISettingConstants::COMPACT_ROSTER)); } void QtMainWindow::handleShowOfflineToggled(bool state) { @@ -343,5 +368,9 @@ void QtMainWindow::setAvailableAdHocCommands(const std::vector<DiscoItems::Item> } } +void QtMainWindow::setBlockingCommandAvailable(bool isAvailable) { + openBlockingListEditor_->setVisible(isAvailable); +} + } diff --git a/Swift/QtUI/QtMainWindow.h b/Swift/QtUI/QtMainWindow.h index 26d25e1..3e6e1d3 100644 --- a/Swift/QtUI/QtMainWindow.h +++ b/Swift/QtUI/QtMainWindow.h @@ -32,11 +32,12 @@ namespace Swift { class QtTabWidget; class SettingsProvider; class QtUIPreferences; + class StatusCache; class QtMainWindow : public QWidget, public MainWindow { Q_OBJECT public: - QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, bool emoticonsExist); + QtMainWindow(SettingsProvider*, UIEventStream* eventStream, QtLoginWindow::QtMenus loginMenus, StatusCache* statusCache, bool emoticonsExist); virtual ~QtMainWindow(); std::vector<QMenu*> getMenus() {return menus_;} void setMyNick(const std::string& name); @@ -52,9 +53,11 @@ namespace Swift { QtChatListWindow* getChatListWindow(); void setRosterModel(Roster* roster); void setAvailableAdHocCommands(const std::vector<DiscoItems::Item>& commands); + void setBlockingCommandAvailable(bool isAvailable); private slots: void handleStatusChanged(StatusShow::Type showType, const QString &statusMessage); void handleSettingChanged(const std::string& settingPath); + void handleCompactRosterToggled(bool); void handleShowOfflineToggled(bool); void handleShowEmoticonsToggled(bool); void handleJoinMUCAction(); @@ -70,6 +73,7 @@ namespace Swift { void handleTabChanged(int index); void handleToggleRequestDeliveryReceipts(bool enabled); void handleShowCertificateInfo(); + void handleEditBlockingList(); private: SettingsProvider* settings_; @@ -81,7 +85,9 @@ namespace Swift { QAction* editUserAction_; QAction* chatUserAction_; QAction* showOfflineAction_; + QAction* compactRosterAction_; QAction* showEmoticonsAction_; + QAction* openBlockingListEditor_; QAction* toggleRequestDeliveryReceipts_; QMenu* serverAdHocMenu_; QtTabWidget* tabs_; diff --git a/Swift/QtUI/QtNameWidget.h b/Swift/QtUI/QtNameWidget.h index 0f00c41..3225879 100644 --- a/Swift/QtUI/QtNameWidget.h +++ b/Swift/QtUI/QtNameWidget.h @@ -31,7 +31,7 @@ namespace Swift { private: enum Mode { ShowNick, - ShowJID, + ShowJID }; SettingsProvider* settings; diff --git a/Swift/QtUI/QtProfileWindow.cpp b/Swift/QtUI/QtProfileWindow.cpp index 0faa78f..b1cdd19 100644 --- a/Swift/QtUI/QtProfileWindow.cpp +++ b/Swift/QtUI/QtProfileWindow.cpp @@ -4,97 +4,82 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + #include "QtProfileWindow.h" +#include "ui_QtProfileWindow.h" -#include <QImage> -#include <QPixmap> -#include <QSizePolicy> -#include <QGridLayout> -#include <QLabel> -#include <QLineEdit> -#include <QPushButton> +#include <QCloseEvent> #include <QMovie> +#include <QShortcut> +#include <QTextDocument> -#include "QtSwiftUtil.h" -#include "QtAvatarWidget.h" +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtUtilities.h> namespace Swift { -QtProfileWindow::QtProfileWindow() { - setWindowTitle(tr("Edit Profile")); - - QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); - setSizePolicy(sizePolicy); - - QVBoxLayout* layout = new QVBoxLayout(this); - layout->setContentsMargins(10, 10, 10, 10); - - QHBoxLayout* topLayout = new QHBoxLayout(); - - avatar = new QtAvatarWidget(this); - topLayout->addWidget(avatar); - - QVBoxLayout* fieldsLayout = new QVBoxLayout(); - - QHBoxLayout* horizontalLayout_2 = new QHBoxLayout(); - nicknameLabel = new QLabel(tr("Nickname:"), this); - horizontalLayout_2->addWidget(nicknameLabel); - nickname = new QLineEdit(this); - horizontalLayout_2->addWidget(nickname); - - fieldsLayout->addLayout(horizontalLayout_2); - - errorLabel = new QLabel(this); - errorLabel->setAlignment(Qt::AlignHCenter); - fieldsLayout->addWidget(errorLabel); - - fieldsLayout->addItem(new QSpacerItem(198, 17, QSizePolicy::Minimum, QSizePolicy::Expanding)); - topLayout->addLayout(fieldsLayout); - - layout->addLayout(topLayout); - - QHBoxLayout* horizontalLayout = new QHBoxLayout(); - horizontalLayout->setContentsMargins(0, 0, 0, 0); - horizontalLayout->addItem(new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum)); - - throbberLabel = new QLabel(this); - throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); - horizontalLayout->addWidget(throbberLabel); - - saveButton = new QPushButton(tr("Save"), this); - saveButton->setDefault( true ); - connect(saveButton, SIGNAL(clicked()), SLOT(handleSave())); - horizontalLayout->addWidget(saveButton); +QtProfileWindow::QtProfileWindow() : + QWidget(), + ui(new Ui::QtProfileWindow) { + ui->setupUi(this); + new QShortcut(QKeySequence::Close, this, SLOT(close())); + ui->throbberLabel->setMovie(new QMovie(":/icons/throbber.gif", QByteArray(), this)); + connect(ui->savePushButton, SIGNAL(clicked()), SLOT(handleSave())); + setEditable(false); + setAttribute(Qt::WA_DeleteOnClose); +} - fieldsLayout->addLayout(horizontalLayout); +QtProfileWindow::~QtProfileWindow() { + delete ui; +} - resize(360, 120); +void QtProfileWindow::setJID(const JID& jid) { + this->jid = jid; + updateTitle(); } -void QtProfileWindow::setVCard(Swift::VCard::ref vcard) { - this->vcard = vcard; - nickname->setText(P2QSTRING(vcard->getNickname())); - avatar->setAvatar(vcard->getPhoto(), vcard->getPhotoType()); +void QtProfileWindow::setVCard(VCard::ref vcard) { + ui->vcard->setVCard(vcard); } void QtProfileWindow::setEnabled(bool b) { - nickname->setEnabled(b); - nicknameLabel->setEnabled(b); - avatar->setEnabled(b); - saveButton->setEnabled(b); + ui->vcard->setEnabled(b); + ui->savePushButton->setEnabled(b); +} + +void QtProfileWindow::setEditable(bool b) { + if (b) { + ui->savePushButton->show(); + ui->vcard->setEditable(true); + } else { + ui->savePushButton->hide(); + ui->vcard->setEditable(false); + } + updateTitle(); } void QtProfileWindow::setProcessing(bool processing) { if (processing) { - throbberLabel->movie()->start(); - throbberLabel->show(); + ui->throbberLabel->movie()->start(); + ui->throbberLabel->show(); + } + else { + ui->throbberLabel->hide(); + ui->throbberLabel->movie()->stop(); + } +} + +void QtProfileWindow::setError(const std::string& error) { + if (!error.empty()) { + ui->errorLabel->setText("<font color='red'>" + QtUtilities::htmlEscape(P2QSTRING(error)) + "</font>"); } else { - throbberLabel->hide(); - throbberLabel->movie()->stop(); + ui->errorLabel->setText(""); } } @@ -103,31 +88,30 @@ void QtProfileWindow::show() { QWidget::activateWindow(); } -void QtProfileWindow::hideEvent(QHideEvent* event) { - QWidget::hideEvent(event); -} - void QtProfileWindow::hide() { QWidget::hide(); } -void QtProfileWindow::handleSave() { - assert(vcard); - vcard->setNickname(Q2PSTRING(nickname->text())); - vcard->setPhoto(avatar->getAvatarData()); - vcard->setPhotoType(avatar->getAvatarType()); - onVCardChangeRequest(vcard); -} - -void QtProfileWindow::setError(const std::string& error) { - if (!error.empty()) { - errorLabel->setText("<font color='red'>" + P2QSTRING(error) + "</font>"); +void QtProfileWindow::updateTitle() { + QString jidString; + if (jid.isValid()) { + jidString = QString(" ( %1 )").arg(P2QSTRING(jid.toString())); } - else { - errorLabel->setText(""); + + if (ui->vcard->isEditable()) { + setWindowTitle(tr("Edit Profile") + jidString); + } else { + setWindowTitle(tr("Show Profile") + jidString); } } +void QtProfileWindow::closeEvent(QCloseEvent* event) { + event->accept(); + onWindowAboutToBeClosed(jid); +} +void QtProfileWindow::handleSave() { + onVCardChangeRequest(ui->vcard->getVCard()); +} } diff --git a/Swift/QtUI/QtProfileWindow.h b/Swift/QtUI/QtProfileWindow.h index edb9cce..a2af63a 100644 --- a/Swift/QtUI/QtProfileWindow.h +++ b/Swift/QtUI/QtProfileWindow.h @@ -4,45 +4,54 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + #pragma once -#include <QWidget> +#include <Swiften/JID/JID.h> #include <Swift/Controllers/UIInterfaces/ProfileWindow.h> -class QLabel; -class QLineEdit; -class QHBoxLayout; -class QPushButton; +#include <QWidget> + +namespace Ui { + class QtProfileWindow; +} namespace Swift { - class QtAvatarWidget; - - class QtProfileWindow : public QWidget, public ProfileWindow { - Q_OBJECT - public: - QtProfileWindow(); - - void setVCard(Swift::VCard::ref); - void setEnabled(bool); - void setProcessing(bool); - virtual void setError(const std::string&); - void show(); - void hide(); - - void hideEvent (QHideEvent* event); - - private slots: - void handleSave(); - - private: - VCard::ref vcard; - QtAvatarWidget* avatar; - QLabel* nicknameLabel; - QLineEdit* nickname; - QLabel* throbberLabel; - QLabel* errorLabel; - QHBoxLayout* horizontalLayout; - QPushButton* saveButton; - }; + +class QtProfileWindow : public QWidget, public ProfileWindow { + Q_OBJECT + + public: + QtProfileWindow(); + virtual ~QtProfileWindow(); + + virtual void setJID(const JID& jid); + virtual void setVCard(VCard::ref vcard); + + virtual void setEnabled(bool b); + virtual void setProcessing(bool processing); + virtual void setError(const std::string& error); + virtual void setEditable(bool b); + + virtual void show(); + virtual void hide(); + + private: + void updateTitle(); + virtual void closeEvent(QCloseEvent* event); + + private slots: + void handleSave(); + + private: + Ui::QtProfileWindow* ui; + JID jid; +}; + } diff --git a/Swift/QtUI/QtProfileWindow.ui b/Swift/QtUI/QtProfileWindow.ui new file mode 100644 index 0000000..68a36da --- /dev/null +++ b/Swift/QtUI/QtProfileWindow.ui @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtProfileWindow</class> + <widget class="QWidget" name="QtProfileWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>334</width> + <height>197</height> + </rect> + </property> + <property name="windowTitle"> + <string>Edit Profile</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0"> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="Swift::QtVCardWidget" name="vcard" native="true"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="errorLabel"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="throbberLabel"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="textInteractionFlags"> + <set>Qt::NoTextInteraction</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="savePushButton"> + <property name="text"> + <string>Save</string> + </property> + <property name="default"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtVCardWidget</class> + <extends>QWidget</extends> + <header>Swift/QtUI/QtVCardWidget/QtVCardWidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtRosterHeader.cpp b/Swift/QtUI/QtRosterHeader.cpp index d32a12e..44459d5 100644 --- a/Swift/QtUI/QtRosterHeader.cpp +++ b/Swift/QtUI/QtRosterHeader.cpp @@ -23,7 +23,7 @@ #include "QtScaledAvatarCache.h" namespace Swift { -QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QWidget(parent) { +QtRosterHeader::QtRosterHeader(SettingsProvider* settings, StatusCache* statusCache, QWidget* parent) : QWidget(parent) { QHBoxLayout* topLayout = new QHBoxLayout(); topLayout->setSpacing(3); topLayout->setContentsMargins(4,4,4,4); @@ -62,7 +62,7 @@ QtRosterHeader::QtRosterHeader(SettingsProvider* settings, QWidget* parent) : QW rightLayout->addLayout(nameAndSecurityLayout); - statusWidget_ = new QtStatusWidget(this); + statusWidget_ = new QtStatusWidget(statusCache, this); connect(statusWidget_, SIGNAL(onChangeStatusRequest(StatusShow::Type, const QString&)), this, SLOT(handleChangeStatusRequest(StatusShow::Type, const QString&))); rightLayout->addWidget(statusWidget_); diff --git a/Swift/QtUI/QtRosterHeader.h b/Swift/QtUI/QtRosterHeader.h index 9527cf4..ad19178 100644 --- a/Swift/QtUI/QtRosterHeader.h +++ b/Swift/QtUI/QtRosterHeader.h @@ -24,11 +24,12 @@ namespace Swift { class QtStatusWidget; class QtNameWidget; class SettingsProvider; + class StatusCache; class QtRosterHeader : public QWidget { Q_OBJECT public: - QtRosterHeader(SettingsProvider* settings, QWidget* parent = NULL); + QtRosterHeader(SettingsProvider* settings, StatusCache* statusCache, QWidget* parent = NULL); void setAvatar(const QString& path); void setJID(const QString& jid); diff --git a/Swift/QtUI/QtSettingsProvider.cpp b/Swift/QtUI/QtSettingsProvider.cpp index 64e88d4..a98cdf3 100644 --- a/Swift/QtUI/QtSettingsProvider.cpp +++ b/Swift/QtUI/QtSettingsProvider.cpp @@ -108,7 +108,7 @@ QSettings* QtSettingsProvider::getQSettings() { } void QtSettingsProvider::updatePermissions() { -#if !defined(Q_WS_WIN) && !defined(Q_WS_MAC) +#if !defined(Q_OS_WIN) && !defined(Q_OS_MAC) QFile file(settings_.fileName()); if (file.exists()) { file.setPermissions(QFile::ReadOwner|QFile::WriteOwner); diff --git a/Swift/QtUI/QtSingleWindow.cpp b/Swift/QtUI/QtSingleWindow.cpp new file mode 100644 index 0000000..c970296 --- /dev/null +++ b/Swift/QtUI/QtSingleWindow.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2010-2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#include <Swift/QtUI/QtSingleWindow.h> + + +#include <Swiften/Base/foreach.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Swift/QtUI/QtChatTabs.h> + +namespace Swift { + +static const QString SINGLE_WINDOW_GEOMETRY = QString("SINGLE_WINDOW_GEOMETRY"); +static const QString SINGLE_WINDOW_SPLITS = QString("SINGLE_WINDOW_SPLITS"); + +QtSingleWindow::QtSingleWindow(QtSettingsProvider* settings) : QSplitter() { + settings_ = settings; + QVariant geometryVariant = settings_->getQSettings()->value(SINGLE_WINDOW_GEOMETRY); + if (geometryVariant.isValid()) { + restoreGeometry(geometryVariant.toByteArray()); + } + connect(this, SIGNAL(splitterMoved(int, int)), this, SLOT(handleSplitterMoved(int, int))); + restoreSplitters(); +} + +QtSingleWindow::~QtSingleWindow() { + +} + +void QtSingleWindow::addWidget(QWidget* widget) { + QtChatTabs* tabs = dynamic_cast<QtChatTabs*>(widget); + if (tabs) { + connect(tabs, SIGNAL(onTitleChanged(const QString&)), this, SLOT(handleTabsTitleChanged(const QString&))); + } + QSplitter::addWidget(widget); +} + +void QtSingleWindow::handleTabsTitleChanged(const QString& title) { + setWindowTitle(title); +} + +void QtSingleWindow::handleSplitterMoved(int, int) { + QList<QVariant> variantValues; + QList<int> intValues = sizes(); + foreach (int value, intValues) { + variantValues.append(QVariant(value)); + } + settings_->getQSettings()->setValue(SINGLE_WINDOW_SPLITS, QVariant(variantValues)); +} + +void QtSingleWindow::restoreSplitters() { + QList<QVariant> variantValues = settings_->getQSettings()->value(SINGLE_WINDOW_SPLITS).toList(); + QList<int> intValues; + foreach (QVariant value, variantValues) { + intValues.append(value.toInt()); + } + setSizes(intValues); +} + +void QtSingleWindow::insertAtFront(QWidget* widget) { + insertWidget(0, widget); + restoreSplitters(); +} + +void QtSingleWindow::handleGeometryChanged() { + settings_->getQSettings()->setValue(SINGLE_WINDOW_GEOMETRY, saveGeometry()); + +} + +void QtSingleWindow::resizeEvent(QResizeEvent*) { + handleGeometryChanged(); +} + +void QtSingleWindow::moveEvent(QMoveEvent*) { + handleGeometryChanged(); +} + +} diff --git a/Swift/QtUI/QtSingleWindow.h b/Swift/QtUI/QtSingleWindow.h new file mode 100644 index 0000000..b55b3c9 --- /dev/null +++ b/Swift/QtUI/QtSingleWindow.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-2012 Kevin Smith + * Licensed under the GNU General Public License v3. + * See Documentation/Licenses/GPLv3.txt for more information. + */ + +#pragma once + +#include <QSplitter> + +namespace Swift { + class QtSettingsProvider; + + class QtSingleWindow : public QSplitter { + Q_OBJECT + public: + QtSingleWindow(QtSettingsProvider* settings); + virtual ~QtSingleWindow(); + void insertAtFront(QWidget* widget); + void addWidget(QWidget* widget); + protected: + void resizeEvent(QResizeEvent*); + void moveEvent(QMoveEvent*); + private slots: + void handleSplitterMoved(int, int); + void handleTabsTitleChanged(const QString& title); + private: + void handleGeometryChanged(); + void restoreSplitters(); + + private: + + QtSettingsProvider* settings_; + }; + +} + diff --git a/Swift/QtUI/QtSoundPlayer.cpp b/Swift/QtUI/QtSoundPlayer.cpp index 387c6f3..3f3782d 100644 --- a/Swift/QtUI/QtSoundPlayer.cpp +++ b/Swift/QtUI/QtSoundPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -9,17 +9,20 @@ #include <QSound> #include <iostream> -#include "SwifTools/Application/ApplicationPathProvider.h" +#include <SwifTools/Application/ApplicationPathProvider.h> +#include <QtSwiftUtil.h> +#include <Swiften/Base/Path.h> namespace Swift { QtSoundPlayer::QtSoundPlayer(ApplicationPathProvider* applicationPathProvider) : applicationPathProvider(applicationPathProvider) { } -void QtSoundPlayer::playSound(SoundEffect sound) { +void QtSoundPlayer::playSound(SoundEffect sound, const std::string& soundResource) { + switch (sound) { case MessageReceived: - playSound("/sounds/message-received.wav"); + playSound(soundResource.empty() ? "/sounds/message-received.wav" : soundResource); break; } } @@ -27,7 +30,10 @@ void QtSoundPlayer::playSound(SoundEffect sound) { void QtSoundPlayer::playSound(const std::string& soundResource) { boost::filesystem::path resourcePath = applicationPathProvider->getResourcePath(soundResource); if (boost::filesystem::exists(resourcePath)) { - QSound::play(resourcePath.string().c_str()); + QSound::play(P2QSTRING(pathToString(resourcePath))); + } + else if (boost::filesystem::exists(soundResource)) { + QSound::play(P2QSTRING(soundResource)); } else { std::cerr << "Unable to find sound: " << soundResource << std::endl; diff --git a/Swift/QtUI/QtSoundPlayer.h b/Swift/QtUI/QtSoundPlayer.h index 6945f45..f8da392 100644 --- a/Swift/QtUI/QtSoundPlayer.h +++ b/Swift/QtUI/QtSoundPlayer.h @@ -19,7 +19,7 @@ namespace Swift { public: QtSoundPlayer(ApplicationPathProvider* applicationPathProvider); - void playSound(SoundEffect sound); + void playSound(SoundEffect sound, const std::string& soundResource); private: void playSound(const std::string& soundResource); diff --git a/Swift/QtUI/QtSpellCheckerWindow.cpp b/Swift/QtUI/QtSpellCheckerWindow.cpp new file mode 100644 index 0000000..db2b1e7 --- /dev/null +++ b/Swift/QtUI/QtSpellCheckerWindow.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "Swift/QtUI/QtSpellCheckerWindow.h" + +#include <Swift/Controllers/Settings/SettingsProvider.h> +#include <Swift/Controllers/SettingConstants.h> +#include <Swift/QtUI/QtUISettingConstants.h> +#include <Swift/QtUI/QtSwiftUtil.h> + +#include <QCoreApplication> +#include <QFileDialog> +#include <QDir> +#include <QStringList> +#include <QTimer> + +namespace Swift { + +QtSpellCheckerWindow::QtSpellCheckerWindow(SettingsProvider* settings, QWidget* parent) : QDialog(parent) { + settings_ = settings; + ui_.setupUi(this); +#ifdef HAVE_HUNSPELL + ui_.hunspellOptions->show(); +#else + ui_.hunspellOptions->hide(); + QTimer::singleShot(0, this, SLOT(shrinkWindow())); +#endif + connect(ui_.spellChecker, SIGNAL(toggled(bool)), this, SLOT(handleChecker(bool))); + connect(ui_.cancel, SIGNAL(clicked()), this, SLOT(handleCancel())); + connect(ui_.apply, SIGNAL(clicked()), this, SLOT(handleApply())); + connect(ui_.pathButton, SIGNAL(clicked()), this, SLOT(handlePathButton())); + setFromSettings(); +} + +void QtSpellCheckerWindow::shrinkWindow() { + resize(0,0); +} + +void QtSpellCheckerWindow::setFromSettings() { + ui_.spellChecker->setChecked(settings_->getSetting(SettingConstants::SPELL_CHECKER)); + ui_.pathContent->setText(P2QSTRING(settings_->getSetting(SettingConstants::DICT_PATH))); + ui_.currentLanguageValue->setText(P2QSTRING(settings_->getSetting(SettingConstants::DICT_FILE))); + std::string currentPath = settings_->getSetting(SettingConstants::DICT_PATH); + QString filename = "*.dic"; + QDir dictDirectory = QDir(P2QSTRING(currentPath)); + QStringList files = dictDirectory.entryList(QStringList(filename), QDir::Files); + showFiles(files); + setEnabled(settings_->getSetting(SettingConstants::SPELL_CHECKER)); +} + +void QtSpellCheckerWindow::handleChecker(bool state) { + setEnabled(state); +} + +void QtSpellCheckerWindow::setEnabled(bool state) { + ui_.pathContent->setEnabled(state); + ui_.languageView->setEnabled(state); + ui_.pathButton->setEnabled(state); + ui_.pathLabel->setEnabled(state); + ui_.currentLanguage->setEnabled(state); + ui_.currentLanguageValue->setEnabled(state); + ui_.language->setEnabled(state); +} + +void QtSpellCheckerWindow::handleApply() { + settings_->storeSetting(SettingConstants::SPELL_CHECKER, ui_.spellChecker->isChecked()); + QList<QListWidgetItem* > selectedLanguage = ui_.languageView->selectedItems(); + if (!selectedLanguage.empty()) { + settings_->storeSetting(SettingConstants::DICT_FILE, Q2PSTRING((selectedLanguage.first())->text())); + } + this->done(0); +} + +void QtSpellCheckerWindow::handleCancel() { + this->done(0); +} + +void QtSpellCheckerWindow::handlePathButton() { + std::string currentPath = settings_->getSetting(SettingConstants::DICT_PATH); + QString dirpath = QFileDialog::getExistingDirectory(this, tr("Dictionary Path"), P2QSTRING(currentPath)); + if (dirpath != P2QSTRING(currentPath)) { + ui_.languageView->clear(); + settings_->storeSetting(SettingConstants::DICT_FILE, ""); + ui_.currentLanguageValue->setText(" "); + } + if (!dirpath.isEmpty()) { + if (!dirpath.endsWith("/")) { + dirpath.append("/"); + } + settings_->storeSetting(SettingConstants::DICT_PATH, Q2PSTRING(dirpath)); + QDir dictDirectory = QDir(dirpath); + ui_.pathContent->setText(dirpath); + QString filename = "*.dic"; + QStringList files = dictDirectory.entryList(QStringList(filename), QDir::Files); + showFiles(files); + } +} + +void QtSpellCheckerWindow::handlePersonalPathButton() { + std::string currentPath = settings_->getSetting(SettingConstants::PERSONAL_DICT_PATH); + QString filename = QFileDialog::getOpenFileName(this, tr("Select Personal Dictionary"), P2QSTRING(currentPath), tr("(*.dic")); + settings_->storeSetting(SettingConstants::PERSONAL_DICT_PATH, Q2PSTRING(filename)); +} + +void QtSpellCheckerWindow::showFiles(const QStringList& files) { + ui_.languageView->clear(); + for (int i = 0; i < files.size(); ++i) { + ui_.languageView->insertItem(i, files[i]); + } +} + +} diff --git a/Swift/QtUI/QtSpellCheckerWindow.h b/Swift/QtUI/QtSpellCheckerWindow.h new file mode 100644 index 0000000..7b63318 --- /dev/null +++ b/Swift/QtUI/QtSpellCheckerWindow.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Vlad Voicu + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include "ui_QtSpellCheckerWindow.h" + +#include <QDialog> + +namespace Swift { + class SettingsProvider; + class QtSpellCheckerWindow : public QDialog, protected Ui::QtSpellCheckerWindow { + Q_OBJECT + public: + QtSpellCheckerWindow(SettingsProvider* settings, QWidget* parent = NULL); + public slots: + void handleChecker(bool state); + void handleCancel(); + void handlePathButton(); + void handlePersonalPathButton(); + void handleApply(); + private slots: + void shrinkWindow(); + private: + void setEnabled(bool state); + void setFromSettings(); + void showFiles(const QStringList& files); + SettingsProvider* settings_; + Ui::QtSpellCheckerWindow ui_; + }; +} diff --git a/Swift/QtUI/QtSpellCheckerWindow.ui b/Swift/QtUI/QtSpellCheckerWindow.ui new file mode 100644 index 0000000..b7f5161 --- /dev/null +++ b/Swift/QtUI/QtSpellCheckerWindow.ui @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtSpellCheckerWindow</class> + <widget class="QDialog" name="QtSpellCheckerWindow"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>399</width> + <height>265</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="spellChecker"> + <property name="text"> + <string>Spell Checker Enabled</string> + </property> + </widget> + </item> + <item> + <widget class="QWidget" name="hunspellOptions" native="true"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="pathLabel"> + <property name="text"> + <string>Dictionary Path:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="pathContent"/> + </item> + <item> + <widget class="QPushButton" name="pathButton"> + <property name="text"> + <string>Change</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="currentLanguage"> + <property name="text"> + <string>Current Language:</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="currentLanguageValue"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QLabel" name="language"> + <property name="text"> + <string>Language:</string> + </property> + </widget> + </item> + <item> + <widget class="QListWidget" name="languageView"/> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QPushButton" name="cancel"> + <property name="text"> + <string>Cancel</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="apply"> + <property name="text"> + <string>Apply</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtStatusWidget.cpp b/Swift/QtUI/QtStatusWidget.cpp index 0e2731a..8cc366a 100644 --- a/Swift/QtUI/QtStatusWidget.cpp +++ b/Swift/QtUI/QtStatusWidget.cpp @@ -6,6 +6,10 @@ #include "QtStatusWidget.h" +#include <algorithm> +#include <boost/lambda/lambda.hpp> +#include <boost/lambda/bind.hpp> + #include <QBoxLayout> #include <QComboBox> #include <QLabel> @@ -23,10 +27,20 @@ #include "Swift/QtUI/QtLineEdit.h" #include "Swift/QtUI/QtSwiftUtil.h" #include <Swift/Controllers/StatusUtil.h> +#include <Swift/Controllers/StatusCache.h> + +namespace lambda = boost::lambda; namespace Swift { -QtStatusWidget::QtStatusWidget(QWidget *parent) : QWidget(parent), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) { +QtStatusWidget::QtStatusWidget(StatusCache* statusCache, QWidget *parent) : QWidget(parent), statusCache_(statusCache), editCursor_(Qt::IBeamCursor), viewCursor_(Qt::PointingHandCursor) { + allTypes_.push_back(StatusShow::Online); + allTypes_.push_back(StatusShow::FFC); + allTypes_.push_back(StatusShow::Away); + allTypes_.push_back(StatusShow::XA); + allTypes_.push_back(StatusShow::DND); + allTypes_.push_back(StatusShow::None); + isClicking_ = false; connecting_ = false; setMaximumHeight(24); @@ -134,15 +148,42 @@ void QtStatusWidget::generateList() { item->setStatusTip(item->toolTip()); item->setData(Qt::UserRole, QVariant(type)); } + std::vector<StatusCache::PreviousStatus> previousStatuses = statusCache_->getMatches(Q2PSTRING(text), 8); + foreach (StatusCache::PreviousStatus savedStatus, previousStatuses) { + if (savedStatus.first.empty() || std::find_if(allTypes_.begin(), allTypes_.end(), + savedStatus.second == lambda::_1 && savedStatus.first == lambda::bind(&statusShowTypeToFriendlyName, lambda::_1)) != allTypes_.end()) { + continue; + } + QListWidgetItem* item = new QListWidgetItem(P2QSTRING(savedStatus.first), menu_); + item->setIcon(icons_[savedStatus.second]); + item->setToolTip(item->text()); + item->setStatusTip(item->toolTip()); + item->setData(Qt::UserRole, QVariant(savedStatus.second)); + } foreach (StatusShow::Type type, icons_.keys()) { + if (Q2PSTRING(text) == statusShowTypeToFriendlyName(type)) { + continue; + } QListWidgetItem* item = new QListWidgetItem(P2QSTRING(statusShowTypeToFriendlyName(type)), menu_); item->setIcon(icons_[type]); item->setToolTip(item->text()); item->setStatusTip(item->toolTip()); item->setData(Qt::UserRole, QVariant(type)); } + resizeMenu(); } +void QtStatusWidget::resizeMenu() { + int height = menu_->sizeHintForRow(0) * menu_->count(); + int marginLeft; + int marginTop; + int marginRight; + int marginBottom; + menu_->getContentsMargins(&marginLeft, &marginTop, &marginRight, &marginBottom); + height += marginTop + marginBottom; + + menu_->setGeometry(menu_->x(), menu_->y(), menu_->width(), height); +} void QtStatusWidget::handleClicked() { editing_ = true; @@ -159,18 +200,11 @@ void QtStatusWidget::handleClicked() { if (x + width > screenWidth) { x = screenWidth - width; } - std::vector<StatusShow::Type> types; - types.push_back(StatusShow::Online); - types.push_back(StatusShow::FFC); - types.push_back(StatusShow::Away); - types.push_back(StatusShow::XA); - types.push_back(StatusShow::DND); - types.push_back(StatusShow::None); - foreach (StatusShow::Type type, types) { - if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) { + //foreach (StatusShow::Type type, allTypes_) { + // if (statusEdit_->text() == P2QSTRING(statusShowTypeToFriendlyName(type))) { statusEdit_->setText(""); - } - } + // } + //} generateList(); height = menu_->sizeHintForRow(0) * menu_->count(); @@ -197,7 +231,7 @@ void QtStatusWidget::viewMode() { disconnect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), this, SLOT(handleApplicationFocusChanged(QWidget*, QWidget*))); editing_ = false; menu_->hide(); - stack_->setCurrentIndex(0); + stack_->setCurrentIndex(0); } void QtStatusWidget::handleEditComplete() { @@ -205,6 +239,7 @@ void QtStatusWidget::handleEditComplete() { statusText_ = newStatusText_; viewMode(); emit onChangeStatusRequest(selectedStatusType_, statusText_); + statusCache_->addRecent(Q2PSTRING(statusText_), selectedStatusType_); } void QtStatusWidget::handleEditCancelled() { diff --git a/Swift/QtUI/QtStatusWidget.h b/Swift/QtUI/QtStatusWidget.h index 75bcf52..87e8d4a 100644 --- a/Swift/QtUI/QtStatusWidget.h +++ b/Swift/QtUI/QtStatusWidget.h @@ -22,10 +22,12 @@ class QMovie; namespace Swift { class QtLineEdit; class QtElidingLabel; + class StatusCache; + class QtStatusWidget : public QWidget { Q_OBJECT public: - QtStatusWidget(QWidget *parent); + QtStatusWidget(StatusCache* statusCache, QWidget *parent); ~QtStatusWidget(); StatusShow::Type getSelectedStatusShow(); void setStatusType(StatusShow::Type type); @@ -45,9 +47,11 @@ namespace Swift { void handleItemClicked(QListWidgetItem* item); static QString getNoMessage(); private: + void resizeMenu(); void viewMode(); void setNewToolTip(); //QComboBox *types_; + StatusCache* statusCache_; QStackedWidget* stack_; QLabel* statusIcon_; QtElidingLabel* statusTextLabel_; @@ -64,6 +68,7 @@ namespace Swift { QMovie* connectingMovie_; bool connecting_; static const QString NO_MESSAGE; + std::vector<StatusShow::Type> allTypes_; }; } diff --git a/Swift/QtUI/QtSwift.cpp b/Swift/QtUI/QtSwift.cpp index 223f3ae..4d4cef9 100644 --- a/Swift/QtUI/QtSwift.cpp +++ b/Swift/QtUI/QtSwift.cpp @@ -1,74 +1,81 @@ /* - * Copyright (c) 2010-2012 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "QtSwift.h" +#include <Swift/QtUI/QtSwift.h> #include <string> -#include <QSplitter> -#include <QFile> +#include <map> + #include <boost/bind.hpp> + +#include <QFile> #include <QMessageBox> #include <QApplication> #include <QMap> #include <qdebug.h> -#include <QtLoginWindow.h> -#include <QtChatTabs.h> -#include <QtSystemTray.h> -#include <QtSoundPlayer.h> -#include <QtSwiftUtil.h> -#include <QtUIFactory.h> -#include <QtChatWindowFactory.h> #include <Swiften/Base/Log.h> -#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h> -#include <Swift/Controllers/Storages/FileStoragesFactory.h> -#include <SwifTools/Application/PlatformApplicationPathProvider.h> -#include <string> +#include <Swiften/Base/Path.h> #include <Swiften/Base/Platform.h> #include <Swiften/Elements/Presence.h> #include <Swiften/Client/Client.h> +#include <Swiften/Base/Paths.h> + +#include <SwifTools/Application/PlatformApplicationPathProvider.h> +#include <SwifTools/AutoUpdater/AutoUpdater.h> +#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h> + +#include <Swift/Controllers/Storages/CertificateFileStorageFactory.h> +#include <Swift/Controllers/Storages/FileStoragesFactory.h> #include <Swift/Controllers/Settings/XMLSettingsProvider.h> #include <Swift/Controllers/Settings/SettingsProviderHierachy.h> #include <Swift/Controllers/MainController.h> #include <Swift/Controllers/ApplicationInfo.h> #include <Swift/Controllers/BuildVersion.h> -#include <SwifTools/AutoUpdater/AutoUpdater.h> -#include <SwifTools/AutoUpdater/PlatformAutoUpdaterFactory.h> -#include "Swiften/Base/Paths.h" +#include <Swift/Controllers/StatusCache.h> + +#include <Swift/QtUI/QtLoginWindow.h> +#include <Swift/QtUI/QtChatTabs.h> +#include <Swift/QtUI/QtSystemTray.h> +#include <Swift/QtUI/QtSoundPlayer.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtUIFactory.h> +#include <Swift/QtUI/QtChatWindowFactory.h> +#include <Swift/QtUI/QtSingleWindow.h> #if defined(SWIFTEN_PLATFORM_WINDOWS) -#include "WindowsNotifier.h" +#include <Swift/QtUI/WindowsNotifier.h> #elif defined(HAVE_GROWL) -#include "SwifTools/Notifier/GrowlNotifier.h" +#include <SwifTools/Notifier/GrowlNotifier.h> #elif defined(SWIFTEN_PLATFORM_LINUX) -#include "FreeDesktopNotifier.h" +#include <Swift/QtUI/FreeDesktopNotifier.h> #else -#include "SwifTools/Notifier/NullNotifier.h" +#include <SwifTools/Notifier/NullNotifier.h> #endif #if defined(SWIFTEN_PLATFORM_MACOSX) -#include "SwifTools/Dock/MacOSXDock.h" +#include <SwifTools/Dock/MacOSXDock.h> #else -#include "SwifTools/Dock/NullDock.h" +#include <SwifTools/Dock/NullDock.h> #endif #if defined(SWIFTEN_PLATFORM_MACOSX) -#include "QtURIHandler.h" +#include <Swift/QtUI/QtURIHandler.h> #elif defined(SWIFTEN_PLATFORM_WIN32) #include <SwifTools/URIHandler/NullURIHandler.h> #else -#include "QtDBUSURIHandler.h" +#include <Swift/QtUI/QtDBUSURIHandler.h> #endif namespace Swift{ #if defined(SWIFTEN_PLATFORM_MACOSX) -#define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml" +//#define SWIFT_APPCAST_URL "http://swift.im/appcast/swift-mac-dev.xml" #else -#define SWIFT_APPCAST_URL "" +//#define SWIFT_APPCAST_URL "" #endif po::options_description QtSwift::getOptionsDescription() { @@ -102,44 +109,46 @@ XMLSettingsProvider* QtSwift::loadSettingsFile(const QString& fileName) { return new XMLSettingsProvider(""); } -QMap<QString, QString> QtSwift::loadEmoticonsFile(const QString& fileName) { - QMap<QString, QString> emoticons; +void QtSwift::loadEmoticonsFile(const QString& fileName, std::map<std::string, std::string>& emoticons) { QFile file(fileName); if (file.exists() && file.open(QIODevice::ReadOnly)) { while (!file.atEnd()) { QString line = file.readLine(); line.replace("\n", ""); line.replace("\r", ""); - qDebug() << "Parsing line : " << line; QStringList tokens = line.split(" "); if (tokens.size() == 2) { - emoticons[tokens[0]] = "file://" + tokens[1]; - qDebug() << "Adding mapping from " << tokens[0] << " to " << tokens[1]; + QString emoticonFile = tokens[1]; + if (!emoticonFile.startsWith(":/") && !emoticonFile.startsWith("qrc:/")) { + emoticonFile = "file://" + emoticonFile; + } + emoticons[Q2PSTRING(tokens[0])] = Q2PSTRING(emoticonFile); } } } - - return emoticons; } QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMainThreadCaller_), autoUpdater_(NULL), idleDetector_(&idleQuerier_, networkFactories_.getTimerFactory(), 1000) { - if (options.count("netbook-mode")) { - splitter_ = new QSplitter(); - } else { - splitter_ = NULL; - } QCoreApplication::setApplicationName(SWIFT_APPLICATION_NAME); QCoreApplication::setOrganizationName(SWIFT_ORGANIZATION_NAME); QCoreApplication::setOrganizationDomain(SWIFT_ORGANIZATION_DOMAIN); QCoreApplication::setApplicationVersion(buildVersion); qtSettings_ = new QtSettingsProvider(); - xmlSettings_ = loadSettingsFile(P2QSTRING((Paths::getExecutablePath() / "system-settings.xml").string())); + xmlSettings_ = loadSettingsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "system-settings.xml"))); settingsHierachy_ = new SettingsProviderHierachy(); settingsHierachy_->addProviderToTopOfStack(xmlSettings_); settingsHierachy_->addProviderToTopOfStack(qtSettings_); - QMap<QString, QString> emoticons = loadEmoticonsFile(P2QSTRING((Paths::getExecutablePath() / "emoticons.txt").string())); + std::map<std::string, std::string> emoticons; + loadEmoticonsFile(":/emoticons/emoticons.txt", emoticons); + loadEmoticonsFile(P2QSTRING(pathToString(Paths::getExecutablePath() / "emoticons.txt")), emoticons); + + if (options.count("netbook-mode")) { + splitter_ = new QtSingleWindow(qtSettings_); + } else { + splitter_ = NULL; + } int numberOfAccounts = 1; try { @@ -150,15 +159,15 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa } if (options.count("debug")) { - Swift::logging = true; + Log::setLogLevel(Swift::Log::debug); } - tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(); + tabs_ = options.count("no-tabs") && !splitter_ ? NULL : new QtChatTabs(splitter_ != NULL); bool startMinimized = options.count("start-minimized") > 0; applicationPathProvider_ = new PlatformApplicationPathProvider(SWIFT_APPLICATION_NAME); - storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir()); - certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory()); - chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, "", emoticons); + storagesFactory_ = new FileStoragesFactory(applicationPathProvider_->getDataDir(), networkFactories_.getCryptoProvider()); + certificateStorageFactory_ = new CertificateFileStorageFactory(applicationPathProvider_->getDataDir(), tlsFactories_.getCertificateFactory(), networkFactories_.getCryptoProvider()); + chatWindowFactory_ = new QtChatWindowFactory(splitter_, settingsHierachy_, qtSettings_, tabs_, ""); soundPlayer_ = new QtSoundPlayer(applicationPathProvider_); // Ugly, because the dock depends on the tray, but the temporary @@ -190,6 +199,8 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa uriHandler_ = new QtDBUSURIHandler(); #endif + statusCache_ = new StatusCache(applicationPathProvider_); + if (splitter_) { splitter_->show(); } @@ -199,7 +210,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa // Don't add the first tray (see note above) systemTrays_.push_back(new QtSystemTray()); } - QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), startMinimized, !emoticons.empty()); + QtUIFactory* uiFactory = new QtUIFactory(settingsHierachy_, qtSettings_, tabs_, splitter_, systemTrays_[i], chatWindowFactory_, networkFactories_.getTimerFactory(), statusCache_, startMinimized, !emoticons.empty()); uiFactories_.push_back(uiFactory); MainController* mainController = new MainController( &clientMainThreadCaller_, @@ -214,6 +225,7 @@ QtSwift::QtSwift(const po::variables_map& options) : networkFactories_(&clientMa notifier_, uriHandler_, &idleDetector_, + emoticons, options.count("latency-debug") > 0); mainControllers_.push_back(mainController); } @@ -243,6 +255,7 @@ QtSwift::~QtSwift() { } delete tabs_; delete splitter_; + delete statusCache_; delete uriHandler_; delete dock_; delete soundPlayer_; diff --git a/Swift/QtUI/QtSwift.h b/Swift/QtUI/QtSwift.h index 42fb50f..1ea8886 100644 --- a/Swift/QtUI/QtSwift.h +++ b/Swift/QtUI/QtSwift.h @@ -50,6 +50,8 @@ namespace Swift { class URIHandler; class SettingsProviderHierachy; class XMLSettingsProvider; + class StatusCache; + class QtSingleWindow; class QtSwift : public QObject { Q_OBJECT @@ -59,7 +61,7 @@ namespace Swift { ~QtSwift(); private: XMLSettingsProvider* loadSettingsFile(const QString& fileName); - QMap<QString, QString> loadEmoticonsFile(const QString& fileName); + void loadEmoticonsFile(const QString& fileName, std::map<std::string, std::string>& emoticons); private: QtEventLoop clientMainThreadCaller_; PlatformTLSFactories tlsFactories_; @@ -71,7 +73,7 @@ namespace Swift { QtSettingsProvider* qtSettings_; XMLSettingsProvider* xmlSettings_; SettingsProviderHierachy* settingsHierachy_; - QSplitter* splitter_; + QtSingleWindow* splitter_; QtSoundPlayer* soundPlayer_; Dock* dock_; URIHandler* uriHandler_; @@ -81,6 +83,7 @@ namespace Swift { CertificateStorageFactory* certificateStorageFactory_; AutoUpdater* autoUpdater_; Notifier* notifier_; + StatusCache* statusCache_; PlatformIdleQuerier idleQuerier_; ActualIdleDetector idleDetector_; #if defined(SWIFTEN_PLATFORM_MACOSX) diff --git a/Swift/QtUI/QtSwiftUtil.h b/Swift/QtUI/QtSwiftUtil.h index 2d0f970..c903af1 100644 --- a/Swift/QtUI/QtSwiftUtil.h +++ b/Swift/QtUI/QtSwiftUtil.h @@ -9,4 +9,6 @@ #define P2QSTRING(a) QString::fromUtf8(a.c_str()) #define Q2PSTRING(a) std::string(a.toUtf8()) +#include <boost/date_time/posix_time/posix_time.hpp> + #define B2QDATE(a) QDateTime::fromTime_t((a - boost::posix_time::from_time_t(0)).total_seconds()) diff --git a/Swift/QtUI/QtSystemTray.cpp b/Swift/QtUI/QtSystemTray.cpp index 2f45709..456a56f 100644 --- a/Swift/QtUI/QtSystemTray.cpp +++ b/Swift/QtUI/QtSystemTray.cpp @@ -9,7 +9,7 @@ #include "Swift/QtUI/QtSystemTray.h" #include <QtDebug> -#ifdef Q_WS_X11 +#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) #include <QDBusInterface> #endif #include <QIcon> @@ -24,7 +24,7 @@ QtSystemTray::QtSystemTray() : QObject(), trayMenu_(0), onlineIcon_(":icons/onli trayIcon_->setToolTip("Swift"); connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(handleIconActivated(QSystemTrayIcon::ActivationReason))); connect(&throbberMovie_, SIGNAL(frameChanged(int)), this, SLOT(handleThrobberFrameChanged(int))); -#ifdef Q_WS_X11 +#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) bool isUnity = QDBusInterface("com.canonical.Unity.Launcher", "/com/canonical/Unity/Launcher").isValid(); if (isUnity) { // Add an activation menu, because this is the only way to get the application to the diff --git a/Swift/QtUI/QtTabbable.h b/Swift/QtUI/QtTabbable.h index baab15c..0b67b19 100644 --- a/Swift/QtUI/QtTabbable.h +++ b/Swift/QtUI/QtTabbable.h @@ -17,10 +17,10 @@ namespace Swift { enum AlertType {NoActivity, WaitingActivity, ImpendingActivity}; ~QtTabbable(); bool isWidgetSelected(); - virtual AlertType getWidgetAlertState() {return NoActivity;}; + virtual AlertType getWidgetAlertState() {return NoActivity;} virtual int getCount() {return 0;} protected: - QtTabbable() : QWidget() {}; + QtTabbable() : QWidget() {} void keyPressEvent(QKeyEvent* event); protected slots: diff --git a/Swift/QtUI/QtTextEdit.cpp b/Swift/QtUI/QtTextEdit.cpp index 3a62325..2c4677e 100644 --- a/Swift/QtUI/QtTextEdit.cpp +++ b/Swift/QtUI/QtTextEdit.cpp @@ -4,17 +4,41 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ +#include <boost/tuple/tuple.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/bind.hpp> + +#include <Swiften/Base/foreach.h> + +#include <SwifTools/SpellCheckerFactory.h> +#include <SwifTools/SpellChecker.h> + #include <Swift/QtUI/QtTextEdit.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtSpellCheckerWindow.h> +#include <Swift/Controllers/SettingConstants.h> +#include <QApplication> #include <QFontMetrics> #include <QKeyEvent> +#include <QDebug> +#include <QMenu> namespace Swift { -QtTextEdit::QtTextEdit(QWidget* parent) : QTextEdit(parent){ +QtTextEdit::QtTextEdit(SettingsProvider* settings, QWidget* parent) : QTextEdit(parent) { connect(this, SIGNAL(textChanged()), this, SLOT(handleTextChanged())); + checker_ = NULL; + settings_ = settings; +#ifdef HAVE_SPELLCHECKER + setUpSpellChecker(); +#endif handleTextChanged(); -}; +} + +QtTextEdit::~QtTextEdit() { + delete checker_; +} void QtTextEdit::keyPressEvent(QKeyEvent* event) { int key = event->key(); @@ -35,13 +59,41 @@ void QtTextEdit::keyPressEvent(QKeyEvent* event) { emit unhandledKeyPressEvent(event); } else if ((key == Qt::Key_Up) - || (key == Qt::Key_Down) - ){ + || (key == Qt::Key_Down)) { emit unhandledKeyPressEvent(event); QTextEdit::keyPressEvent(event); } else { QTextEdit::keyPressEvent(event); +#ifdef HAVE_SPELLCHECKER + if (settings_->getSetting(SettingConstants::SPELL_CHECKER)) { + underlineMisspells(); + } +#endif + } +} + +void QtTextEdit::underlineMisspells() { + QTextCursor cursor = textCursor(); + misspelledPositions_.clear(); + QTextCharFormat normalFormat; + cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor, 1); + cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor, 1); + cursor.setCharFormat(normalFormat); + if (checker_ == NULL) { + return; + } + QTextCharFormat spellingErrorFormat; + spellingErrorFormat.setUnderlineColor(QColor(Qt::red)); + spellingErrorFormat.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); + std::string fragment = Q2PSTRING(cursor.selectedText()); + checker_->checkFragment(fragment, misspelledPositions_); + foreach (PositionPair position, misspelledPositions_) { + cursor.setPosition(boost::get<0>(position), QTextCursor::MoveAnchor); + cursor.setPosition(boost::get<1>(position), QTextCursor::KeepAnchor); + cursor.setCharFormat(spellingErrorFormat); + cursor.clearSelection(); + cursor.setCharFormat(normalFormat); } } @@ -53,6 +105,24 @@ void QtTextEdit::handleTextChanged() { } } +void QtTextEdit::replaceMisspelledWord(const QString& word, int cursorPosition) { + QTextCursor cursor = textCursor(); + PositionPair wordPosition = getWordFromCursor(cursorPosition); + cursor.setPosition(boost::get<0>(wordPosition), QTextCursor::MoveAnchor); + cursor.setPosition(boost::get<1>(wordPosition), QTextCursor::KeepAnchor); + QTextCharFormat normalFormat; + cursor.insertText(word, normalFormat); +} + +PositionPair QtTextEdit::getWordFromCursor(int cursorPosition) { + for (PositionPairList::iterator it = misspelledPositions_.begin(); it != misspelledPositions_.end(); ++it) { + if (cursorPosition >= boost::get<0>(*it) && cursorPosition <= boost::get<1>(*it)) { + return *it; + } + } + return boost::make_tuple(-1,-1); +} + QSize QtTextEdit::sizeHint() const { QFontMetrics inputMetrics(currentFont()); QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999); @@ -66,7 +136,100 @@ QSize QtTextEdit::sizeHint() const { //return QSize(QTextEdit::sizeHint().width(), lineHeight * numberOfLines); } +void QtTextEdit::contextMenuEvent(QContextMenuEvent* event) { + QMenu* menu = createStandardContextMenu(); + QTextCursor cursor = cursorForPosition(event->pos()); +#ifdef HAVE_SPELLCHECKER + QAction* insertPoint = menu->actions().first(); + QAction* settingsAction = new QAction(tr("Spell Checker Options"), menu); + menu->insertAction(insertPoint, settingsAction); + menu->insertAction(insertPoint, menu->addSeparator()); + addSuggestions(menu, event); + QAction* result = menu->exec(event->globalPos()); + if (result == settingsAction) { + spellCheckerSettingsWindow(); + } + for (std::vector<QAction*>::iterator it = replaceWordActions_.begin(); it != replaceWordActions_.end(); ++it) { + if (*it == result) { + replaceMisspelledWord((*it)->text(), cursor.position()); + } + } +#else + menu->exec(event->globalPos()); +#endif + delete menu; +} + +void QtTextEdit::addSuggestions(QMenu* menu, QContextMenuEvent* event) +{ + replaceWordActions_.clear(); + QAction* insertPoint = menu->actions().first(); + QTextCursor cursor = cursorForPosition(event->pos()); + PositionPair wordPosition = getWordFromCursor(cursor.position()); + if (boost::get<0>(wordPosition) < 0) { + // The click was executed outside a spellable word so no + // suggestions are necessary + return; + } + cursor.setPosition(boost::get<0>(wordPosition), QTextCursor::MoveAnchor); + cursor.setPosition(boost::get<1>(wordPosition), QTextCursor::KeepAnchor); + std::vector<std::string> wordList; + checker_->getSuggestions(Q2PSTRING(cursor.selectedText()), wordList); + if (wordList.size() == 0) { + QAction* noSuggestions = new QAction(tr("No Suggestions"), menu); + noSuggestions->setDisabled(true); + menu->insertAction(insertPoint, noSuggestions); + } + else { + for (std::vector<std::string>::iterator it = wordList.begin(); it != wordList.end(); ++it) { + QAction* wordAction = new QAction(it->c_str(), menu); + menu->insertAction(insertPoint, wordAction); + replaceWordActions_.push_back(wordAction); + } + } + menu->insertAction(insertPoint, menu->addSeparator()); +} + + +#ifdef HAVE_SPELLCHECKER +void QtTextEdit::setUpSpellChecker() +{ + SpellCheckerFactory* checkerFactory = new SpellCheckerFactory(); + delete checker_; + if (settings_->getSetting(SettingConstants::SPELL_CHECKER)) { + std::string dictPath = settings_->getSetting(SettingConstants::DICT_PATH); + std::string dictFile = settings_->getSetting(SettingConstants::DICT_FILE); + checker_ = checkerFactory->createSpellChecker(dictPath + dictFile); + delete checkerFactory; + } + else { + checker_ = NULL; + } } +#endif +void QtTextEdit::spellCheckerSettingsWindow() { + if (!spellCheckerWindow_) { + spellCheckerWindow_ = new QtSpellCheckerWindow(settings_); + settings_->onSettingChanged.connect(boost::bind(&QtTextEdit::handleSettingChanged, this, _1)); + spellCheckerWindow_->show(); + } + else { + spellCheckerWindow_->show(); + spellCheckerWindow_->raise(); + spellCheckerWindow_->activateWindow(); + } +} +void QtTextEdit::handleSettingChanged(const std::string& settings) { + if (settings == SettingConstants::SPELL_CHECKER.getKey() + || settings == SettingConstants::DICT_PATH.getKey() + || settings == SettingConstants::DICT_FILE.getKey()) { +#ifdef HAVE_SPELLCHECKER + setUpSpellChecker(); + underlineMisspells(); +#endif + } +} +} diff --git a/Swift/QtUI/QtTextEdit.h b/Swift/QtUI/QtTextEdit.h index 075728b..a8df4d3 100644 --- a/Swift/QtUI/QtTextEdit.h +++ b/Swift/QtUI/QtTextEdit.h @@ -5,20 +5,46 @@ */ #pragma once + +#include <SwifTools/SpellParser.h> + +#include <Swift/Controllers/Settings/SettingsProvider.h> +#include <Swift/Controllers/SettingConstants.h> + #include <QTextEdit> +#include <QPointer> namespace Swift { + class SpellChecker; + class QtSpellCheckerWindow; class QtTextEdit : public QTextEdit { Q_OBJECT public: - QtTextEdit(QWidget* parent = 0); + QtTextEdit(SettingsProvider* settings, QWidget* parent = 0); + virtual ~QtTextEdit(); virtual QSize sizeHint() const; signals: + void wordCorrected(QString& word); void returnPressed(); void unhandledKeyPressEvent(QKeyEvent* event); + public slots: + void handleSettingChanged(const std::string& settings); protected: virtual void keyPressEvent(QKeyEvent* event); + virtual void contextMenuEvent(QContextMenuEvent* event); private slots: void handleTextChanged(); + private: + SpellChecker *checker_; + std::vector<QAction*> replaceWordActions_; + PositionPairList misspelledPositions_; + SettingsProvider *settings_; + QPointer<QtSpellCheckerWindow> spellCheckerWindow_; + void addSuggestions(QMenu* menu, QContextMenuEvent* event); + void replaceMisspelledWord(const QString& word, int cursorPosition); + void setUpSpellChecker(); + void underlineMisspells(); + void spellCheckerSettingsWindow(); + PositionPair getWordFromCursor(int cursorPosition); }; } diff --git a/Swift/QtUI/QtTranslator.h b/Swift/QtUI/QtTranslator.h index fdafaf0..a2129f0 100644 --- a/Swift/QtUI/QtTranslator.h +++ b/Swift/QtUI/QtTranslator.h @@ -16,6 +16,10 @@ class QtTranslator : public Swift::Translator { } virtual std::string translate(const std::string& text, const std::string& context) { +#if QT_VERSION >= 0x050000 + return std::string(QCoreApplication::translate(context.c_str(), text.c_str(), 0).toUtf8()); +#else return std::string(QCoreApplication::translate(context.c_str(), text.c_str(), 0, QCoreApplication::UnicodeUTF8).toUtf8()); +#endif } }; diff --git a/Swift/QtUI/QtUIFactory.cpp b/Swift/QtUI/QtUIFactory.cpp index a154fb0..0c7fbc2 100644 --- a/Swift/QtUI/QtUIFactory.cpp +++ b/Swift/QtUI/QtUIFactory.cpp @@ -4,36 +4,39 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "QtUIFactory.h" +#include <Swift/QtUI/QtUIFactory.h> #include <QSplitter> -#include "QtXMLConsoleWidget.h" -#include "QtChatTabs.h" -#include "QtMainWindow.h" -#include "QtLoginWindow.h" -#include "QtSystemTray.h" -#include "QtSettingsProvider.h" -#include "QtMainWindow.h" -#include "QtChatWindow.h" -#include "QtJoinMUCWindow.h" -#include "QtChatWindowFactory.h" -#include "QtSwiftUtil.h" -#include "MUCSearch/QtMUCSearchWindow.h" -#include "UserSearch/QtUserSearchWindow.h" -#include "QtProfileWindow.h" -#include "QtContactEditWindow.h" -#include "QtAdHocCommandWindow.h" -#include "QtFileTransferListWidget.h" -#include "Whiteboard/QtWhiteboardWindow.h" +#include <Swift/QtUI/QtXMLConsoleWidget.h> +#include <Swift/QtUI/QtChatTabs.h> +#include <Swift/QtUI/QtMainWindow.h> +#include <Swift/QtUI/QtLoginWindow.h> +#include <Swift/QtUI/QtSystemTray.h> +#include <Swift/QtUI/QtSettingsProvider.h> +#include <Swift/QtUI/QtMainWindow.h> +#include <Swift/QtUI/QtChatWindow.h> +#include <Swift/QtUI/QtJoinMUCWindow.h> +#include <Swift/QtUI/QtChatWindowFactory.h> +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/MUCSearch/QtMUCSearchWindow.h> +#include <Swift/QtUI/UserSearch/QtUserSearchWindow.h> +#include <Swift/QtUI/QtProfileWindow.h> +#include <Swift/QtUI/QtContactEditWindow.h> +#include <Swift/QtUI/QtAdHocCommandWindow.h> +#include <Swift/QtUI/QtFileTransferListWidget.h> +#include <Swift/QtUI/QtHighlightEditorWidget.h> +#include <Swift/QtUI/Whiteboard/QtWhiteboardWindow.h> #include <Swift/Controllers/Settings/SettingsProviderHierachy.h> #include <Swift/QtUI/QtUISettingConstants.h> -#include <QtHistoryWindow.h> +#include <Swift/QtUI/QtHistoryWindow.h> #include <Swiften/Whiteboard/WhiteboardSession.h> +#include <Swift/QtUI/QtSingleWindow.h> +#include <Swift/QtUI/QtBlockListEditorWindow.h> namespace Swift { -QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) { +QtUIFactory::QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist) : settings(settings), qtOnlySettings(qtOnlySettings), tabs(tabs), netbookSplitter(netbookSplitter), systemTray(systemTray), chatWindowFactory(chatWindowFactory), timerFactory_(timerFactory), lastMainWindow(NULL), loginWindow(NULL), statusCache(statusCache), startMinimized(startMinimized), emoticonsExist_(emoticonsExist) { chatFontSize = settings->getSetting(QtUISettingConstants::CHATWINDOW_FONT_SIZE); historyFontSize_ = settings->getSetting(QtUISettingConstants::HISTORYWINDOW_FONT_SIZE); } @@ -78,14 +81,14 @@ FileTransferListWidget* QtUIFactory::createFileTransferListWidget() { } MainWindow* QtUIFactory::createMainWindow(UIEventStream* eventStream) { - lastMainWindow = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), emoticonsExist_); + lastMainWindow = new QtMainWindow(settings, eventStream, loginWindow->getMenus(), statusCache, emoticonsExist_); return lastMainWindow; } LoginWindow* QtUIFactory::createLoginWindow(UIEventStream* eventStream) { loginWindow = new QtLoginWindow(eventStream, settings, timerFactory_); if (netbookSplitter) { - netbookSplitter->insertWidget(0, loginWindow); + netbookSplitter->insertAtFront(loginWindow); } connect(systemTray, SIGNAL(clicked()), loginWindow, SLOT(toggleBringToFront())); @@ -143,7 +146,7 @@ void QtUIFactory::handleChatWindowFontResized(int size) { UserSearchWindow* QtUIFactory::createUserSearchWindow(UserSearchWindow::Type type, UIEventStream* eventStream, const std::set<std::string>& groups) { return new QtUserSearchWindow(eventStream, type, groups); -}; +} JoinMUCWindow* QtUIFactory::createJoinMUCWindow(UIEventStream* uiEventStream) { return new QtJoinMUCWindow(uiEventStream); @@ -161,6 +164,14 @@ WhiteboardWindow* QtUIFactory::createWhiteboardWindow(boost::shared_ptr<Whiteboa return new QtWhiteboardWindow(whiteboardSession); } +HighlightEditorWidget* QtUIFactory::createHighlightEditorWidget() { + return new QtHighlightEditorWidget(); +} + +BlockListEditorWidget *QtUIFactory::createBlockListEditorWidget() { + return new QtBlockListEditorWindow(); +} + void QtUIFactory::createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command) { new QtAdHocCommandWindow(command); } diff --git a/Swift/QtUI/QtUIFactory.h b/Swift/QtUI/QtUIFactory.h index 30f0101..662c78e 100644 --- a/Swift/QtUI/QtUIFactory.h +++ b/Swift/QtUI/QtUIFactory.h @@ -26,11 +26,13 @@ namespace Swift { class TimerFactory; class historyWindow_; class WhiteboardSession; + class StatusCache; + class QtSingleWindow; class QtUIFactory : public QObject, public UIFactory { Q_OBJECT public: - QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QSplitter* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, bool startMinimized, bool emoticonsExist); + QtUIFactory(SettingsProviderHierachy* settings, QtSettingsProvider* qtOnlySettings, QtChatTabs* tabs, QtSingleWindow* netbookSplitter, QtSystemTray* systemTray, QtChatWindowFactory* chatWindowFactory, TimerFactory* timerFactory, StatusCache* statusCache, bool startMinimized, bool emoticonsExist); virtual XMLConsoleWidget* createXMLConsoleWidget(); virtual HistoryWindow* createHistoryWindow(UIEventStream*); @@ -46,6 +48,8 @@ namespace Swift { virtual ContactEditWindow* createContactEditWindow(); virtual FileTransferListWidget* createFileTransferListWidget(); virtual WhiteboardWindow* createWhiteboardWindow(boost::shared_ptr<WhiteboardSession> whiteboardSession); + virtual HighlightEditorWidget* createHighlightEditorWidget(); + virtual BlockListEditorWidget* createBlockListEditorWidget(); virtual void createAdHocCommandWindow(boost::shared_ptr<OutgoingAdHocCommandSession> command); private slots: @@ -57,12 +61,13 @@ namespace Swift { SettingsProviderHierachy* settings; QtSettingsProvider* qtOnlySettings; QtChatTabs* tabs; - QSplitter* netbookSplitter; + QtSingleWindow* netbookSplitter; QtSystemTray* systemTray; QtChatWindowFactory* chatWindowFactory; TimerFactory* timerFactory_; QtMainWindow* lastMainWindow; QtLoginWindow* loginWindow; + StatusCache* statusCache; std::vector<QPointer<QtChatWindow> > chatWindows; bool startMinimized; int chatFontSize; diff --git a/Swift/QtUI/QtURLValidator.cpp b/Swift/QtUI/QtURLValidator.cpp index 2df59c4..4d56b98 100644 --- a/Swift/QtUI/QtURLValidator.cpp +++ b/Swift/QtUI/QtURLValidator.cpp @@ -10,11 +10,11 @@ #include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { -QtURLValidator::QtURLValidator(QObject* parent) { +QtURLValidator::QtURLValidator(QObject* parent) : QValidator(parent) { } -QValidator::State QtURLValidator::validate(QString& input, int& pos) const { +QValidator::State QtURLValidator::validate(QString& input, int&) const { URL url = URL::fromString(Q2PSTRING(input)); bool valid = !url.isEmpty(); valid &= (url.getScheme() == "http" || url.getScheme() == "https"); diff --git a/Swift/QtUI/QtUtilities.cpp b/Swift/QtUI/QtUtilities.cpp index be9d179..e9aa4a4 100644 --- a/Swift/QtUI/QtUtilities.cpp +++ b/Swift/QtUI/QtUtilities.cpp @@ -1,13 +1,14 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ #include "QtUtilities.h" +#include <QTextDocument> #include <QWidget> -#ifdef Q_WS_X11 +#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) #include <QX11Info> #include <X11/Xlib.h> #include <X11/Xutil.h> @@ -18,7 +19,7 @@ namespace QtUtilities { void setX11Resource(QWidget* widget, const QString& c) { -#ifdef Q_WS_X11 +#if defined (Q_OS_UNIX) && !defined(Q_OS_MAC) char res_class[] = SWIFT_APPLICATION_NAME; XClassHint hint; hint.res_name = (QString(SWIFT_APPLICATION_NAME) + "-" + c).toUtf8().data(); @@ -30,4 +31,12 @@ void setX11Resource(QWidget* widget, const QString& c) { #endif } +QString htmlEscape(const QString& s) { +#if QT_VERSION >= 0x050000 + return s.toHtmlEscaped(); +#else + return Qt::escape(s); +#endif +} + } diff --git a/Swift/QtUI/QtUtilities.h b/Swift/QtUI/QtUtilities.h index 6e64d6e..40c16bc 100644 --- a/Swift/QtUI/QtUtilities.h +++ b/Swift/QtUI/QtUtilities.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -11,4 +11,5 @@ class QString; namespace QtUtilities { void setX11Resource(QWidget* widget, const QString& c); -}; + QString htmlEscape(const QString& s); +} diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp new file mode 100644 index 0000000..a6afe81 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtCloseButton.h" + +#include <QMouseEvent> +#include <QPainter> +#include <QStyle> +#include <QStyleOption> + +namespace Swift { + +QtCloseButton::QtCloseButton(QWidget *parent) : QAbstractButton(parent) { + lightPixmap = QPixmap(12,12); + lightPixmap.fill(QColor(0,0,0,0)); + QStyleOption opt; + opt.init(this); + opt.state = QStyle::State(0); + opt.state |= QStyle::State_MouseOver; + QPainter lightPixmapPainter(&lightPixmap); + style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &lightPixmapPainter); + + darkPixmap = QPixmap(12,12); + darkPixmap.fill(QColor(0,0,0,0)); + opt.init(this); + opt.state = QStyle::State(0); + QPainter darkPixmapPainter(&darkPixmap); + style()->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, &darkPixmapPainter); +} + +QSize QtCloseButton::sizeHint() const { + return QSize(width(), height()); +} + +bool QtCloseButton::event(QEvent *e) { + if (e->type() == QEvent::Enter || e->type() == QEvent::Leave) { + update(); + } + return QAbstractButton::event(e); +} + +void QtCloseButton::paintEvent(QPaintEvent *) { + QPainter painter(this); + painter.setRenderHint(QPainter::HighQualityAntialiasing); + if (underMouse()) { + painter.drawPixmap(0, 0, height(), height(), darkPixmap); + } else { + painter.drawPixmap(0, 0, height(), height(), lightPixmap); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtCloseButton.h b/Swift/QtUI/QtVCardWidget/QtCloseButton.h new file mode 100644 index 0000000..6ce8d30 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtCloseButton.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QAbstractButton> + +namespace Swift { + + class QtCloseButton : public QAbstractButton { + Q_OBJECT + public: + explicit QtCloseButton(QWidget *parent = 0); + virtual QSize sizeHint() const; + + protected: + virtual bool event(QEvent *e); + virtual void paintEvent(QPaintEvent* ); + + private: + QPixmap lightPixmap; + QPixmap darkPixmap; + }; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp new file mode 100644 index 0000000..b586444 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtRemovableItemDelegate.h" + +#include <QEvent> +#include <QPainter> + +namespace Swift { + +QtRemovableItemDelegate::QtRemovableItemDelegate(const QStyle* style) : style(style) { + +} + +void QtRemovableItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const { + QStyleOption opt; + opt.state = QStyle::State(0); + opt.state |= QStyle::State_MouseOver; + painter->save(); + painter->fillRect(option.rect, option.state & QStyle::State_Selected ? option.palette.highlight() : option.palette.base()); + painter->translate(option.rect.x(), option.rect.y()+(option.rect.height() - 12)/2); + style->drawPrimitive(QStyle::PE_IndicatorTabClose, &opt, painter); + painter->restore(); +} + +QWidget* QtRemovableItemDelegate::createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const { + return NULL; +} + +bool QtRemovableItemDelegate::editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index) { + if (event->type() == QEvent::MouseButtonRelease) { + model->removeRow(index.row()); + return true; + } else { + return QItemDelegate::editorEvent(event, model, option, index); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h new file mode 100644 index 0000000..3d99ad8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtRemovableItemDelegate.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QItemDelegate> + +namespace Swift { + +class QtRemovableItemDelegate : public QItemDelegate { + public: + QtRemovableItemDelegate(const QStyle* style); + + virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex&) const; + virtual QWidget* createEditor(QWidget*, const QStyleOptionViewItem&, const QModelIndex&) const; + + protected: + virtual bool editorEvent(QEvent* event, QAbstractItemModel* model, const QStyleOptionViewItem& option, const QModelIndex& index); + + private: + const QStyle* style; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp new file mode 100644 index 0000000..efe04dc --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtResizableLineEdit.h" + +namespace Swift { + +QtResizableLineEdit::QtResizableLineEdit(QWidget* parent) : + QLineEdit(parent) { + connect(this, SIGNAL(textChanged(QString)), SLOT(textChanged(QString))); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + int marginHeight = 6; + setMaximumHeight(fontMetrics().height() + marginHeight); +} + +QtResizableLineEdit::~QtResizableLineEdit() { +} + +bool QtResizableLineEdit::isEditable() const { + return editable; +} +void QtResizableLineEdit::setEditable(const bool editable) { + this->editable = editable; + if (editable) { + setReadOnly(false); + } else { + setReadOnly(true); + } +} + + +QSize QtResizableLineEdit::sizeHint() const { + int horizontalMargin = 10; +#if QT_VERSION >= 0x040700 + int w = fontMetrics().boundingRect(text().isEmpty() ? placeholderText() : text()).width() + horizontalMargin; +#else + int w = fontMetrics().boundingRect(text().isEmpty() ? QString(" ") : text()).width() + horizontalMargin; +#endif + return QSize(w, height()); +} + +void QtResizableLineEdit::textChanged(QString) { + updateGeometry(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h new file mode 100644 index 0000000..9022d38 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtResizableLineEdit.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QLineEdit> + +namespace Swift { + + class QtResizableLineEdit : public QLineEdit { + Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + + public: + explicit QtResizableLineEdit(QWidget* parent = 0); + ~QtResizableLineEdit(); + + bool isEditable() const; + void setEditable(const bool); + + virtual QSize sizeHint() const; + + private slots: + void textChanged(QString); + + private: + bool editable; + }; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp new file mode 100644 index 0000000..bade009 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtTagComboBox.h" + +#include <QAbstractItemView> +#include <QtGui> + +namespace Swift { + +QtTagComboBox::QtTagComboBox(QWidget* parent) : QComboBox(parent) { + setEditable(false); + displayModel = new QStandardItemModel(); + displayItem = new QStandardItem(); + displayItem->setText(""); + displayModel->insertRow(0, displayItem); + editMenu = new QMenu(); + this->setModel(displayModel); + editable = true; +} + +QtTagComboBox::~QtTagComboBox() { + +} + +bool QtTagComboBox::isEditable() const { + return editable; +} + +void QtTagComboBox::setEditable(const bool editable) { + this->editable = editable; +} + +void QtTagComboBox::addTag(const QString &id, const QString &label) { + QAction* tagAction = new QAction(editMenu); + tagAction->setText(label); + tagAction->setCheckable(true); + tagAction->setData(QString(id)); + editMenu->addAction(tagAction); +} + +void QtTagComboBox::setTag(const QString &id, bool value) { + QList<QAction*> tagActions = editMenu->actions(); + foreach(QAction* action, tagActions) { + if (action->data() == id) { + action->setChecked(value); + updateDisplayItem(); + return; + } + } +} + +bool QtTagComboBox::isTagSet(const QString &id) const { + QList<QAction*> tagActions = editMenu->actions(); + foreach(QAction* action, tagActions) { + if (action->data() == id) { + return action->isChecked(); + } + } + return false; +} + +void QtTagComboBox::showPopup() { + +} + +void QtTagComboBox::hidePopup() { + +} + +bool QtTagComboBox::event(QEvent* event) { + if (event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::KeyRelease) { + if (!editable) return true; + + QPoint p = mapToGlobal(QPoint(0,0)); + p += QPoint(0, height()); + editMenu->exec(p); + updateDisplayItem(); + return true; + } + return QComboBox::event(event); +} + +void QtTagComboBox::updateDisplayItem() { + QList<QAction*> tagActions = editMenu->actions(); + QString text = ""; + foreach(QAction* action, tagActions) { + if (action->isChecked()) { + if (text != "") { + text += ", "; + } + text += action->text(); + } + } + setItemText(0, text); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtTagComboBox.h b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h new file mode 100644 index 0000000..37a60af --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtTagComboBox.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QComboBox> +#include <QMenu> +#include <QStandardItem> +#include <QStandardItemModel> + +namespace Swift { + +class QtTagComboBox : public QComboBox { + Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + + public: + explicit QtTagComboBox(QWidget* parent = 0); + ~QtTagComboBox(); + + bool isEditable() const; + void setEditable(const bool); + + void addTag(const QString& id, const QString& label); + void setTag(const QString& id, bool value); + bool isTagSet(const QString& id) const; + + virtual void showPopup(); + virtual void hidePopup(); + + virtual bool event(QEvent* event); + + private: + void updateDisplayItem(); + + private: + bool editable; + QStandardItemModel* displayModel; + QStandardItem* displayItem; + QMenu* editMenu; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp new file mode 100644 index 0000000..d9bb4fe --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardAddressField.h" + +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardAddressField::QtVCardAddressField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address")) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardAddressField::~QtVCardAddressField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardAddressField::setupContentWidgets() { + textFieldGridLayout = new QGridLayout(); + + streetLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(streetLineEdit, 0, 0, Qt::AlignVCenter); + + poboxLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(poboxLineEdit, 0, 1, Qt::AlignVCenter); + + addressextLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(addressextLineEdit, 1, 0, Qt::AlignVCenter); + + cityLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(cityLineEdit, 2, 0, Qt::AlignVCenter); + + pocodeLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(pocodeLineEdit, 2, 1, Qt::AlignVCenter); + + regionLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(regionLineEdit, 3, 0, Qt::AlignVCenter); + + countryLineEdit = new QtResizableLineEdit(this); + textFieldGridLayout->addWidget(countryLineEdit, 4, 0, Qt::AlignVCenter); + textFieldGridLayout->setVerticalSpacing(2); + getGridLayout()->addLayout(textFieldGridLayout, getGridLayout()->rowCount()-1, 2, 5, 2, Qt::AlignVCenter); + textFieldGridLayoutItem = getGridLayout()->itemAtPosition(getGridLayout()->rowCount()-1, 2); + +#if QT_VERSION >= 0x040700 + streetLineEdit->setPlaceholderText(tr("Street")); + poboxLineEdit->setPlaceholderText(tr("PO Box")); + addressextLineEdit->setPlaceholderText(tr("Address Extension")); + cityLineEdit->setPlaceholderText(tr("City")); + pocodeLineEdit->setPlaceholderText(tr("Postal Code")); + regionLineEdit->setPlaceholderText(tr("Region")); + countryLineEdit->setPlaceholderText(tr("Country")); +#endif + + deliveryTypeLabel = new QLabel(this); + deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-3, 4, Qt::AlignVCenter); + + domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this); + getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + + internationalRadioButton = new QRadioButton(tr("International Delivery"), this); + getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter); + + buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(domesticRadioButton); + buttonGroup->addButton(internationalRadioButton); + + setTabOrder(internationalRadioButton, getTagComboBox()); + getTagComboBox()->addTag("postal", tr("Postal")); + getTagComboBox()->addTag("parcel", tr("Parcel")); + + QtVCardHomeWork::setTagComboBox(getTagComboBox()); + + textFields << streetLineEdit << poboxLineEdit << addressextLineEdit << cityLineEdit << pocodeLineEdit << regionLineEdit << countryLineEdit; + childWidgets << deliveryTypeLabel << domesticRadioButton << internationalRadioButton; +} + +void QtVCardAddressField::customCleanup() { + foreach(QWidget* widget, textFields) { + widget->hide(); + textFieldGridLayout->removeWidget(widget); + } + getGridLayout()->removeItem(textFieldGridLayoutItem); +} + + + +bool QtVCardAddressField::isEmpty() const { + return streetLineEdit->text().isEmpty() && + poboxLineEdit->text().isEmpty() && + addressextLineEdit->text().isEmpty() && + cityLineEdit->text().isEmpty() && + pocodeLineEdit->text().isEmpty() && + regionLineEdit->text().isEmpty() && + countryLineEdit->text().isEmpty(); +} + +void QtVCardAddressField::setAddress(const VCard::Address& address) { + setPreferred(address.isPreferred); + setHome(address.isHome); + setWork(address.isWork); + getTagComboBox()->setTag("postal", address.isPostal); + getTagComboBox()->setTag("parcel", address.isParcel); + domesticRadioButton->setChecked(address.deliveryType == VCard::DomesticDelivery); + internationalRadioButton->setChecked(address.deliveryType == VCard::InternationalDelivery); + streetLineEdit->setText(P2QSTRING(address.street)); + poboxLineEdit->setText(P2QSTRING(address.poBox)); + addressextLineEdit->setText(P2QSTRING(address.addressExtension)); + cityLineEdit->setText(P2QSTRING(address.locality)); + pocodeLineEdit->setText(P2QSTRING(address.postalCode)); + regionLineEdit->setText(P2QSTRING(address.region)); + countryLineEdit->setText(P2QSTRING(address.country)); +} + +VCard::Address QtVCardAddressField::getAddress() const { + VCard::Address address; + address.isPreferred = getPreferred(); + address.isHome = getHome(); + address.isWork = getWork(); + address.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None); + address.isPostal = getTagComboBox()->isTagSet("postal"); + address.isParcel = getTagComboBox()->isTagSet("parcel"); + address.street = Q2PSTRING(streetLineEdit->text()); + address.poBox = Q2PSTRING(poboxLineEdit->text()); + address.addressExtension = Q2PSTRING(addressextLineEdit->text()); + address.locality = Q2PSTRING(cityLineEdit->text()); + address.postalCode = Q2PSTRING(pocodeLineEdit->text()); + address.region = Q2PSTRING(regionLineEdit->text()); + address.country = Q2PSTRING(countryLineEdit->text()); + return address; +} + +void QtVCardAddressField::handleEditibleChanged(bool isEditable) { + if (streetLineEdit) streetLineEdit->setEditable(isEditable); + if (poboxLineEdit) poboxLineEdit->setEditable(isEditable); + if (addressextLineEdit) addressextLineEdit->setEditable(isEditable); + if (cityLineEdit) cityLineEdit->setEditable(isEditable); + if (pocodeLineEdit) pocodeLineEdit->setEditable(isEditable); + if (regionLineEdit) regionLineEdit->setEditable(isEditable); + if (countryLineEdit) countryLineEdit->setEditable(isEditable); + + if (deliveryTypeLabel) { + deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text()); + deliveryTypeLabel->setVisible(!isEditable); + } + if (domesticRadioButton) domesticRadioButton->setVisible(isEditable); + if (internationalRadioButton) internationalRadioButton->setVisible(isEditable); + + foreach (QWidget* widget, textFields) { + QtResizableLineEdit* lineEdit; + if ((lineEdit = dynamic_cast<QtResizableLineEdit*>(widget))) { + lineEdit->setVisible(isEditable ? true : !lineEdit->text().isEmpty()); + lineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); + } + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h new file mode 100644 index 0000000..5a1256a --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressField.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QButtonGroup> +#include <QRadioButton> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardAddressField : public QtVCardGeneralField, public QtVCardHomeWork { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Address", UNLIMITED_INSTANCES, QtVCardAddressField) + + QtVCardAddressField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardAddressField(); + + virtual bool isEmpty() const; + + void setAddress(const VCard::Address& address); + VCard::Address getAddress() const; + + protected: + virtual void setupContentWidgets(); + virtual void customCleanup(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QList<QWidget*> textFields; + QtResizableLineEdit* streetLineEdit; + QtResizableLineEdit* poboxLineEdit; + QtResizableLineEdit* addressextLineEdit; + QtResizableLineEdit* cityLineEdit; + QtResizableLineEdit* pocodeLineEdit; + QtResizableLineEdit* regionLineEdit; + QtResizableLineEdit* countryLineEdit; + QGridLayout* textFieldGridLayout; + QLayoutItem* textFieldGridLayoutItem; + + QLabel* deliveryTypeLabel; + QRadioButton* domesticRadioButton; + QRadioButton* internationalRadioButton; + QButtonGroup* buttonGroup; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp new file mode 100644 index 0000000..20f48b9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardAddressLabelField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardAddressLabelField::QtVCardAddressLabelField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Address Label")) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardAddressLabelField::~QtVCardAddressLabelField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardAddressLabelField::setupContentWidgets() { + addressLabelPlainTextEdit = new QPlainTextEdit(this); + addressLabelPlainTextEdit->setTabChangesFocus(true); + getGridLayout()->addWidget(addressLabelPlainTextEdit, getGridLayout()->rowCount()-1, 2, 3, 2, Qt::AlignVCenter); + + deliveryTypeLabel = new QLabel(this); + deliveryTypeLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + getGridLayout()->addWidget(deliveryTypeLabel, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + + domesticRadioButton = new QRadioButton(tr("Domestic Delivery"), this); + getGridLayout()->addWidget(domesticRadioButton, getGridLayout()->rowCount()-2, 4, Qt::AlignVCenter); + + internationalRadioButton = new QRadioButton(tr("International Delivery"), this); + getGridLayout()->addWidget(internationalRadioButton, getGridLayout()->rowCount()-1, 4, Qt::AlignVCenter); + + buttonGroup = new QButtonGroup(this); + buttonGroup->addButton(domesticRadioButton); + buttonGroup->addButton(internationalRadioButton); + + setTabOrder(internationalRadioButton, getTagComboBox()); + getTagComboBox()->addTag("postal", tr("Postal")); + getTagComboBox()->addTag("parcel", tr("Parcel")); + + QtVCardHomeWork::setTagComboBox(getTagComboBox()); + deliveryTypeLabel->hide(); + childWidgets << addressLabelPlainTextEdit << deliveryTypeLabel << domesticRadioButton << internationalRadioButton; +} + +bool QtVCardAddressLabelField::isEmpty() const { + return addressLabelPlainTextEdit->toPlainText().isEmpty(); +} + +void QtVCardAddressLabelField::setAddressLabel(const VCard::AddressLabel& addressLabel) { + setPreferred(addressLabel.isPreferred); + setHome(addressLabel.isHome); + setWork(addressLabel.isWork); + getTagComboBox()->setTag("postal", addressLabel.isPostal); + getTagComboBox()->setTag("parcel", addressLabel.isParcel); + domesticRadioButton->setChecked(addressLabel.deliveryType == VCard::DomesticDelivery); + internationalRadioButton->setChecked(addressLabel.deliveryType == VCard::InternationalDelivery); + std::string joinedLines = boost::algorithm::join(addressLabel.lines, "\n"); + addressLabelPlainTextEdit->setPlainText(P2QSTRING(joinedLines)); +} + +VCard::AddressLabel QtVCardAddressLabelField::getAddressLabel() const { + VCard::AddressLabel addressLabel; + addressLabel.isPreferred = getPreferred(); + addressLabel.isHome = getHome(); + addressLabel.isWork = getWork(); + addressLabel.deliveryType = domesticRadioButton->isChecked() ? VCard::DomesticDelivery : (internationalRadioButton->isChecked() ? VCard::InternationalDelivery : VCard::None); + addressLabel.isPostal = getTagComboBox()->isTagSet("postal"); + addressLabel.isParcel = getTagComboBox()->isTagSet("parcel"); + + std::string lines = Q2PSTRING(addressLabelPlainTextEdit->toPlainText()); + boost::split(addressLabel.lines, lines, boost::is_any_of("\n")); + return addressLabel; +} + +void QtVCardAddressLabelField::handleEditibleChanged(bool isEditable) { + if (addressLabelPlainTextEdit) { + addressLabelPlainTextEdit->setReadOnly(!isEditable); + addressLabelPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }"); + } + + if (deliveryTypeLabel) { + deliveryTypeLabel->setText(buttonGroup->checkedButton() == 0 ? "" : buttonGroup->checkedButton()->text()); + deliveryTypeLabel->setVisible(!isEditable); + } + if (domesticRadioButton) domesticRadioButton->setVisible(isEditable); + if (internationalRadioButton) internationalRadioButton->setVisible(isEditable); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h new file mode 100644 index 0000000..0e097d9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardAddressLabelField.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QButtonGroup> +#include <QPlainTextEdit> +#include <QRadioButton> +#include <Swiften/Elements/VCard.h> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardAddressLabelField : public QtVCardGeneralField, public QtVCardHomeWork { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("AddressLabel", UNLIMITED_INSTANCES, QtVCardAddressLabelField) + + QtVCardAddressLabelField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardAddressLabelField(); + + virtual bool isEmpty() const; + + void setAddressLabel(const VCard::AddressLabel& addressLabel); + VCard::AddressLabel getAddressLabel() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QPlainTextEdit* addressLabelPlainTextEdit; + + QLabel* deliveryTypeLabel; + QRadioButton* domesticRadioButton; + QRadioButton* internationalRadioButton; + QButtonGroup* buttonGroup; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp new file mode 100644 index 0000000..2afc2f6 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardBirthdayField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardBirthdayField::QtVCardBirthdayField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Birthday"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardBirthdayField::~QtVCardBirthdayField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardBirthdayField::setupContentWidgets() { + birthdayLabel = new QLabel(this); + birthdayLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + birthdayDateEdit = new QDateEdit(this); + birthdayDateEdit->setCalendarPopup(true); + + QHBoxLayout* birthdayLayout = new QHBoxLayout(); + birthdayLayout->addWidget(birthdayLabel); + birthdayLayout->addWidget(birthdayDateEdit); + + getGridLayout()->addLayout(birthdayLayout, getGridLayout()->rowCount()-1, 2, Qt::AlignVCenter); + + getTagComboBox()->hide(); + birthdayLabel->hide(); + childWidgets << birthdayLabel << birthdayDateEdit; +} + +bool QtVCardBirthdayField::isEmpty() const { + return false; +} + +void QtVCardBirthdayField::setBirthday(const boost::posix_time::ptime& birthday) { + birthdayDateEdit->setDate(B2QDATE(birthday).date()); +} + +boost::posix_time::ptime QtVCardBirthdayField::getBirthday() const { + return boost::posix_time::from_time_t(QDateTime(birthdayDateEdit->date()).toTime_t()); +} + +void QtVCardBirthdayField::handleEditibleChanged(bool isEditable) { + birthdayLabel->setText(birthdayDateEdit->date().toString(Qt::DefaultLocaleLongDate)); + birthdayDateEdit->setVisible(isEditable); + birthdayLabel->setVisible(!isEditable); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h new file mode 100644 index 0000000..4be6e27 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardBirthdayField.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QDateEdit> +#include <Swiften/Elements/VCard.h> + +#include "QtCloseButton.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardBirthdayField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Birthday", 1, QtVCardBirthdayField) + + QtVCardBirthdayField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardBirthdayField(); + + virtual bool isEmpty() const; + + void setBirthday(const boost::posix_time::ptime& addressLabel); + boost::posix_time::ptime getBirthday() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QLabel* birthdayLabel; + QDateEdit* birthdayDateEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp new file mode 100644 index 0000000..f907d78 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardDescriptionField.h" + +#include <boost/algorithm/string.hpp> +#include <QFontMetrics> +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardDescriptionField::QtVCardDescriptionField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Description"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardDescriptionField::~QtVCardDescriptionField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardDescriptionField::setupContentWidgets() { + descriptionPlainTextEdit = new QPlainTextEdit(this); + descriptionPlainTextEdit->setMinimumHeight(70); + getGridLayout()->addWidget(descriptionPlainTextEdit, getGridLayout()->rowCount()-1, 2, 2, 2, Qt::AlignVCenter); + getTagComboBox()->hide(); + childWidgets << descriptionPlainTextEdit; +} + +bool QtVCardDescriptionField::isEmpty() const { + return descriptionPlainTextEdit->toPlainText().isEmpty(); +} + +void QtVCardDescriptionField::setDescription(const std::string& description) { + descriptionPlainTextEdit->setPlainText(P2QSTRING(description)); +} + +std::string QtVCardDescriptionField::getDescription() const { + return Q2PSTRING(descriptionPlainTextEdit->toPlainText()); +} + +void QtVCardDescriptionField::handleEditibleChanged(bool isEditable) { + if (descriptionPlainTextEdit) { + if (isEditable) { + descriptionPlainTextEdit->setMinimumHeight(70); + } else { + QFontMetrics inputMetrics(descriptionPlainTextEdit->document()->defaultFont()); + QRect horizontalBounds = contentsRect().adjusted(0,0,0,9999); + QRect boundingRect = inputMetrics.boundingRect(horizontalBounds, Qt::TextWordWrap, descriptionPlainTextEdit->toPlainText() + "A"); + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + int height = boundingRect.height() + top + bottom + inputMetrics.height(); + descriptionPlainTextEdit->setMinimumHeight(height > 70 ? 70 : height); + } + descriptionPlainTextEdit->setReadOnly(!isEditable); + descriptionPlainTextEdit->setStyleSheet(isEditable ? "" : "QPlainTextEdit { background: transparent; }"); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h new file mode 100644 index 0000000..3b1b3d9 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardDescriptionField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QPlainTextEdit> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardDescriptionField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Description", 1, QtVCardDescriptionField) + + QtVCardDescriptionField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardDescriptionField(); + + virtual bool isEmpty() const; + + void setDescription(const std::string& description); + std::string getDescription() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QPlainTextEdit* descriptionPlainTextEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h new file mode 100644 index 0000000..168c01b --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardFieldInfo.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QGridLayout> +#include <QObject> +#include <QString> +#include <typeinfo> + +#define GENERIC_QT_VCARD_FIELD_INFO(MENU_NAME, ALLOWED_INSTANCES, FIELD_CLASS) \ + class FieldInfo : public QtVCardFieldInfo { \ + public: \ + virtual ~FieldInfo() { \ + } \ + \ + virtual QString getMenuName() const { \ + return QObject::tr(MENU_NAME); \ + } \ + \ + virtual int getAllowedInstances() const { \ + return ALLOWED_INSTANCES; \ + } \ + \ + virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const { \ + return new FIELD_CLASS(parent, layout, editable); \ + } \ + \ + virtual bool testInstance(QWidget* widget) const { \ + return dynamic_cast<FIELD_CLASS*>(widget) != 0; \ + } \ + }; + +class QWidget; + +namespace Swift { + + class QtVCardFieldInfo { + public: + static const int UNLIMITED_INSTANCES = -1; + + virtual ~QtVCardFieldInfo() { + } + virtual QString getMenuName() const = 0; + virtual int getAllowedInstances() const = 0; + virtual QWidget* createFieldInstance(QWidget* parent, QGridLayout* layout, bool editable) const = 0; + virtual bool testInstance(QWidget*) const = 0; + }; +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp new file mode 100644 index 0000000..5b3ef87 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardGeneralField.h" + +#include <QHBoxLayout> +#include <Swiften/Base/Log.h> + +namespace Swift { + +QtVCardGeneralField::QtVCardGeneralField(QWidget* parent, QGridLayout* layout, bool editable, int row, QString label, bool preferrable, bool taggable) : + QWidget(parent), preferrable(preferrable), taggable(taggable), layout(layout), row(row), preferredCheckBox(0), label(0), labelText(label), + tagComboBox(0), closeButton(0) { + setEditable(editable); +} + +QtVCardGeneralField::~QtVCardGeneralField() { + +} + +void QtVCardGeneralField::initialize() { + if (preferrable) { + preferredCheckBox = new QCheckBox(this); + preferredCheckBox->setStyleSheet( + "QCheckBox::indicator { width: 18px; height: 18px; }" + "QCheckBox::indicator:checked { image: url(:/icons/star-checked.png); }" + "QCheckBox::indicator:unchecked { image: url(:/icons/star-unchecked); }" + ); + layout->addWidget(preferredCheckBox, row, 0, Qt::AlignVCenter); + childWidgets << preferredCheckBox; + } + label = new QLabel(this); + label->setText(labelText); + layout->addWidget(label, row, 1, Qt::AlignVCenter | Qt::AlignRight); + + tagLabel = new QLabel(this); + tagLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + + tagComboBox = new QtTagComboBox(this); + closeButton = new QtCloseButton(this); + connect(closeButton, SIGNAL(clicked()), SLOT(handleCloseButtonClicked())); + + QHBoxLayout* tagLayout = new QHBoxLayout(); + tagLayout->addWidget(tagLabel); + tagLayout->addWidget(tagComboBox); + + setupContentWidgets(); + layout->addLayout(tagLayout, row, 4, Qt::AlignTop); + layout->addWidget(closeButton, row, 5, Qt::AlignCenter); + closeButton->resize(12, 12); + tagLabel->hide(); + + childWidgets << label << tagComboBox << tagLabel << closeButton; +} + +bool QtVCardGeneralField::isEditable() const { + return editable; +} + +void QtVCardGeneralField::setEditable(bool editable) { + this->editable = editable; + if (tagComboBox) { + if (taggable) { + tagLabel->setText(tagComboBox->itemText(0)); + tagComboBox->setVisible(editable); + tagLabel->setVisible(!editable); + } else { + tagLabel->hide(); + tagComboBox->hide(); + } + } + if (closeButton) closeButton->setVisible(editable); + if (preferredCheckBox) { + if (editable) { + preferredCheckBox->show(); + } else if (!preferredCheckBox->isChecked()) { + preferredCheckBox->hide(); + } + preferredCheckBox->setEnabled(editable); + } + editableChanged(this->editable); +} + +void QtVCardGeneralField::setPreferred(const bool preferred) { + if (preferredCheckBox) preferredCheckBox->setChecked(preferred); +} + +bool QtVCardGeneralField::getPreferred() const { + return preferredCheckBox ? preferredCheckBox->isChecked() : false; +} + +void QtVCardGeneralField::customCleanup() { +} + +QtTagComboBox* QtVCardGeneralField::getTagComboBox() const { + return tagComboBox; +} + +QGridLayout* QtVCardGeneralField::getGridLayout() const { + return layout; +} + +void QtVCardGeneralField::handleCloseButtonClicked() { + customCleanup(); + foreach(QWidget* widget, childWidgets) { + widget->hide(); + layout->removeWidget(widget); + } + deleteField(this); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h new file mode 100644 index 0000000..4afe692 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardGeneralField.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QCheckBox> +#include <QGridLayout> +#include <QLabel> +#include <QWidget> + +#include "QtCloseButton.h" +#include "QtTagComboBox.h" + +namespace Swift { + +/* + * covers features like: + * - preffered (star ceckbox) + * - combo check boxh + * - label + * - remove button + */ +class QtVCardGeneralField : public QWidget { + Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable NOTIFY editableChanged) + Q_PROPERTY(bool empty READ isEmpty) + + public: + explicit QtVCardGeneralField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false, int row = 0, QString label = QString(), + bool preferrable = true, bool taggable = true); + virtual ~QtVCardGeneralField(); + + void initialize(); + + virtual bool isEditable() const; + virtual void setEditable(bool); + + virtual bool isEmpty() const = 0; + + void setPreferred(const bool preferred); + bool getPreferred() const; + + protected: + virtual void setupContentWidgets() = 0; + virtual void customCleanup(); + + QtTagComboBox* getTagComboBox() const; + QGridLayout* getGridLayout() const; + + signals: + void editableChanged(bool); + void deleteField(QtVCardGeneralField*); + + public slots: + void handleCloseButtonClicked(); + + protected: + QList<QWidget*> childWidgets; + + private: + bool editable; + bool preferrable; + bool taggable; + QGridLayout* layout; + int row; + QCheckBox* preferredCheckBox; + QLabel* label; + QString labelText; + QtTagComboBox* tagComboBox; + QLabel* tagLabel; + QtCloseButton* closeButton; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp new file mode 100644 index 0000000..3119a80 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardHomeWork.h" + +namespace Swift { + +QtVCardHomeWork::QtVCardHomeWork() : tagComboBox(0) { +} + +QtVCardHomeWork::~QtVCardHomeWork() { +} + +void QtVCardHomeWork::setTagComboBox(QtTagComboBox* tagBox) { + tagComboBox = tagBox; + tagComboBox->addTag("home", QObject::tr("Home")); + tagComboBox->addTag("work", QObject::tr("Work")); +} + +void QtVCardHomeWork::setHome(const bool home) { + tagComboBox->setTag("home", home); +} + +bool QtVCardHomeWork::getHome() const { + return tagComboBox->isTagSet("home"); +} + +void QtVCardHomeWork::setWork(const bool work) { + tagComboBox->setTag("work", work); +} + +bool QtVCardHomeWork::getWork() const { + return tagComboBox->isTagSet("work"); +} + +} + diff --git a/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h new file mode 100644 index 0000000..768d984 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardHomeWork.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QObject> + +#include "QtTagComboBox.h" + +namespace Swift { + +class QtVCardHomeWork { + public: + QtVCardHomeWork(); + virtual ~QtVCardHomeWork(); + + void setTagComboBox(QtTagComboBox* tagBox); + + void setHome(const bool home); + bool getHome() const; + void setWork(const bool work); + bool getWork() const; + + private: + QtTagComboBox* tagComboBox; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp new file mode 100644 index 0000000..b946fc4 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardInternetEMailField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QTextDocument> +#include <Swiften/Base/Log.h> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtUtilities.h> + +namespace Swift { + +QtVCardInternetEMailField::QtVCardInternetEMailField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("E-Mail")) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardInternetEMailField::~QtVCardInternetEMailField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardInternetEMailField::setupContentWidgets() { + emailLabel = new QLabel(this); + emailLabel->setOpenExternalLinks(true); + emailLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + emailLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 + emailLineEdit->setPlaceholderText(tr("alice@wonderland.lit")); +#endif + QHBoxLayout* emailLayout = new QHBoxLayout(); + emailLayout->addWidget(emailLabel); + emailLayout->addWidget(emailLineEdit); + getGridLayout()->addLayout(emailLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + setTabOrder(emailLineEdit, getTagComboBox()); + QtVCardHomeWork::setTagComboBox(getTagComboBox()); + emailLabel->hide(); + childWidgets << emailLabel << emailLineEdit; +} + +bool QtVCardInternetEMailField::isEmpty() const { + return emailLineEdit->text().isEmpty(); +} + +void QtVCardInternetEMailField::setInternetEMailAddress(const VCard::EMailAddress& address) { + assert(address.isInternet); + setPreferred(address.isPreferred); + setHome(address.isHome); + setWork(address.isWork); + emailLineEdit->setText(P2QSTRING(address.address)); +} + +VCard::EMailAddress QtVCardInternetEMailField::getInternetEMailAddress() const { + VCard::EMailAddress address; + address.isInternet = true; + address.isPreferred = getPreferred(); + address.isHome = getHome(); + address.isWork = getWork(); + address.address = Q2PSTRING(emailLineEdit->text()); + return address; +} + +void QtVCardInternetEMailField::handleEditibleChanged(bool isEditable) { + if (isEditable) { + if (emailLineEdit) emailLineEdit->show(); + if (emailLabel) emailLabel->hide(); + } else { + if (emailLineEdit) emailLineEdit->hide(); + if (emailLabel) { + emailLabel->setText(QString("<a href=\"mailto:%1\">%1</a>").arg(QtUtilities::htmlEscape(emailLineEdit->text()))); + emailLabel->show(); + } + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h new file mode 100644 index 0000000..3f8a27f --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardInternetEMailField.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardInternetEMailField : public QtVCardGeneralField, public QtVCardHomeWork { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("E-Mail", UNLIMITED_INSTANCES, QtVCardInternetEMailField) + + QtVCardInternetEMailField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardInternetEMailField(); + + virtual bool isEmpty() const; + + void setInternetEMailAddress(const VCard::EMailAddress& address); + VCard::EMailAddress getInternetEMailAddress() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QtResizableLineEdit* emailLineEdit; + QLabel* emailLabel; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp new file mode 100644 index 0000000..ecb5533 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardJIDField.h" + +#include <QGridLayout> +#include <QTextDocument> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtUtilities.h> + +namespace Swift { + +QtVCardJIDField::QtVCardJIDField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("JID"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardJIDField::~QtVCardJIDField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardJIDField::setupContentWidgets() { + jidLabel = new QLabel(this); + jidLabel->setOpenExternalLinks(true); + jidLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + jidLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 + jidLineEdit->setPlaceholderText(tr("alice@wonderland.lit")); +#endif + QHBoxLayout* jidLayout = new QHBoxLayout(); + jidLayout->addWidget(jidLabel); + jidLayout->addWidget(jidLineEdit); + getGridLayout()->addLayout(jidLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + + jidLabel->hide(); + getTagComboBox()->hide(); + + childWidgets << jidLabel << jidLineEdit; +} + +bool QtVCardJIDField::isEmpty() const { + return jidLineEdit->text().isEmpty(); +} + +void QtVCardJIDField::setJID(const JID& jid) { + std::string jidStr = jid.toBare().toString(); + jidLineEdit->setText(P2QSTRING(jidStr)); +} + +JID QtVCardJIDField::getJID() const { + return JID(Q2PSTRING(jidLineEdit->text())); +} + +void QtVCardJIDField::handleEditibleChanged(bool isEditable) { + if (isEditable) { + if (jidLineEdit) jidLineEdit->show(); + if (jidLabel) jidLabel->hide(); + } else { + if (jidLineEdit) jidLineEdit->hide(); + if (jidLabel) { + jidLabel->setText(QString("<a href=\"xmpp:%1\">%1</a>").arg(QtUtilities::htmlEscape(jidLineEdit->text()))); + jidLabel->show(); + } + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h new file mode 100644 index 0000000..016bcf8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardJIDField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardJIDField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("JID", UNLIMITED_INSTANCES, QtVCardJIDField) + + QtVCardJIDField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardJIDField(); + + virtual bool isEmpty() const; + + void setJID(const JID& jid); + JID getJID() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QLabel* jidLabel; + QtResizableLineEdit* jidLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp new file mode 100644 index 0000000..e399885 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardOrganizationField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QHeaderView> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardOrganizationField::QtVCardOrganizationField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Organisation"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardOrganizationField::~QtVCardOrganizationField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardOrganizationField::setupContentWidgets() { + organizationLabel = new QLabel(this); + organizationLabel->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard); + organizationLineEdit = new QtResizableLineEdit(this); + QHBoxLayout* organizationLayout = new QHBoxLayout(); + organizationLayout->addWidget(organizationLabel); + organizationLayout->addWidget(organizationLineEdit); + + getGridLayout()->addLayout(organizationLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + + itemDelegate = new QtRemovableItemDelegate(style()); + + unitsTreeWidget = new QTreeWidget(this); + unitsTreeWidget->setColumnCount(2); + unitsTreeWidget->header()->setStretchLastSection(false); + int closeIconWidth = unitsTreeWidget->fontMetrics().height(); + unitsTreeWidget->header()->resizeSection(1, closeIconWidth); + +#if QT_VERSION >= 0x050000 + unitsTreeWidget->header()->setSectionResizeMode(0, QHeaderView::Stretch); +#else + unitsTreeWidget->header()->setResizeMode(0, QHeaderView::Stretch); +#endif + + unitsTreeWidget->setHeaderHidden(true); + unitsTreeWidget->setRootIsDecorated(false); + unitsTreeWidget->setEditTriggers(QAbstractItemView::DoubleClicked); + unitsTreeWidget->setItemDelegateForColumn(1, itemDelegate); + connect(unitsTreeWidget, SIGNAL(itemChanged(QTreeWidgetItem*,int)), SLOT(handleItemChanged(QTreeWidgetItem*,int))); + getGridLayout()->addWidget(unitsTreeWidget, getGridLayout()->rowCount()-1, 4, 2, 1); + + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + unitsTreeWidget->addTopLevelItem(item); + + getTagComboBox()->hide(); + organizationLabel->hide(); + childWidgets << organizationLabel << organizationLineEdit << unitsTreeWidget; +} + +bool QtVCardOrganizationField::isEmpty() const { + return organizationLineEdit->text().isEmpty() && unitsTreeWidget->model()->rowCount() != 0; +} + +void QtVCardOrganizationField::setOrganization(const VCard::Organization& organization) { + organizationLineEdit->setText(P2QSTRING(organization.name)); + unitsTreeWidget->clear(); + foreach(std::string unit, organization.units) { + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList(P2QSTRING(unit)) << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + unitsTreeWidget->addTopLevelItem(item); + } +} + +VCard::Organization QtVCardOrganizationField::getOrganization() const { + VCard::Organization organization; + organization.name = Q2PSTRING(organizationLineEdit->text()); + for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) { + QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i); + if (!row->text(0).isEmpty()) { + organization.units.push_back(Q2PSTRING(row->text(0))); + } + } + + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + unitsTreeWidget->addTopLevelItem(item); + + return organization; +} + +void QtVCardOrganizationField::handleEditibleChanged(bool isEditable) { + if (organizationLineEdit) { + organizationLineEdit->setVisible(isEditable); + organizationLabel->setVisible(!isEditable); + + if (!isEditable) { + QString label; + for(int i=0; i < unitsTreeWidget->topLevelItemCount(); ++i) { + QTreeWidgetItem* row = unitsTreeWidget->topLevelItem(i); + if (!row->text(0).isEmpty()) { + label += row->text(0) + ", "; + } + } + label += organizationLineEdit->text(); + organizationLabel->setText(label); + } + } + if (unitsTreeWidget) unitsTreeWidget->setVisible(isEditable); +} + +void QtVCardOrganizationField::handleItemChanged(QTreeWidgetItem *, int) { + bool hasEmptyRow = false; + QList<QTreeWidgetItem*> rows = unitsTreeWidget->findItems("", Qt::MatchFixedString); + foreach(QTreeWidgetItem* row, rows) { + if (row->text(0).isEmpty()) { + hasEmptyRow = true; + } + } + + if (!hasEmptyRow) { + QTreeWidgetItem* item = new QTreeWidgetItem(QStringList("") << ""); + item->setFlags(item->flags() | Qt::ItemIsEditable); + unitsTreeWidget->addTopLevelItem(item); + } + getTagComboBox()->hide(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h new file mode 100644 index 0000000..917e22a --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardOrganizationField.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include <QTreeWidget> + +#include "QtRemovableItemDelegate.h" +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardOrganizationField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Organization", UNLIMITED_INSTANCES, QtVCardOrganizationField) + + QtVCardOrganizationField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardOrganizationField(); + + virtual bool isEmpty() const; + + void setOrganization(const VCard::Organization& organization); + VCard::Organization getOrganization() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private slots: + void handleItemChanged(QTreeWidgetItem*, int); + + private: + QLabel* organizationLabel; + QtResizableLineEdit* organizationLineEdit; + QTreeWidget* unitsTreeWidget; + QtRemovableItemDelegate* itemDelegate; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp new file mode 100644 index 0000000..3ddc86b --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardPhotoAndNameFields.h" +#include "ui_QtVCardPhotoAndNameFields.h" + +#include <QMenu> + +namespace Swift { + +QtVCardPhotoAndNameFields::QtVCardPhotoAndNameFields(QWidget* parent) : + QWidget(parent), + ui(new Ui::QtVCardPhotoAndNameFields) { + ui->setupUi(this); + ui->lineEditPREFIX->hide(); + ui->lineEditMIDDLE->hide(); + ui->lineEditSUFFIX->hide(); + ui->lineEditFN->hide(); + ui->lineEditNICKNAME->hide(); + ui->labelFULLNAME->hide(); + +#if QT_VERSION >= 0x040700 + ui->lineEditFN->setPlaceholderText(tr("Formatted Name")); + ui->lineEditNICKNAME->setPlaceholderText(tr("Nickname")); + ui->lineEditPREFIX->setPlaceholderText(tr("Prefix")); + ui->lineEditGIVEN->setPlaceholderText(tr("Given Name")); + ui->lineEditMIDDLE->setPlaceholderText(tr("Middle Name")); + ui->lineEditFAMILY->setPlaceholderText(tr("Last Name")); + ui->lineEditSUFFIX->setPlaceholderText(tr("Suffix")); +#endif + + addFieldMenu = new QMenu("Name", this); + + actionSignalMapper = new QSignalMapper(this); + + connect(actionSignalMapper, SIGNAL(mapped(const QString &)), this, SLOT(showField(const QString &))); + prepareAddFieldMenu(); +} + +QtVCardPhotoAndNameFields::~QtVCardPhotoAndNameFields() { + delete ui; + delete actionSignalMapper; +} + +bool QtVCardPhotoAndNameFields::isEditable() const { + return editable; +} + +void QtVCardPhotoAndNameFields::setEditable(bool editable) { + this->editable = editable; + + ui->avatarWidget->setEditable(editable); + ui->lineEditFN->setVisible(editable ? true : !ui->lineEditFN->text().isEmpty()); + ui->lineEditFN->setEditable(editable); + ui->lineEditFN->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}"); + + ui->lineEditNICKNAME->setVisible(editable ? true : !ui->lineEditNICKNAME->text().isEmpty()); + ui->lineEditNICKNAME->setEditable(editable); + ui->lineEditNICKNAME->setStyleSheet(editable ? "" : "QLineEdit {border: none; background-color: transparent;}"); + + // prefix given middle last suffix + ui->lineEditPREFIX->setVisible(editable); + ui->lineEditGIVEN->setVisible(editable); + ui->lineEditMIDDLE->setVisible(editable); + ui->lineEditFAMILY->setVisible(editable); + ui->lineEditSUFFIX->setVisible(editable); + ui->labelFULLNAME->setVisible(!editable); + ui->labelFULLNAME->setText( ui->lineEditPREFIX->text() + " " + ui->lineEditGIVEN->text() + " " + + ui->lineEditMIDDLE->text() + " " + ui->lineEditFAMILY->text() + " " + ui->lineEditSUFFIX->text()); + + prepareAddFieldMenu(); +} + +QMenu* QtVCardPhotoAndNameFields::getAddFieldMenu() const { + return addFieldMenu; +} + +void QtVCardPhotoAndNameFields::setAvatar(const ByteArray &data, const std::string &type) { + ui->avatarWidget->setAvatar(data, type); +} + +ByteArray QtVCardPhotoAndNameFields::getAvatarData() const { + return ui->avatarWidget->getAvatarData(); +} + +std::string QtVCardPhotoAndNameFields::getAvatarType() const { + return ui->avatarWidget->getAvatarType(); +} + +void QtVCardPhotoAndNameFields::setFormattedName(const QString formattedName) { + ui->lineEditFN->setText(formattedName); +} + +QString QtVCardPhotoAndNameFields::getFormattedName() const { + return ui->lineEditFN->text(); +} + +void QtVCardPhotoAndNameFields::setNickname(const QString nickname) { + ui->lineEditNICKNAME->setText(nickname); +} + +QString QtVCardPhotoAndNameFields::getNickname() const { + return ui->lineEditNICKNAME->text(); +} + +void QtVCardPhotoAndNameFields::setPrefix(const QString prefix) { + ui->lineEditPREFIX->setText(prefix); +} + +QString QtVCardPhotoAndNameFields::getPrefix() const { + return ui->lineEditPREFIX->text(); +} + +void QtVCardPhotoAndNameFields::setGivenName(const QString givenName) { + ui->lineEditGIVEN->setText(givenName); +} + +QString QtVCardPhotoAndNameFields::getGivenName() const { + return ui->lineEditGIVEN->text(); +} + +void QtVCardPhotoAndNameFields::setMiddleName(const QString middleName) { + ui->lineEditMIDDLE->setText(middleName); +} + +QString QtVCardPhotoAndNameFields::getMiddleName() const { + return ui->lineEditMIDDLE->text(); +} + +void QtVCardPhotoAndNameFields::setFamilyName(const QString familyName) { + ui->lineEditFAMILY->setText(familyName); +} + +QString QtVCardPhotoAndNameFields::getFamilyName() const { + return ui->lineEditFAMILY->text(); +} + +void QtVCardPhotoAndNameFields::setSuffix(const QString suffix) { + ui->lineEditSUFFIX->setText(suffix); +} + +QString QtVCardPhotoAndNameFields::getSuffix() const { + return ui->lineEditSUFFIX->text(); +} + +void QtVCardPhotoAndNameFields::prepareAddFieldMenu() { + foreach(QAction* action, addFieldMenu->actions()) { + actionSignalMapper->removeMappings(action); + } + + addFieldMenu->clear(); + foreach(QObject* obj, children()) { + QLineEdit* lineEdit = 0; + if (!(lineEdit = dynamic_cast<QLineEdit*>(obj))) continue; + if (lineEdit->isHidden()) { +#if QT_VERSION >= 0x040700 + QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->placeholderText(), actionSignalMapper, SLOT(map())); +#else + QAction* action = addFieldMenu->addAction(QString("Add ") + lineEdit->toolTip(), actionSignalMapper, SLOT(map())); +#endif + actionSignalMapper->setMapping(action, lineEdit->objectName()); + } + } +} + +void QtVCardPhotoAndNameFields::showField(const QString& widgetName) { + QLineEdit* lineEditToShow = findChild<QLineEdit*>(widgetName); + if (lineEditToShow) lineEditToShow->show(); + + prepareAddFieldMenu(); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h new file mode 100644 index 0000000..f279701 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QMenu> +#include <QSignalMapper> +#include <QWidget> +#include <Swiften/Base/ByteArray.h> + +namespace Ui { + class QtVCardPhotoAndNameFields; +} + + +namespace Swift { + + class QtVCardPhotoAndNameFields : public QWidget { + Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + + public: + explicit QtVCardPhotoAndNameFields(QWidget* parent = 0); + ~QtVCardPhotoAndNameFields(); + + bool isEditable() const; + void setEditable(bool); + + QMenu* getAddFieldMenu() const; + + void setAvatar(const ByteArray& data, const std::string& type); + ByteArray getAvatarData() const; + std::string getAvatarType() const; + + void setFormattedName(const QString formattedName); + QString getFormattedName() const; + + void setNickname(const QString nickname); + QString getNickname() const; + + void setPrefix(const QString prefix); + QString getPrefix() const; + + void setGivenName(const QString givenName); + QString getGivenName() const; + + void setMiddleName(const QString middleName); + QString getMiddleName() const; + + void setFamilyName(const QString familyName); + QString getFamilyName() const; + + void setSuffix(const QString suffix); + QString getSuffix() const; + + public slots: + void showField(const QString& widgetName); + + private: + void prepareAddFieldMenu(); + + private: + Ui::QtVCardPhotoAndNameFields* ui; + bool editable; + + QMenu* addFieldMenu; + QSignalMapper* actionSignalMapper; + }; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui new file mode 100644 index 0000000..04da2bc --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardPhotoAndNameFields.ui @@ -0,0 +1,251 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtVCardPhotoAndNameFields</class> + <widget class="QWidget" name="QtVCardPhotoAndNameFields"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>522</width> + <height>81</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" rowstretch="0,0,0,0,0" rowminimumheight="0,0,0,0,0"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <property name="horizontalSpacing"> + <number>5</number> + </property> + <property name="verticalSpacing"> + <number>1</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item row="0" column="0" rowspan="5"> + <widget class="Swift::QtAvatarWidget" name="avatarWidget" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" stdset="0"> + <string/> + </property> + <property name="flat" stdset="0"> + <bool>false</bool> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditFN"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="font"> + <font> + <pointsize>18</pointsize> + <weight>50</weight> + <bold>false</bold> + </font> + </property> + <property name="toolTip"> + <string>Formatted Name</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditNICKNAME"> + <property name="toolTip"> + <string>Nickname</string> + </property> + <property name="frame"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <property name="spacing"> + <number>2</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetMinimumSize</enum> + </property> + <item> + <widget class="QLabel" name="labelFULLNAME"> + <property name="text"> + <string/> + </property> + <property name="textInteractionFlags"> + <set>Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditPREFIX"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Prefix</string> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditGIVEN"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Given Name</string> + </property> + <property name="text"> + <string/> + </property> + <property name="readOnly"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditMIDDLE"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Middle Name</string> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="frame"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditFAMILY"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Last Name</string> + </property> + </widget> + </item> + <item> + <widget class="Swift::QtResizableLineEdit" name="lineEditSUFFIX"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Suffix</string> + </property> + <property name="readOnly"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtResizableLineEdit</class> + <extends>QLineEdit</extends> + <header>QtResizableLineEdit.h</header> + </customwidget> + <customwidget> + <class>Swift::QtAvatarWidget</class> + <extends>QWidget</extends> + <header>Swift/QtUI/QtAvatarWidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp new file mode 100644 index 0000000..8af4e64 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardRoleField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardRoleField::QtVCardRoleField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Role"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardRoleField::~QtVCardRoleField() { +} + +void QtVCardRoleField::setupContentWidgets() { + roleLineEdit = new QtResizableLineEdit(this); + getGridLayout()->addWidget(roleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + getTagComboBox()->hide(); + childWidgets << roleLineEdit; +} + +bool QtVCardRoleField::isEmpty() const { + return roleLineEdit->text().isEmpty(); +} + +void QtVCardRoleField::setRole(const std::string& role) { + roleLineEdit->setText(P2QSTRING(role)); +} + +std::string QtVCardRoleField::getRole() const { + return Q2PSTRING(roleLineEdit->text()); +} + +void QtVCardRoleField::handleEditibleChanged(bool isEditable) { + if (roleLineEdit) { + roleLineEdit->setEditable(isEditable); + roleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h new file mode 100644 index 0000000..3c819ed --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardRoleField.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardRoleField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Role", UNLIMITED_INSTANCES, QtVCardRoleField) + + QtVCardRoleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardRoleField(); + + virtual bool isEmpty() const; + + void setRole(const std::string& role); + std::string getRole() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QtResizableLineEdit* roleLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp new file mode 100644 index 0000000..ee93c01 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardTelephoneField.h" + +#include <QGridLayout> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardTelephoneField::QtVCardTelephoneField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Telephone")) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardTelephoneField::~QtVCardTelephoneField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardTelephoneField::setupContentWidgets() { + telephoneLineEdit = new QtResizableLineEdit(this); +#if QT_VERSION >= 0x040700 + telephoneLineEdit->setPlaceholderText(tr("0118 999 881 999 119 7253")); +#endif + getGridLayout()->addWidget(telephoneLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + setTabOrder(telephoneLineEdit, getTagComboBox()); + QtVCardHomeWork::setTagComboBox(getTagComboBox()); + + getTagComboBox()->addTag("voice", QObject::tr("Voice")); + getTagComboBox()->addTag("fax", QObject::tr("Fax")); + getTagComboBox()->addTag("pager", QObject::tr("Pager")); + getTagComboBox()->addTag("msg", QObject::tr("Voice Messaging")); + getTagComboBox()->addTag("cell", QObject::tr("Cell")); + getTagComboBox()->addTag("video", QObject::tr("Video")); + getTagComboBox()->addTag("bbs", QObject::tr("Bulletin Board System")); + getTagComboBox()->addTag("modem", QObject::tr("Modem")); + getTagComboBox()->addTag("isdn", QObject::tr("ISDN")); + getTagComboBox()->addTag("pcs", QObject::tr("Personal Communication Services")); + + childWidgets << telephoneLineEdit; +} + +bool QtVCardTelephoneField::isEmpty() const { + return telephoneLineEdit->text().isEmpty(); +} + +void QtVCardTelephoneField::setTelephone(const VCard::Telephone& telephone) { + setPreferred(telephone.isPreferred); + setHome(telephone.isHome); + setWork(telephone.isWork); + + telephoneLineEdit->setText(P2QSTRING(telephone.number)); + + getTagComboBox()->setTag("voice", telephone.isVoice); + getTagComboBox()->setTag("fax", telephone.isFax); + getTagComboBox()->setTag("pager", telephone.isPager); + getTagComboBox()->setTag("msg", telephone.isMSG); + getTagComboBox()->setTag("cell", telephone.isCell); + getTagComboBox()->setTag("video", telephone.isVideo); + getTagComboBox()->setTag("bbs", telephone.isBBS); + getTagComboBox()->setTag("modem", telephone.isModem); + getTagComboBox()->setTag("isdn", telephone.isISDN); + getTagComboBox()->setTag("pcs", telephone.isPCS); +} + +VCard::Telephone QtVCardTelephoneField::getTelephone() const { + VCard::Telephone telephone; + + telephone.number = Q2PSTRING(telephoneLineEdit->text()); + + telephone.isPreferred = getPreferred(); + telephone.isHome = getHome(); + telephone.isWork = getWork(); + + telephone.isVoice = getTagComboBox()->isTagSet("voice"); + telephone.isFax = getTagComboBox()->isTagSet("fax"); + telephone.isPager = getTagComboBox()->isTagSet("pager"); + telephone.isMSG = getTagComboBox()->isTagSet("msg"); + telephone.isCell = getTagComboBox()->isTagSet("cell"); + telephone.isVideo = getTagComboBox()->isTagSet("video"); + telephone.isBBS = getTagComboBox()->isTagSet("bbs"); + telephone.isModem = getTagComboBox()->isTagSet("modem"); + telephone.isISDN = getTagComboBox()->isTagSet("isdn"); + telephone.isPCS = getTagComboBox()->isTagSet("pcs"); + + return telephone; +} + +void QtVCardTelephoneField::handleEditibleChanged(bool isEditable) { + if (telephoneLineEdit) { + telephoneLineEdit->setEditable(isEditable); + telephoneLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h new file mode 100644 index 0000000..b433e3c --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTelephoneField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardHomeWork.h" + +namespace Swift { + +class QtVCardTelephoneField : public QtVCardGeneralField, public QtVCardHomeWork { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Telephone", UNLIMITED_INSTANCES, QtVCardTelephoneField) + + QtVCardTelephoneField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardTelephoneField(); + + virtual bool isEmpty() const; + + void setTelephone(const VCard::Telephone& telephone); + VCard::Telephone getTelephone() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QtResizableLineEdit* telephoneLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp new file mode 100644 index 0000000..aac4e31 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardTitleField.h" + +#include <QGridLayout> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> + +namespace Swift { + +QtVCardTitleField::QtVCardTitleField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("Title"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardTitleField::~QtVCardTitleField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardTitleField::setupContentWidgets() { + titleLineEdit = new QtResizableLineEdit(this); + getGridLayout()->addWidget(titleLineEdit, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + getTagComboBox()->hide(); + childWidgets << titleLineEdit; +} + +bool QtVCardTitleField::isEmpty() const { + return titleLineEdit->text().isEmpty(); +} + +void QtVCardTitleField::setTitle(const std::string& title) { + titleLineEdit->setText(P2QSTRING(title)); +} + +std::string QtVCardTitleField::getTitle() const { + return Q2PSTRING(titleLineEdit->text()); +} + +void QtVCardTitleField::handleEditibleChanged(bool isEditable) { + if (titleLineEdit) { + titleLineEdit->setEditable(isEditable); + titleLineEdit->setStyleSheet(isEditable ? "" : "QLineEdit { border: none; background: transparent; }"); + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h new file mode 100644 index 0000000..28dc603 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardTitleField.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardTitleField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("Title", UNLIMITED_INSTANCES, QtVCardTitleField) + + QtVCardTitleField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardTitleField(); + + virtual bool isEmpty() const; + + void setTitle(const std::string& title); + std::string getTitle() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QtResizableLineEdit* titleLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp new file mode 100644 index 0000000..35cc4ce --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardURLField.h" + +#include <QGridLayout> +#include <QHBoxLayout> +#include <QTextDocument> +#include <boost/algorithm/string.hpp> + +#include <Swift/QtUI/QtSwiftUtil.h> +#include <Swift/QtUI/QtUtilities.h> + + +namespace Swift { + +QtVCardURLField::QtVCardURLField(QWidget* parent, QGridLayout *layout, bool editable) : + QtVCardGeneralField(parent, layout, editable, layout->rowCount(), tr("URL"), false, false) { + connect(this, SIGNAL(editableChanged(bool)), SLOT(handleEditibleChanged(bool))); +} + +QtVCardURLField::~QtVCardURLField() { + disconnect(this, SLOT(handleEditibleChanged(bool))); +} + +void QtVCardURLField::setupContentWidgets() { + urlLabel = new QLabel(this); + urlLabel->setOpenExternalLinks(true); + urlLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); + urlLineEdit = new QtResizableLineEdit(this); + + QHBoxLayout* urlLayout = new QHBoxLayout(); + urlLayout->addWidget(urlLabel); + urlLayout->addWidget(urlLineEdit); + + getGridLayout()->addLayout(urlLayout, getGridLayout()->rowCount()-1, 2, 1, 2, Qt::AlignVCenter); + getTagComboBox()->hide(); + urlLabel->hide(); + childWidgets << urlLabel << urlLineEdit; +} + +bool QtVCardURLField::isEmpty() const { + return urlLineEdit->text().isEmpty(); +} + +void QtVCardURLField::setURL(const std::string& url) { + urlLineEdit->setText(P2QSTRING(url)); +} + +std::string QtVCardURLField::getURL() const { + return Q2PSTRING(urlLineEdit->text()); +} + +void QtVCardURLField::handleEditibleChanged(bool isEditable) { + if (isEditable) { + if (urlLineEdit) urlLineEdit->show(); + if (urlLabel) urlLabel->hide(); + } else { + if (urlLineEdit) urlLineEdit->hide(); + if (urlLabel) { + urlLabel->setText(QString("<a href=\"%1\">%1</a>").arg(QtUtilities::htmlEscape(urlLineEdit->text()))); + urlLabel->show(); + } + } +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardURLField.h b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h new file mode 100644 index 0000000..2c011d8 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardURLField.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <Swiften/Elements/VCard.h> + +#include "QtResizableLineEdit.h" +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" + +namespace Swift { + +class QtVCardURLField : public QtVCardGeneralField { + Q_OBJECT + + public: + GENERIC_QT_VCARD_FIELD_INFO("URL", UNLIMITED_INSTANCES, QtVCardURLField) + + QtVCardURLField(QWidget* parent = 0, QGridLayout* layout = 0, bool editable = false); + virtual ~QtVCardURLField(); + + virtual bool isEmpty() const; + + void setURL(const std::string& url); + std::string getURL() const; + + protected: + virtual void setupContentWidgets(); + + public slots: + void handleEditibleChanged(bool isEditable); + + private: + QLabel* urlLabel; + QtResizableLineEdit* urlLineEdit; +}; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp new file mode 100644 index 0000000..1c80fa4 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.cpp @@ -0,0 +1,368 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include "QtVCardWidget.h" +#include "ui_QtVCardWidget.h" + +#include <QDebug> +#include <QLineEdit> +#include <QMenu> + +#include "QtVCardAddressField.h" +#include "QtVCardAddressLabelField.h" +#include "QtVCardBirthdayField.h" +#include "QtVCardDescriptionField.h" +#include "QtVCardGeneralField.h" +#include "QtVCardInternetEMailField.h" +#include "QtVCardJIDField.h" +#include "QtVCardOrganizationField.h" +#include "QtVCardRoleField.h" +#include "QtVCardTelephoneField.h" +#include "QtVCardTitleField.h" +#include "QtVCardURLField.h" + +#include <Swift/QtUI/QtSwiftUtil.h> + +#include <Swiften/Base/Log.h> + +namespace Swift { + +QtVCardWidget::QtVCardWidget(QWidget* parent) : + QWidget(parent), + ui(new ::Ui::QtVCardWidget) { + ui->setupUi(this); + + ui->cardFields->setColumnStretch(0,0); + ui->cardFields->setColumnStretch(1,0); + ui->cardFields->setColumnStretch(2,2); + ui->cardFields->setColumnStretch(3,1); + ui->cardFields->setColumnStretch(4,2); + menu = new QMenu(this); + + menu->addMenu(ui->photoAndName->getAddFieldMenu()); + ui->toolButton->setMenu(menu); + + addFieldType(menu, boost::make_shared<QtVCardInternetEMailField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardTelephoneField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardAddressField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardAddressLabelField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardBirthdayField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardJIDField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardDescriptionField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardRoleField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardTitleField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardOrganizationField::FieldInfo>()); + addFieldType(menu, boost::make_shared<QtVCardURLField::FieldInfo>()); + + setEditable(false); +} + +QtVCardWidget::~QtVCardWidget() { + delete ui; +} + +bool QtVCardWidget::isEditable() const { + return editable; +} + +void QtVCardWidget::setEditable(bool editable) { + this->editable = editable; + + ui->photoAndName->setProperty("editable", QVariant(editable)); + + foreach(QtVCardGeneralField* field, fields) { + field->setEditable(editable); + } + + if (editable) { + ui->toolButton->show(); + //if ((findChild<QtVCardBirthdayField*>() == 0)) { + //} + } else { + ui->toolButton->hide(); + } + + editableChanged(editable); +} + +void QtVCardWidget::setVCard(VCard::ref vcard) { + SWIFT_LOG(debug) << std::endl; + clearFields(); + this->vcard = vcard; + ui->photoAndName->setFormattedName(P2QSTRING(vcard->getFullName())); + ui->photoAndName->setNickname(P2QSTRING(vcard->getNickname())); + ui->photoAndName->setPrefix(P2QSTRING(vcard->getPrefix())); + ui->photoAndName->setGivenName(P2QSTRING(vcard->getGivenName())); + ui->photoAndName->setMiddleName(P2QSTRING(vcard->getMiddleName())); + ui->photoAndName->setFamilyName(P2QSTRING(vcard->getFamilyName())); + ui->photoAndName->setSuffix(P2QSTRING(vcard->getSuffix())); + ui->photoAndName->setAvatar(vcard->getPhoto(), vcard->getPhotoType()); + + foreach (const VCard::EMailAddress& address, vcard->getEMailAddresses()) { + if (address.isInternet) { + QtVCardInternetEMailField* internetEmailField = new QtVCardInternetEMailField(this, ui->cardFields); + internetEmailField->initialize(); + internetEmailField->setInternetEMailAddress(address); + appendField(internetEmailField); + } + } + + foreach (const VCard::Telephone& telephone, vcard->getTelephones()) { + QtVCardTelephoneField* telField = new QtVCardTelephoneField(this, ui->cardFields); + telField->initialize(); + telField->setTelephone(telephone); + appendField(telField); + } + + foreach (const VCard::Address& address, vcard->getAddresses()) { + QtVCardAddressField* addressField = new QtVCardAddressField(this, ui->cardFields); + addressField->initialize(); + addressField->setAddress(address); + appendField(addressField); + } + + foreach (const VCard::AddressLabel& label, vcard->getAddressLabels()) { + QtVCardAddressLabelField* addressLabelField = new QtVCardAddressLabelField(this, ui->cardFields); + addressLabelField->initialize(); + addressLabelField->setAddressLabel(label); + appendField(addressLabelField); + } + + if (!vcard->getBirthday().is_not_a_date_time()) { + QtVCardBirthdayField* bdayField = new QtVCardBirthdayField(this, ui->cardFields); + bdayField->initialize(); + bdayField->setBirthday(vcard->getBirthday()); + appendField(bdayField); + } + + foreach (const JID& jid, vcard->getJIDs()) { + QtVCardJIDField* jidField = new QtVCardJIDField(this, ui->cardFields); + jidField->initialize(); + jidField->setJID(jid); + appendField(jidField); + } + + if (!vcard->getDescription().empty()) { + QtVCardDescriptionField* descField = new QtVCardDescriptionField(this, ui->cardFields); + descField->initialize(); + descField->setDescription(vcard->getDescription()); + appendField(descField); + } + + foreach (const VCard::Organization& org, vcard->getOrganizations()) { + QtVCardOrganizationField* orgField = new QtVCardOrganizationField(this, ui->cardFields); + orgField->initialize(); + orgField->setOrganization(org); + appendField(orgField); + } + + foreach (const std::string& role, vcard->getRoles()) { + QtVCardRoleField* roleField = new QtVCardRoleField(this, ui->cardFields); + roleField->initialize(); + roleField->setRole(role); + appendField(roleField); + } + + foreach (const std::string& title, vcard->getTitles()) { + QtVCardTitleField* titleField = new QtVCardTitleField(this, ui->cardFields); + titleField->initialize(); + titleField->setTitle(title); + appendField(titleField); + } + + foreach (const std::string& url, vcard->getURLs()) { + QtVCardURLField* urlField = new QtVCardURLField(this, ui->cardFields); + urlField->initialize(); + urlField->setURL(url); + appendField(urlField); + } + + setEditable(editable); +} + +VCard::ref QtVCardWidget::getVCard() { + SWIFT_LOG(debug) << std::endl; + clearEmptyFields(); + vcard->setFullName(Q2PSTRING(ui->photoAndName->getFormattedName())); + vcard->setNickname(Q2PSTRING(ui->photoAndName->getNickname())); + vcard->setPrefix(Q2PSTRING(ui->photoAndName->getPrefix())); + vcard->setGivenName(Q2PSTRING(ui->photoAndName->getGivenName())); + vcard->setMiddleName(Q2PSTRING(ui->photoAndName->getMiddleName())); + vcard->setFamilyName(Q2PSTRING(ui->photoAndName->getFamilyName())); + vcard->setSuffix(Q2PSTRING(ui->photoAndName->getSuffix())); + vcard->setPhoto(ui->photoAndName->getAvatarData()); + vcard->setPhotoType(ui->photoAndName->getAvatarType()); + + vcard->clearEMailAddresses(); + vcard->clearJIDs(); + vcard->clearURLs(); + vcard->clearTelephones(); + vcard->clearRoles(); + vcard->clearTitles(); + vcard->clearOrganizations(); + vcard->clearAddresses(); + vcard->clearAddressLabels(); + + + QtVCardBirthdayField* bdayField = NULL; + QtVCardDescriptionField* descriptionField = NULL; + + foreach(QtVCardGeneralField* field, fields) { + QtVCardInternetEMailField* emailField; + if ((emailField = dynamic_cast<QtVCardInternetEMailField*>(field))) { + vcard->addEMailAddress(emailField->getInternetEMailAddress()); + continue; + } + + QtVCardTelephoneField* telephoneField; + if ((telephoneField = dynamic_cast<QtVCardTelephoneField*>(field))) { + vcard->addTelephone(telephoneField->getTelephone()); + continue; + } + + QtVCardAddressField* addressField; + if ((addressField = dynamic_cast<QtVCardAddressField*>(field))) { + vcard->addAddress(addressField->getAddress()); + continue; + } + + QtVCardAddressLabelField* addressLabelField; + if ((addressLabelField = dynamic_cast<QtVCardAddressLabelField*>(field))) { + vcard->addAddressLabel(addressLabelField->getAddressLabel()); + continue; + } + + if ((bdayField = dynamic_cast<QtVCardBirthdayField*>(field))) { + continue; + } + + QtVCardJIDField* jidField; + if ((jidField = dynamic_cast<QtVCardJIDField*>(field))) { + vcard->addJID(jidField->getJID()); + continue; + } + + if ((descriptionField = dynamic_cast<QtVCardDescriptionField*>(field))) { + continue; + } + + QtVCardOrganizationField* orgField; + if ((orgField = dynamic_cast<QtVCardOrganizationField*>(field))) { + vcard->addOrganization(orgField->getOrganization()); + continue; + } + + QtVCardRoleField* roleField; + if ((roleField = dynamic_cast<QtVCardRoleField*>(field))) { + vcard->addRole(roleField->getRole()); + continue; + } + + QtVCardTitleField* titleField; + if ((titleField = dynamic_cast<QtVCardTitleField*>(field))) { + vcard->addTitle(titleField->getTitle()); + continue; + } + + QtVCardURLField* urlField; + if ((urlField = dynamic_cast<QtVCardURLField*>(field))) { + vcard->addURL(urlField->getURL()); + continue; + } + } + + if (bdayField) { + vcard->setBirthday(bdayField->getBirthday()); + } else { + vcard->setBirthday(boost::posix_time::ptime()); + } + + if (descriptionField) { + vcard->setDescription(descriptionField->getDescription()); + } else { + vcard->setDescription(""); + } + + return vcard; +} + +void QtVCardWidget::addField() { + QAction* action = NULL; + if ((action = dynamic_cast<QAction*>(sender()))) { + boost::shared_ptr<QtVCardFieldInfo> fieldInfo = actionFieldInfo[action]; + QWidget* newField = fieldInfo->createFieldInstance(this, ui->cardFields, true); + QtVCardGeneralField* newGeneralField = dynamic_cast<QtVCardGeneralField*>(newField); + if (newGeneralField) { + newGeneralField->initialize(); + } + appendField(newGeneralField); + } +} + +void QtVCardWidget::removeField(QtVCardGeneralField *field) { + fields.remove(field); + delete field; +} + +void QtVCardWidget::addFieldType(QMenu* menu, boost::shared_ptr<QtVCardFieldInfo> fieldType) { + QAction* action = new QAction(tr("Add ") + fieldType->getMenuName(), this); + actionFieldInfo[action] = fieldType; + connect(action, SIGNAL(triggered()), this, SLOT(addField())); + menu->addAction(action); +} + +int QtVCardWidget::fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo> fieldType) { + int instances = 0; + for (int n = 0; n < ui->cardFields->count(); n++) { + if (fieldType->testInstance(ui->cardFields->itemAt(n)->widget())) instances++; + } + return instances; +} + +void layoutDeleteChildren(QLayout *layout) { + while(layout->count() > 0) { + QLayoutItem* child; + if ((child = layout->takeAt(0)) != 0) { + if (child->layout()) { + layoutDeleteChildren(child->layout()); + } + delete child->widget(); + delete child; + } + } +} + +void QtVCardWidget::clearFields() { + foreach(QtVCardGeneralField* field, fields) { + delete field; + } + fields.clear(); + + assert(ui->cardFields->count() >= 0); + layoutDeleteChildren(ui->cardFields); +} + +void QtVCardWidget::clearEmptyFields() { + std::vector<QtVCardGeneralField*> items_to_remove; + foreach (QtVCardGeneralField* field, fields) { + if (field->property("empty").isValid() && field->property("empty").toBool()) { + ui->cardFields->removeWidget(field); + items_to_remove.push_back(field); + delete field; + } + } + + foreach(QtVCardGeneralField* field, items_to_remove) { + fields.remove(field); + } +} + +void QtVCardWidget::appendField(QtVCardGeneralField *field) { + connect(field, SIGNAL(deleteField(QtVCardGeneralField*)), SLOT(removeField(QtVCardGeneralField*))); + fields.push_back(field); +} + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.h b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h new file mode 100644 index 0000000..07f528d --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2012 Tobias Markmann + * Licensed under the simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include <QWidget> +#include <Swiften/Elements/VCard.h> +#include <boost/smart_ptr/make_shared.hpp> + +#include "QtVCardFieldInfo.h" +#include "QtVCardGeneralField.h" +#include "QtVCardPhotoAndNameFields.h" + +namespace Ui { + class QtVCardWidget; +} + +namespace Swift { + + class QtVCardWidget : public QWidget { + Q_OBJECT + Q_PROPERTY(bool editable READ isEditable WRITE setEditable) + + public : + explicit QtVCardWidget(QWidget* parent = 0); + ~QtVCardWidget(); + + bool isEditable() const; + void setEditable(bool); + + void setVCard(VCard::ref vcard); + VCard::ref getVCard(); + + signals: + void editableChanged(bool editable); + + private slots: + void addField(); + void removeField(QtVCardGeneralField* field); + + private: + void addFieldType(QMenu*, boost::shared_ptr<QtVCardFieldInfo>); + int fieldTypeInstances(boost::shared_ptr<QtVCardFieldInfo>); + void clearFields(); + void clearEmptyFields(); + void appendField(QtVCardGeneralField* field); + + private: + VCard::ref vcard; + Ui::QtVCardWidget* ui; + bool editable; + QMenu* menu; + std::list<QtVCardGeneralField*> fields; + std::map<QAction*, boost::shared_ptr<QtVCardFieldInfo> > actionFieldInfo; + }; + +} diff --git a/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui new file mode 100644 index 0000000..eae1006 --- /dev/null +++ b/Swift/QtUI/QtVCardWidget/QtVCardWidget.ui @@ -0,0 +1,137 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>QtVCardWidget</class> + <widget class="QWidget" name="QtVCardWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>535</width> + <height>126</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout" rowstretch="1,0" columnstretch="1,0"> + <property name="margin"> + <number>5</number> + </property> + <item row="0" column="0" colspan="2"> + <layout class="QVBoxLayout" name="card" stretch="0,0,1"> + <property name="spacing"> + <number>2</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetDefaultConstraint</enum> + </property> + <item> + <widget class="Swift::QtVCardPhotoAndNameFields" name="photoAndName" native="true"/> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QScrollArea" name="scrollArea"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Plain</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="widgetResizable"> + <bool>true</bool> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + <widget class="QWidget" name="scrollAreaWidgetContents"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>523</width> + <height>76</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="sizeConstraint"> + <enum>QLayout::SetMinAndMaxSize</enum> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <layout class="QGridLayout" name="cardFields"> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Preferred</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>1000</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </item> + <item row="1" column="1"> + <widget class="QToolButton" name="toolButton"> + <property name="text"> + <string>Add Field</string> + </property> + <property name="popupMode"> + <enum>QToolButton::InstantPopup</enum> + </property> + <property name="autoRaise"> + <bool>false</bool> + </property> + <property name="arrowType"> + <enum>Qt::NoArrow</enum> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>Swift::QtVCardPhotoAndNameFields</class> + <extends>QWidget</extends> + <header>QtVCardPhotoAndNameFields.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Swift/QtUI/QtWebView.cpp b/Swift/QtUI/QtWebView.cpp index 388f06a..1222ee2 100644 --- a/Swift/QtUI/QtWebView.cpp +++ b/Swift/QtUI/QtWebView.cpp @@ -9,6 +9,7 @@ #include <QKeyEvent> #include <QFocusEvent> +#include <boost/numeric/conversion/cast.hpp> #include <QMenu> namespace Swift { @@ -30,7 +31,7 @@ void QtWebView::keyPressEvent(QKeyEvent* event) { modifiers, event->text(), event->isAutoRepeat(), - event->count()); + boost::numeric_cast<unsigned short>(event->count())); QWebView::keyPressEvent(translatedEvent); delete translatedEvent; } diff --git a/Swift/QtUI/QtWin32NotifierWindow.h b/Swift/QtUI/QtWin32NotifierWindow.h index b8d9c77..cd43cf2 100644 --- a/Swift/QtUI/QtWin32NotifierWindow.h +++ b/Swift/QtUI/QtWin32NotifierWindow.h @@ -23,7 +23,7 @@ namespace Swift { } virtual HWND getID() const { - return winId(); + return (HWND) winId(); } }; } diff --git a/Swift/QtUI/QtXMLConsoleWidget.cpp b/Swift/QtUI/QtXMLConsoleWidget.cpp index e674df8..284f3c3 100644 --- a/Swift/QtUI/QtXMLConsoleWidget.cpp +++ b/Swift/QtUI/QtXMLConsoleWidget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Remko Tronçon + * Copyright (c) 2010-2013 Remko Tronçon * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -96,7 +96,11 @@ void QtXMLConsoleWidget::appendTextIfEnabled(const std::string& data, const QCol cursor.insertText(P2QSTRING(data)); cursor.endEditBlock(); - if (scrollToBottom) { + // Checking for the scrollbar again, because it could have appeared after inserting text. + // In practice, I suspect that the scrollbar is always there, but hidden, but since we were + // explicitly testing for this already above, I'm leaving the code in. + scrollBar = textEdit->verticalScrollBar(); + if (scrollToBottom && scrollBar) { scrollBar->setValue(scrollBar->maximum()); } } 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/QtOccupantListWidget.cpp b/Swift/QtUI/Roster/QtOccupantListWidget.cpp index 5d26c46..12dc1e4 100644 --- a/Swift/QtUI/Roster/QtOccupantListWidget.cpp +++ b/Swift/QtUI/Roster/QtOccupantListWidget.cpp @@ -58,6 +58,7 @@ void QtOccupantListWidget::contextMenuEvent(QContextMenuEvent* event) { case ChatWindow::MakeParticipant: text = tr("Make participant"); break; case ChatWindow::MakeVisitor: text = tr("Remove voice"); break; case ChatWindow::AddContact: text = tr("Add to contacts"); break; + case ChatWindow::ShowProfile: text = tr("Show profile"); break; } QAction* action = contextMenu.addAction(text); actions[action] = availableAction; diff --git a/Swift/QtUI/Roster/QtRosterWidget.cpp b/Swift/QtUI/Roster/QtRosterWidget.cpp index 1cf073b..6bf3989 100644 --- a/Swift/QtUI/Roster/QtRosterWidget.cpp +++ b/Swift/QtUI/Roster/QtRosterWidget.cpp @@ -4,23 +4,25 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ -#include "Roster/QtRosterWidget.h" +#include <Swift/QtUI/Roster/QtRosterWidget.h> #include <QContextMenuEvent> #include <QMenu> #include <QInputDialog> #include <QFileDialog> -#include "Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h" -#include "Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h" -#include "Swift/Controllers/UIEvents/RenameGroupUIEvent.h" -#include "Swift/Controllers/UIEvents/SendFileUIEvent.h" -#include "Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h" -#include "QtContactEditWindow.h" -#include "Swift/Controllers/Roster/ContactRosterItem.h" -#include "Swift/Controllers/Roster/GroupRosterItem.h" -#include "Swift/Controllers/UIEvents/UIEventStream.h" -#include "QtSwiftUtil.h" +#include <Swift/Controllers/UIEvents/RequestContactEditorUIEvent.h> +#include <Swift/Controllers/UIEvents/RemoveRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/RenameGroupUIEvent.h> +#include <Swift/Controllers/UIEvents/SendFileUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestWhiteboardUIEvent.h> +#include <Swift/Controllers/UIEvents/ShowProfileForRosterItemUIEvent.h> +#include <Swift/Controllers/UIEvents/RequestChangeBlockStateUIEvent.h> +#include <Swift/QtUI/QtContactEditWindow.h> +#include <Swift/Controllers/Roster/ContactRosterItem.h> +#include <Swift/Controllers/Roster/GroupRosterItem.h> +#include <Swift/Controllers/UIEvents/UIEventStream.h> +#include <Swift/QtUI/QtSwiftUtil.h> namespace Swift { @@ -57,6 +59,18 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) { if (ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item)) { QAction* editContact = contextMenu.addAction(tr("Edit…")); QAction* removeContact = contextMenu.addAction(tr("Remove")); + QAction* showProfileForContact = contextMenu.addAction(tr("Show Profile")); + + QAction* unblockContact = NULL; + if (contact->blockState() == ContactRosterItem::IsBlocked) { + unblockContact = contextMenu.addAction(tr("Unblock")); + } + + QAction* blockContact = NULL; + if (contact->blockState() == ContactRosterItem::IsUnblocked) { + blockContact = contextMenu.addAction(tr("Block")); + } + #ifdef SWIFT_EXPERIMENTAL_FT QAction* sendFile = NULL; if (contact->supportsFeature(ContactRosterItem::FileTransferFeature)) { @@ -69,6 +83,7 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) { startWhiteboardChat = contextMenu.addAction(tr("Start Whiteboard Chat")); } #endif + QAction* result = contextMenu.exec(event->globalPos()); if (result == editContact) { eventStream_->send(boost::make_shared<RequestContactEditorUIEvent>(contact->getJID())); @@ -78,6 +93,15 @@ void QtRosterWidget::contextMenuEvent(QContextMenuEvent* event) { eventStream_->send(boost::make_shared<RemoveRosterItemUIEvent>(contact->getJID())); } } + else if (result == showProfileForContact) { + eventStream_->send(boost::make_shared<ShowProfileForRosterItemUIEvent>(contact->getJID())); + } + else if (unblockContact && result == unblockContact) { + eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Unblocked, contact->getJID())); + } + else if (blockContact && result == blockContact) { + eventStream_->send(boost::make_shared<RequestChangeBlockStateUIEvent>(RequestChangeBlockStateUIEvent::Blocked, contact->getJID())); + } #ifdef SWIFT_EXPERIMENTAL_FT else if (sendFile && result == sendFile) { QString fileName = QFileDialog::getOpenFileName(this, tr("Send File"), "", tr("All Files (*);;")); diff --git a/Swift/QtUI/Roster/QtTreeWidget.cpp b/Swift/QtUI/Roster/QtTreeWidget.cpp index 5fdf138..64d0fcf 100644 --- a/Swift/QtUI/Roster/QtTreeWidget.cpp +++ b/Swift/QtUI/Roster/QtTreeWidget.cpp @@ -10,6 +10,7 @@ #include <boost/bind.hpp> #include <QUrl> +#include <QMimeData> #include <Swiften/Base/Platform.h> #include <Swift/Controllers/Roster/ContactRosterItem.h> diff --git a/Swift/QtUI/Roster/RosterDelegate.cpp b/Swift/QtUI/Roster/RosterDelegate.cpp index 7e6428b..aef588c 100644 --- a/Swift/QtUI/Roster/RosterDelegate.cpp +++ b/Swift/QtUI/Roster/RosterDelegate.cpp @@ -35,6 +35,7 @@ RosterDelegate::~RosterDelegate() { void RosterDelegate::setCompact(bool compact) { compact_ = compact; + emit sizeHintChanged(QModelIndex()); } QSize RosterDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index ) const { @@ -73,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..1b93047 100644 --- a/Swift/QtUI/Roster/RosterModel.cpp +++ b/Swift/QtUI/Roster/RosterModel.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Kevin Smith + * Copyright (c) 2010-2013 Kevin Smith * Licensed under the GNU General Public License v3. * See Documentation/Licenses/GPLv3.txt for more information. */ @@ -16,6 +16,7 @@ #include "Swift/Controllers/Roster/ContactRosterItem.h" #include "Swift/Controllers/Roster/GroupRosterItem.h" #include <Swift/Controllers/StatusUtil.h> +#include <Swiften/Base/Path.h> #include "QtSwiftUtil.h" #include "Swift/QtUI/Roster/QtTreeWidget.h" @@ -40,7 +41,8 @@ void RosterModel::setRoster(Roster* roster) { void RosterModel::reLayout() { //emit layoutChanged(); - reset(); + beginResetModel(); + endResetModel(); // TODO: Not sure if this isn't too early? if (!roster_) { return; } @@ -53,7 +55,7 @@ void RosterModel::reLayout() { void RosterModel::handleChildrenChanged(GroupRosterItem* /*group*/) { reLayout(); -} +} void RosterModel::handleDataChanged(RosterItem* item) { Q_ASSERT(item); @@ -84,7 +86,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 +96,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 +139,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; } @@ -140,7 +151,7 @@ QString RosterModel::getAvatar(RosterItem* item) const { if (!contact) { return ""; } - return QString(contact->getAvatarPath().c_str()); + return P2QSTRING(pathToString(contact->getAvatarPath())); } QString RosterModel::getStatusText(RosterItem* item) const { @@ -152,14 +163,18 @@ QString RosterModel::getStatusText(RosterItem* item) const { QIcon RosterModel::getPresenceIcon(RosterItem* item) const { ContactRosterItem* contact = dynamic_cast<ContactRosterItem*>(item); if (!contact) return QIcon(); + if (contact->blockState() == ContactRosterItem::IsBlocked) { + return QIcon(":/icons/stop.png"); + } + QString iconString; switch (contact->getStatusShow()) { - case StatusShow::Online: iconString = "online";break; - case StatusShow::Away: iconString = "away";break; - case StatusShow::XA: iconString = "away";break; - case StatusShow::FFC: iconString = "online";break; - case StatusShow::DND: iconString = "dnd";break; - case StatusShow::None: iconString = "offline";break; + case StatusShow::Online: iconString = "online";break; + case StatusShow::Away: iconString = "away";break; + case StatusShow::XA: iconString = "away";break; + case StatusShow::FFC: iconString = "online";break; + case StatusShow::DND: iconString = "dnd";break; + case StatusShow::None: iconString = "offline";break; } return QIcon(":/icons/" + iconString + ".png"); } 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/SConscript b/Swift/QtUI/SConscript index 64c3b15..1c3fd70 100644 --- a/Swift/QtUI/SConscript +++ b/Swift/QtUI/SConscript @@ -20,7 +20,12 @@ def generateDefaultTheme(dir) : Import("env") myenv = env.Clone() + +# Disable warnings that affect Qt myenv["CXXFLAGS"] = filter(lambda x : x != "-Wfloat-equal", myenv["CXXFLAGS"]) +if "clang" in env["CC"] : + myenv.Append(CXXFLAGS = ["-Wno-float-equal", "-Wno-shorten-64-to-32", "-Wno-missing-prototypes", "-Wno-unreachable-code", "-Wno-disabled-macro-expansion", "-Wno-unused-private-field", "-Wno-extra-semi", "-Wno-duplicate-enum", "-Wno-missing-variable-declarations", "-Wno-conversion", "-Wno-undefined-reinterpret-cast"]) + myenv.UseFlags(env["SWIFT_CONTROLLERS_FLAGS"]) myenv.UseFlags(env["SWIFTOOLS_FLAGS"]) if myenv["HAVE_XSS"] : @@ -31,7 +36,8 @@ if myenv["HAVE_SPARKLE"] : myenv.UseFlags(env["SPARKLE_FLAGS"]) myenv.UseFlags(env["SWIFTEN_FLAGS"]) myenv.UseFlags(env["SWIFTEN_DEP_FLAGS"]) -myenv.UseFlags(env["BREAKPAD_FLAGS"]) +if myenv.get("HAVE_BREAKPAD") : + myenv.UseFlags(env["BREAKPAD_FLAGS"]) if myenv.get("HAVE_GROWL", False) : myenv.UseFlags(myenv["GROWL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_GROWL"]) @@ -40,6 +46,9 @@ if myenv["swift_mobile"] : if myenv.get("HAVE_SNARL", False) : myenv.UseFlags(myenv["SNARL_FLAGS"]) myenv.Append(CPPDEFINES = ["HAVE_SNARL"]) +if myenv.get("HAVE_HUNSPELL", True): + myenv.Append(CPPDEFINES = ["HAVE_HUNSPELL"]) + myenv.UseFlags(myenv["HUNSPELL_FLAGS"]) if env["PLATFORM"] == "win32" : myenv.Append(LIBS = ["cryptui"]) myenv.UseFlags(myenv["PLATFORM_FLAGS"]) @@ -48,18 +57,23 @@ myenv.Tool("qt4", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("nsis", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("wix", toolpath = ["#/BuildTools/SCons/Tools"]) myenv.Tool("textfile", toolpath = ["#/BuildTools/SCons/Tools"]) -qt4modules = ['QtCore', 'QtGui', 'QtWebKit'] +qt4modules = ['QtCore', 'QtWebKit', 'QtGui'] +if myenv["qt5"] : + qt_version = '5' + qt4modules += ['QtWidgets', 'QtWebKitWidgets', 'QtMultimedia'] +else : + qt_version = '4' if env["PLATFORM"] == "posix" : qt4modules += ["QtDBus"] if env["PLATFORM"] != "win32" and env["PLATFORM"] != "darwin" : qt4modules += ["QtNetwork"] -myenv.EnableQt4Modules(qt4modules, debug = False) +myenv.EnableQt4Modules(qt4modules, debug = False, version = qt_version) myenv.Append(CPPPATH = ["."]) if env["PLATFORM"] == "win32" : - #myenv["LINKFLAGS"] = ["/SUBSYSTEM:CONSOLE"] + #myenv.Append(LINKFLAGS = ["/SUBSYSTEM:CONSOLE"]) myenv.Append(LINKFLAGS = ["/SUBSYSTEM:WINDOWS"]) myenv.Append(LIBS = "qtmain") if myenv.get("HAVE_SCHANNEL", 0) : @@ -73,6 +87,7 @@ myenv.WriteVal("DefaultTheme.qrc", myenv.Value(generateDefaultTheme(myenv.Dir("# sources = [ "main.cpp", "QtAboutWidget.cpp", + "QtSpellCheckerWindow.cpp", "QtAvatarWidget.cpp", "QtUIFactory.cpp", "QtChatWindowFactory.cpp", @@ -81,6 +96,7 @@ sources = [ "QtLoginWindow.cpp", "QtMainWindow.cpp", "QtProfileWindow.cpp", + "QtBlockListEditorWindow.cpp", "QtNameWidget.cpp", "QtSettingsProvider.cpp", "QtStatusWidget.cpp", @@ -107,6 +123,11 @@ sources = [ "QtEditBookmarkWindow.cpp", "QtContactEditWindow.cpp", "QtContactEditWidget.cpp", + "QtSingleWindow.cpp", + "QtHighlightEditorWidget.cpp", + "QtHighlightRulesItemModel.cpp", + "QtHighlightRuleWidget.cpp", + "QtColorToolButton.cpp", "ChatSnippet.cpp", "MessageSnippet.cpp", "SystemMessageSnippet.cpp", @@ -165,6 +186,34 @@ sources = [ "QtURLValidator.cpp" ] +# QtVCardWidget +sources.extend([ + "QtVCardWidget/QtCloseButton.cpp", + "QtVCardWidget/QtRemovableItemDelegate.cpp", + "QtVCardWidget/QtResizableLineEdit.cpp", + "QtVCardWidget/QtTagComboBox.cpp", + "QtVCardWidget/QtVCardHomeWork.cpp", + "QtVCardWidget/QtVCardAddressField.cpp", + "QtVCardWidget/QtVCardAddressLabelField.cpp", + "QtVCardWidget/QtVCardBirthdayField.cpp", + "QtVCardWidget/QtVCardDescriptionField.cpp", + "QtVCardWidget/QtVCardInternetEMailField.cpp", + "QtVCardWidget/QtVCardJIDField.cpp", + "QtVCardWidget/QtVCardOrganizationField.cpp", + "QtVCardWidget/QtVCardPhotoAndNameFields.cpp", + "QtVCardWidget/QtVCardRoleField.cpp", + "QtVCardWidget/QtVCardTelephoneField.cpp", + "QtVCardWidget/QtVCardTitleField.cpp", + "QtVCardWidget/QtVCardURLField.cpp", + "QtVCardWidget/QtVCardGeneralField.cpp", + "QtVCardWidget/QtVCardWidget.cpp" +]) + +myenv.Uic4("QtVCardWidget/QtVCardPhotoAndNameFields.ui") +myenv.Uic4("QtVCardWidget/QtVCardWidget.ui") +myenv.Uic4("QtProfileWindow.ui") + + # Determine the version myenv["SWIFT_VERSION"] = Version.getBuildVersion(env.Dir("#").abspath, "swift") if env["PLATFORM"] == "win32" : @@ -225,6 +274,10 @@ myenv.Uic4("QtAffiliationEditor.ui") myenv.Uic4("QtJoinMUCWindow.ui") myenv.Uic4("QtHistoryWindow.ui") myenv.Uic4("QtConnectionSettings.ui") +myenv.Uic4("QtHighlightRuleWidget.ui") +myenv.Uic4("QtHighlightEditorWidget.ui") +myenv.Uic4("QtBlockListEditorWindow.ui") +myenv.Uic4("QtSpellCheckerWindow.ui") myenv.Qrc("DefaultTheme.qrc") myenv.Qrc("Swift.qrc") @@ -306,7 +359,7 @@ if env.get("SWIFT_INSTALLDIR", "") : env.Install(os.path.join(env["SWIFT_INSTALLDIR"], "share", "swift", dir), resource) if env["PLATFORM"] == "win32" : - if env["DIST"] : + if env["DIST"] or ARGUMENTS.get("dump_trace") : commonResources[""] = commonResources.get("", []) + [ #os.path.join(env["OPENSSL_DIR"], "bin", "ssleay32.dll"), #os.path.join(env["OPENSSL_DIR"], "bin", "libeay32.dll"), @@ -314,13 +367,25 @@ if env["PLATFORM"] == "win32" : ] if env["SWIFTEN_DLL"] : commonResources[""] = commonResources.get("", []) + ["#/Swiften/${SWIFTEN_LIBRARY_FILE}"] - qtimageformats = ["gif", "ico", "jpeg", "mng", "svg", "tiff"] - qtlibs = ["QtCore4", "QtGui4", "QtNetwork4", "QtWebKit4", "QtXMLPatterns4", "phonon4"] + qtplugins = {} + qtplugins["imageformats"] = ["gif", "ico", "jpeg", "mng", "svg", "tiff"] + qtlibs = ["QtCore", "QtGui", "QtNetwork", "QtWebKit", "QtXMLPatterns"] + if qt_version == '4' : + qtlibs.append("phonon") + qtlibs = [lib + '4' for lib in qtlibs] + else : + qtlibs += ['QtQuick', 'QtQml', 'QtV8', 'QtMultimedia', 'QtSql', 'QtSensors', 'QtWidgets', 'QtWebKitWidgets', 'QtMultimediaWidgets', 'QtOpenGL', 'QtPrintSupport'] + qtlibs = [lib.replace('Qt', 'Qt5') for lib in qtlibs] + qtlibs += ['icuin51', 'icuuc51', 'icudt51', 'libGLESv2', 'libEGL'] + qtplugins["platforms"] = ['windows'] + qtplugins["accessible"] = ["taccessiblewidgets"] windowsBundleFiles = myenv.WindowsBundle("Swift", resources = commonResources, - qtimageformats = qtimageformats, - qtlibs = qtlibs) + qtplugins = qtplugins, + qtlibs = qtlibs, + qtversion = qt_version) + if env["DIST"] : #myenv.Append(NSIS_OPTIONS = [ # "/DmsvccRedistributableDir=\"" + env["vcredist"] + "\"", # "/DbuildVersion=" + myenv["SWIFT_VERSION"] @@ -344,7 +409,7 @@ if env["PLATFORM"] == "win32" : outfile.write('}') outfile.close() infile.close() - env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF) + copying = env.Command(["Swift/COPYING.rtf"], ["COPYING"], convertToRTF) wixvariables = { 'VCCRTFile': env["vcredist"], @@ -355,7 +420,7 @@ if env["PLATFORM"] == "win32" : wixincludecontent += "<?define %s = \"%s\" ?>" % (key, wixvariables[key]) wixincludecontent += "</Include>" myenv.WriteVal("..\\Packaging\\Wix\\variables.wxs", env.Value(wixincludecontent)) - myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles) + myenv.WiX_Heat('..\\Packaging\\WiX\\gen_files.wxs', windowsBundleFiles + copying) myenv.WiX_Candle('..\\Packaging\\WiX\\Swift.wixobj', '..\\Packaging\\WiX\\Swift.wxs') myenv.WiX_Candle('..\\Packaging\\WiX\\gen_files.wixobj', '..\\Packaging\\WiX\\gen_files.wxs') myenv.WiX_Light('#/Packages/Swift/Swift-' + myenv["SWIFT_VERSION"] + '.msi', ['..\\Packaging\\WiX\\gen_files.wixobj','..\\Packaging\\WiX\\Swift.wixobj']) diff --git a/Swift/QtUI/Swift.qrc b/Swift/QtUI/Swift.qrc index eb4f7ee..eeef80d 100644 --- a/Swift/QtUI/Swift.qrc +++ b/Swift/QtUI/Swift.qrc @@ -29,5 +29,18 @@ <file alias="icons/polygon.png">../resources/icons/polygon.png</file> <file alias="icons/cursor.png">../resources/icons/cursor.png</file> <file alias="icons/eraser.png">../resources/icons/eraser.png</file> + <file alias="emoticons/emoticons.txt">../resources/emoticons/emoticons.txt</file> + <file alias="emoticons/evilgrin.png">../resources/emoticons/evilgrin.png</file> + <file alias="emoticons/grin.png">../resources/emoticons/grin.png</file> + <file alias="emoticons/happy.png">../resources/emoticons/happy.png</file> + <file alias="emoticons/smile.png">../resources/emoticons/smile.png</file> + <file alias="emoticons/surprised.png">../resources/emoticons/surprised.png</file> + <file alias="emoticons/tongue.png">../resources/emoticons/tongue.png</file> + <file alias="emoticons/unhappy.png">../resources/emoticons/unhappy.png</file> + <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> + <file alias="icons/stop.png">../resources/icons/stop.png</file> </qresource> </RCC> diff --git a/Swift/QtUI/SystemMessageSnippet.cpp b/Swift/QtUI/SystemMessageSnippet.cpp index c78fe36..39349bc 100644 --- a/Swift/QtUI/SystemMessageSnippet.cpp +++ b/Swift/QtUI/SystemMessageSnippet.cpp @@ -10,12 +10,13 @@ namespace Swift { -SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme) : ChatSnippet(appendToPrevious) { +SystemMessageSnippet::SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme, Direction direction) : ChatSnippet(appendToPrevious) { if (appendToPrevious) { - setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(message, time, false, theme))); + setContinuationFallbackSnippet(boost::shared_ptr<ChatSnippet>(new SystemMessageSnippet(message, time, false, theme, direction))); } content_ = theme->getStatus(); + content_.replace("%direction%", directionToCSS(direction)); content_.replace("%message%", wrapResizable("<span class='swift_message'>" + escape(message) + "</span>")); content_.replace("%shortTime%", wrapResizable(escape(time.toString("h:mm")))); content_.replace("%time%", wrapResizable("<span class='swift_time'>" + timeToEscapedString(time) + "</span>")); diff --git a/Swift/QtUI/SystemMessageSnippet.h b/Swift/QtUI/SystemMessageSnippet.h index 69d231f..34b0d40 100644 --- a/Swift/QtUI/SystemMessageSnippet.h +++ b/Swift/QtUI/SystemMessageSnippet.h @@ -15,7 +15,7 @@ class QDateTime; namespace Swift { class SystemMessageSnippet : public ChatSnippet { public: - SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme); + SystemMessageSnippet(const QString& message, const QDateTime& time, bool appendToPrevious, QtChatTheme* theme, Direction direction); virtual ~SystemMessageSnippet(); const QString& getContent() const {return content_;} diff --git a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp index d69c626..02b238e 100644 --- a/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp +++ b/Swift/QtUI/UserSearch/QtUserSearchWindow.cpp @@ -29,7 +29,7 @@ namespace Swift { QtUserSearchWindow::QtUserSearchWindow(UIEventStream* eventStream, UserSearchWindow::Type type, const std::set<std::string>& groups) : eventStream_(eventStream), type_(type), model_(NULL) { setupUi(this); -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif QString title(type == UserSearchWindow::AddContact ? tr("Add Contact") : tr("Chat to User")); @@ -264,7 +264,11 @@ void QtUserSearchWindow::setResultsForm(Form::ref results) { resultsPage_->results_->setModel(newModel); resultsPage_->results_->setItemDelegate(new QItemDelegate()); resultsPage_->results_->setHeaderHidden(false); +#if QT_VERSION >= 0x050000 + resultsPage_->results_->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#else resultsPage_->results_->header()->setResizeMode(QHeaderView::ResizeToContents); +#endif delete model_; model_ = newModel; resultsPage_->setNoResults(model_->rowCount() == 0); diff --git a/Swift/QtUI/Whiteboard/ColorWidget.h b/Swift/QtUI/Whiteboard/ColorWidget.h index 6abdf00..ae1af0f 100644 --- a/Swift/QtUI/Whiteboard/ColorWidget.h +++ b/Swift/QtUI/Whiteboard/ColorWidget.h @@ -10,7 +10,7 @@ namespace Swift { class ColorWidget : public QWidget { - Q_OBJECT; + Q_OBJECT public: ColorWidget(QWidget* parent = 0); QSize sizeHint() const; diff --git a/Swift/QtUI/Whiteboard/FreehandLineItem.h b/Swift/QtUI/Whiteboard/FreehandLineItem.h index f3c6607..b1af3d1 100644 --- a/Swift/QtUI/Whiteboard/FreehandLineItem.h +++ b/Swift/QtUI/Whiteboard/FreehandLineItem.h @@ -10,8 +10,6 @@ #include <QPainter> #include <iostream> -using namespace std; - namespace Swift { class FreehandLineItem : public QGraphicsItem { public: diff --git a/Swift/QtUI/Whiteboard/GView.h b/Swift/QtUI/Whiteboard/GView.h index 88ea326..6a4fd2f 100644 --- a/Swift/QtUI/Whiteboard/GView.h +++ b/Swift/QtUI/Whiteboard/GView.h @@ -18,7 +18,7 @@ namespace Swift { class GView : public QGraphicsView { - Q_OBJECT; + Q_OBJECT public: enum Mode { Rubber, Line, Rect, Circle, HandLine, Text, Polygon, Select }; enum Type { New, Update, MoveUp, MoveDown }; diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp index 50d7f54..89de95e 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.cpp @@ -10,6 +10,7 @@ #include <boost/bind.hpp> #include <boost/smart_ptr/make_shared.hpp> +#include <boost/numeric/conversion/cast.hpp> #include <Swiften/Whiteboard/WhiteboardSession.h> #include <Swiften/Elements/WhiteboardPayload.h> @@ -25,7 +26,7 @@ namespace Swift { QtWhiteboardWindow::QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession) : QWidget() { -#ifndef Q_WS_MAC +#ifndef Q_OS_MAC setWindowIcon(QIcon(":/logo-icon-16.png")); #endif layout = new QVBoxLayout(this); @@ -264,7 +265,9 @@ namespace Swift { std::vector<std::pair<int, int> > points; QVector<QPointF>::const_iterator it = freehandLineItem->points().constBegin(); for ( ; it != freehandLineItem->points().constEnd(); ++it) { - points.push_back(std::pair<int, int>(it->x()+item->pos().x(), it->y()+item->pos().y())); + points.push_back(std::pair<int, int>( + boost::numeric_cast<int>(it->x()+item->pos().x()), + boost::numeric_cast<int>(it->y()+item->pos().y()))); } element->setColor(WhiteboardColor(color.red(), color.green(), color.blue(), color.alpha())); @@ -310,7 +313,9 @@ namespace Swift { std::vector<std::pair<int, int> > points; QVector<QPointF>::const_iterator it = polygon.begin(); for (; it != polygon.end(); ++it) { - points.push_back(std::pair<int, int>(it->x()+item->pos().x(), it->y()+item->pos().y())); + points.push_back(std::pair<int, int>( + boost::numeric_cast<int>(it->x()+item->pos().x()), + boost::numeric_cast<int>(it->y()+item->pos().y()))); } element->setPoints(points); @@ -328,10 +333,10 @@ namespace Swift { QGraphicsEllipseItem* ellipseItem = qgraphicsitem_cast<QGraphicsEllipseItem*>(item); if (ellipseItem) { QRectF rect = ellipseItem->rect(); - int cx = rect.x()+rect.width()/2 + item->pos().x(); - int cy = rect.y()+rect.height()/2 + item->pos().y(); - int rx = rect.width()/2; - int ry = rect.height()/2; + int cx = boost::numeric_cast<int>(rect.x()+rect.width()/2 + item->pos().x()); + int cy = boost::numeric_cast<int>(rect.y()+rect.height()/2 + item->pos().y()); + int rx = boost::numeric_cast<int>(rect.width()/2); + int ry = boost::numeric_cast<int>(rect.height()/2); WhiteboardEllipseElement::ref element = boost::make_shared<WhiteboardEllipseElement>(cx, cy, rx, ry); QColor penColor = ellipseItem->pen().color(); diff --git a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h index 4665ef0..3957bb7 100644 --- a/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h +++ b/Swift/QtUI/Whiteboard/QtWhiteboardWindow.h @@ -30,7 +30,7 @@ namespace Swift { class QtWhiteboardWindow : public QWidget, public WhiteboardWindow { - Q_OBJECT; + Q_OBJECT public: QtWhiteboardWindow(WhiteboardSession::ref whiteboardSession); void show(); diff --git a/Swift/QtUI/Whiteboard/TextDialog.h b/Swift/QtUI/Whiteboard/TextDialog.h index f4d9a13..64a0fe2 100644 --- a/Swift/QtUI/Whiteboard/TextDialog.h +++ b/Swift/QtUI/Whiteboard/TextDialog.h @@ -16,12 +16,10 @@ #include <iostream> -using namespace std; - namespace Swift { class TextDialog : public QDialog { - Q_OBJECT; + Q_OBJECT public: TextDialog(QGraphicsTextItem* item, QWidget* parent = 0); diff --git a/Swift/QtUI/WinUIHelpers.cpp b/Swift/QtUI/WinUIHelpers.cpp index 3942ac1..161ff1d 100644 --- a/Swift/QtUI/WinUIHelpers.cpp +++ b/Swift/QtUI/WinUIHelpers.cpp @@ -47,7 +47,7 @@ void WinUIHelpers::displayCertificateChainAsSheet(QWidget* parent, const std::ve CRYPTUI_VIEWCERTIFICATE_STRUCT viewDialogProperties = { 0 }; viewDialogProperties.dwSize = sizeof(viewDialogProperties); - viewDialogProperties.hwndParent = parent->winId(); + viewDialogProperties.hwndParent = (HWND) parent->winId(); viewDialogProperties.dwFlags = CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE | CRYPTUI_ENABLE_REVOCATION_CHECKING; viewDialogProperties.pCertContext = certificate_chain.get(); viewDialogProperties.cStores = 1; diff --git a/Swift/QtUI/main.cpp b/Swift/QtUI/main.cpp index d02cce6..d734713 100644 --- a/Swift/QtUI/main.cpp +++ b/Swift/QtUI/main.cpp @@ -21,6 +21,7 @@ #include <SwifTools/Application/PlatformApplicationPathProvider.h> #include <SwifTools/CrashReporter.h> #include <stdlib.h> +#include <Swiften/Base/Path.h> #include "QtSwift.h" #include "QtTranslator.h" @@ -33,7 +34,9 @@ int main(int argc, char* argv[]) { Swift::CrashReporter crashReporter(applicationPathProvider.getDataDir() / "crashes"); +#if QT_VERSION < 0x050000 QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); +#endif // Parse program options boost::program_options::options_description desc = Swift::QtSwift::getOptionsDescription(); @@ -62,21 +65,23 @@ int main(int argc, char* argv[]) { } // Translation +#if QT_VERSION < 0x050000 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); +#endif boost::filesystem::path someTranslationPath = applicationPathProvider.getResourcePath("/translations/swift_en.qm"); QTranslator qtTranslator; if (!someTranslationPath.empty()) { #if QT_VERSION >= 0x040800 if (vm.count("language") > 0) { - qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(vm["language"].as<std::string>()), someTranslationPath.parent_path().string().c_str()); + qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + P2QSTRING(vm["language"].as<std::string>()), P2QSTRING(Swift::pathToString(someTranslationPath.parent_path()))); } else { - qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", someTranslationPath.parent_path().string().c_str()); + qtTranslator.load(QLocale::system(), QString(SWIFT_APPLICATION_NAME).toLower(), "_", P2QSTRING(Swift::pathToString(someTranslationPath))); } #else //std::cout << "Loading " << std::string(QLocale::system().name().toUtf8()) << std::endl; - qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + QLocale::system().name(), someTranslationPath.parent_path().string().c_str()); + qtTranslator.load(QString(SWIFT_APPLICATION_NAME).toLower() + "_" + QLocale::system().name(), P2QSTRING(Swift::pathToString(someTranslationPath))); #endif } app.installTranslator(&qtTranslator); |