From 1df2746a27d759569c884fa5cca4a15e0b8bc87b Mon Sep 17 00:00:00 2001 From: spark_xp Date: Sun, 1 May 2011 16:03:17 +0000 Subject: [PATCH] =?utf8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=81=AE=E3=82=A4?= =?utf8?q?=E3=83=A1=E3=83=BC=E3=82=B8=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?utf8?q?=E5=8F=96=E5=BE=97=E3=81=AB=E6=99=82=E9=96=93=E3=81=8C=E6=8E=9B?= =?utf8?q?=E3=81=8B=E3=81=A3=E3=81=A6=E3=81=84=E3=81=9F=E5=95=8F=E9=A1=8C?= =?utf8?q?=E3=82=92=E8=A7=A3=E6=B1=BA=E3=80=82=E7=94=BB=E5=83=8F=E3=83=95?= =?utf8?q?=E3=82=A1=E3=82=A4=E3=83=AB=E3=82=92=E4=BA=88=E3=82=81=E3=83=80?= =?utf8?q?=E3=82=A6=E3=83=B3=E3=83=AD=E3=83=BC=E3=83=89=E3=81=97=E3=81=A6?= =?utf8?q?=E3=81=8A=E3=81=8D=E3=80=81=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?utf8?q?=E3=83=A5=E3=81=A8=E3=81=97=E3=81=A6=E4=BF=9D=E5=AD=98=E3=81=97?= =?utf8?q?=E3=81=A6=E3=81=8A=E3=81=8F=E3=80=82=E3=81=93=E3=81=AE=E4=BF=AE?= =?utf8?q?=E6=AD=A3=E3=81=AB=E3=82=88=E3=82=8A=E3=82=BD=E3=83=95=E3=83=88?= =?utf8?q?=E3=82=A6=E3=82=A7=E3=82=A2=E3=81=AE=E5=8B=95=E4=BD=9C=E3=81=8C?= =?utf8?q?=E6=97=A9=E3=81=8F=E3=81=AA=E3=81=A3=E3=81=9F=E3=80=82=20refs=20?= =?utf8?q?#24966?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit git-svn-id: http://svn.sourceforge.jp/svnroot/nt-manager/NishioTweetManager/trunk@119 d8c9ecd3-d47d-4367-8645-de82c00e513f --- src/twitter/cache/TwitterImageCache.java | 166 ++++ src/twitter/gui/component/TweetTabbedTable.java | 1186 ++++++++++++----------- src/twitter/gui/component/TweetTableModel.java | 43 +- src/twitter/manage/TweetConfiguration.java | 10 +- 4 files changed, 822 insertions(+), 583 deletions(-) create mode 100644 src/twitter/cache/TwitterImageCache.java diff --git a/src/twitter/cache/TwitterImageCache.java b/src/twitter/cache/TwitterImageCache.java new file mode 100644 index 0000000..ff99ab8 --- /dev/null +++ b/src/twitter/cache/TwitterImageCache.java @@ -0,0 +1,166 @@ +package twitter.cache; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.util.Set; +import java.util.TreeSet; + +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; + +import twitter.manage.TweetConfiguration; + +/** + * イメージファイルのキャッシュを行うクラス + * + * @author nishio + * + */ +public class TwitterImageCache { + + // イメージのキャッシュを保存するディレクトリ + private static final String CACHE_DIRECTORY = TweetConfiguration.CACHE_DIRECTORY; + // ディスク上に存在しているイメージ + private Set existImageList = null; + //キャッシュ + private static TwitterImageCache cache = new TwitterImageCache(); + + /** + * コンストラクタ + */ + private TwitterImageCache() { + existImageList = new TreeSet(); + + // キャッシュを保存するディレクトリを作成 + File logDir = new File("./" + CACHE_DIRECTORY); + if (!logDir.exists()) { + // ディレクトリが存在しないので作成する + if (logDir.mkdir() == false) { + System.err.println(CACHE_DIRECTORY + "ディレクトリを作成できませんでした"); + } + } + //HDD上にあるキャッシュが存在することを登録 + loadExistImageList(); + } + + /** + * ローカルに保存されているキャッシュデータ一覧を登録 + */ + public void loadExistImageList() { + String path = "./" + CACHE_DIRECTORY; + File directory = new File( path ); + //ファイル一覧を取得 + File[] fileList = directory.listFiles(); + for( File f : fileList ) { + String filename = f.getName(); + //ファイルが存在することを登録 + this.existImageList.add( filename ); + } + } + + /** + * インスタンスを取得する + * @return + */ + public static TwitterImageCache getInstance() { + return cache; + } + + /** + * Imageを取得する + * + * @param imageURL + * @return + */ + public ImageIcon getProfileImage(String imageURL) { + // キャッシュ名取得 + String cacheFilename = getCacheFilenameFromURL(imageURL); + if( cacheFilename == null ) { + return null; + } + // イメージ + ImageIcon icon = null; + //保存するファイルのパス + String path = "./" + CACHE_DIRECTORY + "/" + cacheFilename; + + // キャッシュが存在するかどうか + if ( !existImageList.contains(cacheFilename)) { + // ネットからイメージをダウンロードする + URL url; + try { + url = new URL(imageURL); + URLConnection connection = url.openConnection(); + InputStream inputStream = connection.getInputStream(); + // ファイルに保存 + File file = new File( path ); + FileOutputStream outputStream = new FileOutputStream(file, false); + /*int data; + while( (data = inputStream.read() ) != -1 ) { + outputStream.write( data ); + }*/ + //データダウンロードを高速化するため、512バイトずつ取得 + byte buf[] = new byte[512]; + int len; + while( (len = inputStream.read( buf )) != -1 ) { + outputStream.write( buf, 0, len ); + } + //ストリームを閉じる + outputStream.close(); + inputStream.close(); + + //HDD上に存在するイメージとして記録 + existImageList.add( cacheFilename ); + + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch( Exception e ) { + e.printStackTrace(); + } + } + + if( existImageList.contains( cacheFilename ) ) { + //HDDからイメージを取得 + icon = new ImageIcon( path ); + }/*else { + //真っ黒のアイコン画像 + Image image = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB); + Graphics g = image.getGraphics(); + g.setColor( Color.BLACK ); + g.fillRect(0, 0, 100, 100); + g.dispose(); + icon = new ImageIcon( image ); + }*/ + + return icon; + } + + /** + * URLからキャッシュ上に保存されている画像データ名に変換する URLは、例えば + * http://a3.twimg.com/profile_images/870379120/nemui_tora_normal.png であったら、 + * 870379120_nemui_tora_normal.png というキャッシュ名に変換する + * + * @param url + * @return + */ + public String getCacheFilenameFromURL(String url) { + String[] urlSplit = url.split("/"); + int len = urlSplit.length; + String filename = null; + // キャッシュ名の変換 + if (urlSplit.length >= 2) { + filename = urlSplit[len - 2] + "_" + urlSplit[len - 1]; + } + return filename; + } +} diff --git a/src/twitter/gui/component/TweetTabbedTable.java b/src/twitter/gui/component/TweetTabbedTable.java index 2f7c5ab..a74c0b1 100644 --- a/src/twitter/gui/component/TweetTabbedTable.java +++ b/src/twitter/gui/component/TweetTabbedTable.java @@ -32,586 +32,616 @@ import twitter4j.TwitterException; /** * ツイートを表示するテーブルを扱うクラス + * * @author nishio */ public class TweetTabbedTable { - //ツイートを表示するテーブル - private JTable table; - //ツイートを表示するテーブルのモデル - private TweetTableModel model; - //未読のツイート数 - private int uncheckedTweet; - //ツイートを取得する時に行うアクション - private TweetGetter tweetGetter; - //タブに表示される名前 - private String title; - //タブ - private JTabbedPane tabbedPane; - //スクロールペーン - private JScrollPane scrollPane; - //ツイートを管理するクラス - private TweetManager tweetManager; - //メインアクション - private TweetMainAction mainAction; - //テーブルの高さ - private int tableElementHeight; - //新しく取得した部分のテーブルの色 - private Color newTableColor = null; - //テーブルに追加できる要素の最大数 - private int tableElementMaxSize = 200; - //自動更新に使うタイマーのID - private String timerID; - - - /** - * - * @param tweetGetter tweet取得時に行うアクション - * @param title タブに表示するタイトル - * @param tabbedPane テーブルを追加するタブ - * @param tableElementHeight テーブルの高さ - * @param tweetManager ツイート管理クラス - * @param mainAction メインアクション - * @param newTableColor 新しく取得した部分の色 - * @param tableElementMaxSize テーブルに格納できる要素の最大数 - * @param timerID 自動更新につかうタイマーのID - */ - public TweetTabbedTable(TweetGetter tweetGetter, - String title, JTabbedPane tabbedPane, int tableElementHeight, - TweetManager tweetManager, TweetMainAction mainAction, Color newTableColor, - int tableElementMaxSize, String timerID) { - this.tweetGetter = tweetGetter; - this.title = title; - this.tabbedPane = tabbedPane; - this.tweetManager = tweetManager; - this.mainAction = mainAction; - this.tableElementHeight = tableElementHeight; - this.newTableColor = newTableColor; - this.tableElementMaxSize = tableElementMaxSize; - this.timerID = timerID; - - table = new JTable(); - model = new TweetTableModel(); - uncheckedTweet = 0; - scrollPane = new JScrollPane(); - } - - /** - * Tweet情報を表示するテーブルをタブに追加 - */ - public void addTableToTab() { - //テーブルをタブに追加 - getTable().setModel(model); - getTable().getTableHeader().setReorderingAllowed(false); - getTable().addMouseListener(new java.awt.event.MouseAdapter() { - @Override - public void mousePressed(java.awt.event.MouseEvent evt) { - jTableMousePressed(evt); - } - @Override - public void mouseReleased(java.awt.event.MouseEvent evt) { - jTableMouseReleased(evt); - } - }); - - //tweetを表示するテーブルを作成 - createTweetTable(getTable()); - - //スクロールペーン追加 - scrollPane.setViewportView(getTable()); - //タブにテーブル追加 - tabbedPane.addTab(this.title, scrollPane); - } - - /** - * Tweetを表示するテーブルを作成 - * - * @param model - * @return - */ - private void createTweetTable(final JTable table) { - - table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); - table.setShowVerticalLines(false); - table.setShowHorizontalLines(true); - - // Comment部分のColumnを複数行コメントが表示できるようにする - TableColumnModel mdl = table.getColumnModel(); - TableColumn col = mdl.getColumn(1); - TweetCommentRenderer commentRenderer = new TweetCommentRenderer(); - col.setCellRenderer(commentRenderer); - // INfo部分のColumnを複数行表示できるように - TweetCommentRenderer infoRenderer = new TweetCommentRenderer(); - col = mdl.getColumn(2); - col.setCellRenderer(infoRenderer); - col.setMaxWidth(200); - col.setMinWidth(150); - // TODO:とりあえず幅指定した部分 - // あとでファイルに幅情報などを保存しておき,それを読み込んで設定するような仕様に変更する - // ユーザImageを表示する部分 - col = mdl.getColumn(0); - col.setCellRenderer(new UserImageRenderer()); - col.setMinWidth(50); - col.setMaxWidth(50); - // 選択したセルの情報をDetailInfoへと表示 - // JTableを右クリックでも選択できるようにする - // また,同じ行を2回クリックできるようにする - table.addMouseListener(new MouseAdapter() { - - @Override - public void mousePressed(MouseEvent e) { - // いったんSelectしていた情報を削除 - table.clearSelection(); - // if (e.getButton() == MouseEvent.BUTTON3) { - Point p = e.getPoint(); - int col = table.columnAtPoint(p); - int row = table.rowAtPoint(p); - table.changeSelection(row, col, false, false); - - //テーブルで選択した要素を詳細情報として表示 - mainAction.setDetailInformationFromTable(table); - // } - } - }); - //キー受付 - table.addKeyListener(new KeyAdapter() { - - @Override - public void keyReleased(KeyEvent ke) { - mainAction.setDetailInformationFromTable(table); - } - - }); - // MouseEventを追加 - table.addMouseListener(commentRenderer); - //table.addMouseMotionListener(commentRenderer); - table.addMouseListener(infoRenderer); - //table.addMouseMotionListener(infoRenderer); - } - - /** - * テーブル情報更新 - */ - public void updateTweetTable() { - try { - // API残り回数を取得 - int remainingHits = tweetManager.getRateLimitStatus().getRemainingHits(); - if (remainingHits <= 0) { - return; - } - // ツイート情報 - List tweet = tweetGetter.getNewTweetData(); - // まだ見ていないtweet数を追加 - this.setUncheckedTweet(this.getUncheckedTweet() + tweet.size()); - - // まだチェックしていないtweetの数をタブにも表示 - if (this.getUncheckedTweet() > 0) { - tabbedPane.setTitleAt(this.getTabSetNum(), this.title + "(" - + this.getUncheckedTweet() + ")"); - } - // ツイートをテーブルに追加 - for (Status t : tweet) { - this.getModel().insertTweet(t); - this.getTable().setRowHeight(0, getTableElementHeight()); - } - // 新規した部分の背景色を変更 - TableCellRenderer renderer = getTable().getCellRenderer(0, 2); - if (renderer instanceof TweetCommentRenderer) { - if (this.getUncheckedTweet() - 1 >= 0) { - ((TweetCommentRenderer) renderer).updateNewCellRow( - this.getUncheckedTweet(), newTableColor); - } else { - ((TweetCommentRenderer) renderer).updateNewCellRow(-1, - newTableColor); - } - } - // 古いデータを削除 - getModel().removeOldTweet( getTableElementMaxSize() ); - //時間情報リフレッシュ - getModel().refreshTime(); - //新しい情報 - int newNum = 0; - if( tweet != null ) { - newNum = tweet.size(); - } - //情報を取得したことをステータスバーに表示 - mainAction.information( this.getTitle() + "タブのツイートを" + newNum + - "件取得しました. (APIリクエスト残数は" + remainingHits - + "回です)"); - - //情報間隔毎に設定を保存 - this.mainAction.saveProperties(); - } catch (TwitterException e1) { - e1.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - /** - * マウスクリック時の動作 - * @param evt - */ - private void jTableMousePressed(java.awt.event.MouseEvent evt) { - //右クリックメニュー表示 - showPopup(evt); - //未読ツイート分を0にする - this.setUncheckedTweet(0); - this.setTitle( this.getTitle() ); - } - - /** - * マウスリリース時の動作 - * @param evt - */ - private void jTableMouseReleased(java.awt.event.MouseEvent evt) { - //右クリックメニュー表示 - showPopup(evt); - } - - /** - * ポップアップメニューを作成 - * @param e - */ - private void showPopup(MouseEvent e) { - if (e.isPopupTrigger()) { - // 右クリックのメニューを表示 - getRightClickPopup().show(e.getComponent(), e.getX(), - e.getY()); - } - } - - /** - * 右クリックを押した時のポップアップメニューを取得 - * - * @return - */ - private JPopupMenu getRightClickPopup() { - //if (rightClickPopup == null) { - JPopupMenu rightClickPopup = new JPopupMenu(); - - JMenuItem replyMenuItem = new JMenuItem("この発言に返信(Reply)"); - replyMenuItem.setMnemonic('R'); - replyMenuItem.addActionListener(new java.awt.event.ActionListener() { - - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスにreply - mainAction.actionSetReplyStatusToTweetBoxPane(); - } - }); - - JMenuItem replyAllMenuItem = new JMenuItem("この発言に返信(Reply All)"); - replyAllMenuItem.setMnemonic('A'); - replyAllMenuItem.addActionListener(new java.awt.event.ActionListener() { - - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスにreply all - mainAction.actionSetReplyAllStatusToTweetBoxPane(); - } - }); - - - JMenuItem retweetMenuItem = new JMenuItem("発言を公式リツイート(RT)"); - retweetMenuItem.setMnemonic('R'); - retweetMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスをRetweet - mainAction.actionRetweet(); - } - }); - - JMenuItem quoteMenuItem = new JMenuItem("発言を引用ツイート(QT)"); - quoteMenuItem.setMnemonic('Q'); - quoteMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスをQT - mainAction.actionSetQuoteStatusToTweetBoxPane(); - } - }); - - JMenuItem unofficialRetweetMenuItem = new JMenuItem("発言をコメント付きリツイート(非公式RT)"); - unofficialRetweetMenuItem.setMnemonic('T'); - unofficialRetweetMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスをコメント付Retweet - mainAction.actionCopySelectedStatusToTweetBoxPane(); - } - }); - - JMenuItem directMessageMenuItem = new JMenuItem("ダイレクトメッセージを送信(D)"); - directMessageMenuItem.setMnemonic('D'); - directMessageMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // ダイレクトメッセージ送信ダイアログを表示 - mainAction.actionShowDirectMessageDialog(); - } - }); - - JMenuItem statusBrowserMenuItem = new JMenuItem("この発言をブラウザで開く(O)"); - statusBrowserMenuItem.setMnemonic('O'); - statusBrowserMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのステータスをブラウザで開く - mainAction.actionOpenStatusURL(); - } - }); - - JMenuItem userMenuItem = new JMenuItem("この人の発言を別タブで開く(U)"); - userMenuItem.setMnemonic('U'); - userMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 指定した人の発言を開く - mainAction.actionSelectedUserTimeline(); - } - }); - - JMenuItem createFavMenuItem = new JMenuItem( - "この発言をお気に入りに追加(F)"); - createFavMenuItem.setMnemonic('F'); - createFavMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionCreateFavorite(); - } - }); - - JMenuItem destroyFavMenuItem = new JMenuItem( - "この発言をお気に入りから削除"); - destroyFavMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionDestroyFavorite(); - } - }); - - JMenuItem createdListMenuItem = new JMenuItem( - "このユーザが作成したリスト一覧"); - createdListMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionShowSelectedUserList(ListGetterSelection.CREATED); - } - }); - - JMenuItem subscriptionListMenuItem = new JMenuItem( - "このユーザが購読しているリスト一覧"); - subscriptionListMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionShowSelectedUserList(ListGetterSelection.SUBSCRIPTION); - } - }); - - JMenuItem membershipsListMenuItem = new JMenuItem( - "このユーザがフォローされているリスト一覧"); - membershipsListMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionShowSelectedUserList(ListGetterSelection.MEMBERSHIPS); - } - }); - - - - //指定した発言がRTかどうか判定 - int sc = table.getSelectedRowCount(); - if (sc == 1 && table != null) { - Status st = mainAction.getTweetTableInformation(table, table.getModel()); - - JMenuItem openBrowserUserInformationMenuItem = new JMenuItem( - "この人の発言をブラウザで開く(B)"); - openBrowserUserInformationMenuItem.setMnemonic('B'); - openBrowserUserInformationMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionOpenUserURL(); - } - }); - - JMenuItem openFavMenuItem = new JMenuItem( - "この人のお気に入りを開く"); - openFavMenuItem.addActionListener(new java.awt.event.ActionListener() { - - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - // 選択したセルのユーザ情報をブラウザで開く - mainAction.actionOpenUserFav(); - } - }); - - - //メニューアイテムを追加 - //返信 - rightClickPopup.add(replyMenuItem); - //返信all - rightClickPopup.add(replyAllMenuItem); - //公式RT - rightClickPopup.add(retweetMenuItem); - //非公式RT - rightClickPopup.add(unofficialRetweetMenuItem); - //QT - rightClickPopup.add(quoteMenuItem); - //ダイレクトメッセージ - rightClickPopup.add(directMessageMenuItem); - //発言をブラウザで開く - rightClickPopup.add(statusBrowserMenuItem); - //この人の発言を別タブで開く - rightClickPopup.add(userMenuItem); - //この人のtimelineを開く - rightClickPopup.add(openBrowserUserInformationMenuItem); - //この人のfavを開く - rightClickPopup.add(openFavMenuItem); - //この人が作成したリスト - rightClickPopup.add(createdListMenuItem); - //この人が購読しているリスト - rightClickPopup.add(subscriptionListMenuItem); - //この人がフォローされているリスト一覧 - rightClickPopup.add(membershipsListMenuItem); - - try { - if (st.isRetweet()) { - //Retweetのときのみ表示するメニュー - } - if (st.isFavorited()) { - //お気に入りに追加されている時のみ表示するメニュー - //お気に入り追加 - rightClickPopup.add(destroyFavMenuItem); - } else { - rightClickPopup.add(createFavMenuItem); - } - }catch(Exception e) { - //TODO:ここの無視部分をなんとかする - //DMのときはisFavoritedができない - } - } - //} - return rightClickPopup; - } - - /** - * @return the title - */ - public String getTitle() { - return title; - } - - /** - * @param title the title to set - */ - public void setTitle(String title) { - this.title = title; - this.tabbedPane.setTitleAt( this.getTabSetNum() , title); - } - - /** - * 自分自信がタブのどの場所に位置しているのかを取得 - * @return - */ - public int getTabSetNum() { - int tabCount = this.tabbedPane.getTabCount(); - for(int i=0; i < tabCount; i++) { - Component c = this.tabbedPane.getComponentAt(i); - if( c instanceof JScrollPane ) { - if ( c == this.scrollPane ) { - return i; - } - } - } - return 0; - } - - /** - * @return the tableElementHeight - */ - public int getTableElementHeight() { - return tableElementHeight; - } - - /** - * @param tableElementHeight the tableElementHeight to set - */ - public void setTableElementHeight(int tableElementHeight) { - this.tableElementHeight = tableElementHeight; - } - - /** - * @return the tableElementMaxSize - */ - public int getTableElementMaxSize() { - return tableElementMaxSize; - } - - /** - * @param tableElementMaxSize the tableElementMaxSize to set - */ - public void setTableElementMaxSize(int tableElementMaxSize) { - this.tableElementMaxSize = tableElementMaxSize; - } - - /** - * @return the model - */ - public TweetTableModel getModel() { - return model; - } - - /** - * @param model the model to set - */ - public void setModel(TweetTableModel model) { - this.model = model; - } - - - /** - * @return the uncheckedTweet - */ - public int getUncheckedTweet() { - return uncheckedTweet; - } - - /** - * @param uncheckedTweet the uncheckedTweet to set - */ - public void setUncheckedTweet(int uncheckedTweet) { - this.uncheckedTweet = uncheckedTweet; - } - - /** - * @return the timerID - */ - public String getTimerID() { - return timerID; - } - - /** - * @return the table - */ - public JTable getTable() { - return table; - } + // ツイートを表示するテーブル + private JTable table; + // ツイートを表示するテーブルのモデル + private TweetTableModel model; + // 未読のツイート数 + private int uncheckedTweet; + // ツイートを取得する時に行うアクション + private TweetGetter tweetGetter; + // タブに表示される名前 + private String title; + // タブ + private JTabbedPane tabbedPane; + // スクロールペーン + private JScrollPane scrollPane; + // ツイートを管理するクラス + private TweetManager tweetManager; + // メインアクション + private TweetMainAction mainAction; + // テーブルの高さ + private int tableElementHeight; + // 新しく取得した部分のテーブルの色 + private Color newTableColor = null; + // テーブルに追加できる要素の最大数 + private int tableElementMaxSize = 200; + // 自動更新に使うタイマーのID + private String timerID; + + /** + * + * @param tweetGetter + * tweet取得時に行うアクション + * @param title + *  タブに表示するタイトル + * @param tabbedPane + *  テーブルを追加するタブ + * @param tableElementHeight + * テーブルの高さ + * @param tweetManager + *  ツイート管理クラス + * @param mainAction + * メインアクション + * @param newTableColor + * 新しく取得した部分の色 + * @param tableElementMaxSize + * テーブルに格納できる要素の最大数 + * @param timerID + * 自動更新につかうタイマーのID + */ + public TweetTabbedTable(TweetGetter tweetGetter, String title, + JTabbedPane tabbedPane, int tableElementHeight, + TweetManager tweetManager, TweetMainAction mainAction, + Color newTableColor, int tableElementMaxSize, String timerID) { + this.tweetGetter = tweetGetter; + this.title = title; + this.tabbedPane = tabbedPane; + this.tweetManager = tweetManager; + this.mainAction = mainAction; + this.tableElementHeight = tableElementHeight; + this.newTableColor = newTableColor; + this.tableElementMaxSize = tableElementMaxSize; + this.timerID = timerID; + + table = new JTable(); + model = new TweetTableModel(); + uncheckedTweet = 0; + scrollPane = new JScrollPane(); + } + + /** + * Tweet情報を表示するテーブルをタブに追加 + */ + public void addTableToTab() { + // テーブルをタブに追加 + getTable().setModel(model); + getTable().getTableHeader().setReorderingAllowed(false); + getTable().addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mousePressed(java.awt.event.MouseEvent evt) { + jTableMousePressed(evt); + } + + @Override + public void mouseReleased(java.awt.event.MouseEvent evt) { + jTableMouseReleased(evt); + } + }); + + // tweetを表示するテーブルを作成 + createTweetTable(getTable()); + + // スクロールペーン追加 + scrollPane.setViewportView(getTable()); + // タブにテーブル追加 + tabbedPane.addTab(this.title, scrollPane); + } + + /** + * Tweetを表示するテーブルを作成 + * + * @param model + * @return + */ + private void createTweetTable(final JTable table) { + + table.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); + table.setShowVerticalLines(false); + table.setShowHorizontalLines(true); + + // Comment部分のColumnを複数行コメントが表示できるようにする + TableColumnModel mdl = table.getColumnModel(); + TableColumn col = mdl.getColumn(1); + TweetCommentRenderer commentRenderer = new TweetCommentRenderer(); + col.setCellRenderer(commentRenderer); + // INfo部分のColumnを複数行表示できるように + TweetCommentRenderer infoRenderer = new TweetCommentRenderer(); + col = mdl.getColumn(2); + col.setCellRenderer(infoRenderer); + col.setMaxWidth(200); + col.setMinWidth(150); + // TODO:とりあえず幅指定した部分 + // あとでファイルに幅情報などを保存しておき,それを読み込んで設定するような仕様に変更する + // ユーザImageを表示する部分 + col = mdl.getColumn(0); + col.setCellRenderer(new UserImageRenderer()); + col.setMinWidth(50); + col.setMaxWidth(50); + // 選択したセルの情報をDetailInfoへと表示 + // JTableを右クリックでも選択できるようにする + // また,同じ行を2回クリックできるようにする + table.addMouseListener(new MouseAdapter() { + + @Override + public void mousePressed(MouseEvent e) { + // いったんSelectしていた情報を削除 + table.clearSelection(); + // if (e.getButton() == MouseEvent.BUTTON3) { + Point p = e.getPoint(); + int col = table.columnAtPoint(p); + int row = table.rowAtPoint(p); + table.changeSelection(row, col, false, false); + + // テーブルで選択した要素を詳細情報として表示 + mainAction.setDetailInformationFromTable(table); + // } + } + }); + // キー受付 + table.addKeyListener(new KeyAdapter() { + + @Override + public void keyReleased(KeyEvent ke) { + mainAction.setDetailInformationFromTable(table); + } + + }); + // MouseEventを追加 + table.addMouseListener(commentRenderer); + // table.addMouseMotionListener(commentRenderer); + table.addMouseListener(infoRenderer); + // table.addMouseMotionListener(infoRenderer); + } + + /** + * テーブル情報更新 + */ + public void updateTweetTable() { + try { + // API残り回数を取得 + int remainingHits = tweetManager.getRateLimitStatus() + .getRemainingHits(); + if (remainingHits <= 0) { + return; + } + // ツイート情報 + List tweet = tweetGetter.getNewTweetData(); + // まだ見ていないtweet数を追加 + this.setUncheckedTweet(this.getUncheckedTweet() + tweet.size()); + + // まだチェックしていないtweetの数をタブにも表示 + if (this.getUncheckedTweet() > 0) { + tabbedPane.setTitleAt(this.getTabSetNum(), this.title + "(" + + this.getUncheckedTweet() + ")"); + } + // ツイートをテーブルに追加 + this.getModel().insertTweet(tweet); + //テーブルの高さを整える + int tableHeight = getTableElementHeight(); + for(int i=0; i < tweet.size(); i++) { + this.getTable().setRowHeight(i, tableHeight); + } + /* + for (Status t : tweet) { + this.getModel().insertTweet(t); + this.getTable().setRowHeight(0, getTableElementHeight()); + }*/ + // 新規した部分の背景色を変更 + TableCellRenderer renderer = getTable().getCellRenderer(0, 2); + if (renderer instanceof TweetCommentRenderer) { + if (this.getUncheckedTweet() - 1 >= 0) { + ((TweetCommentRenderer) renderer).updateNewCellRow(this + .getUncheckedTweet(), newTableColor); + } else { + ((TweetCommentRenderer) renderer).updateNewCellRow(-1, + newTableColor); + } + } + // 古いデータを削除 + getModel().removeOldTweet(getTableElementMaxSize()); + // 時間情報リフレッシュ + getModel().refreshTime(); + // 新しい情報 + int newNum = 0; + if (tweet != null) { + newNum = tweet.size(); + } + // 情報を取得したことをステータスバーに表示 + mainAction.information(this.getTitle() + "タブのツイートを" + newNum + + "件取得しました. (APIリクエスト残数は" + remainingHits + "回です)"); + + // 情報間隔毎に設定を保存 + this.mainAction.saveProperties(); + } catch (TwitterException e1) { + e1.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * マウスクリック時の動作 + * + * @param evt + */ + private void jTableMousePressed(java.awt.event.MouseEvent evt) { + // 右クリックメニュー表示 + showPopup(evt); + // 未読ツイート分を0にする + this.setUncheckedTweet(0); + this.setTitle(this.getTitle()); + } + + /** + * マウスリリース時の動作 + * + * @param evt + */ + private void jTableMouseReleased(java.awt.event.MouseEvent evt) { + // 右クリックメニュー表示 + showPopup(evt); + } + + /** + * ポップアップメニューを作成 + * + * @param e + */ + private void showPopup(MouseEvent e) { + if (e.isPopupTrigger()) { + // 右クリックのメニューを表示 + getRightClickPopup().show(e.getComponent(), e.getX(), e.getY()); + } + } + + /** + * 右クリックを押した時のポップアップメニューを取得 + * + * @return + */ + private JPopupMenu getRightClickPopup() { + // if (rightClickPopup == null) { + JPopupMenu rightClickPopup = new JPopupMenu(); + + JMenuItem replyMenuItem = new JMenuItem("この発言に返信(Reply)"); + replyMenuItem.setMnemonic('R'); + replyMenuItem.addActionListener(new java.awt.event.ActionListener() { + + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスにreply + mainAction.actionSetReplyStatusToTweetBoxPane(); + } + }); + + JMenuItem replyAllMenuItem = new JMenuItem("この発言に返信(Reply All)"); + replyAllMenuItem.setMnemonic('A'); + replyAllMenuItem.addActionListener(new java.awt.event.ActionListener() { + + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスにreply all + mainAction.actionSetReplyAllStatusToTweetBoxPane(); + } + }); + + JMenuItem retweetMenuItem = new JMenuItem("発言を公式リツイート(RT)"); + retweetMenuItem.setMnemonic('R'); + retweetMenuItem.addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスをRetweet + mainAction.actionRetweet(); + } + }); + + JMenuItem quoteMenuItem = new JMenuItem("発言を引用ツイート(QT)"); + quoteMenuItem.setMnemonic('Q'); + quoteMenuItem.addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスをQT + mainAction.actionSetQuoteStatusToTweetBoxPane(); + } + }); + + JMenuItem unofficialRetweetMenuItem = new JMenuItem( + "発言をコメント付きリツイート(非公式RT)"); + unofficialRetweetMenuItem.setMnemonic('T'); + unofficialRetweetMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスをコメント付Retweet + mainAction.actionCopySelectedStatusToTweetBoxPane(); + } + }); + + JMenuItem directMessageMenuItem = new JMenuItem("ダイレクトメッセージを送信(D)"); + directMessageMenuItem.setMnemonic('D'); + directMessageMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // ダイレクトメッセージ送信ダイアログを表示 + mainAction.actionShowDirectMessageDialog(); + } + }); + + JMenuItem statusBrowserMenuItem = new JMenuItem("この発言をブラウザで開く(O)"); + statusBrowserMenuItem.setMnemonic('O'); + statusBrowserMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのステータスをブラウザで開く + mainAction.actionOpenStatusURL(); + } + }); + + JMenuItem userMenuItem = new JMenuItem("この人の発言を別タブで開く(U)"); + userMenuItem.setMnemonic('U'); + userMenuItem.addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 指定した人の発言を開く + mainAction.actionSelectedUserTimeline(); + } + }); + + JMenuItem createFavMenuItem = new JMenuItem("この発言をお気に入りに追加(F)"); + createFavMenuItem.setMnemonic('F'); + createFavMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction.actionCreateFavorite(); + } + }); + + JMenuItem destroyFavMenuItem = new JMenuItem("この発言をお気に入りから削除"); + destroyFavMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction.actionDestroyFavorite(); + } + }); + + JMenuItem createdListMenuItem = new JMenuItem("このユーザが作成したリスト一覧"); + createdListMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction + .actionShowSelectedUserList(ListGetterSelection.CREATED); + } + }); + + JMenuItem subscriptionListMenuItem = new JMenuItem("このユーザが購読しているリスト一覧"); + subscriptionListMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction + .actionShowSelectedUserList(ListGetterSelection.SUBSCRIPTION); + } + }); + + JMenuItem membershipsListMenuItem = new JMenuItem( + "このユーザがフォローされているリスト一覧"); + membershipsListMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction + .actionShowSelectedUserList(ListGetterSelection.MEMBERSHIPS); + } + }); + + // 指定した発言がRTかどうか判定 + int sc = table.getSelectedRowCount(); + if (sc == 1 && table != null) { + Status st = mainAction.getTweetTableInformation(table, table + .getModel()); + + JMenuItem openBrowserUserInformationMenuItem = new JMenuItem( + "この人の発言をブラウザで開く(B)"); + openBrowserUserInformationMenuItem.setMnemonic('B'); + openBrowserUserInformationMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction.actionOpenUserURL(); + } + }); + + JMenuItem openFavMenuItem = new JMenuItem("この人のお気に入りを開く"); + openFavMenuItem + .addActionListener(new java.awt.event.ActionListener() { + + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + // 選択したセルのユーザ情報をブラウザで開く + mainAction.actionOpenUserFav(); + } + }); + + // メニューアイテムを追加 + // 返信 + rightClickPopup.add(replyMenuItem); + // 返信all + rightClickPopup.add(replyAllMenuItem); + // 公式RT + rightClickPopup.add(retweetMenuItem); + // 非公式RT + rightClickPopup.add(unofficialRetweetMenuItem); + // QT + rightClickPopup.add(quoteMenuItem); + // ダイレクトメッセージ + rightClickPopup.add(directMessageMenuItem); + // 発言をブラウザで開く + rightClickPopup.add(statusBrowserMenuItem); + // この人の発言を別タブで開く + rightClickPopup.add(userMenuItem); + // この人のtimelineを開く + rightClickPopup.add(openBrowserUserInformationMenuItem); + // この人のfavを開く + rightClickPopup.add(openFavMenuItem); + // この人が作成したリスト + rightClickPopup.add(createdListMenuItem); + // この人が購読しているリスト + rightClickPopup.add(subscriptionListMenuItem); + // この人がフォローされているリスト一覧 + rightClickPopup.add(membershipsListMenuItem); + + try { + if (st.isRetweet()) { + // Retweetのときのみ表示するメニュー + } + if (st.isFavorited()) { + // お気に入りに追加されている時のみ表示するメニュー + // お気に入り追加 + rightClickPopup.add(destroyFavMenuItem); + } else { + rightClickPopup.add(createFavMenuItem); + } + } catch (Exception e) { + // TODO:ここの無視部分をなんとかする + // DMのときはisFavoritedができない + } + } + // } + return rightClickPopup; + } + + /** + * @return the title + */ + public String getTitle() { + return title; + } + + /** + * @param title + * the title to set + */ + public void setTitle(String title) { + this.title = title; + this.tabbedPane.setTitleAt(this.getTabSetNum(), title); + } + + /** + * 自分自信がタブのどの場所に位置しているのかを取得 + * + * @return + */ + public int getTabSetNum() { + int tabCount = this.tabbedPane.getTabCount(); + for (int i = 0; i < tabCount; i++) { + Component c = this.tabbedPane.getComponentAt(i); + if (c instanceof JScrollPane) { + if (c == this.scrollPane) { + return i; + } + } + } + return 0; + } + + /** + * @return the tableElementHeight + */ + public int getTableElementHeight() { + return tableElementHeight; + } + + /** + * @param tableElementHeight + * the tableElementHeight to set + */ + public void setTableElementHeight(int tableElementHeight) { + this.tableElementHeight = tableElementHeight; + } + + /** + * @return the tableElementMaxSize + */ + public int getTableElementMaxSize() { + return tableElementMaxSize; + } + + /** + * @param tableElementMaxSize + * the tableElementMaxSize to set + */ + public void setTableElementMaxSize(int tableElementMaxSize) { + this.tableElementMaxSize = tableElementMaxSize; + } + + /** + * @return the model + */ + public TweetTableModel getModel() { + return model; + } + + /** + * @param model + * the model to set + */ + public void setModel(TweetTableModel model) { + this.model = model; + } + + /** + * @return the uncheckedTweet + */ + public int getUncheckedTweet() { + return uncheckedTweet; + } + + /** + * @param uncheckedTweet + * the uncheckedTweet to set + */ + public void setUncheckedTweet(int uncheckedTweet) { + this.uncheckedTweet = uncheckedTweet; + } + + /** + * @return the timerID + */ + public String getTimerID() { + return timerID; + } + + /** + * @return the table + */ + public JTable getTable() { + return table; + } } diff --git a/src/twitter/gui/component/TweetTableModel.java b/src/twitter/gui/component/TweetTableModel.java index 121366c..2bb0032 100644 --- a/src/twitter/gui/component/TweetTableModel.java +++ b/src/twitter/gui/component/TweetTableModel.java @@ -4,6 +4,7 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -11,6 +12,7 @@ import java.util.List; import javax.swing.ImageIcon; import javax.swing.table.DefaultTableModel; +import twitter.cache.TwitterImageCache; import twitter.util.HTMLEncode; import twitter4j.Status; @@ -128,9 +130,12 @@ public class TweetTableModel extends DefaultTableModel { // 1:ユーザのイメージ // 2:ユーザのつぶやき // 3.つぶやいた時間,つぶやいたクライアントなど + + //イメージデータをキャッシュから取得 + TwitterImageCache imageCache = TwitterImageCache.getInstance(); Object[] obj = { - new ImageIcon(t.getUser().getProfileImageURL()), + imageCache.getProfileImage( t.getUser().getProfileImageURL().toString() ), "" + t.getUser().getScreenName() + " " + HTMLEncode.encode(t.getText()), tweetDateFormat.format(t.getCreatedAt()) + "
" @@ -143,6 +148,42 @@ public class TweetTableModel extends DefaultTableModel { } // super.addRow(obj); } + + /** + * Tweetをテーブルに追加 + * @param list + */ + public void insertTweet(List list) { + //イメージデータをキャッシュから取得 + TwitterImageCache imageCache = TwitterImageCache.getInstance(); + + //イメージだけあらかじめ取得しておく + ImageIcon[] icons = new ImageIcon[ list.size() ]; + int i=0; + for(Status t : list ) { + icons[i] = imageCache.getProfileImage( t.getUser().getProfileImageURL().toString() ); + i++; + } + + int j=0; + for(Status t : list ) { + //キャッシュを使ってイメージを取得 + Object[] obj = { + icons[j], + "" + t.getUser().getScreenName() + " " + + HTMLEncode.encode(t.getText()), + tweetDateFormat.format(t.getCreatedAt()) + "
" + + t.getSource() + "から" }; + System.out.println( t.getUser().getProfileImageURL() ); + try { + super.insertRow(0, obj); + tweetStatus.add(0, t); + } catch (Exception e) { + e.printStackTrace(); + } + j++; + } + } /** * Tweetテーブルに存在する要素数 diff --git a/src/twitter/manage/TweetConfiguration.java b/src/twitter/manage/TweetConfiguration.java index 61e5d43..7b429a4 100644 --- a/src/twitter/manage/TweetConfiguration.java +++ b/src/twitter/manage/TweetConfiguration.java @@ -9,6 +9,8 @@ package twitter.manage; public class TweetConfiguration { // ログを保存するディレクトリ名 public static final String LOG_DIRECTORY = "log"; + // キャッシュデータを保存するディレクトリ名 + public static final String CACHE_DIRECTORY = "cache"; // タイムラインを保存するディレクトリ名 public static final String TIMELINE_DIRECTORY = "timeline"; // 文字コード @@ -21,8 +23,8 @@ public class TweetConfiguration { public static final String TWEET_INFO_FILENAME = "previnfo.properties"; // 基本設定を保存するファイル名 public static final String BASIC_SETTING_FILENAME = "setting.properties"; - //アカウント情報を保存するファイル名 - public static final String ACCOUNT_INFORMATION_FILENAME = "account.properties"; - // search twitterのURL - public static final String SEARCH_TWITTER_HOSTNAME = "search.twitter.com"; + // アカウント情報を保存するファイル名 + public static final String ACCOUNT_INFORMATION_FILENAME = "account.properties"; + // search twitterのURL + public static final String SEARCH_TWITTER_HOSTNAME = "search.twitter.com"; } -- 2.11.0