Simon Hausmann <hausmann@webkit.org>
[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 <qwebview.h>
32 #include <qwebframe.h>
33 #include <qwebsettings.h>
34
35 #include <QtGui>
36 #include <QDebug>
37 #if QT_VERSION >= 0x040400
38 #include <QPrintPreviewDialog>
39 #endif
40
41
42 class InfoWidget :public QProgressBar {
43     Q_OBJECT
44 public:
45     InfoWidget(QWidget *parent)
46         : QProgressBar(parent), m_progress(0)
47     {
48         setMinimum(0);
49         setMaximum(100);
50     }
51     QSize sizeHint() const
52     {
53         QSize size(100, 20);
54         return size;
55     }
56 public slots:
57     void startLoad()
58     {
59         setValue(m_progress);
60         show();
61     }
62     void changeLoad(int change)
63     {
64         m_progress = change;
65         setValue(change);
66     }
67     void endLoad()
68     {
69         QTimer::singleShot(1000, this, SLOT(hide()));
70         m_progress = 0;
71     }
72
73 protected:
74     int m_progress;
75 };
76
77 class HoverLabel : public QWidget {
78     Q_OBJECT
79 public:
80     HoverLabel(QWidget *parent=0)
81         : QWidget(parent),
82           m_animating(false),
83           m_percent(0)
84     {
85         m_timer.setInterval(1000/30);
86         m_hideTimer.setInterval(500);
87         m_hideTimer.setSingleShot(true);
88         connect(&m_timer, SIGNAL(timeout()),
89                 this, SLOT(update()));
90         connect(&m_hideTimer, SIGNAL(timeout()),
91                 this, SLOT(hide()));
92     }
93
94 public slots:
95     void setHoverLink(const QString &link) {
96         m_link = link;
97         if (m_link.isEmpty()) {
98             m_hideTimer.start();
99         } else {
100             m_hideTimer.stop();
101             m_oldSize = m_newSize;
102             m_newSize = sizeForFont();
103             resetAnimation();
104             updateSize();
105             show();
106             repaint();
107         }
108     }
109     QSize sizeForFont() const {
110         QFont f = font();
111         QFontMetrics fm(f);
112         return QSize(fm.width(m_link) + 10, fm.height() + 6);
113     }
114     QSize sizeHint() const {
115         if (!m_animating)
116             return sizeForFont();
117         else
118             return (m_newSize.width() > m_oldSize.width()) ? m_newSize : m_oldSize;
119     }
120     void updateSize() {
121         QRect r = geometry();
122         QSize newSize = sizeHint();
123         r = QRect(r.x(), r.y(), newSize.width(), newSize.height());
124         setGeometry(r);
125     }
126     void resetAnimation() {
127         m_animating = true;
128         m_percent = 0;
129         if (!m_timer.isActive())
130             m_timer.start();
131     }
132 protected:
133     void paintEvent(QPaintEvent *e) {
134         QPainter p(this);
135         p.setClipRect(e->rect());
136         p.setPen(QPen(Qt::black, 1));
137         QLinearGradient gradient(rect().topLeft(), rect().bottomLeft());
138         gradient.setColorAt(0, QColor(255, 255, 255, 220));
139         gradient.setColorAt(1, QColor(193, 193, 193, 220));
140         p.setBrush(QBrush(gradient));
141         QSize size;
142         {
143             //draw a nicely rounded corner rectangle. to avoid unwanted
144             // borders we move the coordinates outsize the our clip region
145             size = interpolate(m_oldSize, m_newSize, m_percent);
146             QRect r(-1, 0, size.width(), size.height()+2);
147             const int roundness = 20;
148             QPainterPath path;
149             path.moveTo(r.x(), r.y());
150             path.lineTo(r.topRight().x()-roundness, r.topRight().y());
151             path.cubicTo(r.topRight().x(), r.topRight().y(),
152                          r.topRight().x(), r.topRight().y(),
153                          r.topRight().x(), r.topRight().y() + roundness);
154             path.lineTo(r.bottomRight());
155             path.lineTo(r.bottomLeft());
156             path.closeSubpath();
157             p.setRenderHint(QPainter::Antialiasing);
158             p.drawPath(path);
159         }
160         if (m_animating) {
161             if (qFuzzyCompare(m_percent, 1)) {
162                 m_animating = false;
163                 m_percent = 0;
164                 m_timer.stop();
165             } else {
166                 m_percent += 0.1;
167                 if (m_percent >= 0.99) {
168                     m_percent = 1;
169                 }
170             }
171         }
172
173         QString txt;
174         QFontMetrics fm(fontMetrics());
175         txt = fm.elidedText(m_link, Qt::ElideRight, size.width()-5);
176         p.drawText(5, height()-6, txt);
177     }
178
179 private:
180     QSize interpolate(const QSize &src, const QSize &dst, qreal percent) {
181         int widthDiff  = int((dst.width() - src.width())  * percent);
182         int heightDiff = int((dst.height() - src.height()) * percent);
183         return QSize(src.width()  + widthDiff,
184                      src.height() + heightDiff);
185     }
186     QString m_link;
187     bool    m_animating;
188     QTimer  m_timer;
189     QTimer  m_hideTimer;
190     QSize   m_oldSize;
191     QSize   m_newSize;
192     qreal   m_percent;
193 };
194
195 class SearchEdit;
196
197 class ClearButton : public QPushButton {
198     Q_OBJECT
199 public:
200     ClearButton(QWidget *w)
201         : QPushButton(w)
202     {
203         setMinimumSize(24, 24);
204         setFixedSize(24, 24);
205         setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
206     }
207     void paintEvent(QPaintEvent *event)
208     {
209         Q_UNUSED(event);
210         QPainter painter(this);
211         int height = parentWidget()->geometry().height();
212         int width = height; //parentWidget()->geometry().width();
213
214         painter.setRenderHint(QPainter::Antialiasing, true);
215         painter.setPen(hasFocus() ? Qt::darkGray : Qt::lightGray);
216         painter.setBrush(isDown() ?
217                          QColor(140, 140, 190) :
218                          underMouse() ? QColor(220, 220, 255) : QColor(200, 200, 230)
219             );
220         painter.drawEllipse(4, 4, width - 8, height - 8);
221         painter.setPen(Qt::white);
222         int border = 8;
223         painter.drawLine(border, border, width - border, height - border);
224         painter.drawLine(border, height - border, width - border, border);
225     }
226 };
227
228 class SearchEdit : public QLineEdit
229 {
230     Q_OBJECT
231 public:
232     SearchEdit(const QString &str, QWidget *parent = 0)
233         : QLineEdit(str, parent)
234     {
235         setMinimumSize(QSize(400, 24));
236         setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
237
238         setStyleSheet(":enabled { padding-right: 27px }");
239         clearButton = new ClearButton(this);
240         clearButton->setGeometry(QRect(geometry().right() - 27,
241                                        geometry().top() - 2,
242                                        geometry().right(), geometry().bottom()));
243         clearButton->setVisible(true);
244 #ifndef QT_NO_CURSOR
245         clearButton->setCursor(Qt::ArrowCursor);
246 #endif
247 #ifndef QT_NO_TOOLTIP
248         clearButton->setToolTip("Clear");
249 #endif
250         connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
251     }
252     ~SearchEdit() { }
253 protected:
254     virtual void paintEvent(QPaintEvent *e) {
255         QLineEdit::paintEvent(e);
256         if(text().isEmpty())
257             clearButton->setVisible(false);
258         else
259             clearButton->setVisible(true);
260     }
261     virtual void resizeEvent(QResizeEvent *) {
262         clearButton->setParent(this);
263         clearButton->setGeometry(QRect(width()-27,
264                                        0,
265                                        24, 24));
266     }
267     virtual void moveEvent(QMoveEvent *) {
268         clearButton->setParent(this);
269         clearButton->setGeometry(QRect(width()-27, 1,
270                                        24, 24));
271     }
272
273     QPushButton *clearButton;
274 };
275
276 class WebPage : public QWebPage
277 {
278 public:
279     inline WebPage(QWidget *parent) : QWebPage(parent) {}
280
281     virtual QWebPage *createWindow(QWebPage::WebWindowType);
282 };
283
284 class MainWindow : public QMainWindow
285 {
286     Q_OBJECT
287 public:
288     MainWindow(const QUrl &url = QUrl())
289     {
290         view = new QWebView(this);
291         view->setPage(new WebPage(view));
292         info = new InfoWidget(this);
293
294         connect(view, SIGNAL(loadStarted()),
295                 info, SLOT(startLoad()));
296         connect(view, SIGNAL(loadProgress(int)),
297                 info, SLOT(changeLoad(int)));
298         connect(view, SIGNAL(loadFinished()),
299                 info, SLOT(endLoad()));
300         connect(view, SIGNAL(loadFinished()),
301                 this, SLOT(loadFinished()));
302         connect(view, SIGNAL(titleChanged(const QString&)),
303                 this, SLOT(setWindowTitle(const QString&)));
304         connect(view->page(), SIGNAL(linkHovered(const QString&, const QString&, const QString &)),
305                 this, SLOT(showLinkHover(const QString&, const QString&)));
306         connect(view->page(), SIGNAL(windowCloseRequested()), this, SLOT(deleteLater()));
307
308
309         setCentralWidget(view);
310
311         QToolBar *bar = addToolBar("Navigation");
312         urlEdit = new SearchEdit(url.toString());
313         urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy());
314         connect(urlEdit, SIGNAL(returnPressed()),
315                 SLOT(changeLocation()));
316         bar->addAction(view->pageAction(QWebPage::Back));
317         bar->addAction(view->pageAction(QWebPage::Stop));
318         bar->addAction(view->pageAction(QWebPage::Forward));
319         QAction* reloadAction = view->pageAction(QWebPage::Reload);
320         reloadAction->setShortcut(QKeySequence::Refresh);
321         bar->addAction(reloadAction);
322         bar->addSeparator();
323         bar->addAction(view->pageAction(QWebPage::Cut));
324         bar->addAction(view->pageAction(QWebPage::Copy));
325         bar->addAction(view->pageAction(QWebPage::Paste));
326         bar->addSeparator();
327         bar->addAction(view->pageAction(QWebPage::Undo));
328         bar->addAction(view->pageAction(QWebPage::Redo));
329
330 #if QT_VERSION >= 0x040400
331         bar->addSeparator();
332         bar->addAction(tr("Print"), this, SLOT(print()));
333 #endif
334
335         addToolBarBreak();
336         bar = addToolBar("Location");
337         bar->addWidget(new QLabel(tr("Location:")));
338         bar->addWidget(urlEdit);
339
340         hoverLabel = new HoverLabel(this);
341         hoverLabel->hide();
342
343         if (url.isValid())
344             view->load(url);
345
346         info->raise();
347     }
348     inline QWebPage *webPage() const { return view->page(); }
349 protected slots:
350     void changeLocation()
351     {
352         QUrl url(urlEdit->text());
353         view->load(url);
354         view->setFocus(Qt::OtherFocusReason);
355     }
356     void loadFinished()
357     {
358         urlEdit->setText(view->url().toString());
359     }
360     void showLinkHover(const QString &link, const QString &toolTip)
361     {
362         //statusBar()->showMessage(link);
363         hoverLabel->setHoverLink(link);
364 #ifndef QT_NO_TOOLTIP
365         if (!toolTip.isEmpty())
366             QToolTip::showText(QCursor::pos(), toolTip);
367 #endif
368     }
369     void print()
370     {
371 #if QT_VERSION >= 0x040400
372         QPrintPreviewDialog dlg(this);
373         connect(&dlg, SIGNAL(paintRequested(QPrinter *)),
374                 view, SLOT(print(QPrinter *)));
375         dlg.exec();
376 #endif
377     }
378 protected:
379     void resizeEvent(QResizeEvent *) {
380         QSize hoverSize = hoverLabel->sizeHint();
381         hoverLabel->setGeometry(0, height()-hoverSize.height(),
382                                 300, hoverSize.height());
383         QSize infoSize = info->sizeHint();
384         info->setGeometry(width() - infoSize.width(),
385                 height() - infoSize.height(),
386                 infoSize.width(), infoSize.height());
387     }
388 private:
389     QWebView *view;
390     QLineEdit *urlEdit;
391     HoverLabel *hoverLabel;
392     InfoWidget *info;
393 };
394
395 QWebPage *WebPage::createWindow(QWebPage::WebWindowType)
396 {
397     MainWindow *mw = new MainWindow;
398     return mw->webPage();
399 }
400
401 #include "main.moc"
402
403 int main(int argc, char **argv)
404 {
405     QApplication app(argc, argv);
406     QString url = QString("%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html"));
407
408     QWebSettings::setMaximumPagesInCache(4);
409
410     app.setApplicationName("QtLauncher");
411     app.setApplicationVersion("0.1");
412
413     QWebSettings::setObjectCacheCapacities((16*1024*1024)/8, (16*1024*1024)/8, 16*1024*1024);
414
415     QWebSettings::globalSettings()->setAttribute(QWebSettings::PluginsEnabled, true);
416     QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
417
418     const QStringList args = app.arguments();
419     if (args.count() > 1)
420         url = args.at(1);
421
422     QUrl qurl(url);
423     if (qurl.scheme().isEmpty())
424         qurl = QUrl::fromLocalFile(QFileInfo(url).absoluteFilePath());
425     MainWindow window(qurl);
426     window.show();
427
428     return app.exec();
429 }