Mercurial > projects > qtd
comparison demos/browser/history.d @ 65:a62227112f5b
more porting
author | mandel |
---|---|
date | Tue, 19 May 2009 17:58:33 +0000 |
parents | 71b382c10ef6 |
children | 3b98e3fecd9b |
comparison
equal
deleted
inserted
replaced
64:753b1973273b | 65:a62227112f5b |
---|---|
38 ** $QT_END_LICENSE$ | 38 ** $QT_END_LICENSE$ |
39 ** | 39 ** |
40 ****************************************************************************/ | 40 ****************************************************************************/ |
41 module history; | 41 module history; |
42 | 42 |
43 #include "autosaver.h" | |
44 #include "browserapplication.h" | |
45 | 43 |
46 import QtCore.QBuffer; | 44 import QtCore.QBuffer; |
47 import QtCore.QDir; | 45 import QtCore.QDir; |
48 import QtCore.QFile; | 46 import QtCore.QFile; |
49 import QtCore.QFileInfo; | 47 import QtCore.QFileInfo; |
61 import QtWebKit.QWebHistoryInterface; | 59 import QtWebKit.QWebHistoryInterface; |
62 import QtWebKit.QWebSettings; | 60 import QtWebKit.QWebSettings; |
63 | 61 |
64 import QtCore.QDebug; | 62 import QtCore.QDebug; |
65 | 63 |
66 | |
67 #include "modelmenu.h" | |
68 | |
69 import QtCore.QDateTime; | 64 import QtCore.QDateTime; |
70 import QtCore.QHash; | 65 import QtCore.QHash; |
71 import QtCore.QObject; | 66 import QtCore.QObject; |
72 import QtCore.QTimer; | 67 import QtCore.QTimer; |
73 import QtCore.QUrl; | 68 import QtCore.QUrl; |
74 | 69 |
75 import QtGui.QSortFilterProxyModel; | 70 import QtGui.QSortFilterProxyModel; |
76 | 71 |
77 import QWebHistoryInterface; | 72 import QWebHistoryInterface; |
78 | 73 |
74 | |
75 import autosaver | |
76 import browserapplication; | |
77 import modelmenu; | |
78 | |
79 | |
79 static const unsigned int HISTORY_VERSION = 23; | 80 static const unsigned int HISTORY_VERSION = 23; |
80 | 81 |
81 | 82 |
82 class HistoryItem | 83 class HistoryItem |
83 { | 84 { |
84 public: | 85 public: |
85 this() {} | 86 |
86 this(const QString &u, const QDateTime &d = QDateTime(), const QString &t = QString()) | 87 this() {} |
87 { | 88 |
88 title = t; | 89 this(QString u, QDateTime d = QDateTime(), QString t = QString()) |
89 url = u; | 90 { |
90 dateTime = d; | 91 title = t; |
91 } | 92 url = u; |
92 | 93 dateTime = d; |
93 inline bool operator==(const HistoryItem &other) | 94 } |
94 { return other.title == title | 95 |
95 && other.url == url && other.dateTime == dateTime; | 96 //bool operator==(HistoryItem &other) |
96 } | 97 int opEquals(HistoryItem other) |
97 | 98 { |
98 // history is sorted in reverse | 99 return cast(int) (other.title == title && other.url == url && other.dateTime == dateTime); |
99 inline bool operator <(const HistoryItem &other) | 100 } |
100 { return dateTime > other.dateTime; } | 101 |
101 | 102 // history is sorted in reverse |
102 QString title; | 103 //bool operator <(HistoryItem &other) |
103 QString url; | 104 int opCmp(HistoryItem other) |
104 QDateTime dateTime; | 105 { |
106 return cast(int) (dateTime > other.dateTime); | |
107 } | |
108 | |
109 QString title; | |
110 QString url; | |
111 QDateTime dateTime; | |
105 } | 112 } |
106 | 113 |
107 /* | |
108 class AutoSaver; | |
109 class HistoryModel; | |
110 class HistoryFilterModel; | |
111 class HistoryTreeModel; | |
112 */ | |
113 | 114 |
114 class HistoryManager : public QWebHistoryInterface | 115 class HistoryManager : public QWebHistoryInterface |
115 { | 116 { |
116 Q_OBJECT | 117 //Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) |
117 Q_PROPERTY(int historyLimit READ historyLimit WRITE setHistoryLimit) | 118 |
118 | 119 mixin Signal!("historyReset"); |
119 signals: | 120 mixin Signal!("entryAdded", HistoryItem item); |
120 void historyReset(); | 121 mixin Signal!("entryRemoved", HistoryItem item); |
121 void entryAdded(const HistoryItem &item); | 122 mixin Signal!("entryUpdated", int offset); |
122 void entryRemoved(const HistoryItem &item); | |
123 void entryUpdated(int offset); | |
124 | 123 |
125 public: | 124 public: |
126 | 125 |
127 this(QObject *parent = null) | 126 this(QObject parent = null) |
128 { | 127 { |
129 super(parent); | 128 super(parent); |
130 m_saveTimer = new AutoSaver(this); | 129 m_saveTimer = new AutoSaver(this); |
131 m_historyLimit = 30; | 130 m_historyLimit = 30; |
132 m_historyModel = 0; | 131 m_historyModel = 0; |
133 m_historyFilterModel = 0; | 132 m_historyFilterModel = 0; |
134 m_historyTreeModel = 0; | 133 m_historyTreeModel = 0; |
135 | 134 |
136 m_expiredTimer.setSingleShot(true); | 135 m_expiredTimer.setSingleShot(true); |
137 connect(&m_expiredTimer, SIGNAL(timeout()), | 136 |
138 this, SLOT(checkForExpired())); | 137 m_expiredTimer.timeout.connect(&this.checkForExpired); |
139 connect(this, SIGNAL(entryAdded(const HistoryItem &)), | 138 this.entryAdded.connect(&m_saveTimer.changeOccurred); |
140 m_saveTimer, SLOT(changeOccurred())); | 139 this.entryRemoved.connect(&m_saveTimer.changeOccurred); |
141 connect(this, SIGNAL(entryRemoved(const HistoryItem &)), | 140 |
142 m_saveTimer, SLOT(changeOccurred())); | |
143 load(); | 141 load(); |
144 | 142 |
145 m_historyModel = new HistoryModel(this, this); | 143 m_historyModel = new HistoryModel(this, this); |
146 m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); | 144 m_historyFilterModel = new HistoryFilterModel(m_historyModel, this); |
147 m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); | 145 m_historyTreeModel = new HistoryTreeModel(m_historyFilterModel, this); |
148 | 146 |
149 // QWebHistoryInterface will delete the history manager | 147 // QWebHistoryInterface will delete the history manager |
150 QWebHistoryInterface::setDefaultInterface(this); | 148 QWebHistoryInterface.setDefaultInterface(this); |
151 } | 149 } |
152 | 150 |
153 | 151 |
154 ~this() | 152 ~this() |
155 { | 153 { |
156 m_saveTimer.saveIfNeccessary(); | 154 m_saveTimer.saveIfNeccessary(); |
157 } | 155 } |
158 | 156 |
159 | 157 |
160 bool historyContains(const QString &url) | 158 bool historyContains(QString &url) |
161 { | 159 { |
162 return m_historyFilterModel.historyContains(url); | 160 return m_historyFilterModel.historyContains(url); |
163 } | 161 } |
164 | 162 |
165 void addHistoryEntry(QString &url) | 163 void addHistoryEntry(QString &url) |
166 { | 164 { |
167 QUrl cleanUrl(url); | 165 QUrl cleanUrl(url); |
168 cleanUrl.setPassword(QString()); | 166 cleanUrl.setPassword(QString()); |
169 cleanUrl.setHost(cleanUrl.host().toLower()); | 167 cleanUrl.setHost(cleanUrl.host().toLower()); |
170 HistoryItem item(cleanUrl.toString(), QDateTime::currentDateTime()); | 168 auto item = new HistoryItem(cleanUrl.toString(), QDateTime.currentDateTime()); |
171 addHistoryItem(item); | 169 addHistoryItem(item); |
172 } | 170 } |
173 | 171 |
174 void updateHistoryItem(QUrl &url, QString &title) | 172 void updateHistoryItem(QUrl &url, QString &title) |
175 { | 173 { |
226 emit historyReset(); | 224 emit historyReset(); |
227 } | 225 } |
228 | 226 |
229 | 227 |
230 // History manager keeps around these models for use by the completer and other classes | 228 // History manager keeps around these models for use by the completer and other classes |
231 HistoryModel *historyModel(); | 229 HistoryModel historyModel(); |
232 { | 230 { |
233 return m_historyModel; | 231 return m_historyModel; |
234 } | 232 } |
235 | 233 |
236 | 234 |
237 HistoryFilterModel *historyFilterModel() | 235 HistoryFilterModel historyFilterModel() |
238 { | 236 { |
239 return m_historyFilterModel; | 237 return m_historyFilterModel; |
240 } | 238 } |
241 | 239 |
242 | 240 |
243 HistoryTreeModel *historyTreeModel() | 241 HistoryTreeModel historyTreeModel() |
244 { | 242 { |
245 return m_historyTreeModel; | 243 return m_historyTreeModel; |
246 } | 244 } |
247 | 245 |
248 | 246 |
263 QSettings settings; | 261 QSettings settings; |
264 settings.beginGroup(QLatin1String("history")); | 262 settings.beginGroup(QLatin1String("history")); |
265 m_historyLimit = settings.value(QLatin1String("historyLimit"), 30).toInt(); | 263 m_historyLimit = settings.value(QLatin1String("historyLimit"), 30).toInt(); |
266 } | 264 } |
267 | 265 |
268 private slots: | |
269 void save() | |
270 { | |
271 QSettings settings; | |
272 settings.beginGroup(QLatin1String("history")); | |
273 settings.setValue(QLatin1String("historyLimit"), m_historyLimit); | |
274 | |
275 bool saveAll = m_lastSavedUrl.isEmpty(); | |
276 int first = m_history.count() - 1; | |
277 if (!saveAll) { | |
278 // find the first one to save | |
279 for (int i = 0; i < m_history.count(); ++i) { | |
280 if (m_history.at(i).url == m_lastSavedUrl) { | |
281 first = i - 1; | |
282 break; | |
283 } | |
284 } | |
285 } | |
286 if (first == m_history.count() - 1) | |
287 saveAll = true; | |
288 | |
289 QString directory = QDesktopServices::storageLocation(QDesktopServices::DataLocation); | |
290 if (directory.isEmpty()) | |
291 directory = QDir::homePath() + QLatin1String("/.") + QCoreApplication::applicationName(); | |
292 if (!QFile::exists(directory)) { | |
293 QDir dir; | |
294 dir.mkpath(directory); | |
295 } | |
296 | |
297 QFile historyFile(directory + QLatin1String("/history")); | |
298 // When saving everything use a temporary file to prevent possible data loss. | |
299 QTemporaryFile tempFile; | |
300 tempFile.setAutoRemove(false); | |
301 bool open = false; | |
302 if (saveAll) { | |
303 open = tempFile.open(); | |
304 } else { | |
305 open = historyFile.open(QFile::Append); | |
306 } | |
307 | |
308 if (!open) { | |
309 qWarning() << "Unable to open history file for saving" | |
310 << (saveAll ? tempFile.fileName() : historyFile.fileName()); | |
311 return; | |
312 } | |
313 | |
314 QDataStream out(saveAll ? &tempFile : &historyFile); | |
315 for (int i = first; i >= 0; --i) { | |
316 QByteArray data; | |
317 QDataStream stream(&data, QIODevice::WriteOnly); | |
318 HistoryItem item = m_history.at(i); | |
319 stream << HISTORY_VERSION << item.url << item.dateTime << item.title; | |
320 out << data; | |
321 } | |
322 tempFile.close(); | |
323 | |
324 if (saveAll) { | |
325 if (historyFile.exists() && !historyFile.remove()) | |
326 qWarning() << "History: error removing old history." << historyFile.errorString(); | |
327 if (!tempFile.rename(historyFile.fileName())) | |
328 qWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); | |
329 } | |
330 m_lastSavedUrl = m_history.value(0).url; | |
331 } | |
332 | |
333 void checkForExpired() | |
334 { | |
335 if (m_historyLimit < 0 || m_history.isEmpty()) | |
336 return; | |
337 | |
338 QDateTime now = QDateTime::currentDateTime(); | |
339 int nextTimeout = 0; | |
340 | |
341 while (!m_history.isEmpty()) { | |
342 QDateTime checkForExpired = m_history.last().dateTime; | |
343 checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); | |
344 if (now.daysTo(checkForExpired) > 7) { | |
345 // check at most in a week to prevent int overflows on the timer | |
346 nextTimeout = 7 * 86400; | |
347 } else { | |
348 nextTimeout = now.secsTo(checkForExpired); | |
349 } | |
350 if (nextTimeout > 0) | |
351 break; | |
352 HistoryItem item = m_history.takeLast(); | |
353 // remove from saved file also | |
354 m_lastSavedUrl = QString(); | |
355 emit entryRemoved(item); | |
356 } | |
357 | |
358 if (nextTimeout > 0) | |
359 m_expiredTimer.start(nextTimeout * 1000); | |
360 } | |
361 | |
362 protected: | |
363 void addHistoryItem(HistoryItem &item) | |
364 { | |
365 QWebSettings *globalSettings = QWebSettings::globalSettings(); | |
366 if (globalSettings.testAttribute(QWebSettings::PrivateBrowsingEnabled)) | |
367 return; | |
368 | |
369 m_history.prepend(item); | |
370 emit entryAdded(item); | |
371 if (m_history.count() == 1) | |
372 checkForExpired(); | |
373 } | |
374 | |
375 private: | 266 private: |
376 | 267 |
377 void load() | 268 void save() |
378 { | 269 { |
379 loadSettings(); | 270 QSettings settings; |
380 | 271 settings.beginGroup(QLatin1String("history")); |
381 QFile historyFile(QDesktopServices::storageLocation(QDesktopServices::DataLocation) | 272 settings.setValue(QLatin1String("historyLimit"), m_historyLimit); |
382 + QLatin1String("/history")); | 273 |
383 if (!historyFile.exists()) | 274 bool saveAll = m_lastSavedUrl.isEmpty(); |
384 return; | 275 int first = m_history.count() - 1; |
385 if (!historyFile.open(QFile::ReadOnly)) { | 276 if (!saveAll) { |
386 qWarning() << "Unable to open history file" << historyFile.fileName(); | 277 // find the first one to save |
387 return; | 278 for (int i = 0; i < m_history.count(); ++i) { |
388 } | 279 if (m_history.at(i).url == m_lastSavedUrl) { |
389 | 280 first = i - 1; |
390 QList<HistoryItem> list; | 281 break; |
391 QDataStream in(&historyFile); | 282 } |
392 // Double check that the history file is sorted as it is read in | 283 } |
393 bool needToSort = false; | 284 } |
394 HistoryItem lastInsertedItem; | 285 if (first == m_history.count() - 1) |
395 QByteArray data; | 286 saveAll = true; |
396 QDataStream stream; | 287 |
397 QBuffer buffer; | 288 QString directory = QDesktopServices.storageLocation(QDesktopServices.DataLocation); |
398 stream.setDevice(&buffer); | 289 if (directory.isEmpty()) |
399 while (!historyFile.atEnd()) { | 290 directory = QDir.homePath() + QLatin1String("/.") + QCoreApplication.applicationName(); |
400 in >> data; | 291 if (!QFile.exists(directory)) { |
401 buffer.close(); | 292 QDir dir; |
402 buffer.setBuffer(&data); | 293 dir.mkpath(directory); |
403 buffer.open(QIODevice::ReadOnly); | 294 } |
404 quint32 ver; | 295 |
405 stream >> ver; | 296 QFile historyFile(directory + QLatin1String("/history")); |
406 if (ver != HISTORY_VERSION) | 297 // When saving everything use a temporary file to prevent possible data loss. |
407 continue; | 298 QTemporaryFile tempFile; |
408 HistoryItem item; | 299 tempFile.setAutoRemove(false); |
409 stream >> item.url; | 300 bool open = false; |
410 stream >> item.dateTime; | 301 if (saveAll) { |
411 stream >> item.title; | 302 open = tempFile.open(); |
412 | 303 } else { |
413 if (!item.dateTime.isValid()) | 304 open = historyFile.open(QFile.Append); |
414 continue; | 305 } |
415 | 306 |
416 if (item == lastInsertedItem) { | 307 if (!open) { |
417 if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) | 308 qWarning() << "Unable to open history file for saving" |
418 list[0].title = item.title; | 309 << (saveAll ? tempFile.fileName() : historyFile.fileName()); |
419 continue; | 310 return; |
420 } | 311 } |
421 | 312 |
422 if (!needToSort && !list.isEmpty() && lastInsertedItem < item) | 313 QDataStream out_ = (saveAll ? tempFile : historyFile); |
423 needToSort = true; | 314 for (int i = first; i >= 0; --i) { |
424 | 315 QByteArray data; |
425 list.prepend(item); | 316 auto stream = new QDataStream(data, QIODevice.WriteOnly); |
426 lastInsertedItem = item; | 317 HistoryItem item = m_history.at(i); |
427 } | 318 stream << HISTORY_VERSION << item.url << item.dateTime << item.title; |
428 if (needToSort) | 319 out_ << data; |
429 qSort(list.begin(), list.end()); | 320 } |
430 | 321 tempFile.close(); |
431 setHistory(list, true); | 322 |
432 | 323 if (saveAll) { |
433 // If we had to sort re-write the whole history sorted | 324 if (historyFile.exists() && !historyFile.remove()) |
434 if (needToSort) { | 325 qWarning() << "History: error removing old history." << historyFile.errorString(); |
435 m_lastSavedUrl = QString(); | 326 if (!tempFile.rename(historyFile.fileName())) |
436 m_saveTimer.changeOccurred(); | 327 qWarning() << "History: error moving new history over old." << tempFile.errorString() << historyFile.fileName(); |
437 } | 328 } |
329 m_lastSavedUrl = m_history.value(0).url; | |
330 } | |
331 | |
332 void checkForExpired() | |
333 { | |
334 if (m_historyLimit < 0 || m_history.isEmpty()) | |
335 return; | |
336 | |
337 QDateTime now = QDateTime.currentDateTime(); | |
338 int nextTimeout = 0; | |
339 | |
340 while (!m_history.isEmpty()) { | |
341 QDateTime checkForExpired = m_history.last().dateTime; | |
342 checkForExpired.setDate(checkForExpired.date().addDays(m_historyLimit)); | |
343 if (now.daysTo(checkForExpired) > 7) { | |
344 // check at most in a week to prevent int overflows on the timer | |
345 nextTimeout = 7 * 86400; | |
346 } else { | |
347 nextTimeout = now.secsTo(checkForExpired); | |
348 } | |
349 if (nextTimeout > 0) | |
350 break; | |
351 HistoryItem item = m_history.takeLast(); | |
352 // remove from saved file also | |
353 m_lastSavedUrl = QString(); | |
354 emit entryRemoved(item); | |
355 } | |
356 | |
357 if (nextTimeout > 0) | |
358 m_expiredTimer.start(nextTimeout * 1000); | |
359 } | |
360 | |
361 protected: | |
362 | |
363 void addHistoryItem(HistoryItem item) | |
364 { | |
365 QWebSettings globalSettings = QWebSettings.globalSettings(); | |
366 if (globalSettings.testAttribute(QWebSettings.PrivateBrowsingEnabled)) | |
367 return; | |
368 | |
369 m_history.prepend(item); | |
370 emit entryAdded(item); | |
371 if (m_history.count() == 1) | |
372 checkForExpired(); | |
373 } | |
374 | |
375 private: | |
376 | |
377 void load() | |
378 { | |
379 loadSettings(); | |
380 | |
381 historyFile = new QFile(QDesktopServices.storageLocation(QDesktopServices.DataLocation) + QLatin1String("/history")); | |
382 if (!historyFile.exists()) | |
383 return; | |
384 if (!historyFile.open(QFile.ReadOnly)) { | |
385 qWarning() << "Unable to open history file" << historyFile.fileName(); | |
386 return; | |
387 } | |
388 | |
389 QList<HistoryItem> list; | |
390 auto in_ = new QDataStream(&historyFile); | |
391 // Double check that the history file is sorted as it is read in | |
392 bool needToSort = false; | |
393 HistoryItem lastInsertedItem; | |
394 QByteArray data; | |
395 QDataStream stream; | |
396 QBuffer buffer; | |
397 stream.setDevice(buffer); | |
398 while (!historyFile.atEnd()) { | |
399 in_ >> data; | |
400 buffer.close(); | |
401 buffer.setBuffer(data); | |
402 buffer.open(QIODevice.ReadOnly); | |
403 quint32 ver; | |
404 stream >> ver; | |
405 if (ver != HISTORY_VERSION) | |
406 continue; | |
407 HistoryItem item; | |
408 stream >> item.url; | |
409 stream >> item.dateTime; | |
410 stream >> item.title; | |
411 | |
412 if (!item.dateTime.isValid()) | |
413 continue; | |
414 | |
415 if (item == lastInsertedItem) { | |
416 if (lastInsertedItem.title.isEmpty() && !list.isEmpty()) | |
417 list[0].title = item.title; | |
418 continue; | |
419 } | |
420 | |
421 if (!needToSort && !list.isEmpty() && lastInsertedItem < item) | |
422 needToSort = true; | |
423 | |
424 list.prepend(item); | |
425 lastInsertedItem = item; | |
426 } | |
427 | |
428 if (needToSort) | |
429 qSort(list.begin(), list.end()); | |
430 | |
431 setHistory(list, true); | |
432 | |
433 // If we had to sort re-write the whole history sorted | |
434 if (needToSort) { | |
435 m_lastSavedUrl = QString(); | |
436 m_saveTimer.changeOccurred(); | |
437 } | |
438 } | |
439 | |
440 AutoSaver m_saveTimer; | |
441 int m_historyLimit; | |
442 QTimer m_expiredTimer; | |
443 QList<HistoryItem> m_history; | |
444 QString m_lastSavedUrl; | |
445 | |
446 HistoryModel m_historyModel; | |
447 HistoryFilterModel m_historyFilterModel; | |
448 HistoryTreeModel m_historyTreeModel; | |
438 } | 449 } |
439 | |
440 AutoSaver *m_saveTimer; | |
441 int m_historyLimit; | |
442 QTimer m_expiredTimer; | |
443 QList<HistoryItem> m_history; | |
444 QString m_lastSavedUrl; | |
445 | |
446 HistoryModel *m_historyModel; | |
447 HistoryFilterModel *m_historyFilterModel; | |
448 HistoryTreeModel *m_historyTreeModel; | |
449 }; | |
450 | 450 |
451 class HistoryModel : public QAbstractTableModel | 451 class HistoryModel : public QAbstractTableModel |
452 { | 452 { |
453 Q_OBJECT | 453 public: |
454 | 454 |
455 public slots: | 455 void historyReset() |
456 void historyReset() | 456 { |
457 { | 457 reset(); |
458 reset(); | 458 } |
459 } | 459 |
460 | 460 void entryAdded() |
461 void entryAdded() | 461 { |
462 { | 462 beginInsertRows(QModelIndex(), 0, 0); |
463 beginInsertRows(QModelIndex(), 0, 0); | 463 endInsertRows(); |
464 endInsertRows(); | 464 } |
465 } | 465 |
466 | 466 void entryUpdated(int offset) |
467 void entryUpdated(int offset) | 467 { |
468 { | 468 QModelIndex idx = index(offset, 0); |
469 QModelIndex idx = index(offset, 0); | 469 emit dataChanged(idx, idx); |
470 emit dataChanged(idx, idx); | 470 } |
471 } | |
472 | 471 |
473 public: | 472 public: |
474 | 473 |
475 enum Roles { | 474 enum Roles { |
476 DateRole = Qt.UserRole + 1, | 475 DateRole = Qt.UserRole + 1, |
477 DateTimeRole = Qt.UserRole + 2, | 476 DateTimeRole = Qt.UserRole + 2, |
478 UrlRole = Qt.UserRole + 3, | 477 UrlRole = Qt.UserRole + 3, |
479 UrlStringRole = Qt.UserRole + 4 | 478 UrlStringRole = Qt.UserRole + 4 |
480 }; | 479 } |
481 | 480 |
482 this(HistoryManager *history, QObject *parent = null) | 481 this(HistoryManager history, QObject parent = null) |
483 { | 482 { |
484 super(parent); | 483 super(parent); |
485 m_history = history; | 484 m_history = history; |
486 | 485 |
487 Q_ASSERT(m_history); | 486 assert(m_history); |
488 connect(m_history, SIGNAL(historyReset()), | 487 |
489 this, SLOT(historyReset())); | 488 m_history.historyReset.connect(&this.historyReset); |
490 connect(m_history, SIGNAL(entryRemoved(HistoryItem &)), | 489 m_history.entryRemoved.connect(&this.historyReset); |
491 this, SLOT(historyReset())); | 490 m_history.entryAdded.connect(&this.entryAdded); |
492 | 491 m_history.entryUpdated.connect(&this.entryUpdated); |
493 connect(m_history, SIGNAL(entryAdded(HistoryItem &)), | |
494 this, SLOT(entryAdded())); | |
495 connect(m_history, SIGNAL(entryUpdated(int)), | |
496 this, SLOT(entryUpdated(int))); | |
497 } | 492 } |
498 | 493 |
499 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole) | 494 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole) |
500 { | 495 { |
501 if (orientation == Qt.Horizontal && role == Qt.DisplayRole) { | 496 if (orientation == Qt.Horizontal && role == Qt.DisplayRole) { |
502 switch (section) { | 497 switch (section) { |
503 case 0: return tr("Title"); | 498 case 0: return tr("Title"); |
504 case 1: return tr("Address"); | 499 case 1: return tr("Address"); |
505 } | 500 } |
506 } | 501 } |
507 return QAbstractTableModel::headerData(section, orientation, role); | 502 return QAbstractTableModel.headerData(section, orientation, role); |
508 } | 503 } |
509 | 504 |
510 QVariant data(QModelIndex &index, int role = Qt.DisplayRole) | 505 QVariant data(QModelIndex index, int role = Qt.DisplayRole) |
511 { | 506 { |
512 QList<HistoryItem> lst = m_history.history(); | 507 QList<HistoryItem> lst = m_history.history(); |
513 if (index.row() < 0 || index.row() >= lst.size()) | 508 if (index.row() < 0 || index.row() >= lst.size()) |
514 return QVariant(); | 509 return QVariant(); |
515 | 510 |
516 const HistoryItem &item = lst.at(index.row()); | 511 HistoryItem item = lst.at(index.row()); |
517 switch (role) { | 512 switch (role) { |
518 case DateTimeRole: | 513 case DateTimeRole: |
519 return item.dateTime; | 514 return item.dateTime; |
520 case DateRole: | 515 case DateRole: |
521 return item.dateTime.date(); | 516 return item.dateTime.date(); |
539 return item.url; | 534 return item.url; |
540 } | 535 } |
541 } | 536 } |
542 case Qt.DecorationRole: | 537 case Qt.DecorationRole: |
543 if (index.column() == 0) { | 538 if (index.column() == 0) { |
544 return BrowserApplication::instance().icon(item.url); | 539 return BrowserApplication.instance().icon(item.url); |
545 } | 540 } |
546 } | 541 } |
547 return QVariant(); | 542 return QVariant(); |
548 } | 543 } |
549 | 544 |
550 int columnCount(QModelIndex &parent = QModelIndex()) | 545 int columnCount(QModelIndex parent = QModelIndex()) |
551 { | 546 { |
552 return (parent.isValid()) ? 0 : 2; | 547 return (parent.isValid()) ? 0 : 2; |
553 } | 548 } |
554 | 549 |
555 int rowCount(QModelIndex &parent = QModelIndex()) | 550 int rowCount(QModelIndex parent = QModelIndex()) |
556 { | 551 { |
557 return (parent.isValid()) ? 0 : m_history.history().count(); | 552 return (parent.isValid()) ? 0 : m_history.history().count(); |
558 } | 553 } |
559 | 554 |
560 bool removeRows(int row, int count, QModelIndex &parent = QModelIndex()) | 555 bool removeRows(int row, int count, QModelIndex parent = QModelIndex()) |
561 { | 556 { |
562 if (parent.isValid()) | 557 if (parent.isValid()) |
563 return false; | 558 return false; |
564 int lastRow = row + count - 1; | 559 int lastRow = row + count - 1; |
565 beginRemoveRows(parent, row, lastRow); | 560 beginRemoveRows(parent, row, lastRow); |
566 QList<HistoryItem> lst = m_history.history(); | 561 QList<HistoryItem> lst = m_history.history(); |
567 for (int i = lastRow; i >= row; --i) | 562 for (int i = lastRow; i >= row; --i) |
568 lst.removeAt(i); | 563 lst.removeAt(i); |
569 disconnect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); | 564 m_history.historyReset.disconnect(&this.historyReset); |
570 m_history.setHistory(lst); | 565 m_history.setHistory(lst); |
571 connect(m_history, SIGNAL(historyReset()), this, SLOT(historyReset())); | 566 m_history.historyReset.connect(&this.historyReset); |
572 endRemoveRows(); | 567 endRemoveRows(); |
573 return true; | 568 return true; |
574 } | 569 } |
575 | 570 |
576 | 571 |
577 private: | 572 private: |
578 HistoryManager *m_history; | 573 HistoryManager m_history; |
579 } | 574 } |
580 | 575 |
581 const uint MOVEDROWS = 15; | 576 const uint MOVEDROWS = 15; |
582 | 577 |
583 /*! | 578 /*! |
584 Proxy model that will remove any duplicate entries. | 579 Proxy model that will remove any duplicate entries. |
585 Both m_sourceRow and m_historyHash store their offsets not from | 580 Both m_sourceRow and m_historyHash store their offsets not from |
586 the front of the list, but as offsets from the back. | 581 the front of the list, but as offsets from the back. |
587 */ | 582 */ |
588 class HistoryFilterModel : public QAbstractProxyModel | 583 class HistoryFilterModel : public QAbstractProxyModel |
589 { | 584 { |
590 Q_OBJECT | |
591 | |
592 public: | 585 public: |
593 | 586 |
594 this(QAbstractItemModel *sourceModel, QObject *parent = null) | 587 this(QAbstractItemModel sourceModel, QObject parent = null) |
595 { | 588 { |
596 super(parent); | 589 super(parent); |
597 m_loaded = false; | 590 m_loaded = false; |
598 setSourceModel(sourceModel); | 591 setSourceModel(sourceModel); |
599 } | 592 } |
600 | 593 |
601 inline bool historyContains(QString &url) | 594 bool historyContains(QString url) |
602 { | 595 { |
603 load(); | 596 load(); |
604 return m_historyHash.contains(url); | 597 return m_historyHash.contains(url); |
605 } | 598 } |
606 | 599 |
607 int historyLocation(QString &url); | 600 int historyLocation(QString url); |
608 { | 601 { |
609 load(); | 602 load(); |
610 if (!m_historyHash.contains(url)) | 603 if (!m_historyHash.contains(url)) |
611 return 0; | 604 return 0; |
612 return sourceModel().rowCount() - m_historyHash.value(url); | 605 return sourceModel().rowCount() - m_historyHash.value(url); |
613 } | 606 } |
614 | 607 |
615 QModelIndex mapFromSource(QModelIndex &sourceIndex) | 608 QModelIndex mapFromSource(QModelIndex sourceIndex) |
616 { | 609 { |
617 load(); | 610 load(); |
618 QString url = sourceIndex.data(HistoryModel.UrlStringRole).toString(); | 611 QString url = sourceIndex.data(HistoryModel.UrlStringRole).toString(); |
619 if (!m_historyHash.contains(url)) | 612 if (!m_historyHash.contains(url)) |
620 return QModelIndex(); | 613 return QModelIndex(); |
621 | 614 |
622 // This can be done in a binary search, but we can't use qBinary find | 615 // This can be done in a binary search, but we can't use qBinary find |
623 // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); | 616 // because it can't take: qBinaryFind(m_sourceRow.end(), m_sourceRow.begin(), v); |
624 // so if this is a performance bottlneck then convert to binary search, until then | 617 // so if this is a performance bottlneck then convert to binary search, until then |
625 // the cleaner/easier to read code wins the day. | 618 // the cleaner/easier to read code wins the day. |
626 int realRow = -1; | 619 int realRow = -1; |
627 int sourceModelRow = sourceModel().rowCount() - sourceIndex.row(); | 620 int sourceModelRow = sourceModel().rowCount() - sourceIndex.row(); |
628 | 621 |
629 for (int i = 0; i < m_sourceRow.count(); ++i) { | 622 for (int i = 0; i < m_sourceRow.count(); ++i) { |
630 if (m_sourceRow.at(i) == sourceModelRow) { | 623 if (m_sourceRow.at(i) == sourceModelRow) { |
631 realRow = i; | 624 realRow = i; |
632 break; | 625 break; |
633 } | 626 } |
634 } | 627 } |
635 if (realRow == -1) | 628 if (realRow == -1) |
636 return QModelIndex(); | 629 return QModelIndex(); |
637 | 630 |
638 return createIndex(realRow, sourceIndex.column(), sourceModel().rowCount() - sourceIndex.row()); | 631 return createIndex(realRow, sourceIndex.column(), sourceModel().rowCount() - sourceIndex.row()); |
639 } | 632 } |
640 | 633 |
641 | 634 |
642 QModelIndex mapToSource(QModelIndex &proxyIndex) | 635 QModelIndex mapToSource(QModelIndex proxyIndex) |
643 { | 636 { |
644 load(); | 637 load(); |
645 int sourceRow = sourceModel().rowCount() - proxyIndex.internalId(); | 638 int sourceRow = sourceModel().rowCount() - proxyIndex.internalId(); |
646 return sourceModel().index(sourceRow, proxyIndex.column()); | 639 return sourceModel().index(sourceRow, proxyIndex.column()); |
647 } | 640 } |
648 | 641 |
649 void setSourceModel(QAbstractItemModel *newSourceModel) | 642 void setSourceModel(QAbstractItemModel newSourceModel) |
650 { | 643 { |
651 if (sourceModel()) { | 644 if (sourceModel()) { |
652 disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); | 645 sourceModel.modelReset.disconnect(&this.sourceReset); |
653 disconnect(sourceModel(), SIGNAL(dataChanged(QModelIndex &, QModelIndex &)), | 646 sourceModel.dataChanged.disconnect(&this.dataChanged); |
654 this, SLOT(dataChanged(QModelIndex &, QModelIndex &))); | 647 sourceModel.rowsInserted.disconnect(&this.sourceRowsInserted); |
655 disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 648 sourceModel.rowsRemoved.disconnect(&this.sourceRowsRemoved); |
656 this, SLOT(sourceRowsInserted(QModelIndex &, int, int))); | 649 } |
657 disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 650 |
658 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | 651 QAbstractProxyModel.setSourceModel(newSourceModel); |
659 } | |
660 | |
661 QAbstractProxyModel::setSourceModel(newSourceModel); | |
662 | 652 |
663 if (sourceModel()) { | 653 if (sourceModel()) { |
664 m_loaded = false; | 654 m_loaded = false; |
665 connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); | 655 sourceModel.modelReset.connect(&this.sourceReset); |
666 connect(sourceModel(), SIGNAL(dataChanged(QModelIndex &, QModelIndex &)), | 656 sourceModel.dataChanged.connect(&this.sourceDataChanged); |
667 this, SLOT(sourceDataChanged(QModelIndex &, QModelIndex &))); | 657 sourceModel.rowsInserted.connect(&this.sourceRowsInserted); |
668 connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 658 sourceModel.rowsRemoved.connect(&this.sourceRowsRemoved); |
669 this, SLOT(sourceRowsInserted(QModelIndex &, int, int))); | |
670 connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | |
671 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | |
672 } | 659 } |
673 } | 660 } |
674 | 661 |
675 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole); | 662 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole); |
676 { | 663 { |
677 return sourceModel().headerData(section, orientation, role); | 664 return sourceModel().headerData(section, orientation, role); |
678 } | 665 } |
679 | 666 |
680 int rowCount(QModelIndex &parent = QModelIndex()) | 667 int rowCount(QModelIndex parent = QModelIndex()) |
681 { | 668 { |
682 load(); | 669 load(); |
683 if (parent.isValid()) | 670 if (parent.isValid()) |
684 return 0; | 671 return 0; |
685 return m_historyHash.count(); | 672 return m_historyHash.count(); |
686 } | 673 } |
687 | 674 |
688 int columnCount(QModelIndex &parent = QModelIndex()); | 675 int columnCount(QModelIndex parent = QModelIndex()); |
689 { | 676 { |
690 return (parent.isValid()) ? 0 : 2; | 677 return (parent.isValid()) ? 0 : 2; |
691 } | 678 } |
692 | 679 |
693 QModelIndex index(int, int, QModelIndex& = QModelIndex()) | 680 QModelIndex index(int, int, QModelIndex = QModelIndex()) |
694 { | 681 { |
695 load(); | 682 load(); |
696 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent)) | 683 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent)) |
697 return QModelIndex(); | 684 return QModelIndex(); |
698 | 685 |
699 return createIndex(row, column, m_sourceRow[row]); | 686 return createIndex(row, column, m_sourceRow[row]); |
700 } | 687 } |
701 | 688 |
702 | 689 QModelIndex parent(QModelIndex index= QModelIndex()) |
703 QModelIndex parent(QModelIndex& index= QModelIndex()) | |
704 { | 690 { |
705 return QModelIndex(); | 691 return QModelIndex(); |
706 } | 692 } |
707 | 693 |
708 /* | 694 /* |
709 Removing a continuous block of rows will remove filtered rows too as this is | 695 Removing a continuous block of rows will remove filtered rows too as this is |
710 the users intention. | 696 the users intention. |
711 */ | 697 */ |
712 bool removeRows(int row, int count, QModelIndex &parent = QModelIndex()); | 698 bool removeRows(int row, int count, QModelIndex parent = QModelIndex()); |
713 { | 699 { |
714 if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) | 700 if (row < 0 || count <= 0 || row + count > rowCount(parent) || parent.isValid()) |
715 return false; | 701 return false; |
716 int lastRow = row + count - 1; | 702 int lastRow = row + count - 1; |
717 disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 703 sourceModel.rowsRemoved,disconnect(&this.sourceRowsRemoved); |
718 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | |
719 beginRemoveRows(parent, row, lastRow); | 704 beginRemoveRows(parent, row, lastRow); |
720 int oldCount = rowCount(); | 705 int oldCount = rowCount(); |
721 int start = sourceModel().rowCount() - m_sourceRow.value(row); | 706 int start = sourceModel().rowCount() - m_sourceRow.value(row); |
722 int end = sourceModel().rowCount() - m_sourceRow.value(lastRow); | 707 int end = sourceModel().rowCount() - m_sourceRow.value(lastRow); |
723 sourceModel().removeRows(start, end - start + 1); | 708 sourceModel().removeRows(start, end - start + 1); |
724 endRemoveRows(); | 709 endRemoveRows(); |
725 connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 710 sourceModel.rowsRemoved.connect(&this.sourceRowsRemoved); |
726 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | |
727 m_loaded = false; | 711 m_loaded = false; |
728 if (oldCount - count != rowCount()) | 712 if (oldCount - count != rowCount()) |
729 reset(); | 713 reset(); |
730 return true; | 714 return true; |
731 } | 715 } |
732 | 716 |
733 | 717 |
734 QVariant data(QModelIndex &index, int role = Qt.DisplayRole); | 718 QVariant data(QModelIndex index, int role = Qt.DisplayRole); |
735 { | 719 { |
736 return QAbstractProxyModel::data(index, role); | 720 return QAbstractProxyModel.data(index, role); |
737 } | 721 } |
738 | 722 |
739 private slots: | 723 private slots: |
740 | 724 |
741 void sourceReset() | 725 void sourceReset() |
743 m_loaded = false; | 727 m_loaded = false; |
744 reset(); | 728 reset(); |
745 } | 729 } |
746 | 730 |
747 | 731 |
748 void sourceDataChanged(QModelIndex &topLeft, QModelIndex &bottomRight) | 732 void sourceDataChanged(QModelIndex topLeft, QModelIndex bottomRight) |
749 { | 733 { |
750 emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); | 734 emit dataChanged(mapFromSource(topLeft), mapFromSource(bottomRight)); |
751 } | 735 } |
752 | 736 |
753 void sourceRowsRemoved(QModelIndex &, int start, int end) | 737 void sourceRowsRemoved(QModelIndex , int start, int end) |
754 { | 738 { |
755 Q_UNUSED(start); | 739 //Q_UNUSED(start); |
756 Q_UNUSED(end); | 740 //Q_UNUSED(end); |
757 sourceReset(); | 741 sourceReset(); |
758 } | 742 } |
759 | 743 |
760 | 744 |
761 void sourceRowsInserted(QModelIndex &parent, int start, int end) | 745 void sourceRowsInserted(QModelIndex parent, int start, int end) |
762 { | 746 { |
763 Q_ASSERT(start == end && start == 0); | 747 assert(start == end && start == 0); |
764 Q_UNUSED(end); | 748 //Q_UNUSED(end); |
765 if (!m_loaded) | 749 if (!m_loaded) |
766 return; | 750 return; |
767 QModelIndex idx = sourceModel().index(start, 0, parent); | 751 QModelIndex idx = sourceModel().index(start, 0, parent); |
768 QString url = idx.data(HistoryModel.UrlStringRole).toString(); | 752 QString url = idx.data(HistoryModel.UrlStringRole).toString(); |
769 if (m_historyHash.contains(url)) { | 753 if (m_historyHash.contains(url)) { |
798 } | 782 } |
799 m_loaded = true; | 783 m_loaded = true; |
800 } | 784 } |
801 | 785 |
802 | 786 |
803 mutable QList<int> m_sourceRow; | 787 QList<int> m_sourceRow; |
804 mutable QHash<QString, int> m_historyHash; | 788 QHash<QString, int> m_historyHash; |
805 mutable bool m_loaded; | 789 bool m_loaded; |
806 } | 790 } |
807 | 791 |
808 /* | 792 /* |
809 The history menu | 793 The history menu |
810 - Removes the first twenty entries and puts them as children of the top level. | 794 - Removes the first twenty entries and puts them as children of the top level. |
811 - If there are less then twenty entries then the first folder is also removed. | 795 - If there are less then twenty entries then the first folder is also removed. |
812 | 796 |
813 The mapping is done by knowing that HistoryTreeModel is over a table | 797 The mapping is done by knowing that HistoryTreeModel is over a table |
814 We store that row offset in our index's private data. | 798 We store that row offset in our index's private data. |
815 */ | 799 */ |
816 class HistoryMenuModel : public QAbstractProxyModel | 800 class HistoryMenuModel : public QAbstractProxyModel |
817 { | 801 { |
818 Q_OBJECT | |
819 | |
820 public: | 802 public: |
821 | 803 |
822 /* | 804 /* |
823 Maps the first bunch of items of the source model to the root | 805 Maps the first bunch of items of the source model to the root |
824 */ | 806 */ |
825 HistoryMenuModel(HistoryTreeModel *sourceModel, QObject *parent = null) | 807 HistoryMenuModel(HistoryTreeModel sourceModel, QObject parent = null) |
826 { | 808 { |
827 super(parent); | 809 super(parent); |
828 m_treeModel = sourceModel; | 810 m_treeModel = sourceModel; |
829 setSourceModel(sourceModel); | 811 setSourceModel(sourceModel); |
830 } | 812 } |
831 | 813 |
832 int columnCount(QModelIndex &parent) | 814 int columnCount(QModelIndex parent) |
833 { | 815 { |
834 return m_treeModel.columnCount(mapToSource(parent)); | 816 return m_treeModel.columnCount(mapToSource(parent)); |
835 } | 817 } |
836 | 818 |
837 int rowCount(QModelIndex &parent = QModelIndex()); | 819 int rowCount(QModelIndex parent = QModelIndex()); |
838 { | 820 { |
839 if (parent.column() > 0) | 821 if (parent.column() > 0) |
840 return 0; | 822 return 0; |
841 | 823 |
842 if (!parent.isValid()) { | 824 if (!parent.isValid()) { |
857 if (idx == sourceModel().index(0, 0)) | 839 if (idx == sourceModel().index(0, 0)) |
858 return defaultCount - bumpedRows(); | 840 return defaultCount - bumpedRows(); |
859 return defaultCount; | 841 return defaultCount; |
860 } | 842 } |
861 | 843 |
862 QModelIndex mapFromSource(QModelIndex &sourceIndex) | 844 QModelIndex mapFromSource(QModelIndex sourceIndex) |
863 { | 845 { |
864 // currently not used or autotested | 846 // currently not used or autotested |
865 Q_ASSERT(false); | 847 assert(false); |
866 int sr = m_treeModel.mapToSource(sourceIndex).row(); | 848 int sr = m_treeModel.mapToSource(sourceIndex).row(); |
867 return createIndex(sourceIndex.row(), sourceIndex.column(), sr); | 849 return createIndex(sourceIndex.row(), sourceIndex.column(), sr); |
868 } | 850 } |
869 | 851 |
870 QModelIndex mapToSource(QModelIndex &proxyIndex) | 852 QModelIndex mapToSource(QModelIndex proxyIndex) |
871 { | 853 { |
872 if (!proxyIndex.isValid()) | 854 if (!proxyIndex.isValid()) |
873 return QModelIndex(); | 855 return QModelIndex(); |
874 | 856 |
875 if (proxyIndex.internalId() == -1) { | 857 if (proxyIndex.internalId() == -1) { |
887 } | 869 } |
888 | 870 |
889 | 871 |
890 QModelIndex index(int, int, QModelIndex &parent = QModelIndex()); | 872 QModelIndex index(int, int, QModelIndex &parent = QModelIndex()); |
891 { | 873 { |
892 if (row < 0 | 874 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.column() > 0) |
893 || column < 0 || column >= columnCount(parent) | |
894 || parent.column() > 0) | |
895 return QModelIndex(); | 875 return QModelIndex(); |
876 | |
896 if (!parent.isValid()) | 877 if (!parent.isValid()) |
897 return createIndex(row, column, -1); | 878 return createIndex(row, column, -1); |
898 | 879 |
899 QModelIndex treeIndexParent = mapToSource(parent); | 880 QModelIndex treeIndexParent = mapToSource(parent); |
900 | 881 |
907 if (historyRow == -1) | 888 if (historyRow == -1) |
908 historyRow = treeIndex.row(); | 889 historyRow = treeIndex.row(); |
909 return createIndex(row, column, historyRow); | 890 return createIndex(row, column, historyRow); |
910 } | 891 } |
911 | 892 |
912 QModelIndex parent(QModelIndex &index = QModelIndex()); | 893 QModelIndex parent(QModelIndex index = QModelIndex()); |
913 { | 894 { |
914 int offset = index.internalId(); | 895 int offset = index.internalId(); |
915 if (offset == -1 || !index.isValid()) | 896 if (offset == -1 || !index.isValid()) |
916 return QModelIndex(); | 897 return QModelIndex(); |
917 | 898 |
934 return qMin(m_treeModel.rowCount(first), MOVEDROWS); | 915 return qMin(m_treeModel.rowCount(first), MOVEDROWS); |
935 } | 916 } |
936 | 917 |
937 private: | 918 private: |
938 | 919 |
939 HistoryTreeModel *m_treeModel; | 920 HistoryTreeModel m_treeModel; |
940 } | 921 } |
941 | 922 |
942 // Menu that is dynamically populated from the history | 923 // Menu that is dynamically populated from the history |
943 class HistoryMenu : public ModelMenu | 924 class HistoryMenu : public ModelMenu |
944 { | 925 { |
945 Q_OBJECT | 926 mixin Signal!("openUrl", QUrl url); |
946 | 927 |
947 signals: | |
948 void openUrl(QUrl &url); | |
949 | |
950 public: | 928 public: |
951 | 929 |
952 this(QWidget *parent = null) | 930 this(QWidget parent = null) |
953 { | 931 { |
954 super(parent); | 932 super(parent); |
955 m_history = 0; | 933 m_history = 0; |
956 connect(this, SIGNAL(activated(QModelIndex &)), | 934 connect(this, SIGNAL(activated(QModelIndex )), |
957 this, SLOT(activated(QModelIndex &))); | 935 this, SLOT(activated(QModelIndex ))); |
958 setHoverRole(HistoryModel.UrlStringRole); | 936 setHoverRole(HistoryModel.UrlStringRole); |
959 } | 937 } |
960 | 938 |
961 void setInitialActions(QList<QAction*> actions) | 939 void setInitialActions(QList<QAction> actions) |
962 { | 940 { |
963 m_initialActions = actions; | 941 m_initialActions = actions; |
964 for (int i = 0; i < m_initialActions.count(); ++i) | 942 for (int i = 0; i < m_initialActions.count(); ++i) |
965 addAction(m_initialActions.at(i)); | 943 addAction(m_initialActions.at(i)); |
966 } | 944 } |
968 protected: | 946 protected: |
969 | 947 |
970 bool prePopulated() | 948 bool prePopulated() |
971 { | 949 { |
972 if (!m_history) { | 950 if (!m_history) { |
973 m_history = BrowserApplication::historyManager(); | 951 m_history = BrowserApplication.historyManager(); |
974 m_historyMenuModel = new HistoryMenuModel(m_history.historyTreeModel(), this); | 952 m_historyMenuModel = new HistoryMenuModel(m_history.historyTreeModel(), this); |
975 setModel(m_historyMenuModel); | 953 setModel(m_historyMenuModel); |
976 } | 954 } |
977 // initial actions | 955 // initial actions |
978 for (int i = 0; i < m_initialActions.count(); ++i) | 956 for (int i = 0; i < m_initialActions.count(); ++i) |
987 void postPopulated() | 965 void postPopulated() |
988 { | 966 { |
989 if (m_history.history().count() > 0) | 967 if (m_history.history().count() > 0) |
990 addSeparator(); | 968 addSeparator(); |
991 | 969 |
992 QAction *showAllAction = new QAction(tr("Show All History"), this); | 970 QAction showAllAction = new QAction(tr("Show All History"), this); |
993 connect(showAllAction, SIGNAL(triggered()), this, SLOT(showHistoryDialog())); | 971 showAllAction.triggered.connect(&this.showHistoryDialog); |
994 addAction(showAllAction); | 972 addAction(showAllAction); |
995 | 973 |
996 QAction *clearAction = new QAction(tr("Clear History"), this); | 974 QAction clearAction = new QAction(tr("Clear History"), this); |
997 connect(clearAction, SIGNAL(triggered()), m_history, SLOT(clear())); | 975 clearAction.triggered.connect(&m_history.clear); |
998 addAction(clearAction); | 976 addAction(clearAction); |
999 } | 977 } |
1000 | 978 |
1001 private slots: | 979 private slots: |
1002 void activated(QModelIndex &index) | 980 void activated(QModelIndex index) |
1003 { | 981 { |
1004 emit openUrl(index.data(HistoryModel.UrlRole).toUrl()); | 982 emit openUrl(index.data(HistoryModel.UrlRole).toUrl()); |
1005 } | 983 } |
1006 | 984 |
1007 void showHistoryDialog() | 985 void showHistoryDialog() |
1008 { | 986 { |
1009 HistoryDialog *dialog = new HistoryDialog(this); | 987 auto dialog = new HistoryDialog(this); |
1010 connect(dialog, SIGNAL(openUrl(QUrl&)), | 988 dialog.openUrl(QUrl).connect(&this.openUrl(QUrl)); |
1011 this, SIGNAL(openUrl(QUrl&))); | |
1012 dialog.show(); | 989 dialog.show(); |
1013 } | 990 } |
1014 | 991 |
1015 | 992 |
1016 private: | 993 private: |
1017 | 994 |
1018 HistoryManager *m_history; | 995 HistoryManager m_history; |
1019 HistoryMenuModel *m_historyMenuModel; | 996 HistoryMenuModel m_historyMenuModel; |
1020 QList<QAction*> m_initialActions; | 997 QList<QAction> m_initialActions; |
1021 } | 998 } |
1022 | 999 |
1023 // proxy model for the history model that | 1000 // proxy model for the history model that |
1024 // exposes each url http://www.foo.com and it url starting at the host www.foo.com | 1001 // exposes each url http://www.foo.com and it url starting at the host www.foo.com |
1025 class HistoryCompletionModel : public QAbstractProxyModel | 1002 class HistoryCompletionModel : public QAbstractProxyModel |
1026 { | 1003 { |
1027 Q_OBJECT | |
1028 | 1004 |
1029 public: | 1005 public: |
1030 this(QObject *parent = null) | 1006 this(QObject parent = null) |
1031 { | 1007 { |
1032 super(parent); | 1008 super(parent); |
1033 } | 1009 } |
1034 | 1010 |
1035 QVariant data(QModelIndex &index, int role) | 1011 QVariant data(QModelIndex index, int role) |
1036 { | 1012 { |
1037 if (sourceModel() | 1013 if (sourceModel() |
1038 && (role == Qt.EditRole || role == Qt.DisplayRole) | 1014 && (role == Qt.EditRole || role == Qt.DisplayRole) |
1039 && index.isValid()) { | 1015 && index.isValid()) { |
1040 QModelIndex idx = mapToSource(index); | 1016 QModelIndex idx = mapToSource(index); |
1041 idx = idx.sibling(idx.row(), 1); | 1017 idx = idx.sibling(idx.row(), 1); |
1042 QString urlString = idx.data(HistoryModel.UrlStringRole).toString(); | 1018 QString urlString = idx.data(HistoryModel.UrlStringRole).toString(); |
1043 if (index.row() % 2) { | 1019 if (index.row() % 2) { |
1044 QUrl url = urlString; | 1020 QUrl url = urlString; |
1045 QString s = url.toString(QUrl::RemoveScheme | QUrl::RemoveUserInfo | QUrl::StripTrailingSlash); | 1021 QString s = url.toString(QUrl.RemoveScheme | QUrl.RemoveUserInfo | QUrl.StripTrailingSlash); |
1046 return s.mid(2); // strip // from the front | 1022 return s.mid(2); // strip // from the front |
1047 } | 1023 } |
1048 return urlString; | 1024 return urlString; |
1049 } | 1025 } |
1050 return QAbstractProxyModel::data(index, role); | 1026 return QAbstractProxyModel.data(index, role); |
1051 } | 1027 } |
1052 | 1028 |
1053 int rowCount(QModelIndex &parent = QModelIndex()) | 1029 int rowCount(QModelIndex parent = QModelIndex()) |
1054 { | 1030 { |
1055 return (parent.isValid() || !sourceModel()) ? 0 : sourceModel().rowCount(parent) * 2; | 1031 return (parent.isValid() || !sourceModel()) ? 0 : sourceModel().rowCount(parent) * 2; |
1056 } | 1032 } |
1057 | 1033 |
1058 int columnCount(QModelIndex &parent = QModelIndex()) | 1034 int columnCount(QModelIndex parent = QModelIndex()) |
1059 { | 1035 { |
1060 return (parent.isValid()) ? 0 : 1; | 1036 return (parent.isValid()) ? 0 : 1; |
1061 } | 1037 } |
1062 | 1038 |
1063 QModelIndex mapFromSource(QModelIndex &sourceIndex) | 1039 QModelIndex mapFromSource(QModelIndex sourceIndex) |
1064 { | 1040 { |
1065 int row = sourceIndex.row() * 2; | 1041 int row = sourceIndex.row() * 2; |
1066 return index(row, sourceIndex.column()); | 1042 return index(row, sourceIndex.column()); |
1067 } | 1043 } |
1068 | 1044 |
1069 | 1045 |
1070 QModelIndex mapToSource(QModelIndex &proxyIndex) | 1046 QModelIndex mapToSource(QModelIndex proxyIndex) |
1071 { | 1047 { |
1072 if (!sourceModel()) | 1048 if (!sourceModel()) |
1073 return QModelIndex(); | 1049 return QModelIndex(); |
1074 int row = proxyIndex.row() / 2; | 1050 int row = proxyIndex.row() / 2; |
1075 return sourceModel().index(row, proxyIndex.column()); | 1051 return sourceModel().index(row, proxyIndex.column()); |
1076 } | 1052 } |
1077 | 1053 |
1078 QModelIndex index(int, int, QModelIndex& = QModelIndex()) | 1054 QModelIndex index(int, int, QModelIndex = QModelIndex()) |
1079 { | 1055 { |
1080 if (row < 0 || row >= rowCount(parent) | 1056 if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent)) |
1081 || column < 0 || column >= columnCount(parent)) | |
1082 return QModelIndex(); | 1057 return QModelIndex(); |
1083 return createIndex(row, column, 0); | 1058 return createIndex(row, column, 0); |
1084 } | 1059 } |
1085 | 1060 |
1086 QModelIndex parent(QModelIndex& index= QModelIndex()); | 1061 QModelIndex parent(QModelIndex index= QModelIndex()); |
1087 { | 1062 { |
1088 return QModelIndex(); | 1063 return QModelIndex(); |
1089 } | 1064 } |
1090 | 1065 |
1091 void setSourceModel(QAbstractItemModel *sourceModel); | 1066 void setSourceModel(QAbstractItemModel sourceModel); |
1092 { | 1067 { |
1093 if (sourceModel()) { | 1068 if (sourceModel()) { |
1094 disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); | 1069 sourceModel.modelReset.disconnect(&this.sourceReset); |
1095 disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 1070 sourceModel.rowsInserted(QModelIndex , int, int).disconnect(&this.sourceReset); |
1096 this, SLOT(sourceReset())); | 1071 sourceModel.rowsRemoved(QModelIndex , int, int).disconnect(&this.sourceReset); |
1097 disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 1072 } |
1098 this, SLOT(sourceReset())); | 1073 |
1099 } | 1074 QAbstractProxyModel.setSourceModel(newSourceModel); |
1100 | |
1101 QAbstractProxyModel::setSourceModel(newSourceModel); | |
1102 | 1075 |
1103 if (newSourceModel) { | 1076 if (newSourceModel) { |
1104 connect(newSourceModel, SIGNAL(modelReset()), this, SLOT(sourceReset())); | 1077 newSourceModel.modelReset.connect(&this.sourceReset); |
1105 connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 1078 sourceModel.rowsInserted(QModelIndex , int, int).connect(&this.sourceReset); |
1106 this, SLOT(sourceReset())); | 1079 sourceModel.rowsRemoved(QModelIndex , int, int).connect(&this.sourceReset); |
1107 connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | |
1108 this, SLOT(sourceReset())); | |
1109 } | 1080 } |
1110 | 1081 |
1111 reset(); | 1082 reset(); |
1112 } | 1083 } |
1113 | 1084 |
1122 // proxy model for the history model that converts the list | 1093 // proxy model for the history model that converts the list |
1123 // into a tree, one top level node per day. | 1094 // into a tree, one top level node per day. |
1124 // Used in the HistoryDialog. | 1095 // Used in the HistoryDialog. |
1125 class HistoryTreeModel : public QAbstractProxyModel | 1096 class HistoryTreeModel : public QAbstractProxyModel |
1126 { | 1097 { |
1127 Q_OBJECT | |
1128 | |
1129 public: | 1098 public: |
1130 | 1099 |
1131 this(QAbstractItemModel *sourceModel, QObject *parent = null) | 1100 this(QAbstractItemModel sourceModel, QObject parent = null) |
1132 { | 1101 { |
1133 super(parent); | 1102 super(parent); |
1134 setSourceModel(sourceModel); | 1103 setSourceModel(sourceModel); |
1135 } | 1104 } |
1136 | 1105 |
1137 QVariant data(QModelIndex &index, int role = Qt.DisplayRole); | 1106 QVariant data(QModelIndex index, int role = Qt.DisplayRole); |
1138 { | 1107 { |
1139 if ((role == Qt.EditRole || role == Qt.DisplayRole)) { | 1108 if ((role == Qt.EditRole || role == Qt.DisplayRole)) { |
1140 int start = index.internalId(); | 1109 int start = index.internalId(); |
1141 if (start == 0) { | 1110 if (start == 0) { |
1142 int offset = sourceDateRow(index.row()); | 1111 int offset = sourceDateRow(index.row()); |
1143 if (index.column() == 0) { | 1112 if (index.column() == 0) { |
1144 QModelIndex idx = sourceModel().index(offset, 0); | 1113 QModelIndex idx = sourceModel().index(offset, 0); |
1145 QDate date = idx.data(HistoryModel.DateRole).toDate(); | 1114 QDate date = idx.data(HistoryModel.DateRole).toDate(); |
1146 if (date == QDate::currentDate()) | 1115 if (date == QDate.currentDate()) |
1147 return tr("Earlier Today"); | 1116 return tr("Earlier Today"); |
1148 return date.toString(QLatin1String("dddd, MMMM d, yyyy")); | 1117 return date.toString(QLatin1String("dddd, MMMM d, yyyy")); |
1149 } | 1118 } |
1150 if (index.column() == 1) { | 1119 if (index.column() == 1) { |
1151 return tr("%1 items").arg(rowCount(index.sibling(index.row(), 0))); | 1120 return tr("%1 items").arg(rowCount(index.sibling(index.row(), 0))); |
1158 int offset = sourceDateRow(index.row()); | 1127 int offset = sourceDateRow(index.row()); |
1159 QModelIndex idx = sourceModel().index(offset, 0); | 1128 QModelIndex idx = sourceModel().index(offset, 0); |
1160 return idx.data(HistoryModel.DateRole); | 1129 return idx.data(HistoryModel.DateRole); |
1161 } | 1130 } |
1162 | 1131 |
1163 return QAbstractProxyModel::data(index, role); | 1132 return QAbstractProxyModel.data(index, role); |
1164 } | 1133 } |
1165 | 1134 |
1166 | 1135 |
1167 int columnCount(QModelIndex &parent); | 1136 int columnCount(QModelIndex parent); |
1168 { | 1137 { |
1169 return sourceModel().columnCount(mapToSource(parent)); | 1138 return sourceModel().columnCount(mapToSource(parent)); |
1170 } | 1139 } |
1171 | 1140 |
1172 | 1141 |
1173 int rowCount(QModelIndex &parent = QModelIndex()) | 1142 int rowCount(QModelIndex parent = QModelIndex()) |
1174 { | 1143 { |
1175 if ( parent.internalId() != 0 | 1144 if ( parent.internalId() != 0 || parent.column() > 0 || !sourceModel()) |
1176 || parent.column() > 0 | 1145 return 0; |
1177 || !sourceModel()) | 1146 |
1178 return 0; | 1147 // row count OF dates |
1179 | 1148 if (!parent.isValid()) { |
1180 // row count OF dates | 1149 if (!m_sourceRowCache.isEmpty()) |
1181 if (!parent.isValid()) { | 1150 return m_sourceRowCache.count(); |
1182 if (!m_sourceRowCache.isEmpty()) | 1151 QDate currentDate; |
1183 return m_sourceRowCache.count(); | 1152 int rows = 0; |
1184 QDate currentDate; | 1153 int totalRows = sourceModel().rowCount(); |
1185 int rows = 0; | 1154 |
1186 int totalRows = sourceModel().rowCount(); | 1155 for (int i = 0; i < totalRows; ++i) { |
1187 | 1156 QDate rowDate = sourceModel().index(i, 0).data(HistoryModel.DateRole).toDate(); |
1188 for (int i = 0; i < totalRows; ++i) { | 1157 if (rowDate != currentDate) { |
1189 QDate rowDate = sourceModel().index(i, 0).data(HistoryModel.DateRole).toDate(); | 1158 m_sourceRowCache.append(i); |
1190 if (rowDate != currentDate) { | 1159 currentDate = rowDate; |
1191 m_sourceRowCache.append(i); | 1160 ++rows; |
1192 currentDate = rowDate; | 1161 } |
1193 ++rows; | 1162 } |
1194 } | 1163 assert(m_sourceRowCache.count() == rows); |
1195 } | 1164 return rows; |
1196 Q_ASSERT(m_sourceRowCache.count() == rows); | 1165 } |
1197 return rows; | 1166 |
1198 } | 1167 // row count FOR a date |
1199 | 1168 int start = sourceDateRow(parent.row()); |
1200 // row count FOR a date | 1169 int end = sourceDateRow(parent.row() + 1); |
1201 int start = sourceDateRow(parent.row()); | 1170 return (end - start); |
1202 int end = sourceDateRow(parent.row() + 1); | 1171 } |
1203 return (end - start); | 1172 |
1204 } | 1173 |
1205 | 1174 QModelIndex mapFromSource(QModelIndex sourceIndex); |
1206 | |
1207 QModelIndex mapFromSource(QModelIndex &sourceIndex); | |
1208 { | 1175 { |
1209 if (!sourceIndex.isValid()) | 1176 if (!sourceIndex.isValid()) |
1210 return QModelIndex(); | 1177 return QModelIndex(); |
1211 | 1178 |
1212 if (m_sourceRowCache.isEmpty()) | 1179 if (m_sourceRowCache.isEmpty()) |
1213 rowCount(QModelIndex()); | 1180 rowCount(QModelIndex()); |
1214 | 1181 |
1215 QList<int>::iterator it; | 1182 QList<int>.iterator it; |
1216 it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); | 1183 it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), sourceIndex.row()); |
1217 if (*it != sourceIndex.row()) | 1184 if (*it != sourceIndex.row()) |
1218 --it; | 1185 --it; |
1219 int dateRow = qMax(0, it - m_sourceRowCache.begin()); | 1186 int dateRow = qMax(0, it - m_sourceRowCache.begin()); |
1220 int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); | 1187 int row = sourceIndex.row() - m_sourceRowCache.at(dateRow); |
1221 return createIndex(row, sourceIndex.column(), dateRow + 1); | 1188 return createIndex(row, sourceIndex.column(), dateRow + 1); |
1222 } | 1189 } |
1223 | 1190 |
1224 | 1191 |
1225 QModelIndex mapToSource(QModelIndex &proxyIndex) | 1192 QModelIndex mapToSource(QModelIndex proxyIndex) |
1226 { | 1193 { |
1227 int offset = proxyIndex.internalId(); | 1194 int offset = proxyIndex.internalId(); |
1228 if (offset == 0) | 1195 if (offset == 0) |
1229 return QModelIndex(); | 1196 return QModelIndex(); |
1230 int startDateRow = sourceDateRow(offset - 1); | 1197 int startDateRow = sourceDateRow(offset - 1); |
1231 return sourceModel().index(startDateRow + proxyIndex.row(), proxyIndex.column()); | 1198 return sourceModel().index(startDateRow + proxyIndex.row(), proxyIndex.column()); |
1232 } | 1199 } |
1233 | 1200 |
1234 | 1201 |
1235 QModelIndex index(int row, int column, QModelIndex &parent = QModelIndex()) | 1202 QModelIndex index(int row, int column, QModelIndex parent = QModelIndex()) |
1236 { | 1203 { |
1237 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.column() > 0) | 1204 if (row < 0 || column < 0 || column >= columnCount(parent) || parent.column() > 0) |
1238 return QModelIndex(); | 1205 return QModelIndex(); |
1239 | 1206 |
1240 if (!parent.isValid()) | 1207 if (!parent.isValid()) |
1241 return createIndex(row, column, 0); | 1208 return createIndex(row, column, 0); |
1242 return createIndex(row, column, parent.row() + 1); | 1209 return createIndex(row, column, parent.row() + 1); |
1243 } | 1210 } |
1244 | 1211 |
1245 | 1212 |
1246 QModelIndex parent(QModelIndex &index= QModelIndex()) | 1213 QModelIndex parent(QModelIndex index = QModelIndex()) |
1247 { | 1214 { |
1248 int offset = index.internalId(); | 1215 int offset = index.internalId(); |
1249 if (offset == 0 || !index.isValid()) | 1216 if (offset == 0 || !index.isValid()) |
1250 return QModelIndex(); | 1217 return QModelIndex(); |
1251 return createIndex(offset - 1, 0, 0); | 1218 return createIndex(offset - 1, 0, 0); |
1252 } | 1219 } |
1253 | 1220 |
1254 | 1221 |
1255 bool hasChildren(QModelIndex &parent = QModelIndex()) | 1222 bool hasChildren(QModelIndex parent = QModelIndex()) |
1256 { | 1223 { |
1257 QModelIndex grandparent = parent.parent(); | 1224 QModelIndex grandparent = parent.parent(); |
1258 if (!grandparent.isValid()) | 1225 if (!grandparent.isValid()) |
1259 return true; | 1226 return true; |
1260 return false; | 1227 return false; |
1261 } | 1228 } |
1262 | 1229 |
1263 | 1230 |
1264 Qt.ItemFlags flags(QModelIndex &index) | 1231 Qt.ItemFlags flags(QModelIndex index) |
1265 { | 1232 { |
1266 if (!index.isValid()) | 1233 if (!index.isValid()) |
1267 return Qt.NoItemFlags; | 1234 return Qt.NoItemFlags; |
1268 return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled; | 1235 return Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled; |
1269 } | 1236 } |
1270 | 1237 |
1271 bool removeRows(int row, int count, QModelIndex &parent = QModelIndex()); | 1238 bool removeRows(int row, int count, QModelIndex parent = QModelIndex()); |
1272 { | 1239 { |
1273 if (row < 0 || count <= 0 || row + count > rowCount(parent)) | 1240 if (row < 0 || count <= 0 || row + count > rowCount(parent)) |
1274 return false; | 1241 return false; |
1275 | 1242 |
1276 if (parent.isValid()) { | 1243 if (parent.isValid()) { |
1277 // removing pages | 1244 // removing pages |
1278 int offset = sourceDateRow(parent.row()); | 1245 int offset = sourceDateRow(parent.row()); |
1279 return sourceModel().removeRows(offset + row, count); | 1246 return sourceModel().removeRows(offset + row, count); |
1280 } else { | 1247 } else { |
1281 // removing whole dates | 1248 // removing whole dates |
1282 for (int i = row + count - 1; i >= row; --i) { | 1249 for (int i = row + count - 1; i >= row; --i) { |
1283 QModelIndex dateParent = index(i, 0); | 1250 QModelIndex dateParent = index(i, 0); |
1284 int offset = sourceDateRow(dateParent.row()); | 1251 int offset = sourceDateRow(dateParent.row()); |
1285 if (!sourceModel().removeRows(offset, rowCount(dateParent))) | 1252 if (!sourceModel().removeRows(offset, rowCount(dateParent))) |
1286 return false; | 1253 return false; |
1287 } | 1254 } |
1288 } | 1255 } |
1289 return true; | 1256 return true; |
1290 } | 1257 } |
1291 | 1258 |
1292 | 1259 |
1293 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole) | 1260 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole) |
1294 { | 1261 { |
1295 return sourceModel().headerData(section, orientation, role); | 1262 return sourceModel().headerData(section, orientation, role); |
1296 } | 1263 } |
1297 | 1264 |
1298 | 1265 |
1299 | 1266 |
1300 void setSourceModel(QAbstractItemModel *newSourceModel) | 1267 void setSourceModel(QAbstractItemModel newSourceModel) |
1301 { | 1268 { |
1302 if (sourceModel()) { | 1269 if (sourceModel()) { |
1303 disconnect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); | 1270 sourceModel.modelReset.disconnect(&this.sourceReset); |
1304 disconnect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); | 1271 sourceModel.layoutChanged.disconnect(&this.sourceReset); |
1305 disconnect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 1272 sourceModel.rowsInserted.disconnect(&this.sourceRowsInserted); |
1306 this, SLOT(sourceRowsInserted(QModelIndex &, int, int))); | 1273 sourceModel.rowsRemoved.disconnect(&this.sourceRowsRemoved); |
1307 disconnect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 1274 } |
1308 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | 1275 |
1309 } | 1276 QAbstractProxyModel.setSourceModel(newSourceModel); |
1310 | 1277 |
1311 QAbstractProxyModel::setSourceModel(newSourceModel); | 1278 if (newSourceModel) { |
1312 | 1279 sourceModel.modelReset.connect(&this.sourceReset); |
1313 if (newSourceModel) { | 1280 sourceModel.layoutChanged.connect(&this.sourceReset); |
1314 connect(sourceModel(), SIGNAL(modelReset()), this, SLOT(sourceReset())); | 1281 sourceModel.rowsInserted.connect(&this.sourceRowsInserted); |
1315 connect(sourceModel(), SIGNAL(layoutChanged()), this, SLOT(sourceReset())); | 1282 sourceModel.rowsRemoved.connect(&this.sourceRowsRemoved); |
1316 connect(sourceModel(), SIGNAL(rowsInserted(QModelIndex &, int, int)), | 1283 } |
1317 this, SLOT(sourceRowsInserted(QModelIndex &, int, int))); | 1284 |
1318 connect(sourceModel(), SIGNAL(rowsRemoved(QModelIndex &, int, int)), | 1285 reset(); |
1319 this, SLOT(sourceRowsRemoved(QModelIndex &, int, int))); | 1286 } |
1320 } | |
1321 | |
1322 reset(); | |
1323 } | |
1324 | 1287 |
1325 | 1288 |
1326 private slots: | 1289 private slots: |
1327 | 1290 |
1328 void sourceReset() | 1291 void sourceReset() |
1329 { | 1292 { |
1330 m_sourceRowCache.clear(); | 1293 m_sourceRowCache.clear(); |
1331 reset(); | 1294 reset(); |
1332 } | 1295 } |
1333 | 1296 |
1334 void sourceRowsInserted(QModelIndex &parent, int start, int end); | 1297 void sourceRowsInserted(QModelIndex parent, int start, int end); |
1335 { | 1298 { |
1336 Q_UNUSED(parent); // Avoid warnings when compiling release | 1299 Q_UNUSED(parent); // Avoid warnings when compiling release |
1337 Q_ASSERT(!parent.isValid()); | 1300 assert(!parent.isValid()); |
1338 if (start != 0 || start != end) { | 1301 if (start != 0 || start != end) { |
1339 m_sourceRowCache.clear(); | 1302 m_sourceRowCache.clear(); |
1340 reset(); | 1303 reset(); |
1341 return; | 1304 return; |
1342 } | 1305 } |
1351 beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); | 1314 beginInsertRows(treeParent, treeIndex.row(), treeIndex.row()); |
1352 endInsertRows(); | 1315 endInsertRows(); |
1353 } | 1316 } |
1354 } | 1317 } |
1355 | 1318 |
1356 | 1319 void sourceRowsRemoved(QModelIndex parent, int start, int end); |
1357 void sourceRowsRemoved(QModelIndex &parent, int start, int end); | 1320 { |
1358 { | 1321 //Q_UNUSED(parent); // Avoid warnings when compiling release |
1359 Q_UNUSED(parent); // Avoid warnings when compiling release | 1322 assert(!parent.isValid()); |
1360 Q_ASSERT(!parent.isValid()); | 1323 if (m_sourceRowCache.isEmpty()) |
1361 if (m_sourceRowCache.isEmpty()) | 1324 return; |
1362 return; | 1325 for (int i = end; i >= start;) { |
1363 for (int i = end; i >= start;) { | 1326 QList<int>::iterator it; |
1364 QList<int>::iterator it; | 1327 it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); |
1365 it = qLowerBound(m_sourceRowCache.begin(), m_sourceRowCache.end(), i); | 1328 // playing it safe |
1366 // playing it safe | 1329 if (it == m_sourceRowCache.end()) { |
1367 if (it == m_sourceRowCache.end()) { | 1330 m_sourceRowCache.clear(); |
1368 m_sourceRowCache.clear(); | 1331 reset(); |
1369 reset(); | 1332 return; |
1370 return; | 1333 } |
1371 } | 1334 |
1372 | 1335 if (*it != i) |
1373 if (*it != i) | 1336 --it; |
1374 --it; | 1337 int row = qMax(0, it - m_sourceRowCache.begin()); |
1375 int row = qMax(0, it - m_sourceRowCache.begin()); | 1338 int offset = m_sourceRowCache[row]; |
1376 int offset = m_sourceRowCache[row]; | 1339 QModelIndex dateParent = index(row, 0); |
1377 QModelIndex dateParent = index(row, 0); | 1340 // If we can remove all the rows in the date do that and skip over them |
1378 // If we can remove all the rows in the date do that and skip over them | 1341 int rc = rowCount(dateParent); |
1379 int rc = rowCount(dateParent); | 1342 if (i - rc + 1 == offset && start <= i - rc + 1) { |
1380 if (i - rc + 1 == offset && start <= i - rc + 1) { | 1343 beginRemoveRows(QModelIndex(), row, row); |
1381 beginRemoveRows(QModelIndex(), row, row); | 1344 m_sourceRowCache.removeAt(row); |
1382 m_sourceRowCache.removeAt(row); | 1345 i -= rc + 1; |
1383 i -= rc + 1; | 1346 } else { |
1384 } else { | 1347 beginRemoveRows(dateParent, i - offset, i - offset); |
1385 beginRemoveRows(dateParent, i - offset, i - offset); | 1348 ++row; |
1386 ++row; | 1349 --i; |
1387 --i; | 1350 } |
1388 } | 1351 for (int j = row; j < m_sourceRowCache.count(); ++j) |
1389 for (int j = row; j < m_sourceRowCache.count(); ++j) | 1352 --m_sourceRowCache[j]; |
1390 --m_sourceRowCache[j]; | 1353 endRemoveRows(); |
1391 endRemoveRows(); | 1354 } |
1392 } | 1355 } |
1393 } | |
1394 | 1356 |
1395 private: | 1357 private: |
1396 | 1358 |
1397 // Translate the top level date row into the offset where that date starts | 1359 // Translate the top level date row into the offset where that date starts |
1398 int sourceDateRow(int row) | 1360 int sourceDateRow(int row) |
1409 return sourceModel().rowCount(); | 1371 return sourceModel().rowCount(); |
1410 } | 1372 } |
1411 return m_sourceRowCache.at(row); | 1373 return m_sourceRowCache.at(row); |
1412 } | 1374 } |
1413 | 1375 |
1414 mutable QList<int> m_sourceRowCache; | 1376 QList<int> m_sourceRowCache; |
1415 } | 1377 } |
1416 | 1378 |
1417 // A modified QSortFilterProxyModel that always accepts the root nodes in the tree | 1379 // A modified QSortFilterProxyModel that always accepts the root nodes in the tree |
1418 // so filtering is only done on the children. | 1380 // so filtering is only done on the children. |
1419 // Used in the HistoryDialog | 1381 // Used in the HistoryDialog |
1420 class TreeProxyModel : public QSortFilterProxyModel | 1382 class TreeProxyModel : public QSortFilterProxyModel |
1421 { | 1383 { |
1422 Q_OBJECT | |
1423 | |
1424 public: | 1384 public: |
1425 this(QObject *parent = null) | 1385 |
1386 this(QObject parent = null) | |
1426 { | 1387 { |
1427 super(parent); | 1388 super(parent); |
1428 setSortRole(HistoryModel.DateTimeRole); | 1389 setSortRole(HistoryModel.DateTimeRole); |
1429 setFilterCaseSensitivity(Qt.CaseInsensitive); | 1390 setFilterCaseSensitivity(Qt.CaseInsensitive); |
1430 } | 1391 } |
1431 | 1392 |
1432 protected: | 1393 protected: |
1433 bool filterAcceptsRow(int source_row, QModelIndex &source_parent) | 1394 bool filterAcceptsRow(int source_row, QModelIndex source_parent) |
1434 { | 1395 { |
1435 if (!source_parent.isValid()) | 1396 if (!source_parent.isValid()) |
1436 return true; | 1397 return true; |
1437 return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); | 1398 return QSortFilterProxyModel.filterAcceptsRow(source_row, source_parent); |
1438 } | 1399 } |
1439 } | 1400 } |
1440 | 1401 |
1441 #include "ui_history.h" | 1402 import ui_history; |
1442 | 1403 |
1443 class HistoryDialog : public QDialog, public Ui_HistoryDialog | 1404 class HistoryDialog : public QDialog, public Ui_HistoryDialog |
1444 { | 1405 { |
1445 Q_OBJECT | 1406 mixin Signal!("openUrl", QUrl url); |
1446 | |
1447 signals: | |
1448 void openUrl(QUrl &url); | |
1449 | 1407 |
1450 public: | 1408 public: |
1451 | 1409 |
1452 this(QWidget *parent = null, HistoryManager *history = null) : QDialog(parent) | 1410 this(QWidget parent = null, HistoryManager history = null) : QDialog(parent) |
1453 { | 1411 { |
1454 HistoryManager *history = setHistory; | 1412 HistoryManager history = setHistory; |
1455 if (!history) | 1413 if (!history) |
1456 history = BrowserApplication::historyManager(); | 1414 history = BrowserApplication.historyManager(); |
1457 setupUi(this); | 1415 setupUi(this); |
1458 tree.setUniformRowHeights(true); | 1416 tree.setUniformRowHeights(true); |
1459 tree.setSelectionBehavior(QAbstractItemView::SelectRows); | 1417 tree.setSelectionBehavior(QAbstractItemView.SelectRows); |
1460 tree.setTextElideMode(Qt.ElideMiddle); | 1418 tree.setTextElideMode(Qt.ElideMiddle); |
1461 QAbstractItemModel *model = history.historyTreeModel(); | 1419 auto model = history.historyTreeModel(); |
1462 TreeProxyModel *proxyModel = new TreeProxyModel(this); | 1420 auto proxyModel = new TreeProxyModel(this); |
1463 connect(search, SIGNAL(textChanged(QString)), | 1421 search.textChanged(QString).connect(&proxyModel.setFilterFixedString(QString)); |
1464 proxyModel, SLOT(setFilterFixedString(QString))); | 1422 removeButton.clicked.connect(&tree.removeOne); |
1465 connect(removeButton, SIGNAL(clicked()), tree, SLOT(removeOne())); | 1423 removeAllButton.clicked.connect(&history.clear); |
1466 connect(removeAllButton, SIGNAL(clicked()), history, SLOT(clear())); | |
1467 proxyModel.setSourceModel(model); | 1424 proxyModel.setSourceModel(model); |
1468 tree.setModel(proxyModel); | 1425 tree.setModel(proxyModel); |
1469 tree.setExpanded(proxyModel.index(0, 0), true); | 1426 tree.setExpanded(proxyModel.index(0, 0), true); |
1470 tree.setAlternatingRowColors(true); | 1427 tree.setAlternatingRowColors(true); |
1471 QFontMetrics fm(font()); | 1428 QFontMetrics fm = font(); |
1472 int header = fm.width(QLatin1Char('m')) * 40; | 1429 int header = fm.width(QLatin1Char('m')) * 40; |
1473 tree.header().resizeSection(0, header); | 1430 tree.header().resizeSection(0, header); |
1474 tree.header().setStretchLastSection(true); | 1431 tree.header().setStretchLastSection(true); |
1475 connect(tree, SIGNAL(activated(QModelIndex&)), | 1432 tree.activated(QModelIndex).connect(this.open); |
1476 this, SLOT(open())); | |
1477 tree.setContextMenuPolicy(Qt.CustomContextMenu); | 1433 tree.setContextMenuPolicy(Qt.CustomContextMenu); |
1478 connect(tree, SIGNAL(customContextMenuRequested(QPoint &)), | 1434 tree.customContextMenuRequested(QPoint).connect(&this.customContextMenuRequested(QPoint)); |
1479 this, SLOT(customContextMenuRequested(QPoint &))); | 1435 } |
1480 } | 1436 |
1481 | 1437 private: |
1482 private slots: | 1438 |
1483 | 1439 void customContextMenuRequested(QPoint pos) |
1484 void customContextMenuRequested(QPoint &pos) | |
1485 { | 1440 { |
1486 QMenu menu; | 1441 QMenu menu; |
1487 QModelIndex index = tree.indexAt(pos); | 1442 QModelIndex index = tree.indexAt(pos); |
1488 index = index.sibling(index.row(), 0); | 1443 index = index.sibling(index.row(), 0); |
1489 if (index.isValid() && !tree.model().hasChildren(index)) { | 1444 if (index.isValid() && !tree.model().hasChildren(index)) { |
1490 menu.addAction(tr("Open"), this, SLOT(open())); | 1445 menu.addAction(tr("Open"), this, SLOT(open())); |
1491 menu.addSeparator(); | 1446 menu.addSeparator(); |
1492 menu.addAction(tr("Copy"), this, SLOT(copy())); | 1447 menu.addAction(tr("Copy"), this, SLOT(copy())); |
1493 } | 1448 } |
1494 menu.addAction(tr("Delete"), tree, SLOT(removeOne())); | 1449 menu.addAction(tr("Delete"), tree, SLOT(removeOne())); |
1495 menu.exec(QCursor::pos()); | 1450 menu.exec(QCursor.pos()); |
1496 } | 1451 } |
1497 | 1452 |
1498 | 1453 |
1499 void open() | 1454 void open() |
1500 { | 1455 { |
1509 QModelIndex index = tree.currentIndex(); | 1464 QModelIndex index = tree.currentIndex(); |
1510 if (!index.parent().isValid()) | 1465 if (!index.parent().isValid()) |
1511 return; | 1466 return; |
1512 QString url = index.data(HistoryModel.UrlStringRole).toString(); | 1467 QString url = index.data(HistoryModel.UrlStringRole).toString(); |
1513 | 1468 |
1514 QClipboard *clipboard = QApplication::clipboard(); | 1469 QClipboard clipboard = QApplication.clipboard(); |
1515 clipboard.setText(url); | 1470 clipboard.setText(url); |
1516 } | 1471 } |
1517 } | 1472 } |