Reviewed by Mark Rowe.
[WebKit-https.git] / WebKit / qt / QtLauncher / main.cpp
1 /*
2  * Copyright (C) 2006 George Staikos <staikos@kde.org>
3  * Copyright (C) 2006 Dirk Mueller <mueller@kde.org>
4  * Copyright (C) 2006 Zack Rusin <zack@kde.org>
5  * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org>
6  *
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
22  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include <qwebpage.h>
31 #include <qwebframe.h>
32 #include <qwebsettings.h>
33
34 #include <QtGui>
35 #include <QDebug>
36
37
38
39 class InfoWidget :public QProgressBar {
40     Q_OBJECT
41 public:
42     InfoWidget(QWidget *parent)
43         : QProgressBar(parent), m_progress(0)
44     {
45         setMinimum(0);
46         setMaximum(100);
47     }
48     QSize sizeHint() const
49     {
50         QSize size(100, 20);
51         return size;
52     }
53 public slots:
54     void startLoad()
55     {
56         setValue(m_progress);
57         show();
58     }
59     void changeLoad(int change)
60     {
61         m_progress = change;
62         setValue(change);
63     }
64     void endLoad()
65     {
66         QTimer::singleShot(1000, this, SLOT(hide()));
67         m_progress = 0;
68     }
69
70 protected:
71     int m_progress;
72 };
73
74 class HoverLabel : public QWidget {
75     Q_OBJECT
76 public:
77     HoverLabel(QWidget *parent=0)
78         : QWidget(parent),
79           m_animating(false),
80           m_percent(0)
81     {
82         m_timer.setInterval(1000/30);
83         m_hideTimer.setInterval(500);
84         m_hideTimer.setSingleShot(true);
85         connect(&m_timer, SIGNAL(timeout()),
86                 this, SLOT(update()));
87         connect(&m_hideTimer, SIGNAL(timeout()),
88                 this, SLOT(hide()));
89     }
90
91 public slots:
92     void setHoverLink(const QString &link) {
93         m_link = link;
94         if (m_link.isEmpty()) {
95             m_hideTimer.start();
96         } else {
97             m_hideTimer.stop();
98             m_oldSize = m_newSize;
99             m_newSize = sizeForFont();
100             resetAnimation();
101             updateSize();
102             show();
103             repaint();
104         }
105     }
106     QSize sizeForFont() const {
107         QFont f = font();
108         QFontMetrics fm(f);
109         return QSize(fm.width(m_link) + 10, fm.height() + 6);
110     }
111     QSize sizeHint() const {
112         if (!m_animating)
113             return sizeForFont();
114         else
115             return (m_newSize.width() > m_oldSize.width()) ? m_newSize : m_oldSize;
116     }
117     void updateSize() {
118         QRect r = geometry();
119         QSize newSize = sizeHint();
120         r = QRect(r.x(), r.y(), newSize.width(), newSize.height());
121         setGeometry(r);
122     }
123     void resetAnimation() {
124         m_animating = true;
125         m_percent = 0;
126         if (!m_timer.isActive())
127             m_timer.start();
128     }
129 protected:
130     void paintEvent(QPaintEvent *e) {
131         QPainter p(this);
132         p.setClipRect(e->rect());
133         p.setPen(QPen(Qt::black, 1));
134         QLinearGradient gradient(rect().topLeft(), rect().bottomLeft());
135         gradient.setColorAt(0, QColor(255, 255, 255, 220));
136         gradient.setColorAt(1, QColor(193, 193, 193, 220));
137         p.setBrush(QBrush(gradient));
138         QSize size;
139         {
140             //draw a nicely rounded corner rectangle. to avoid unwanted
141             // borders we move the coordinates outsize the our clip region
142             size = interpolate(m_oldSize, m_newSize, m_percent);
143             QRect r(-1, 0, size.width(), size.height()+2);
144             const int roundness = 20;
145             QPainterPath path;
146             path.moveTo(r.x(), r.y());
147             path.lineTo(r.topRight().x()-roundness, r.topRight().y());
148             path.cubicTo(r.topRight().x(), r.topRight().y(),
149                          r.topRight().x(), r.topRight().y(),
150                          r.topRight().x(), r.topRight().y() + roundness);
151             path.lineTo(r.bottomRight());
152             path.lineTo(r.bottomLeft());
153             path.closeSubpath();
154             p.setRenderHint(QPainter::Antialiasing);
155             p.drawPath(path);
156         }
157         if (m_animating) {
158             if (qFuzzyCompare(m_percent, 1)) {
159                 m_animating = false;
160                 m_percent = 0;
161                 m_timer.stop();
162             } else {
163                 m_percent += 0.1;
164                 if (m_percent >= 0.99) {
165                     m_percent = 1;
166                 }
167             }
168         }
169
170         QString txt;
171         QFontMetrics fm(fontMetrics());
172         txt = fm.elidedText(m_link, Qt::ElideRight, size.width()-5);
173         p.drawText(5, height()-6, txt);
174     }
175
176 private:
177     QSize interpolate(const QSize &src, const QSize &dst, qreal percent) {
178         int widthDiff  = int((dst.width() - src.width())  * percent);
179         int heightDiff = int((dst.height() - src.height()) * percent);
180         return QSize(src.width()  + widthDiff,
181                      src.height() + heightDiff);
182     }
183     QString m_link;
184     bool    m_animating;
185     QTimer  m_timer;
186     QTimer  m_hideTimer;
187     QSize   m_oldSize;
188     QSize   m_newSize;
189     qreal   m_percent;
190 };
191
192 class SearchEdit;
193
194 class ClearButton : public QPushButton {
195     Q_OBJECT
196 public:
197     ClearButton(QWidget *w)
198         : QPushButton(w)
199     {
200         setMinimumSize(24, 24);
201         setFixedSize(24, 24);
202         setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
203     }
204     void paintEvent(QPaintEvent *event)
205     {
206         Q_UNUSED(event);
207         QPainter painter(this);
208         int height = parentWidget()->geometry().height();
209         int width = height; //parentWidget()->geometry().width();
210
211         painter.setRenderHint(QPainter::Antialiasing, true);
212         painter.setPen(Qt::lightGray);
213         painter.setBrush(isDown() ?
214                          QColor(140, 140, 190) :
215                          underMouse() ? QColor(220, 220, 255) : QColor(200, 200, 230)
216             );
217         painter.drawEllipse(4, 4, width - 8, height - 8);
218         painter.setPen(Qt::white);
219         int border = 8;
220         painter.drawLine(border, border, width - border, height - border);
221         painter.drawLine(border, height - border, width - border, border);
222     }
223 };
224
225 class SearchEdit : public QLineEdit
226 {
227     Q_OBJECT
228 public:
229     SearchEdit(const QString &str, QWidget *parent = 0)
230         : QLineEdit(str, parent)
231     {
232         setMinimumSize(QSize(400, 24));
233         setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
234
235         setStyleSheet(":enabled { padding-right: 27px }");
236         clearButton = new ClearButton(this);
237         clearButton->setGeometry(QRect(geometry().right() - 27,
238                                        geometry().top() - 2,
239                                        geometry().right(), geometry().bottom()));
240         clearButton->setVisible(true);
241 #ifndef QT_NO_CURSOR
242         clearButton->setCursor(Qt::ArrowCursor);
243 #endif
244 #ifndef QT_NO_TOOLTIP
245         clearButton->setToolTip("Clear");
246 #endif
247         connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
248     }
249     ~SearchEdit() { }
250 protected:
251     virtual void paintEvent(QPaintEvent *e) {
252         QLineEdit::paintEvent(e);
253         if(text().isEmpty())
254             clearButton->setVisible(false);
255         else
256             clearButton->setVisible(true);
257     }
258     virtual void resizeEvent(QResizeEvent *) {
259         clearButton->setParent(this);
260         clearButton->setGeometry(QRect(width()-27,
261                                        0,
262                                        24, 24));
263     }
264     virtual void moveEvent(QMoveEvent *) {
265         clearButton->setParent(this);
266         clearButton->setGeometry(QRect(width()-27, 1,
267                                        24, 24));
268     }
269
270     QPushButton *clearButton;
271 };
272
273 class WebPage : public QWebPage
274 {
275 public:
276     inline WebPage(QWidget *parent) : QWebPage(parent) {}
277
278     virtual QWebPage *createWindow();
279 };
280
281 class MainWindow : public QMainWindow
282 {
283     Q_OBJECT
284 public:
285     MainWindow(const QUrl &url = QUrl())
286     {
287         page = new WebPage(this);
288         InfoWidget *info = new InfoWidget(page);
289         info->setGeometry(20, 20, info->sizeHint().width(),
290                           info->sizeHint().height());
291         connect(page, SIGNAL(loadStarted(QWebFrame*)),
292                 info, SLOT(startLoad()));
293         connect(page, SIGNAL(loadProgressChanged(int)),
294                 info, SLOT(changeLoad(int)));
295         connect(page, SIGNAL(loadFinished(QWebFrame*)),
296                 info, SLOT(endLoad()));
297         connect(page, SIGNAL(loadFinished(QWebFrame*)),
298                 this, SLOT(loadFinished()));
299         connect(page, SIGNAL(titleChanged(const QString&)),
300                 this, SLOT(setWindowTitle(const QString&)));
301         connect(page, SIGNAL(hoveringOverLink(const QString&, const QString&)),
302                 this, SLOT(showLinkHover(const QString&, const QString&)));
303
304
305         setCentralWidget(page);
306
307         QToolBar *bar = addToolBar("Navigation");
308         urlEdit = new SearchEdit(url.toString());
309         urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy());
310         connect(urlEdit, SIGNAL(returnPressed()),
311                 SLOT(changeLocation()));
312         bar->addAction(page->action(QWebPage::GoBack));
313         bar->addAction(page->action(QWebPage::Stop));
314         bar->addAction(page->action(QWebPage::GoForward));
315         bar->addSeparator();
316         bar->addAction(page->action(QWebPage::Cut));
317         bar->addAction(page->action(QWebPage::Copy));
318         bar->addAction(page->action(QWebPage::Paste));
319         bar->addSeparator();
320         bar->addAction(page->action(QWebPage::Undo));
321         bar->addAction(page->action(QWebPage::Redo));
322
323         addToolBarBreak();
324         bar = addToolBar("Location");
325         bar->addWidget(new QLabel(tr("Location:")));
326         bar->addWidget(urlEdit);
327
328         hoverLabel = new HoverLabel(this);
329         hoverLabel->hide();
330
331         if (url.isValid())
332             page->open(url);
333
334         info->raise();
335     }
336     inline QWebPage *webPage() const { return page; }
337 protected slots:
338     void changeLocation()
339     {
340         QUrl url(urlEdit->text());
341         page->open(url);
342     }
343     void loadFinished()
344     {
345         urlEdit->setText(page->url().toString());
346     }
347     void showLinkHover(const QString &link, const QString &toolTip)
348     {
349         //statusBar()->showMessage(link);
350         hoverLabel->setHoverLink(link);
351 #ifndef QT_NO_TOOLTIP
352         if (!toolTip.isEmpty())
353             QToolTip::showText(QCursor::pos(), toolTip);
354 #endif
355     }
356 protected:
357     void resizeEvent(QResizeEvent *) {
358         QSize hoverSize = hoverLabel->sizeHint();
359         hoverLabel->setGeometry(0, height()-hoverSize.height(),
360                                 300, hoverSize.height());
361     }
362 private:
363     QWebPage *page;
364     QLineEdit *urlEdit;
365     HoverLabel *hoverLabel;
366 };
367
368 QWebPage *WebPage::createWindow()
369 {
370     MainWindow *mw = new MainWindow;
371     return mw->webPage();
372 }
373
374 #include "main.moc"
375
376 int main(int argc, char **argv)
377 {
378     QString url = QString("%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html"));
379     QApplication app(argc, argv);
380
381     QWebSettings::defaultSettings()->setAttribute(QWebSettings::PluginsEnabled);
382
383     const QStringList args = app.arguments();
384     if (args.count() > 1)
385         url = args.at(1);
386
387     QUrl qurl(url);
388     if (qurl.scheme().isEmpty())
389         qurl = QUrl::fromLocalFile(QFileInfo(url).absoluteFilePath());
390     MainWindow window(qurl);
391     window.show();
392
393     return app.exec();
394 }