Added a bunch of key shortcuts.
[grr] / src / contentwindow.cpp
1 #include <QMenuBar>
2 #include <QDebug>
3 #include <QDesktopServices>
4 #include <QtGui>
5 #include <QtWebKit>
6 #include <QtMaemo5>
7
8 #include <QtGui/QX11Info>
9 #include <X11/Xlib.h>
10 #include <X11/Xatom.h>
11
12 #include "contentwindow.h"
13
14 /* Got ViewportItem and GraphicsView from maemobrowser in the qt examples. The
15  * whole point of this is to get a finger friendly web widget */
16
17 class ViewportItem : public QGraphicsWidget, public QAbstractKineticScroller {
18         Q_OBJECT
19
20         public:
21                 ViewportItem() : QGraphicsWidget(), QAbstractKineticScroller(), m_widget(0), m_ignoreEvents(false) {
22                         setFlag(QGraphicsItem::ItemHasNoContents, true);
23                         setFlag(QGraphicsItem::ItemClipsChildrenToShape, true);
24                         setFlag(QGraphicsItem::ItemClipsToShape, true);
25                         setAttribute(Qt::WA_OpaquePaintEvent, true);
26                         setFiltersChildEvents(true);
27                 }
28
29                 void setWidget(QGraphicsWidget *widget) {
30                         if (m_widget) {
31                                 m_widget->setParentItem(0);
32                                 delete m_widget;
33                         }
34                         m_widget = widget;
35                         if (m_widget) {
36                                 m_widget->setParentItem(this);
37                                 m_widget->setAttribute(Qt::WA_OpaquePaintEvent, true);
38
39                                 if (qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
40                                         connect(m_widget, SIGNAL(loadProgress(int)), this, SLOT(resizeWebViewToFrame()));
41                                         connect(m_widget, SIGNAL(loadFinished(bool)), this, SLOT(resizeWebViewToFrame()));
42                                         resizeWebViewToFrame();
43                                 }
44                         }
45                 }
46
47         signals:
48                 void showNextEntry();
49                 void showPrevEntry();
50                 void showOriginal();
51                 void toggleStarred();
52                 void toggleShared();
53                 void toggleRead();
54
55         protected:
56                 bool sceneEventFilter(QGraphicsItem *i, QEvent *e) {
57                         bool res = false;
58                         if (i && (i == m_widget) && !m_ignoreEvents && m_widget->isEnabled()) {
59                                 switch (e->type()) {
60                                 case QEvent::GraphicsSceneMousePress:
61                                 case QEvent::GraphicsSceneMouseMove:
62                                 case QEvent::GraphicsSceneMouseRelease:
63                                         res = handleMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
64                                         break;
65                                 default:
66                                         break;
67                                 }
68                         }
69                         // prevent text selection and image dragging
70                         if (e->type() == QEvent::GraphicsSceneMouseMove)
71                                 return true;
72                         return res ? true : QGraphicsWidget::sceneEventFilter(i, e);
73                 }
74
75                 void keyPressEvent(QKeyEvent *e) {
76                         int x, y, w, h;
77
78                         switch(e->key()) {
79                         case Qt::Key_Down:
80                                 y = m_widget->y() - 50;
81                                 h = (m_widget->size() - size()).height();
82                                 m_widget->setY(y < -h ? -h : y);
83                                 break;
84
85                         case Qt::Key_Up:
86                                 y = m_widget->y() + 50;
87                                 m_widget->setY(y > 0 ? 0 : y);
88                                 break;
89
90                         case Qt::Key_Right:
91                                 x = m_widget->x() - 50;
92                                 w = (m_widget->size() - size()).width();
93                                 m_widget->setX(x < -w ? -w : x);
94                                 break;
95
96                         case Qt::Key_Left:
97                                 x = m_widget->x() + 50;
98                                 m_widget->setX(x > 0 ? 0 : x);
99                                 break;
100
101                         case Qt::Key_F7:
102                                 if(qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
103                                         QGraphicsWebView *wv = (QGraphicsWebView *)m_widget;
104                                         wv->setZoomFactor(wv->zoomFactor() * 1.25);
105                                         resizeWebViewToFrame();
106                                 }
107                                 break;
108
109                         case Qt::Key_F8:
110                                 if(qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
111                                         QGraphicsWebView *wv = (QGraphicsWebView *)m_widget;
112                                         wv->setZoomFactor(wv->zoomFactor() / 1.25);
113                                         resizeWebViewToFrame();
114                                 }
115                                 break;
116
117                         case Qt::Key_J:
118                         case Qt::Key_N:
119                                 emit showNextEntry();
120                                 break;
121
122                         case Qt::Key_K:
123                         case Qt::Key_P:
124                                 emit showPrevEntry();
125                                 break;
126
127                         case Qt::Key_Space:
128                                 if(e->modifiers() & Qt::ShiftModifier) {
129                                         y = m_widget->y() + size().height();
130                                         m_widget->setY(y > 0 ? 0 : y);
131                                 }
132                                 else {
133                                         y = m_widget->y() - size().height();;
134                                         h = (m_widget->size() - size()).height();
135                                         m_widget->setY(y < -h ? -h : y);
136                                 }
137                                 break;
138
139                         case Qt::Key_S:
140                                 if(e->modifiers() & Qt::ShiftModifier)
141                                         emit toggleShared();
142                                 else
143                                         emit toggleStarred();
144                                 break;
145
146                         case Qt::Key_V:
147                                 emit showOriginal();
148                                 break;
149
150                         case Qt::Key_M:
151                                 emit toggleRead();
152                                 break;
153                         }
154                 }
155
156                 QSize viewportSize() const {
157                         return size().toSize();
158                 }
159
160                 QPoint maximumScrollPosition() const {
161                         QSizeF s = m_widget ? m_widget->size() - size() : QSize(0, 0);
162                         return QPoint(qMax(0, int(s.width())), qMax(0, int(s.height())));
163                 }
164
165                 QPoint scrollPosition() const {
166                         return m_widget ? -m_widget->pos().toPoint() + m_overShoot : QPoint();
167                 }
168
169                 void setScrollPosition(const QPoint &p, const QPoint &overShoot) {
170                         m_overShoot = overShoot;
171                         if (m_widget)
172                                 m_widget->setPos(-p + m_overShoot);
173                 }
174
175                 void sendEvent(QGraphicsItem *i, QEvent *e) {
176                         m_ignoreEvents = true;
177                         scene()->sendEvent(i, e);
178                         m_ignoreEvents = false;
179                 }
180
181         private slots:
182                 void resizeWebViewToFrame() {
183                         if (QGraphicsWebView *view = qgraphicsitem_cast<QGraphicsWebView *>(m_widget)) {
184                                 if (view->page() && view->page()->mainFrame()) {
185                                         QSizeF s = view->page()->mainFrame()->contentsSize();
186                                         /* TODO: Figure out the proper way to
187                                          * get this 800 pixels. */
188                                         QSizeF s2 = size();
189                                         s2.setWidth(800);
190                                         s = s.expandedTo(s2);
191                                         view->setGeometry(QRectF(view->geometry().topLeft(), s));
192                                 }
193                         }
194                 }
195
196         private:
197                 QGraphicsWidget *m_widget;
198                 bool m_ignoreEvents;
199                 QPoint m_overShoot;
200 };
201
202 class GraphicsView : public QGraphicsView {
203         Q_OBJECT
204         
205         public:
206                 GraphicsView() : QGraphicsView(new QGraphicsScene()), viewport(0) {
207                         setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate);
208                         setOptimizationFlags(QGraphicsView::DontSavePainterState);
209
210                         setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
211                         setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
212
213                         setFrameShape(QFrame::NoFrame);
214                         setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
215
216                         viewport = new ViewportItem();
217                         scene()->addItem(viewport);
218                 }
219
220                 ViewportItem *viewportItem() const {
221                         return viewport;
222                 }
223
224         protected:
225                 void resizeEvent(QResizeEvent *e) {
226                         QGraphicsView::resizeEvent(e);
227                         setUpdatesEnabled(false);
228
229                         if (!viewport)
230                                 return;
231
232                         QRectF rect(QPointF(0, 0), size());
233                         scene()->setSceneRect(rect);
234
235                         viewport->setGeometry(rect);
236                         setUpdatesEnabled(true);
237                         update();
238                 }
239
240         private:
241                 ViewportItem *viewport;
242 };
243
244 ContentWindow::ContentWindow(QWidget *parent, Entry *e) : QMainWindow(parent) {
245         setAttribute(Qt::WA_Maemo5StackedWindow);
246
247         QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
248
249         starred = new QAction(tr("Starred"), this);
250         starred->setCheckable(true);
251         menuBar()->addAction(starred);
252
253         shared = new QAction(tr("Shared"), this);
254         shared->setCheckable(true);
255         menuBar()->addAction(shared);
256
257         keepUnread = new QAction(tr("Keep unread"), this);
258         keepUnread->setCheckable(true);
259         menuBar()->addAction(keepUnread);
260
261         menuBar()->addAction(tr("See original"), this, SLOT(seeOriginal()));
262
263         GraphicsView *gv = new GraphicsView();
264         webview = new QGraphicsWebView();
265         gv->viewportItem()->setWidget(webview);
266
267         webview->settings()->setFontSize(QWebSettings::MinimumFontSize, 22);
268         webview->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks);
269
270         connect(webview, SIGNAL(loadFinished(bool)), SLOT(loadFinished(bool)));
271         connect(webview, SIGNAL(loadStarted()), SLOT(loadStarted()));
272         connect(webview->page(), SIGNAL(linkClicked(const QUrl &)), SLOT(showLink(const QUrl &)));
273         connect(gv->viewportItem(), SIGNAL(showNextEntry()), SIGNAL(showNextEntry()));
274         connect(gv->viewportItem(), SIGNAL(showPrevEntry()), SIGNAL(showPrevEntry()));
275         connect(gv->viewportItem(), SIGNAL(showOriginal()), SLOT(seeOriginal()));
276         connect(gv->viewportItem(), SIGNAL(toggleStarred()), SLOT(toggleStarred()));
277         connect(gv->viewportItem(), SIGNAL(toggleShared()), SLOT(toggleShared()));
278         connect(gv->viewportItem(), SIGNAL(toggleRead()), SLOT(toggleRead()));
279
280         setCentralWidget(gv);
281         gv->viewportItem()->setFocus();
282         gv->viewportItem()->grabKeyboard();
283
284         grabZoomKeys(true);
285
286         entry = NULL;
287
288         showEntry(e);
289 }
290
291 ContentWindow::~ContentWindow() {
292         delete(webview);
293 }
294
295 void ContentWindow::loadStarted() {
296         setAttribute(Qt::WA_Maemo5ShowProgressIndicator, true);
297 }
298
299 void ContentWindow::loadFinished(bool) {
300         setAttribute(Qt::WA_Maemo5ShowProgressIndicator, false);
301 }
302
303 void ContentWindow::showLink(const QUrl &url) {
304         /* Attempt to launch external browser */
305         if(!QDesktopServices::openUrl(url))
306                 webview->setUrl(url); /* Failed... Show inline */
307 }
308
309 void ContentWindow::seeOriginal() {
310         showLink(entry->link);
311 }
312
313 void ContentWindow::toggleStarred() {
314         starred->toggle();
315         if(starred->isChecked()) {
316                 QMaemo5InformationBox::information(this, "Starred",
317                         QMaemo5InformationBox::DefaultTimeout);
318         }
319         else {
320                 QMaemo5InformationBox::information(this, "Star removed",
321                         QMaemo5InformationBox::DefaultTimeout);
322         }
323 }
324
325 void ContentWindow::toggleShared() {
326         shared->toggle();
327         if(shared->isChecked()) {
328                 QMaemo5InformationBox::information(this, "Shared",
329                         QMaemo5InformationBox::DefaultTimeout);
330         }
331         else {
332                 QMaemo5InformationBox::information(this, "Unshared",
333                         QMaemo5InformationBox::DefaultTimeout);
334         }
335 }
336
337 void ContentWindow::toggleRead() {
338         if(!keepUnread->isEnabled()) {
339                 QMaemo5InformationBox::information(this, "Read state locked",
340                         QMaemo5InformationBox::DefaultTimeout);
341                 return;
342         }
343
344         keepUnread->toggle();
345         if(keepUnread->isChecked()) {
346                 QMaemo5InformationBox::information(this, "Marked unread",
347                         QMaemo5InformationBox::DefaultTimeout);
348         }
349         else {
350                 QMaemo5InformationBox::information(this, "Marked read",
351                         QMaemo5InformationBox::DefaultTimeout);
352         }
353 }
354
355 void ContentWindow::showEntry(Entry *e) {
356         if(entry) {
357                 /* Store settings of previously shown entry */
358                 entry->markRead(!keepUnread->isChecked());
359                 entry->markStar(starred->isChecked());
360                 entry->markShared(shared->isChecked());
361         }
362
363         entry = e;
364
365         starred->setChecked((entry->flags & ENTRY_FLAG_STARRED));
366         shared->setChecked((entry->flags & ENTRY_FLAG_SHARED));
367         keepUnread->setChecked(false);
368         keepUnread->setEnabled((entry->flags & ENTRY_FLAG_LOCKED) == 0);
369
370         setWindowTitle(entry->title);
371
372         webview->setHtml(entry->content);
373 }
374
375 void ContentWindow::closeEvent(QCloseEvent *event) {
376         grabZoomKeys(false);
377         entry->markRead(!keepUnread->isChecked());
378         entry->markStar(starred->isChecked());
379         entry->markShared(shared->isChecked());
380         QMainWindow::closeEvent(event);
381 }
382
383 void ContentWindow::grabZoomKeys(bool grab) {
384         if(!winId())
385                 return;
386
387         unsigned long val = (grab) ? 1 : 0;
388         Atom atom = XInternAtom(QX11Info::display(), "_HILDON_ZOOM_KEY_ATOM", False);
389         if(!atom)
390                 return;
391
392         XChangeProperty (QX11Info::display(), winId(), atom, XA_INTEGER, 32, PropModeReplace,
393                 reinterpret_cast<unsigned char *>(&val), 1);
394 }
395
396 #include "contentwindow.moc"