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());
278             }else if( timerID.equals( TimerID.createMentionID() ) ) {
279                 //Mentionの周期情報更新
280                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetMentionPeriod() );
281             }else if( timerID.equals( TimerID.createDirectMessageID() ) ) {
282                 //DMの周期情報更新
283                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetDirectMessagePeriod());
284             }else if( timerID.equals( TimerID.createSendDirectMessageID() ) ) {
285                 //SendDMの周期情報更新
286                 this.tweetTaskManager.updateTaskPeriod(timerID, this.getGetSendDirectMessagePeriod());
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                 t.updateTweetTable();
800             }
801
802             // API残り回数を取得
803             int remainingHits = tweetManager.getRateLimitStatus().getRemainingHits();
804             // 取得したコメント数をステータスバーに表示
805             information("新しいツイートを取得しました. (APIリクエスト残数は" + remainingHits
806                     + "回です)");
807         } catch (Exception e1) {
808             e1.printStackTrace();
809         }
810     }
811
812     /**
813      * つぶやける残り文字数の更新
814      *
815      * @param e
816      */
817     public void actionUpdateTweetMessageCount() {
818         int len = 140 - (tweetBoxPane.getText().length());
819         if (len < 0) {
820             len = 0;
821         }
822         tweetMessageCountLabel.setText(len + "");
823     }
824
825     /**
826      * 基本設定用ダイアログを取得
827      *
828      * @return
829      */
830     public ConfigurationDialog getConfigurationDialog() {
831         if (configurationDialog == null) {
832             configurationDialog = new ConfigurationDialog(mainFrame, this);
833         }
834         return configurationDialog;
835     }
836
837     /**
838      * twitter全体からキーワード検索ダイアログを表示
839      * @return
840      */
841     public KeywordSearchDialog getKeywordSearchDialog() {
842         if( keywordSearchDialog == null ) {
843             keywordSearchDialog = new KeywordSearchDialog(mainFrame, true, this);
844         }
845         return keywordSearchDialog;
846     }
847
848     /**
849      * アカウント情報設定ダイアログを取得
850      * @return
851      */
852     public AccountDialog getAccountDialog() {
853         if( accountDialog == null ) {
854             accountDialog = new AccountDialog(mainFrame, true, tweetManager, this);
855         }
856         return accountDialog;
857     }
858
859     /**
860      * ダイレクトメッセージ送信用ダイアログを取得
861      *
862      * @return
863      */
864     public DirectMessageDialog getDirectMessageDialog() {
865         if (directMessageDialog == null) {
866             directMessageDialog = new DirectMessageDialog(mainFrame);
867             directMessageDialog.setTitle("ダイレクトメッセージを送信");
868         }
869         return directMessageDialog;
870     }
871
872     /**
873      * テーブルで選択したツイートを詳細情報としてセット
874      * @param table
875      */
876     public void setDetailInformationFromTable(JTable table) {
877         int sc = table.getSelectedRowCount();
878         String infoMessage = "";
879
880         if (sc == 1) {
881             Status st = getTweetTableInformation(table, table.getModel());
882             infoMessage = st.getText();
883             // tweetMessageBox内のURLをhtmlリンクへ変換
884             infoMessage = actionReplaceTweetMessageBoxURLLink(infoMessage);
885             // @ユーザ情報をhtmlリンクへ変換
886             infoMessage = actionReplaceTweetMessageBoxUserInfo(infoMessage);
887             // #ハッシュタグ情報をhtmlリンクへ変換
888             infoMessage = actionReplaceTweetMessageBoxHashTab(infoMessage);
889             // 詳細情報にテーブルで選択した人のツイート情報を表示
890             tweetMessageBox.setText(infoMessage);
891             // user icon
892             userImageLabel.setIcon(new ImageIcon(st.getUser().getProfileImageURL()));
893             // user name and id
894             userNameLabel.setText(st.getUser().getName()
895                     + " / " + st.getUser().getScreenName());
896             // update Time
897             updateTimeLabel.setText(DateFormat.getInstance().format( st.getCreatedAt() ));
898             // ユーザ自己紹介文
899             userIntroBox.setText(st.getUser().getDescription());
900             // フォローされている数
901             followerLabel.setText(st.getUser().getFollowersCount()
902                     + "");
903             // フォローしている数
904             followingLabel.setText(st.getUser().getFriendsCount()
905                     + "");
906             // 現在地
907             locationLabel.setText(st.getUser().getLocation());
908             // Web
909             if (st.getUser().getURL() != null) {
910                 userWebBox.setText("<a href=\""
911                         + st.getUser().getURL() + "\">"
912                         + st.getUser().getScreenName()
913                         + "のWebを開く" + "</a>");
914             } else {
915                 userWebBox.setText("");
916             }
917             // client
918             clientNameLabel.setText(" via " + st.getSource());
919             // Update
920             updateLabel.setText(st.getUser().getStatusesCount()
921                     + "");
922         }
923     }
924
925     /**
926      *
927      * @return
928      */
929     public AboutDialog getAboutDialog() {
930         if (aboutDialog == null) {
931             aboutDialog = new AboutDialog(mainFrame, true);
932         }
933         return aboutDialog;
934     }
935
936     /**
937      * テーブルで選択した場所のTweet情報を取得
938      *
939      * @return
940      */
941     public Status getTweetTableInformation(JTable table, TableModel model) {
942         int index = table.convertRowIndexToModel(table.getSelectedRow());
943         Status status = null;
944         if (model instanceof TweetTableModel) {
945             status = ((TweetTableModel) model).getTweetStatus(index);
946             // 現在選択したセルのユーザ名を保存しておく
947             this.selectedUsername = status.getUser().getScreenName();
948             // 現在選択したセルのユーザURLを保存しておく
949             this.selectedUserImageURL = status.getUser().getProfileImageURL();
950             // 選択したStatusを保存しておく
951             this.currentStatus = status;
952         }
953         return status;
954     }
955
956     /**
957      * ステータスバーに情報を表示する
958      *
959      * @param message
960      */
961     public void information(String message) {
962         statusBarLabel.setText(message);
963     }
964
965     /**
966      * 設定ファイルを読み込む
967      *
968      * @throws IOException
969      * @throws FileNotFoundException
970      */
971     public void loadProperties() throws FileNotFoundException, IOException {
972         if (property == null) {
973             this.property = new Properties();
974         }
975         property.load(new FileInputStream("./" + PROPERTIES_DIRECTORY + "/"
976                 + BASIC_SETTING_FILENAME));
977         // 設定読み込み
978         String gtp = this.property.getProperty("getTimelinePeriod");
979         String gmp = this.property.getProperty("getMentionPeriod");
980         String gdmp = this.property.getProperty("getDirectMessagePeriod");
981         String gsdmp = this.property.getProperty("getSendDirectMessagePeriod");
982
983         String ntrgb = this.property.getProperty("newTableColorRGB");
984
985         this.tlFontName = this.property.getProperty("tlFontName");
986         this.detailFontName = this.property.getProperty("detailFontName");
987
988         String tfs = this.property.getProperty("tlFontSize");
989         String dfs = this.property.getProperty("detailFontSize");
990         String teh = this.property.getProperty("tableElementHeight");
991
992         //メインフレームの大きさ
993         String mfw = this.property.getProperty("mainFrameWidth");
994         String mfh = this.property.getProperty("mainFrameHeight");
995
996         try {
997             this.newTableColor = new Color(Integer.parseInt(ntrgb));
998             this.tlFontSize = Integer.parseInt(tfs);
999             this.detailFontSize = Integer.parseInt(dfs);
1000             this.tableElementHeight = Integer.parseInt(teh);
1001             this.mainFrameWidth = Integer.parseInt(mfw);
1002             this.mainFrameHeight = Integer.parseInt(mfh);
1003
1004             //更新間隔
1005             this.getTimelinePeriod = Integer.parseInt(gtp);
1006             this.getMentionPeriod = Integer.parseInt(gmp);
1007             this.getDirectMessagePeriod = Integer.parseInt(gdmp);
1008             this.getSendDirectMessagePeriod = Integer.parseInt(gsdmp);
1009         } catch (NumberFormatException e) {
1010             e.printStackTrace();
1011         }
1012     }
1013
1014     /**
1015      * 設定ファイルを保存する
1016      *
1017      * @throws IOException
1018      */
1019     public void saveProperties() throws IOException {
1020         // 設定ファイルディレクトリを作成
1021         File logDir = new File("./" + PROPERTIES_DIRECTORY);
1022         if (!logDir.exists()) {
1023             // ディレクトリが存在しないので作成する
1024             if (logDir.mkdir() == false) {
1025                 throw new IOException(PROPERTIES_DIRECTORY
1026                         + "ディレクトリを作成できませんでした.");
1027             }
1028         }
1029         if (property == null) {
1030             this.property = new Properties();
1031         }
1032
1033         //情報更新間隔
1034         this.property.setProperty("getTimelinePeriod", this.getTimelinePeriod + "");
1035         this.property.setProperty("getMentionPeriod", this.getMentionPeriod + "");
1036         this.property.setProperty("getDirectMessagePeriod", this.getDirectMessagePeriod + "");
1037         this.property.setProperty("getSendDirectMessagePeriod", this.getSendDirectMessagePeriod + "");
1038
1039         this.property.setProperty("newTableColorRGB", newTableColor.getRGB()
1040                 + "");
1041         this.property.setProperty("tlFontName", this.tlFontName);
1042         this.property.setProperty("tlFontSize", this.tlFontSize + "");
1043         this.property.setProperty("detailFontName", this.detailFontName);
1044         this.property.setProperty("detailFontSize", this.detailFontSize + "");
1045         this.property.setProperty("tableElementHeight", this.tableElementHeight
1046                 + "");
1047
1048         //main frame size
1049         if( this.mainFrame.getExtendedState() == JFrame.NORMAL ) {
1050             this.mainFrameWidth = this.mainFrame.getWidth();
1051             this.mainFrameHeight = this.mainFrame.getHeight();
1052         }
1053         this.property.setProperty("mainFrameWidth", this.mainFrameWidth + "");
1054         this.property.setProperty("mainFrameHeight", this.mainFrameHeight + "");
1055         // プロパティのリストを保存
1056         property.store(new FileOutputStream("./" + PROPERTIES_DIRECTORY + "/"
1057                 + BASIC_SETTING_FILENAME), null);
1058     }
1059
1060     /**
1061      * newTableColorを取得します。
1062      *
1063      * @return newTableColor
1064      */
1065     public Color getNewTableColor() {
1066         return newTableColor;
1067     }
1068
1069     /**
1070      * newTableColorを設定します。
1071      *
1072      * @param newTableColor
1073      *            newTableColor
1074      */
1075     public void setNewTableColor(Color newTableColor) {
1076         this.newTableColor = newTableColor;
1077     }
1078
1079     /**
1080      * tlFontNameを取得します。
1081      *
1082      * @return tlFontName
1083      */
1084     public String getTlFontName() {
1085         return tlFontName;
1086     }
1087
1088     /**
1089      * tlFontNameを設定します。
1090      *
1091      * @param tlFontName
1092      *            tlFontName
1093      */
1094     public void setTlFontName(String tlFontName) {
1095         this.tlFontName = tlFontName;
1096     }
1097
1098     /**
1099      * tlFontSizeを取得します。
1100      *
1101      * @return tlFontSize
1102      */
1103     public int getTlFontSize() {
1104         return tlFontSize;
1105     }
1106
1107     /**
1108      * tlFontSizeを設定します。
1109      *
1110      * @param tlFontSize
1111      *            tlFontSize
1112      */
1113     public void setTlFontSize(int tlFontSize) {
1114         this.tlFontSize = tlFontSize;
1115     }
1116
1117     /**
1118      * detailFontNameを取得します。
1119      *
1120      * @return detailFontName
1121      */
1122     public String getDetailFontName() {
1123         return detailFontName;
1124     }
1125
1126     /**
1127      * detailFontNameを設定します。
1128      *
1129      * @param detailFontName
1130      *            detailFontName
1131      */
1132     public void setDetailFontName(String detailFontName) {
1133         this.detailFontName = detailFontName;
1134     }
1135
1136     /**
1137      * detailFontSizeを取得します。
1138      *
1139      * @return detailFontSize
1140      */
1141     public int getDetailFontSize() {
1142         return detailFontSize;
1143     }
1144
1145     /**
1146      * detailFontSizeを設定します。
1147      *
1148      * @param detailFontSize
1149      *            detailFontSize
1150      */
1151     public void setDetailFontSize(int detailFontSize) {
1152         this.detailFontSize = detailFontSize;
1153     }
1154
1155     /**
1156      * tableElementHeightを取得します。
1157      *
1158      * @return tableElementHeight
1159      */
1160     public int getTableElementHeight() {
1161         return tableElementHeight;
1162     }
1163
1164     /**
1165      * tableElementHeightを設定します。
1166      *
1167      * @param tableElementHeight
1168      *            tableElementHeight
1169      */
1170     public void setTableElementHeight(int tableElementHeight) {
1171         this.tableElementHeight = tableElementHeight;
1172     }
1173
1174     /**
1175      * @return the getTimelinePeriod
1176      */
1177     public int getGetTimelinePeriod() {
1178         return getTimelinePeriod;
1179     }
1180
1181     /**
1182      * @param getTimelinePeriod the getTimelinePeriod to set
1183      */
1184     public void setGetTimelinePeriod(int getTimelinePeriod) {
1185         this.getTimelinePeriod = getTimelinePeriod;
1186     }
1187
1188     /**
1189      * @return the getMentionPeriod
1190      */
1191     public int getGetMentionPeriod() {
1192         return getMentionPeriod;
1193     }
1194
1195     /**
1196      * @param getMentionPeriod the getMentionPeriod to set
1197      */
1198     public void setGetMentionPeriod(int getMentionPeriod) {
1199         this.getMentionPeriod = getMentionPeriod;
1200     }
1201
1202     /**
1203      * @return the getDirectMessagePeriod
1204      */
1205     public int getGetDirectMessagePeriod() {
1206         return getDirectMessagePeriod;
1207     }
1208
1209     /**
1210      * @param getDirectMessagePeriod the getDirectMessagePeriod to set
1211      */
1212     public void setGetDirectMessagePeriod(int getDirectMessagePeriod) {
1213         this.getDirectMessagePeriod = getDirectMessagePeriod;
1214     }
1215
1216     /**
1217      * @return the getSendDirectMessagePeriod
1218      */
1219     public int getGetSendDirectMessagePeriod() {
1220         return getSendDirectMessagePeriod;
1221     }
1222
1223     /**
1224      * @param getSendDirectMessagePeriod the getSendDirectMessagePeriod to set
1225      */
1226     public void setGetSendDirectMessagePeriod(int getSendDirectMessagePeriod) {
1227         this.getSendDirectMessagePeriod = getSendDirectMessagePeriod;
1228     }
1229 }