Show webfinger if displayName is unknown for an author.
[larjonas-pumpa.git] / src / messagewindow.cpp
blob93d7ff17a536371df817216a5f2514d9754b6f1a
1 /*
2 Copyright 2013-2015 Mats Sjöberg
4 This file is part of the Pumpa programme.
6 Pumpa is free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Pumpa is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pumpa. If not, see <http://www.gnu.org/licenses/>.
20 #include "messagewindow.h"
21 #include "pumpa_defines.h"
22 #include "pumpasettings.h"
23 #include "util.h"
25 #include <QFileDialog>
26 #include <QMessageBox>
27 #include <QInputDialog>
29 //------------------------------------------------------------------------------
31 const int max_picture_size = 160;
33 //------------------------------------------------------------------------------
35 PictureLabel::PictureLabel(QWidget* parent) : QLabel(parent) {}
37 //------------------------------------------------------------------------------
39 void PictureLabel::setOriginalPixmap(QPixmap p) {
40 m_originalPixmap = p;
41 setPixmap(p);
44 //------------------------------------------------------------------------------
46 void PictureLabel::resizeEvent(QResizeEvent* event) {
47 if (m_originalPixmap.isNull())
48 return;
50 QSize size = event->size();
51 setPixmap(m_originalPixmap.scaled(size, Qt::KeepAspectRatio));
54 //------------------------------------------------------------------------------
56 MessageWindow::MessageWindow(PumpaSettings* s, const RecipientList* rl,
57 QWidget* parent) :
58 QDialog(parent),
59 m_addressLayout(NULL),
60 m_editing(false),
61 m_isReply(false),
62 m_obj(NULL),
63 m_s(s),
64 m_rl(rl)
66 setMinimumSize(QSize(400,400));
67 setWindowTitle(CLIENT_FANCY_NAME);
69 m_infoLabel = new QLabel(this);
71 m_markdownCheckBox = new QCheckBox(tr("Use Markdown"), this);
72 connect(m_markdownCheckBox, SIGNAL(stateChanged(int)),
73 this, SLOT(onMarkdownChecked(int)));
75 m_markupLabel = new QLabel(this);
76 m_markupLabel->setText(QString("<a href=\"%2\">" + tr("[help]") + "</a>").
77 arg(MARKUP_DOC_URL));
78 m_markupLabel->setOpenExternalLinks(true);
79 m_markupLabel->setTextInteractionFlags(Qt::TextSelectableByMouse |
80 Qt::LinksAccessibleByMouse);
82 m_infoLayout = new QHBoxLayout;
83 m_infoLayout->addWidget(m_infoLabel);
84 m_infoLayout->addStretch();
85 m_infoLayout->addWidget(m_markdownCheckBox);
86 m_infoLayout->addWidget(m_markupLabel);
88 m_toRecipients = new MessageRecipients(this);
89 m_ccRecipients = new MessageRecipients(this);
91 m_toLabel = new QLabel(tr("To:"), this);
92 m_ccLabel = new QLabel(tr("Cc:"), this);
94 m_addressLayout = new QFormLayout;
95 m_addressLayout->addRow(m_toLabel, m_toRecipients);
96 m_addressLayout->addRow(m_ccLabel, m_ccRecipients);
97 m_addressLayout->setContentsMargins(0, 0, 0, 0);
99 m_addPictureButton = new TextToolButton(this);
100 connect(m_addPictureButton, SIGNAL(clicked()), this, SLOT(onAddPicture()));
102 m_removePictureButton = new TextToolButton(tr("&Remove picture"), this);
103 connect(m_removePictureButton, SIGNAL(clicked()),
104 this, SLOT(onRemovePicture()));
106 m_addToButton = new TextToolButton(tr("+ &To"), this);
107 connect(m_addToButton, SIGNAL(clicked()), this, SLOT(onAddTo()));
109 m_addCcButton = new TextToolButton(tr("+ &Cc"), this);
110 connect(m_addCcButton, SIGNAL(clicked()), this, SLOT(onAddCc()));
112 m_pictureButtonLayout = new QHBoxLayout;
113 m_pictureButtonLayout->addWidget(m_addPictureButton, 0, Qt::AlignTop);
114 m_pictureButtonLayout->addWidget(m_removePictureButton, 0, Qt::AlignTop);
115 m_pictureButtonLayout->addStretch();
116 m_pictureButtonLayout->addWidget(m_addToButton, 0, Qt::AlignTop);
117 m_pictureButtonLayout->addWidget(m_addCcButton, 0, Qt::AlignTop);
119 m_pictureLabel = new PictureLabel(this);
120 // m_pictureLabel->setScaledContents(true);
121 m_pictureLabel->setMaximumSize(max_picture_size, max_picture_size);
122 m_pictureLabel->setFocusPolicy(Qt::NoFocus);
123 m_pictureLabel->setFrameStyle(QFrame::Panel | QFrame::Sunken);
125 m_title = new QLineEdit(this);
126 #if QT_VERSION >= 0x040700
127 m_title->setPlaceholderText(tr("Title (optional)"));
128 #endif
129 connect(m_title, SIGNAL(textChanged(const QString&)),
130 this, SLOT(updatePreview()));
132 m_textEdit = new MessageEdit(m_s, this);
133 connect(m_textEdit, SIGNAL(ready()), this, SLOT(accept()));
134 connect(m_textEdit, SIGNAL(textChanged()), this, SLOT(updatePreview()));
135 connect(m_textEdit, SIGNAL(addRecipient(QASActor*)),
136 this, SLOT(onAddRecipient(QASActor*)));
138 m_previewLabel = new RichTextLabel;
139 m_previewLabel->setSizePolicy(QSizePolicy::Ignored,
140 QSizePolicy::Maximum);
141 m_previewArea = new QScrollArea(this);
142 m_previewArea->setWidget(m_previewLabel);
144 m_previewArea->setWidgetResizable(true);
146 m_charCountLabel = new QLabel(this);
148 QWidget* qw = new QWidget(this);
149 QVBoxLayout* qwLayout = new QVBoxLayout;
150 qwLayout->addWidget(m_textEdit);
151 qwLayout->addWidget(m_charCountLabel);
152 qwLayout->setMargin(0);
153 qw->setLayout(qwLayout);
155 m_splitter = new QSplitter(Qt::Vertical, this);
156 m_splitter->addWidget(qw);
157 m_splitter->addWidget(m_previewArea);
158 m_splitter->setChildrenCollapsible(false);
160 m_layout = new QVBoxLayout;
161 m_layout->addLayout(m_infoLayout);
162 m_layout->addLayout(m_addressLayout);
163 m_layout->addLayout(m_pictureButtonLayout);
164 m_layout->addWidget(m_pictureLabel, 0, Qt::AlignHCenter);
165 m_layout->addWidget(m_title);
166 m_layout->addWidget(m_splitter);
168 m_cancelButton = new QPushButton(tr("Ca&ncel"));
169 connect(m_cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
171 m_previewButton = new QPushButton(tr("&Preview"));
172 connect(m_previewButton, SIGNAL(clicked()), this, SLOT(togglePreview()));
174 m_sendButton = new QPushButton(tr("&Send message"));
175 connect(m_sendButton, SIGNAL(clicked()), this, SLOT(accept()));
176 m_sendButton->setDefault(true);
178 m_buttonLayout = new QHBoxLayout;
179 m_buttonLayout->addWidget(m_cancelButton);
180 m_buttonLayout->addWidget(m_previewButton);
181 m_buttonLayout->addWidget(m_sendButton);
182 m_layout->addLayout(m_buttonLayout);
184 setLayout(m_layout);
186 m_textEdit->setFocus(Qt::OtherFocusReason);
188 QTextCursor cursor = m_textEdit->textCursor();
189 cursor.movePosition(QTextCursor::End);
190 m_textEdit->setTextCursor(cursor);
193 //------------------------------------------------------------------------------
195 void MessageWindow::onMarkdownChecked(int state) {
196 if (!m_editing) {
197 if (state == Qt::Unchecked) {
198 m_s->useMarkdown(false);
199 } else {
200 m_s->useMarkdown(true);
203 updatePreview();
206 //------------------------------------------------------------------------------
208 void MessageWindow::setCompletions(const MessageEdit::completion_t*
209 completions) {
210 if (m_textEdit)
211 m_textEdit->setCompletions(completions);
214 //------------------------------------------------------------------------------
216 void MessageWindow::addToRecipientList(QString name, QASObject* obj) {
217 m_recipientList.append(name);
218 if (!m_recipientSelection.contains(name))
219 m_recipientSelection.insert(name, obj);
222 //------------------------------------------------------------------------------
224 void MessageWindow::addRecipientWindow(MessageRecipients* mr, QString title) {
225 QInputDialog* dialog = new QInputDialog(this);
226 // dialog->setOption(QInputDialog::UseListViewForComboBoxItems);
227 dialog->setComboBoxItems(m_recipientList);
228 dialog->setComboBoxEditable(true);
229 dialog->setLabelText(title);
230 dialog->setInputMode(QInputDialog::TextInput);
231 dialog->setWindowTitle(CLIENT_FANCY_NAME);
233 dialog->exec();
235 if (dialog->result() == QDialog::Accepted) {
236 QString text = dialog->textValue();
237 if (m_recipientSelection.contains(text))
238 mr->addRecipient(m_recipientSelection[text]);
242 //------------------------------------------------------------------------------
244 void MessageWindow::onAddTo() {
245 addRecipientWindow(m_toRecipients, tr("Select recipient (To)"));
248 //------------------------------------------------------------------------------
250 void MessageWindow::onAddCc() {
251 addRecipientWindow(m_ccRecipients, tr("Select recipient (Cc)"));
254 //------------------------------------------------------------------------------
256 void MessageWindow::onAddRecipient(QASActor* actor) {
257 m_toRecipients->addRecipient(actor);
260 //------------------------------------------------------------------------------
262 void MessageWindow::initWindow(QString title, QString buttonText,
263 bool showRecipients) {
264 setWindowTitle(QString(CLIENT_FANCY_NAME) + " - " + title);
265 m_infoLabel->setText(title);
267 m_sendButton->setText(buttonText);
269 m_markdownCheckBox->setChecked(m_s->useMarkdown() || m_editing);
271 m_previewArea->setVisible(m_s->showPreview());
273 m_toRecipients->setVisible(showRecipients);
274 m_ccRecipients->setVisible(showRecipients);
275 m_toLabel->setVisible(showRecipients);
276 m_ccLabel->setVisible(showRecipients);
277 m_addToButton->setVisible(showRecipients);
278 m_addCcButton->setVisible(showRecipients);
280 m_charCountLabel->setVisible(m_s->showCharCount());
282 updatePreview(true);
283 updateAddPicture();
286 //------------------------------------------------------------------------------
288 void MessageWindow::newMessage(QASObject* obj, QASObjectList* to,
289 QASObjectList* cc) {
290 if (m_editing)
291 clear();
293 m_editing = false;
295 QASObject* origObj = obj;
296 if (obj && !m_s->commentOnComments() && obj->inReplyTo())
297 obj = obj->inReplyTo();
299 m_isReply = (obj != NULL);
300 m_obj = obj;
302 if (m_isReply)
303 m_title->clear();
305 QString title = m_isReply ? tr("Post a reply") : tr("Post a note");
306 QString buttonText = m_isReply ? tr("&Send comment") : tr("&Send post");
308 initWindow(title, buttonText, true);
310 m_recipientList.clear();
311 for (int i=0; i<m_rl->size(); ++i) {
312 QASObject* obj = m_rl->at(i);
313 addToRecipientList(obj->displayName() + " (list)", obj);
316 m_markdownCheckBox->setEnabled(true);
318 const MessageEdit::completion_t* completions = m_textEdit->getCompletions();
319 MessageEdit::completion_t::const_iterator it = completions->constBegin();
320 for (; it != completions->constEnd(); ++it)
321 addToRecipientList(it.key(), it.value());
323 if (!m_isReply) {
324 // A new post, use default recipients
325 setDefaultRecipients(m_toRecipients, m_s->defaultToAddress());
326 setDefaultRecipients(m_ccRecipients, m_s->defaultCcAddress());
327 } else {
328 // For a reply, copy recipients from parent
329 copyRecipients(m_toRecipients, to);
330 copyRecipients(m_ccRecipients, cc);
332 // add original post author to recipients
333 if (obj->author() && !obj->author()->isYou())
334 m_toRecipients->addRecipient(obj->author());
336 // if this is a reply to a comment add comment author to
337 // recipients as well
338 if (origObj != obj && origObj->author() && !origObj->author()->isYou())
339 m_toRecipients->addRecipient(origObj->author());
343 //------------------------------------------------------------------------------
345 void MessageWindow::editMessage(QASObject* obj) {
346 m_editing = true;
347 m_obj = obj;
349 QString type = obj->type();
350 m_isReply = type == "comment";
352 QString title = tr("Edit object");
353 // we do this for the sake of easier translation
354 if (type == "note")
355 title = tr("Edit post");
356 else if (type == "comment")
357 title = tr("Edit comment");
358 else if (type == "image")
359 title = tr("Edit image post");
361 QString buttonText = tr("&Update object");
362 // we do this for the sake of easier translation
363 if (type == "note")
364 buttonText = tr("&Update post");
365 else if (type == "comment")
366 buttonText = tr("&Update comment");
367 else if (type == "image")
368 buttonText = tr("&Update image post");
370 m_markdownCheckBox->setEnabled(false);
372 m_textEdit->setPlainText(obj->content());
373 m_title->setText(obj->displayName());
375 initWindow(title, buttonText, false);
378 //------------------------------------------------------------------------------
380 void MessageWindow::copyRecipients(MessageRecipients* mr, QASObjectList* ol) {
381 mr->clear();
382 if (ol) {
383 RecipientList rl = ol->toRecipientList();
384 for (int i=0; i<rl.size(); ++i) {
385 QASActor* actor = rl[i]->asActor();
386 bool isYou = actor && actor->isYou();
388 if (!isYou && !rl[i]->type().isEmpty())
389 mr->addRecipient(rl[i]);
394 //------------------------------------------------------------------------------
396 void MessageWindow::setDefaultRecipients(MessageRecipients* mr,
397 int defAddress) {
398 mr->clear();
399 if (defAddress == RECIPIENT_PUBLIC)
400 mr->addRecipient(m_rl->at(0));
401 if (defAddress == RECIPIENT_FOLLOWERS)
402 mr->addRecipient(m_rl->at(1));
405 //------------------------------------------------------------------------------
407 void MessageWindow::clear() {
408 m_imageFileName = "";
409 m_textEdit->clear();
410 m_title->clear();
411 m_previewLabel->setText("");
414 //------------------------------------------------------------------------------
416 void MessageWindow::showEvent(QShowEvent*) {
417 m_textEdit->setFocus(Qt::OtherFocusReason);
418 m_textEdit->selectAll();
419 activateWindow();
422 //------------------------------------------------------------------------------
424 void MessageWindow::accept() {
425 m_textEdit->hideCompletion();
426 QString msg = m_textEdit->toPlainText();
428 RecipientList to = m_toRecipients->recipients();
429 RecipientList cc = m_ccRecipients->recipients();
431 QString title = m_title->text();
433 if (m_editing) {
434 emit sendEdit(m_obj, msg, title);
435 } else if (m_isReply) {
436 emit sendReply(m_obj, msg, to, cc);
437 } else {
438 if (m_imageFileName.isEmpty()) {
439 emit sendMessage(msg, title, to, cc);
440 } else {
441 emit sendImage(msg, title, m_imageFileName, to, cc);
444 QDialog::accept();
447 //------------------------------------------------------------------------------
449 void MessageWindow::onAddPicture() {
450 QString fileName =
451 QFileDialog::getOpenFileName(this, tr("Select Image"), "",
452 tr("Image files (*.png *.jpg *.jpeg *.gif)"
453 ";;All files (*.*)"));
455 if (!fileName.isEmpty()) {
456 m_imageFileName = fileName;
457 updateAddPicture();
461 //------------------------------------------------------------------------------
463 void MessageWindow::onRemovePicture() {
464 m_imageFileName = "";
465 updateAddPicture();
468 //------------------------------------------------------------------------------
470 void MessageWindow::updateAddPicture() {
471 if (m_isReply || m_editing) {
472 m_addPictureButton->setVisible(false);
473 m_removePictureButton->setVisible(false);
474 m_pictureLabel->setVisible(false);
475 m_title->setVisible(!m_isReply);
476 m_removePictureButton->setVisible(false);
477 return;
480 QPixmap p;
481 if (!m_imageFileName.isEmpty()) {
482 p.load(m_imageFileName);
483 if (p.isNull()) {
484 QMessageBox::critical(this, tr("Sorry!"),
485 tr("That file didn't appear to be an image."));
486 m_imageFileName = "";
490 m_addPictureButton->setVisible(true);
491 m_title->setVisible(true);
492 if (m_imageFileName.isEmpty()) {
493 m_addPictureButton->setText(tr("&Add picture"));
494 m_removePictureButton->setVisible(false);
495 m_pictureLabel->setVisible(false);
496 } else {
497 QPixmap scaledPixmap = p.scaled(max_picture_size,
498 max_picture_size,
499 Qt::KeepAspectRatio);
500 m_pictureLabel->setOriginalPixmap(scaledPixmap);
501 m_addPictureButton->setText(tr("&Change picture"));
502 m_removePictureButton->setVisible(true);
503 m_pictureLabel->setVisible(true);
507 //------------------------------------------------------------------------------
509 void MessageWindow::updatePreview(bool force) {
510 QString previewText = addTextMarkup(m_textEdit->toPlainText(),
511 m_s->useMarkdown() || m_editing);
512 if (m_charCountLabel->isVisible() || force) {
513 QString strippedText = previewText;
514 strippedText.remove(QRegExp(HTML_TAG_REGEX));
515 #ifdef DEBUG_MARKUP
516 qDebug() << "STRIPPEDTEXT" << strippedText;
517 #endif
518 QString ccText = QString(tr("Characters: %1")).arg(strippedText.count());
519 m_charCountLabel->setText(ccText);
522 if (m_previewArea->isVisible() || force) {
523 QString titleText = processTitle(m_title->text(), true);
524 if (!titleText.isEmpty())
525 previewText = "<p><b>" + titleText + "</b></p>" + previewText;
526 m_previewLabel->setText(previewText);
530 //------------------------------------------------------------------------------
532 void MessageWindow::togglePreview() {
533 bool visible = !m_previewArea->isVisible();
534 m_previewArea->setVisible(visible);
535 m_s->showPreview(visible);
536 updatePreview();
537 m_textEdit->setFocus(Qt::OtherFocusReason);