comparison demos/browser/bookmarks.d @ 73:7bfd46c330dc

more porting
author mandel
date Fri, 22 May 2009 10:59:00 +0000
parents b149ef2cb18b
children 37caa90ce503
comparison
equal deleted inserted replaced
72:b149ef2cb18b 73:7bfd46c330dc
73 const char[] BOOKMARKBAR = "Bookmarks Bar"; 73 const char[] BOOKMARKBAR = "Bookmarks Bar";
74 const char[] BOOKMARKMENU = "Bookmarks Menu"; 74 const char[] BOOKMARKMENU = "Bookmarks Menu";
75 75
76 76
77 /*! 77 /*!
78 Bookmark manager, owner of the bookmarks, loads, saves and basic tasks 78 Bookmark manager, owner of the bookmarks, loads, saves and basic tasks
79 */ 79 */
80
81 class BookmarksManager : public QObject 80 class BookmarksManager : public QObject
82 { 81 {
82 mixin Signal!("entryAdded", BookmarkNode item);
83 mixin Signal!("entryRemoved", BookmarkNode parent, int row, BookmarkNode item);
84 mixin Signal!("entryChanged", BookmarkNode item);
85
86 public:
87
88 this(QObject parent = null)
89 {
90 super(parent);
91 m_loaded = false;
92 m_saveTimer = new AutoSaver(this);
93 m_bookmarkRootNode = null;
94 m_bookmarkModel = null;
95 this.entryAdded.connect(&m_saveTimer.changeOccurred);
96 this.entryRemoved.connect(&m_saveTimer.changeOccurred);
97 this.entryChanged.connect(&m_saveTimer.changeOccurred);
98 }
99
100 ~this()
101 {
102 m_saveTimer.saveIfNeccessary();
103 }
104
105 void addBookmark(BookmarkNode parent, BookmarkNode node, int row = -1)
106 {
107 if (!m_loaded)
108 return;
109 assert(parent);
110 InsertBookmarksCommand command = new InsertBookmarksCommand(this, parent, node, row);
111 m_commands.push(command);
112 }
113
114
115 void removeBookmark(BookmarkNode node);
116 {
117 if (!m_loaded)
118 return;
119
120 assert(node);
121 BookmarkNode parent = node.parent();
122 int row = parent.children().indexOf(node);
123 RemoveBookmarksCommand command = new RemoveBookmarksCommand(this, parent, row);
124 m_commands.push(command);
125 }
126
127 void setTitle(BookmarkNode node, QString newTitle);
128 {
129 if (!m_loaded)
130 return;
131
132 assert(node);
133 ChangeBookmarkCommand command = new ChangeBookmarkCommand(this, node, newTitle, true);
134 m_commands.push(command);
135 }
136
137
138 void setUrl(BookmarkNode node, QString newUrl);
139 {
140 if (!m_loaded)
141 return;
142
143 assert(node);
144 ChangeBookmarkCommand command = new ChangeBookmarkCommand(this, node, newUrl, false);
145 m_commands.push(command);
146 }
147
148
149 void changeExpanded()
150 {
151 m_saveTimer.changeOccurred();
152 }
153
154 BookmarkNode bookmarks();
155 {
156 if (!m_loaded)
157 load();
158 return m_bookmarkRootNode;
159 }
160
161 BookmarkNode menu();
162 {
163 if (!m_loaded)
164 load();
165
166 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
167 BookmarkNode node = m_bookmarkRootNode.children().at(i);
168 if (node.title == tr(BOOKMARKMENU))
169 return node;
170 }
171 assert(false);
172 return 0;
173 }
174
175 BookmarkNode toolbar();
176 {
177 if (!m_loaded)
178 load();
179
180 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
181 BookmarkNode node = m_bookmarkRootNode.children().at(i);
182 if (node.title == tr(BOOKMARKBAR))
183 return node;
184 }
185 assert(false);
186 return 0;
187 }
188
189 BookmarksModel bookmarksModel()
190 {
191 if (!m_bookmarkModel)
192 m_bookmarkModel = new BookmarksModel(this, this);
193 return m_bookmarkModel;
194 }
195
196 QUndoStack undoRedoStack() { return m_commands; };
197
198 public:
199
200 void importBookmarks()
201 {
202 QString fileName = QFileDialog.getOpenFileName(0, tr("Open File"), QString(), tr("XBEL (*.xbel *.xml)"));
203 if (fileName.isEmpty())
204 return;
205
206 XbelReader reader;
207 BookmarkNode importRootNode = reader.read(fileName);
208 if (reader.error() != QXmlStreamReader.NoError) {
209 QMessageBox.warning(0, QLatin1String("Loading Bookmark"),
210 tr("Error when loading bookmarks on line %1, column %2:\n"
211 "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
212 }
213
214 importRootNode.setType(BookmarkNode.Folder);
215 importRootNode.title = (tr("Imported %1").arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)));
216 addBookmark(menu(), importRootNode);
217 }
218
219
220 void exportBookmarks()
221 {
222 QString fileName = QFileDialog.getSaveFileName(0, tr("Save File"),
223 tr("%1 Bookmarks.xbel").arg(QCoreApplication.applicationName()),
224 tr("XBEL (*.xbel *.xml)"));
225 if (fileName.isEmpty())
226 return;
227
228 XbelWriter writer;
229 if (!writer.write(fileName, m_bookmarkRootNode))
230 QMessageBox.critical(0, tr("Export error"), tr("error saving bookmarks"));
231 }
232
233 private:
234
235 void save()
236 {
237 if (!m_loaded)
238 return;
239
240 XbelWriter writer;
241 QString dir = QDesktopServices.storageLocation(QDesktopServices.DataLocation);
242 QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
243 if (!writer.write(bookmarkFile, m_bookmarkRootNode))
244 qWarning() << "BookmarkManager: error saving to" << bookmarkFile;
245 }
246
247 private:
83 248
84 mixin Signal!("entryAdded", BookmarkNode item); 249 void load()
85 mixin Signal!("entryRemoved", BookmarkNode parent, int row, BookmarkNode item); 250 {
86 mixin Signal!("entryChanged", BookmarkNode item); 251 if (m_loaded)
87 252 return;
88 public: 253 m_loaded = true;
89 BookmarksManager(QObject parent = null) 254
90 { 255 QString dir = QDesktopServices.storageLocation(QDesktopServices.DataLocation);
91 super(parent); 256 QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
92 m_loaded = false; 257 if (!QFile.exists(bookmarkFile))
93 m_saveTimer = new AutoSaver(this); 258 bookmarkFile = QLatin1String(":defaultbookmarks.xbel");
94 m_bookmarkRootNode = null; 259
95 m_bookmarkModel = null; 260 XbelReader reader;
96 this.entryAdded.connect(&m_saveTimer.changeOccurred); 261 m_bookmarkRootNode = reader.read(bookmarkFile);
97 this.entryRemoved.connect(&m_saveTimer.changeOccurred); 262 if (reader.error() != QXmlStreamReader.NoError) {
98 this.entryChanged.connect(&m_saveTimer.changeOccurred); 263 QMessageBox.warning(0, QLatin1String("Loading Bookmark"),
99 } 264 tr("Error when loading bookmarks on line %1, column %2:\n"
100 265 "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
101 266 }
102 ~this() 267
103 { 268 BookmarkNode toolbar = null;
104 m_saveTimer.saveIfNeccessary(); 269 BookmarkNode menu = null;
105 } 270 QList<BookmarkNode> others;
106 271 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
107 272 BookmarkNode node = m_bookmarkRootNode.children().at(i);
108 void addBookmark(BookmarkNode parent, BookmarkNode node, int row = -1) 273 if (node.type() == BookmarkNode.Folder) {
109 { 274 // Automatically convert
110 if (!m_loaded) 275 if (node.title == tr("Toolbar Bookmarks") && !toolbar) {
111 return; 276 node.title = tr(BOOKMARKBAR);
112 assert(parent); 277 }
113 InsertBookmarksCommand command = new InsertBookmarksCommand(this, parent, node, row); 278 if (node.title == tr(BOOKMARKBAR) && !toolbar) {
114 m_commands.push(command); 279 toolbar = node;
115 } 280 }
116 281
117 282 // Automatically convert
118 void removeBookmark(BookmarkNode node); 283 if (node.title == tr("Menu") && !menu) {
119 { 284 node.title = tr(BOOKMARKMENU);
120 if (!m_loaded) 285 }
121 return; 286 if (node.title == tr(BOOKMARKMENU) && !menu) {
122 287 menu = node;
123 assert(node); 288 }
124 BookmarkNode parent = node.parent(); 289 } else {
125 int row = parent.children().indexOf(node); 290 others.append(node);
126 RemoveBookmarksCommand command = new RemoveBookmarksCommand(this, parent, row); 291 }
127 m_commands.push(command); 292 m_bookmarkRootNode.remove(node);
128 } 293 }
129 294 assert(m_bookmarkRootNode.children().count() == 0);
130 void setTitle(BookmarkNode node, QString newTitle); 295 if (!toolbar) {
131 { 296 toolbar = new BookmarkNode(BookmarkNode.Folder, m_bookmarkRootNode);
132 if (!m_loaded) 297 toolbar.title = tr(BOOKMARKBAR);
133 return; 298 } else {
134 299 m_bookmarkRootNode.add(toolbar);
135 assert(node); 300 }
136 ChangeBookmarkCommand command = new ChangeBookmarkCommand(this, node, newTitle, true); 301
137 m_commands.push(command); 302 if (!menu) {
138 } 303 menu = new BookmarkNode(BookmarkNode.Folder, m_bookmarkRootNode);
139 304 menu.title = tr(BOOKMARKMENU);
140 305 } else {
141 void setUrl(BookmarkNode node, QString newUrl); 306 m_bookmarkRootNode.add(menu);
142 { 307 }
143 if (!m_loaded) 308
144 return; 309 for (int i = 0; i < others.count(); ++i)
145 310 menu.add(others.at(i));
146 assert(node); 311 }
147 ChangeBookmarkCommand command = new ChangeBookmarkCommand(this, node, newUrl, false); 312
148 m_commands.push(command); 313 bool m_loaded;
149 } 314 AutoSaver m_saveTimer;
150 315 BookmarkNode m_bookmarkRootNode;
151 316 BookmarksModel m_bookmarkModel;
152 void changeExpanded() 317 QUndoStack m_commands;
153 {
154 m_saveTimer.changeOccurred();
155 }
156
157 BookmarkNode bookmarks();
158 {
159 if (!m_loaded)
160 load();
161 return m_bookmarkRootNode;
162 }
163
164 BookmarkNode menu();
165 {
166 if (!m_loaded)
167 load();
168
169 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
170 BookmarkNode node = m_bookmarkRootNode.children().at(i);
171 if (node.title == tr(BOOKMARKMENU))
172 return node;
173 }
174 assert(false);
175 return 0;
176 }
177
178 BookmarkNode toolbar();
179 {
180 if (!m_loaded)
181 load();
182
183 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
184 BookmarkNode node = m_bookmarkRootNode.children().at(i);
185 if (node.title == tr(BOOKMARKBAR))
186 return node;
187 }
188 assert(false);
189 return 0;
190 }
191
192 BookmarksModel bookmarksModel()
193 {
194 if (!m_bookmarkModel)
195 m_bookmarkModel = new BookmarksModel(this, this);
196 return m_bookmarkModel;
197 }
198
199 QUndoStack undoRedoStack() { return m_commands; };
200
201 public slots:
202 void importBookmarks()
203 {
204 QString fileName = QFileDialog.getOpenFileName(0, tr("Open File"),
205 QString(),
206 tr("XBEL (*.xbel *.xml)"));
207 if (fileName.isEmpty())
208 return;
209
210 XbelReader reader;
211 BookmarkNode importRootNode = reader.read(fileName);
212 if (reader.error() != QXmlStreamReader.NoError) {
213 QMessageBox.warning(0, QLatin1String("Loading Bookmark"),
214 tr("Error when loading bookmarks on line %1, column %2:\n"
215 "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
216 }
217
218 importRootNode.setType(BookmarkNode.Folder);
219 importRootNode.title = (tr("Imported %1").arg(QDate.currentDate().toString(Qt.SystemLocaleShortDate)));
220 addBookmark(menu(), importRootNode);
221 }
222
223
224 void exportBookmarks()
225 {
226 QString fileName = QFileDialog.getSaveFileName(0, tr("Save File"),
227 tr("%1 Bookmarks.xbel").arg(QCoreApplication.applicationName()),
228 tr("XBEL (*.xbel *.xml)"));
229 if (fileName.isEmpty())
230 return;
231
232 XbelWriter writer;
233 if (!writer.write(fileName, m_bookmarkRootNode))
234 QMessageBox.critical(0, tr("Export error"), tr("error saving bookmarks"));
235 }
236
237 private slots:
238 void save()
239 {
240 if (!m_loaded)
241 return;
242
243 XbelWriter writer;
244 QString dir = QDesktopServices.storageLocation(QDesktopServices.DataLocation);
245 QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
246 if (!writer.write(bookmarkFile, m_bookmarkRootNode))
247 qWarning() << "BookmarkManager: error saving to" << bookmarkFile;
248 }
249
250 private:
251 void load()
252 {
253 if (m_loaded)
254 return;
255 m_loaded = true;
256
257 QString dir = QDesktopServices.storageLocation(QDesktopServices.DataLocation);
258 QString bookmarkFile = dir + QLatin1String("/bookmarks.xbel");
259 if (!QFile.exists(bookmarkFile))
260 bookmarkFile = QLatin1String(":defaultbookmarks.xbel");
261
262 XbelReader reader;
263 m_bookmarkRootNode = reader.read(bookmarkFile);
264 if (reader.error() != QXmlStreamReader.NoError) {
265 QMessageBox.warning(0, QLatin1String("Loading Bookmark"),
266 tr("Error when loading bookmarks on line %1, column %2:\n"
267 "%3").arg(reader.lineNumber()).arg(reader.columnNumber()).arg(reader.errorString()));
268 }
269
270 BookmarkNode toolbar = null;
271 BookmarkNode menu = null;
272 QList<BookmarkNode> others;
273 for (int i = m_bookmarkRootNode.children().count() - 1; i >= 0; --i) {
274 BookmarkNode node = m_bookmarkRootNode.children().at(i);
275 if (node.type() == BookmarkNode.Folder) {
276 // Automatically convert
277 if (node.title == tr("Toolbar Bookmarks") && !toolbar) {
278 node.title = tr(BOOKMARKBAR);
279 }
280 if (node.title == tr(BOOKMARKBAR) && !toolbar) {
281 toolbar = node;
282 }
283
284 // Automatically convert
285 if (node.title == tr("Menu") && !menu) {
286 node.title = tr(BOOKMARKMENU);
287 }
288 if (node.title == tr(BOOKMARKMENU) && !menu) {
289 menu = node;
290 }
291 } else {
292 others.append(node);
293 }
294 m_bookmarkRootNode.remove(node);
295 }
296 assert(m_bookmarkRootNode.children().count() == 0);
297 if (!toolbar) {
298 toolbar = new BookmarkNode(BookmarkNode.Folder, m_bookmarkRootNode);
299 toolbar.title = tr(BOOKMARKBAR);
300 } else {
301 m_bookmarkRootNode.add(toolbar);
302 }
303
304 if (!menu) {
305 menu = new BookmarkNode(BookmarkNode.Folder, m_bookmarkRootNode);
306 menu.title = tr(BOOKMARKMENU);
307 } else {
308 m_bookmarkRootNode.add(menu);
309 }
310
311 for (int i = 0; i < others.count(); ++i)
312 menu.add(others.at(i));
313 }
314
315
316 bool m_loaded;
317 AutoSaver m_saveTimer;
318 BookmarkNode m_bookmarkRootNode;
319 BookmarksModel m_bookmarkModel;
320 QUndoStack m_commands;
321
322 friend class RemoveBookmarksCommand;
323 friend class ChangeBookmarkCommand;
324 } 318 }
325 319
326 class RemoveBookmarksCommand : public QUndoCommand 320 class RemoveBookmarksCommand : public QUndoCommand
327 { 321 {
328
329 public: 322 public:
330 this(BookmarksManager m_bookmarkManagaer, BookmarkNode parent, int row) 323
331 { 324 this(BookmarksManager m_bookmarkManagaer, BookmarkNode parent, int row)
332 super(BookmarksManager.tr("Remove Bookmark")) 325 {
333 m_row = row; 326 super(BookmarksManager.tr("Remove Bookmark"))
334 m_bookmarkManagaer = m_bookmarkManagaer; 327 m_row = row;
335 m_node = parent.children().value(row); 328 m_bookmarkManagaer = m_bookmarkManagaer;
336 m_parent = parent; 329 m_node = parent.children().value(row);
337 m_done = false; 330 m_parent = parent;
331 m_done = false;
332 }
333
334 ~this()
335 {
336 if (m_done && !m_node.parent()) {
337 delete m_node;
338 }
339 }
340
341 void undo()
342 {
343 m_parent.add(m_node, m_row);
344 emit m_bookmarkManagaer.entryAdded(m_node);
345 m_done = false;
346 }
347
348 void redo()
349 {
350 m_parent.remove(m_node);
351 emit m_bookmarkManagaer.entryRemoved(m_parent, m_row, m_node);
352 m_done = true;
353 }
354
355 protected:
356
357 int m_row;
358 BookmarksManager m_bookmarkManagaer;
359 BookmarkNode m_node;
360 BookmarkNode m_parent;
361 bool m_done;
338 } 362 }
339
340 ~this()
341 {
342 if (m_done && !m_node.parent()) {
343 delete m_node;
344 }
345 }
346
347 void undo()
348 {
349 m_parent.add(m_node, m_row);
350 emit m_bookmarkManagaer.entryAdded(m_node);
351 m_done = false;
352 }
353
354 void redo()
355 {
356 m_parent.remove(m_node);
357 emit m_bookmarkManagaer.entryRemoved(m_parent, m_row, m_node);
358 m_done = true;
359 }
360
361 protected:
362 int m_row;
363 BookmarksManager m_bookmarkManagaer;
364 BookmarkNode m_node;
365 BookmarkNode m_parent;
366 bool m_done;
367 };
368 363
369 class InsertBookmarksCommand : public RemoveBookmarksCommand 364 class InsertBookmarksCommand : public RemoveBookmarksCommand
370 { 365 {
371
372 public: 366 public:
373 this(BookmarksManager m_bookmarkManagaer, 367 this(BookmarksManager m_bookmarkManagaer, BookmarkNode parent, BookmarkNode node, int row)
374 BookmarkNode parent, BookmarkNode node, int row) 368 {
375 { 369 super(m_bookmarkManagaer, parent, row);
376 super(m_bookmarkManagaer, parent, row); 370
377 371 setText(BookmarksManager.tr("Insert Bookmark"));
378 setText(BookmarksManager.tr("Insert Bookmark")); 372 m_node = node;
379 m_node = node; 373 }
380 } 374
381 375 void undo() { RemoveBookmarksCommand.redo(); }
382 void undo() { RemoveBookmarksCommand.redo(); } 376 void redo() { RemoveBookmarksCommand.undo(); }
383 void redo() { RemoveBookmarksCommand.undo(); }
384 } 377 }
385 378
386 class ChangeBookmarkCommand : public QUndoCommand 379 class ChangeBookmarkCommand : public QUndoCommand
387 { 380 {
388
389 public: 381 public:
390 this(BookmarksManager m_bookmarkManagaer, 382
391 BookmarkNode node, QString newValue, bool title) 383 this(BookmarksManager m_bookmarkManagaer, BookmarkNode node, QString newValue, bool title)
392 { 384 {
393 super(); 385 super();
394 m_bookmarkManagaer = m_bookmarkManagaer; 386 m_bookmarkManagaer = m_bookmarkManagaer;
395 m_title = title; 387 m_title = title;
396 m_newValue = newValue; 388 m_newValue = newValue;
397 m_node = node; 389 m_node = node;
398 if (m_title) { 390 if (m_title) {
399 m_oldValue = m_node.title; 391 m_oldValue = m_node.title;
400 setText(BookmarksManager.tr("Name Change")); 392 setText(BookmarksManager.tr("Name Change"));
401 } else { 393 } else {
402 m_oldValue = m_node.url; 394 m_oldValue = m_node.url;
403 setText(BookmarksManager.tr("Address Change")); 395 setText(BookmarksManager.tr("Address Change"));
404 } 396 }
397 }
398
399 void undo()
400 {
401 if (m_title)
402 m_node.title = m_oldValue;
403 else
404 m_node.url = m_oldValue;
405 emit m_bookmarkManagaer.entryChanged(m_node);
406 }
407
408 void redo()
409 {
410 if (m_title)
411 m_node.title = m_newValue;
412 else
413 m_node.url = m_newValue;
414 emit m_bookmarkManagaer.entryChanged(m_node);
415 }
416
417 private:
418
419 BookmarksManager m_bookmarkManagaer;
420 bool m_title;
421 QString m_oldValue;
422 QString m_newValue;
423 BookmarkNode m_node;
405 } 424 }
406 425
407 void undo()
408 {
409 if (m_title)
410 m_node.title = m_oldValue;
411 else
412 m_node.url = m_oldValue;
413 emit m_bookmarkManagaer.entryChanged(m_node);
414 }
415
416 void redo()
417 {
418 if (m_title)
419 m_node.title = m_newValue;
420 else
421 m_node.url = m_newValue;
422 emit m_bookmarkManagaer.entryChanged(m_node);
423 }
424
425 private:
426 BookmarksManager m_bookmarkManagaer;
427 bool m_title;
428 QString m_oldValue;
429 QString m_newValue;
430 BookmarkNode m_node;
431 }
432
433 /*! 426 /*!
434 BookmarksModel is a QAbstractItemModel wrapper around the BookmarkManager 427 BookmarksModel is a QAbstractItemModel wrapper around the BookmarkManager
435 */ 428 */
436 import qt.gui.QIcon; 429 import qt.gui.QIcon;
437 430
438 class BookmarksModel : public QAbstractItemModel 431 class BookmarksModel : public QAbstractItemModel
439 { 432 {
440
441 public: 433 public:
442 void entryAdded(BookmarkNode item) 434
443 { 435 void entryAdded(BookmarkNode item)
444 assert(item && item.parent()); 436 {
445 int row = item.parent().children().indexOf(item); 437 assert(item && item.parent());
446 BookmarkNode parent = item.parent(); 438 int row = item.parent().children().indexOf(item);
447 // item was already added so remove beore beginInsertRows is called 439 BookmarkNode parent = item.parent();
448 parent.remove(item); 440 // item was already added so remove beore beginInsertRows is called
449 beginInsertRows(index(parent), row, row); 441 parent.remove(item);
450 parent.add(item, row); 442 beginInsertRows(index(parent), row, row);
451 endInsertRows(); 443 parent.add(item, row);
452 } 444 endInsertRows();
453 445 }
454 446
455 void entryRemoved(BookmarkNode parent, int row, BookmarkNode item); 447
456 { 448 void entryRemoved(BookmarkNode parent, int row, BookmarkNode item);
457 // item was already removed, re-add so beginRemoveRows works 449 {
458 parent.add(item, row); 450 // item was already removed, re-add so beginRemoveRows works
459 beginRemoveRows(index(parent), row, row); 451 parent.add(item, row);
460 parent.remove(item); 452 beginRemoveRows(index(parent), row, row);
461 endRemoveRows(); 453 parent.remove(item);
462 } 454 endRemoveRows();
463 455 }
464 void entryChanged(BookmarkNode item); 456
465 { 457 void entryChanged(BookmarkNode item);
466 QModelIndex idx = index(item); 458 {
467 emit dataChanged(idx, idx); 459 QModelIndex idx = index(item);
468 } 460 emit dataChanged(idx, idx);
461 }
462
469 public: 463 public:
470 enum Roles { 464
471 TypeRole = Qt.UserRole + 1, 465 enum Roles {
472 UrlRole = Qt.UserRole + 2, 466 TypeRole = Qt.UserRole + 1,
473 UrlStringRole = Qt.UserRole + 3, 467 UrlRole = Qt.UserRole + 2,
474 SeparatorRole = Qt.UserRole + 4 468 UrlStringRole = Qt.UserRole + 3,
475 } 469 SeparatorRole = Qt.UserRole + 4
476 470 }
477 BookmarksModel(BookmarksManager bookmarkManager, QObject parent = null) 471
478 { 472 BookmarksModel(BookmarksManager bookmarkManager, QObject parent = null)
479 super(parent) 473 {
480 m_endMacro = false; 474 super(parent)
481 m_bookmarksManager = bookmarkManager; 475 m_endMacro = false;
482 bookmarkManager.entryAdded.connect(&this.entryAdded); 476 m_bookmarksManager = bookmarkManager;
483 bookmarkManager.entryRemoved.connect(&this.entryRemoved); 477 bookmarkManager.entryAdded.connect(&this.entryAdded);
484 bookmarkManager.entryChanged.connect(&this.entryChanged); 478 bookmarkManager.entryRemoved.connect(&this.entryRemoved);
485 } 479 bookmarkManager.entryChanged.connect(&this.entryChanged);
480 }
486 481
487 482
488 inline BookmarksManager bookmarksManager() { return m_bookmarksManager; } 483 BookmarksManager bookmarksManager() { return m_bookmarksManager; }
489 484
490 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole); 485 QVariant headerData(int section, Qt.Orientation orientation, int role = Qt.DisplayRole);
491 { 486 {
492 if (orientation == Qt.Horizontal && role == Qt.DisplayRole) { 487 if (orientation == Qt.Horizontal && role == Qt.DisplayRole) {
493 switch (section) { 488 switch (section) {
494 case 0: return tr("Title"); 489 case 0: return tr("Title");
495 case 1: return tr("Address"); 490 case 1: return tr("Address");
496 } 491 }
497 } 492 }
498 return QAbstractItemModel.headerData(section, orientation, role); 493 return QAbstractItemModel.headerData(section, orientation, role);
499 } 494 }
500 495
501 496
502 QVariant data(QModelIndex index, int role = Qt.DisplayRole) 497 QVariant data(QModelIndex index, int role = Qt.DisplayRole)
503 { 498 {
504 if (!index.isValid() || index.model() != this) 499 if (!index.isValid() || index.model() != this)
505 return QVariant(); 500 return QVariant();
506 501
507 BookmarkNode bookmarkNode = node(index); 502 BookmarkNode bookmarkNode = node(index);
508 switch (role) { 503 switch (role) {
509 case Qt.EditRole: 504 case Qt.EditRole:
510 case Qt.DisplayRole: 505 case Qt.DisplayRole:
511 if (bookmarkNode.type() == BookmarkNode.Separator) { 506 if (bookmarkNode.type() == BookmarkNode.Separator) {
512 switch (index.column()) { 507 switch (index.column()) {
513 case 0: return QString(50, 0xB7); 508 case 0: return QString(50, 0xB7);
514 case 1: return QString(); 509 case 1: return QString();
515 } 510 }
516 } 511 }
517 512
518 switch (index.column()) { 513 switch (index.column()) {
519 case 0: return bookmarkNode.title; 514 case 0: return bookmarkNode.title;
520 case 1: return bookmarkNode.url; 515 case 1: return bookmarkNode.url;
521 } 516 }
522 break; 517 break;
523 case BookmarksModel.UrlRole: 518 case BookmarksModel.UrlRole:
524 return QUrl(bookmarkNode.url); 519 return QUrl(bookmarkNode.url);
525 break; 520 break;
526 case BookmarksModel.UrlStringRole: 521 case BookmarksModel.UrlStringRole:
527 return bookmarkNode.url; 522 return bookmarkNode.url;
528 break; 523 break;
529 case BookmarksModel.TypeRole: 524 case BookmarksModel.TypeRole:
530 return bookmarkNode.type(); 525 return bookmarkNode.type();
531 break; 526 break;
532 case BookmarksModel.SeparatorRole: 527 case BookmarksModel.SeparatorRole:
533 return (bookmarkNode.type() == BookmarkNode.Separator); 528 return (bookmarkNode.type() == BookmarkNode.Separator);
534 break; 529 break;
535 case Qt.DecorationRole: 530 case Qt.DecorationRole:
536 if (index.column() == 0) { 531 if (index.column() == 0) {
537 if (bookmarkNode.type() == BookmarkNode.Folder) 532 if (bookmarkNode.type() == BookmarkNode.Folder)
538 return QApplication.style().standardIcon(QStyle.SP_DirIcon); 533 return QApplication.style().standardIcon(QStyle.SP_DirIcon);
539 return BrowserApplication.instance().icon(bookmarkNode.url); 534 return BrowserApplication.instance().icon(bookmarkNode.url);
540 } 535 }
541 } 536 }
542 537
543 return QVariant(); 538 return QVariant();
544 } 539 }
545 540
546 int columnCount(QModelIndex parent = QModelIndex()) 541 int columnCount(QModelIndex parent = QModelIndex())
547 { 542 {
548 return (parent.column() > 0) ? 0 : 2; 543 return (parent.column() > 0) ? 0 : 2;
549 } 544 }
550 545
551 int rowCount(QModelIndex parent = QModelIndex()) 546 int rowCount(QModelIndex parent = QModelIndex())
552 { 547 {
553 if (parent.column() > 0) 548 if (parent.column() > 0)
554 return 0; 549 return 0;
555 550
556 if (!parent.isValid()) 551 if (!parent.isValid())
557 return m_bookmarksManager.bookmarks().children().count(); 552 return m_bookmarksManager.bookmarks().children().count();
558 553
559 const BookmarkNode item = static_cast<BookmarkNode>(parent.internalPointer()); 554 BookmarkNode item = static_cast<BookmarkNode>(parent.internalPointer());
560 return item.children().count(); 555 return item.children().count();
561 } 556 }
562 557
563 QModelIndex index(int, int, QModelIndex& = QModelIndex()) 558 QModelIndex index(int, int, QModelIndex& = QModelIndex())
564 { 559 {
565 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent)) 560 if (row < 0 || column < 0 || row >= rowCount(parent) || column >= columnCount(parent))
566 return QModelIndex(); 561 return QModelIndex();
567 562
568 // get the parent node 563 // get the parent node
569 BookmarkNode parentNode = node(parent); 564 BookmarkNode parentNode = node(parent);
570 return createIndex(row, column, parentNode.children().at(row)); 565 return createIndex(row, column, parentNode.children().at(row));
571 } 566 }
572 567
573 QModelIndex parent(QModelIndex& index= QModelIndex()) 568 QModelIndex parent(QModelIndex& index= QModelIndex())
574 { 569 {
575 if (!index.isValid()) 570 if (!index.isValid())
576 return QModelIndex(); 571 return QModelIndex();
577 572
578 BookmarkNode itemNode = node(index); 573 BookmarkNode itemNode = node(index);
579 BookmarkNode parentNode = (itemNode ? itemNode.parent() : 0); 574 BookmarkNode parentNode = (itemNode ? itemNode.parent() : 0);
580 if (!parentNode || parentNode == m_bookmarksManager.bookmarks()) 575 if (!parentNode || parentNode == m_bookmarksManager.bookmarks())
581 return QModelIndex(); 576 return QModelIndex();
582 577
583 // get the parent's row 578 // get the parent's row
584 BookmarkNode grandParentNode = parentNode.parent(); 579 BookmarkNode grandParentNode = parentNode.parent();
585 int parentRow = grandParentNode.children().indexOf(parentNode); 580 int parentRow = grandParentNode.children().indexOf(parentNode);
586 assert(parentRow >= 0); 581 assert(parentRow >= 0);
587 return createIndex(parentRow, 0, parentNode); 582 return createIndex(parentRow, 0, parentNode);
588 } 583 }
589 584
590 585 Qt.ItemFlags flags(QModelIndex index)
591 Qt.ItemFlags flags(QModelIndex index) 586 {
592 { 587 if (!index.isValid())
593 if (!index.isValid()) 588 return Qt.NoItemFlags;
594 return Qt.NoItemFlags; 589
595 590 Qt.ItemFlags flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled;
596 Qt.ItemFlags flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled; 591
597 592 BookmarkNode bookmarkNode = node(index);
598 BookmarkNode bookmarkNode = node(index); 593
599 594 if (bookmarkNode != m_bookmarksManager.menu() && bookmarkNode != m_bookmarksManager.toolbar()) {
600 if (bookmarkNode != m_bookmarksManager.menu() && bookmarkNode != m_bookmarksManager.toolbar()) { 595 flags |= Qt.ItemIsDragEnabled;
601 flags |= Qt.ItemIsDragEnabled; 596 if (bookmarkNode.type() != BookmarkNode.Separator)
602 if (bookmarkNode.type() != BookmarkNode.Separator) 597 flags |= Qt.ItemIsEditable;
603 flags |= Qt.ItemIsEditable; 598 }
604 } 599 if (hasChildren(index))
605 if (hasChildren(index)) 600 flags |= Qt.ItemIsDropEnabled;
606 flags |= Qt.ItemIsDropEnabled; 601 return flags;
607 return flags; 602 }
608 } 603
609 604 Qt.DropActions supportedDropActions ();
610 Qt.DropActions supportedDropActions (); 605 {
611 { 606 return Qt.CopyAction | Qt.MoveAction;
612 return Qt.CopyAction | Qt.MoveAction; 607 }
613 } 608
614 609
615 610 bool removeRows(int row, int count, QModelIndex parent = QModelIndex());
616 bool removeRows(int row, int count, QModelIndex parent = QModelIndex()); 611 {
617 { 612 if (row < 0 || count <= 0 || row + count > rowCount(parent))
618 if (row < 0 || count <= 0 || row + count > rowCount(parent)) 613 return false;
619 return false; 614
620 615 BookmarkNode bookmarkNode = node(parent);
621 BookmarkNode bookmarkNode = node(parent); 616 for (int i = row + count - 1; i >= row; --i) {
622 for (int i = row + count - 1; i >= row; --i) { 617 BookmarkNode node = bookmarkNode.children().at(i);
623 BookmarkNode node = bookmarkNode.children().at(i); 618 if (node == m_bookmarksManager.menu() || node == m_bookmarksManager.toolbar())
624 if (node == m_bookmarksManager.menu() 619 continue;
625 || node == m_bookmarksManager.toolbar()) 620
626 continue; 621 m_bookmarksManager.removeBookmark(node);
627 622 }
628 m_bookmarksManager.removeBookmark(node); 623 if (m_endMacro) {
629 } 624 m_bookmarksManager.undoRedoStack().endMacro();
630 if (m_endMacro) { 625 m_endMacro = false;
631 m_bookmarksManager.undoRedoStack().endMacro(); 626 }
632 m_endMacro = false; 627 return true;
633 } 628 }
634 return true;
635 }
636 629
637 bool setData(QModelIndex index, QVariant value, int role = Qt.EditRole) 630 bool setData(QModelIndex index, QVariant value, int role = Qt.EditRole)
638 { 631 {
639 if (!index.isValid() || (flags(index) & Qt.ItemIsEditable) == 0) 632 if (!index.isValid() || (flags(index) & Qt.ItemIsEditable) == 0)
640 return false; 633 return false;
641 634
642 BookmarkNode item = node(index); 635 BookmarkNode item = node(index);
643 636
644 switch (role) { 637 switch (role) {
645 case Qt.EditRole: 638 case Qt.EditRole:
646 case Qt.DisplayRole: 639 case Qt.DisplayRole:
647 if (index.column() == 0) { 640 if (index.column() == 0) {
648 m_bookmarksManager.setTitle(item, value.toString()); 641 m_bookmarksManager.setTitle(item, value.toString());
649 break; 642 break;
650 } 643 }
651 if (index.column() == 1) { 644 if (index.column() == 1) {
652 m_bookmarksManager.setUrl(item, value.toString()); 645 m_bookmarksManager.setUrl(item, value.toString());
653 break; 646 break;
654 } 647 }
655 return false; 648 return false;
656 case BookmarksModel.UrlRole: 649 case BookmarksModel.UrlRole:
657 m_bookmarksManager.setUrl(item, value.toUrl().toString()); 650 m_bookmarksManager.setUrl(item, value.toUrl().toString());
658 break; 651 break;
659 case BookmarksModel.UrlStringRole: 652 case BookmarksModel.UrlStringRole:
660 m_bookmarksManager.setUrl(item, value.toString()); 653 m_bookmarksManager.setUrl(item, value.toString());
661 break; 654 break;
662 default: 655 default:
663 break; 656 break;
664 return false; 657 return false;
665 } 658 }
666 659
667 return true; 660 return true;
668 } 661 }
669 662
670 663
671 QMimeData mimeData(QModelIndexList indexes); 664 QMimeData mimeData(QModelIndexList indexes);
672 { 665 {
673 QMimeData mimeData = new QMimeData(); 666 QMimeData mimeData = new QMimeData();
674 QByteArray data; 667 QByteArray data;
675 QDataStream stream(&data, QIODevice.WriteOnly); 668 auto stream = new QDataStream(&data, QIODevice.WriteOnly);
676 foreach (QModelIndex index, indexes) { 669 foreach (QModelIndex index, indexes) {
677 if (index.column() != 0 || !index.isValid()) 670 if (index.column() != 0 || !index.isValid())
678 continue; 671 continue;
679 QByteArray encodedData; 672 QByteArray encodedData;
680 QBuffer buffer(&encodedData); 673 auto buffer = new QBuffer(&encodedData);
681 buffer.open(QBuffer.ReadWrite); 674 buffer.open(QBuffer.ReadWrite);
682 XbelWriter writer; 675 XbelWriter writer;
683 const BookmarkNode parentNode = node(index); 676 const BookmarkNode parentNode = node(index);
684 writer.write(&buffer, parentNode); 677 writer.write(&buffer, parentNode);
685 stream << encodedData; 678 stream << encodedData;
686 } 679 }
687 mimeData.setData(MIMETYPE, data); 680 mimeData.setData(MIMETYPE, data);
688 return mimeData; 681 return mimeData;
689 } 682 }
690 683
691 684
692 const char[] MIMETYPE = QLatin1String("application/bookmarks.xbel") 685 const char[] MIMETYPE = QLatin1String("application/bookmarks.xbel")
693 686
694 QStringList mimeTypes() 687 QStringList mimeTypes()
695 { 688 {
696 QStringList types; 689 QStringList types;
697 types << MIMETYPE; 690 types << MIMETYPE;
698 return types; 691 return types;
699 } 692 }
700 693
701 bool dropMimeData(QMimeData data, Qt.DropAction action, int row, int column, QModelIndex parent) 694 bool dropMimeData(QMimeData data, Qt.DropAction action, int row, int column, QModelIndex parent)
702 { 695 {
703 if (action == Qt.IgnoreAction) 696 if (action == Qt.IgnoreAction)
704 return true; 697 return true;
705 698
706 if (!data.hasFormat(MIMETYPE) || column > 0) 699 if (!data.hasFormat(MIMETYPE) || column > 0)
707 return false; 700 return false;
708 701
709 QByteArray ba = data.data(MIMETYPE); 702 QByteArray ba = data.data(MIMETYPE);
710 QDataStream stream(&ba, QIODevice.ReadOnly); 703 QDataStream stream(&ba, QIODevice.ReadOnly);
711 if (stream.atEnd()) 704 if (stream.atEnd())
712 return false; 705 return false;
713 706
714 QUndoStack undoStack = m_bookmarksManager.undoRedoStack(); 707 QUndoStack undoStack = m_bookmarksManager.undoRedoStack();
715 undoStack.beginMacro(QLatin1String("Move Bookmarks")); 708 undoStack.beginMacro(QLatin1String("Move Bookmarks"));
716 709
717 while (!stream.atEnd()) { 710 while (!stream.atEnd()) {
718 QByteArray encodedData; 711 QByteArray encodedData;
719 stream >> encodedData; 712 stream >> encodedData;
720 QBuffer buffer(&encodedData); 713 QBuffer buffer(&encodedData);
721 buffer.open(QBuffer.ReadOnly); 714 buffer.open(QBuffer.ReadOnly);
722 715
723 XbelReader reader; 716 XbelReader reader;
724 BookmarkNode rootNode = reader.read(&buffer); 717 BookmarkNode rootNode = reader.read(&buffer);
725 QList<BookmarkNode> children = rootNode.children(); 718 QList<BookmarkNode> children = rootNode.children();
726 for (int i = 0; i < children.count(); ++i) { 719 for (int i = 0; i < children.count(); ++i) {
727 BookmarkNode bookmarkNode = children.at(i); 720 BookmarkNode bookmarkNode = children.at(i);
728 rootNode.remove(bookmarkNode); 721 rootNode.remove(bookmarkNode);
729 row = qMax(0, row); 722 row = qMax(0, row);
730 BookmarkNode parentNode = node(parent); 723 BookmarkNode parentNode = node(parent);
731 m_bookmarksManager.addBookmark(parentNode, bookmarkNode, row); 724 m_bookmarksManager.addBookmark(parentNode, bookmarkNode, row);
732 m_endMacro = true; 725 m_endMacro = true;
733 } 726 }
734 delete rootNode; 727 delete rootNode;
735 } 728 }
736 return true; 729 return true;
737 } 730 }
738 731
739 732 bool hasChildren(QModelIndex parent = QModelIndex())
740 bool hasChildren(QModelIndex parent = QModelIndex()) 733 {
741 { 734 if (!parent.isValid())
742 if (!parent.isValid()) 735 return true;
743 return true; 736 const BookmarkNode parentNode = node(parent);
744 const BookmarkNode parentNode = node(parent); 737 return (parentNode.type() == BookmarkNode.Folder);
745 return (parentNode.type() == BookmarkNode.Folder); 738 }
746 } 739
747 740 BookmarkNode node(QModelIndex index)
748 BookmarkNode node(QModelIndex index) 741 {
749 { 742 BookmarkNode itemNode = static_cast<BookmarkNode>(index.internalPointer());
750 BookmarkNode itemNode = static_cast<BookmarkNode>(index.internalPointer()); 743 if (!itemNode)
751 if (!itemNode) 744 return m_bookmarksManager.bookmarks();
752 return m_bookmarksManager.bookmarks(); 745 return itemNode;
753 return itemNode; 746 }
754 } 747
755 748 QModelIndex index(BookmarkNode node)
756 QModelIndex index(BookmarkNode node) 749 {
757 { 750 BookmarkNode parent = node.parent();
758 BookmarkNode parent = node.parent(); 751 if (!parent)
759 if (!parent) 752 return QModelIndex();
760 return QModelIndex(); 753 return createIndex(parent.children().indexOf(node), 0, node);
761 return createIndex(parent.children().indexOf(node), 0, node); 754 }
762 }
763 755
764 private: 756 private:
765 757
766 bool m_endMacro; 758 bool m_endMacro;
767 BookmarksManager m_bookmarksManager; 759 BookmarksManager m_bookmarksManager;
768 } 760 }
769 761
770 // Menu that is dynamically populated from the bookmarks 762 // Menu that is dynamically populated from the bookmarks
771 import modelmenu; 763 import modelmenu;
764
772 class BookmarksMenu : public ModelMenu 765 class BookmarksMenu : public ModelMenu
773 { 766 {
774 mixin Signal!("openUrl", QUrl url); 767 mixin Signal!("openUrl", QUrl url);
775 768
776 public: 769 public:
777 BookmarksMenu(QWidget parent = null) 770
778 { 771 BookmarksMenu(QWidget parent = null)
779 super(parent); 772 {
780 m_bookmarksManager = 0; 773 super(parent);
781 this.activated.connect(&this.activated); 774 m_bookmarksManager = 0;
782 setMaxRows(-1); 775 this.activated.connect(&this.activated);
783 setHoverRole(BookmarksModel.UrlStringRole); 776 setMaxRows(-1);
784 setSeparatorRole(BookmarksModel.SeparatorRole); 777 setHoverRole(BookmarksModel.UrlStringRole);
785 } 778 setSeparatorRole(BookmarksModel.SeparatorRole);
786 void setInitialActions(QList<QAction> actions); 779 }
787 { 780
788 m_initialActions = actions; 781 void setInitialActions(QList<QAction> actions);
789 for (int i = 0; i < m_initialActions.count(); ++i) 782 {
790 addAction(m_initialActions.at(i)); 783 m_initialActions = actions;
791 } 784 for (int i = 0; i < m_initialActions.count(); ++i)
785 addAction(m_initialActions.at(i));
786 }
787
792 protected: 788 protected:
789
793 bool prePopulated() 790 bool prePopulated()
794 { 791 {
795 m_bookmarksManager = BrowserApplication.bookmarksManager(); 792 m_bookmarksManager = BrowserApplication.bookmarksManager();
796 setModel(m_bookmarksManager.bookmarksModel()); 793 setModel(m_bookmarksManager.bookmarksModel());
797 setRootIndex(m_bookmarksManager.bookmarksModel().index(1, 0)); 794 setRootIndex(m_bookmarksManager.bookmarksModel().index(1, 0));
798 // initial actions 795 // initial actions
799 for (int i = 0; i < m_initialActions.count(); ++i) 796 for (int i = 0; i < m_initialActions.count(); ++i)
800 addAction(m_initialActions.at(i)); 797 addAction(m_initialActions.at(i));
801 if (!m_initialActions.isEmpty()) 798 if (!m_initialActions.isEmpty())
802 addSeparator(); 799 addSeparator();
803 createMenu(model().index(0, 0), 1, this); 800 createMenu(model().index(0, 0), 1, this);
804 return true; 801 return true;
805 } 802 }
806
807 private slots:
808 void activated(QModelIndex index)
809 {
810 emit openUrl(index.data(BookmarksModel.UrlRole).toUrl());
811 }
812 803
813 private: 804 private:
814 BookmarksManager m_bookmarksManager; 805
815 QList<QAction> m_initialActions; 806 void activated(QModelIndex index)
807 {
808 emit openUrl(index.data(BookmarksModel.UrlRole).toUrl());
809 }
810
811 private:
812
813 BookmarksManager m_bookmarksManager;
814 QList<QAction> m_initialActions;
816 } 815 }
817 816
818 /* 817 /*
819 Proxy model that filters out the bookmarks so only the folders 818 Proxy model that filters out the bookmarks so only the folders
820 are left behind. Used in the add bookmark dialog combobox. 819 are left behind. Used in the add bookmark dialog combobox.
821 */ 820 */
822 import qt.gui.QSortFilterProxyModel; 821 import qt.gui.QSortFilterProxyModel;
823 822
824 class AddBookmarkProxyModel : public QSortFilterProxyModel 823 class AddBookmarkProxyModel : public QSortFilterProxyModel
825 { 824 {
826 Q_OBJECT
827 public: 825 public:
828 this(QObject parent = null) 826
829 { 827 this(QObject parent = null)
830 super(parent); 828 {
829 super(parent);
830 }
831
832 int columnCount(QModelIndex parent = QModelIndex())
833 {
834 return qMin(1, QSortFilterProxyModel.columnCount(parent));
835 }
836
837 protected:
838
839 bool filterAcceptsRow(int source_row, QModelIndex source_parent)
840 {
841 QModelIndex idx = sourceModel().index(source_row, 0, source_parent);
842 return sourceModel().hasChildren(idx);
843 }
831 } 844 }
832 int columnCount(QModelIndex parent = QModelIndex())
833 {
834 return qMin(1, QSortFilterProxyModel.columnCount(parent));
835 }
836 protected:
837 bool filterAcceptsRow(int source_row, QModelIndex source_parent)
838 {
839 QModelIndex idx = sourceModel().index(source_row, 0, source_parent);
840 return sourceModel().hasChildren(idx);
841 }
842 }
843 845
844 /*! 846 /*!
845 Add bookmark dialog 847 Add bookmark dialog
846 */ 848 */
847 849
848 import ui_addbookmarkdialog; 850 import ui_addbookmarkdialog;
849 851
850 class AddBookmarkDialog : public QDialog, public Ui_AddBookmarkDialog 852 class AddBookmarkDialog : public QDialog, public Ui_AddBookmarkDialog
851 { 853 {
852
853 public: 854 public:
854 this(QString url, QString title, QWidget parent = null, BookmarksManager bookmarkManager = null) 855
855 : QDialog(parent) 856 this(QString url, QString title, QWidget parent = null, BookmarksManager bookmarkManager = null)
856 { 857 : QDialog(parent)
857 m_url = url; 858 {
858 m_bookmarksManager = bookmarkManager; 859 m_url = url;
859 860 m_bookmarksManager = bookmarkManager;
860 setWindowFlags(Qt.Sheet); 861
861 if (!m_bookmarksManager) 862 setWindowFlags(Qt.Sheet);
862 m_bookmarksManager = BrowserApplication.bookmarksManager(); 863 if (!m_bookmarksManager)
863 setupUi(this); 864 m_bookmarksManager = BrowserApplication.bookmarksManager();
864 QTreeView view = new QTreeView(this); 865 setupUi(this);
865 m_proxyModel = new AddBookmarkProxyModel(this); 866 QTreeView view = new QTreeView(this);
866 BookmarksModel model = m_bookmarksManager.bookmarksModel(); 867 m_proxyModel = new AddBookmarkProxyModel(this);
867 m_proxyModel.setSourceModel(model); 868 BookmarksModel model = m_bookmarksManager.bookmarksModel();
868 view.setModel(m_proxyModel); 869 m_proxyModel.setSourceModel(model);
869 view.expandAll(); 870 view.setModel(m_proxyModel);
870 view.header().setStretchLastSection(true); 871 view.expandAll();
871 view.header().hide(); 872 view.header().setStretchLastSection(true);
872 view.setItemsExpandable(false); 873 view.header().hide();
873 view.setRootIsDecorated(false); 874 view.setItemsExpandable(false);
874 view.setIndentation(10); 875 view.setRootIsDecorated(false);
875 location.setModel(m_proxyModel); 876 view.setIndentation(10);
876 view.show(); 877 location.setModel(m_proxyModel);
877 location.setView(view); 878 view.show();
878 BookmarkNode menu = m_bookmarksManager.menu(); 879 location.setView(view);
879 QModelIndex idx = m_proxyModel.mapFromSource(model.index(menu)); 880 BookmarkNode menu = m_bookmarksManager.menu();
880 view.setCurrentIndex(idx); 881 QModelIndex idx = m_proxyModel.mapFromSource(model.index(menu));
881 location.setCurrentIndex(idx.row()); 882 view.setCurrentIndex(idx);
882 name.setText(title); 883 location.setCurrentIndex(idx.row());
883 } 884 name.setText(title);
885 }
884 886
885 private: 887 private:
886 888
887 void accept() 889 void accept()
888 { 890 {
889 QModelIndex index = location.view().currentIndex(); 891 QModelIndex index = location.view().currentIndex();
890 index = m_proxyModel.mapToSource(index); 892 index = m_proxyModel.mapToSource(index);
891 if (!index.isValid()) 893 if (!index.isValid())
892 index = m_bookmarksManager.bookmarksModel().index(0, 0); 894 index = m_bookmarksManager.bookmarksModel().index(0, 0);
893 BookmarkNode parent = m_bookmarksManager.bookmarksModel().node(index); 895 BookmarkNode parent = m_bookmarksManager.bookmarksModel().node(index);
894 BookmarkNode bookmark = new BookmarkNode(BookmarkNode.Bookmark); 896 BookmarkNode bookmark = new BookmarkNode(BookmarkNode.Bookmark);
895 bookmark.url = m_url; 897 bookmark.url = m_url;
896 bookmark.title = name.text(); 898 bookmark.title = name.text();
897 m_bookmarksManager.addBookmark(parent, bookmark); 899 m_bookmarksManager.addBookmark(parent, bookmark);
898 QDialog.accept(); 900 QDialog.accept();
899 } 901 }
900 902
901 private: 903 private:
902 QString m_url; 904
903 BookmarksManager m_bookmarksManager; 905 QString m_url;
904 AddBookmarkProxyModel m_proxyModel; 906 BookmarksManager m_bookmarksManager;
907 AddBookmarkProxyModel m_proxyModel;
905 } 908 }
906 909
907 import ui_bookmarks; 910 import ui_bookmarks;
908 911
909 //class TreeProxyModel; 912 //class TreeProxyModel;
910 class BookmarksDialog : public QDialog, public Ui_BookmarksDialog 913 class BookmarksDialog : public QDialog, public Ui_BookmarksDialog
911 { 914 {
915 mixin Signal!("openUrl", QUrl url);
916
917 public:
918
919 this(QWidget parent = null, BookmarksManager manager = null)
920 : QDialog(parent)
921 {
922 m_bookmarksManager = manager;
923 if (!m_bookmarksManager)
924 m_bookmarksManager = BrowserApplication.bookmarksManager();
925 setupUi(this);
926
927 tree.setUniformRowHeights(true);
928 tree.setSelectionBehavior(QAbstractItemView.SelectRows);
929 tree.setSelectionMode(QAbstractItemView.ContiguousSelection);
930 tree.setTextElideMode(Qt.ElideMiddle);
931 m_bookmarksModel = m_bookmarksManager.bookmarksModel();
932 m_proxyModel = new TreeProxyModel(this);
933 search.textChanged.connect(&m_proxyModel.setFilterFixedString);
934 removeButton.clicked.connect(&tree.removeOne);
935 m_proxyModel.setSourceModel(m_bookmarksModel);
936 tree.setModel(m_proxyModel);
937 tree.setDragDropMode(QAbstractItemView.InternalMove);
938 tree.setExpanded(m_proxyModel.index(0, 0), true);
939 tree.setAlternatingRowColors(true);
940 auto fm = new QFontMetrics(font());
941 int header = fm.width(QLatin1Char('m')) * 40;
942 tree.header().resizeSection(0, header);
943 tree.header().setStretchLastSection(true);
944 tree.activated.connect(&this.open);
945 tree.setContextMenuPolicy(Qt.CustomContextMenu);
946 tree.customContextMenuRequested.connect(&this.customContextMenuRequested);
947 addFolderButton.clicked.connect(&this.newFolder);
948 expandNodes(m_bookmarksManager.bookmarks());
949 setAttribute(Qt.WA_DeleteOnClose);
950 }
912 951
913 mixin Signal!("openUrl", QUrl url); 952 ~this()
914 953 {
915 public: 954 if (saveExpandedNodes(tree.rootIndex()))
916 this(QWidget parent = null, BookmarksManager manager = null) 955 m_bookmarksManager.changeExpanded();
917 : QDialog(parent) 956 }
918 {
919 m_bookmarksManager = manager;
920 if (!m_bookmarksManager)
921 m_bookmarksManager = BrowserApplication.bookmarksManager();
922 setupUi(this);
923
924 tree.setUniformRowHeights(true);
925 tree.setSelectionBehavior(QAbstractItemView.SelectRows);
926 tree.setSelectionMode(QAbstractItemView.ContiguousSelection);
927 tree.setTextElideMode(Qt.ElideMiddle);
928 m_bookmarksModel = m_bookmarksManager.bookmarksModel();
929 m_proxyModel = new TreeProxyModel(this);
930 search.textChanged.connect(&m_proxyModel.setFilterFixedString);
931 removeButton.clicked.connect(&tree.removeOne);
932 m_proxyModel.setSourceModel(m_bookmarksModel);
933 tree.setModel(m_proxyModel);
934 tree.setDragDropMode(QAbstractItemView.InternalMove);
935 tree.setExpanded(m_proxyModel.index(0, 0), true);
936 tree.setAlternatingRowColors(true);
937 auto fm = new QFontMetrics(font());
938 int header = fm.width(QLatin1Char('m')) * 40;
939 tree.header().resizeSection(0, header);
940 tree.header().setStretchLastSection(true);
941 tree.activated.connect(&this.open);
942 tree.setContextMenuPolicy(Qt.CustomContextMenu);
943 tree.customContextMenuRequested.connect(&this.customContextMenuRequested);
944 addFolderButton.clicked.connect(&this.newFolder);
945 expandNodes(m_bookmarksManager.bookmarks());
946 setAttribute(Qt.WA_DeleteOnClose);
947 }
948 ~this()
949 {
950 if (saveExpandedNodes(tree.rootIndex()))
951 m_bookmarksManager.changeExpanded();
952 }
953
954 private slots:
955 void customContextMenuRequested(QPoint pos)
956 {
957 QMenu menu;
958 QModelIndex index = tree.indexAt(pos);
959 index = index.sibling(index.row(), 0);
960 if (index.isValid() && !tree.model().hasChildren(index)) {
961 menu.addAction(tr("Open"), this, SLOT(open()));
962 menu.addSeparator();
963 }
964 menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
965 menu.exec(QCursor.pos());
966 }
967 void open()
968 {
969 QModelIndex index = tree.currentIndex();
970 if (!index.parent().isValid())
971 return;
972 emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel.UrlRole).toUrl());
973 }
974
975 void newFolder()
976 {
977 QModelIndex currentIndex = tree.currentIndex();
978 QModelIndex idx = currentIndex;
979 if (idx.isValid() && !idx.model().hasChildren(idx))
980 idx = idx.parent();
981 if (!idx.isValid())
982 idx = tree.rootIndex();
983 idx = m_proxyModel.mapToSource(idx);
984 BookmarkNode parent = m_bookmarksManager.bookmarksModel().node(idx);
985 BookmarkNode node = new BookmarkNode(BookmarkNode.Folder);
986 node.title = tr("New Folder");
987 m_bookmarksManager.addBookmark(parent, node, currentIndex.row() + 1);
988 }
989 957
990 private: 958 private:
991 void expandNodes(BookmarkNode node) 959
992 { 960 void customContextMenuRequested(QPoint pos)
993 for (int i = 0; i < node.children().count(); ++i) { 961 {
994 BookmarkNode childNode = node.children()[i]; 962 QMenu menu;
995 if (childNode.expanded) { 963 QModelIndex index = tree.indexAt(pos);
996 QModelIndex idx = m_bookmarksModel.index(childNode); 964 index = index.sibling(index.row(), 0);
997 idx = m_proxyModel.mapFromSource(idx); 965 if (index.isValid() && !tree.model().hasChildren(index)) {
998 tree.setExpanded(idx, true); 966 menu.addAction(tr("Open"), this, SLOT(open()));
999 expandNodes(childNode); 967 menu.addSeparator();
1000 } 968 }
1001 } 969 menu.addAction(tr("Delete"), tree, SLOT(removeOne()));
1002 } 970 menu.exec(QCursor.pos());
1003 971 }
1004 bool saveExpandedNodes(QModelIndex parent) 972
1005 { 973 void open()
1006 bool changed = false; 974 {
1007 for (int i = 0; i < m_proxyModel.rowCount(parent); ++i) { 975 QModelIndex index = tree.currentIndex();
1008 QModelIndex child = m_proxyModel.index(i, 0, parent); 976 if (!index.parent().isValid())
1009 QModelIndex sourceIndex = m_proxyModel.mapToSource(child); 977 return;
1010 BookmarkNode childNode = m_bookmarksModel.node(sourceIndex); 978 emit openUrl(index.sibling(index.row(), 1).data(BookmarksModel.UrlRole).toUrl());
1011 bool wasExpanded = childNode.expanded; 979 }
1012 if (tree.isExpanded(child)) { 980
1013 childNode.expanded = true; 981 void newFolder()
1014 changed |= saveExpandedNodes(child); 982 {
1015 } else { 983 QModelIndex currentIndex = tree.currentIndex();
1016 childNode.expanded = false; 984 QModelIndex idx = currentIndex;
1017 } 985 if (idx.isValid() && !idx.model().hasChildren(idx))
1018 changed |= (wasExpanded != childNode.expanded); 986 idx = idx.parent();
1019 } 987 if (!idx.isValid())
1020 return changed; 988 idx = tree.rootIndex();
1021 } 989 idx = m_proxyModel.mapToSource(idx);
1022 990 BookmarkNode parent = m_bookmarksManager.bookmarksModel().node(idx);
1023 BookmarksManager m_bookmarksManager; 991 BookmarkNode node = new BookmarkNode(BookmarkNode.Folder);
1024 BookmarksModel m_bookmarksModel; 992 node.title = tr("New Folder");
1025 TreeProxyModel m_proxyModel; 993 m_bookmarksManager.addBookmark(parent, node, currentIndex.row() + 1);
994 }
995
996 private:
997 void expandNodes(BookmarkNode node)
998 {
999 for (int i = 0; i < node.children().count(); ++i) {
1000 BookmarkNode childNode = node.children()[i];
1001 if (childNode.expanded) {
1002 QModelIndex idx = m_bookmarksModel.index(childNode);
1003 idx = m_proxyModel.mapFromSource(idx);
1004 tree.setExpanded(idx, true);
1005 expandNodes(childNode);
1006 }
1007 }
1008 }
1009
1010 bool saveExpandedNodes(QModelIndex parent)
1011 {
1012 bool changed = false;
1013 for (int i = 0; i < m_proxyModel.rowCount(parent); ++i) {
1014 QModelIndex child = m_proxyModel.index(i, 0, parent);
1015 QModelIndex sourceIndex = m_proxyModel.mapToSource(child);
1016 BookmarkNode childNode = m_bookmarksModel.node(sourceIndex);
1017 bool wasExpanded = childNode.expanded;
1018 if (tree.isExpanded(child)) {
1019 childNode.expanded = true;
1020 changed |= saveExpandedNodes(child);
1021 } else {
1022 childNode.expanded = false;
1023 }
1024 changed |= (wasExpanded != childNode.expanded);
1025 }
1026 return changed;
1027 }
1028
1029 BookmarksManager m_bookmarksManager;
1030 BookmarksModel m_bookmarksModel;
1031 TreeProxyModel m_proxyModel;
1026 } 1032 }
1027 1033
1028 import qt.gui.QToolBar; 1034 import qt.gui.QToolBar;
1029 1035
1030 class BookmarksToolBar : public QToolBar 1036 class BookmarksToolBar : public QToolBar
1031 { 1037 {
1032 mixin Signal!("openUrl", QUrl url); 1038 mixin Signal!("openUrl", QUrl url);
1033 1039
1034 public: 1040 public:
1035 BookmarksToolBar(BookmarksModel model, QWidget parent = null) 1041
1036 { 1042 this(BookmarksModel model, QWidget parent = null)
1037 super(tr("Bookmark"), parent); 1043 {
1038 m_bookmarksModel = model; 1044 super(tr("Bookmark"), parent);
1039 this.actionTriggered.connect(&this.triggered); 1045 m_bookmarksModel = model;
1040 setRootIndex(model.index(0, 0)); 1046 this.actionTriggered.connect(&this.triggered);
1041 m_bookmarksModel.modelReset.connect(&this.build); 1047 setRootIndex(model.index(0, 0));
1042 m_bookmarksModel.rowsInserted.connect(&this.build); 1048 m_bookmarksModel.modelReset.connect(&this.build);
1043 m_bookmarksModel.rowsRemoved.connect(&this.build); 1049 m_bookmarksModel.rowsInserted.connect(&this.build);
1044 m_bookmarksModel.dataChanged.connect(&this.build); 1050 m_bookmarksModel.rowsRemoved.connect(&this.build);
1045 setAcceptDrops(true); 1051 m_bookmarksModel.dataChanged.connect(&this.build);
1052 setAcceptDrops(true);
1053 }
1054
1055 void setRootIndex(QModelIndex index);
1056 {
1057 m_root = index;
1058 build();
1059 }
1060
1061 QModelIndex rootIndex()
1062 {
1063 return m_root;
1064 }
1065
1066 protected:
1067
1068 void dragEnterEvent(QDragEnterEvent event)
1069 {
1070 const QMimeData mimeData = event.mimeData();
1071 if (mimeData.hasUrls())
1072 event.acceptProposedAction();
1073 QToolBar.dragEnterEvent(event);
1074 }
1075
1076 void dropEvent(QDropEvent event)
1077 {
1078 const QMimeData mimeData = event.mimeData();
1079 if (mimeData.hasUrls() && mimeData.hasText()) {
1080 QList<QUrl> urls = mimeData.urls();
1081 QAction action = actionAt(event.pos());
1082 QString dropText;
1083 if (action)
1084 dropText = action.text();
1085 int row = -1;
1086 QModelIndex parentIndex = m_root;
1087 for (int i = 0; i < m_bookmarksModel.rowCount(m_root); ++i) {
1088 QModelIndex idx = m_bookmarksModel.index(i, 0, m_root);
1089 QString title = idx.data().toString();
1090 if (title == dropText) {
1091 row = i;
1092 if (m_bookmarksModel.hasChildren(idx)) {
1093 parentIndex = idx;
1094 row = -1;
1095 }
1096 break;
1097 }
1098 }
1099 BookmarkNode bookmark = new BookmarkNode(BookmarkNode.Bookmark);
1100 bookmark.url = urls.at(0).toString();
1101 bookmark.title = mimeData.text();
1102
1103 BookmarkNode parent = m_bookmarksModel.node(parentIndex);
1104 BookmarksManager bookmarksManager = m_bookmarksModel.bookmarksManager();
1105 bookmarksManager.addBookmark(parent, bookmark, row);
1106 event.acceptProposedAction();
1107 }
1108 QToolBar.dropEvent(event);
1109 }
1110
1111 private:
1112
1113 void triggered(QAction action)
1114 {
1115 QVariant v = action.data();
1116 if (v.canConvert<QUrl>()) {
1117 emit openUrl(v.toUrl());
1118 }
1119 }
1120
1121 void activated(QModelIndex index)
1122 {
1123 emit openUrl(index.data(BookmarksModel.UrlRole).toUrl());
1124 }
1125
1126 void build()
1127 {
1128 clear();
1129 for (int i = 0; i < m_bookmarksModel.rowCount(m_root); ++i) {
1130 QModelIndex idx = m_bookmarksModel.index(i, 0, m_root);
1131 if (m_bookmarksModel.hasChildren(idx)) {
1132 QToolButton button = new QToolButton(this);
1133 button.setPopupMode(QToolButton.InstantPopup);
1134 button.setArrowType(Qt.DownArrow);
1135 button.setText(idx.data().toString());
1136 ModelMenu menu = new ModelMenu(this);
1137 menu.activated.connect(&this.activated);
1138 menu.setModel(m_bookmarksModel);
1139 menu.setRootIndex(idx);
1140 menu.addAction(new QAction(menu));
1141 button.setMenu(menu);
1142 button.setToolButtonStyle(Qt.ToolButtonTextOnly);
1143 QAction a = addWidget(button);
1144 a.setText(idx.data().toString());
1145 } else {
1146 QAction action = addAction(idx.data().toString());
1147 action.setData(idx.data(BookmarksModel.UrlRole));
1148 }
1149 }
1150 }
1151
1152 private:
1153
1154 BookmarksModel m_bookmarksModel;
1155 QPersistentModelIndex m_root;
1046 } 1156 }
1047
1048 void setRootIndex(QModelIndex index);
1049 {
1050 m_root = index;
1051 build();
1052 }
1053
1054 QModelIndex rootIndex()
1055 {
1056 return m_root;
1057 }
1058
1059 protected:
1060 void dragEnterEvent(QDragEnterEvent event)
1061 {
1062 const QMimeData mimeData = event.mimeData();
1063 if (mimeData.hasUrls())
1064 event.acceptProposedAction();
1065 QToolBar.dragEnterEvent(event);
1066 }
1067
1068 void dropEvent(QDropEvent event)
1069 {
1070 const QMimeData mimeData = event.mimeData();
1071 if (mimeData.hasUrls() && mimeData.hasText()) {
1072 QList<QUrl> urls = mimeData.urls();
1073 QAction action = actionAt(event.pos());
1074 QString dropText;
1075 if (action)
1076 dropText = action.text();
1077 int row = -1;
1078 QModelIndex parentIndex = m_root;
1079 for (int i = 0; i < m_bookmarksModel.rowCount(m_root); ++i) {
1080 QModelIndex idx = m_bookmarksModel.index(i, 0, m_root);
1081 QString title = idx.data().toString();
1082 if (title == dropText) {
1083 row = i;
1084 if (m_bookmarksModel.hasChildren(idx)) {
1085 parentIndex = idx;
1086 row = -1;
1087 }
1088 break;
1089 }
1090 }
1091 BookmarkNode bookmark = new BookmarkNode(BookmarkNode.Bookmark);
1092 bookmark.url = urls.at(0).toString();
1093 bookmark.title = mimeData.text();
1094
1095 BookmarkNode parent = m_bookmarksModel.node(parentIndex);
1096 BookmarksManager bookmarksManager = m_bookmarksModel.bookmarksManager();
1097 bookmarksManager.addBookmark(parent, bookmark, row);
1098 event.acceptProposedAction();
1099 }
1100 QToolBar.dropEvent(event);
1101 }
1102
1103 private slots:
1104 void triggered(QAction action)
1105 {
1106 QVariant v = action.data();
1107 if (v.canConvert<QUrl>()) {
1108 emit openUrl(v.toUrl());
1109 }
1110 }
1111
1112 void activated(QModelIndex index)
1113 {
1114 emit openUrl(index.data(BookmarksModel.UrlRole).toUrl());
1115 }
1116
1117
1118 void build()
1119 {
1120 clear();
1121 for (int i = 0; i < m_bookmarksModel.rowCount(m_root); ++i) {
1122 QModelIndex idx = m_bookmarksModel.index(i, 0, m_root);
1123 if (m_bookmarksModel.hasChildren(idx)) {
1124 QToolButton button = new QToolButton(this);
1125 button.setPopupMode(QToolButton.InstantPopup);
1126 button.setArrowType(Qt.DownArrow);
1127 button.setText(idx.data().toString());
1128 ModelMenu menu = new ModelMenu(this);
1129 menu.activated.connect(&this.activated);
1130 menu.setModel(m_bookmarksModel);
1131 menu.setRootIndex(idx);
1132 menu.addAction(new QAction(menu));
1133 button.setMenu(menu);
1134 button.setToolButtonStyle(Qt.ToolButtonTextOnly);
1135 QAction a = addWidget(button);
1136 a.setText(idx.data().toString());
1137 } else {
1138 QAction action = addAction(idx.data().toString());
1139 action.setData(idx.data(BookmarksModel.UrlRole));
1140 }
1141 }
1142 }
1143
1144 private:
1145 BookmarksModel m_bookmarksModel;
1146 QPersistentModelIndex m_root;
1147 }