OSDN Git Service

f3402f27d26087b73b735263245e129f7ad0c394
[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.QMouseEvent;\r
46 import com.trolltech.qt.gui.QTreeWidget;\r
47 import com.trolltech.qt.gui.QTreeWidgetItem;\r
48 \r
49 import cx.fbn.nevernote.Global;\r
50 import cx.fbn.nevernote.filters.TagCounter;\r
51 import cx.fbn.nevernote.signals.NoteSignal;\r
52 import cx.fbn.nevernote.signals.TagSignal;\r
53 import cx.fbn.nevernote.sql.DatabaseConnection;\r
54 \r
55 public class TagTreeWidget extends QTreeWidget {\r
56         private QAction editAction;\r
57         private QAction deleteAction;\r
58         private QAction addAction;\r
59         private QAction iconAction;\r
60         public TagSignal tagSignal;\r
61         public NoteSignal noteSignal;\r
62         private boolean showAllTags;\r
63         private final DatabaseConnection db;\r
64         private HashMap<String, QIcon>  icons;\r
65         public Signal0 selectionSignal;\r
66         public String selectedTag;\r
67         private boolean rightButtonClicked;\r
68         \r
69         \r
70         public TagTreeWidget(DatabaseConnection d) {\r
71                 List<String> headers = new ArrayList<String>();\r
72                 headers.add(tr("Tags"));\r
73                 headers.add("");\r
74                 showAllTags = true;\r
75                 setAcceptDrops(true);\r
76                 setDragEnabled(true);\r
77                 setColumnCount(2);\r
78                 header().setResizeMode(0, QHeaderView.ResizeMode.ResizeToContents);\r
79                 header().setResizeMode(1, QHeaderView.ResizeMode.Stretch);\r
80                 header().setMovable(false);\r
81                 db = d;\r
82                 selectionSignal = new Signal0();\r
83                 tagSignal = new TagSignal();\r
84                 noteSignal = new NoteSignal();\r
85                 setDragDropMode(QAbstractItemView.DragDropMode.DragDrop);\r
86         setHeaderLabels(headers);\r
87 //      setSelectionMode(QAbstractItemView.SelectionMode.MultiSelection);\r
88         setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection);\r
89         \r
90         selectedTag = "";\r
91         itemClicked.connect(this, "itemClicked()");\r
92                 int width = Global.getColumnWidth("tagTreeName");\r
93                 if (width>0)\r
94                         setColumnWidth(0, width);\r
95 \r
96         }\r
97         \r
98         public void setEditAction(QAction e) {\r
99                 editAction = e;\r
100         }\r
101         public void setDeleteAction(QAction d) {\r
102                 deleteAction = d;\r
103         }\r
104         public void setAddAction(QAction a) {\r
105                 addAction = a;\r
106         }\r
107         public void setIconAction(QAction i) {\r
108                 iconAction = i;\r
109         }\r
110         \r
111         // Insert a new tag into the tree.  This is used when we dynamically add a \r
112         // new tag after the full tag tree has been built.  It only adds to the\r
113         // top level.\r
114         public void insertTag(String name, String guid) {\r
115         String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
116                 QIcon icon = new QIcon(iconPath+"tag.png");\r
117                 QTreeWidgetItem child;\r
118                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
119                 \r
120                 // Build new tag & add it\r
121                 child = new QTreeWidgetItem();\r
122                 child.setText(0, name);\r
123                 child.setIcon(0,icon);\r
124                 child.setText(2, guid);\r
125                 child.setTextAlignment(1, ra.value());\r
126                 addTopLevelItem(child);\r
127                 \r
128                 // Resort the list\r
129                 resizeColumnToContents(0);\r
130         resizeColumnToContents(1);\r
131         sortItems(0, SortOrder.AscendingOrder);\r
132         }\r
133         \r
134         public void load(List<Tag> tags) {\r
135         Tag tag;\r
136         List<QTreeWidgetItem> index = new ArrayList<QTreeWidgetItem>();\r
137         QTreeWidgetItem child;\r
138                         \r
139         //Clear out the tree & reload\r
140         clear();\r
141         String iconPath = new String("classpath:cx/fbn/nevernote/icons/");\r
142                 QIcon icon = new QIcon(iconPath+"tag.png");\r
143         \r
144                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
145         \r
146                 // Create a copy.  We delete them out as they are found\r
147                 List<Tag> tempList = new ArrayList<Tag>();\r
148                 for (int i=0; i<tags.size(); i++) {\r
149                         tempList.add(tags.get(i));\r
150                 }\r
151                 \r
152         while (tempList.size() > 0) {\r
153                 for (int i=0; i<tempList.size(); i++) {\r
154                         tag = tempList.get(i);\r
155                         if (tag.getParentGuid()==null || tag.getParentGuid().equals("")) {\r
156                                 child = new QTreeWidgetItem();\r
157                                 child.setText(0, tag.getName());\r
158                                 if (icons != null && !icons.containsKey(tag.getGuid())) {\r
159                                         child.setIcon(0, icon);\r
160                                 } else {\r
161                                         child.setIcon(0, icons.get(tag.getGuid()));\r
162                                 }\r
163 \r
164                                 child.setText(2, tag.getGuid());\r
165                                 child.setTextAlignment(1, ra.value());\r
166                                 index.add(child);\r
167                                 addTopLevelItem(child);\r
168                                 tempList.remove(i);\r
169                         } else {\r
170                                 // We need to find the parent\r
171                                 for (int j=0; j<index.size(); j++) {\r
172                                         if (index.get(j).text(2).equals(tag.getParentGuid())) {\r
173                                         child = new QTreeWidgetItem();\r
174                                         child.setText(0, tag.getName());\r
175                                         child.setIcon(0, icon);\r
176                                         child.setText(2, tag.getGuid());\r
177                                         child.setTextAlignment(1, ra.value());\r
178                                         tempList.remove(i);\r
179                                         index.add(child);                                               \r
180                                         index.get(j).addChild(child);\r
181                                         }\r
182                                 }\r
183                         }\r
184                 } \r
185         }\r
186         resizeColumnToContents(0);\r
187         resizeColumnToContents(1);\r
188         sortItems(0, SortOrder.AscendingOrder);\r
189         }\r
190         // Show (unhide) all tags\r
191         public void showAllTags(boolean value) {\r
192                 showAllTags = value;\r
193         }\r
194         public void unhideAllTags() {\r
195                 MatchFlags flags = new MatchFlags();\r
196                 flags.set(MatchFlag.MatchWildcard);\r
197                 flags.set(MatchFlag.MatchRecursive);\r
198                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
199                 for (int i=0; i<children.size(); i++) {\r
200                         children.get(i).setHidden(false);\r
201                 }\r
202         }\r
203         // update the display with the current number of notes\r
204         public void updateCounts(List<TagCounter> counts) {\r
205                                 \r
206                 MatchFlags flags = new MatchFlags();\r
207                 flags.set(MatchFlag.MatchWildcard);\r
208                 flags.set(MatchFlag.MatchRecursive);\r
209 //              List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();\r
210                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
211                 \r
212                 QBrush black = new QBrush();\r
213                 black.setColor(QColor.black);\r
214                 QBrush blue = new QBrush();\r
215                 blue.setColor(QColor.blue);\r
216                 if (!Global.tagBehavior().equalsIgnoreCase("ColorActive"))\r
217                         blue.setColor(QColor.black);\r
218                 \r
219                 for (int i=0; i<children.size(); i++) {\r
220                         children.get(i).setText(1,"0");\r
221                         children.get(i).setForeground(0, black);                        \r
222                         children.get(i).setForeground(1, black);\r
223                         if (!showAllTags && (Global.tagBehavior().equalsIgnoreCase("HideInactiveCount") || Global.tagBehavior().equalsIgnoreCase("NoHideInactiveCount")))\r
224                                 children.get(i).setHidden(true);\r
225                         else\r
226                                 children.get(i).setHidden(false);\r
227                         if (children.get(i).isSelected())\r
228                                 children.get(i).setHidden(false);\r
229                 }\r
230                 for (int i=0; i<counts.size(); i++) {\r
231                         for (int j=0; j<children.size(); j++) {\r
232                                 String guid = children.get(j).text(2);\r
233                                 if (counts.get(i).getGuid().equals(guid)) {\r
234                                         children.get(j).setText(1, new Integer(counts.get(i).getCount()).toString());\r
235                                         if (counts.get(i).getCount() > 0 || children.get(j).isSelected()) {\r
236                                                 children.get(j).setForeground(0, blue);                 \r
237                                                 children.get(j).setForeground(1, blue);\r
238                                                 QTreeWidgetItem parent = children.get(j);\r
239                                                 while (parent != null) {\r
240                                                         parent.setForeground(0, blue);                  \r
241                                                         parent.setForeground(1, blue);\r
242                                                         parent.setHidden(false);\r
243                                                         parent = parent.parent();\r
244                                                 }\r
245                                         }\r
246                                 }\r
247                         }\r
248                 }\r
249         }\r
250 \r
251         \r
252         public boolean selectGuid(String guid) {\r
253                 MatchFlags flags = new MatchFlags();\r
254                 flags.set(MatchFlag.MatchWildcard);\r
255                 flags.set(MatchFlag.MatchRecursive);\r
256 //              List<QTreeWidgetItem> children = new ArrayList<QTreeWidgetItem>();\r
257                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
258 \r
259                 for (int i=0; i<children.size(); i++) {\r
260                         if (children.get(i).text(2).equals(guid)) {\r
261                                 children.get(i).setSelected(true);\r
262                                 return true;\r
263                         }\r
264                 }\r
265                 return false;\r
266         }\r
267         \r
268          @Override\r
269          protected void dragMoveEvent(QDragMoveEvent event) {\r
270                 if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
271                         if (event.answerRect().intersects(childrenRect()))\r
272                                 event.acceptProposedAction();\r
273                         return;\r
274                 }\r
275          }\r
276 \r
277         \r
278         @Override\r
279         public void dragEnterEvent(QDragEnterEvent event) {\r
280                 if (event.mimeData().hasFormat("application/x-nevernote-note")) {\r
281                         event.accept();\r
282                         return;\r
283                 }\r
284                 if (event.source() == this) {\r
285                         if (Global.tagBehavior().equals("HideInactiveCount")) {\r
286                                 event.ignore();\r
287                                 return;\r
288                         }\r
289                         event.mimeData().setData("application/x-nevernote-tag", new QByteArray(currentItem().text(2)));\r
290                         event.accept();\r
291                         return;\r
292                 }\r
293                 event.ignore();\r
294         }\r
295 \r
296         @Override\r
297         public boolean dropMimeData(QTreeWidgetItem parent, int index, QMimeData data, Qt.DropAction action) {\r
298                 if (data.hasFormat("application/x-nevernote-tag")) {\r
299                         QByteArray d = data.data("application/x-nevernote-tag");\r
300                         String current = d.toString();\r
301                         \r
302                         // Check we don't do a dumb thing like move a parent to a child of itself\r
303                         if (!checkParent(parent, current))\r
304                                 return false;\r
305                         QTreeWidgetItem newChild;\r
306                         if (parent == null) {\r
307 //                              tagSignal.changeParent.emit(current, "");\r
308                                 db.getTagTable().updateTagParent(current, "");\r
309                                 newChild = new QTreeWidgetItem(this);\r
310                         } else {\r
311 //                              tagSignal.changeParent.emit(current, parent.text(2));\r
312                                 db.getTagTable().updateTagParent(current, parent.text(2));\r
313                                 newChild = new QTreeWidgetItem(parent);\r
314                         }\r
315                         copyTreeItem(currentItem(), newChild);\r
316                         currentItem().setHidden(true);\r
317                         sortItems(0, SortOrder.AscendingOrder);\r
318                         return true;\r
319                 }\r
320                 \r
321                 // If we are dropping a note\r
322                 if (data.hasFormat("application/x-nevernote-note")) {\r
323                         String notebookGuid = db.getTagTable().getNotebookGuid(parent.text(2));\r
324                         QByteArray d = data.data("application/x-nevernote-note");\r
325                         String s = d.toString();\r
326                         String noteGuidArray[] = s.split(" ");\r
327                         for (String element : noteGuidArray) {\r
328                                 Note n = db.getNoteTable().getNote(element.trim(), false, false, false, false, false);\r
329                                 \r
330                                 // Check that...\r
331                                 // 1.) Check that tag isn't already assigned to that note\r
332                                 // 2.) Check that that tag is valid for that notebook or the tag isn't notebook specific\r
333                                 // 3.) Check that the notebook isn't read only.\r
334                                 if (!db.getNoteTable().noteTagsTable.checkNoteNoteTags(element.trim(), parent.text(2)) &&\r
335                                                 (notebookGuid == null || n.getNotebookGuid().equalsIgnoreCase(notebookGuid) || notebookGuid.equals("")) &&\r
336                                                 !db.getNotebookTable().isReadOnly(n.getNotebookGuid())) {\r
337                                         db.getNoteTable().noteTagsTable.saveNoteTag(element.trim(), parent.text(2));\r
338                                         noteSignal.tagsAdded.emit(element.trim(), parent.text(2));\r
339                                 }\r
340                         }\r
341                         //tagSignal.listChanged.emit();\r
342                         \r
343                         return true;\r
344                 }\r
345                 return false;\r
346         }\r
347         \r
348         @Override\r
349         public void contextMenuEvent(QContextMenuEvent event) {\r
350                 QMenu menu = new QMenu(this);\r
351                 menu.addAction(addAction);\r
352                 menu.addAction(editAction);\r
353                 menu.addAction(deleteAction);\r
354                 menu.addSeparator();\r
355                 menu.addAction(iconAction);\r
356                 menu.exec(event.globalPos());\r
357         }\r
358         \r
359         public void setIcons(HashMap<String, QIcon> i) {\r
360                 icons = i;\r
361         }\r
362         \r
363         // Copy an individual item within the tree.  I need to do this because\r
364         // Qt doesn't call the dropMimeData on a move, just a copy.\r
365         private void copyTreeItem(QTreeWidgetItem source, QTreeWidgetItem target) {\r
366                 target.setText(0, source.text(0));\r
367                 target.setIcon(0, source.icon(0));\r
368                 target.setText(1, source.text(1));\r
369                 target.setText(2, source.text(2));\r
370                 Qt.Alignment ra = new Qt.Alignment(Qt.AlignmentFlag.AlignRight);\r
371                 target.setTextAlignment(1, ra.value());\r
372                 \r
373                 for (int i=0; i<source.childCount(); i++) {\r
374                         QTreeWidgetItem newChild = new QTreeWidgetItem(target);\r
375                         copyTreeItem(source.child(i), newChild);\r
376                         source.child(i).setHidden(true);\r
377                 }\r
378                 return;\r
379         }\r
380         \r
381         // Check that we don't copy a parent as a child of a current child.\r
382         private boolean checkParent(QTreeWidgetItem parent, String child) {\r
383                 if (parent != null)\r
384                         if (parent.text(2).equals(child))\r
385                                 return false;\r
386                 if (parent == null)\r
387                         return true;\r
388                 return checkParent(parent.parent(), child);\r
389         }\r
390 \r
391 \r
392         public void selectSavedSearch(QTreeWidgetItem item) {\r
393                 MatchFlags flags = new MatchFlags();\r
394                 flags.set(MatchFlag.MatchWildcard);\r
395                 flags.set(MatchFlag.MatchRecursive);\r
396                 List <QTreeWidgetItem>  children = findItems("*", flags);\r
397                 \r
398                 for (int j=0; j<children.size(); j++) {\r
399                         String guid = children.get(j).text(2);\r
400                         if (item.text(2).equals(guid)) {\r
401                                 children.get(j).setSelected(true);\r
402                         }\r
403                 }\r
404         }\r
405 \r
406         @SuppressWarnings("unused")\r
407         private void itemClicked() {\r
408                 \r
409                 List<QTreeWidgetItem> selectedItem = selectedItems();\r
410                 if (selectedItem.size() == 1) {\r
411                         if (selectedItem.get(0).text(0).equalsIgnoreCase(selectedTag) && !rightButtonClicked) {\r
412                                 selectedTag = "";\r
413                                 clearSelection();\r
414                         } else {\r
415                                 selectedTag = selectedItem.get(0).text(0);\r
416                         }\r
417                         \r
418                 }\r
419                 selectionSignal.emit();\r
420         }\r
421 \r
422         \r
423         @Override\r
424         public void mousePressEvent(QMouseEvent e) {\r
425                 if (e.button() == Qt.MouseButton.RightButton)\r
426                         rightButtonClicked = true;\r
427                 else\r
428                         rightButtonClicked = false;\r
429                 super.mousePressEvent(e);\r
430         }\r
431 }\r