OSDN Git Service

- Gui cleanup - Rework count threads to reduce SQL overhead & improve performance.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / gui / NotebookTreeWidget.java
1 /*\r
2  * This file is part of NeverNote \r
3  * Copyright 2009 Randy Baumgarte\r
4  * \r
5  * This file may be licensed under the terms of of the\r
6  * GNU General Public License Version 2 (the ``GPL'').\r
7  *\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
12  *\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
17  *\r
18 */\r
19 \r
20 package cx.fbn.nevernote.gui;\r
21 \r
22 import java.util.ArrayList;\r
23 import java.util.HashMap;\r
24 import java.util.List;\r
25 \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
46 \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
51 \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
67         \r
68         public void setAddAction(QAction a) {\r
69                 addAction = a;\r
70         }\r
71         \r
72         public void setPublishAction(QAction p) {\r
73                 publishAction = p;\r
74         }\r
75         \r
76         public void setShareAction(QAction s) {\r
77                 shareAction = s;\r
78         }\r
79         \r
80         public void setDeleteAction(QAction d) {\r
81                 deleteAction = d;\r
82         }\r
83         \r
84         public void setEditAction(QAction e) {\r
85                 editAction = e;\r
86         }\r
87         \r
88         public void setStackAction(QAction e) {\r
89                 stackAction = e;\r
90         }\r
91         \r
92         public void setIconAction(QAction e) {\r
93                 iconAction = e;\r
94         }\r
95         \r
96         public NotebookTreeWidget(DatabaseConnection db) {\r
97                 noteSignal = new NoteSignal();\r
98                 this.db = db;\r
99 //              setProperty("hideTree", true);\r
100                 List<String> labels = new ArrayList<String>();\r
101                 labels.add("Notebooks");\r
102                 labels.add("");\r
103                 setAcceptDrops(true);\r
104                 setDragEnabled(true);\r
105                 setColumnCount(2);\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
115                 } else\r
116                         setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);\r
117 \r
118                 stacks = new HashMap<String, QTreeWidgetItem>();\r
119 //      int width = Global.getColumnWidth("notebookTreeName");\r
120 //              if (width>0)\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
127         }\r
128         \r
129         public void selectNotebook(QTreeWidgetItem item) {\r
130                 QTreeWidgetItem root = invisibleRootItem();\r
131                 QTreeWidgetItem child;\r
132                 \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
137                                 return;\r
138                         }\r
139                 }\r
140                 \r
141         }\r
142         \r
143         public boolean selectGuid(String guid) {\r
144                 QTreeWidgetItem root = invisibleRootItem();\r
145                 QTreeWidgetItem child;\r
146 \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
151                                 return true;\r
152                         }\r
153                 }\r
154                 return false;\r
155         }\r
156         \r
157         public void setIcons(HashMap<String, QIcon> i) {\r
158                 icons = i;\r
159         }\r
160         \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
168                 \r
169                 if (localBooks.contains(guid) && \r
170                         (name.equalsIgnoreCase("Conflicting Changes") ||\r
171                          name.equalsIgnoreCase("Conflicting Changes (Local)")))\r
172                                 return redIcon;\r
173                 if (localBooks.contains(guid)) {\r
174                         return yellowIcon;\r
175                 }\r
176                 if (isPublished)\r
177                         return blueIcon;\r
178                 \r
179                 if (db.getNotebookTable().isLinked(guid))\r
180                         return orangeIcon;\r
181 \r
182                 return greenIcon;\r
183         }\r
184         \r
185         public void load(List<Notebook> books, List<String> localBooks) {\r
186         Notebook book;\r
187         NTreeWidgetItem child;\r
188         \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
195         }\r
196         \r
197         clear();\r
198         stacks.clear();\r
199         \r
200         if (books == null)\r
201                 return;\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
211                 } else {\r
212                         child.setIcon(0, icons.get(book.getGuid()));\r
213                 }\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
218                 else {\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
225                         } else\r
226                                 parent = stacks.get(stackName);\r
227                         parent.addChild(child);\r
228                         \r
229                 }\r
230         }\r
231 \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
236                 \r
237                 if (allIcon == null)\r
238                         allIcon = new QIcon(iconPath+"notebook-green.png");\r
239                 \r
240                 child = new NTreeWidgetItem();\r
241                 child.setIcon(0, allIcon);\r
242                 child.setText(0, "All Notebooks");\r
243                 child.setText(2, "");\r
244                 child.setTextAlignment(1, ra.value());\r
245                 insertTopLevelItem(0,child);\r
246         }\r
247         resizeColumnToContents(0);\r
248         resizeColumnToContents(1);\r
249         \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
257                         }\r
258                 }\r
259         }\r
260         }\r
261 \r
262 \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
267                 \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
273                 else\r
274                         blue.setColor(QColor.black);\r
275                 int total=0;\r
276                 \r
277                 \r
278                 int size = books.size();\r
279                 if (Global.mimicEvernoteInterface)\r
280                         size++;\r
281                 \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
293                                 }\r
294                                 parent.setText(1, new Integer(localTotal).toString());\r
295                         } else\r
296                                 total = total+updateCounts(child, books, counts, blue, black);\r
297                 }\r
298                 \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
305                         }\r
306                 }\r
307         }\r
308         \r
309         private int updateCounts(QTreeWidgetItem child, List<Notebook> books, List<NotebookCounter> counts, QBrush blue, QBrush black) {\r
310                 int total=0;\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
323                                         }\r
324                                 }\r
325                         }\r
326                 }\r
327                 return total;\r
328         }\r
329         \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
338                         names.add(text);\r
339                 }\r
340                 return names;\r
341         }\r
342         \r
343         @Override\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
356         }\r
357         \r
358         \r
359         @Override\r
360         public void dragEnterEvent(QDragEnterEvent event) {\r
361                 if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
362                         event.accept();\r
363                         return;\r
364                 }\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
371                                                 event.ignore();\r
372                                                 return;\r
373                                         }\r
374                         }\r
375                         event.accept();\r
376                         return;\r
377                 }\r
378                 event.ignore();\r
379         }\r
380         \r
381         \r
382          @Override\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
395                                 }\r
396 */                              \r
397                         }\r
398                         if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
399                                 if (event.answerRect().intersects(childrenRect()))\r
400                                         event.acceptProposedAction();\r
401                                 return;\r
402                         }\r
403             }\r
404 \r
405         \r
406         @Override\r
407         public boolean dropMimeData(QTreeWidgetItem parent, int index, QMimeData data, Qt.DropAction action) {\r
408                 if (data.hasFormat("application/x-nevernote-notebook")) {\r
409                         return false;\r
410                 }\r
411                 \r
412                 // This is really dead code.  it is the beginning of logic to create stacks by\r
413                 // dragging.\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
417                         \r
418                         // If dropping to the top level, then remove the stack\r
419                         if (parent == null) {\r
420                                 db.getNotebookTable().clearStack(current);\r
421                                 return true;\r
422                         } \r
423                         \r
424                         // If trying to drop under the "All notebooks" then ignore\r
425                         if (parent.text(2).equals(""))\r
426                                 return false;\r
427                         \r
428                         \r
429                         // If we are NOT droping directly onto the stack icon\r
430                         // we need to find the stack widget\r
431                         String stackName;\r
432                         QTreeWidgetItem stackItem;\r
433                         List<QTreeWidgetItem> currentItems = selectedItems();\r
434                         if (!parent.text(2).equalsIgnoreCase("STACK")) {\r
435                                 \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
440                                 } else {\r
441                                         \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
449                                                 else\r
450                                                         break;\r
451                                         }\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
456                                 }\r
457                         } else {\r
458                                 stackName = parent.text(0);\r
459                                 stackItem = parent;\r
460                         }\r
461                         \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
466                         }\r
467                         db.getNotebookTable().setStack(current, stackName);             \r
468                         stackItem.addChildren(newItems);\r
469                         \r
470                         return true;\r
471                 }\r
472                 \r
473                 \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
478                                         return false;\r
479                         \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
485                                 \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
493                                         )) {\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
497                                         }\r
498                                 }\r
499                         }\r
500                         return true;\r
501                 }\r
502                 return false;\r
503         }\r
504         \r
505 \r
506         private QTreeWidgetItem createStackIcon(String stackName, Qt.Alignment ra) {\r
507                 String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
508                 QIcon stackIcon;\r
509                 stackIcon = db.getSystemIconTable().getIcon(stackName, "STACK");\r
510                 \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
519                 return parent;\r
520         }\r
521 \r
522         \r
523         \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
535 \r
536                 return target;\r
537         }\r
538 \r
539         \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
547                                 clearSelection();\r
548                         } else {\r
549                                 selectedNotebook = selectedItem.get(0).text(0);\r
550                         }\r
551                         \r
552                 }\r
553                 selectionSignal.emit();\r
554         }\r
555 \r
556         \r
557         @Override\r
558         public void mousePressEvent(QMouseEvent e) {\r
559                 if (e.button() == Qt.MouseButton.RightButton)\r
560                         rightButtonClicked = true;\r
561                 else\r
562                         rightButtonClicked = false;\r
563                 super.mousePressEvent(e);\r
564         }\r
565 }\r