Remember reading progress in scrolling mode. Basic nightmode.
[mebook:mebook.git] / ui / bookview.cpp
1 #include "bookview.h"
2
3 #include <QDebug>
4 #include <QAction>
5 #include <QWebElement>
6 #include <QWebFrame>
7 #include <QMouseEvent>
8 #include <QWebSettings>
9 #include <QAbstractKineticScroller>
10
11
12 BookView::BookView(QWidget *parent) : QWebView(parent)
13 {
14     setMouseTracking(false); 
15     viewMode = ScrollingSectionMode; 
16
17     snapEnabled = true;
18
19     setCSSSetting("white-space", "normal");
20     setHTMLAttribute("width", "450");
21     
22 }
23
24 BookView::~BookView()
25 {
26 }
27
28 void BookView::setBook(Book *openBook)
29 {
30     book = openBook;
31     setNightMode(false);
32     if(book){
33         if(!book->isLoaded())
34             book->load();
35         //Clear cache so all images get reloaded. If cache is not cleared we might end up having wrong cover image etc.
36         QWebSettings *settings = page()->settings()->globalSettings();
37         settings->clearMemoryCaches();
38
39         //Using custom QNetworkAccessManager so css and images load properly.
40         QNetworkAccessManager *manager = book->getResourceManager();    
41         if(page()->networkAccessManager() != manager){ 
42             if(manager){
43                 page()->setNetworkAccessManager(manager);
44             }
45         }
46
47         QString content;
48
49         if(book->getProgress().isValid()){
50             if(viewMode==ScrollingSectionMode){
51                 content = book->getSection(book->getProgress().getLink().toInt());        
52                 currentSection = book->getProgress().getLink().toInt();
53                 connect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadingPageFinished(bool)));
54             }else if(viewMode==ScrollingBookMode){
55                 //TODO:Scroll to right spot.
56             }
57         }else if(viewMode == ScrollingBookMode){ 
58             content =  book->getBook();
59         } else if(viewMode == ScrollingSectionMode){
60             content = book->getCover();
61             currentSection = 0;
62         }
63
64         //For some reason a dummy url has to used or else the custom QNetworkAccessManager won't get any calls.
65         if(!content.isEmpty()){
66             setContent(content);
67         }
68
69
70     }
71     update();
72 }
73
74 void BookView::goToSection(int section)
75
76     if(viewMode == ScrollingBookMode){
77         QWebElement element = page()->mainFrame()->findFirstElement("#section"+QString::number(section));
78         int y  = page()->mainFrame()->scrollPosition().y() - element.geometry().y();
79         page()->mainFrame()->scroll(0, -y);
80     } else if(viewMode == ScrollingSectionMode){
81         setContent(book->getSection(section));
82         currentSection = section;
83     }
84 }
85
86 void BookView::goToChapter(QString chapter)
87 {
88     goToSection(book->getSectionNumber(chapter));
89 }
90
91 void BookView::setFont(const QString &family, int size)
92 {
93     QWebSettings *settings = page()->settings()->globalSettings();
94     settings->setFontFamily(QWebSettings::SerifFont, family);
95     settings->setFontFamily(QWebSettings::SansSerifFont, family);
96     settings->setFontFamily(QWebSettings::StandardFont, family);
97     settings->setFontSize(QWebSettings::MinimumFontSize, size);
98 }
99
100 void BookView::setNightMode(bool toggle)
101 {
102     if(toggle){
103         setCSSSetting("color", "#FFFFFF");
104         setCSSSetting("border-color", "#FFFFFF");
105         setCSSSetting("background-color", "#181818");
106     }
107     else{
108         setCSSSetting("color", "#000000");
109         setCSSSetting("border-color", "#000000");
110         setCSSSetting("background-color", "#ececec");
111     }
112     
113 }
114
115 void BookView::mousePressEvent(QMouseEvent *event)
116 {
117     if(viewMode == PageMode || viewMode == ScrollingSectionMode)
118         swipeStart = event->pos();    
119 }
120
121 void BookView::mouseMoveEvent(QMouseEvent *event)
122 {
123     if(viewMode == ScrollingSectionMode){
124        if(!swipeStart.isNull() && event->pos().x() > 0){
125             QWebPage *wpage = page();
126             if(swipeStart.x() - event->pos().x() > 100){
127                 QString chapter = book->getNextSection(currentSection);
128                 setContent(chapter);
129                 swipeStart = QPoint(0, 0);
130             } else if(swipeStart.x() - event->pos().x() < -100){
131                 QString chapter = book->getPreviousSection(currentSection);
132                 setContent(chapter);
133                 swipeStart = QPoint(0, 0);
134             }
135        }
136
137    }
138 }
139
140 void BookView::volumeUpPressed(){
141    if(snapEnabled){
142         previousOffset = page()->mainFrame()->scrollPosition();
143         int max = page()->mainFrame()->contentsSize().height() - page()->viewportSize().height();
144         if(previousOffset.y() < max){
145             int jump = page()->viewportSize().height();
146
147             page()->mainFrame()->setScrollPosition(previousOffset + QPoint(0, jump));
148         }else{
149             QString chapter = book->getNextSection(currentSection);
150             setContent(chapter);
151
152         }
153    }
154 }
155
156 void BookView::volumeDownPressed()
157 {
158    if(snapEnabled){
159         previousOffset = page()->mainFrame()->scrollPosition();
160         if(previousOffset.y() != 0){
161             int jump = page()->viewportSize().height();
162             page()->mainFrame()->setScrollPosition(previousOffset + QPoint(0, -jump));
163         }else{
164             QString chapter = book->getPreviousSection(currentSection);
165             setContent(chapter);
166         }
167    }
168 }
169
170
171
172 QList<QWebElement> BookView::hitElements(const QLine &line)
173 {
174     QList<QWebElement> hitElements;
175
176     const int points = 30;
177     QPoint delta(line.dx() / points, line.dy() / points);
178     
179     QPoint point = line.p1();
180     for(int i = 0; i < points -1; ++i)
181     {
182         point += delta;
183         QWebHitTestResult hit = page()->mainFrame()->hitTestContent(point);
184         if(!hit.element().isNull())
185             hitElements.push_back(hit.element());
186     }
187
188     return hitElements;
189 }
190
191 void BookView::setContent(QString content)
192 {
193     parseLinks(content);
194     page()->mainFrame()->setHtml(content, QUrl(dummyURL));
195     makeDOMChangesEffective();
196 }
197
198 //Urls in text cause trouble because they can't be reflowed
199 //TODO: Replace removed url with a link
200 void BookView::parseLinks(QString &content)
201 {
202     content = content.replace(QRegExp("[\\[\\s:]\\http://[\\]/#.a-z0-9]*"), "<a href=#>(link)</a>");
203 }
204
205
206
207 void BookView::setCSSSetting(QString setting, QString value)
208 {
209     cssSettings.insert(setting, value);
210 }
211
212 void BookView::setHTMLAttribute(QString attribute, QString value)
213 {
214     htmlAttributes.insert(attribute, value);
215 }
216
217 void BookView::makeDOMChangesEffective()
218 {
219     QWebElement documentElement = page()->mainFrame()->documentElement();
220     //Change DOM
221     setDOMElementSettings(documentElement);
222     page()->mainFrame()->setHtml(documentElement.toOuterXml(), QUrl(dummyURL));
223 }
224
225 void BookView::setDOMElementSettings(QWebElement &parentElement)
226 {
227     QWebElement element = parentElement.firstChild(); 
228     while(!element.isNull())
229     {
230         //CSS
231         for(QHash<QString, QString>::iterator iter = cssSettings.begin(); iter != cssSettings.end(); ++iter){
232             element.setStyleProperty(iter.key(), (*iter));
233         }
234
235         //HTML attributes
236         for(QHash<QString, QString>::iterator iter = htmlAttributes.begin(); iter != htmlAttributes.end(); ++iter){
237             if(element.hasAttribute(iter.key()))
238                 element.setAttribute(iter.key(), *iter);
239         }
240         setDOMElementSettings(element);
241         element = element.nextSibling();
242     }
243 }
244
245 //Progress saved by sectionnumber, 
246 void BookView::saveProgress()
247 {
248     QList<QWebElement> elements = hitElements(QLine(1, 1, page()->viewportSize().width() -1, page()->viewportSize().height()));
249     QString link;
250     if(viewMode == ScrollingSectionMode)
251         link = QString::number(currentSection);
252
253     QString element;
254     QString text;
255     if(!elements.isEmpty()){
256         QWebElement webElement;
257         for(QList<QWebElement>::iterator iter = elements.begin(); iter != elements.end(); ++iter){
258             if(!(*iter).isNull() && (*iter).localName() != "html" && (*iter).localName() != "body"){
259                 webElement = (*iter);
260                 break;
261             }
262         }
263         if(!webElement.classes().isEmpty())
264             element = webElement.classes().first();
265         else
266             element = webElement.localName();
267         text = webElement.toPlainText();
268         QStringList tlist = text.split(" ");
269         if(tlist.count() > 3)
270             text = tlist[0] + tlist[1] + tlist[2];
271
272     }
273
274     int scrollPosition = page()->mainFrame()->scrollPosition().y();
275
276     BookProgress progress;
277     progress.setProgress(link, element, text, scrollPosition);
278     book->setProgress(progress);
279
280 }
281
282 void BookView::loadingPageFinished(bool ok)
283 {
284     QAbstractKineticScroller *scroller = property("kineticScroller").value<QAbstractKineticScroller*>();
285     scroller->setMinimumVelocity(10.0);
286     scroller->scrollTo(QPoint(0, book->getProgress().getScroll()));
287     disconnect(this, SIGNAL(loadFinished(bool)), this, SLOT(loadingPageFinished(bool)));
288 }