File indexing completed on 2024-04-28 04:55:40

0001 /*
0002     This file is part of Choqok, the KDE micro-blogging client
0003 
0004     SPDX-FileCopyrightText: 2008-2012 Mehrdad Momeny <mehrdad.momeny@gmail.com>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007 */
0008 
0009 #include "postwidget.h"
0010 
0011 #include <QCloseEvent>
0012 #include <QGridLayout>
0013 #include <QTimer>
0014 #include <QPushButton>
0015 
0016 #include <KLocalizedString>
0017 #include <KMessageBox>
0018 
0019 #include "choqokappearancesettings.h"
0020 #include "choqokbehaviorsettings.h"
0021 #include "choqoktools.h"
0022 #include "choqokuiglobal.h"
0023 #include "libchoqokdebug.h"
0024 #include "mediamanager.h"
0025 #include "quickpost.h"
0026 #include "timelinewidget.h"
0027 #include "textbrowser.h"
0028 #include "urlutils.h"
0029 
0030 static const int _15SECS = 15000;
0031 static const int _MINUTE = 60000;
0032 static const int _HOUR = 60 * _MINUTE;
0033 
0034 using namespace Choqok;
0035 using namespace Choqok::UI;
0036 
0037 class PostWidget::Private
0038 {
0039 public:
0040     Private(Account *account, Choqok::Post *post)
0041         : mCurrentPost(post), mCurrentAccount(account), dir(QLatin1String("ltr")), timeline(nullptr)
0042     {
0043         mCurrentPost->owners++;
0044 
0045         if (!mCurrentPost->media.isEmpty()) {
0046             imageUrl = mCurrentPost->media;
0047         }
0048     }
0049     QGridLayout *buttonsLayout;
0050     QMap<QString, QPushButton *> mUiButtons; //<Object name, Button>
0051     Post *mCurrentPost;
0052     Account *mCurrentAccount;
0053 //         bool mRead;
0054     QTimer mTimer;
0055 
0056     //BEGIN UI contents:
0057     QString mSign;
0058     QString mContent;
0059     QString mProfileImage;
0060     QString mImage;
0061     QUrl imageUrl;
0062     QString dir;
0063     QPixmap originalImage;
0064     QString extraContents;
0065     //END UI contents;
0066 
0067     QStringList detectedUrls;
0068 
0069     TimelineWidget *timeline;
0070     
0071     static const QLatin1String resourceImageUrl;
0072 };
0073 
0074 const QString mImageTemplate(QLatin1String("<div style=\"padding-top:5px;padding-bottom:3px;\"><img width=\"%1\" height=\"%2\" src=\"%3\"/></div>"));
0075 
0076 const QLatin1String PostWidget::Private::resourceImageUrl("img://postImage");
0077 
0078 const QString PostWidget::baseTextTemplate(QLatin1String("<table height=\"100%\" width=\"100%\"><tr><td width=\"48\" style=\"padding-right: 5px;\">%1</td><td dir=\"%4\" style=\"padding-right:3px;\"><p>%2</p></td></tr>%5%6<tr><td></td><td style=\"font-size:small;\" dir=\"ltr\" align=\"right\" valign=\"bottom\">%3</td></tr></table>"));
0079 
0080 const QString PostWidget::baseStyle(QLatin1String("QTextBrowser {border: 1px solid rgb(150,150,150);\
0081 border-radius:5px; color:%1; background-color:%2; %3}\
0082 QPushButton{border:0px} QPushButton::menu-indicator{image:none;}"));
0083 
0084 const QString PostWidget::hrefTemplate(QLatin1String("<a href='%1' title='%1' target='_blank'>%2</a>"));
0085 
0086 const QRegExp PostWidget::dirRegExp(QLatin1String("(RT|RD)|(@([^\\s\\W]+))|(#([^\\s\\W]+))|(!([^\\s\\W]+))"));
0087 
0088 QString PostWidget::readStyle;
0089 QString PostWidget::unreadStyle;
0090 QString PostWidget::ownStyle;
0091 const QString PostWidget::webIconText(QLatin1String("&#9755;"));
0092 
0093 PostWidget::PostWidget(Account *account, Choqok::Post *post, QWidget *parent/* = 0*/)
0094     : QWidget(parent), _mainWidget(new TextBrowser(this)), d(new Private(account, post))
0095 {
0096     setAttribute(Qt::WA_DeleteOnClose);
0097     _mainWidget->setFrameShape(QFrame::NoFrame);
0098     if (isOwnPost()) {
0099         d->mCurrentPost->isRead = true;
0100     }
0101     d->mTimer.start(_MINUTE);
0102     connect(&d->mTimer, &QTimer::timeout, this, &PostWidget::updateUi);
0103     connect(_mainWidget, &TextBrowser::clicked, this, &PostWidget::mousePressEvent);
0104     connect(_mainWidget, &TextBrowser::anchorClicked, this, &PostWidget::checkAnchor);
0105 
0106     d->timeline = qobject_cast<TimelineWidget *>(parent);
0107 
0108     setHeight();
0109 }
0110 
0111 void PostWidget::checkAnchor(const QUrl &url)
0112 {
0113     if (url.scheme() == QLatin1String("choqok")) {
0114         if (url.host() == QLatin1String("showoriginalpost")) {
0115             setContent(prepareStatus(currentPost()->content).replace(QLatin1String("<a href"), QLatin1String("<a style=\"text-decoration:none\" href"), Qt::CaseInsensitive));
0116             updateUi();
0117         }
0118     } else {
0119         Choqok::openUrl(url);
0120     }
0121 }
0122 
0123 PostWidget::~PostWidget()
0124 {
0125     if (d->mCurrentPost->owners < 2) {
0126         delete d->mCurrentPost;
0127     } else {
0128         d->mCurrentPost->owners--;
0129     }
0130     delete d;
0131 }
0132 
0133 Account *PostWidget::currentAccount()
0134 {
0135     return d->mCurrentAccount;
0136 }
0137 
0138 QString PostWidget::generateSign()
0139 {
0140     QString ss = QStringLiteral("<b>%1 - </b>").arg(getUsernameHyperlink(d->mCurrentPost->author));
0141 
0142     QDateTime time;
0143     if (d->mCurrentPost->repeatedDateTime.isNull()) {
0144         time = d->mCurrentPost->creationDateTime;
0145     } else {
0146         time = d->mCurrentPost->repeatedDateTime;
0147     }
0148 
0149     ss += QStringLiteral("<a href=\"%1\" title=\"%2\">%3</a>").arg(d->mCurrentPost->link.toDisplayString())
0150             .arg(time.toString(Qt::DefaultLocaleLongDate)).arg(QStringLiteral("%1"));
0151 
0152     if (!d->mCurrentPost->source.isEmpty()) {
0153         ss += QLatin1String(" - ") + d->mCurrentPost->source;
0154     }
0155 
0156     return ss;
0157 }
0158 
0159 QString PostWidget::getUsernameHyperlink(const Choqok::User &user) const
0160 {
0161     return QStringLiteral("<a href=\"%1\" title=\"%2\">%3</a>")
0162             .arg(d->mCurrentAccount->microblog()->profileUrl(d->mCurrentAccount, user.userName).toDisplayString())
0163             .arg(user.description.isEmpty() ? user.realName : user.description)
0164             .arg(user.userName);
0165 }
0166 
0167 void PostWidget::setupUi()
0168 {
0169     setLayout(new QVBoxLayout);
0170     layout()->setMargin(0);
0171     layout()->setContentsMargins(0, 0, 0, 0);
0172     layout()->addWidget(_mainWidget);
0173     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
0174     _mainWidget->setFocusProxy(this);
0175 
0176     d->buttonsLayout = new QGridLayout(_mainWidget);
0177     d->buttonsLayout->setRowStretch(0, 100);
0178     d->buttonsLayout->setColumnStretch(5, 100);
0179     d->buttonsLayout->setMargin(0);
0180     d->buttonsLayout->setSpacing(0);
0181 
0182     _mainWidget->setLayout(d->buttonsLayout);
0183     connect(_mainWidget, &TextBrowser::textChanged, this, &PostWidget::setHeight);
0184 
0185 }
0186 
0187 void PostWidget::initUi()
0188 {
0189     setupUi();
0190     _mainWidget->document()->addResource(QTextDocument::ImageResource, QUrl(QLatin1String("img://profileImage")),
0191                                          MediaManager::self()->defaultImage());
0192 
0193     if (isRemoveAvailable()) {
0194         QPushButton *btnRemove = addButton(QLatin1String("btnRemove"), i18nc("@info:tooltip", "Remove"), QLatin1String("edit-delete"));
0195         connect(btnRemove, &QPushButton::clicked, this, &PostWidget::removeCurrentPost);
0196     }
0197 
0198     if (isResendAvailable()) {
0199         QPushButton *btnResend = addButton(QLatin1String("btnResend"), i18nc("@info:tooltip", "ReSend"), QLatin1String("retweet"));
0200         connect(btnResend, &QPushButton::clicked, this, &PostWidget::slotResendPost);
0201     }
0202 
0203     d->mProfileImage = QLatin1String("<img src=\"img://profileImage\" title=\"") + d->mCurrentPost->author.realName + QLatin1String("\" width=\"48\" height=\"48\" />");
0204     d->mContent = prepareStatus(d->mCurrentPost->content);
0205     d->mSign = generateSign();
0206     setupAvatar();
0207     fetchImage();
0208     d->dir = getDirection(d->mCurrentPost->content);
0209     setUiStyle();
0210 
0211     d->mContent.replace(QLatin1String("<a href"), QLatin1String("<a style=\"text-decoration:none\" href"), Qt::CaseInsensitive);
0212     d->mContent.replace(QLatin1String("\n"), QLatin1String("<br/>"));
0213     d->extraContents.replace(QLatin1String("<a href"), QLatin1String("<a style=\"text-decoration:none\" href"), Qt::CaseInsensitive);
0214     d->mSign.replace(QLatin1String("<a href"), QLatin1String("<a style=\"text-decoration:none\" href"), Qt::CaseInsensitive);
0215 
0216     updateUi();
0217 }
0218 
0219 void PostWidget::updateUi()
0220 {
0221     QDateTime time;
0222     if (currentPost()->repeatedDateTime.isNull()) {
0223         time = currentPost()->creationDateTime;
0224     } else {
0225         time = currentPost()->repeatedDateTime;
0226     }
0227 
0228     _mainWidget->setHtml(baseTextTemplate.arg( d->mProfileImage,                     /*1*/
0229                                                d->mContent,                          /*2*/
0230                                                d->mSign.arg(formatDateTime(time)),   /*3*/
0231                                                d->dir,                               /*4*/
0232                                                d->mImage,                            /*5*/
0233                                                d->extraContents                      /*6*/
0234                                                ));
0235 }
0236 
0237 void PostWidget::setStyle(const QColor &color, const QColor &back, const QColor &read, const QColor &readBack, const QColor &own, const QColor &ownBack, const QFont &font)
0238 {
0239     QString fntStr = QLatin1String("font-family:\"") + font.family() + QLatin1String("\"; font-size:") + QString::number(font.pointSize()) + QLatin1String("pt;");
0240     fntStr += (font.bold() ? QLatin1String(" font-weight:bold;") : QString()) + (font.italic() ? QLatin1String(" font-style:italic;") : QString());
0241     unreadStyle = baseStyle.arg(getColorString(color), getColorString(back), fntStr);
0242     readStyle = baseStyle.arg(getColorString(read), getColorString(readBack), fntStr);
0243     ownStyle = baseStyle.arg(getColorString(own), getColorString(ownBack), fntStr);
0244 }
0245 
0246 QPushButton *PostWidget::addButton(const QString &objName, const QString &toolTip, const QString &icon)
0247 {
0248     return addButton(objName, toolTip, QIcon::fromTheme(icon));
0249 
0250 }
0251 
0252 QPushButton *PostWidget::addButton(const QString &objName, const QString &toolTip, const QIcon &icon)
0253 {
0254     QPushButton *button = new QPushButton(icon, QString(), _mainWidget);
0255     button->setObjectName(objName);
0256     button->setToolTip(toolTip);
0257     button->setIconSize(QSize(16, 16));
0258     button->setMinimumSize(QSize(20, 20));
0259     button->setMaximumSize(QSize(20, 20));
0260     button->setFlat(true);
0261     button->setVisible(false);
0262     button->setCursor(Qt::PointingHandCursor);
0263 
0264     d->mUiButtons.insert(objName, button);
0265     d->buttonsLayout->addWidget(button, 1, d->mUiButtons.count());
0266     return button;
0267 }
0268 
0269 Post *PostWidget::currentPost() const
0270 {
0271     return d->mCurrentPost;
0272 }
0273 
0274 void PostWidget::setCurrentPost(Post *post)
0275 {
0276     d->mCurrentPost = post;
0277 }
0278 
0279 void PostWidget::setRead(bool read/* = true*/)
0280 {
0281     if (!read && !currentPost()->isRead &&
0282             currentAccount()->username().compare(currentPost()->author.userName, Qt::CaseInsensitive) == 0) {
0283         d->mCurrentPost->isRead = true; ///Always Set own posts as read.
0284         setUiStyle();
0285     } else if (currentPost()->isRead != read) {
0286         d->mCurrentPost->isRead = read;
0287         setUiStyle();
0288     }
0289 }
0290 
0291 void PostWidget::setReadWithSignal()
0292 {
0293     if (!isRead()) {
0294         setRead();
0295         Q_EMIT postReaded();
0296     }
0297 }
0298 
0299 bool PostWidget::isRead() const
0300 {
0301     return currentPost()->isRead;
0302 }
0303 
0304 void PostWidget::setUiStyle()
0305 {
0306     if (isOwnPost()) {
0307         setStyleSheet(ownStyle);
0308     } else {
0309         if (currentPost()->isRead) {
0310             setStyleSheet(readStyle);
0311         } else {
0312             setStyleSheet(unreadStyle);
0313         }
0314     }
0315     setHeight();
0316 }
0317 
0318 bool PostWidget::isOwnPost()
0319 {
0320     return currentAccount()->username().compare(currentPost()->author.userName, Qt::CaseInsensitive) == 0;
0321 }
0322 
0323 void PostWidget::setHeight()
0324 {
0325     _mainWidget->document()->setTextWidth(width() - 2);
0326     int h = _mainWidget->document()->size().toSize().height() + 2;
0327     setFixedHeight(h);
0328 }
0329 
0330 void PostWidget::closeEvent(QCloseEvent *event)
0331 {
0332     clearFocus();
0333     if (!isRead()) {
0334         setReadWithSignal();
0335     }
0336     Q_EMIT aboutClosing(currentPost()->postId, this);
0337     event->accept();
0338 }
0339 
0340 void PostWidget::mousePressEvent(QMouseEvent *ev)
0341 {
0342     if (!isRead()) {
0343         setReadWithSignal();
0344     }
0345     QWidget::mousePressEvent(ev);
0346 }
0347 
0348 void PostWidget::resizeEvent(QResizeEvent *event)
0349 {
0350     updatePostImage( event->size().width() );
0351     setHeight();
0352     updateUi();
0353     QWidget::resizeEvent(event);
0354 }
0355 
0356 void PostWidget::enterEvent(QEvent *event)
0357 {
0358     for (QPushButton *btn: buttons()) {
0359         if (btn) { //A crash happens here :/
0360             btn->show();
0361         }
0362     }
0363     QWidget::enterEvent(event);
0364 }
0365 
0366 void PostWidget::leaveEvent(QEvent *event)
0367 {
0368     for (QPushButton *btn: buttons()) {
0369         if (btn) {
0370             btn->hide();
0371         }
0372     }
0373     QWidget::enterEvent(event);
0374 }
0375 
0376 void PostWidget::updatePostImage(int width)
0377 {
0378     if ( !d->originalImage.isNull() ) {
0379         // TODO: Find a way to calculate the difference we need to subtract.
0380         width -= 76;
0381         
0382         QPixmap newPixmap = d->originalImage.scaledToWidth(width, Qt::SmoothTransformation);
0383         auto newW = newPixmap.width();
0384         auto newH = newPixmap.height();
0385         auto origW = d->originalImage.width();
0386         auto origH = d->originalImage.height();
0387         
0388         const QUrl url(d->resourceImageUrl);
0389         // only use scaled image if it's smaller than the original one
0390         if (newW <= origW && newH <= origH) { // never scale up
0391             d->mImage = mImageTemplate.arg(QString::number(newW), QString::number(newH), d->resourceImageUrl);
0392             _mainWidget->document()->addResource(QTextDocument::ImageResource, url, newPixmap);
0393         } else {
0394             d->mImage = mImageTemplate.arg(QString::number(origW), QString::number(origH), d->resourceImageUrl);
0395             _mainWidget->document()->addResource(QTextDocument::ImageResource, url, d->originalImage);
0396         }
0397     }
0398 }
0399 
0400 QString PostWidget::prepareStatus(const QString &txt)
0401 {
0402     QString text = removeTags(txt);
0403 
0404     d->detectedUrls = UrlUtils::detectUrls(text);
0405     for (const QString &url: d->detectedUrls) {
0406         QString httpUrl(url);
0407         if (!httpUrl.startsWith(QLatin1String("http"), Qt::CaseInsensitive) &&
0408                 !httpUrl.startsWith(QLatin1String("ftp"), Qt::CaseInsensitive)) {
0409             httpUrl.prepend(QLatin1String("http://"));
0410             text.replace(url, httpUrl);
0411         }
0412 
0413         text.replace(url, hrefTemplate.arg(httpUrl, url));
0414     }
0415 
0416     text = UrlUtils::detectEmails(text);
0417 
0418     if (AppearanceSettings::isEmoticonsEnabled()) {
0419         text = MediaManager::self()->parseEmoticons(text);
0420     }
0421 
0422     return text;
0423 }
0424 
0425 QString PostWidget::removeTags(const QString &text) const
0426 {
0427     QString txt(text);
0428 
0429     txt.replace(QLatin1Char('<'), QLatin1String("&lt;"));
0430     txt.replace(QLatin1Char('>'), QLatin1String("&gt;"));
0431 
0432     return txt;
0433 }
0434 QLatin1String PostWidget::getDirection(QString txt)
0435 {
0436     txt.remove(dirRegExp);
0437     txt = txt.trimmed();
0438     if (txt.isRightToLeft()) {
0439         return QLatin1String("rtl");
0440     }
0441     else {
0442         return QLatin1String("ltr");
0443     }
0444 }
0445 
0446 QString PostWidget::formatDateTime(const QDateTime &time)
0447 {
0448     if (!time.isValid()) {
0449         return tr("Invalid Time");
0450     }
0451     auto seconds = time.secsTo(QDateTime::currentDateTime());
0452     if (seconds <= 15) {
0453         d->mTimer.setInterval(_15SECS);
0454         return i18n("Just now");
0455     }
0456 
0457     if (seconds <= 45) {
0458         d->mTimer.setInterval(_15SECS);
0459         return i18np("1 sec ago", "%1 secs ago", seconds);
0460     }
0461 
0462     auto minutes = (seconds - 45 + 59) / 60;
0463     if (minutes <= 45) {
0464         d->mTimer.setInterval(_MINUTE);
0465         return i18np("1 min ago", "%1 mins ago", minutes);
0466     }
0467 
0468     auto hours = (seconds - 45 * 60 + 3599) / 3600;
0469     if (hours <= 18) {
0470         d->mTimer.setInterval(_MINUTE * 15);
0471         return i18np("1 hour ago", "%1 hours ago", hours);
0472     }
0473 
0474     d->mTimer.setInterval(_HOUR);
0475     auto days = (seconds - 18 * 3600 + 24 * 3600 - 1) / (24 * 3600);
0476     return i18np("1 day ago", "%1 days ago", days);
0477 }
0478 
0479 void PostWidget::removeCurrentPost()
0480 {
0481     if (KMessageBox::warningYesNo(this, i18n("Are you sure you want to remove this post from the server?")) == KMessageBox::Yes) {
0482         connect(d->mCurrentAccount->microblog(), &MicroBlog::postRemoved,
0483                 this, &PostWidget::slotCurrentPostRemoved);
0484         connect(d->mCurrentAccount->microblog(), &MicroBlog::errorPost, this, &PostWidget::slotPostError);
0485         setReadWithSignal();
0486         d->mCurrentAccount->microblog()->removePost(d->mCurrentAccount, d->mCurrentPost);
0487     }
0488 }
0489 
0490 void PostWidget::slotCurrentPostRemoved(Account *theAccount, Post *post)
0491 {
0492     if (theAccount == currentAccount() && post == d->mCurrentPost) {
0493         this->close();
0494     }
0495 }
0496 
0497 void PostWidget::slotResendPost()
0498 {
0499     QString text = generateResendText();
0500     setReadWithSignal();
0501     if ((BehaviorSettings::resendWithQuickPost() || currentAccount()->isReadOnly()) && Global::quickPostWidget()) {
0502         Global::quickPostWidget()->setText(text);
0503     } else {
0504         Q_EMIT resendPost(text);
0505     }
0506 }
0507 
0508 QString PostWidget::generateResendText()
0509 {
0510     if (BehaviorSettings::useCustomRT()) {
0511         return QString(BehaviorSettings::customRT()) + QLatin1String(" @") + currentPost()->author.userName + QLatin1String(": ") + currentPost()->content;
0512     } else {
0513         QChar re(0x267B);
0514         return QString(re) + QLatin1String(" @") + currentPost()->author.userName + QLatin1String(": ") + currentPost()->content;
0515     }
0516 }
0517 
0518 void PostWidget::fetchImage()
0519 {
0520     if (d->imageUrl.isEmpty()) {
0521         return;
0522     }
0523 
0524     QPixmap pix = MediaManager::self()->fetchImage(d->imageUrl, MediaManager::Async);
0525 
0526     if (!pix.isNull()) {
0527         slotImageFetched(d->imageUrl, pix);
0528     } else {
0529         connect(MediaManager::self(), &MediaManager::imageFetched, this,
0530                 &PostWidget::slotImageFetched);
0531     }
0532 }
0533 
0534 void PostWidget::slotImageFetched(const QUrl &remoteUrl, const QPixmap &pixmap)
0535 {
0536     if (remoteUrl == d->imageUrl) {
0537         disconnect(MediaManager::self(), &MediaManager::imageFetched, this, &PostWidget::slotImageFetched);
0538         d->originalImage = pixmap;
0539         updatePostImage( width() );
0540         updateUi();
0541     }
0542 }
0543 
0544 void PostWidget::setupAvatar()
0545 {
0546     QPixmap pix = MediaManager::self()->fetchImage(d->mCurrentPost->author.profileImageUrl,
0547                   MediaManager::Async);
0548     if (!pix.isNull()) {
0549         avatarFetched(d->mCurrentPost->author.profileImageUrl, pix);
0550     } else {
0551         connect(MediaManager::self(), &MediaManager::imageFetched, this, &PostWidget::avatarFetched);
0552         connect(MediaManager::self(), &MediaManager::fetchError, this, &PostWidget::avatarFetchError);
0553     }
0554 }
0555 
0556 void PostWidget::avatarFetched(const QUrl &remoteUrl, const QPixmap &pixmap)
0557 {
0558     if (remoteUrl == d->mCurrentPost->author.profileImageUrl) {
0559         const QUrl url(QLatin1String("img://profileImage"));
0560         _mainWidget->document()->addResource(QTextDocument::ImageResource, url, pixmap);
0561         updateUi();
0562         disconnect(MediaManager::self(), &MediaManager::imageFetched, this, &PostWidget::avatarFetched);
0563         disconnect(MediaManager::self(), &MediaManager::fetchError, this, &PostWidget::avatarFetchError);
0564     }
0565 }
0566 
0567 void PostWidget::avatarFetchError(const QUrl &remoteUrl, const QString &errMsg)
0568 {
0569     Q_UNUSED(errMsg);
0570     if (remoteUrl == d->mCurrentPost->author.profileImageUrl) {
0571         ///Avatar fetching is failed! but will not disconnect to get the img if it fetches later!
0572         const QUrl url(QLatin1String("img://profileImage"));
0573         _mainWidget->document()->addResource(QTextDocument::ImageResource,
0574                                              url, QIcon::fromTheme(QLatin1String("image-missing")).pixmap(48));
0575         updateUi();
0576     }
0577 }
0578 
0579 QMap<QString, QPushButton * > &PostWidget::buttons()
0580 {
0581     return d->mUiButtons;
0582 }
0583 
0584 void PostWidget::slotPostError(Account *theAccount, Choqok::Post *post,
0585                                MicroBlog::ErrorType , const QString &errorMessage)
0586 {
0587     if (theAccount == currentAccount() && post == d->mCurrentPost) {
0588         qCDebug(CHOQOK) << errorMessage;
0589         disconnect(d->mCurrentAccount->microblog(), &MicroBlog::postRemoved,
0590                    this, &PostWidget::slotCurrentPostRemoved);
0591         disconnect(d->mCurrentAccount->microblog(), &MicroBlog::errorPost,
0592                    this, &PostWidget::slotPostError);
0593     }
0594 }
0595 
0596 QString PostWidget::avatarText() const
0597 {
0598     return d->mProfileImage;
0599 }
0600 
0601 void PostWidget::setAvatarText(const QString &text)
0602 {
0603     d->mProfileImage = text;
0604     updateUi();
0605 }
0606 
0607 QString PostWidget::content() const
0608 {
0609     return d->mContent;
0610 }
0611 
0612 void PostWidget::setContent(const QString &content)
0613 {
0614     d->mContent = content;
0615     updateUi();
0616 }
0617 
0618 QStringList PostWidget::urls()
0619 {
0620     return d->detectedUrls;
0621 }
0622 
0623 QString PostWidget::sign() const
0624 {
0625     return d->mSign;
0626 }
0627 
0628 void PostWidget::setSign(const QString &sign)
0629 {
0630     d->mSign = sign;
0631     updateUi();
0632 }
0633 
0634 void PostWidget::deleteLater()
0635 {
0636     close();
0637 }
0638 
0639 TextBrowser *PostWidget::mainWidget()
0640 {
0641     return _mainWidget;
0642 }
0643 
0644 void PostWidget::wheelEvent(QWheelEvent *event)
0645 {
0646     event->ignore();
0647 }
0648 
0649 void PostWidget::addAction(QAction *action)
0650 {
0651     TextBrowser::addAction(action);
0652 }
0653 
0654 TimelineWidget *PostWidget::timelineWidget() const
0655 {
0656     return d->timeline;
0657 }
0658 
0659 class PostWidgetUserData::Private
0660 {
0661 public:
0662     Private(PostWidget *postwidget)
0663         : postWidget(postwidget)
0664     {}
0665     PostWidget *postWidget;
0666 };
0667 
0668 PostWidgetUserData::PostWidgetUserData(PostWidget *postWidget)
0669     : QObjectUserData(), d(new Private(postWidget))
0670 {
0671 
0672 }
0673 
0674 PostWidgetUserData::~PostWidgetUserData()
0675 {
0676     delete d;
0677 }
0678 
0679 PostWidget *PostWidgetUserData::postWidget()
0680 {
0681     return d->postWidget;
0682 }
0683 
0684 void PostWidgetUserData::setPostWidget(PostWidget *widget)
0685 {
0686     d->postWidget = widget;
0687 }
0688 
0689 QString PostWidget::getBaseStyle()
0690 {
0691     return baseStyle;
0692 }
0693 
0694 bool PostWidget::isRemoveAvailable()
0695 {
0696     return d->mCurrentAccount->username().compare(d->mCurrentPost->author.userName, Qt::CaseInsensitive) == 0;
0697 }
0698 
0699 bool PostWidget::isResendAvailable()
0700 {
0701     return d->mCurrentAccount->username().compare(d->mCurrentPost->author.userName, Qt::CaseInsensitive) != 0;
0702 }
0703 
0704 void PostWidget::setExtraContents(const QString& text)
0705 {
0706     d->extraContents = text;
0707 }
0708 
0709 QString PostWidget::extraContents() const
0710 {
0711     return d->extraContents;
0712 }
0713 
0714 #include "moc_postwidget.cpp"