OSDN Git Service

Evenoteの関連ノート取得を別スレッドで実行するよう試作。まだまだ問題あり。
[neighbornote/NeighborNote.git] / src / cx / fbn / nevernote / gui / RensoNoteList.java
1 /*
2  * This file is part of NeighborNote
3  * Copyright 2013 Yuki Takahashi
4  * 
5  * This file may be licensed under the terms of of the
6  * GNU General Public License Version 2 (the ``GPL'').
7  *
8  * Software distributed under the License is distributed
9  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
10  * express or implied. See the GPL for the specific language
11  * governing rights and limitations.
12  *
13  * You should have received a copy of the GPL along with this
14  * program. If not, go to http://www.gnu.org/licenses/gpl.html
15  * or write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18 */
19
20 // ICHANGED
21 package cx.fbn.nevernote.gui;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Set;
28
29 import com.evernote.edam.type.Note;
30 import com.trolltech.qt.QThread;
31 import com.trolltech.qt.core.QSize;
32 import com.trolltech.qt.core.Qt.MouseButton;
33 import com.trolltech.qt.gui.QAction;
34 import com.trolltech.qt.gui.QApplication;
35 import com.trolltech.qt.gui.QContextMenuEvent;
36 import com.trolltech.qt.gui.QListWidget;
37 import com.trolltech.qt.gui.QListWidgetItem;
38 import com.trolltech.qt.gui.QMenu;
39
40 import cx.fbn.nevernote.Global;
41 import cx.fbn.nevernote.NeverNote;
42 import cx.fbn.nevernote.sql.DatabaseConnection;
43 import cx.fbn.nevernote.threads.ENRelatedNotesRunner;
44 import cx.fbn.nevernote.threads.SyncRunner;
45 import cx.fbn.nevernote.utilities.ApplicationLogger;
46
47 public class RensoNoteList extends QListWidget {
48         private final DatabaseConnection conn;
49         private final ApplicationLogger logger;
50         private final HashMap<QListWidgetItem, String> rensoNoteListItems;
51         private final List<RensoNoteListItem> rensoNoteListTrueItems;
52         private String rensoNotePressedItemGuid;
53         private final QAction openNewTabAction;
54         private final QAction starAction;
55         private final QAction unstarAction;
56         private final QAction excludeNoteAction;
57         private final NeverNote parent;
58         private final QMenu menu;
59         private final SyncRunner syncRunner;
60         private final ENRelatedNotesRunner ENRelatedNotesRunner;
61         private final QThread ENRelatedNotesThread;
62         private int allPointSum;
63
64         public RensoNoteList(DatabaseConnection c, NeverNote p, SyncRunner syncRunner) {
65                 logger = new ApplicationLogger("rensoNoteList.log");
66                 logger.log(logger.HIGH, "Setting up rensoNoteList");
67                 allPointSum = 0;
68
69                 this.conn = c;
70                 this.parent = p;
71                 this.syncRunner = syncRunner;
72                 
73                 this.ENRelatedNotesRunner = new ENRelatedNotesRunner(this.syncRunner);
74                 this.ENRelatedNotesRunner.ENRelatedNotesSignal.getENRelatedNotesFinished.connect(this, "ENRelatedNotesComplete()");
75                 this.ENRelatedNotesThread = new QThread(ENRelatedNotesRunner, "ENRelatedNotes Thread");
76                 this.ENRelatedNotesThread.start();
77                 
78                 rensoNoteListItems = new HashMap<QListWidgetItem, String>();
79                 rensoNoteListTrueItems = new ArrayList<RensoNoteListItem>();
80                 
81                 this.itemPressed.connect(this, "rensoNoteItemPressed(QListWidgetItem)");
82                 
83                 // コンテキストメニュー作成
84                 menu = new QMenu(this);
85                 // 新しいタブで開くアクション生成
86                 openNewTabAction = new QAction(tr("Open in New Tab"), this);
87                 openNewTabAction.setToolTip(tr("Open this note in new tab"));
88                 openNewTabAction.triggered.connect(parent, "openNewTabFromRNL()");
89                 // スターをつけるアクション生成
90                 starAction = new QAction(tr("Add Star"), this);
91                 starAction.setToolTip(tr("Add Star to this item"));
92                 starAction.triggered.connect(parent, "starNote()");
93                 // スターを外すアクション生成
94                 unstarAction = new QAction(tr("Remove Star"), this);
95                 unstarAction.setToolTip(tr("Remove Star from this item"));
96                 unstarAction.triggered.connect(parent, "unstarNote()");
97                 // このノートを除外するアクション生成
98                 excludeNoteAction = new QAction(tr("Exclude"), this);
99                 excludeNoteAction.setToolTip(tr("Exclude this note from RensoNoteList"));
100                 excludeNoteAction.triggered.connect(parent, "excludeNote()");
101                 // コンテキストメニューに登録
102                 menu.addAction(openNewTabAction);
103                 menu.addAction(excludeNoteAction);
104                 menu.aboutToHide.connect(this, "contextMenuHidden()");
105                 
106                 logger.log(logger.HIGH, "rensoNoteList setup complete");
107         }
108
109         public void refreshRensoNoteList(String guid) {
110                 logger.log(logger.HIGH, "Entering RensoNoteList.refreshRensoNoteList");
111
112                 this.clear();
113                 rensoNoteListItems.clear();
114                 rensoNoteListTrueItems.clear();
115
116                 if (!this.isEnabled()) {
117                         return;
118                 }
119                 if (guid == null || guid.equals("")) {
120                         return;
121                 }
122
123                 HashMap<String, Integer> mergedHistory = new HashMap<String, Integer>();
124                 
125                 // Evernoteの関連ノートを別スレッドで取得
126                 ENRelatedNotesRunner.addGuid(guid);
127 //              ENRelatedNotesRunner.setGuid(guid);
128 //              ENRelatedNotesThread.finished.connect(this, "ENRelatedNotesComplete()");
129                 
130 //              RelatedResult result = getENRelatedNotes(guid);
131 //              List<Note> relatedNotes = new ArrayList<Note>();
132 //              if (result != null) {
133 //                      relatedNotes = result.getNotes();
134 //              }
135 //              if (relatedNotes != null && !relatedNotes.isEmpty()) {
136 //                      HashMap<String, Integer> ENRelatedNotes = new HashMap<String, Integer>();
137 //                      
138 //                      for (Note relatedNote : relatedNotes) {
139 //                              String relatedGuid = relatedNote.getGuid();
140 //                              ENRelatedNotes.put(relatedGuid, 1);
141 //                      }
142 //                      addWeight(ENRelatedNotes, 10);
143 //                      mergedHistory = mergeHistory(ENRelatedNotes, mergedHistory);
144 //              }
145                 
146                 // browseHistory<guid, 回数(ポイント)>
147                 HashMap<String, Integer> browseHistory = conn.getHistoryTable().getBehaviorHistory("browse", guid);
148                 addWeight(browseHistory, Global.getBrowseWeight());
149                 mergedHistory = mergeHistory(browseHistory, new HashMap<String, Integer>());
150                 
151                 // copy&pasteHistory<guid, 回数(ポイント)>
152                 HashMap<String, Integer> copyAndPasteHistory = conn.getHistoryTable().getBehaviorHistory("copy & paste", guid);
153                 addWeight(copyAndPasteHistory, Global.getCopyPasteWeight());
154                 mergedHistory = mergeHistory(copyAndPasteHistory, mergedHistory);
155                 
156                 // addNewNoteHistory<guid, 回数(ポイント)>
157                 HashMap<String, Integer> addNewNoteHistory = conn.getHistoryTable().getBehaviorHistory("addNewNote", guid);
158                 addWeight(addNewNoteHistory, Global.getAddNewNoteWeight());
159                 mergedHistory = mergeHistory(addNewNoteHistory, mergedHistory);
160                 
161                 // rensoItemClickHistory<guid, 回数(ポイント)>
162                 HashMap<String, Integer> rensoItemClickHistory = conn.getHistoryTable().getBehaviorHistory("rensoItemClick", guid);
163                 addWeight(rensoItemClickHistory, Global.getRensoItemClickWeight());
164                 mergedHistory = mergeHistory(rensoItemClickHistory, mergedHistory);
165                 
166                 // sameTagHistory<guid, 回数(ポイント)>
167                 HashMap<String, Integer> sameTagHistory = conn.getHistoryTable().getBehaviorHistory("sameTag", guid);
168                 addWeight(sameTagHistory, Global.getSameTagWeight());
169                 mergedHistory = mergeHistory(sameTagHistory, mergedHistory);
170                 
171                 // sameNotebookNoteHistory<guid, 回数(ポイント)>
172                 HashMap<String, Integer> sameNotebookHistory = conn.getHistoryTable().getBehaviorHistory("sameNotebook", guid);
173                 addWeight(sameNotebookHistory, Global.getSameNotebookWeight());
174                 mergedHistory = mergeHistory(sameNotebookHistory, mergedHistory);
175                 
176                 // すべての関連ポイントの合計を取得(関連度のパーセント算出に利用)
177                 allPointSum = 0;
178                 for (int p : mergedHistory.values()) {
179                         allPointSum += p;
180                 }
181                 
182                 addRensoNoteList(mergedHistory);
183
184                 logger.log(logger.HIGH, "Leaving RensoNoteList.refreshRensoNoteList");
185         }
186         
187 //      private RelatedResult getENRelatedNotes(String guid) {
188 //              RelatedQuery rquery = new RelatedQuery();
189 //              rquery.setNoteGuid(guid);
190 //              RelatedResultSpec resultSpec = new RelatedResultSpec();
191 //              resultSpec.setMaxNotes(Constants.EDAM_RELATED_MAX_NOTES);
192 //              if (syncRunner != null && syncRunner.localNoteStore != null) {
193 //                      try {
194 //                              RelatedResult result = syncRunner.localNoteStore.findRelated(syncRunner.authToken, rquery, resultSpec);
195 //                              return result;
196 //                      } catch (EDAMUserException e) {
197 //                              // TODO 自動生成された catch ブロック
198 //                              e.printStackTrace();
199 //                      } catch (EDAMSystemException e) {
200 //                              // TODO 自動生成された catch ブロック
201 //                              e.printStackTrace();
202 //                      } catch (EDAMNotFoundException e) {
203 //                              // TODO 自動生成された catch ブロック
204 //                              e.printStackTrace();
205 //                      } catch (TException e) {
206 //                              // TODO 自動生成された catch ブロック
207 //                              e.printStackTrace();
208 //                      }
209 //              }
210 //              return null;
211 //      }
212
213         // 操作回数に重み付けする
214         private void addWeight(HashMap<String, Integer> history, int weight){
215                 Set<String> keySet = history.keySet();
216                 Iterator<String> hist_iterator = keySet.iterator();
217                 while(hist_iterator.hasNext()){
218                         String key = hist_iterator.next();
219                         history.put(key, history.get(key) * weight);
220                 }
221         }
222         
223         // 引数1と引数2をマージしたハッシュマップを返す
224         private HashMap<String, Integer> mergeHistory(HashMap<String, Integer> History1, HashMap<String, Integer> History2){
225                 HashMap<String, Integer> mergedHistory = new HashMap<String, Integer>();
226                 
227                 mergedHistory.putAll(History1);
228                 
229                 Set<String> keySet = History2.keySet();
230                 Iterator<String> hist2_iterator = keySet.iterator();
231                 while(hist2_iterator.hasNext()){
232                         String key = hist2_iterator.next();
233                         if(mergedHistory.containsKey(key)){
234                                 mergedHistory.put(key, mergedHistory.get(key) + History2.get(key));
235                         }else {
236                                 mergedHistory.put(key, History2.get(key));
237                         }
238                 }
239
240                 return mergedHistory;
241         }
242         
243         private void addRensoNoteList(HashMap<String, Integer> History){
244                 String currentNoteGuid = new String(parent.getCurrentNoteGuid());
245                 
246                 // スター付きノートとスター無しノートを分ける
247                 HashMap<String, Integer> staredNotes = new HashMap<String, Integer>();  // スター付きノートのマップ
248                 HashMap<String, Integer> normalNotes = new HashMap<String, Integer>();  // スター無しノートのマップ
249                 for (String nextGuid: History.keySet()) {
250                         int relationPoint = History.get(nextGuid);
251                         boolean isStared = conn.getStaredTable().existNote(currentNoteGuid, nextGuid);
252                         if (isStared) {
253                                 staredNotes.put(nextGuid, relationPoint);
254                         } else {
255                                 normalNotes.put(nextGuid, relationPoint);
256                         }
257                 }
258                 
259                 // 連想ノートリストアイテムの最大表示数まで繰り返す
260                 for (int i = 0; i < Global.getRensoListItemMaximum(); i++) {
261                         // スター付きノートがあれば先に処理する
262                         HashMap<String, Integer> tmpMap = new HashMap<String, Integer>();
263                         if (!staredNotes.isEmpty()) {
264                                 tmpMap = staredNotes;
265                         }else if (!normalNotes.isEmpty()) {
266                                 tmpMap = normalNotes;
267                         }
268                         
269                         // 操作回数が多い順に取り出して連想ノートリストに追加する
270                         if (!tmpMap.isEmpty()) {
271                                 int maxNum = -1;
272                                 String maxGuid = new String();
273                                 
274                                 for (String nextGuid: tmpMap.keySet()) {
275                                         int relationPoint = tmpMap.get(nextGuid);
276                                         
277                                         // 最大ノート探索する
278                                         if (relationPoint > maxNum) {
279                                                 maxNum = relationPoint;
280                                                 maxGuid = nextGuid;
281                                         }
282                                 }
283                                 
284                                 // 次の最大値探索で邪魔なので最大値をHashMapから削除
285                                 tmpMap.remove(maxGuid);
286         
287                                 // 関連度最大のノートがアクティブか確認
288                                 Note maxNote = conn.getNoteTable().getNote(maxGuid, true, false, false, false, true);
289                                 boolean isNoteActive = false;
290                                 if(maxNote != null) {
291                                         isNoteActive = maxNote.isActive();
292                                 }
293                                 
294                                 // 存在していて、かつ関連度0でなければノート情報を取得して連想ノートリストに追加
295                                 if (isNoteActive && maxNum > 0) {
296                                         // スター付きか確認
297                                         boolean isStared;
298                                         isStared = conn.getStaredTable().existNote(currentNoteGuid, maxGuid);
299                                         
300                                         QListWidgetItem item = new QListWidgetItem();
301                                         RensoNoteListItem myItem = new RensoNoteListItem(maxNote, maxNum, isStared, allPointSum, conn, this);
302                                         item.setSizeHint(new QSize(0, 90));
303                                         this.addItem(item);
304                                         this.setItemWidget(item, myItem);
305                                         rensoNoteListItems.put(item, maxGuid);
306                                         rensoNoteListTrueItems.add(myItem);
307                                 } else {
308                                         break;
309                                 }
310                         }
311                 }
312         }
313
314         // リストのアイテムから対象ノートのguidを取得
315         public String getNoteGuid(QListWidgetItem item) {
316                 return rensoNoteListItems.get(item);
317         }
318         
319         // 関連ノートリストの右クリックメニュー
320         @Override
321         public void contextMenuEvent(QContextMenuEvent event){
322                 if (rensoNotePressedItemGuid == null || rensoNotePressedItemGuid.equals("")) {
323                         return;
324                 }
325                 
326                 // STAR, UNSTARがあれば、一度消す
327                 List<QAction> menuActions = new ArrayList<QAction>(menu.actions());
328                 if (menuActions.contains(starAction)) {
329                         menu.removeAction(starAction);
330                 }
331                 if (menuActions.contains(unstarAction)) {
332                         menu.removeAction(unstarAction);
333                 }
334                 
335                 // 対象アイテムがスター付きなら「UNSTAR」、スター無しなら「STAR」を追加
336                 String currentNoteGuid = parent.getCurrentNoteGuid();
337                 boolean isExist = conn.getStaredTable().existNote(currentNoteGuid, rensoNotePressedItemGuid);
338                 if (isExist) {
339                         menu.insertAction(excludeNoteAction, unstarAction);
340                 } else {
341                         menu.insertAction(excludeNoteAction, starAction);
342                 }
343                 
344                 // コンテキストメニューを表示
345                 menu.exec(event.globalPos());
346                 
347                 rensoNotePressedItemGuid = null;
348         }
349         
350         // コンテキストメニューが表示されているかどうか
351         public boolean isContextMenuVisible() {
352                 return menu.isVisible();
353         }
354         
355         // コンテキストメニューが閉じられた時
356         @SuppressWarnings("unused")
357         private void contextMenuHidden() {
358                 for (int i = 0; i < rensoNoteListTrueItems.size(); i++) {
359                         RensoNoteListItem item = rensoNoteListTrueItems.get(i);
360                         item.setDefaultBackground();
361                 }
362         }
363         
364         // ユーザが連想ノートリストのアイテムを選択した時の処理
365         @SuppressWarnings("unused")
366         private void rensoNoteItemPressed(QListWidgetItem current) {
367                 rensoNotePressedItemGuid = null;
368                 // 右クリックだったときの処理
369                 if (QApplication.mouseButtons().isSet(MouseButton.RightButton)) {
370                         rensoNotePressedItemGuid = getNoteGuid(current);
371                 }
372         }
373         
374         // Evernoteの関連ノートの取得が完了
375         @SuppressWarnings("unused")
376         private void ENRelatedNotesComplete() {
377 //              List<String> relatedNoteGuids = ENRelatedNotesRunner.getRelatedGuids();
378                 List<String> relatedNoteGuids = ENRelatedNotesRunner.getENRelatedNoteGuids();
379                 System.out.println(relatedNoteGuids);
380         }
381         
382         // Evernoteの関連ノート取得スレッドを終了させる
383         public void stopThread() {
384                 ENRelatedNotesRunner.setKeepRunning(false);
385                 // TODO keepRunningをfalseにしても、guidQueueが次のキューが来るまで待機するので終了できない。guidQueueに終了命令を処理できる仕組みを要追加。
386         }
387 }