OSDN Git Service

Add shared notebook editing & syncing logic.
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / gui / TagTreeWidget.java
1 /*\r
2  * This file is part of NeverNote \r
3  * Copyright 2009,2010 Randy Baumgarte\r
4  * Copyright 2010 Hiroshi Miura\r
5  * \r
6  * This file may be licensed under the terms of of the\r
7  * GNU General Public License Version 2 (the ``GPL'').\r
8  *\r
9  * Software distributed under the License is distributed\r
10  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either\r
11  * express or implied. See the GPL for the specific language\r
12  * governing rights and limitations.\r
13  *\r
14  * You should have received a copy of the GPL along with this\r
15  * program. If not, go to http://www.gnu.org/licenses/gpl.html\r
16  * or write to the Free Software Foundation, Inc.,\r
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
18  *\r
19 */\r
20 \r
21 package cx.fbn.nevernote.gui;\r
22 \r
23 import java.util.ArrayList;\r
24 import java.util.HashMap;\r
25 import java.util.List;\r
26 \r
27 import com.evernote.edam.type.Note;\r
28 import com.evernote.edam.type.Tag;\r
29 import com.trolltech.qt.core.QByteArray;\r
30 import com.trolltech.qt.core.QMimeData;\r
31 import com.trolltech.qt.core.Qt;\r
32 import com.trolltech.qt.core.Qt.MatchFlag;\r
33 import com.trolltech.qt.core.Qt.MatchFlags;\r
34 import com.trolltech.qt.core.Qt.SortOrder;\r
35 import com.trolltech.qt.gui.QAbstractItemView;\r
36 import com.trolltech.qt.gui.QAction;\r
37 import com.trolltech.qt.gui.QBrush;\r
38 import com.trolltech.qt.gui.QColor;\r
39 import com.trolltech.qt.gui.QContextMenuEvent;\r
40 import com.trolltech.qt.gui.QDragEnterEvent;\r
41 import com.trolltech.qt.gui.QDragMoveEvent;\r
42 import com.trolltech.qt.gui.QHeaderView;\r
43 import com.trolltech.qt.gui.QIcon;\r
44 import com.trolltech.qt.gui.QMenu;\r
45 import com.trolltech.qt.gui.QTreeWidget;\r
46 import com.trolltech.qt.gui.QTreeWidgetItem;\r
47 \r
48 import cx.fbn.nevernote.Global;\r
49 import cx.fbn.nevernote.filters.TagCounter;\r
50 import cx.fbn.nevernote.signals.NoteSignal;\r
51 import cx.fbn.nevernote.signals.TagSignal;\r
52 import cx.fbn.nevernote.sql.DatabaseConnection;\r
53 \r
54 public class TagTreeWidget extends QTreeWidget {\r
55         private QAction editAction;\r
56         private QAction deleteAction;\r
57         private QAction addAction;\r
58         private QAction iconAction;\r
59         public TagSignal tagSignal;\r
60         public NoteSignal noteSignal;\r
61         private boolean showAllTags;\r
62         private final DatabaseConnection db;\r
63         private HashMap<String, QIcon>  icons;\r
64         \r
65         \r
66         public TagTreeWidget(DatabaseConnection d) {\r
67                 List<String> headers = new ArrayList<String>();\r
68                 headers.add(tr("Tags"));\r
69                 headers.add("");\r
70                 showAllTags = true;\r
71                 setAcceptDrops(true);\r
72                 setDragEnabled(true);\r
73                 setColumnCount(2);\r
74                 header().setResizeMode(0, QHeaderView.ResizeMode.ResizeToContents);\r
75                 header().setResizeMode(1, QHeaderView.ResizeMode.Stretch);\r
76                 header().setMovable(false);\r
77                 db = d;\r
78                 tagSignal = new TagSignal();\r
79                 noteSignal = new NoteSignal();\r
80                 setDragDropMode(QAbstractItemView.DragDropMode.DragDrop);\r
81         setHeaderLabels(headers);\r
82 //      setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection);\r
83         setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);\r
84         \r
85                 int width = Global.getColumnWidth("tagTreeName");\r
86                 if (width>0)\r
87                         setColumnWidth(0, width);\r
88 \r
89         }\r
90         \r
91         public void setEditAction(QAction e) {\r
92                 editAction = e;\r
93         }\r
94         public void setDeleteAction(QAction d) {\r
95                 deleteAction = d;\r
96         }\r
97         public void setAddAction(QAction a) {\r
98                 addAction = a;\r
99         }\r
100         public void setIconAction(QAction i) {\r
101                 iconAction = i;\r
102         }\r
103         \r
104         // Insert a new tag into the tree.  This is used when we dynamically add a \r
105         // new tag after the full tag tree has been built.  It only adds to the\r
106         // top level.\r
107         public void insertTag(String name, String guid) {\r
108         String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
109                 QIcon icon = new QIcon(iconPath+"tag.png");\r
110                 QTreeWidgetItem child;\r
111                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
112                 \r
113                 // Build new tag & add it\r
114                 child = new QTreeWidgetItem();\r
115                 child.setText(0, name);\r
116                 child.setIcon(0,icon);\r
117                 child.setText(2, guid);\r
118                 child.setTextAlignment(1, ra.value());\r
119                 addTopLevelItem(child);\r
120                 \r
121                 // Resort the list\r
122                 resizeColumnToContents(0);\r
123         resizeColumnToContents(1);\r
124         sortItems(0, SortOrder.AscendingOrder);\r
125         }\r
126         \r
127         public void load(List<Tag> tags) {\r
128         Tag tag;\r
129         List<QTreeWidgetItem> index = new ArrayList<QTreeWidgetItem>();\r
130         QTreeWidgetItem child;\r
131                         \r
132         //Clear out the tree & reload\r
133         clear();\r
134         String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
135                 QIcon icon = new QIcon(iconPath+"tag.png");\r
136         \r
137                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
138         \r
139                 // Create a copy.  We delete them out as they are found\r
140                 List<Tag> tempList = new ArrayList<Tag>();\r
141                 for (int i=0; i<tags.size(); i++) {\r
142                         tempList.add(tags.get(i));\r
143                 }\r
144                 \r
145         while (tempList.size() > 0) {\r
146                 for (int i=0; i<tempList.size(); i++) {\r
147                         tag = tempList.get(i);\r
148                         if (tag.getParentGuid()==null || tag.getParentGuid().equals("")) {\r
149                                 child = new QTreeWidgetItem();\r
150                                 child.setText(0, tag.getName());\r
151                                 if (icons != null && !icons.containsKey(tag.getGuid())) {\r
152                                         child.setIcon(0, icon);\r
153                                 } else {\r
154                                         child.setIcon(0, icons.get(tag.getGuid()));\r
155                                 }\r
156 \r
157                                 child.setText(2, tag.getGuid());\r
158                                 child.setTextAlignment(1, ra.value());\r
159                                 index.add(child);\r
160                                 addTopLevelItem(child);\r
161                                 tempList.remove(i);\r
162                         } else {\r
163                                 // We need to find the parent\r
164                                 for (int j=0; j<index.size(); j++) {\r
165                                         if (index.get(j).text(2).equals(tag.getParentGuid())) {\r
166                                         child = new QTreeWidgetItem();\r
167                                         child.setText(0, tag.getName());\r
168                                         child.setIcon(0, icon);\r
169                                         child.setText(2, tag.getGuid());\r
170                                         child.setTextAlignment(1, ra.value());\r
171                                         tempList.remove(i);\r
172                                         index.add(child);                                               \r
173                                         index.get(j).addChild(child);\r
174                                         }\r
175                                 }\r
176                         }\r
177                 } \r
178         }\r
179         resizeColumnToContents(0);\r
180         resizeColumnToContents(1);\r
181         sortItems(0, SortOrder.AscendingOrder);\r
182         }\r
183         // Show (unhide) all tags\r
184         public void showAllTags(boolean value) {\r
185                 showAllTags = value;\r
186         }\r
187         public void unhideAllTags() {\r
188                 MatchFlags flags = new MatchFlags();\r
189                 flags.set(MatchFlag.MatchWildcard);\r
190                 flags.set(MatchFlag.MatchRecursive);\r
191                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
192                 for (int i=0; i<children.size(); i++) {\r
193                         children.get(i).setHidden(false);\r
194                 }\r
195         }\r
196         // update the display with the current number of notes\r
197         public void updateCounts(List<TagCounter> counts) {\r
198                                 \r
199                 MatchFlags flags = new MatchFlags();\r
200                 flags.set(MatchFlag.MatchWildcard);\r
201                 flags.set(MatchFlag.MatchRecursive);\r
202 //              List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();\r
203                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
204                 \r
205                 QBrush black = new QBrush();\r
206                 black.setColor(QColor.black);\r
207                 QBrush blue = new QBrush();\r
208                 blue.setColor(QColor.blue);\r
209                 if (!Global.tagBehavior().equalsIgnoreCase("ColorActive"))\r
210                         blue.setColor(QColor.black);\r
211                 \r
212                 for (int i=0; i<children.size(); i++) {\r
213                         children.get(i).setText(1,"0");\r
214                         children.get(i).setForeground(0, black);                        \r
215                         children.get(i).setForeground(1, black);\r
216                         if (!showAllTags && (Global.tagBehavior().equalsIgnoreCase("HideInactiveCount") || Global.tagBehavior().equalsIgnoreCase("NoHideInactiveCount")))\r
217                                 children.get(i).setHidden(true);\r
218                         else\r
219                                 children.get(i).setHidden(false);\r
220                         if (children.get(i).isSelected())\r
221                                 children.get(i).setHidden(false);\r
222                 }\r
223                 for (int i=0; i<counts.size(); i++) {\r
224                         for (int j=0; j<children.size(); j++) {\r
225                                 String guid = children.get(j).text(2);\r
226                                 if (counts.get(i).getGuid().equals(guid)) {\r
227                                         children.get(j).setText(1, new Integer(counts.get(i).getCount()).toString());\r
228                                         if (counts.get(i).getCount() > 0 || children.get(j).isSelected()) {\r
229                                                 children.get(j).setForeground(0, blue);                 \r
230                                                 children.get(j).setForeground(1, blue);\r
231                                                 QTreeWidgetItem parent = children.get(j);\r
232                                                 while (parent != null) {\r
233                                                         parent.setForeground(0, blue);                  \r
234                                                         parent.setForeground(1, blue);\r
235                                                         parent.setHidden(false);\r
236                                                         parent = parent.parent();\r
237                                                 }\r
238                                         }\r
239                                 }\r
240                         }\r
241                 }\r
242         }\r
243 \r
244         \r
245         public boolean selectGuid(String guid) {\r
246                 MatchFlags flags = new MatchFlags();\r
247                 flags.set(MatchFlag.MatchWildcard);\r
248                 flags.set(MatchFlag.MatchRecursive);\r
249 //              List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();\r
250                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
251 \r
252                 for (int i=0; i<children.size(); i++) {\r
253                         if (children.get(i).text(2).equals(guid)) {\r
254                                 children.get(i).setSelected(true);\r
255                                 return true;\r
256                         }\r
257                 }\r
258                 return false;\r
259         }\r
260         \r
261          @Override\r
262          protected void dragMoveEvent(QDragMoveEvent event) {\r
263                 if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
264                         if (event.answerRect().intersects(childrenRect()))\r
265                                 event.acceptProposedAction();\r
266                         return;\r
267                 }\r
268          }\r
269 \r
270         \r
271         @Override\r
272         public void dragEnterEvent(QDragEnterEvent event) {\r
273                 if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
274                         event.accept();\r
275                         return;\r
276                 }\r
277                 if (event.source() == this) {\r
278                         if (Global.tagBehavior().equals("HideInactiveCount")) {\r
279                                 event.ignore();\r
280                                 return;\r
281                         }\r
282                         event.mimeData().setData("application/x-nevernote-tag", new QByteArray(currentItem().text(2)));\r
283                         event.accept();\r
284                         return;\r
285                 }\r
286                 event.ignore();\r
287         }\r
288 \r
289         @Override\r
290         public boolean dropMimeData(QTreeWidgetItem parent, int index, QMimeData data, Qt.DropAction action) {\r
291                 if (data.hasFormat("application/x-nevernote-tag")) {\r
292                         QByteArray d = data.data("application/x-nevernote-tag");\r
293                         String current = d.toString();\r
294                         \r
295                         // Check we don't do a dumb thing like move a parent to a child of itself\r
296                         if (!checkParent(parent, current))\r
297                                 return false;\r
298                         QTreeWidgetItem newChild;\r
299                         if (parent == null) {\r
300 //                              tagSignal.changeParent.emit(current, "");\r
301                                 db.getTagTable().updateTagParent(current, "");\r
302                                 newChild = new QTreeWidgetItem(this);\r
303                         } else {\r
304 //                              tagSignal.changeParent.emit(current, parent.text(2));\r
305                                 db.getTagTable().updateTagParent(current, parent.text(2));\r
306                                 newChild = new QTreeWidgetItem(parent);\r
307                         }\r
308                         copyTreeItem(currentItem(), newChild);\r
309                         currentItem().setHidden(true);\r
310                         sortItems(0, SortOrder.AscendingOrder);\r
311                         return true;\r
312                 }\r
313                 \r
314                 // If we are dropping a note\r
315                 if (data.hasFormat("application/x-nevernote-note")) {\r
316                         String notebookGuid = db.getTagTable().getNotebookGuid(parent.text(2));\r
317                         QByteArray d = data.data("application/x-nevernote-note");\r
318                         String s = d.toString();\r
319                         String noteGuidArray[] = s.split(" ");\r
320                         for (String element : noteGuidArray) {\r
321                                 Note n = db.getNoteTable().getNote(element.trim(), false, false, false, false, false);\r
322                                 \r
323                                 // Check that...\r
324                                 // 1.) Check that tag isn't already assigned to that note\r
325                                 // 2.) Check that that tag is valid for that notebook or the tag isn't notebook specific\r
326                                 // 3.) Check that the notebook isn't read only.\r
327                                 if (!db.getNoteTable().noteTagsTable.checkNoteNoteTags(element.trim(), parent.text(2)) &&\r
328                                                 (notebookGuid == null || n.getNotebookGuid().equalsIgnoreCase(notebookGuid) || notebookGuid.equals("")) &&\r
329                                                 !db.getNotebookTable().isReadOnly(n.getNotebookGuid())) {\r
330                                         db.getNoteTable().noteTagsTable.saveNoteTag(element.trim(), parent.text(2));\r
331                                         noteSignal.tagsAdded.emit(element.trim(), parent.text(2));\r
332                                 }\r
333                         }\r
334                         //tagSignal.listChanged.emit();\r
335                         \r
336                         return true;\r
337                 }\r
338                 return false;\r
339         }\r
340         \r
341         @Override\r
342         public void contextMenuEvent(QContextMenuEvent event) {\r
343                 QMenu menu = new QMenu(this);\r
344                 menu.addAction(addAction);\r
345                 menu.addAction(editAction);\r
346                 menu.addAction(deleteAction);\r
347                 menu.addSeparator();\r
348                 menu.addAction(iconAction);\r
349                 menu.exec(event.globalPos());\r
350         }\r
351         \r
352         public void setIcons(HashMap<String, QIcon> i) {\r
353                 icons = i;\r
354         }\r
355         \r
356         // Copy an individual item within the tree.  I need to do this because\r
357         // Qt doesn't call the dropMimeData on a move, just a copy.\r
358         private void copyTreeItem(QTreeWidgetItem source, QTreeWidgetItem target) {\r
359                 target.setText(0, source.text(0));\r
360                 target.setIcon(0, source.icon(0));\r
361                 target.setText(1, source.text(1));\r
362                 target.setText(2, source.text(2));\r
363                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
364                 target.setTextAlignment(1, ra.value());\r
365                 \r
366                 for (int i=0; i<source.childCount(); i++) {\r
367                         QTreeWidgetItem newChild = new QTreeWidgetItem(target);\r
368                         copyTreeItem(source.child(i), newChild);\r
369                         source.child(i).setHidden(true);\r
370                 }\r
371                 return;\r
372         }\r
373         \r
374         // Check that we don't copy a parent as a child of a current child.\r
375         private boolean checkParent(QTreeWidgetItem parent, String child) {\r
376                 if (parent != null)\r
377                         if (parent.text(2).equals(child))\r
378                                 return false;\r
379                 if (parent == null)\r
380                         return true;\r
381                 return checkParent(parent.parent(), child);\r
382         }\r
383 \r
384 \r
385         public void selectSavedSearch(QTreeWidgetItem item) {\r
386                 MatchFlags flags = new MatchFlags();\r
387                 flags.set(MatchFlag.MatchWildcard);\r
388                 flags.set(MatchFlag.MatchRecursive);\r
389                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
390                 \r
391                 for (int j=0; j<children.size(); j++) {\r
392                         String guid = children.get(j).text(2);\r
393                         if (item.text(2).equals(guid)) {\r
394                                 children.get(j).setSelected(true);\r
395                         }\r
396                 }\r
397         }\r
398 }\r