OSDN Git Service

ユーザの詳細情報表示内の画像表示にキャッシュを使うように改良。これにより処理速度が向上 refs #24966
[nt-manager/nt-manager.git] / src / twitter / gui / action / TweetMainAction.java
1 package twitter.gui.action;
2
3 import java.awt.Color;
4 import java.awt.Desktop;
5 import java.awt.Dimension;
6 import java.awt.Font;
7 import java.awt.Point;
8 import java.awt.TrayIcon;
9 import java.awt.event.ActionEvent;
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.FileOutputStream;
14 import java.io.IOException;
15 import java.io.UnsupportedEncodingException;
16 import java.net.URI;
17 import java.net.URL;
18 import java.net.URLEncoder;
19 import java.text.DateFormat;
20 import java.util.ArrayList;
21 import java.util.Comparator;
22 import java.util.List;
23 import java.util.Properties;
24 import java.util.Set;
25 import java.util.TreeSet;
26 import java.util.logging.Level;
27 import java.util.logging.Logger;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import javax.swing.ImageIcon;
31 import javax.swing.JCheckBoxMenuItem;
32
33 import javax.swing.JEditorPane;
34 import javax.swing.JFrame;
35 import javax.swing.JLabel;
36 import javax.swing.JOptionPane;
37 import javax.swing.JPanel;
38 import javax.swing.JTabbedPane;
39 import javax.swing.JTable;
40 import javax.swing.JTextPane;
41 import javax.swing.JToggleButton;
42 import javax.swing.table.TableModel;
43 import javax.swing.text.Style;
44 import javax.swing.text.StyleConstants;
45 import javax.swing.text.html.HTMLDocument;
46 import javax.swing.text.html.StyleSheet;
47 import twitter.action.TweetDirectMessageGetter;
48 import twitter.action.TweetFavoriteGetter;
49 import twitter.action.TweetGetter;
50 import twitter.action.TweetListGetter;
51 import twitter.action.TweetMentionGetter;
52 import twitter.action.TweetSearchResultGetter;
53 import twitter.action.TweetSendDirectMessageGetter;
54 import twitter.action.TweetTimelineGetter;
55 import twitter.action.TweetUserTimelineGetter;
56 import twitter.action.list.ListGetterSelection;
57 import twitter.action.list.UserListGetter;
58 import twitter.action.list.UserListMembershipsGetter;
59 import twitter.action.list.UserListSpecificUserListsGetter;
60 import twitter.action.list.UserListSubscriptionGetter;
61 import twitter.cache.TwitterImageCache;
62 import twitter.gui.component.DnDTabbedPane;
63
64 import twitter.gui.component.TweetTabbedTable;
65 import twitter.gui.component.TweetTableModel;
66 import twitter.gui.form.AboutDialog;
67 import twitter.gui.form.AccountDialog;
68 import twitter.gui.form.ConfigurationDialog;
69 import twitter.gui.form.DirectMessageDialog;
70 import twitter.gui.form.HashtagSearchDialog;
71 import twitter.gui.form.KeywordSearchDialog;
72 import twitter.gui.form.UserListDialog;
73 import twitter.manage.TweetConfiguration;
74 import twitter.manage.TweetManager;
75 import twitter.task.ExistTimerIDException;
76 import twitter.task.TimerID;
77 import twitter.task.TweetTaskException;
78 import twitter.task.TweetTaskManager;
79 import twitter.task.TweetUpdateTask;
80 import twitter.util.HTMLEncode;
81 import twitter4j.Status;
82 import twitter4j.TwitterException;
83
84 /**
85  * GUIのアクション部分
86  * 
87  * @author nishio
88  * 
89  */
90 public class TweetMainAction {
91
92         // 基本設定を保存するファイル名
93         public static final String BASIC_SETTING_FILENAME = TweetConfiguration.BASIC_SETTING_FILENAME;
94         // httpのパターン
95         private static final Pattern convURLLinkPtn = Pattern.compile(
96                         "(http://|https://){1}[\\w\\.\\-/:\\#\\?\\=\\&\\;\\%\\~\\+]+",
97                         Pattern.CASE_INSENSITIVE);
98         // default char encoding
99         private static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
100         // 設定ファイルを保存するディレクトリ名
101         public static final String PROPERTIES_DIRECTORY = "properties";
102         // search twitterのクエリ
103         private static final String SEARCH_QUERY = "search?q=";
104         // search twitterのURL
105         private static final String SEARCH_TWITTER_URL = "http://"
106                         + TweetConfiguration.SEARCH_TWITTER_HOSTNAME + "/";
107         // Direct Messageタブに表示する文字
108         public static final String TAB_DIRECT_MESSAGE_STRING = "Message";
109         // Mentionタブに表示する文字
110         public static final String TAB_MENTION_STRING = "Mention";
111         // タイムラインタブに表示する文字
112         public static final String TAB_TIMELINE_STRING = "Timeline";
113         // Send Direct Messageタブに表示する文字
114         public static final String TAB_SEND_DIRECT_MESSAGE_STRING = "Send";
115         // テーブルのデータ量が以下の値を超えたら古いデータから削除
116         private static final int TABLE_ELEMENT_MAX_SIZE = 200;
117         // twitterの公式URL
118         private static final String TWITTER_URL = "http://twitter.com/";
119         // 基本設定用ダイアログ
120         private ConfigurationDialog configurationDialog = null;
121         // 現在選択しているStatus情報
122         private Status currentStatus = null;
123         // reply予定のStatus
124         private Status replyStatus = null;
125         // 詳細情報パネル
126         private JPanel detailInfoPanel = null;
127         // ダイレクトメッセージ送信用ダイアログ
128         private DirectMessageDialog directMessageDialog = null;
129         // Twitter全体からキーワード検索ダイアログ
130         private KeywordSearchDialog keywordSearchDialog = null;
131         // hashtag search dialog
132         private HashtagSearchDialog hashtagSearchDialog = null;
133         // 新しく取得した部分のテーブルカラー
134         private Color newTableColor = new Color(224, 255, 255);
135         // TLのフォント名
136         private String tlFontName = "Takao Pゴシック";
137         // TLのフォントサイズ
138         private int tlFontSize = 13;
139         // 詳細情報のフォント名
140         private String detailFontName = "Takao Pゴシック";
141         // 詳細情報のフォントサイズ
142         private int detailFontSize = 13;
143         // テーブル1要素の高さ
144         private int tableElementHeight = 50;
145         // メインフレームの幅
146         private int mainFrameWidth = 729;
147         // メインフレームの高さ
148         private int mainFrameHeight = 629;
149         // MainFrame
150         private JFrame mainFrame = null;
151         // 設定
152         private Properties property = null;
153         // 現在テーブルで選択しているユーザ画像のURL
154         private URL selectedUserImageURL = null;
155         // 現在テーブルで選択しているユーザの名前
156         private String selectedUsername = null;
157         // ステータス表示ラベル
158         private JLabel statusBarLabel = null;
159         // 自分がつぶやきをかく領域
160         private JTextPane tweetBoxPane = null;
161         // 自分がつぶやきを書く領域のスクロールペーン
162         private JPanel tweetBoxRegionPane = null;
163         // tweet情報などを表示するタブ
164         private JTabbedPane tweetMainTab = null;
165         // Tweet管理
166         private TweetManager tweetManager = null;
167         // tweetを表示するTextPane
168         private JEditorPane tweetMessageBox = null;
169         // つぶやくことができる文字数を表示するラベル
170         private JLabel tweetMessageCountLabel = null;
171         private int uncheckedDirectMessageCount = 0;
172         private int uncheckedMentionTweetCount = 0;
173         // 自分宛のメッセージを通知バーに表示するか
174         private boolean isNotifyMentionMessage = true;
175         private boolean isNotifyDirectMessage = true;
176
177         // Tweetの詳細情報を表示する部分
178         private JLabel userImageLabel = null;
179         private JLabel userNameLabel = null;
180         private JLabel updateTimeLabel = null;
181         private JLabel followerLabel = null;
182         private JLabel followingLabel = null;
183         private JLabel locationLabel = null;
184         private JEditorPane clientNameLabel = null;
185         private JLabel updateLabel = null;
186         private JEditorPane userIntroBox = null;
187         private JEditorPane userWebBox = null;
188         // トレイアイコン
189         private TrayIcon trayIcon = null;
190
191         // checkbox関係
192         private javax.swing.JToggleButton timelineToggleButton;
193         private javax.swing.JToggleButton mentionToggleButton;
194         private javax.swing.JToggleButton dmToggleButton;
195         private javax.swing.JToggleButton sendDMToggleButton;
196
197         private javax.swing.JCheckBoxMenuItem timelineCheckBoxMenuItem;
198         private javax.swing.JCheckBoxMenuItem mentionCheckBoxMenuItem;
199         private javax.swing.JCheckBoxMenuItem dmCheckBoxMenuItem;
200         private javax.swing.JCheckBoxMenuItem sendCheckBoxMenuItem;
201
202         // 新しく取得したtweetでまだ参照していない数
203         private int uncheckedTimelineTweetCount = 0;
204         private AboutDialog aboutDialog = null;
205         // アカウント情報表示ダイアログ
206         private AccountDialog accountDialog;
207         // ツイートを表示するテーブル管理
208         private List<TweetTabbedTable> tweetTabbedTableList = new ArrayList<TweetTabbedTable>();
209         // ツイートテーブルの情報を一定間隔で更新するクラスを作成
210         private TweetTaskManager tweetTaskManager = new TweetTaskManager();
211
212         // リストダイアログ
213         private UserListDialog userListDialog = null;
214
215         // 情報更新間隔[sec]
216         private int getTimelinePeriod = 60;
217         private int getMentionPeriod = 60 * 3;
218         private int getDirectMessagePeriod = 60 * 15;
219         private int getSendDirectMessagePeriod = 60 * 30;
220
221         /**
222          * 
223          * @param mainFrame
224          * @param tweetManager
225          * @param statusBarLabel
226          * @param tweetTableModel
227          * @param mentionTableModel
228          * @param directMessageTableModel
229          * @param sendDirectMessageTableModel
230          * @param mainTweetTable
231          * @param mentionTable
232          * @param directMessageTable
233          * @param sendDirectMessageTable
234          * @param tweetBoxPane
235          * @param tweetBoxScrollPane
236          * @param tweetMessageCountLabel
237          * @param detailInfoPanel
238          * @param tweetMainTab
239          * @param tweetMessageBox
240          * @param userImageLabel
241          * @param userNameLabel
242          * @param updateTimeLabel
243          * @param followerLabel
244          * @param followingLabel
245          * @param locationLabel
246          * @param clientNameLabel
247          * @param updateLabel
248          * @param userIntroBox
249          * @param userWebBox
250          */
251         public TweetMainAction(JFrame mainFrame, TweetManager tweetManager,
252                         JLabel statusBarLabel, JTextPane tweetBoxPane,
253                         JPanel tweetBoxScrollPane, JLabel tweetMessageCountLabel,
254                         JPanel detailInfoPanel, JTabbedPane tweetMainTab,
255                         JEditorPane tweetMessageBox, JLabel userImageLabel,
256                         JLabel userNameLabel, JLabel updateTimeLabel, JLabel followerLabel,
257                         JLabel followingLabel, JLabel locationLabel,
258                         JEditorPane clientNameLabel, JLabel updateLabel,
259                         JEditorPane userIntroBox, JEditorPane userWebBox,
260                         JToggleButton timelineToggleButton,
261                         JToggleButton mentionToggleButton, JToggleButton dmToggleButton,
262                         JToggleButton sendToggleButton,
263                         JCheckBoxMenuItem timelineCheckBoxMenuItem,
264                         JCheckBoxMenuItem mentionCheckBoxMenuItem,
265                         JCheckBoxMenuItem dmCheckBoxMenuItem,
266                         JCheckBoxMenuItem sendCheckBoxMenuItem, TrayIcon trayIcon) {
267                 this.mainFrame = mainFrame;
268                 this.tweetManager = tweetManager;
269                 this.statusBarLabel = statusBarLabel;
270                 this.tweetBoxPane = tweetBoxPane;
271                 this.tweetMessageCountLabel = tweetMessageCountLabel;
272                 this.detailInfoPanel = detailInfoPanel;
273                 this.tweetMainTab = tweetMainTab;
274                 this.tweetMessageBox = tweetMessageBox;
275                 this.tweetBoxRegionPane = tweetBoxScrollPane;
276
277                 // 詳細情報部分
278                 this.userImageLabel = userImageLabel;
279                 this.userNameLabel = userNameLabel;
280                 this.updateTimeLabel = updateTimeLabel;
281                 this.userIntroBox = userIntroBox;
282                 this.followerLabel = followerLabel;
283                 this.followingLabel = followingLabel;
284                 this.locationLabel = locationLabel;
285                 this.userWebBox = userWebBox;
286                 this.clientNameLabel = clientNameLabel;
287                 this.updateLabel = updateLabel;
288
289                 this.timelineCheckBoxMenuItem = timelineCheckBoxMenuItem;
290                 this.timelineToggleButton = timelineToggleButton;
291                 this.mentionCheckBoxMenuItem = mentionCheckBoxMenuItem;
292                 this.mentionToggleButton = mentionToggleButton;
293                 this.dmCheckBoxMenuItem = dmCheckBoxMenuItem;
294                 this.dmToggleButton = dmToggleButton;
295                 this.sendCheckBoxMenuItem = sendCheckBoxMenuItem;
296                 this.sendDMToggleButton = sendToggleButton;
297
298                 // トレイアイコン
299                 this.trayIcon = trayIcon;
300
301                 // 罰ボタンを押した時のイベントを追加
302                 if (this.tweetMainTab instanceof DnDTabbedPane) {
303                         ((DnDTabbedPane) this.tweetMainTab).setMainAction(this);
304                 }
305
306                 // 設定ファイルの読み込み
307                 try {
308                         loadProperties();
309                 } catch (FileNotFoundException e) {
310                         e.printStackTrace();
311                 } catch (IOException e) {
312                         e.printStackTrace();
313                 }
314                 // フォント情報を反映
315                 updateFontInformationToComponent();
316
317                 // フレームの大きさを反映
318                 mainFrame.setSize(this.mainFrameWidth, this.mainFrameHeight);
319                 mainFrame.setPreferredSize(new Dimension(this.mainFrameWidth,
320                                 this.mainFrameHeight));
321         }
322
323         /**
324          * チェックボックスの状態を更新
325          */
326         public void updateCheckboxInformation() {
327                 boolean timeline = this.isExistTimelineTab();
328                 boolean mention = this.isExistMentionTab();
329                 boolean dm = this.isExistDirectMessageTab();
330                 boolean send = this.isExistSendDirectMessageTab();
331
332                 this.timelineCheckBoxMenuItem.setSelected(timeline);
333                 this.timelineToggleButton.setSelected(timeline);
334                 this.mentionCheckBoxMenuItem.setSelected(mention);
335                 this.mentionToggleButton.setSelected(mention);
336                 this.dmCheckBoxMenuItem.setSelected(dm);
337                 this.dmToggleButton.setSelected(dm);
338                 this.sendCheckBoxMenuItem.setSelected(send);
339                 this.sendDMToggleButton.setSelected(send);
340         }
341
342         /**
343          * Timeline, Mention , DM, SendDMの情報更新間隔を取得し,その情報をテーブルに反映
344          */
345         public void updatePeriodInformationToComponent() {
346                 // すべてのテーブルにフォント情報を反映
347                 for (TweetTabbedTable t : this.tweetTabbedTableList) {
348                         String timerID = t.getTimerID();
349                         if (timerID.equals(TimerID.createTimelineID())) {
350                                 // TLの周期情報更新
351                                 this.tweetTaskManager.updateTaskPeriod(timerID, this
352                                                 .getGetTimelinePeriod(), false);
353                         } else if (timerID.equals(TimerID.createMentionID())) {
354                                 // Mentionの周期情報更新
355                                 this.tweetTaskManager.updateTaskPeriod(timerID, this
356                                                 .getGetMentionPeriod(), false);
357                         } else if (timerID.equals(TimerID.createDirectMessageID())) {
358                                 // DMの周期情報更新
359                                 this.tweetTaskManager.updateTaskPeriod(timerID, this
360                                                 .getGetDirectMessagePeriod(), false);
361                         } else if (timerID.equals(TimerID.createSendDirectMessageID())) {
362                                 // SendDMの周期情報更新
363                                 this.tweetTaskManager.updateTaskPeriod(timerID, this
364                                                 .getGetSendDirectMessagePeriod(), false);
365                         }
366                 }
367         }
368
369         // フォント情報をコンポーネントに反映
370         public void updateFontInformationToComponent() {
371                 try {
372                         Font tlFont = null;
373                         if (this.tlFontName != null) {
374                                 tlFont = new Font(this.tlFontName, Font.PLAIN, this.tlFontSize);
375                         }
376                         Font detailFont = null;
377                         if (this.detailFontName != null) {
378                                 detailFont = new Font(this.detailFontName, Font.PLAIN,
379                                                 this.detailFontSize);
380                         }
381
382                         // すべてのテーブルにフォント情報を反映
383                         for (TweetTabbedTable t : this.tweetTabbedTableList) {
384                                 t.getTable().setFont(tlFont);
385                         }
386
387                         // tweetメッセージボックスのフォントはhtmlレベルで変更する必要がある
388                         this.tweetMessageBox.setFont(detailFont);
389                         // htmlフォント変更
390                         HTMLDocument doc = (HTMLDocument) this.tweetMessageBox
391                                         .getDocument();
392                         StyleSheet[] style = doc.getStyleSheet().getStyleSheets();
393                         for (int i = style.length - 1; i >= 0; i--) {
394                                 Style body = style[i].getStyle("body");
395                                 if (body != null) {
396                                         StyleConstants
397                                                         .setFontFamily(body, detailFont.getFontName());
398                                         StyleConstants.setFontSize(body, detailFont.getSize());
399                                 }
400                         }
401
402                 } catch (Exception e) {
403                         e.printStackTrace();
404                 }
405         }
406
407         /**
408          * 新しいタブを追加
409          * 
410          * @param timerID
411          *            TimerIDクラスで生成したタイマーID
412          * @param period
413          *            情報更新間隔[sec]
414          * @param tweetGetter
415          *            実行するアクション
416          * @param tabTitle
417          *            追加するタブのタイトル
418          */
419         public void actionAddTab(String timerID, int period,
420                         TweetGetter tweetGetter, String tabTitle) {
421                 // 周期的に情報を更新する
422                 if (period > 0) {
423                         try {
424                                 // テーブルを作成
425                                 final TweetTabbedTable table = new TweetTabbedTable(
426                                                 tweetGetter, tabTitle, this.tweetMainTab,
427                                                 this.tableElementHeight, this.tweetManager, this,
428                                                 newTableColor, TABLE_ELEMENT_MAX_SIZE, timerID);
429
430                                 this.tweetTaskManager.addTask(timerID, new TweetUpdateTask() {
431
432                                         @Override
433                                         public void runTask() throws TweetTaskException {
434                                                 // ツイート情報を一定間隔で更新
435                                                 table.updateTweetTable();
436                                         }
437                                 });
438                                 // 更新開始
439                                 this.tweetTaskManager.startTask(timerID, period * 1000L);
440
441                                 // タブにテーブルを追加
442                                 table.addTableToTab();
443                                 // タブリストに追加
444                                 this.tweetTabbedTableList.add(table);
445                                 // searchTable.updateTweetTable();
446                                 // フォーカスを新しいタブに移す
447                                 this.actionRequestForusToLastTab();
448                         } catch (TweetTaskException ex) {
449                                 Logger.getLogger(TweetMainAction.class.getName()).log(
450                                                 Level.SEVERE, null, ex);
451                         }
452                 }
453
454                 // フォント情報を更新
455                 this.updateFontInformationToComponent();
456                 // テーブルの高さをすべて更新
457                 this.updateTableHeight(this.getTableElementHeight());
458         }
459
460         /**
461          * mentionタブを追加する
462          * 
463          * @param period
464          *            情報更新間隔[sec]
465          */
466         public void actionAddMentionTab(int period) {
467                 TimerID timerID = TimerID.getInstance();
468                 String id = TimerID.createMentionID();
469                 try {
470                         // 既にIDが存在していたらここで例外発生
471                         timerID.addID(id);
472                         // 検索結果を表示するタブを生成
473                         if (this.isNotifyMentionMessage) {
474                                 // メッセージが到着したら通知を行う
475                                 actionAddTab(id, period, new TweetMentionGetter(tweetManager,
476                                                 this.trayIcon), TweetMainAction.TAB_MENTION_STRING);
477                         } else {
478                                 actionAddTab(id, period, new TweetMentionGetter(tweetManager),
479                                                 TweetMainAction.TAB_MENTION_STRING);
480                         }
481                 } catch (ExistTimerIDException ex) {
482                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
483                                         JOptionPane.ERROR_MESSAGE);
484                 }
485         }
486
487         /**
488          * お気に入りタブを追加
489          * 
490          * @param screenName
491          *            nullで自分自身を取得,指定するとscreenNameのFav取得
492          */
493         public void actionAddFavoriteTab(String screenName) {
494                 TimerID timerID = TimerID.getInstance();
495                 String id = TimerID.createFavoriteID(screenName);
496                 try {
497                         // 既にIDが存在していたらここで例外発生
498                         timerID.addID(id);
499                         String favTitle;
500                         if (screenName == null) {
501                                 favTitle = "お気に入り";
502                         } else {
503                                 favTitle = screenName + "のお気に入り";
504                         }
505                         // 検索結果を表示するタブを生成
506                         actionAddTab(id, Integer.MAX_VALUE, new TweetFavoriteGetter(
507                                         tweetManager, screenName), favTitle);
508                 } catch (ExistTimerIDException ex) {
509                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
510                                         JOptionPane.ERROR_MESSAGE);
511                 }
512         }
513
514         /**
515          * timelineタブを追加する
516          * 
517          * @param period
518          *            [sec]
519          */
520         public void actionAddTimelineTab(int period) {
521                 TimerID timerID = TimerID.getInstance();
522                 String id = TimerID.createTimelineID();
523                 try {
524                         // 既にIDが存在していたらここで例外発生
525                         timerID.addID(id);
526                         // 検索結果を表示するタブを生成
527                         actionAddTab(id, period, new TweetTimelineGetter(tweetManager),
528                                         TweetMainAction.TAB_TIMELINE_STRING);
529                 } catch (ExistTimerIDException ex) {
530                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
531                                         JOptionPane.ERROR_MESSAGE);
532                 }
533         }
534
535         /**
536          * ダイレクトメッセージタブを追加する
537          * 
538          * @param period
539          *            更新間隔[sec]
540          */
541         public void actionAddDirectMessageTab(int period) {
542                 TimerID timerID = TimerID.getInstance();
543                 String id = TimerID.createDirectMessageID();
544                 try {
545                         // 既にIDが存在していたらここで例外発生
546                         timerID.addID(id);
547                         // 検索結果を表示するタブを生成
548                         if (this.isNotifyDirectMessage == true) {
549                                 actionAddTab(id, period, new TweetDirectMessageGetter(
550                                                 tweetManager, this.trayIcon),
551                                                 TweetMainAction.TAB_DIRECT_MESSAGE_STRING);
552                         } else {
553                                 actionAddTab(id, period, new TweetDirectMessageGetter(
554                                                 tweetManager),
555                                                 TweetMainAction.TAB_DIRECT_MESSAGE_STRING);
556                         }
557                 } catch (ExistTimerIDException ex) {
558                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
559                                         JOptionPane.ERROR_MESSAGE);
560                 }
561         }
562
563         /**
564          * SendDMタブを追加する
565          * 
566          * @param period
567          */
568         public void actionAddSendDirectMessageTab(int period) {
569                 TimerID timerID = TimerID.getInstance();
570                 String id = TimerID.createSendDirectMessageID();
571                 try {
572                         // 既にIDが存在していたらここで例外発生
573                         timerID.addID(id);
574                         // 検索結果を表示するタブを生成
575                         actionAddTab(id, period, new TweetSendDirectMessageGetter(
576                                         tweetManager),
577                                         TweetMainAction.TAB_SEND_DIRECT_MESSAGE_STRING);
578                 } catch (ExistTimerIDException ex) {
579                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
580                                         JOptionPane.ERROR_MESSAGE);
581                 }
582         }
583
584         /**
585          * 指定したリストをタブに追加
586          * 
587          * @param username
588          * @param listID
589          * @param listFullname
590          * @param period
591          */
592         public void actionAddListTab(String username, int listID,
593                         String listFullname, int period) {
594                 TimerID timerID = TimerID.getInstance();
595                 String id = TimerID.createUserListID(username, listID);
596                 try {
597                         // 既にIDが存在していたらここで例外発生
598                         timerID.addID(id);
599                         // 検索結果を表示するタブを生成
600                         actionAddTab(id, period, new TweetListGetter(tweetManager,
601                                         username, listID), listFullname);
602                 } catch (ExistTimerIDException ex) {
603                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
604                                         JOptionPane.ERROR_MESSAGE);
605                 }
606         }
607
608         /**
609          * ツイート検索結果を表示するタブを新しく追加
610          * 
611          * @param searchWord
612          * @param period
613          *            更新周期[sec] 0以下の場合は更新しない
614          */
615         public void actionAddNewSearchResultTab(String searchWord, int period) {
616                 TimerID timerID = TimerID.getInstance();
617                 String id = TimerID.createSearchTimerID(searchWord);
618                 try {
619                         // 既にIDが存在していたらここで例外発生
620                         timerID.addID(id);
621                         // 検索結果を表示するタブを生成
622                         actionAddTab(id, period, new TweetSearchResultGetter(
623                                         this.tweetManager, searchWord), searchWord);
624                 } catch (ExistTimerIDException ex) {
625                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
626                                         JOptionPane.ERROR_MESSAGE);
627                 }
628         }
629
630         /**
631          * 指定したユーザの発言を表示
632          * 
633          * @param username
634          *            タブのタイトルにつけるユーザ名
635          * @param userID
636          *            ユーザID
637          * @param period
638          *            更新周期[sec]
639          */
640         public void actionAddUserTimelineTab(String username, long userID,
641                         int period) {
642                 TimerID timerID = TimerID.getInstance();
643                 String id = TimerID.createUserTimelineID(userID);
644                 try {
645                         // 既にIDが存在していたらここで例外発生
646                         timerID.addID(id);
647                         // 検索結果を表示するタブを生成
648                         actionAddTab(id, period, new TweetUserTimelineGetter(tweetManager,
649                                         userID), username + "の発言");
650                 } catch (ExistTimerIDException ex) {
651                         JOptionPane.showMessageDialog(null, "そのタブは既に存在しています", "Error",
652                                         JOptionPane.ERROR_MESSAGE);
653                 }
654         }
655
656         /**
657          * タイムラインタブが存在しているか
658          * 
659          * @return
660          */
661         public boolean isExistTimelineTab() {
662                 TimerID timerID = TimerID.getInstance();
663                 String id = TimerID.createTimelineID();
664                 return timerID.contains(id);
665         }
666
667         /**
668          * Mentionタブが存在するか
669          * 
670          * @return
671          */
672         public boolean isExistMentionTab() {
673                 TimerID timerID = TimerID.getInstance();
674                 String id = TimerID.createMentionID();
675                 return timerID.contains(id);
676         }
677
678         /**
679          * DMタブが存在するか
680          * 
681          * @return
682          */
683         public boolean isExistDirectMessageTab() {
684                 TimerID timerID = TimerID.getInstance();
685                 String id = TimerID.createDirectMessageID();
686                 return timerID.contains(id);
687         }
688
689         /**
690          * 送信済みDMタブが存在するか
691          * 
692          * @return
693          */
694         public boolean isExistSendDirectMessageTab() {
695                 TimerID timerID = TimerID.getInstance();
696                 String id = TimerID.createSendDirectMessageID();
697                 return timerID.contains(id);
698         }
699
700         /**
701          * 基本設定ダイアログを開く
702          */
703         public void actionBasicSettingDialog() {
704                 // ダイレクトメッセージ送信用ダイアログを開く
705                 Point loc = getConfigurationDialog().getLocation();
706                 loc.translate(20, 20);
707                 ConfigurationDialog dialog = getConfigurationDialog();
708                 dialog.setLocation(loc);
709                 dialog.setVisible(true);
710         }
711
712         /**
713          * reply設定
714          */
715         public void actionSetReplyStatusToTweetBoxPane() {
716                 // 選択した部分
717                 this.setReplyStatus(currentStatus);
718
719                 Status s = this.getCurrentStatus();
720                 if (s.isRetweet()) {
721                         s = s.getRetweetedStatus();
722                 }
723                 // コメントしたユーザ名
724                 String username = s.getUser().getScreenName();
725                 this.tweetBoxPane.setText("@" + username + " ");
726
727                 // 情報表示
728                 this.information(username + "さんに返信");
729         }
730
731         /**
732          * reply All設定
733          */
734         public void actionSetReplyAllStatusToTweetBoxPane() {
735                 // 選択し多分
736                 this.setReplyStatus(null);
737
738                 Status s = this.getCurrentStatus();
739                 if (s.isRetweet()) {
740                         s = s.getRetweetedStatus();
741                 }
742                 // コメントしたユーザ名
743                 String username = s.getUser().getScreenName();
744                 this.tweetBoxPane.setText("@" + username + " ");
745
746                 // 情報表示
747                 this.information(username + "さんに返信");
748         }
749
750         /**
751          * 引用Tweet
752          */
753         public void actionSetQuoteStatusToTweetBoxPane() {
754                 // 選択した部分
755                 this.setReplyStatus(currentStatus);
756                 Status s = this.getCurrentStatus();
757                 if (s.isRetweet()) {
758                         s = s.getRetweetedStatus();
759                 }
760                 // コメントしたユーザ名
761                 String username = s.getUser().getScreenName();
762                 // コメント
763                 String message = s.getText();
764                 this.tweetBoxPane.setText("QT @" + username + ": " + message);
765
766                 // 情報表示
767                 this.information(username + "さんのメッセージを引用ツイート");
768         }
769
770         /**
771          * 選択したtweetを非公式RT
772          */
773         public void actionCopySelectedStatusToTweetBoxPane() {
774                 Status s = this.getCurrentStatus();
775                 if (s.isRetweet()) {
776                         s = s.getRetweetedStatus();
777                 }
778                 // コメントしたユーザ名
779                 String username = s.getUser().getScreenName();
780                 // コメント
781                 String message = s.getText();
782                 this.tweetBoxPane.setText("RT @" + username + ": " + message);
783         }
784
785         /**
786          * 詳細情報表示ボタンを押した時の動作
787          * 
788          * @param e
789          */
790         public void actionDetailInfoButton(ActionEvent e) {
791                 if (detailInfoPanel.isVisible()) {
792                         detailInfoPanel.setVisible(false);
793                 } else {
794                         detailInfoPanel.setVisible(true);
795                 }
796         }
797
798         /**
799          * 詳細情報ボタンが表示されているか
800          * 
801          * @return
802          */
803         public boolean isDetailInfoPanelVisible() {
804                 return detailInfoPanel.isVisible();
805         }
806
807         /**
808          * 書き込みメッセージボックスの表示ONOFFボタンを押した時の動作
809          * 
810          * @param e
811          */
812         public void actionShowTweetboxButton(ActionEvent e) {
813                 if (this.tweetBoxRegionPane.isVisible()) {
814                         this.tweetBoxRegionPane.setVisible(false);
815                 } else {
816                         this.tweetBoxRegionPane.setVisible(true);
817                 }
818         }
819
820         /**
821          * 書き込みメッセージボックス領域が表示されているか
822          */
823         public boolean isShowTweetBoxVisible() {
824                 return this.tweetBoxRegionPane.isVisible();
825         }
826
827         /**
828          * 選択しているタブを削除
829          */
830         public void actionRemoveFocusedTabbedTable() {
831                 int selected = this.tweetMainTab.getSelectedIndex();
832                 actionRemoveTabbedTable(selected);
833         }
834
835         /**
836          * 指定したIDのタブを削除
837          * 
838          * @param timerID
839          */
840         public void actionRemoveTabbedTable(String timerID) {
841                 int deleteTabIndex = -1;
842
843                 for (int i = 0; i < tweetTabbedTableList.size(); i++) {
844                         TweetTabbedTable table = tweetTabbedTableList.get(i);
845                         if (table.getTimerID().equals(timerID)) {
846                                 // 消したいタブが見つかった
847                                 deleteTabIndex = i;
848                                 break;
849                         }
850                 }
851
852                 if (deleteTabIndex >= 0) {
853                         int selected = this.tweetTabbedTableList.get(deleteTabIndex)
854                                         .getTabSetNum();
855                         // タブを削除
856                         this.tweetMainTab.remove(selected);
857                         // 削除
858                         this.tweetTabbedTableList.remove(deleteTabIndex);
859                         // 自動更新しているタブを削除
860                         this.tweetTaskManager.shutdownTask(timerID);
861                         // ID削除
862                         TimerID idManager = TimerID.getInstance();
863                         idManager.removeID(timerID);
864
865                         // checkboxの状態更新
866                         this.updateCheckboxInformation();
867                 }
868         }
869
870         /**
871          * 指定した場所にあるタブを削除
872          * 
873          * @param removeTabIndex
874          */
875         public void actionRemoveTabbedTable(int removeTabIndex) {
876                 int selected = removeTabIndex;
877                 // タブの何番目に消したいテーブルがあるのかと,tweetTabbedTableListの何番目に消したいテーブルがあるのかは違う
878                 // これを探してくる必要がある
879
880                 // 選択したタブのテーブルを取得
881                 int deleteTabIndex = -1;
882                 for (int i = 0; i < tweetTabbedTableList.size(); i++) {
883                         TweetTabbedTable table = tweetTabbedTableList.get(i);
884                         if (selected == table.getTabSetNum()) {
885                                 // 消したいタブが見つかった
886                                 deleteTabIndex = i;
887                                 break;
888                         }
889                 }
890
891                 if (deleteTabIndex >= 0) {
892                         // タブを削除
893                         this.tweetMainTab.remove(selected);
894                         // タブのタイマーID
895                         String timerID = this.tweetTabbedTableList.get(deleteTabIndex)
896                                         .getTimerID();
897                         // 削除
898                         this.tweetTabbedTableList.remove(deleteTabIndex);
899                         // 自動更新しているタブを削除
900                         this.tweetTaskManager.shutdownTask(timerID);
901                         // ID削除
902                         TimerID idManager = TimerID.getInstance();
903                         idManager.removeID(timerID);
904
905                         // checkboxの状態更新
906                         this.updateCheckboxInformation();
907                 }
908         }
909
910         /**
911          * 終了ボタンを押した時の動作
912          * 
913          * @param e
914          */
915         public void actionExitButton(ActionEvent e) {
916                 System.exit(0);
917         }
918
919         /**
920          * 選択した発言をブラウザで開く
921          */
922         public void actionOpenStatusURL() {
923                 try {
924                         // ユーザ名
925                         String userName = this.getCurrentStatus().getUser().getScreenName();
926                         // 発言のstatusID
927                         long statusID = this.getCurrentStatus().getId();
928                         Desktop.getDesktop().browse(
929                                         new URI(TWITTER_URL + userName + "/statuses/" + statusID));
930                 } catch (Exception ex) {
931                         ex.printStackTrace();
932                         JOptionPane.showMessageDialog(null, "エラーによりブラウザを起動できませんでした.",
933                                         "Error", JOptionPane.ERROR_MESSAGE);
934                 }
935         }
936
937         /**
938          * 選択したユーザのFavを開く
939          */
940         public void actionOpenUserFav() {
941                 try {
942                         Status s = this.getCurrentStatus();
943                         if (s.isRetweet()) {
944                                 s = s.getRetweetedStatus();
945                         }
946                         String userName = s.getUser().getScreenName();
947                         this.actionAddFavoriteTab(userName);
948                 } catch (Exception ex) {
949                         ex.printStackTrace();
950                 }
951         }
952
953         /**
954          * 自分自身のFavを開く
955          */
956         public void actionOpenFav() {
957                 String username = this.tweetManager.getScreenName();
958                 this.actionAddFavoriteTab(username);
959         }
960
961         /**
962          * 選択したユーザ情報をブラウザで開く
963          */
964         public void actionOpenUserURL() {
965                 try {
966                         Status s = this.getCurrentStatus();
967                         if (s.isRetweet()) {
968                                 s = s.getRetweetedStatus();
969                         }
970                         String userName = s.getUser().getScreenName();
971                         Desktop.getDesktop().browse(new URI(TWITTER_URL + userName));
972                 } catch (Exception ex) {
973                         ex.printStackTrace();
974                         JOptionPane.showMessageDialog(null, "エラーによりブラウザを起動できませんでした.",
975                                         "Error", JOptionPane.ERROR_MESSAGE);
976                 }
977         }
978
979         /**
980          * Tweet取得時間情報を更新
981          */
982         public void actionRefreshTime() {
983                 // タブに存在する時間情報を更新
984                 for (TweetTabbedTable t : this.tweetTabbedTableList) {
985                         TweetTableModel model = t.getModel();
986                         if (model != null) {
987                                 model.refreshTime();
988                         }
989                 }
990         }
991
992         /**
993          * TweetMessageBox内にある#ハッシュタグ の部分をa hrefリンクに変換
994          * 
995          * @param message
996          */
997         public String actionReplaceTweetMessageBoxHashTab(String message) {
998                 // #で始まる情報
999                 Pattern userPtn = Pattern.compile("#[0-9A-Z_]+",
1000                                 Pattern.CASE_INSENSITIVE);
1001                 Matcher matcher = userPtn.matcher(message);
1002
1003                 // #で始まる情報一覧を抜き出す
1004                 Set<String> findList = new TreeSet<String>();
1005                 while (matcher.find()) {
1006                         findList.add(matcher.group(0));
1007                 }
1008                 // 指定した情報をすべてリンクへ変更
1009                 for (String f : findList) {
1010                         try {
1011                                 message = message.replaceAll(f + "$|" + f + "\\s", "<a href=\""
1012                                                 + SEARCH_TWITTER_URL + SEARCH_QUERY
1013                                                 + URLEncoder.encode(f, DEFAULT_CHARACTER_ENCODING)
1014                                                 + "\">" + "$0</a>");
1015                         } catch (UnsupportedEncodingException e) {
1016                                 e.printStackTrace();
1017                         }
1018                 }
1019                 return message;
1020         }
1021
1022         /**
1023          * TweetMessageBox内にあるリンクをa hrefリンクに変換
1024          * 
1025          * @param message
1026          */
1027         public String actionReplaceTweetMessageBoxURLLink(String message) {
1028                 Matcher matcher = convURLLinkPtn.matcher(message);
1029                 return matcher.replaceAll("<a href=\"$0\">$0</a>");
1030         }
1031
1032         /**
1033          * @ユーザ名の部分をa hrefリンクに変換
1034          * @param message
1035          */
1036         public String actionReplaceTweetMessageBoxUserInfo(String message) {
1037                 // @で始まる情報
1038                 Pattern userPtn = Pattern.compile("@[0-9A-Z_]+",
1039                                 Pattern.CASE_INSENSITIVE);
1040                 Matcher matcher = userPtn.matcher(message);
1041                 // @で始まるユーザ名一覧を抜き出す
1042                 Set<String> findList = new TreeSet<String>();
1043                 while (matcher.find()) {
1044                         findList.add(matcher.group(0));
1045                 }
1046                 // 指定したユーザ名をすべてリンクへ変更
1047                 for (String f : findList) {
1048                         message = message.replaceAll(f, "<a href=\"" + TWITTER_URL
1049                                         + f.substring(1) + "\">" + f + "</a>");
1050                 }
1051                 return message;
1052         }
1053
1054         /**
1055          * まだ見ていないdirectMessage数を0にする
1056          */
1057         public void actionResetUncheckedDirectMessageCount() {
1058                 uncheckedDirectMessageCount = 0;
1059                 tweetMainTab.setTitleAt(2, TAB_DIRECT_MESSAGE_STRING);
1060         }
1061
1062         /**
1063          * まだ見ていないmention数を0にする
1064          */
1065         public void actionResetUncheckedMentionTweetCount() {
1066                 uncheckedMentionTweetCount = 0;
1067                 tweetMainTab.setTitleAt(1, TAB_MENTION_STRING);
1068         }
1069
1070         /**
1071          * まだ見ていないtweet数を0にする
1072          */
1073         public void actionResetUncheckedTimelineTweetCount() {
1074                 uncheckedTimelineTweetCount = 0;
1075                 tweetMainTab.setTitleAt(0, TAB_TIMELINE_STRING);
1076         }
1077
1078         /**
1079          * 選択したユーザの発言を開く
1080          */
1081         public void actionSelectedUserTimeline() {
1082                 Status status = null;
1083                 if (this.getCurrentStatus().isRetweet()) {
1084                         status = this.getCurrentStatus().getRetweetedStatus();
1085                 } else {
1086                         status = this.getCurrentStatus();
1087                 }
1088                 String username = status.getUser().getScreenName();
1089                 long userID = status.getUser().getId();
1090                 // ユーザ
1091                 actionAddUserTimelineTab(username, userID, this.getGetTimelinePeriod());
1092         }
1093
1094         /**
1095          * 選択したユーザが作成したリスト一覧を表示
1096          * 
1097          * @param selection
1098          */
1099         public void actionShowSelectedUserList(ListGetterSelection selection) {
1100                 Status status = null;
1101                 if (this.getCurrentStatus().isRetweet()) {
1102                         status = this.getCurrentStatus().getRetweetedStatus();
1103                 } else {
1104                         status = this.getCurrentStatus();
1105                 }
1106                 String username = status.getUser().getScreenName();
1107                 actionShowUserListDialog(status.getUser().getScreenName(), selection);
1108         }
1109
1110         /**
1111          * 選択しているツイートをお気に入りに追加
1112          */
1113         public void actionCreateFavorite() {
1114                 Status status = null;
1115                 if (this.getCurrentStatus().isRetweet()) {
1116                         status = this.getCurrentStatus().getRetweetedStatus();
1117                 } else {
1118                         status = this.getCurrentStatus();
1119                 }
1120                 // 選択しているtweetのstatus id
1121                 long statusID = status.getId();
1122                 // コメントしたユーザ名
1123                 String username = status.getUser().getScreenName();
1124                 // コメント
1125                 String message = status.getText();
1126                 // 発言が長すぎる場合,後半をカット
1127                 if (message.length() > 30) {
1128                         message = message.substring(0, 30) + " ...(以下略)";
1129                 }
1130                 // Retweetしていいかどうかの確認
1131                 int ret = JOptionPane.showConfirmDialog(mainFrame, username + " さんの発言:"
1132                                 + message + "\nをお気に入りに追加しますか?", "Favの確認",
1133                                 JOptionPane.YES_NO_OPTION);
1134                 if (ret == JOptionPane.YES_OPTION) {
1135                         try {
1136                                 // Retweetを行う
1137                                 this.tweetManager.createFavorite(statusID);
1138                         } catch (TwitterException e) {
1139                                 JOptionPane.showMessageDialog(null, "エラーによりお気に入りに追加できませんでした.",
1140                                                 "Fav Error", JOptionPane.ERROR_MESSAGE);
1141                         }
1142                 }
1143         }
1144
1145         /**
1146          * 選択しているツイートをお気に入りから外す
1147          */
1148         public void actionDestroyFavorite() {
1149                 Status status = null;
1150                 if (this.getCurrentStatus().isRetweet()) {
1151                         status = this.getCurrentStatus().getRetweetedStatus();
1152                 } else {
1153                         status = this.getCurrentStatus();
1154                 }
1155                 // 選択しているtweetのstatus id
1156                 long statusID = status.getId();
1157                 // コメントしたユーザ名
1158                 String username = status.getUser().getScreenName();
1159                 // コメント
1160                 String message = status.getText();
1161                 // 発言が長すぎる場合,後半をカット
1162                 if (message.length() > 30) {
1163                         message = message.substring(0, 30) + " ...(以下略)";
1164                 }
1165                 // Retweetしていいかどうかの確認
1166                 int ret = JOptionPane.showConfirmDialog(mainFrame, username + " さんの発言:"
1167                                 + message + "\nをお気に入りから削除しますか?", "Favの確認",
1168                                 JOptionPane.YES_NO_OPTION);
1169                 if (ret == JOptionPane.YES_OPTION) {
1170                         try {
1171                                 // Retweetを行う
1172                                 this.tweetManager.destroyFavorite(statusID);
1173                         } catch (TwitterException e) {
1174                                 JOptionPane.showMessageDialog(null, "エラーによりお気に入りから削除できませんでした.",
1175                                                 "Fav Error", JOptionPane.ERROR_MESSAGE);
1176                         }
1177                 }
1178         }
1179
1180         /**
1181          * 現在選択しているステータスを公式Retweet
1182          */
1183         public void actionRetweet() {
1184                 Status status = null;
1185                 if (this.getCurrentStatus().isRetweet()) {
1186                         status = this.getCurrentStatus().getRetweetedStatus();
1187                 } else {
1188                         status = this.getCurrentStatus();
1189                 }
1190
1191                 // 選択しているtweetのstatus id
1192                 long statusID = status.getId();
1193                 // コメントしたユーザ名
1194                 String username = status.getUser().getScreenName();
1195                 // コメント
1196                 String message = status.getText();
1197                 // 発言が長すぎる場合,後半をカット
1198                 if (message.length() > 30) {
1199                         message = message.substring(0, 30) + " ...(以下略)";
1200                 }
1201                 // Retweetしていいかどうかの確認
1202                 int ret = JOptionPane.showConfirmDialog(mainFrame, username + " さんの発言:"
1203                                 + message + "\nをRetweetしますか?", "Retweetの確認",
1204                                 JOptionPane.YES_NO_OPTION);
1205                 if (ret == JOptionPane.YES_OPTION) {
1206                         try {
1207                                 // Retweetを行う
1208                                 this.tweetManager.retweet(statusID);
1209                         } catch (TwitterException e) {
1210                                 JOptionPane.showMessageDialog(null, "エラーによりRetweetできませんでした.",
1211                                                 "Retweet Error", JOptionPane.ERROR_MESSAGE);
1212                         }
1213                 }
1214         }
1215
1216         /**
1217          * テーブルの高さを更新
1218          * 
1219          * @param height
1220          */
1221         public void updateTableHeight(int height) {
1222                 this.tableElementHeight = height;
1223                 for (TweetTabbedTable t : this.tweetTabbedTableList) {
1224                         t.getTable().setRowHeight(tableElementHeight);
1225                 }
1226         }
1227
1228         /**
1229          * ダイレクトメッセージダイアログ表示
1230          */
1231         public void actionShowDirectMessageDialog() {
1232                 // ダイレクトメッセージ送信用ダイアログを開く
1233                 Point loc = getDirectMessageDialog().getLocation();
1234                 // loc.translate(20, 20);
1235                 DirectMessageDialog dialog = getDirectMessageDialog();
1236                 // dialog.setLocation(loc);
1237                 dialog.setLocationRelativeTo(null);
1238                 dialog.setVisible(true);
1239                 dialog.setUserInformation(this.selectedUsername,
1240                                 this.selectedUserImageURL, this.tweetManager);
1241         }
1242
1243         /**
1244          * Aboutダイアログを表示
1245          */
1246         public void actionShowAboutDialog() {
1247                 Point loc = getDirectMessageDialog().getLocation();
1248                 // loc.translate(20, 20);
1249                 AboutDialog dialog = getAboutDialog();
1250                 dialog.setLocationRelativeTo(null);
1251                 // dialog.setLocation(loc);
1252                 dialog.setVisible(true);
1253         }
1254
1255         /**
1256          * アカウントダイアログを表示
1257          */
1258         public void actionShowAccountDialog() {
1259                 // TODO: location取得のコードおかしい
1260                 Point loc = getDirectMessageDialog().getLocation();
1261                 // loc.translate(20, 20);
1262                 AccountDialog dialog = getAccountDialog();
1263                 dialog.setLocationRelativeTo(null);
1264                 // dialog.setLocation(loc);
1265                 dialog.setVisible(true);
1266         }
1267
1268         /**
1269          * Twitter全体からキーワード検索ダイアログを表示
1270          */
1271         public void actionShowKeywordSearchDialog() {
1272                 Point loc = getDirectMessageDialog().getLocation();
1273                 KeywordSearchDialog dialog = getKeywordSearchDialog();
1274                 dialog.setSearchWord("");
1275                 dialog.setLocationRelativeTo(null);
1276                 dialog.setVisible(true);
1277         }
1278
1279         /**
1280          * Twitter全体からキーワード検索ダイアログを表示
1281          * 
1282          * @param searchWord
1283          *            searchwordボックスに予め設定するワード
1284          */
1285         public void actionShowKeywordSearchDialog(String searchWord) {
1286                 Point loc = getDirectMessageDialog().getLocation();
1287                 KeywordSearchDialog dialog = getKeywordSearchDialog();
1288                 dialog.setSearchWord(searchWord);
1289                 dialog.setLocationRelativeTo(null);
1290                 dialog.setVisible(true);
1291         }
1292
1293         /**
1294          * ハッシュタグ検索ダイアログを表示
1295          */
1296         public void actionShowHashtagSearchDialog() {
1297                 Point loc = getDirectMessageDialog().getLocation();
1298                 HashtagSearchDialog dialog = getHashtagSearchDialog();
1299                 dialog.setLocationRelativeTo(null);
1300                 dialog.setVisible(true);
1301         }
1302
1303         /**
1304          * リストダイアログを表示
1305          * 
1306          * @param listUserName
1307          *            リストを保持しているユーザの名前
1308          * @param selection
1309          *            CREATED: 指定したユーザが作成したリスト SUBSCRIPTION: 指定したユーザがフォローしているリスト
1310          *            MEMBERSHIPS: 指定したユーザが追加されているリスト
1311          */
1312         public void actionShowUserListDialog(String listUserName,
1313                         ListGetterSelection selection) {
1314                 UserListDialog dialog = getUserListDialog(listUserName, selection);
1315                 Point loc = dialog.getLocation();
1316                 dialog.setLocationRelativeTo(null);
1317                 dialog.setVisible(true);
1318         }
1319
1320         /**
1321          * tweetBoxPaneに書かれた文字をつぶやく
1322          */
1323         public void actionTweet() {
1324                 if (this.replyStatus != null) {
1325                         tweetManager.replyTweet(tweetBoxPane.getText(), this.replyStatus
1326                                         .getId());
1327                 } else {
1328                         tweetManager.tweet(tweetBoxPane.getText());
1329                 }
1330                 // ツイートした旨を表示
1331                 this.information("メッセージをつぶやきました. 発言:" + tweetBoxPane.getText());
1332
1333                 tweetBoxPane.setText(""); // テキストをクリア
1334
1335         }
1336
1337         /**
1338          * Tweet情報を更新
1339          * 
1340          * @param e
1341          */
1342         public void actionUpdateButton(java.awt.event.ActionEvent e) {
1343                 try {
1344                         // タブ上に存在するテーブルの情報を更新
1345                         for (TweetTabbedTable t : this.tweetTabbedTableList) {
1346                                 String timerID = t.getTimerID();
1347                                 this.tweetTaskManager.resetTask(timerID, true);
1348                         }
1349
1350                         // API残り回数を取得
1351                         int remainingHits = tweetManager.getRateLimitStatus()
1352                                         .getRemainingHits();
1353                         // 取得したコメント数をステータスバーに表示
1354                         information("新しいツイートを取得しました. (APIリクエスト残数は" + remainingHits + "回です)");
1355                 } catch (Exception e1) {
1356                         e1.printStackTrace();
1357                 }
1358         }
1359
1360         /**
1361          * 新しく追加したタブにフォーカスを移す
1362          */
1363         public void actionRequestForusToLastTab() {
1364                 int lasttab = this.tweetMainTab.getTabCount() - 1;
1365                 if (lasttab >= 0) {
1366                         this.tweetMainTab.setSelectedIndex(lasttab);
1367                 }
1368         }
1369
1370         /**
1371          * 指定した番号のタブにフォーカスを移す
1372          * 
1373          * @param index
1374          */
1375         public void actionRequestFocusToTab(int index) {
1376                 if (index >= 0) {
1377                         this.tweetMainTab.setSelectedIndex(index);
1378                 }
1379         }
1380
1381         /**
1382          * 選択しているタブにあるテーブル情報だけを更新
1383          */
1384         public void actionFocusedTableUpdate() {
1385                 int selected = this.tweetMainTab.getSelectedIndex();
1386                 try {
1387                         if (selected >= 0) {
1388                                 // タブ上に存在するテーブルの情報を更新
1389                                 TweetTabbedTable t = this.tweetTabbedTableList.get(selected);
1390                                 String timerID = t.getTimerID();
1391                                 this.tweetTaskManager.resetTask(timerID, true);
1392                                 // API残り回数を取得
1393                                 int remainingHits = tweetManager.getRateLimitStatus()
1394                                                 .getRemainingHits();
1395                                 // 取得したコメント数をステータスバーに表示
1396                                 information(t.getTitle() + "タブのツイートを" + t.getUncheckedTweet()
1397                                                 + "件取得しました. (APIリクエスト残数は" + remainingHits + "回です)");
1398                         }
1399                 } catch (Exception e1) {
1400                         e1.printStackTrace();
1401                 }
1402         }
1403
1404         /**
1405          * つぶやける残り文字数の更新
1406          * 
1407          * @param e
1408          */
1409         public void actionUpdateTweetMessageCount() {
1410                 int len = 140 - (tweetBoxPane.getText().length());
1411                 if (len < 0) {
1412                         len = 0;
1413                 }
1414                 tweetMessageCountLabel.setText(len + "");
1415
1416                 // 残りつぶやき数140の場合,reply状態も解除する
1417                 if (len == 140) {
1418                         this.setReplyStatus(null);
1419                 }
1420         }
1421
1422         /**
1423          * 基本設定用ダイアログを取得
1424          * 
1425          * @return
1426          */
1427         public ConfigurationDialog getConfigurationDialog() {
1428                 if (configurationDialog == null) {
1429                         configurationDialog = new ConfigurationDialog(mainFrame, true, this);
1430                 }
1431                 return configurationDialog;
1432         }
1433
1434         /**
1435          * リストダイアログを取得
1436          * 
1437          * @param listUserName
1438          *            リストを保持しているユーザの名前
1439          * @param selection
1440          * @return
1441          */
1442         public UserListDialog getUserListDialog(String listUserName,
1443                         ListGetterSelection selection) {
1444                 UserListGetter getter = null;
1445                 switch (selection) {
1446                 case CREATED:
1447                         getter = new UserListSpecificUserListsGetter(tweetManager);
1448                         break;
1449                 case MEMBERSHIPS:
1450                         getter = new UserListMembershipsGetter(tweetManager);
1451                         break;
1452                 case SUBSCRIPTION: /* fall through */
1453                 default:
1454                         getter = new UserListSubscriptionGetter(tweetManager);
1455                         break;
1456                 }
1457                 userListDialog = new UserListDialog(mainFrame, true, this, getter,
1458                                 listUserName);
1459                 return userListDialog;
1460         }
1461
1462         /**
1463          * 自身のスクリーン名を取得
1464          * 
1465          * @return
1466          */
1467         public String getScreenName() {
1468                 return tweetManager.getScreenName();
1469         }
1470
1471         /**
1472          * twitter全体からキーワード検索ダイアログを表示
1473          * 
1474          * @return
1475          */
1476         public KeywordSearchDialog getKeywordSearchDialog() {
1477                 if (keywordSearchDialog == null) {
1478                         keywordSearchDialog = new KeywordSearchDialog(mainFrame, true, this);
1479                 }
1480                 return keywordSearchDialog;
1481         }
1482
1483         /**
1484          * hashtag検索ダイアログ
1485          * 
1486          * @return
1487          */
1488         public HashtagSearchDialog getHashtagSearchDialog() {
1489                 if (hashtagSearchDialog == null) {
1490                         hashtagSearchDialog = new HashtagSearchDialog(mainFrame, true,
1491                                         this, tweetManager);
1492                 }
1493                 return hashtagSearchDialog;
1494         }
1495
1496         /**
1497          * アカウント情報設定ダイアログを取得
1498          * 
1499          * @return
1500          */
1501         public AccountDialog getAccountDialog() {
1502                 if (accountDialog == null) {
1503                         accountDialog = new AccountDialog(mainFrame, true, tweetManager,
1504                                         this);
1505                 }
1506                 return accountDialog;
1507         }
1508
1509         /**
1510          * ダイレクトメッセージ送信用ダイアログを取得
1511          * 
1512          * @return
1513          */
1514         public DirectMessageDialog getDirectMessageDialog() {
1515                 if (directMessageDialog == null) {
1516                         directMessageDialog = new DirectMessageDialog(mainFrame);
1517                         directMessageDialog.setTitle("ダイレクトメッセージを送信");
1518                 }
1519                 return directMessageDialog;
1520         }
1521
1522         /**
1523          * テーブルで選択したツイートを詳細情報としてセット
1524          * 
1525          * @param table
1526          */
1527         public void setDetailInformationFromTable(JTable table) {
1528                 int sc = table.getSelectedRowCount();
1529                 String infoMessage = "";
1530
1531                 // 選択している行が1行だけの場合,情報を表示する
1532                 if (sc == 1 && table != null) {
1533                         Status st = getTweetTableInformation(table, table.getModel());
1534                         // RTの場合,もとの発言を表示
1535                         if (st.isRetweet()) {
1536                                 st = st.getRetweetedStatus();
1537                         }
1538                         infoMessage = st.getText();
1539                         // メッセージのHTMLエンコードを行う
1540                         infoMessage = HTMLEncode.encode(infoMessage);
1541                         // tweetMessageBox内のURLをhtmlリンクへ変換
1542                         infoMessage = actionReplaceTweetMessageBoxURLLink(infoMessage);
1543                         // @ユーザ情報をhtmlリンクへ変換
1544                         infoMessage = actionReplaceTweetMessageBoxUserInfo(infoMessage);
1545                         // #ハッシュタグ情報をhtmlリンクへ変換
1546                         infoMessage = actionReplaceTweetMessageBoxHashTab(infoMessage);
1547                         // 詳細情報にテーブルで選択した人のツイート情報を表示
1548                         tweetMessageBox.setText(infoMessage);
1549                         // user icon
1550                         //アイコンをキャッシュから取得
1551                         ImageIcon icon = TwitterImageCache.getInstance().getProfileImage( st.getUser().getProfileImageURL().toString() );
1552                         userImageLabel.setIcon( icon );
1553                         // user name and id
1554                         userNameLabel.setText(st.getUser().getName() + " / "
1555                                         + st.getUser().getScreenName());
1556                         // update Time
1557                         updateTimeLabel.setText(DateFormat.getInstance().format(
1558                                         st.getCreatedAt()));
1559                         // ユーザ自己紹介文
1560                         userIntroBox.setText(st.getUser().getDescription());
1561                         // フォローされている数
1562                         followerLabel.setText(st.getUser().getFollowersCount() + "");
1563                         // フォローしている数
1564                         followingLabel.setText(st.getUser().getFriendsCount() + "");
1565                         // 現在地
1566                         locationLabel.setText(st.getUser().getLocation());
1567                         // Web
1568                         if (st.getUser().getURL() != null) {
1569                                 userWebBox.setText("<a href=\"" + st.getUser().getURL() + "\">"
1570                                                 + st.getUser().getScreenName() + "のWebを開く" + "</a>");
1571                         } else {
1572                                 userWebBox.setText("");
1573                         }
1574                         // client
1575                         clientNameLabel.setText(" via " + st.getSource());
1576                         // Update
1577                         updateLabel.setText(st.getUser().getStatusesCount() + "");
1578                 }
1579         }
1580
1581         /**
1582          * 
1583          * @return
1584          */
1585         public AboutDialog getAboutDialog() {
1586                 if (aboutDialog == null) {
1587                         aboutDialog = new AboutDialog(mainFrame, true);
1588                 }
1589                 return aboutDialog;
1590         }
1591
1592         /**
1593          * テーブルで選択した場所のTweet情報を取得
1594          * 
1595          * @return
1596          */
1597         public Status getTweetTableInformation(JTable table, TableModel model) {
1598                 int index = table.convertRowIndexToModel(table.getSelectedRow());
1599                 Status status = null;
1600                 if (model instanceof TweetTableModel) {
1601                         status = ((TweetTableModel) model).getTweetStatus(index);
1602                         // 現在選択したセルのユーザ名を保存しておく
1603                         this.selectedUsername = status.getUser().getScreenName();
1604                         // 現在選択したセルのユーザURLを保存しておく
1605                         this.selectedUserImageURL = status.getUser().getProfileImageURL();
1606                         // 選択したStatusを保存しておく
1607                         this.setCurrentStatus(status);
1608                 }
1609                 return status;
1610         }
1611
1612         /**
1613          * ステータスバーに情報を表示する
1614          * 
1615          * @param message
1616          */
1617         public void information(String message) {
1618                 statusBarLabel.setText(message);
1619         }
1620
1621         /**
1622          * 設定ファイルを読み込む
1623          * 
1624          * @throws IOException
1625          * @throws FileNotFoundException
1626          */
1627         public void loadProperties() throws FileNotFoundException, IOException {
1628                 if (property == null) {
1629                         this.property = new Properties();
1630                 }
1631                 property.load(new FileInputStream("./" + PROPERTIES_DIRECTORY + "/"
1632                                 + BASIC_SETTING_FILENAME));
1633                 // 設定読み込み
1634                 String gtp = this.property.getProperty("getTimelinePeriod");
1635                 String gmp = this.property.getProperty("getMentionPeriod");
1636                 String gdmp = this.property.getProperty("getDirectMessagePeriod");
1637                 String gsdmp = this.property.getProperty("getSendDirectMessagePeriod");
1638
1639                 String ntrgb = this.property.getProperty("newTableColorRGB");
1640
1641                 this.tlFontName = this.property.getProperty("tlFontName");
1642                 this.detailFontName = this.property.getProperty("detailFontName");
1643
1644                 String tfs = this.property.getProperty("tlFontSize");
1645                 String dfs = this.property.getProperty("detailFontSize");
1646                 String teh = this.property.getProperty("tableElementHeight");
1647
1648                 // メインフレームの大きさ
1649                 String mfw = this.property.getProperty("mainFrameWidth");
1650                 String mfh = this.property.getProperty("mainFrameHeight");
1651
1652                 // メッセージ通知を行うか
1653                 String nm = this.property.getProperty("notifyMessage");
1654                 String ndm = this.property.getProperty("notifyDirectMessage");
1655
1656                 try {
1657                         this.newTableColor = new Color(Integer.parseInt(ntrgb));
1658                         this.tlFontSize = Integer.parseInt(tfs);
1659                         this.detailFontSize = Integer.parseInt(dfs);
1660                         this.tableElementHeight = Integer.parseInt(teh);
1661                         this.mainFrameWidth = Integer.parseInt(mfw);
1662                         this.mainFrameHeight = Integer.parseInt(mfh);
1663
1664                         // 更新間隔
1665                         this.getTimelinePeriod = Integer.parseInt(gtp);
1666                         this.getMentionPeriod = Integer.parseInt(gmp);
1667                         this.getDirectMessagePeriod = Integer.parseInt(gdmp);
1668                         this.getSendDirectMessagePeriod = Integer.parseInt(gsdmp);
1669
1670                         // 通知関係
1671                         this.isNotifyMentionMessage = Boolean.parseBoolean(nm);
1672                         this.isNotifyMentionMessage = Boolean.parseBoolean(ndm);
1673                 } catch (NumberFormatException e) {
1674                         e.printStackTrace();
1675                 }
1676         }
1677
1678         /**
1679          * 設定ファイルを保存する
1680          * 
1681          * @throws IOException
1682          */
1683         public void saveProperties() throws IOException {
1684                 // 設定ファイルディレクトリを作成
1685                 File logDir = new File("./" + PROPERTIES_DIRECTORY);
1686                 if (!logDir.exists()) {
1687                         // ディレクトリが存在しないので作成する
1688                         if (logDir.mkdir() == false) {
1689                                 throw new IOException(PROPERTIES_DIRECTORY
1690                                                 + "ディレクトリを作成できませんでした.");
1691                         }
1692                 }
1693                 if (property == null) {
1694                         this.property = new Properties();
1695                 }
1696
1697                 // 情報更新間隔
1698                 this.property.setProperty("getTimelinePeriod", this.getTimelinePeriod
1699                                 + "");
1700                 this.property.setProperty("getMentionPeriod", this.getMentionPeriod
1701                                 + "");
1702                 this.property.setProperty("getDirectMessagePeriod",
1703                                 this.getDirectMessagePeriod + "");
1704                 this.property.setProperty("getSendDirectMessagePeriod",
1705                                 this.getSendDirectMessagePeriod + "");
1706
1707                 this.property.setProperty("newTableColorRGB", newTableColor.getRGB()
1708                                 + "");
1709                 this.property.setProperty("tlFontName", this.tlFontName);
1710                 this.property.setProperty("tlFontSize", this.tlFontSize + "");
1711                 this.property.setProperty("detailFontName", this.detailFontName);
1712                 this.property.setProperty("detailFontSize", this.detailFontSize + "");
1713                 this.property.setProperty("tableElementHeight", this.tableElementHeight
1714                                 + "");
1715
1716                 // main frame size
1717                 if (this.mainFrame.getExtendedState() == JFrame.NORMAL) {
1718                         this.mainFrameWidth = this.mainFrame.getWidth();
1719                         this.mainFrameHeight = this.mainFrame.getHeight();
1720                 }
1721                 this.property.setProperty("mainFrameWidth", this.mainFrameWidth + "");
1722                 this.property.setProperty("mainFrameHeight", this.mainFrameHeight + "");
1723
1724                 // メッセージ通知を行うか
1725                 this.property.setProperty("notifyMention", this.isNotifyMentionMessage
1726                                 + "");
1727                 this.property.setProperty("notifyDirectMessage",
1728                                 this.isNotifyDirectMessage + "");
1729
1730                 // プロパティのリストを保存
1731                 property.store(new FileOutputStream("./" + PROPERTIES_DIRECTORY + "/"
1732                                 + BASIC_SETTING_FILENAME), null);
1733         }
1734
1735         /**
1736          * newTableColorを取得します。
1737          * 
1738          * @return newTableColor
1739          */
1740         public Color getNewTableColor() {
1741                 return newTableColor;
1742         }
1743
1744         /**
1745          * newTableColorを設定します。
1746          * 
1747          * @param newTableColor
1748          *            newTableColor
1749          */
1750         public void setNewTableColor(Color newTableColor) {
1751                 this.newTableColor = newTableColor;
1752         }
1753
1754         /**
1755          * tlFontNameを取得します。
1756          * 
1757          * @return tlFontName
1758          */
1759         public String getTlFontName() {
1760                 return tlFontName;
1761         }
1762
1763         /**
1764          * tlFontNameを設定します。
1765          * 
1766          * @param tlFontName
1767          *            tlFontName
1768          */
1769         public void setTlFontName(String tlFontName) {
1770                 this.tlFontName = tlFontName;
1771         }
1772
1773         /**
1774          * tlFontSizeを取得します。
1775          * 
1776          * @return tlFontSize
1777          */
1778         public int getTlFontSize() {
1779                 return tlFontSize;
1780         }
1781
1782         /**
1783          * tlFontSizeを設定します。
1784          * 
1785          * @param tlFontSize
1786          *            tlFontSize
1787          */
1788         public void setTlFontSize(int tlFontSize) {
1789                 this.tlFontSize = tlFontSize;
1790         }
1791
1792         /**
1793          * detailFontNameを取得します。
1794          * 
1795          * @return detailFontName
1796          */
1797         public String getDetailFontName() {
1798                 return detailFontName;
1799         }
1800
1801         /**
1802          * detailFontNameを設定します。
1803          * 
1804          * @param detailFontName
1805          *            detailFontName
1806          */
1807         public void setDetailFontName(String detailFontName) {
1808                 this.detailFontName = detailFontName;
1809         }
1810
1811         /**
1812          * detailFontSizeを取得します。
1813          * 
1814          * @return detailFontSize
1815          */
1816         public int getDetailFontSize() {
1817                 return detailFontSize;
1818         }
1819
1820         /**
1821          * detailFontSizeを設定します。
1822          * 
1823          * @param detailFontSize
1824          *            detailFontSize
1825          */
1826         public void setDetailFontSize(int detailFontSize) {
1827                 this.detailFontSize = detailFontSize;
1828         }
1829
1830         /**
1831          * tableElementHeightを取得します。
1832          * 
1833          * @return tableElementHeight
1834          */
1835         public int getTableElementHeight() {
1836                 return tableElementHeight;
1837         }
1838
1839         /**
1840          * tableElementHeightを設定します。
1841          * 
1842          * @param tableElementHeight
1843          *            tableElementHeight
1844          */
1845         public void setTableElementHeight(int tableElementHeight) {
1846                 this.tableElementHeight = tableElementHeight;
1847         }
1848
1849         /**
1850          * @return the getTimelinePeriod
1851          */
1852         public int getGetTimelinePeriod() {
1853                 return getTimelinePeriod;
1854         }
1855
1856         /**
1857          * @param getTimelinePeriod
1858          *            the getTimelinePeriod to set
1859          */
1860         public void setGetTimelinePeriod(int getTimelinePeriod) {
1861                 this.getTimelinePeriod = getTimelinePeriod;
1862         }
1863
1864         /**
1865          * @return the getMentionPeriod
1866          */
1867         public int getGetMentionPeriod() {
1868                 return getMentionPeriod;
1869         }
1870
1871         /**
1872          * @param getMentionPeriod
1873          *            the getMentionPeriod to set
1874          */
1875         public void setGetMentionPeriod(int getMentionPeriod) {
1876                 this.getMentionPeriod = getMentionPeriod;
1877         }
1878
1879         /**
1880          * @return the getDirectMessagePeriod
1881          */
1882         public int getGetDirectMessagePeriod() {
1883                 return getDirectMessagePeriod;
1884         }
1885
1886         /**
1887          * @param getDirectMessagePeriod
1888          *            the getDirectMessagePeriod to set
1889          */
1890         public void setGetDirectMessagePeriod(int getDirectMessagePeriod) {
1891                 this.getDirectMessagePeriod = getDirectMessagePeriod;
1892         }
1893
1894         /**
1895          * @return the getSendDirectMessagePeriod
1896          */
1897         public int getGetSendDirectMessagePeriod() {
1898                 return getSendDirectMessagePeriod;
1899         }
1900
1901         /**
1902          * @param getSendDirectMessagePeriod
1903          *            the getSendDirectMessagePeriod to set
1904          */
1905         public void setGetSendDirectMessagePeriod(int getSendDirectMessagePeriod) {
1906                 this.getSendDirectMessagePeriod = getSendDirectMessagePeriod;
1907         }
1908
1909         /**
1910          * @return the currentStatus
1911          */
1912         public Status getCurrentStatus() {
1913                 return currentStatus;
1914         }
1915
1916         /**
1917          * @param currentStatus
1918          *            the currentStatus to set
1919          */
1920         public void setCurrentStatus(Status currentStatus) {
1921                 this.currentStatus = currentStatus;
1922         }
1923
1924         /**
1925          * 
1926          * @return
1927          */
1928         public Status getReplyStatus() {
1929                 return replyStatus;
1930         }
1931
1932         /**
1933          * 
1934          * @param status
1935          * @return
1936          */
1937         public void setReplyStatus(Status status) {
1938                 this.replyStatus = status;
1939         }
1940 }