diff options
Diffstat (limited to 'src/gui/messageform.cpp')
-rw-r--r-- | src/gui/messageform.cpp | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/src/gui/messageform.cpp b/src/gui/messageform.cpp new file mode 100644 index 0000000..ad801be --- /dev/null +++ b/src/gui/messageform.cpp @@ -0,0 +1,657 @@ + + +/* + Copyright (C) 2005-2009 Michel de Boer <michel@twinklephone.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifdef HAVE_KDE +#include <kfiledialog.h> +#include <kiconloader.h> +#include <kmimetype.h> +#include <krun.h> +#include <kuserprofile.h> +#else +#include "utils/mime_database.h" +//Added by qt3to4: +#include <QKeyEvent> +#include <QLabel> +#include <QPixmap> +#include <Q3Frame> +#include <Q3PopupMenu> +#include <QCloseEvent> +#endif + +#include "gui.h" +#include "sockets/url.h" +#include <Q3StyleSheet> +#include "audits/memman.h" +#include "util.h" +#include <QPixmap> +#include <QCursor> +#include <Q3FileDialog> +#include "utils/file_utils.h" +#include <QFile> +#include "sendfileform.h" +#include <QStatusBar> +#include <Q3Frame> +#include "messageform.h" + + +using namespace utils; + +/** Maximum width for an inline image */ +#define MAX_WIDTH_IMG_INLINE 400 + +/** Maximum height for an inline image */ +#define MAX_HEIGHT_IMG_INLINE 400 + +#define IMG_SCALE_FACTOR(width, height) (std::min<float>( float(MAX_WIDTH_IMG_INLINE) / (width), float(MAX_HEIGHT_IMG_INLINE) / (height) ) ) + +MessageForm::MessageForm(QWidget* parent, const char* name, Qt::WindowFlags fl) + : Q3MainWindow(parent, name, fl) +{ + setupUi(this); + + (void)statusBar(); + init(); +} + +/* + * Destroys the object and frees any allocated resources + */ +MessageForm::~MessageForm() +{ + destroy(); + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void MessageForm::languageChange() +{ + retranslateUi(this); +} + + +void MessageForm::init() +{ + setWindowFlags(windowFlags() | Qt::WDestructiveClose); + + _getAddressForm = 0; + _remotePartyComplete = false; + + // Add label to display size of typed message + _msgSizeLabel = new QLabel(this); + statusBar()->addWidget(_msgSizeLabel); + showMessageSize(); + + // Set toolbutton icons for disabled options. + setDisabledIcon(addressToolButton, "kontact_contacts-disabled.png"); + + attachmentPopupMenu = new Q3PopupMenu(this); + MEMMAN_NEW(attachmentPopupMenu); + + connect(attachmentPopupMenu, SIGNAL(activated(int)), + this, SLOT(attachmentPopupActivated(int))); + + _serviceMap = NULL; + _saveAsDialog = NULL; + + _isComposingLabel = NULL; + + // When the user edits the message, the composition indication + // will be set to active. + connect(msgLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(setLocalComposingIndicationActive())); +} + +void MessageForm::destroy() +{ + if (_getAddressForm) { + MEMMAN_DELETE(_getAddressForm); + delete _getAddressForm; + } + + MEMMAN_DELETE(attachmentPopupMenu); + delete attachmentPopupMenu; + +#ifdef HAVE_KDE + vector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap; + if (serviceMap) { + MEMMAN_DELETE(serviceMap); + delete serviceMap; + } +#endif + + if (_saveAsDialog) { + MEMMAN_DELETE(_saveAsDialog); + delete _saveAsDialog; + } + + if (_isComposingLabel) { + MEMMAN_DELETE(_isComposingLabel); + delete _isComposingLabel; + } +} + +void MessageForm::closeEvent(QCloseEvent *e) +{ + MEMMAN_DELETE(this); // destructive close + Q3MainWindow::closeEvent(e); +} + + +void MessageForm::show() +{ + if (toLineEdit->text().isEmpty()) { + sendFileAction->setEnabled(false); + toLineEdit->setFocus(); + } else { + // Once a message session has been created, the + // source and destination cannot be changed anymore. + fromComboBox->setEnabled(false); + toLineEdit->setEnabled(false); + addressToolButton->setEnabled(false); + sendFileAction->setEnabled(true); + msgLineEdit->setFocus(); + } + + Q3MainWindow::show(); +} + +void MessageForm::selectUserConfig(t_user *user_config) +{ + for (int i = 0; i < fromComboBox->count(); i++) { + if (fromComboBox->text(i) == + user_config->get_profile_name().c_str()) + { + fromComboBox->setCurrentItem(i); + break; + } + } +} + +void MessageForm::showAddressBook() +{ + if (!_getAddressForm) { + _getAddressForm = new GetAddressForm( + this, "select address", true); + MEMMAN_NEW(_getAddressForm); + } + + connect(_getAddressForm, + SIGNAL(address(const QString &)), + this, SLOT(selectedAddress(const QString &))); + + _getAddressForm->show(); +} + +void MessageForm::selectedAddress(const QString &address) +{ + toLineEdit->setText(address); +} + +/** + * Check if there is a valid sender and receiver. If so, then set the sender + * and receiver addresses in the message session object. + */ +bool MessageForm::updateMessageSession() +{ + string display, dest_str; + t_user *from_user = phone->ref_user_profile( + fromComboBox->currentText().ascii()); + if (!from_user) { + // The user profile is not active anymore + fromComboBox->setFocus(); + return false; + } + + ui->expand_destination(from_user, toLineEdit->text().stripWhiteSpace().ascii(), + display, dest_str); + t_url dest(dest_str); + + if (!dest.is_valid()) { + toLineEdit->setFocus(); + toLineEdit->selectAll(); + return false; + } + + _msgSession->set_user(from_user); + _msgSession->set_remote_party(t_display_url(dest, display)); + + setRemotePartyCaption(); + + return true; +} + +/** + * Determine if a message can be sent, i.e. there is a valid sender and + * receiver. If a message can be sent, then lock the sender and receiver. + * @return True if message can be sent, false otherwise. + */ +bool MessageForm::prepareSendMessage() { + if (toLineEdit->text().isEmpty()) { + // No recipient selected. + return false; + } + + if (toLineEdit->isEnabled()) { + if (!updateMessageSession()) { + // No valid sender and receiver. + return false; + } + + // Once a message session has been created, the + // source and destination cannot be changed anymore. + fromComboBox->setEnabled(false); + toLineEdit->setEnabled(false); + addressToolButton->setEnabled(false); + + } + + return true; +} + +/** Send a text message */ +void MessageForm::sendMessage() { + if (msgLineEdit->text().isEmpty()) { + // Nothing to send + return; + } + + if (!prepareSendMessage()) { + return; + } + + _msgSession->send_msg(msgLineEdit->text().ascii(), im::TXT_PLAIN); +} + +/** + * Send a file. + * @param filename [in] Name of file to send. + * @param subject [in] Subject to set in the message. + */ +void MessageForm::sendFile(const QString &filename, const QString &subject) { + if (!prepareSendMessage()) { + return; + } + + t_media media("application/octet-stream"); + +#ifdef HAVE_KDE + // Get mime type for the attachment + KMimeType::Ptr pMime = KMimeType::findByURL(filename); + media = t_media(pMime->name().ascii()); +#else + string mime_type = mime_database->get_mimetype(filename.ascii()); + if (!mime_type.empty()) { + media = t_media(mime_type); + } +#endif + + _msgSession->send_file(filename.ascii(), media, subject.ascii()); +} + +/** + * Add a message to the converstation broswer. + * @param msg [in] The message to add. + * @param name [in] The name of the sender of the message. + */ +void MessageForm::addMessage(const im::t_msg &msg, const QString &name) +{ + QString s = "<b>"; + + // Timestamp and name of sender + if (msg.direction == im::MSG_DIR_IN) s += "<font color=\"blue\">"; + s += time2str(msg.timestamp, "%H:%M:%S ").c_str(); + s += Q3StyleSheet::escape(name); + if (msg.direction == im::MSG_DIR_IN) s += "</font>"; + s += "</b>"; + + // Subject + if (!msg.subject.empty()) { + s += "<br>"; + s += "<b>"; + s += "Subject: "; + s += "</b>"; + s += msg.subject.c_str(); + } + + // Text message + if (!msg.message.empty()) { + s += "<br>"; + if (msg.format == im::TXT_HTML) { + s += msg.message.c_str(); + } else { + s += Q3StyleSheet::escape(msg.message.c_str()); + } + } + + // Attachment + if (msg.has_attachment) { + s += "<br>"; + s += "<a href=\""; + s += msg.attachment_filename.c_str(); + s += "\">"; + + bool show_attachment_inline = false; + bool scale_image = false; + int scaled_width = 0, scaled_height = 0; + + if (msg.attachment_media.type == "image") { + // Show image inline if possible + QPixmap image (msg.attachment_filename.c_str()); + if (!image.isNull()) { + show_attachment_inline = true; + if (image.width() > MAX_WIDTH_IMG_INLINE && + image.height() > MAX_HEIGHT_IMG_INLINE) + { + // Shrink image + scaled_width = int(image.width() * IMG_SCALE_FACTOR(image.width(), image.height())); + scaled_height = int(image.height() * IMG_SCALE_FACTOR(image.width(), image.height())); + scale_image = true; + } + } + } + + s += "<img "; + + if (scale_image) { + s += "width="; + s += QString().setNum(scaled_width); + s += " height="; + s += QString().setNum(scaled_height); + s += " "; + } + + s += "src=\""; + + if (show_attachment_inline) { + s += msg.attachment_filename.c_str(); + } else { + // Show an icon representing the attachment +#ifdef HAVE_KDE + KIconLoader iconLoader; + QString iconName = KMimeType::iconForURL( + msg.attachment_filename.c_str()); + s += iconLoader.iconPath(iconName, KIcon::Desktop); +#else + // Set icon based on main mime type + s += "mime_"; + s += msg.attachment_media.type.c_str(); + s += ".png"; +#endif + } + + s += "\">"; + s+= "<br>"; + + s += msg.attachment_save_as_name.c_str(); + + s += "</a> "; + + if (scale_image) { + s += "<br>"; + s += "<i>"; + s += tr("image size is scaled down in preview"); + s += "</i>"; + } + + // Store the association between the tempory file name of the + // save attachment and the suggested save-as file name. When + // the user clicks on the attachment, only the temporary file name + // is available. Through this association the suggested file name + // can be retrieved. + _filenameMap[msg.attachment_filename] = msg.attachment_save_as_name; + } + + conversationBrowser->append(s); +} + +void MessageForm::displayError(const QString &errorMsg) +{ + QString s = "<font color =\"red\">"; + s += "<b>"; + s += tr("Delivery failure").ascii(); + s += ": </b>"; + s += Q3StyleSheet::escape(errorMsg); + s += "</font>"; + + conversationBrowser->append(s); +} + +void MessageForm::displayDeliveryNotification(const QString ¬ification) +{ + QString s = "<font color =\"darkgreen\">"; + s += "<b>"; + s += tr("Delivery notification").ascii(); + s += ": </b>"; + s += Q3StyleSheet::escape(notification); + s += "</font>"; + + conversationBrowser->append(s); +} + +void MessageForm::setRemotePartyCaption(void) { + if (!_msgSession) return; + t_user *user = _msgSession->get_user(); + t_display_url remote_party = _msgSession->get_remote_party(); + setCaption(ui->format_sip_address(user, + remote_party.display, remote_party.url).c_str()); +} + +void MessageForm::showAttachmentPopupMenu(const QString &attachment) { +#ifdef HAVE_KDE + vector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap; + + if (serviceMap) { + MEMMAN_DELETE(serviceMap); + delete serviceMap; + } + serviceMap = new vector<KService::Ptr>; + MEMMAN_NEW(serviceMap); + _serviceMap = (void *)serviceMap; +#endif + + int id = 0; // Identity of popup menu item + + // Store attachment. When the user selects an attachment we still + // know which attachment was clicked. + clickedAttachment = attachment; + + attachmentPopupMenu->clear(); + + QIcon saveIcon(QPixmap(":/icons/images/save_as.png")); + attachmentPopupMenu->insertItem(saveIcon, "Save as...", id++); + +#ifdef HAVE_KDE + // Get mime type for the attachment + KMimeType::Ptr pMime = KMimeType::findByURL(attachment); + + // Get applications that can open the mime type + KServiceTypeProfile::OfferList services = KServiceTypeProfile::offers( + pMime->name(), "Application"); + + KServiceTypeProfile::OfferList::ConstIterator it; + for (it = services.begin(); it != services.end(); ++it) { + KService::Ptr service = (*it).service(); + serviceMap->push_back(service); + QString menuText = tr("Open with %1...").arg(service->name()); + QPixmap pixmap = service->pixmap(KIcon::Small); + QIcon iconSet; + iconSet.setPixmap(pixmap, QIcon::Small); + attachmentPopupMenu->insertItem(iconSet, menuText, id++); + } + + QIcon openIcon(QPixmap(":/icons/images/fileopen.png")); + attachmentPopupMenu->insertItem(openIcon, tr("Open with..."), id++); +#endif + + attachmentPopupMenu->popup(QCursor::pos()); +} + +void MessageForm::attachmentPopupActivated(int id) { +#ifdef HAVE_KDE + vector<KService::Ptr> *serviceMap = (vector<KService::Ptr> *)_serviceMap; + assert(serviceMap); +#endif + + if (id == 0) { +#ifdef HAVE_KDE + KFileDialog *d = new KFileDialog(QString::null, QString::null, this, 0, true); + MEMMAN_NEW(d); + d->setOperationMode(KFileDialog::Saving); + + connect(d, SIGNAL(okClicked()), this, + SLOT(saveAttachment())); +#else + Q3FileDialog *d = new Q3FileDialog(QString::null, QString::null, this, 0, true); + MEMMAN_NEW(d); + d->setMode(Q3FileDialog::AnyFile); + + connect(d, SIGNAL(fileSelected(const QString &)), this, + SLOT(saveAttachment())); +#endif + d->setSelection(_filenameMap[clickedAttachment.ascii()].c_str()); + d->setCaption(tr("Save attachment as...")); + + if (_saveAsDialog) { + MEMMAN_DELETE(_saveAsDialog); + delete _saveAsDialog; + } + _saveAsDialog = d; + + d->show(); +#ifdef HAVE_KDE + } else if (id > serviceMap->size()) { + KURL::List urls; + urls << clickedAttachment; + KRun::displayOpenWithDialog(urls, false); + } else { + KURL::List urls; + urls << clickedAttachment; + KRun::run(*serviceMap->at(id-1), urls); +#endif + } +} + +void MessageForm::saveAttachment() { +#ifdef HAVE_KDE + KFileDialog *d = dynamic_cast<KFileDialog *>(_saveAsDialog); +#else + Q3FileDialog *d = dynamic_cast<Q3FileDialog *>(_saveAsDialog); +#endif + QString filename = d->selectedFile(); + + if (QFile::exists(filename)) { + bool overwrite = ((t_gui *)ui)->cb_ask_msg(this, + tr("File already exists. Do you want to overwrite this file?").ascii(), + MSG_WARNING); + + if (!overwrite) return; + } + + if (!filecopy(clickedAttachment.ascii(), filename.ascii())) { + ((t_gui *)ui)->cb_show_msg(this, tr("Failed to save attachment.").ascii(), + MSG_CRITICAL); + } +} + +/** Choose a file to send */ +void MessageForm::chooseFileToSend() +{ + // Indicate that a message is being composed. + setLocalComposingIndicationActive(); + + SendFileForm *form = new SendFileForm(); + MEMMAN_NEW(form); + // Form will auto destruct itself on close. + + connect(form, SIGNAL(selected(const QString &, const QString &)), + this, SLOT(sendFile(const QString &, const QString &))); + + form->show(); +} + +/** + * Show an is-composing indication in the status bar. + * @param name [in] The name of the sender of the message. + */ +void MessageForm::setComposingIndication(const QString &name) +{ + + if (!_isComposingLabel) { + _isComposingLabel = new QLabel(NULL); + MEMMAN_NEW(_isComposingLabel); + + _isComposingLabel->setText(tr("%1 is typing a message.").arg(name)); + _isComposingLabel->setFrameStyle(Q3Frame::NoFrame | Q3Frame::Plain); + statusBar()->addWidget(_isComposingLabel); + } +} + +/** Clear an is-composing indication from the status bar. */ +void MessageForm::clearComposingIndication() +{ + if (_isComposingLabel) { + statusBar()->removeWidget(_isComposingLabel); + statusBar()->clear(); + + MEMMAN_DELETE(_isComposingLabel); + delete _isComposingLabel; + _isComposingLabel = NULL; + } +} + +/** Set the local composition indication to active. */ +void MessageForm::setLocalComposingIndicationActive() +{ + _msgSession->set_local_composing_state(im::COMPOSING_STATE_ACTIVE); +} + +void MessageForm::keyPressEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (sendPushButton->isEnabled()) { + sendPushButton->animateClick(); + } + break; + default: + e->ignore(); + } +} + +void MessageForm::toAddressChanged(const QString &address) +{ + sendFileAction->setEnabled(!address.isEmpty()); +} + +/** Show the size of the typed message */ +void MessageForm::showMessageSize() +{ + uint len = msgLineEdit->text().length(); + + QString s(tr("Size")); + s += ": "; + s += QString().setNum(len); + + _msgSizeLabel->setText(s); +} |