TwitterMicroBlog plugin made operational.
[choqok:choqok-obsolete.git] / libchoqok / ui / postwidget.cpp
1 /*
2 This file is part of Choqok, the KDE micro-blogging client
3
4 Copyright (C) 2008-2009 Mehrdad Momeny <mehrdad.momeny@gmail.com>
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License as
8 published by the Free Software Foundation; either version 2 of
9 the License or (at your option) version 3 or any later version
10 accepted by the membership of KDE e.V. (or its successor approved
11 by the membership of KDE e.V.), which shall act as a proxy
12 defined in Section 14 of version 3 of the license.
13
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see http://www.gnu.org/licenses/
22 */
23 #include "postwidget.h"
24 #include <QTimer>
25 #include <qboxlayout.h>
26 #include <microblog.h>
27 #include <KLocale>
28 #include <KPushButton>
29 #include <QGridLayout>
30
31 static const int _15SECS = 15000;
32 static const int _MINUTE = 60000;
33 static const int _HOUR = 60*_MINUTE;
34
35 namespace Choqok
36 {
37 class PostWidget::PWPrivate
38 {
39 public:
40     PWPrivate(Account *account)
41     :currentAccount(account), read(false)
42     {}
43     static QString getColorString(const QColor& color)
44     {
45         return "rgb(" + QString::number(color.red()) + ',' + QString::number(color.green()) + ',' +
46         QString::number(color.blue()) + ')';
47     }
48     static void setStyle(const QColor& color, const QColor& back, const QColor& read, const QColor& readBack)
49     {
50         style = baseStyle.arg(getColorString(color),getColorString(back),getColorString(read),getColorString(readBack));
51     }
52     Post currentPost;
53     Account *currentAccount;
54     bool read;
55     QTimer timer;
56
57     //BEGIN UI contents:
58     QString sign;
59     QString content;
60     QString image;
61     //END UI contens;
62
63     static const QString baseText;
64     static const QString baseStyle;
65     static QString style;
66     static const QRegExp mUrlRegExp;
67 };
68
69 const QString PostWidget::PWPrivate::baseText ( "<table width=\"100%\"><tr><td rowspan=\"2\"\
70 width=\"48\">%1</td><td><p>%2</p></td></tr><tr><td style=\"font-size:small;\" align=\"right\">%3</td></tr></table>");
71 const QString PostWidget::PWPrivate::baseStyle ("QFrame.StatusWidget {border: 1px solid rgb(150,150,150);\
72 border-radius:5px;}\
73 QFrame.PostWidget[read=false] {color: %1; background-color: %2}\
74 QFrame.PostWidget[read=true] {color: %3; background-color: %4}");
75 const QRegExp PostWidget::PWPrivate::mUrlRegExp("((ftps?|https?)://[^\\s<>\"]+[^!,\\.\\s<>'\"\\)\\]])");
76 QString PostWidget::PWPrivate::style;
77
78 PostWidget::PostWidget( Account *account, QWidget *parent/* = 0*/ )
79 :KTextBrowser(parent), d(new PWPrivate(account))
80 {
81     setupUi();
82     d->timer.start( _MINUTE );
83     connect( &d->timer, SIGNAL( timeout() ), this, SLOT( updateUi()) );
84 }
85 PostWidget::~PostWidget()
86 {
87     delete d;
88 }
89
90 QString PostWidget::generateSign()
91 {
92     d->sign = "<b><a href='"+ d->currentAccount->microblog()->profileUrl( d->currentPost.author.userName ) +"' title=\"" +
93     d->currentPost.author.description + "\">" + d->currentPost.author.userName +
94     "</a> - </b>";
95
96     d->sign += "<a href=\"" + d->currentPost.link +
97     "\" title=\"" + d->currentPost.creationDateTime.toString() + "\">%1</a>";
98
99     if( !d->currentPost.source.isNull() )
100         d->sign += " - " + d->currentPost.source;
101
102     return d->sign;
103 }
104
105 void PostWidget::setupUi()
106 {
107     setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
108     setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
109     setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
110     setFrameShape(NoFrame);
111     setLineWidth(0);
112     setFrameShadow(Plain);
113
114     buttonsLayout = new QGridLayout(this);
115     buttonsLayout->setRowStretch(0,100);
116     buttonsLayout->setColumnStretch(5,100);
117     buttonsLayout->setMargin(0);
118     buttonsLayout->setSpacing(0);
119
120     this->setLayout(buttonsLayout);
121 //     KIcon icon("image-loading");
122 //     QPixmap defaultImage = icon.pixmap(48);
123 //TODO Add default image!
124     document()->addResource( QTextDocument::ImageResource, QUrl("img://profileImage"), QPixmap() );
125     connect(this,SIGNAL(textChanged()),this,SLOT(setHeight()));
126
127 }
128
129 void PostWidget::updateUi()
130 {
131     setHtml( PWPrivate::baseText.arg( d->image, d->content, d->sign.arg( formatDateTime( d->currentPost.creationDateTime ) ) ) );
132 }
133
134 void PostWidget::initUi()
135 {
136     KPushButton *btnResend = getButton("btnResend", i18nc( "@info:tooltip", "RePost" ), "retweet" );
137     buttonsLayout->addWidget( btnResend, 1, 0 );
138     connect(btnResend, SIGNAL(clicked(bool)), SLOT(slotResendPost()));
139     if(d->currentAccount->username() == d->currentPost.author.userName) {
140         KPushButton *btnRemove = getButton("btnRemove", i18nc( "@info:tooltip", "Remove" ), "edit-delete" );
141         buttonsLayout->addWidget( btnRemove, 1, 1 );
142         connect(btnRemove, SIGNAL(clicked(bool)), SLOT(removeCurrentPost()));
143     }
144
145     d->image = "<img src=\"img://profileImage\" title=\""+ d->currentPost.author.realName +"\" width=\"48\" height=\"48\" />";
146     d->content = prepareStatus(d->currentPost.content);
147     generateSign();
148     setDirection();
149     setUiStyle();
150     //TODO setUserImage
151     updateUi();
152 }
153
154 void PostWidget::setStyle(const QColor& color, const QColor& back, const QColor& read, const QColor& readBack)
155 {
156     PWPrivate::setStyle(color, back, read, readBack);
157 }
158
159 KPushButton * PostWidget::getButton(const QString & objName, const QString & toolTip, const QString & icon)
160 {
161     KPushButton * button = new KPushButton(KIcon(icon),QString());
162     button->setObjectName(objName);
163     button->setToolTip(toolTip);
164     button->setIconSize(QSize(12,12));
165     button->setMinimumSize(QSize(20, 20));
166     button->setMaximumSize(QSize(20, 20));
167     button->setFlat(true);
168     button->setVisible(false);
169     button->setCursor(Qt::PointingHandCursor);
170     return button;
171 }
172
173 Post PostWidget::currentPost() const
174 {
175     return d->currentPost;
176 }
177
178 void PostWidget::setCurrentPost( const Post &newPost)
179 {
180     d->currentPost = newPost;
181     initUi();
182 }
183
184 void PostWidget::setRead(bool read/* = true*/)
185 {
186     d->read = read;
187     setUiStyle();
188 }
189
190 bool PostWidget::isRead() const
191 {
192     return d->read;
193 }
194
195 void PostWidget::setUiStyle()
196 {
197     setStyleSheet(PWPrivate::style);
198 }
199
200 void PostWidget::setHeight()
201 {
202     document()->setTextWidth(width()-2);
203     int h = document()->size().toSize().height()+2;
204     setMinimumHeight(h);
205     setMaximumHeight(h);
206 }
207
208 void PostWidget::resizeEvent ( QResizeEvent * event )
209 {
210     setHeight();
211     KTextBrowser::resizeEvent(event);
212 }
213
214 void PostWidget::enterEvent ( QEvent * event )
215 {
216     foreach(KPushButton *btn, buttons){
217         btn->show();
218     }
219     KTextBrowser::enterEvent(event);
220 }
221
222 void PostWidget::leaveEvent ( QEvent * event )
223 {
224     foreach(KPushButton *btn, buttons){
225         btn->hide();
226     }
227     KTextBrowser::enterEvent(event);
228 }
229
230 QString PostWidget::prepareStatus( const QString &txt )
231 {
232     QString text = txt;
233     text.replace( '<', "&lt;" );
234     text.replace( '>', "&gt;" );
235     text.replace( " www.", " http://www." );
236     if ( text.startsWith( QLatin1String("www.") ) )
237         text.prepend( "http://" );
238     text.replace(PWPrivate::mUrlRegExp,"<a href='\\1' title='\\1'>\\1</a>");
239
240     //TODO Add support for PLUGINS! e.g. Smilie, UnTiny, ...
241
242     return text;
243 }
244
245 void PostWidget::setDirection()
246 {
247     QString txt = d->currentPost.content;
248     if(txt.startsWith('@'))
249         txt.remove(QRegExp("(^)@([^\\s\\W]+)"));
250     if(txt.startsWith('#'))
251         txt.remove(QRegExp("(^)#([^\\s\\W]+)"));
252     if(txt.startsWith('!'))
253         txt.remove(QRegExp("(^)!([^\\s\\W]+)"));
254     txt.prepend(' ');
255     if( txt.isRightToLeft() ) {
256         QTextOption options(document()->defaultTextOption());
257         options.setTextDirection( Qt::RightToLeft );
258         document()->setDefaultTextOption(options);
259     }
260 }
261
262 QString PostWidget::formatDateTime( const KDateTime &time )
263 {
264     int seconds = time.secsTo( KDateTime::currentLocalDateTime() );
265     if ( seconds <= 15 ) {
266         d->timer.setInterval( _15SECS );
267         return i18n( "Just now" );
268     }
269
270     if ( seconds <= 45 ) {
271         d->timer.setInterval( _15SECS );
272         return i18np( "1 sec ago", "%1 secs ago", seconds );
273     }
274
275     int minutes = ( seconds - 45 + 59 ) / 60;
276     if ( minutes <= 45 ) {
277         d->timer.setInterval( _MINUTE );
278         return i18np( "1 min ago", "%1 mins ago", minutes );
279     }
280
281     int hours = ( seconds - 45 * 60 + 3599 ) / 3600;
282     if ( hours <= 18 ) {
283         d->timer.setInterval( _MINUTE * 15 );
284         return i18np( "1 hour ago", "%1 hours ago", hours );
285     }
286
287     d->timer.setInterval( _HOUR );
288     int days = ( seconds - 18 * 3600 + 24 * 3600 - 1 ) / ( 24 * 3600 );
289     return i18np( "1 day ago", "%1 days ago", days );
290 }
291
292 void PostWidget::removeCurrentPost()
293 {
294     connect(d->currentAccount->microblog(), SIGNAL(postRemoved(Post)), SLOT(slotCurrentPostRemoved(Post)));
295     d->currentAccount->microblog()->removePost(&d->currentPost);
296 }
297
298 void PostWidget::slotCurrentPostRemoved( const Post &post )
299 {
300     if(post.postId == d->currentPost.postId)
301         this->close();
302 }
303
304 void PostWidget::slotResendPost()
305 {
306     QChar re(0x267B);
307     QString text =  QString(re) + " @" + d->currentPost.author.userName + ": " + d->currentPost.content;
308     emit resendPost(text);
309 }
310
311 }