2 * This file is part of NixNote/NeighborNote
\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
20 package cx.fbn.nevernote.gui;
\r
22 import java.util.ArrayList;
\r
23 import java.util.HashMap;
\r
24 import java.util.List;
\r
26 import com.evernote.edam.type.Note;
\r
27 import com.evernote.edam.type.Notebook;
\r
28 import com.trolltech.qt.core.QByteArray;
\r
29 import com.trolltech.qt.core.QMimeData;
\r
30 import com.trolltech.qt.core.Qt;
\r
31 import com.trolltech.qt.core.Qt.SortOrder;
\r
32 import com.trolltech.qt.gui.QAbstractItemView;
\r
33 import com.trolltech.qt.gui.QAction;
\r
34 import com.trolltech.qt.gui.QBrush;
\r
35 import com.trolltech.qt.gui.QColor;
\r
36 import com.trolltech.qt.gui.QContextMenuEvent;
\r
37 import com.trolltech.qt.gui.QDragEnterEvent;
\r
38 import com.trolltech.qt.gui.QDragMoveEvent;
\r
39 import com.trolltech.qt.gui.QHeaderView;
\r
40 import com.trolltech.qt.gui.QIcon;
\r
41 import com.trolltech.qt.gui.QMenu;
\r
42 import com.trolltech.qt.gui.QMouseEvent;
\r
43 import com.trolltech.qt.gui.QTreeWidget;
\r
44 import com.trolltech.qt.gui.QTreeWidgetItem;
\r
45 import com.trolltech.qt.gui.QTreeWidgetItem.ChildIndicatorPolicy;
\r
47 import cx.fbn.nevernote.Global;
\r
48 import cx.fbn.nevernote.filters.NotebookCounter;
\r
49 import cx.fbn.nevernote.signals.NoteSignal;
\r
50 import cx.fbn.nevernote.sql.DatabaseConnection;
\r
52 public class NotebookTreeWidget extends QTreeWidget {
\r
53 private QAction deleteAction;
\r
54 private QAction addAction;
\r
55 private QAction editAction;
\r
56 private QAction iconAction;
\r
57 private QAction stackAction;
\r
58 private QAction publishAction;
\r
59 private QAction shareAction;
\r
60 public NoteSignal noteSignal;
\r
61 public Signal0 selectionSignal;
\r
62 private String selectedNotebook;
\r
63 private HashMap<String, QIcon> icons;
\r
64 private final DatabaseConnection db;
\r
65 private final HashMap<String, QTreeWidgetItem> stacks;
\r
66 private boolean rightButtonClicked;
\r
68 public void setAddAction(QAction a) {
\r
72 public void setPublishAction(QAction p) {
\r
76 public void setShareAction(QAction s) {
\r
80 public void setDeleteAction(QAction d) {
\r
84 public void setEditAction(QAction e) {
\r
88 public void setStackAction(QAction e) {
\r
92 public void setIconAction(QAction e) {
\r
96 public NotebookTreeWidget(DatabaseConnection db) {
\r
97 noteSignal = new NoteSignal();
\r
99 // setProperty("hideTree", true);
\r
100 List<String> labels = new ArrayList<String>();
\r
101 labels.add(tr("Notebooks"));
\r
103 setAcceptDrops(true);
\r
104 setDragEnabled(true);
\r
106 header().setResizeMode(0, QHeaderView.ResizeMode.ResizeToContents);
\r
107 header().setResizeMode(1, QHeaderView.ResizeMode.Stretch);
\r
108 header().setMovable(false);
\r
109 header().setStyleSheet("QHeaderView::section {border: 0.0em;}");
\r
110 setHeaderLabels(labels);
\r
111 setDragDropMode(QAbstractItemView.DragDropMode.DragDrop);
\r
112 // If we want to mimic Evernote's notebook selection
\r
113 if (Global.mimicEvernoteInterface) {
\r
114 setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection);
\r
116 setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);
\r
118 stacks = new HashMap<String, QTreeWidgetItem>();
\r
119 // int width = Global.getColumnWidth("notebookTreeName");
\r
121 // setColumnWidth(0, width);
\r
122 // previousMouseOver = new QTreeWidgetItem();
\r
123 selectionSignal = new Signal0();
\r
124 selectedNotebook = "";
\r
125 rightButtonClicked = false;
\r
126 itemClicked.connect(this, "itemClicked()");
\r
129 public void selectNotebook(QTreeWidgetItem item) {
\r
130 QTreeWidgetItem root = invisibleRootItem();
\r
131 QTreeWidgetItem child;
\r
133 for (int i=0; i<root.childCount(); i++) {
\r
134 child = root.child(i);
\r
135 if (child.text(2).equals(item.text(2))) {
\r
136 child.setSelected(true);
\r
143 public boolean selectGuid(String guid) {
\r
144 QTreeWidgetItem root = invisibleRootItem();
\r
145 QTreeWidgetItem child;
\r
147 for (int i=0; i<root.childCount(); i++) {
\r
148 child = root.child(i);
\r
149 if (child.text(2).equals(guid)) {
\r
150 child.setSelected(true);
\r
157 public void setIcons(HashMap<String, QIcon> i) {
\r
161 public QIcon findDefaultIcon(String guid, String name, List<String> localBooks, boolean isPublished) {
\r
162 String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
\r
163 QIcon blueIcon = new QIcon(iconPath+"notebook-blue.png");
\r
164 QIcon greenIcon = new QIcon(iconPath+"notebook-green.png");
\r
165 QIcon redIcon = new QIcon(iconPath+"notebook-red.png");
\r
166 QIcon yellowIcon = new QIcon(iconPath+"notebook-yellow.png");
\r
167 QIcon orangeIcon = new QIcon(iconPath+"notebook-orange.png");
\r
169 if (localBooks.contains(guid) &&
\r
170 (name.equalsIgnoreCase("Conflicting Changes") ||
\r
171 name.equalsIgnoreCase("Conflicting Changes (Local)")))
\r
173 if (localBooks.contains(guid)) {
\r
179 if (db.getNotebookTable().isLinked(guid))
\r
185 public void load(List<Notebook> books, List<String> localBooks) {
\r
187 NTreeWidgetItem child;
\r
189 /* First, let's find out which stacks are expanded */
\r
190 QTreeWidgetItem root = invisibleRootItem();
\r
191 List<String> expandedStacks = new ArrayList<String>();
\r
192 for (int i=0; i<root.childCount(); i++) {
\r
193 if (root.child(i).isExpanded())
\r
194 expandedStacks.add(root.child(i).text(0));
\r
202 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
\r
203 for (int i=0; i<books.size(); i++) {
\r
204 book = books.get(i);
\r
205 child = new NTreeWidgetItem();
\r
206 child.setChildIndicatorPolicy(ChildIndicatorPolicy.DontShowIndicatorWhenChildless);
\r
207 child.setText(0, book.getName());
\r
208 if (icons != null && !icons.containsKey(book.getGuid())) {
\r
209 QIcon icon = findDefaultIcon(book.getGuid(), book.getName(), localBooks, book.isPublished());
\r
210 child.setIcon(0, icon);
\r
212 child.setIcon(0, icons.get(book.getGuid()));
\r
214 child.setTextAlignment(1, ra.value());
\r
215 child.setText(2, book.getGuid());
\r
216 if (book.getStack() == null || book.getStack().equalsIgnoreCase(""))
\r
217 addTopLevelItem(child);
\r
219 String stackName = book.getStack();
\r
220 QTreeWidgetItem parent;
\r
221 if (!stacks.containsKey(stackName)) {
\r
222 parent = createStackIcon(stackName, ra);
\r
223 addTopLevelItem(parent);
\r
224 stacks.put(stackName, parent);
\r
226 parent = stacks.get(stackName);
\r
227 parent.addChild(child);
\r
232 sortItems(0, SortOrder.AscendingOrder);
\r
233 if (Global.mimicEvernoteInterface) {
\r
234 String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
\r
235 QIcon allIcon = db.getSystemIconTable().getIcon("All Notebooks", "ALLNOTEBOOK");
\r
237 if (allIcon == null)
\r
238 allIcon = new QIcon(iconPath+"notebook-green.png");
\r
240 child = new NTreeWidgetItem();
\r
241 child.setIcon(0, allIcon);
\r
242 child.setText(0, tr("All Notebooks"));
\r
243 child.setText(2, "");
\r
244 child.setTextAlignment(1, ra.value());
\r
245 insertTopLevelItem(0,child);
\r
247 resizeColumnToContents(0);
\r
248 resizeColumnToContents(1);
\r
250 // Finally, expand the stacks back out
\r
251 root = invisibleRootItem();
\r
252 for (int i=0; i<root.childCount(); i++) {
\r
253 for (int j=0; j<expandedStacks.size(); j++) {
\r
254 if (root.child(i).text(0).equalsIgnoreCase(expandedStacks.get(j))) {
\r
255 expandItem(root.child(i));
\r
256 j=expandedStacks.size();
\r
263 // update the display with the current number of notes
\r
264 public void updateCounts(List<Notebook> books, List<NotebookCounter> counts) {
\r
265 QTreeWidgetItem root = invisibleRootItem();
\r
266 QTreeWidgetItem child;
\r
268 QBrush blue = new QBrush();
\r
269 QBrush black = new QBrush();
\r
270 black.setColor(QColor.black);
\r
271 if (Global.tagBehavior().equalsIgnoreCase("ColorActive") && !Global.mimicEvernoteInterface)
\r
272 blue.setColor(QColor.blue);
\r
274 blue.setColor(QColor.black);
\r
278 int size = books.size();
\r
279 if (Global.mimicEvernoteInterface)
\r
282 for (int i=0; i<size; i++) {
\r
283 child = root.child(i);
\r
284 if (child != null && child.childCount() > 0) {
\r
285 int count = child.childCount();
\r
286 QTreeWidgetItem parent = child;
\r
287 int localTotal = 0;
\r
288 for (int j=0; j<count; j++) {
\r
289 child = parent.child(j);
\r
290 int childCount = updateCounts(child, books, counts, blue, black);
\r
291 total = total+childCount;
\r
292 localTotal = localTotal+childCount;
\r
294 parent.setText(1, new Integer(localTotal).toString());
\r
296 total = total+updateCounts(child, books, counts, blue, black);
\r
299 for (int i=0; i<size; i++) {
\r
300 child = root.child(i);
\r
301 if (child != null) {
\r
302 String guid = child.text(2);
\r
303 if (guid.equals("") && Global.mimicEvernoteInterface)
\r
304 child.setText(1, new Integer(total).toString());
\r
309 private int updateCounts(QTreeWidgetItem child, List<Notebook> books, List<NotebookCounter> counts, QBrush blue, QBrush black) {
\r
311 if (child != null) {
\r
312 String guid = child.text(2);
\r
313 child.setText(1,"0");
\r
314 child.setForeground(0, black);
\r
315 child.setForeground(1, black);
\r
316 for (int j=0; j<counts.size(); j++) {
\r
317 if (counts.get(j).getGuid().equals(guid)) {
\r
318 child.setText(1, new Integer(counts.get(j).getCount()).toString());
\r
319 total = counts.get(j).getCount();
\r
320 if (counts.get(j).getCount() > 0) {
\r
321 child.setForeground(0, blue);
\r
322 child.setForeground(1, blue);
\r
330 // Return a list of the notebook guids, ordered by the current display order.
\r
331 public List<String> getNotebookGuids() {
\r
332 List<String> names = new ArrayList<String>();
\r
333 QTreeWidgetItem root = invisibleRootItem();
\r
334 QTreeWidgetItem child;
\r
335 for (int i=0; i<root.childCount(); i++) {
\r
336 child = root.child(i);
\r
337 String text = child.text(2);
\r
344 public void contextMenuEvent(QContextMenuEvent event) {
\r
345 QMenu menu = new QMenu(this);
\r
346 menu.addAction(addAction);
\r
347 menu.addAction(editAction);
\r
348 menu.addAction(deleteAction);
\r
349 menu.addAction(stackAction);
\r
350 menu.addSeparator();
\r
351 menu.addAction(publishAction);
\r
352 menu.addAction(shareAction);
\r
353 menu.addSeparator();
\r
354 menu.addAction(iconAction);
\r
355 menu.exec(event.globalPos());
\r
360 public void dragEnterEvent(QDragEnterEvent event) {
\r
361 if (event.mimeData().hasFormat("application/x-nevernote-note")) {
\r
365 if (event.source() == this) {
\r
366 event.mimeData().setData("application/x-nevernote-notebook", new QByteArray(currentItem().text(2)));
\r
367 List<QTreeWidgetItem> selected = selectedItems();
\r
368 for (int i=0; i<selected.size(); i++) {
\r
369 if (selected.get(i).text(2).equalsIgnoreCase("STACK") ||
\r
370 selected.get(i).text(2).equals("")) {
\r
383 protected void dragMoveEvent(QDragMoveEvent event) {
\r
384 // if (event.mimeData().hasFormat("text/plain") &&
\r
385 //event.answerRect().intersects(dropFrame.geometry()))
\r
386 QTreeWidgetItem treeItem = itemAt(event.pos().x(), event.pos().y());
\r
387 if (treeItem != null) {
\r
388 /* if (!previousMouseOver.text(0).equalsIgnoreCase(treeItem.text(0))) {
\r
389 previousMouseOver.setSelected(previousMouseOverWasSelected);
\r
390 previousMouseOverWasSelected = treeItem.isSelected();
\r
391 previousMouseOver = treeItem;
\r
392 blockSignals(true);
\r
393 treeItem.setSelected(true);
\r
394 blockSignals(false);
\r
398 if (event.mimeData().hasFormat("application/x-nevernote-note")) {
\r
399 if (event.answerRect().intersects(childrenRect()))
\r
400 event.acceptProposedAction();
\r
407 public boolean dropMimeData(QTreeWidgetItem parent, int index, QMimeData data, Qt.DropAction action) {
\r
408 if (data.hasFormat("application/x-nevernote-notebook")) {
\r
412 // This is really dead code. it is the beginning of logic to create stacks by
\r
414 if (data.hasFormat("application/x-nevernote-notebook")) {
\r
415 QByteArray d = data.data("application/x-nevernote-notebook");
\r
416 String current = d.toString();
\r
418 // If dropping to the top level, then remove the stack
\r
419 if (parent == null) {
\r
420 db.getNotebookTable().clearStack(current);
\r
424 // If trying to drop under the "All notebooks" then ignore
\r
425 if (parent.text(2).equals(""))
\r
429 // If we are NOT droping directly onto the stack icon
\r
430 // we need to find the stack widget
\r
432 QTreeWidgetItem stackItem;
\r
433 List<QTreeWidgetItem> currentItems = selectedItems();
\r
434 if (!parent.text(2).equalsIgnoreCase("STACK")) {
\r
436 // If a parent stack exists, then use it.
\r
437 if (parent.parent() != null) {
\r
438 stackName = parent.parent().text(0);
\r
439 stackItem = parent.parent();
\r
442 currentItems.add(parent);
\r
443 // If a stack doesn't exist, then we need to create one
\r
444 stackName = "New Stack";
\r
445 // Find a new stack name that isn't in use
\r
446 for (int i=1; i<101; i++) {
\r
447 if (stacks.containsKey(stackName))
\r
448 stackName = "New Stack(" +new Integer(i).toString() + ")";
\r
452 db.getNotebookTable().setStack(parent.text(2), stackName);
\r
453 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
\r
454 stackItem = createStackIcon(stackName, ra);
\r
455 addTopLevelItem(stackItem);
\r
458 stackName = parent.text(0);
\r
459 stackItem = parent;
\r
462 List<QTreeWidgetItem> newItems = new ArrayList<QTreeWidgetItem>();
\r
463 for (int i=0; i<currentItems.size(); i++) {
\r
464 newItems.add(copyTreeItem(currentItems.get(i)));
\r
465 currentItems.get(i).setHidden(true);
\r
467 db.getNotebookTable().setStack(current, stackName);
\r
468 stackItem.addChildren(newItems);
\r
474 // If we are dropping a note onto a notebook
\r
475 if (data.hasFormat("application/x-nevernote-note")) {
\r
476 // If we are dropping onto a read-only notebook, we are done.
\r
477 if (db.getNotebookTable().isReadOnly(parent.text(2)))
\r
480 QByteArray d = data.data("application/x-nevernote-note");
\r
481 String s = d.toString();
\r
482 String noteGuidArray[] = s.split(" ");
\r
483 for (String element : noteGuidArray) {
\r
484 Note n = db.getNoteTable().getNote(element.trim(), false, false, false, false, true);
\r
486 // We need to be sure that...
\r
487 // 1.) We are not dropping onto the "All Notebooks" stack
\r
488 // 2.) We are not dropping onto a stack
\r
489 // 3.) We are actually dropping onto a different notebook.
\r
490 if (!parent.text(2).equalsIgnoreCase("") &&
\r
491 !parent.text(2).equalsIgnoreCase(tr("STACK")) &&
\r
492 !(n.getNotebookGuid().equalsIgnoreCase(parent.text(2))
\r
494 noteSignal.notebookChanged.emit(element.trim(), parent.text(2));
\r
495 if (db.getNotebookTable().isLinked(parent.text(2))) {
\r
496 noteSignal.tagsChanged.emit(element.trim(), new ArrayList<String>());
\r
506 private QTreeWidgetItem createStackIcon(String stackName, Qt.Alignment ra) {
\r
507 String iconPath = new String("classpath:cx/fbn/nevernote/icons/");
\r
509 stackIcon = db.getSystemIconTable().getIcon(stackName, "STACK");
\r
511 if (stackIcon == null)
\r
512 stackIcon = new QIcon(iconPath+"books2.png");
\r
513 QTreeWidgetItem parent = new QTreeWidgetItem();
\r
514 stacks.put(stackName, parent);
\r
515 parent.setText(0, stackName);
\r
516 parent.setIcon(0, stackIcon);
\r
517 parent.setText(2, "STACK");
\r
518 parent.setTextAlignment(1, ra.value());
\r
524 // Copy an individual item within the tree. I need to do this because
\r
525 // Qt doesn't call the dropMimeData on a move, just a copy.
\r
526 private QTreeWidgetItem copyTreeItem(QTreeWidgetItem source) {
\r
527 QTreeWidgetItem target = new QTreeWidgetItem(this);
\r
528 target.setText(0, source.text(0));
\r
529 target.setIcon(0, source.icon(0));
\r
530 target.setText(1, source.text(1));
\r
531 target.setText(2, source.text(2));
\r
532 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);
\r
533 target.setTextAlignment(1, ra.value());
\r
534 source.setHidden(true);
\r
540 @SuppressWarnings("unused")
\r
541 private void itemClicked() {
\r
542 List<QTreeWidgetItem> selectedItem = selectedItems();
\r
543 if (selectedItem.size() == 1) {
\r
544 if (selectedItem.get(0).text(0).equalsIgnoreCase(selectedNotebook) &&
\r
545 !Global.mimicEvernoteInterface && !rightButtonClicked) {
\r
546 selectedNotebook = "";
\r
549 selectedNotebook = selectedItem.get(0).text(0);
\r
553 selectionSignal.emit();
\r
558 public void mousePressEvent(QMouseEvent e) {
\r
559 if (e.button() == Qt.MouseButton.RightButton)
\r
560 rightButtonClicked = true;
\r
562 rightButtonClicked = false;
\r
563 super.mousePressEvent(e);
\r