2 * This file is part of NeverNote
\r
3 * Copyright 2009 Randy Baumgarte
\r
5 * This file may be licensed under the terms of of the
\r
6 * GNU General Public License Version 2 (the ``GPL'').
\r
8 * Software distributed under the License is distributed
\r
9 * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
\r
10 * express or implied. See the GPL for the specific language
\r
11 * governing rights and limitations.
\r
13 * You should have received a copy of the GPL along with this
\r
14 * program. If not, go to http://www.gnu.org/licenses/gpl.html
\r
15 * or write to the Free Software Foundation, Inc.,
\r
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
\r
19 package cx.fbn.nevernote.gui;
\r
21 import java.text.SimpleDateFormat;
\r
22 import java.util.List;
\r
23 import java.util.SortedMap;
\r
25 import com.evernote.edam.type.Note;
\r
26 import com.trolltech.qt.core.QByteArray;
\r
27 import com.trolltech.qt.core.QDateTime;
\r
28 import com.trolltech.qt.core.QModelIndex;
\r
29 import com.trolltech.qt.core.Qt;
\r
30 import com.trolltech.qt.core.Qt.SortOrder;
\r
31 import com.trolltech.qt.gui.QAbstractItemView;
\r
32 import com.trolltech.qt.gui.QAction;
\r
33 import com.trolltech.qt.gui.QApplication;
\r
34 import com.trolltech.qt.gui.QColor;
\r
35 import com.trolltech.qt.gui.QContextMenuEvent;
\r
36 import com.trolltech.qt.gui.QDragEnterEvent;
\r
37 import com.trolltech.qt.gui.QDropEvent;
\r
38 import com.trolltech.qt.gui.QFontMetrics;
\r
39 import com.trolltech.qt.gui.QHeaderView;
\r
40 import com.trolltech.qt.gui.QKeyEvent;
\r
41 import com.trolltech.qt.gui.QMenu;
\r
42 import com.trolltech.qt.gui.QStandardItemModel;
\r
43 import com.trolltech.qt.gui.QTableView;
\r
44 import com.trolltech.qt.gui.QKeySequence.StandardKey;
\r
46 import cx.fbn.nevernote.Global;
\r
47 import cx.fbn.nevernote.filters.NoteSortFilterProxyModel;
\r
48 import cx.fbn.nevernote.signals.NoteSignal;
\r
49 import cx.fbn.nevernote.utilities.ApplicationLogger;
\r
50 import cx.fbn.nevernote.utilities.ListManager;
\r
52 public class TableView extends QTableView {
\r
53 // private final ListManager runner;
\r
54 private final ApplicationLogger logger;
\r
55 public QStandardItemModel model; // Standard item model
\r
56 public NoteSortFilterProxyModel proxyModel; // note sort model
\r
57 private QAction deleteAction;
\r
58 private QAction addAction;
\r
59 private QAction restoreAction;
\r
60 private QAction noteHistoryAction;
\r
61 private QAction duplicateAction;
\r
62 private QAction mergeNotesAction;
\r
64 // Note title colors
\r
65 private QAction noteTitleColorWhite;
\r
66 private QAction noteTitleColorRed;
\r
67 private QAction noteTitleColorBlue;
\r
68 private QAction noteTitleColorGreen;
\r
69 private QAction noteTitleColorYellow;
\r
70 private QAction noteTitleColorBlack;
\r
71 private QAction noteTitleColorGray;
\r
72 private QAction noteTitleColorCyan;
\r
73 private QAction noteTitleColorMagenta;
\r
77 public QHeaderView header;
\r
79 public Signal1<String> rowChanged;
\r
80 public Signal0 resetViewport;
\r
81 public NoteSignal noteSignal;
\r
83 public TableView(ApplicationLogger l) {
\r
85 header = horizontalHeader();
\r
86 header.setMovable(true);
\r
88 noteSignal = new NoteSignal();
\r
89 setAcceptDrops(true);
\r
90 setDragEnabled(true);
\r
91 setDragDropMode(QAbstractItemView.DragDropMode.DragDrop);
\r
92 setDropIndicatorShown(false);
\r
94 model = new QStandardItemModel(0,Global.noteTableColumnCount, this);
\r
96 model.setHeaderData(Global.noteTableCreationPosition, Qt.Orientation.Horizontal, "Date Created");
\r
97 model.setHeaderData(Global.noteTableTagPosition, Qt.Orientation.Horizontal, "Tags");
\r
98 model.setHeaderData(Global.noteTableGuidPosition, Qt.Orientation.Horizontal, "Guid");
\r
99 model.setHeaderData(Global.noteTableNotebookPosition, Qt.Orientation.Horizontal, "Notebook");
\r
100 model.setHeaderData(Global.noteTableTitlePosition, Qt.Orientation.Horizontal, "Title");
\r
101 model.setHeaderData(Global.noteTableChangedPosition, Qt.Orientation.Horizontal, "Date Changed");
\r
102 model.setHeaderData(Global.noteTableAuthorPosition, Qt.Orientation.Horizontal, "Author");
\r
103 model.setHeaderData(Global.noteTableSourceUrlPosition, Qt.Orientation.Horizontal, "Source Url");
\r
104 model.setHeaderData(Global.noteTableSubjectDatePosition, Qt.Orientation.Horizontal, "Subject Date");
\r
105 model.setHeaderData(Global.noteTableSynchronizedPosition, Qt.Orientation.Horizontal, "Synchronized");
\r
106 header.sortIndicatorChanged.connect(this, "resetViewport()");
\r
108 proxyModel = new NoteSortFilterProxyModel(this);
\r
109 proxyModel.setSourceModel(model);
\r
111 setAlternatingRowColors(false);
\r
112 setModel(proxyModel);
\r
114 setSortingEnabled(true);
\r
115 int sortCol = proxyModel.sortColumn();
\r
116 SortOrder sortOrder = proxyModel.sortOrder();
\r
117 sortByColumn(sortCol, sortOrder);
\r
119 setSelectionBehavior(SelectionBehavior.SelectRows);
\r
120 setSelectionMode(SelectionMode.SingleSelection);
\r
121 verticalHeader().setVisible(false);
\r
122 hideColumn(Global.noteTableGuidPosition); // Hide the guid column
\r
123 setShowGrid(false);
\r
124 setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers);
\r
126 QFontMetrics f = QApplication.fontMetrics();
\r
127 fontHeight = f.height();
\r
128 rowChanged = new Signal1<String>();
\r
129 resetViewport = new Signal0();
\r
132 // This should rescroll to the current item in the list when a column is
\r
133 // sorted. Somehow I can't get this to work, but this part is correct.
\r
134 @SuppressWarnings("unused")
\r
135 private void resetViewport() {
\r
136 // if (currentIndex() == null)
\r
139 // resetViewport.emit();
\r
143 public void load(ListManager runner, boolean reload) {
\r
145 proxyModel.clear();
\r
146 setSortingEnabled(true);
\r
147 for (int i=0; i<runner.getNoteIndex().size(); i++) {
\r
148 if (Global.showDeleted == true && !runner.getNoteIndex().get(i).isActive())
\r
149 proxyModel.addGuid(runner.getNoteIndex().get(i).getGuid());
\r
150 if (!Global.showDeleted == true && runner.getNoteIndex().get(i).isActive())
\r
151 proxyModel.addGuid(runner.getNoteIndex().get(i).getGuid());
\r
154 logger.log(logger.EXTREME, "TableView.load() reload starting.");
\r
155 proxyModel.filter();
\r
156 logger.log(logger.EXTREME, "TableView.load() leaving reload.");
\r
157 QFontMetrics f = QApplication.fontMetrics();
\r
158 fontHeight = f.height();
\r
159 for (int i=0; i<model.rowCount(); i++)
\r
160 setRowHeight(i, fontHeight);
\r
161 resetViewport.emit();
\r
164 logger.log(logger.EXTREME, "TableView.load() Filling table data from scratch");
\r
165 model.setRowCount(runner.getMasterNoteIndex().size());
\r
167 for (int i=0; i<runner.getMasterNoteIndex().size(); i++) {
\r
168 if (runner.getMasterNoteIndex().get(i) != null) {
\r
169 insertRow(runner, runner.getMasterNoteIndex().get(i), false, i);
\r
176 width = Global.getColumnWidth("noteTableCreationPosition");
\r
177 if (width>0) setColumnWidth(Global.noteTableCreationPosition, width);
\r
178 width = Global.getColumnWidth("noteTableChangedPosition");
\r
179 if (width>0) setColumnWidth(Global.noteTableChangedPosition, width);
\r
180 width = Global.getColumnWidth("noteTableTitlePosition");
\r
181 if (width>0) setColumnWidth(Global.noteTableTitlePosition, width);
\r
182 width = Global.getColumnWidth("noteTableTagPosition");
\r
183 if (width>0) setColumnWidth(Global.noteTableTagPosition, width);
\r
184 width = Global.getColumnWidth("noteTableGuidPosition");
\r
185 if (width>0) setColumnWidth(Global.noteTableGuidPosition, width);
\r
186 width = Global.getColumnWidth("noteTableNotebookPosition");
\r
187 if (width>0) setColumnWidth(Global.noteTableNotebookPosition, width);
\r
188 width = Global.getColumnWidth("noteTableSourceUrlPosition");
\r
189 if (width>0) setColumnWidth(Global.noteTableSourceUrlPosition, width);
\r
190 width = Global.getColumnWidth("noteTableAuthorPosition");
\r
191 if (width>0) setColumnWidth(Global.noteTableAuthorPosition, width);
\r
192 width = Global.getColumnWidth("noteTableSubjectDatePosition");
\r
193 if (width>0) setColumnWidth(Global.noteTableSubjectDatePosition, width);
\r
194 width = Global.getColumnWidth("noteTableSynchronizedPosition");
\r
195 if (width>0) setColumnWidth(Global.noteTableSynchronizedPosition, width);
\r
197 int from = header.visualIndex(Global.noteTableCreationPosition);
\r
198 int to = Global.getColumnPosition("noteTableCreationPosition");
\r
199 if (to>=0) header.moveSection(from, to);
\r
201 from = header.visualIndex(Global.noteTableTitlePosition);
\r
202 to = Global.getColumnPosition("noteTableTitlePosition");
\r
203 if (to>=0) header.moveSection(from, to);
\r
205 from = header.visualIndex(Global.noteTableTagPosition);
\r
206 to = Global.getColumnPosition("noteTableTagPosition");
\r
207 if (to>=0) header.moveSection(from, to);
\r
209 from = header.visualIndex(Global.noteTableNotebookPosition);
\r
210 to = Global.getColumnPosition("noteTableNotebookPosition");
\r
211 if (to>=0) header.moveSection(from, to);
\r
213 from = header.visualIndex(Global.noteTableChangedPosition);
\r
214 to = Global.getColumnPosition("noteTableChangedPosition");
\r
215 if (to>=0) header.moveSection(from, to);
\r
217 from = header.visualIndex(Global.noteTableSourceUrlPosition);
\r
218 to = Global.getColumnPosition("noteTableSourceUrlPosition");
\r
219 if (to>=0) header.moveSection(from, to);
\r
221 from = header.visualIndex(Global.noteTableAuthorPosition);
\r
222 to = Global.getColumnPosition("noteTableAuthorPosition");
\r
223 if (to>=0) header.moveSection(from, to);
\r
225 from = header.visualIndex(Global.noteTableSubjectDatePosition);
\r
226 to = Global.getColumnPosition("noteTableSubjectDatePosition");
\r
227 if (to>=0) header.moveSection(from, to);
\r
229 from = header.visualIndex(Global.noteTableSynchronizedPosition);
\r
230 to = Global.getColumnPosition("noteTableSynchronizedPosition");
\r
231 if (to>=0) header.moveSection(from, to);
\r
233 proxyModel.filter();
\r
235 QFontMetrics f = QApplication.fontMetrics();
\r
236 fontHeight = f.height();
\r
237 for (int i=0; i<model.rowCount(); i++)
\r
238 setRowHeight(i, fontHeight);
\r
240 resetViewport.emit();
\r
243 public void insertRow(ListManager runner, Note tempNote, boolean newNote, int row) {
\r
245 proxyModel.addGuid(tempNote.getGuid());
\r
246 String fmt = Global.getDateFormat() + " " + Global.getTimeFormat();
\r
247 String dateTimeFormat = new String(fmt);
\r
248 SimpleDateFormat simple = new SimpleDateFormat(dateTimeFormat);
\r
251 String tagNames = runner.getTagNamesForNote(tempNote);
\r
252 StringBuilder creationDate = new StringBuilder(simple.format(tempNote.getCreated()));
\r
253 StringBuilder changedDate = new StringBuilder(simple.format(tempNote.getUpdated()));
\r
254 StringBuilder subjectDate;
\r
255 if (tempNote.getAttributes().getSubjectDate() == 0)
\r
256 subjectDate = creationDate;
\r
258 subjectDate = new StringBuilder(simple.format(tempNote.getAttributes().getSubjectDate()));
\r
260 String sync = "true";
\r
261 for (int i=0; i<runner.getUnsynchronizedNotes().size(); i++) {
\r
262 if (runner.getUnsynchronizedNotes().get(i).equalsIgnoreCase(tempNote.getGuid())) {
\r
264 i = runner.getUnsynchronizedNotes().size();
\r
268 if (row > model.rowCount())
\r
269 model.insertRow(0);
\r
272 row = model.rowCount();
\r
273 model.insertRow(row);
\r
274 // row = model.rowCount()-1;
\r
277 QColor backgroundColor = new QColor(QColor.white);
\r
278 QColor foregroundColor = new QColor(QColor.black);
\r
280 if (runner.titleColors != null && runner.titleColors.containsKey(tempNote.getGuid())) {
\r
281 int color = runner.titleColors.get(tempNote.getGuid());
\r
282 backgroundColor.setRgb(color);
\r
284 if (backgroundColor.rgb() == QColor.black.rgb() || backgroundColor.rgb() == QColor.blue.rgb())
\r
285 foregroundColor.setRgb(QColor.white.rgb());
\r
288 QDateTime created = QDateTime.fromString(creationDate.toString(), fmt);
\r
289 QDateTime changed = QDateTime.fromString(changedDate.toString(), fmt);
\r
290 QDateTime subjectDateFormat = QDateTime.fromString(subjectDate.toString(), fmt);
\r
291 model.setData(model.index(row, Global.noteTableCreationPosition), created.toString(fmt));
\r
292 model.setData(model.index(row, Global.noteTableChangedPosition), changed.toString(fmt));
\r
293 model.setData(model.index(row, Global.noteTableTitlePosition), tempNote.getTitle());
\r
294 model.setData(model.index(row, Global.noteTableTagPosition), tagNames);
\r
295 model.setData(model.index(row, Global.noteTableGuidPosition), tempNote.getGuid());
\r
296 model.setData(model.index(row, Global.noteTableSubjectDatePosition), subjectDateFormat.toString(fmt));
\r
297 model.setData(model.index(row, Global.noteTableAuthorPosition), tempNote.getAttributes().getAuthor());
\r
298 model.setData(model.index(row, Global.noteTableSourceUrlPosition), tempNote.getAttributes().getSourceURL());
\r
299 model.setData(model.index(row, Global.noteTableNotebookPosition), runner.getNotebookNameByGuid(tempNote.getNotebookGuid()));
\r
300 model.setData(model.index(row, Global.noteTableSynchronizedPosition), sync);
\r
302 for (int i=0; i<Global.noteTableColumnCount; i++) {
\r
303 model.setData(row, i, backgroundColor, Qt.ItemDataRole.BackgroundRole);
\r
304 model.setData(row, i, foregroundColor, Qt.ItemDataRole.ForegroundRole);
\r
308 QFontMetrics f = QApplication.fontMetrics();
\r
309 fontHeight = f.height();
\r
310 for (int i=0; i<model.rowCount(); i++)
\r
311 setRowHeight(i, fontHeight);
\r
314 protected boolean filterAcceptsRow(int sourceRow, QModelIndex sourceParent) {
\r
318 public void setAddAction(QAction a) {
\r
322 public void setMergeNotesAction(QAction a) {
\r
323 mergeNotesAction = a;
\r
326 public void setNoteHistoryAction(QAction a) {
\r
327 noteHistoryAction = a;
\r
330 public void setDeleteAction(QAction d) {
\r
334 public void setRestoreAction(QAction r) {
\r
337 public void setNoteDuplicateAction(QAction d) {
\r
338 duplicateAction = d;
\r
342 public void keyPressEvent(QKeyEvent e) {
\r
343 if (e.matches(StandardKey.MoveToStartOfDocument)) {
\r
344 if (model.rowCount() > 0) {
\r
349 if (e.matches(StandardKey.MoveToEndOfDocument)) {
\r
350 if (model.rowCount() > 0) {
\r
352 selectRow(model().rowCount()-1);
\r
355 super.keyPressEvent(e);
\r
359 public void contextMenuEvent(QContextMenuEvent event) {
\r
360 QMenu menu = new QMenu(this);
\r
361 if (Global.showDeleted) {
\r
362 menu.addAction(restoreAction);
\r
364 menu.addAction(addAction);
\r
366 menu.addAction(deleteAction);
\r
367 menu.addSeparator();
\r
368 menu.addAction(duplicateAction);
\r
369 menu.addAction(noteHistoryAction);
\r
370 menu.addAction(mergeNotesAction);
\r
372 QMenu titleColorMenu = new QMenu();
\r
373 titleColorMenu.setTitle("Title Color");
\r
374 menu.addMenu(titleColorMenu);
\r
375 noteTitleColorWhite = new QAction(titleColorMenu);
\r
376 noteTitleColorRed = new QAction(titleColorMenu);
\r
377 noteTitleColorBlue = new QAction(titleColorMenu);
\r
378 noteTitleColorGreen = new QAction(titleColorMenu);
\r
379 noteTitleColorYellow = new QAction(titleColorMenu);
\r
380 noteTitleColorBlack = new QAction(titleColorMenu);
\r
381 noteTitleColorGray = new QAction(titleColorMenu);
\r
382 noteTitleColorCyan = new QAction(titleColorMenu);
\r
383 noteTitleColorMagenta = new QAction(titleColorMenu);
\r
385 noteTitleColorWhite.setText("White");
\r
386 noteTitleColorRed.setText("Red");
\r
387 noteTitleColorBlue.setText("Blue");
\r
388 noteTitleColorGreen.setText("Green");
\r
389 noteTitleColorYellow.setText("Yellow");
\r
390 noteTitleColorBlack.setText("Black");
\r
391 noteTitleColorGray.setText("Gray");
\r
392 noteTitleColorCyan.setText("Cyan");
\r
393 noteTitleColorMagenta.setText("Magenta");
\r
395 titleColorMenu.addAction(noteTitleColorWhite);
\r
396 titleColorMenu.addAction(noteTitleColorRed);
\r
397 titleColorMenu.addAction(noteTitleColorBlue);
\r
398 titleColorMenu.addAction(noteTitleColorGreen);
\r
399 titleColorMenu.addAction(noteTitleColorYellow);
\r
400 titleColorMenu.addAction(noteTitleColorBlack);
\r
401 titleColorMenu.addAction(noteTitleColorGray);
\r
402 titleColorMenu.addAction(noteTitleColorCyan);
\r
403 titleColorMenu.addAction(noteTitleColorMagenta);
\r
405 noteTitleColorWhite.triggered.connect(this, "titleColorWhite()");
\r
407 noteTitleColorWhite.triggered.connect(this, "titleColorWhite()");
\r
408 noteTitleColorRed.triggered.connect(this, "titleColorRed()");
\r
409 noteTitleColorBlue.triggered.connect(this, "titleColorBlue()");
\r
410 noteTitleColorGreen.triggered.connect(this, "titleColorGreen()");
\r
411 noteTitleColorYellow.triggered.connect(this, "titleColorYellow()");
\r
412 noteTitleColorBlack.triggered.connect(this, "titleColorBlack()");
\r
413 noteTitleColorGray.triggered.connect(this, "titleColorGray()");
\r
414 noteTitleColorCyan.triggered.connect(this, "titleColorCyan()");
\r
415 noteTitleColorMagenta.triggered.connect(this, "titleColorMagenta()");
\r
417 menu.exec(event.globalPos());
\r
421 @SuppressWarnings("unused")
\r
422 private void titleColorWhite() {noteSignal.titleColorChanged.emit(QColor.white.rgb());}
\r
423 @SuppressWarnings("unused")
\r
424 private void titleColorRed() {noteSignal.titleColorChanged.emit(QColor.red.rgb());}
\r
425 @SuppressWarnings("unused")
\r
426 private void titleColorBlue() {noteSignal.titleColorChanged.emit(QColor.blue.rgb());}
\r
427 @SuppressWarnings("unused")
\r
428 private void titleColorGreen() {noteSignal.titleColorChanged.emit(QColor.green.rgb());}
\r
429 @SuppressWarnings("unused")
\r
430 private void titleColorYellow(){noteSignal.titleColorChanged.emit(QColor.yellow.rgb());}
\r
431 @SuppressWarnings("unused")
\r
432 private void titleColorBlack() {noteSignal.titleColorChanged.emit(QColor.black.rgb());}
\r
433 @SuppressWarnings("unused")
\r
434 private void titleColorGray() {noteSignal.titleColorChanged.emit(QColor.gray.rgb());}
\r
435 @SuppressWarnings("unused")
\r
436 private void titleColorCyan() {noteSignal.titleColorChanged.emit(QColor.cyan.rgb());}
\r
437 @SuppressWarnings("unused")
\r
438 private void titleColorMagenta() {noteSignal.titleColorChanged.emit(QColor.magenta.rgb());}
\r
443 public void dragEnterEvent(QDragEnterEvent event) {
\r
444 StringBuffer guid = new StringBuffer(1000);
\r
446 showColumn(Global.noteTableGuidPosition);
\r
447 List<QModelIndex> selections = selectionModel().selectedRows();
\r
448 hideColumn(Global.noteTableGuidPosition);
\r
450 if (selections.size() > 0) {
\r
452 for (int i=0; i<selections.size(); i++) {
\r
453 int row = selections.get(i).row();
\r
454 index = proxyModel.index(row, Global.noteTableGuidPosition);
\r
455 SortedMap<Integer, Object> ix = proxyModel.itemData(index);
\r
456 guid.append((String)ix.values().toArray()[0]);
\r
460 event.mimeData().setData("application/x-nevernote-note", new QByteArray(guid.toString()));
\r
466 public void dropEvent(QDropEvent event) {
\r
467 if (event.source() == this)
\r
471 // Return a column width
\r
472 public int getColumnWidth(int col) {
\r
473 return columnWidth(col);
\r
478 public void scrollTo(final QModelIndex index, ScrollHint hint) {
\r
479 QRect area = viewport().rect();
\r
480 QRect rect = visualRect(index);
\r
482 if (rect.top() < area.top())
\r
483 verticalScrollBar().setValue(
\r
484 verticalScrollBar().value() + rect.top() - area.top());
\r
485 else if (rect.bottom() > area.bottom())
\r
486 verticalScrollBar().setValue(
\r
487 verticalScrollBar().value() + Math.min(
\r
488 rect.bottom() - area.bottom(), rect.top() - area.top()));
\r
493 protected void updateGeometries() {
\r
494 verticalScrollBar().setPageStep(viewport().height());
\r
495 verticalScrollBar().setRange(0, Math.max(0, viewport().height()));
\r
498 protected int verticalOffset() {
\r
499 return verticalScrollBar().value();
\r