OSDN Git Service

今すぐ更新ボタンを押すと,しばらく動きが固まってしまうバグを修正
[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.event.ActionEvent;
9 import java.io.File;
10 import java.io.FileInputStream;
11 import java.io.FileNotFoundException;
12 import java.io.FileOutputStream;
13 import java.io.IOException;
14 import java.io.UnsupportedEncodingException;
15 import java.net.URI;
16 import java.net.URL;
17 import java.net.URLEncoder;
18 import java.text.DateFormat;
19 import java.util.ArrayList;
20 import java.util.List;
21 import java.util.Properties;
22 import java.util.Set;
23 import java.util.TreeSet;
24 import java.util.logging.Level;
25 import java.util.logging.Logger;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 import javax.swing.ImageIcon;
29
30 import javax.swing.JEditorPane;
31 import javax.swing.JFrame;
32 import javax.swing.JLabel;
33 import javax.swing.JOptionPane;
34 import javax.swing.JPanel;
35 import javax.swing.JScrollPane;
36 import javax.swing.JTabbedPane;
37 import javax.swing.JTable;
38 import javax.swing.JTextPane;
39 import javax.swing.table.TableModel;
40 import javax.swing.text.Style;
41 import javax.swing.text.StyleConstants;
42 import javax.swing.text.html.HTMLDocument;
43 import javax.swing.text.html.StyleSheet;
44 import twitter.action.TweetDirectMessageGetter;
45 import twitter.action.TweetGetter;
46 import twitter.action.TweetMentionGetter;
47 import twitter.action.TweetSearchResultGetter;
48 import twitter.action.TweetSendDirectMessageGetter;
49 import twitter.action.TweetTimelineGetter;
50
51 import twitter.gui.component.TweetTabbedTable;
52 import twitter.gui.component.TweetTableModel;
53 import twitter.gui.form.AboutDialog;
54 import twitter.gui.form.AccountDialog;
55 import twitter.gui.form.ConfigurationDialog;
56 import twitter.gui.form.DirectMessageDialog;
57 import twitter.gui.form.KeywordSearchDialog;
58 import twitter.manage.TweetConfiguration;
59 import twitter.manage.TweetManager;
60 import twitter.task.ExistTimerIDException;
61 import twitter.task.TimerID;
62 import twitter.task.TweetTaskException;
63 import twitter.task.TweetTaskManager;
64 import twitter.task.TweetUpdateTask;
65 import twitter4j.Status;
66 import twitter4j.TwitterException;
67
68 /**
69  * GUIのアクション部分
70  * 
71  * @author nishio
72  * 
73  */
74 public class TweetMainAction {
75
76     // 基本設定を保存するファイル名
77     public static final String BASIC_SETTING_FILENAME = TweetConfiguration.BASIC_SETTING_FILENAME;
78     // httpのパターン
79     private static final Pattern convURLLinkPtn = Pattern.compile(
80             "(http://|https://){1}[\\w\\.\\-/:\\#\\?\\=\\&\\;\\%\\~\\+]+",
81             Pattern.CASE_INSENSITIVE);
82     // default char encoding
83     private static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
84     // 設定ファイルを保存するディレクトリ名
85     public static final String PROPERTIES_DIRECTORY = "properties";
86     // search twitterのクエリ
87     private static final String SEARCH_QUERY = "search?q=";
88     // search twitterのURL
89     private static final String SEARCH_TWITTER_URL = "http://search.twitter.com/";
90     // Direct Messageタブに表示する文字
91     public static final String TAB_DIRECT_MESSAGE_STRING = "Message";
92     // Mentionタブに表示する文字
93     public static final String TAB_MENTION_STRING = "Mention";
94     // タイムラインタブに表示する文字
95     public static final String TAB_TIMELINE_STRING = "Timeline";
96     //Send Direct Messageタブに表示する文字
97     public static final String TAB_SEND_DIRECT_MESSAGE_STRING = "Send";
98     // テーブルのデータ量が以下の値を超えたら古いデータから削除
99     private static final int TABLE_ELEMENT_MAX_SIZE = 200;
100     // twitterの公式URL
101     private static final String TWITTER_URL = "http://twitter.com/";
102     // 基本設定用ダイアログ
103     private ConfigurationDialog configurationDialog = null;
104     // 現在選択しているStatus情報
105     private Status currentStatus = null;
106     // 詳細情報パネル
107     private JPanel detailInfoPanel = null;
108     // ダイレクトメッセージ送信用ダイアログ
109     private DirectMessageDialog directMessageDialog = null;
110     //Twitter全体からキーワード検索ダイアログ
111     private KeywordSearchDialog keywordSearchDialog = null;
112     // 新しく取得した部分のテーブルカラー
113     private Color newTableColor = new Color(224, 255, 255);
114     // TLのフォント名
115     private String tlFontName = "Takao Pゴシック";
116     // TLのフォントサイズ
117     private int tlFontSize = 13;
118     // 詳細情報のフォント名
119     private String detailFontName = "Takao Pゴシック";
120     // 詳細情報のフォントサイズ
121     private int detailFontSize = 13;
122     // テーブル1要素の高さ
123     private int tableElementHeight = 50;
124     //メインフレームの幅
125     private int mainFrameWidth = 729;
126     //メインフレームの高さ
127     private int mainFrameHeight = 629;
128     // MainFrame
129     private JFrame mainFrame = null;
130     // 設定
131     private Properties property = null;
132     // 現在テーブルで選択しているユーザ画像のURL
133     private URL selectedUserImageURL = null;
134     // 現在テーブルで選択しているユーザの名前
135     private String selectedUsername = null;
136     // ステータス表示ラベル
137     private JLabel statusBarLabel = null;
138     // 自分がつぶやきをかく領域
139     private JTextPane tweetBoxPane = null;
140     //自分がつぶやきを書く領域のスクロールペーン
141     private JScrollPane tweetBoxScrollPane = null;
142     // tweet情報などを表示するタブ
143     private JTabbedPane tweetMainTab = null;
144     // Tweet管理
145     private TweetManager tweetManager = null;
146     // tweetを表示するTextPane
147     private JEditorPane tweetMessageBox = null;
148     // つぶやくことができる文字数を表示するラベル
149     private JLabel tweetMessageCountLabel = null;
150     private int uncheckedDirectMessageCount = 0;
151     private int uncheckedMentionTweetCount = 0;
152
153     //Tweetの詳細情報を表示する部分
154     private JLabel userImageLabel = null;
155     private JLabel userNameLabel = null;
156     private JLabel updateTimeLabel = null;
157     private JLabel followerLabel = null;
158     private JLabel followingLabel = null;
159     private JLabel locationLabel = null;
160     private JEditorPane clientNameLabel = null;
161     private JLabel updateLabel = null;
162     private JEditorPane userIntroBox = null;
163     private JEditorPane userWebBox = null;
164
165     // 新しく取得したtweetでまだ参照していない数
166     private int uncheckedTimelineTweetCount = 0;
167     private AboutDialog aboutDialog = null;
168     //アカウント情報表示ダイアログ
169     private AccountDialog accountDialog;
170     //ツイートを表示するテーブル管理
171     private List<TweetTabbedTable> tweetTabbedTableList = new ArrayList<TweetTabbedTable>();
172     //ツイートテーブルの情報を一定間隔で更新するクラスを作成
173     private TweetTaskManager tweetTaskManager = new TweetTaskManager();
174
175     //情報更新間隔[sec]
176     private int getTimelinePeriod = 60;
177     private int getMentionPeriod = 60 * 3;
178     private int getDirectMessagePeriod = 60 * 15;
179     private int getSendDirectMessagePeriod = 60 * 30;
180
181     /**
182      *
183      * @param mainFrame
184      * @param tweetManager
185      * @param statusBarLabel
186      * @param tweetTableModel
187      * @param mentionTableModel
188      * @param directMessageTableModel
189      * @param sendDirectMessageTableModel
190      * @param mainTweetTable
191      * @param mentionTable
192      * @param directMessageTable
193      * @param sendDirectMessageTable
194      * @param tweetBoxPane
195      * @param tweetBoxScrollPane
196      * @param tweetMessageCountLabel
197      * @param detailInfoPanel
198      * @param tweetMainTab
199      * @param tweetMessageBox
200      * @param userImageLabel
201      * @param userNameLabel
202      * @param updateTimeLabel
203      * @param followerLabel
204      * @param followingLabel
205      * @param locationLabel
206      * @param clientNameLabel
207      * @param updateLabel
208      * @param userIntroBox
209      * @param userWebBox
210      */
211     public TweetMainAction(JFrame mainFrame,
212             TweetManager tweetManager,
213             JLabel statusBarLabel,
214             JTextPane tweetBoxPane,
215             JScrollPane tweetBoxScrollPane,
216             JLabel tweetMessageCountLabel,
217             JPanel detailInfoPanel,
218             JTabbedPane tweetMainTab,
219             JEditorPane tweetMessageBox,
220             JLabel userImageLabel,
221             JLabel userNameLabel,
222             JLabel updateTimeLabel,
223             JLabel followerLabel,
224             JLabel followingLabel,
225             JLabel locationLabel,
226             JEditorPane clientNameLabel,
227             JLabel updateLabel,
228             JEditorPane userIntroBox,
229             JEditorPane userWebBox) {
230         this.mainFrame = mainFrame;
231         this.tweetManager = tweetManager;
232         this.statusBarLabel = statusBarLabel;
233         this.tweetBoxPane = tweetBoxPane;
234         this.tweetMessageCountLabel = tweetMessageCountLabel;
235         this.detailInfoPanel = detailInfoPanel;
236         this.tweetMainTab = tweetMainTab;
237         this.tweetMessageBox = tweetMessageBox;
238         this.tweetBoxScrollPane = tweetBoxScrollPane;
239
240         //詳細情報部分
241         this.userImageLabel = userImageLabel;
242         this.userNameLabel = userNameLabel;
243         this.updateTimeLabel = updateTimeLabel;
244         this.userIntroBox = userIntroBox;
245         this.followerLabel = followerLabel;
246         this.followingLabel = followingLabel;
247         this.locationLabel = locationLabel;
248         this.userWebBox = userWebBox;
249         this.clientNameLabel = clientNameLabel;
250         this.updateLabel = updateLabel;
251
252         // 設定ファイルの読み込み
253         try {
254             loadProperties();
255         } catch (FileNotFoundException e) {
256             e.printStackTrace();
257         } catch (IOException e) {
258             e.printStackTrace();
259         }
260         // フォント情報を反映
261         updateFontInformationToComponent();
262
263         //フレームの大きさを反映
264         mainFrame.setSize(this.mainFrameWidth, this.mainFrameHeight);
265         mainFrame.setPreferredSize(new Dimension(this.mainFrameWidth, this.mainFrameHeight));
266     }
267
268     /**
269      * Timeline, Mention , DM, SendDMの情報更新間隔を取得し,その情報をテーブルに反映
270      */
271     public void updatePeriodInformationToComponent() {
272         //すべてのテーブルにフォント情報を反映
273         for (TweetTabbedTable t : this.tweetTabbedTableList) {
274             String timerID = t.getTimerID();
275             if( timerID.equals( TimerID.createTimelineID() ) ) {
276                 //TLの周期情報更新
277                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetTimelinePeriod(), false);
278             }else if( timerID.equals( TimerID.createMentionID() ) ) {
279                 //Mentionの周期情報更新
280                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetMentionPeriod(), false );
281             }else if( timerID.equals( TimerID.createDirectMessageID() ) ) {
282                 //DMの周期情報更新
283                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetDirectMessagePeriod(), false);
284             }else if( timerID.equals( TimerID.createSendDirectMessageID() ) ) {
285                 //SendDMの周期情報更新
286                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetSendDirectMessagePeriod(), false);
287             }
288         }
289     }
290
291     // フォント情報をコンポーネントに反映
292     public void updateFontInformationToComponent() {
293         try {
294             Font tlFont = null;
295             if (this.tlFontName != null) {
296                 tlFont = new Font(this.tlFontName, Font.PLAIN, this.tlFontSize);
297             }
298             Font detailFont = null;
299             if (this.detailFontName != null) {
300                 detailFont = new Font(this.detailFontName, Font.PLAIN,
301                         this.detailFontSize);
302             }
303
304             //すべてのテーブルにフォント情報を反映
305             for( TweetTabbedTable t : this.tweetTabbedTableList) {
306                 t.getTable().setFont(tlFont);
307             }
308
309             // tweetメッセージボックスのフォントはhtmlレベルで変更する必要がある
310             this.tweetMessageBox.setFont(detailFont);
311             // htmlフォント変更
312             HTMLDocument doc = (HTMLDocument) this.tweetMessageBox.getDocument();
313             StyleSheet[] style = doc.getStyleSheet().getStyleSheets();
314             for (int i = style.length - 1; i >= 0; i--) {
315                 Style body = style[i].getStyle("body");
316                 if (body != null) {
317                     StyleConstants.setFontFamily(body, detailFont.getFontName());
318                     StyleConstants.setFontSize(body, detailFont.getSize());
319                 }
320             }
321
322         } catch (Exception e) {
323             e.printStackTrace();
324         }
325     }
326
327     /**
328      * 新しいタブを追加
329      * @param timerID TimerIDクラスで生成したタイマーID
330      * @param period 情報更新間隔[sec]
331      * @param tweetGetter 実行するアクション
332      * @param tabTitle 追加するタブのタイトル
333      */
334     public void actionAddTab(String timerID, int period, TweetGetter tweetGetter, String tabTitle) {
335         int numOfTab = this.tweetTabbedTableList.size();
336         //すでに追加されているタブの数
337
338         //周期的に情報を更新する
339         if( period > 0 ) {
340             try {
341                 //テーブルを作成
342                 final TweetTabbedTable table = new TweetTabbedTable(tweetGetter, tabTitle,
343                         this.tweetMainTab, numOfTab,
344                         this.tableElementHeight, this.tweetManager,
345                         this, newTableColor, tableElementHeight, timerID);
346
347                 this.tweetTaskManager.addTask(timerID, new TweetUpdateTask() {
348
349                     @Override
350                     public void runTask() throws TweetTaskException {
351                         //ツイート情報を一定間隔で更新
352                         table.updateTweetTable();
353                     }
354                 });
355                 //更新開始
356                 this.tweetTaskManager.startTask(timerID, period * 1000L);
357
358                 //タブにテーブルを追加
359                 table.addTableToTab();
360                 //タブリストに追加
361                 this.tweetTabbedTableList.add(table);
362                 //searchTable.updateTweetTable();
363             } catch (TweetTaskException ex) {
364                 Logger.getLogger(TweetMainAction.class.getName()).log(Level.SEVERE, null, ex);
365             }
366         }
367
368         //フォント情報を更新
369         this.updateFontInformationToComponent();
370         //テーブルの高さをすべて更新
371         this.updateTableHeight( this.getTableElementHeight() );
372     }
373
374     /**
375      * mentionタブを追加する
376      * @param period 情報更新間隔[sec]
377      */
378     public void actionAddMentionTab(int period) {
379         TimerID timerID = TimerID.getInstance();
380         String id = TimerID.createMentionID();
381         try {
382             //既にIDが存在していたらここで例外発生
383             timerID.addID(id);
384             //検索結果を表示するタブを生成
385             actionAddTab(id, period, new TweetMentionGetter(tweetManager),
386                     TweetMainAction.TAB_MENTION_STRING);
387         } catch (ExistTimerIDException ex) {
388             JOptionPane.showMessageDialog(null, "そのタブは既に存在しています",
389                     "Error", JOptionPane.ERROR_MESSAGE);
390         }
391     }
392
393     /**
394      * timelineタブを追加する
395      * @param period[sec]
396      */
397     public void actionAddTimelineTab(int period) {
398         TimerID timerID = TimerID.getInstance();
399         String id = TimerID.createTimelineID();
400         try {
401             //既にIDが存在していたらここで例外発生
402             timerID.addID(id);
403             //検索結果を表示するタブを生成
404             actionAddTab(id, period, new TweetTimelineGetter(tweetManager),
405                     TweetMainAction.TAB_TIMELINE_STRING);
406         } catch (ExistTimerIDException ex) {
407             JOptionPane.showMessageDialog(null, "そのタブは既に存在しています",
408                     "Error", JOptionPane.ERROR_MESSAGE);
409         }
410     }
411
412     /**
413      * ダイレクトメッセージタブを追加する
414      * @param period 更新間隔[sec]
415      */
416     public void actionAddDirectMessageTab(int period) {
417         TimerID timerID = TimerID.getInstance();
418         String id = TimerID.createDirectMessageID();
419         try {
420             //既にIDが存在していたらここで例外発生
421             timerID.addID(id);
422             //検索結果を表示するタブを生成
423             actionAddTab(id, period, new TweetDirectMessageGetter(tweetManager),
424                     TweetMainAction.TAB_DIRECT_MESSAGE_STRING);
425         } catch (ExistTimerIDException ex) {
426             JOptionPane.showMessageDialog(null, "そのタブは既に存在しています",
427                     "Error", JOptionPane.ERROR_MESSAGE);
428         }
429     }
430
431     /**
432      * SendDMタブを追加する
433      * @param period
434      */
435     public void actionAddSendDirectMessageTab(int period) {
436         TimerID timerID = TimerID.getInstance();
437         String id = TimerID.createSendDirectMessageID();
438         try {
439             //既にIDが存在していたらここで例外発生
440             timerID.addID(id);
441             //検索結果を表示するタブを生成
442             actionAddTab(id, period, new TweetSendDirectMessageGetter(tweetManager),
443                     TweetMainAction.TAB_SEND_DIRECT_MESSAGE_STRING);
444         } catch (ExistTimerIDException ex) {
445             JOptionPane.showMessageDialog(null, "そのタブは既に存在しています",
446                     "Error", JOptionPane.ERROR_MESSAGE);
447         }
448     }
449
450
451     /**
452      * ツイート検索結果を表示するタブを新しく追加
453      * @param searchWord
454      * @param period 更新周期[sec] 0以下の場合は更新しない
455      */
456     public void actionAddNewSearchResultTab(String searchWord, int period) {
457         TimerID timerID = TimerID.getInstance();
458         String id = TimerID.createSearchTimerID(searchWord);
459         try {
460             //既にIDが存在していたらここで例外発生
461             timerID.addID(id);
462             //検索結果を表示するタブを生成
463             actionAddTab(id, period, new TweetSearchResultGetter(this.tweetManager, searchWord), searchWord);
464         } catch (ExistTimerIDException ex) {
465             JOptionPane.showMessageDialog(null, "そのタブは既に存在しています",
466                     "Error", JOptionPane.ERROR_MESSAGE);
467         }
468     }
469
470     /**
471      * 基本設定ダイアログを開く
472      */
473     public void actionBasicSettingDialog() {
474         // ダイレクトメッセージ送信用ダイアログを開く
475         Point loc = getConfigurationDialog().getLocation();
476         loc.translate(20, 20);
477         ConfigurationDialog dialog = getConfigurationDialog();
478         dialog.setLocation(loc);
479         dialog.setVisible(true);
480     }
481
482     /**
483      * 選択したtweetをRT
484      */
485     public void actionCopySelectedStatusToTweetBoxPane() {
486         // コメントしたユーザ名
487         String username = this.currentStatus.getUser().getScreenName();
488         // コメント
489         String message = this.currentStatus.getText();
490         this.tweetBoxPane.setText("RT: @" + username + ": " + message);
491     }
492
493     /**
494      * 詳細情報表示ボタンを押した時の動作
495      *
496      * @param e
497      */
498     public void actionDetailInfoButton(ActionEvent e) {
499         if (detailInfoPanel.isVisible()) {
500             detailInfoPanel.setVisible(false);
501         } else {
502             detailInfoPanel.setVisible(true);
503         }
504     }
505
506     /**
507      * 書き込みメッセージボックスの表示ONOFFボタンを押した時の動作
508      * @param e
509      */
510     public void actionShowTweetboxButton(ActionEvent e) {
511         if( this.tweetBoxScrollPane.isVisible() ) {
512             this.tweetBoxScrollPane.setVisible(false);
513         }else {
514             this.tweetBoxScrollPane.setVisible(true);
515         }
516     }
517
518     /**
519      * 選択しているタブを削除
520      */
521     public void actionRemoveFocusedTabbedTable() {
522         int selected = this.tweetMainTab.getSelectedIndex();
523         int deleteTabIndex = selected;
524         
525         if( deleteTabIndex >= 0 ) {
526             //タブを削除
527             this.tweetMainTab.remove(deleteTabIndex);
528             int tabSetNum = this.tweetTabbedTableList.get(deleteTabIndex).getTabSetNum();
529             //タブのタイマーID
530             String timerID = this.tweetTabbedTableList.get(deleteTabIndex).getTimerID();
531
532             //削除
533             this.tweetTabbedTableList.remove(deleteTabIndex);
534
535             //削除した分,既存のタブ番号を1つずつずらさなければならない
536             int tabNum = this.tweetTabbedTableList.size();
537             for(int i = tabSetNum; i < tabNum; i++) {
538                 TweetTabbedTable table = this.tweetTabbedTableList.get( i );
539                 table.setTabSetNum( table.getTabSetNum() - 1);
540             }
541
542             //自動更新しているタブを削除
543             this.tweetTaskManager.shutdownTask( timerID );
544             //ID削除
545             TimerID idManager = TimerID.getInstance();
546             idManager.removeID(timerID);
547         }
548     }
549
550     /**
551      * 終了ボタンを押した時の動作
552      *
553      * @param e
554      */
555     public void actionExitButton(ActionEvent e) {
556         System.exit(0);
557     }
558
559     /**
560      * 選択した発言をブラウザで開く
561      */
562     public void actionOpenStatusURL() {
563         try {
564             // ユーザ名
565             String userName = this.currentStatus.getUser().getScreenName();
566             // 発言のstatusID
567             long statusID = this.currentStatus.getId();
568             Desktop.getDesktop().browse(
569                     new URI(TWITTER_URL + userName + "/statuses/" + statusID));
570         } catch (Exception ex) {
571             ex.printStackTrace();
572             JOptionPane.showMessageDialog(null, "エラーによりブラウザを起動できませんでした.",
573                     "Error", JOptionPane.ERROR_MESSAGE);
574         }
575     }
576
577     /**
578      * 選択したユーザ情報をブラウザで開く
579      */
580     public void actionOpenUserURL() {
581         try {
582             String userName = this.currentStatus.getUser().getScreenName();
583             Desktop.getDesktop().browse(new URI(TWITTER_URL + userName));
584         } catch (Exception ex) {
585             ex.printStackTrace();
586             JOptionPane.showMessageDialog(null, "エラーによりブラウザを起動できませんでした.",
587                     "Error", JOptionPane.ERROR_MESSAGE);
588         }
589     }
590
591     /**
592      * Tweet取得時間情報を更新
593      */
594     public void actionRefreshTime() {
595         //タブに存在する時間情報を更新
596         for(TweetTabbedTable t : this.tweetTabbedTableList ) {
597             TweetTableModel model = t.getModel();
598             if( model != null ) {
599                 model.refreshTime();
600             }
601         }
602     }
603
604     /**
605      * TweetMessageBox内にある#ハッシュタグ の部分をa hrefリンクに変換
606      *
607      * @param message
608      */
609     public String actionReplaceTweetMessageBoxHashTab(String message) {
610         // #で始まる情報
611         Pattern userPtn = Pattern.compile("#[0-9A-Z_]+",
612                 Pattern.CASE_INSENSITIVE);
613         Matcher matcher = userPtn.matcher(message);
614         /*
615          * if( matcher.matches() ) { matcher. }
616          */
617         // #で始まる情報一覧を抜き出す
618         Set<String> findList = new TreeSet<String>();
619         while (matcher.find()) {
620             findList.add(matcher.group(0));
621         }
622         // 指定した情報をすべてリンクへ変更
623         for (String f : findList) {
624             try {
625                 message = message.replaceAll(f, "<a href=\""
626                         + SEARCH_TWITTER_URL + SEARCH_QUERY
627                         + URLEncoder.encode(f, DEFAULT_CHARACTER_ENCODING)
628                         + "\">" + f + "</a>");
629             } catch (UnsupportedEncodingException e) {
630                 e.printStackTrace();
631             }
632         }
633         return message;
634     }
635
636     /**
637      * TweetMessageBox内にあるリンクをa hrefリンクに変換
638      *
639      * @param message
640      */
641     public String actionReplaceTweetMessageBoxURLLink(String message) {
642         Matcher matcher = convURLLinkPtn.matcher(message);
643         return matcher.replaceAll("<a href=\"$0\">$0</a>");
644     }
645
646     /**
647      * @ユーザ名の部分をa hrefリンクに変換
648      * @param message
649      */
650     public String actionReplaceTweetMessageBoxUserInfo(String message) {
651         // @で始まる情報
652         Pattern userPtn = Pattern.compile("@[0-9A-Z_]+",
653                 Pattern.CASE_INSENSITIVE);
654         Matcher matcher = userPtn.matcher(message);
655         // @で始まるユーザ名一覧を抜き出す
656         Set<String> findList = new TreeSet<String>();
657         while (matcher.find()) {
658             findList.add(matcher.group(0));
659         }
660         // 指定したユーザ名をすべてリンクへ変更
661         for (String f : findList) {
662             message = message.replaceAll(f, "<a href=\"" + TWITTER_URL
663                     + f.substring(1) + "\">" + f + "</a>");
664         }
665         return message;
666     }
667
668     /**
669      * まだ見ていないdirectMessage数を0にする
670      */
671     public void actionResetUncheckedDirectMessageCount() {
672         uncheckedDirectMessageCount = 0;
673         tweetMainTab.setTitleAt(2, TAB_DIRECT_MESSAGE_STRING);
674     }
675
676     /**
677      * まだ見ていないmention数を0にする
678      */
679     public void actionResetUncheckedMentionTweetCount() {
680         uncheckedMentionTweetCount = 0;
681         tweetMainTab.setTitleAt(1, TAB_MENTION_STRING);
682     }
683
684     /**
685      * まだ見ていないtweet数を0にする
686      */
687     public void actionResetUncheckedTimelineTweetCount() {
688         uncheckedTimelineTweetCount = 0;
689         tweetMainTab.setTitleAt(0, TAB_TIMELINE_STRING);
690     }
691
692     /**
693      * 現在選択しているステータスを公式Retweet
694      */
695     public void actionRetweet() {
696         // 選択しているtweetのstatus id
697         long statusID = this.currentStatus.getId();
698         // コメントしたユーザ名
699         String username = this.currentStatus.getUser().getScreenName();
700         // コメント
701         String message = this.currentStatus.getText();
702         // 発言が長すぎる場合,後半をカット
703         if (message.length() > 30) {
704             message = message.substring(0, 30) + " ...(以下略)";
705         }
706         // Retweetしていいかどうかの確認
707         int ret = JOptionPane.showConfirmDialog(mainFrame, username + " さんの発言:"
708                 + message + "\nをRetweetしますか?", "Retweetの確認",
709                 JOptionPane.YES_NO_OPTION);
710         if (ret == JOptionPane.YES_OPTION) {
711             try {
712                 // Retweetを行う
713                 this.tweetManager.retweet(statusID);
714             } catch (TwitterException e) {
715                 JOptionPane.showMessageDialog(null, "エラーによりRetweetできませんでした.",
716                         "Retweet Error", JOptionPane.ERROR_MESSAGE);
717             }
718         }
719     }
720
721     /**
722      * テーブルの高さを更新
723      *
724      * @param height
725      */
726     public void updateTableHeight(int height) {
727         this.tableElementHeight = height;
728         for( TweetTabbedTable t : this.tweetTabbedTableList) {
729             t.getTable().setRowHeight(tableElementHeight);
730         }
731     }
732
733     /**
734      * ダイレクトメッセージダイアログ表示
735      */
736     public void actionShowDirectMessageDialog() {
737         // ダイレクトメッセージ送信用ダイアログを開く
738         Point loc = getDirectMessageDialog().getLocation();
739         //loc.translate(20, 20);
740         DirectMessageDialog dialog = getDirectMessageDialog();
741         //dialog.setLocation(loc);
742         dialog.setLocationRelativeTo(null);
743         dialog.setVisible(true);
744         dialog.setUserInformation(this.selectedUsername,
745                 this.selectedUserImageURL, this.tweetManager);
746     }
747
748     /**
749      * Aboutダイアログを表示
750      */
751     public void actionShowAboutDialog() {
752         Point loc = getDirectMessageDialog().getLocation();
753         //loc.translate(20, 20);
754         AboutDialog dialog = getAboutDialog();
755         dialog.setLocationRelativeTo(null);
756         //dialog.setLocation(loc);
757         dialog.setVisible(true);
758     }
759
760     /**
761      * アカウントダイアログを表示
762      */
763     public void actionShowAccountDialog() {
764         Point loc = getDirectMessageDialog().getLocation();
765         //loc.translate(20, 20);
766         AccountDialog dialog = getAccountDialog();
767         dialog.setLocationRelativeTo(null);
768        // dialog.setLocation(loc);
769         dialog.setVisible(true);
770     }
771
772     /**
773      * Twitter全体からキーワード検索ダイアログを表示
774      */
775     public void actionShowKeywordSearchDialog() {
776         Point loc = getDirectMessageDialog().getLocation();
777         KeywordSearchDialog dialog = getKeywordSearchDialog();
778         dialog.setLocationRelativeTo(null);
779         dialog.setVisible(true);
780     }
781
782     /**
783      * tweetBoxPaneに書かれた文字をつぶやく
784      */
785     public void actionTweet() {
786         tweetManager.tweet(tweetBoxPane.getText());
787         tweetBoxPane.setText(""); // テキストをクリア
788     }
789
790     /**
791      * Tweet情報を更新
792      *
793      * @param e
794      */
795     public void actionUpdateButton(java.awt.event.ActionEvent e) {
796         try {
797             //タブ上に存在するテーブルの情報を更新
798             for(TweetTabbedTable t : this.tweetTabbedTableList ) {
799                 String timerID = t.getTimerID();
800                 this.tweetTaskManager.resetTask(timerID, true);
801             }
802
803             // API残り回数を取得
804             int remainingHits = tweetManager.getRateLimitStatus().getRemainingHits();
805             // 取得したコメント数をステータスバーに表示
806             information("新しいツイートを取得しました. (APIリクエスト残数は" + remainingHits
807                     + "回です)");
808         } catch (Exception e1) {
809             e1.printStackTrace();
810         }
811     }
812
813     /**
814      * つぶやける残り文字数の更新
815      *
816      * @param e
817      */
818     public void actionUpdateTweetMessageCount() {
819         int len = 140 - (tweetBoxPane.getText().length());
820         if (len < 0) {
821             len = 0;
822         }
823         tweetMessageCountLabel.setText(len + "");
824     }
825
826     /**
827      * 基本設定用ダイアログを取得
828      *
829      * @return
830      */
831     public ConfigurationDialog getConfigurationDialog() {
832         if (configurationDialog == null) {
833             configurationDialog = new ConfigurationDialog(mainFrame, this);
834         }
835         return configurationDialog;
836     }
837
838     /**
839      * twitter全体からキーワード検索ダイアログを表示
840      * @return
841      */
842     public KeywordSearchDialog getKeywordSearchDialog() {
843         if( keywordSearchDialog == null ) {
844             keywordSearchDialog = new KeywordSearchDialog(mainFrame, true, this);
845         }
846         return keywordSearchDialog;
847     }
848
849     /**
850      * アカウント情報設定ダイアログを取得
851      * @return
852      */
853     public AccountDialog getAccountDialog() {
854         if( accountDialog == null ) {
855             accountDialog = new AccountDialog(mainFrame, true, tweetManager, this);
856         }
857         return accountDialog;
858     }
859
860     /**
861      * ダイレクトメッセージ送信用ダイアログを取得
862      *
863      * @return
864      */
865     public DirectMessageDialog getDirectMessageDialog() {
866         if (directMessageDialog == null) {
867             directMessageDialog = new DirectMessageDialog(mainFrame);
868             directMessageDialog.setTitle("ダイレクトメッセージを送信");
869         }
870         return directMessageDialog;
871     }
872
873     /**
874      * テーブルで選択したツイートを詳細情報としてセット
875      * @param table
876      */
877     public void setDetailInformationFromTable(JTable table) {
878         int sc = table.getSelectedRowCount();
879         String infoMessage = "";
880
881         if (sc == 1) {
882             Status st = getTweetTableInformation(table, table.getModel());
883             infoMessage = st.getText();
884             // tweetMessageBox内のURLをhtmlリンクへ変換
885             infoMessage = actionReplaceTweetMessageBoxURLLink(infoMessage);
886             // @ユーザ情報をhtmlリンクへ変換
887             infoMessage = actionReplaceTweetMessageBoxUserInfo(infoMessage);
888             // #ハッシュタグ情報をhtmlリンクへ変換
889             infoMessage = actionReplaceTweetMessageBoxHashTab(infoMessage);
890             // 詳細情報にテーブルで選択した人のツイート情報を表示
891             tweetMessageBox.setText(infoMessage);
892             // user icon
893             userImageLabel.setIcon(new ImageIcon(st.getUser().getProfileImageURL()));
894             // user name and id
895             userNameLabel.setText(st.getUser().getName()
896                     + " / " + st.getUser().getScreenName());
897             // update Time
898             updateTimeLabel.setText(DateFormat.getInstance().format( st.getCreatedAt() ));
899             // ユーザ自己紹介文
900             userIntroBox.setText(st.getUser().getDescription());
901             // フォローされている数
902             followerLabel.setText(st.getUser().getFollowersCount()
903                     + "");
904             // フォローしている数
905             followingLabel.setText(st.getUser().getFriendsCount()
906                     + "");
907             // 現在地
908             locationLabel.setText(st.getUser().getLocation());
909             // Web
910             if (st.getUser().getURL() != null) {
911                 userWebBox.setText("<a href=\""
912                         + st.getUser().getURL() + "\">"
913                         + st.getUser().getScreenName()
914                         + "のWebを開く" + "</a>");
915             } else {
916                 userWebBox.setText("");
917             }
918             // client
919             clientNameLabel.setText(" via " + st.getSource());
920             // Update
921             updateLabel.setText(st.getUser().getStatusesCount()
922                     + "");
923         }
924     }
925
926     /**
927      *
928      * @return
929      */
930     public AboutDialog getAboutDialog() {
931         if (aboutDialog == null) {
932             aboutDialog = new AboutDialog(mainFrame, true);
933         }
934         return aboutDialog;
935     }
936
937     /**
938      * テーブルで選択した場所のTweet情報を取得
939      *
940      * @return
941      */
942     public Status getTweetTableInformation(JTable table, TableModel model) {
943         int index = table.convertRowIndexToModel(table.getSelectedRow());
944         Status status = null;
945         if (model instanceof TweetTableModel) {
946             status = ((TweetTableModel) model).getTweetStatus(index);
947             // 現在選択したセルのユーザ名を保存しておく
948             this.selectedUsername = status.getUser().getScreenName();
949             // 現在選択したセルのユーザURLを保存しておく
950             this.selectedUserImageURL = status.getUser().getProfileImageURL();
951             // 選択したStatusを保存しておく
952             this.currentStatus = status;
953         }
954         return status;
955     }
956
957     /**
958      * ステータスバーに情報を表示する
959      *
960      * @param message
961      */
962     public void information(String message) {
963         statusBarLabel.setText(message);
964     }
965
966     /**
967      * 設定ファイルを読み込む
968      *
969      * @throws IOException
970      * @throws FileNotFoundException
971      */
972     public void loadProperties() throws FileNotFoundException, IOException {
973         if (property == null) {
974             this.property = new Properties();
975         }
976         property.load(new FileInputStream("./" + PROPERTIES_DIRECTORY + "/"
977                 + BASIC_SETTING_FILENAME));
978         // 設定読み込み
979         String gtp = this.property.getProperty("getTimelinePeriod");
980         String gmp = this.property.getProperty("getMentionPeriod");
981         String gdmp = this.property.getProperty("getDirectMessagePeriod");
982         String gsdmp = this.property.getProperty("getSendDirectMessagePeriod");
983
984         String ntrgb = this.property.getProperty("newTableColorRGB");
985
986         this.tlFontName = this.property.getProperty("tlFontName");
987         this.detailFontName = this.property.getProperty("detailFontName");
988
989         String tfs = this.property.getProperty("tlFontSize");
990         String dfs = this.property.getProperty("detailFontSize");
991         String teh = this.property.getProperty("tableElementHeight");
992
993         //メインフレームの大きさ
994         String mfw = this.property.getProperty("mainFrameWidth");
995         String mfh = this.property.getProperty("mainFrameHeight");
996
997         try {
998             this.newTableColor = new Color(Integer.parseInt(ntrgb));
999             this.tlFontSize = Integer.parseInt(tfs);
1000             this.detailFontSize = Integer.parseInt(dfs);
1001             this.tableElementHeight = Integer.parseInt(teh);
1002             this.mainFrameWidth = Integer.parseInt(mfw);
1003             this.mainFrameHeight = Integer.parseInt(mfh);
1004
1005             //更新間隔
1006             this.getTimelinePeriod = Integer.parseInt(gtp);
1007             this.getMentionPeriod = Integer.parseInt(gmp);
1008             this.getDirectMessagePeriod = Integer.parseInt(gdmp);
1009             this.getSendDirectMessagePeriod = Integer.parseInt(gsdmp);
1010         } catch (NumberFormatException e) {
1011             e.printStackTrace();
1012         }
1013     }
1014
1015     /**
1016      * 設定ファイルを保存する
1017      *
1018      * @throws IOException
1019      */
1020     public void saveProperties() throws IOException {
1021         // 設定ファイルディレクトリを作成
1022         File logDir = new File("./" + PROPERTIES_DIRECTORY);
1023         if (!logDir.exists()) {
1024             // ディレクトリが存在しないので作成する
1025             if (logDir.mkdir() == false) {
1026                 throw new IOException(PROPERTIES_DIRECTORY
1027                         + "ディレクトリを作成できませんでした.");
1028             }
1029         }
1030         if (property == null) {
1031             this.property = new Properties();
1032         }
1033
1034         //情報更新間隔
1035         this.property.setProperty("getTimelinePeriod", this.getTimelinePeriod + "");
1036         this.property.setProperty("getMentionPeriod", this.getMentionPeriod + "");
1037         this.property.setProperty("getDirectMessagePeriod", this.getDirectMessagePeriod + "");
1038         this.property.setProperty("getSendDirectMessagePeriod", this.getSendDirectMessagePeriod + "");
1039
1040         this.property.setProperty("newTableColorRGB", newTableColor.getRGB()
1041                 + "");
1042         this.property.setProperty("tlFontName", this.tlFontName);
1043         this.property.setProperty("tlFontSize", this.tlFontSize + "");
1044         this.property.setProperty("detailFontName", this.detailFontName);
1045         this.property.setProperty("detailFontSize", this.detailFontSize + "");
1046         this.property.setProperty("tableElementHeight", this.tableElementHeight
1047                 + "");
1048
1049         //main frame size
1050         if( this.mainFrame.getExtendedState() == JFrame.NORMAL ) {
1051             this.mainFrameWidth = this.mainFrame.getWidth();
1052             this.mainFrameHeight = this.mainFrame.getHeight();
1053         }
1054         this.property.setProperty("mainFrameWidth", this.mainFrameWidth + "");
1055         this.property.setProperty("mainFrameHeight", this.mainFrameHeight + "");
1056         // プロパティのリストを保存
1057         property.store(new FileOutputStream("./" + PROPERTIES_DIRECTORY + "/"
1058                 + BASIC_SETTING_FILENAME), null);
1059     }
1060
1061     /**
1062      * newTableColorを取得します。
1063      *
1064      * @return newTableColor
1065      */
1066     public Color getNewTableColor() {
1067         return newTableColor;
1068     }
1069
1070     /**
1071      * newTableColorを設定します。
1072      *
1073      * @param newTableColor
1074      *            newTableColor
1075      */
1076     public void setNewTableColor(Color newTableColor) {
1077         this.newTableColor = newTableColor;
1078     }
1079
1080     /**
1081      * tlFontNameを取得します。
1082      *
1083      * @return tlFontName
1084      */
1085     public String getTlFontName() {
1086         return tlFontName;
1087     }
1088
1089     /**
1090      * tlFontNameを設定します。
1091      *
1092      * @param tlFontName
1093      *            tlFontName
1094      */
1095     public void setTlFontName(String tlFontName) {
1096         this.tlFontName = tlFontName;
1097     }
1098
1099     /**
1100      * tlFontSizeを取得します。
1101      *
1102      * @return tlFontSize
1103      */
1104     public int getTlFontSize() {
1105         return tlFontSize;
1106     }
1107
1108     /**
1109      * tlFontSizeを設定します。
1110      *
1111      * @param tlFontSize
1112      *            tlFontSize
1113      */
1114     public void setTlFontSize(int tlFontSize) {
1115         this.tlFontSize = tlFontSize;
1116     }
1117
1118     /**
1119      * detailFontNameを取得します。
1120      *
1121      * @return detailFontName
1122      */
1123     public String getDetailFontName() {
1124         return detailFontName;
1125     }
1126
1127     /**
1128      * detailFontNameを設定します。
1129      *
1130      * @param detailFontName
1131      *            detailFontName
1132      */
1133     public void setDetailFontName(String detailFontName) {
1134         this.detailFontName = detailFontName;
1135     }
1136
1137     /**
1138      * detailFontSizeを取得します。
1139      *
1140      * @return detailFontSize
1141      */
1142     public int getDetailFontSize() {
1143         return detailFontSize;
1144     }
1145
1146     /**
1147      * detailFontSizeを設定します。
1148      *
1149      * @param detailFontSize
1150      *            detailFontSize
1151      */
1152     public void setDetailFontSize(int detailFontSize) {
1153         this.detailFontSize = detailFontSize;
1154     }
1155
1156     /**
1157      * tableElementHeightを取得します。
1158      *
1159      * @return tableElementHeight
1160      */
1161     public int getTableElementHeight() {
1162         return tableElementHeight;
1163     }
1164
1165     /**
1166      * tableElementHeightを設定します。
1167      *
1168      * @param tableElementHeight
1169      *            tableElementHeight
1170      */
1171     public void setTableElementHeight(int tableElementHeight) {
1172         this.tableElementHeight = tableElementHeight;
1173     }
1174
1175     /**
1176      * @return the getTimelinePeriod
1177      */
1178     public int getGetTimelinePeriod() {
1179         return getTimelinePeriod;
1180     }
1181
1182     /**
1183      * @param getTimelinePeriod the getTimelinePeriod to set
1184      */
1185     public void setGetTimelinePeriod(int getTimelinePeriod) {
1186         this.getTimelinePeriod = getTimelinePeriod;
1187     }
1188
1189     /**
1190      * @return the getMentionPeriod
1191      */
1192     public int getGetMentionPeriod() {
1193         return getMentionPeriod;
1194     }
1195
1196     /**
1197      * @param getMentionPeriod the getMentionPeriod to set
1198      */
1199     public void setGetMentionPeriod(int getMentionPeriod) {
1200         this.getMentionPeriod = getMentionPeriod;
1201     }
1202
1203     /**
1204      * @return the getDirectMessagePeriod
1205      */
1206     public int getGetDirectMessagePeriod() {
1207         return getDirectMessagePeriod;
1208     }
1209
1210     /**
1211      * @param getDirectMessagePeriod the getDirectMessagePeriod to set
1212      */
1213     public void setGetDirectMessagePeriod(int getDirectMessagePeriod) {
1214         this.getDirectMessagePeriod = getDirectMessagePeriod;
1215     }
1216
1217     /**
1218      * @return the getSendDirectMessagePeriod
1219      */
1220     public int getGetSendDirectMessagePeriod() {
1221         return getSendDirectMessagePeriod;
1222     }
1223
1224     /**
1225      * @param getSendDirectMessagePeriod the getSendDirectMessagePeriod to set
1226      */
1227     public void setGetSendDirectMessagePeriod(int getSendDirectMessagePeriod) {
1228         this.getSendDirectMessagePeriod = getSendDirectMessagePeriod;
1229     }
1230 }