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 }