OSDN Git Service

a758753f1227327ad7eae24c71d1d153791b6da8
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / AbsRecordedListView.java
1 package tainavi;\r
2 \r
3 import java.awt.BorderLayout;\r
4 import java.awt.Color;\r
5 import java.awt.Component;\r
6 import java.awt.Dimension;\r
7 import java.awt.event.ComponentAdapter;\r
8 import java.awt.event.ComponentEvent;\r
9 import java.util.ArrayList;\r
10 import java.util.Comparator;\r
11 import java.util.HashMap;\r
12 \r
13 import javax.swing.JPanel;\r
14 import javax.swing.JScrollPane;\r
15 import javax.swing.JTable;\r
16 import javax.swing.event.ListSelectionEvent;\r
17 import javax.swing.event.ListSelectionListener;\r
18 import javax.swing.event.TableModelEvent;\r
19 import javax.swing.table.DefaultTableColumnModel;\r
20 import javax.swing.table.DefaultTableModel;\r
21 import javax.swing.table.TableCellRenderer;\r
22 import javax.swing.table.TableColumn;\r
23 import javax.swing.table.TableModel;\r
24 import javax.swing.table.TableRowSorter;\r
25 \r
26 \r
27 /**\r
28  * 録画結果一覧タブのクラス\r
29  */\r
30 public abstract class AbsRecordedListView extends JPanel {\r
31 \r
32         private static final long serialVersionUID = 1L;\r
33 \r
34         public static void setDebug(boolean b) {debug = b; }\r
35         private static boolean debug = false;\r
36         \r
37 \r
38         /*******************************************************************************\r
39          * 抽象メソッド\r
40          ******************************************************************************/\r
41         \r
42         protected abstract Env getEnv();\r
43         protected abstract Bounds getBoundsEnv();\r
44         \r
45         protected abstract HDDRecorderList getRecorderList();\r
46         \r
47         //protected abstract StatusWindow getStWin(); \r
48         //protected abstract StatusTextArea getMWin();\r
49         //protected abstract AbsReserveDialog getReserveDialog();\r
50         \r
51         protected abstract Component getParentComponent();\r
52         \r
53         protected abstract void ringBeep();\r
54         \r
55         /**\r
56          * @see Viewer.VWToolBar#getSelectedRecorder()\r
57          */\r
58         protected abstract String getSelectedRecorderOnToolbar();\r
59         \r
60 \r
61         /*******************************************************************************\r
62          * 定数\r
63          ******************************************************************************/\r
64         \r
65         private static final String MSGID = "[録画結果一覧] ";\r
66         private static final String ERRID = "[ERROR]"+MSGID;\r
67         private static final String DBGID = "[DEBUG]"+MSGID;\r
68         \r
69         \r
70         /*******************************************************************************\r
71          * 部品\r
72          ******************************************************************************/\r
73         \r
74         // オブジェクト\r
75         private final Env env = getEnv();\r
76         private final Bounds bounds = getBoundsEnv();\r
77         private final HDDRecorderList recorders = getRecorderList();\r
78 \r
79         //private final StatusWindow StWin = getStWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
80         //private final StatusTextArea MWin = getMWin();                        // これは起動時に作成されたまま変更されないオブジェクト\r
81         \r
82         private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
83         \r
84         // メソッド\r
85         //private void StdAppendMessage(String message) { System.out.println(message); }\r
86         //private void StdAppendError(String message) { System.err.println(message); }\r
87         //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
88         //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
89         \r
90         /**\r
91          * カラム定義\r
92          */\r
93         \r
94         public static HashMap<String,Integer> getColumnIniWidthMap() {\r
95                 if (rcmap.size() == 0 ) {\r
96                         for ( RecedColumn rc : RecedColumn.values() ) {\r
97                                 rcmap.put(rc.toString(),rc.getIniWidth());      // toString()!\r
98                         }\r
99                 }\r
100                 return rcmap;\r
101         }\r
102         \r
103         private static final HashMap<String,Integer> rcmap = new HashMap<String, Integer>();\r
104         \r
105         public static enum RecedColumn {\r
106                 START           ("開始",                      150),\r
107                 END                     ("終了",                      50),\r
108                 LENGTH          ("長さ",                      50),\r
109                 DROP            ("ドロップ",                50),\r
110                 DROPMPEG        ("MPEG",                50),\r
111                 TITLE           ("番組タイトル",  300),\r
112                 CHNAME          ("チャンネル名",  150),\r
113                 RESULT          ("録画結果",                200),\r
114                 RECORDER        ("レコーダ",                200),\r
115                 ;\r
116 \r
117                 private String name;\r
118                 private int iniWidth;\r
119 \r
120                 private RecedColumn(String name, int iniWidth) {\r
121                         this.name = name;\r
122                         this.iniWidth = iniWidth;\r
123                 }\r
124 \r
125                 public String getName() {\r
126                         return name;\r
127                 }\r
128 \r
129                 public int getIniWidth() {\r
130                         return iniWidth;\r
131                 }\r
132                 \r
133                 public int getColumn() {\r
134                         return ordinal();\r
135                 }\r
136         };\r
137         \r
138 \r
139         /*******************************************************************************\r
140          * コンポーネント\r
141          ******************************************************************************/\r
142 \r
143         private class RecordedItem extends RowItem implements Cloneable {\r
144         \r
145                 String start;   // YYYY/MM/DD(WD) hh:mm\r
146                 String end;                     // hh:mm\r
147                 Integer length;\r
148                 Integer drop;\r
149                 Integer dropmpeg;\r
150                 String title;\r
151                 String chname;\r
152                 String result;\r
153                 String recorder;\r
154 \r
155                 String hide_detail;\r
156                 Boolean hide_succeeded;\r
157                 \r
158                 @Override\r
159                 protected void myrefresh(RowItem o) {\r
160                         RecordedItem c = (RecordedItem) o;\r
161                         \r
162                         c.addData(start);\r
163                         c.addData(end);\r
164                         c.addData(length);\r
165                         c.addData(drop);\r
166                         c.addData(dropmpeg);\r
167                         c.addData(title);\r
168                         c.addData(chname);\r
169                         c.addData(result);\r
170                         c.addData(recorder);\r
171                 }\r
172                 \r
173                 public RecordedItem clone() {\r
174                         return (RecordedItem) super.clone();\r
175                 }\r
176         }\r
177 \r
178         // ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない\r
179         private class ReservedTableModel extends DefaultTableModel {\r
180 \r
181                 private static final long serialVersionUID = 1L;\r
182                 \r
183                 @Override\r
184                 public Object getValueAt(int row, int column) {\r
185                         RecordedItem c = rowView.get(row); \r
186                         if ( c.getColumnCount() > column ) {\r
187                                 if ( column == RecedColumn.LENGTH.getColumn() ) {\r
188                                         return String.valueOf(c.length)+"m";\r
189                                 }\r
190                                 return c.get(column);\r
191                         }\r
192                         return null;\r
193                 }\r
194                 \r
195                 @Override\r
196                 public int getRowCount() {\r
197                         return rowView.size();\r
198                 }\r
199 \r
200                 public ReservedTableModel(String[] colname, int i) {\r
201                         super(colname,i);\r
202                 }\r
203                 \r
204         }\r
205         \r
206         //private final RecordedItem sa = new RecordedItem();\r
207         \r
208         private JScrollPane jsc_list = null;\r
209         private JScrollPane jsc_detail = null;\r
210         private JTextAreaWithPopup jta_detail = null;\r
211         private JNETable jTable_reced = null;\r
212         private JTable jTable_rowheader = null;\r
213 \r
214         private DefaultTableModel tableModel_reced = null;\r
215         \r
216         private DefaultTableModel rowheaderModel_reced = null;\r
217 \r
218         // 表示用のテーブル\r
219         private final RowItemList<RecordedItem> rowView = new RowItemList<RecordedItem>();\r
220         \r
221         // テーブルの実体\r
222         private final RowItemList<RecordedItem> rowData = new RowItemList<RecordedItem>();\r
223         \r
224         /*******************************************************************************\r
225          * コンストラクタ\r
226          ******************************************************************************/\r
227         \r
228         public AbsRecordedListView() {\r
229                 \r
230                 super();\r
231                 \r
232                 this.setLayout(new BorderLayout());\r
233                 this.add(getJScrollPane_list(), BorderLayout.CENTER);\r
234                 this.add(getJTextPane_detail(), BorderLayout.PAGE_END);\r
235                 \r
236                 // バグ対応\r
237                 /*\r
238                 if ( bounds.getRecordedColumnSize() == null ) {\r
239                         System.err.println(ERRID+"なんらかの不具合によりテーブルのカラム幅設定が取得できませんでした。設定はリセットされました。申し訳ありません。");\r
240                         bounds.setRecordedColumnSize(rcmap);\r
241                 }\r
242                 else {\r
243                         for ( Entry<String, Integer> en : rcmap.entrySet() ) {\r
244                                 try {\r
245                                         bounds.getListedColumnSize().get(en.getKey());\r
246                                 }\r
247                                 catch (NullPointerException e) {\r
248                                         System.err.println(ERRID+en.getKey()+", "+e.toString());\r
249                                         bounds.getListedColumnSize().put(en.getKey(),en.getValue());\r
250                                 }\r
251                         }\r
252                 }\r
253                 */\r
254                 \r
255                 \r
256                 //\r
257                 this.addComponentListener(cl_tabShown);\r
258         }\r
259 \r
260         /*******************************************************************************\r
261          * アクション\r
262          ******************************************************************************/\r
263         \r
264         // 対外的な\r
265         \r
266         /**\r
267          * 予約一覧を描画してほしいかなって\r
268          * ★synchronized(rowData)★\r
269          * @see #cl_tabShown\r
270          */\r
271         public void redrawRecordedList() {\r
272                 // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
273                 synchronized ( rowView ) {\r
274                         _redrawRecordedList();\r
275                 }\r
276         }\r
277         \r
278         private void _redrawRecordedList() {\r
279                 //\r
280                 rowData.clear();\r
281                 \r
282                 // 選択されたレコーダ\r
283                 String myself = getSelectedRecorderOnToolbar();\r
284                 HDDRecorderList recs = recorders.findInstance(myself);\r
285 \r
286                 for ( HDDRecorder recorder : recs )\r
287                 {\r
288                         if ( recorder.isBackgroundOnly() ) {\r
289                                 continue;\r
290                         }\r
291                         \r
292                         // 並べ替えるために新しいリストを作成する\r
293                         for ( RecordedInfo ro : recorder.getRecorded() ) {\r
294                                                                 \r
295                                 RecordedItem sa = new RecordedItem();\r
296                                 \r
297                                 sa.start = ro.getDate()+" "+ro.getAhh()+":"+ro.getAmm();        // YYYY/MM/DD(WD) hh:mm\r
298                                 sa.end = ro.getZhh()+":"+ro.getZmm();\r
299                                 sa.length = ro.getLength();\r
300                                 sa.drop = ro.getDrop();\r
301                                 sa.dropmpeg = ro.getDrop_mpeg();\r
302                                 sa.title = ro.getTitle();\r
303                                 sa.chname = ro.getCh_name();\r
304                                 sa.result = ro.getResult();\r
305                                 sa.recorder = recorder.Myself();\r
306                                 \r
307                                 sa.hide_detail = ro.getDetail();\r
308                                 sa.hide_succeeded = ro.getSucceeded();\r
309                                 \r
310                                 sa.fireChanged();\r
311                                 \r
312                                 addRow(sa);\r
313                         }\r
314                 }\r
315                 \r
316                 // 表示用\r
317                 rowView.clear();\r
318                 for ( RecordedItem a : rowData ) {\r
319                         rowView.add(a);\r
320                 }\r
321 \r
322                 tableModel_reced.fireTableDataChanged();\r
323                 ((DefaultTableModel)jTable_rowheader.getModel()).fireTableDataChanged();\r
324                 \r
325                 jta_detail.setText(null);\r
326 \r
327                 //jta_detail.setText("レコーダから予約結果の一覧を取得して表示します。現在の対応レコーダはTvRock/EpgDataCap_Bonのみです。");\r
328         }\r
329         \r
330         \r
331         /**\r
332          * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)\r
333          */\r
334         public void redrawListByKeywordFilter(SearchKey keyword, String target) {\r
335                 \r
336                 rowView.clear();\r
337                 \r
338                 // 情報を一行ずつチェックする\r
339                 if ( keyword != null ) {\r
340                         for ( RecordedItem a : rowData ) {\r
341                                 \r
342                                 ProgDetailList tvd = new ProgDetailList();\r
343                                 tvd.title = a.title;\r
344                                 tvd.titlePop = TraceProgram.replacePop(tvd.title);\r
345                                 \r
346                                 // タイトルを整形しなおす\r
347                                 boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);\r
348                                 \r
349                                 if ( isFind ) {\r
350                                         rowView.add(a);\r
351                                 }\r
352                         }\r
353                 }\r
354                 else {\r
355                         for ( RecordedItem a : rowData ) {\r
356                                 rowView.add(a);\r
357                         }\r
358                 }\r
359                 \r
360                 // fire!\r
361                 tableModel_reced.fireTableDataChanged();\r
362                 rowheaderModel_reced.fireTableDataChanged();\r
363         }\r
364         \r
365         /**\r
366          * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)\r
367          */\r
368         public void copyColumnWidth() {\r
369                 DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_reced.getColumnModel();\r
370                 TableColumn column = null;\r
371                 for ( RecedColumn rc : RecedColumn.values() ) {\r
372                         if ( rc.getIniWidth() < 0 ) {\r
373                                 continue;\r
374                         }\r
375                         column = columnModel.getColumn(rc.ordinal());\r
376                         //bounds.getRecordedColumnSize().put(rc.toString(), column.getPreferredWidth());\r
377                 }\r
378         }\r
379         \r
380         /**\r
381          * テーブルの行番号の表示のON/OFF\r
382          */\r
383         public void setRowHeaderVisible(boolean b) {\r
384                 jsc_list.getRowHeader().setVisible(b);\r
385         }\r
386         \r
387         // 内部的な\r
388         \r
389         /**\r
390          * テーブル(の中の人)に追加\r
391          */\r
392         private void addRow(RecordedItem data) {\r
393                 // 有効データ\r
394                 int n=0;\r
395                 for ( ; n<rowData.size(); n++ ) {\r
396                         RecordedItem c = rowData.get(n);\r
397                         if ( c.start.compareTo(data.start) < 0 ) {\r
398                                 break;\r
399                         }\r
400                 }\r
401                 rowData.add(n,data);\r
402         }\r
403 \r
404         /*******************************************************************************\r
405          * リスナー\r
406          ******************************************************************************/\r
407         \r
408         /**\r
409          * タブが開かれたら表を書き換える\r
410          * ★synchronized(rowData)★\r
411          * @see #redrawRecordedList()\r
412          */\r
413         private final ComponentAdapter cl_tabShown = new ComponentAdapter() {\r
414                 @Override\r
415                 public void componentShown(ComponentEvent e) {\r
416                         // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★\r
417                         synchronized ( rowView ) {\r
418                                 _redrawRecordedList();\r
419                         }\r
420                 }\r
421         };\r
422 \r
423         /**\r
424          *  行を選択すると詳細が表示されるようにする\r
425          */\r
426         private final ListSelectionListener lsSelectListner = new ListSelectionListener() {\r
427                 @Override\r
428                 public void valueChanged(ListSelectionEvent e) {\r
429                         if(e.getValueIsAdjusting()) return;\r
430                         if (jTable_reced.getSelectedRow() >= 0) {\r
431                                 int row = jTable_reced.convertRowIndexToModel(jTable_reced.getSelectedRow());\r
432                                 RecordedItem c = rowView.get(row);\r
433                                 jta_detail.setText(c.hide_detail);\r
434                                 jta_detail.setCaretPosition(0);\r
435                         }\r
436                 }\r
437         };\r
438         \r
439         /*******************************************************************************\r
440          * コンポーネント\r
441          ******************************************************************************/\r
442 \r
443         private JScrollPane getJScrollPane_list() {\r
444                 \r
445                 if ( jsc_list == null ) {\r
446                         jsc_list = new JScrollPane();\r
447                         \r
448                         jsc_list.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowView));\r
449                         jsc_list.setViewportView(getNETable_reced());\r
450                         \r
451                         Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);\r
452                         jsc_list.getRowHeader().setPreferredSize(d);\r
453                         \r
454                         this.setRowHeaderVisible(env.getRowHeaderVisible());\r
455                 }\r
456                 \r
457                 return jsc_list;\r
458         }\r
459 \r
460         private JScrollPane getJTextPane_detail() {\r
461                 if ( jsc_detail == null ) {\r
462                         jsc_detail = new JScrollPane(jta_detail = new JTextAreaWithPopup(),JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
463                         jta_detail.setRows(8);\r
464                         jta_detail.setEditable(false);\r
465                         jta_detail.setBackground(Color.LIGHT_GRAY);\r
466                 }\r
467                 return jsc_detail;\r
468         }\r
469         \r
470         private JNETable getNETable_reced() {\r
471                 if (jTable_reced == null) {\r
472                         \r
473                         ArrayList<String> cola = new ArrayList<String>();\r
474                         for ( RecedColumn rc : RecedColumn.values() ) {\r
475                                 if ( rc.getIniWidth() >= 0 ) {\r
476                                         cola.add(rc.getName());\r
477                                 }\r
478                         }\r
479                         String[] colname = cola.toArray(new String[0]);\r
480                         \r
481                         tableModel_reced = new ReservedTableModel(colname, 0);\r
482                         jTable_reced = new JNETableRecorded(tableModel_reced, false);\r
483                         jTable_reced.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);\r
484                         \r
485                         // ヘッダのモデル\r
486                         rowheaderModel_reced = (DefaultTableModel) jTable_rowheader.getModel();\r
487                         \r
488                         // ソータを付ける\r
489                         TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_reced);\r
490                         jTable_reced.setRowSorter(sorter);\r
491                         \r
492                         // 数値でソートする項目用の計算式(番組長とか)\r
493                         final Comparator<String> titlecomp = new Comparator<String>() {\r
494                                 \r
495                                 @Override\r
496                                 public int compare(String o1, String o2) {\r
497                                         String t1 = TraceProgram.replacePop(o1.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
498                                         String t2 = TraceProgram.replacePop(o2.replaceAll(TVProgram.titlePrefixRemoveExpr, "")).replaceFirst(TVProgram.epnoNormalizeExpr, "$1\\0$2");\r
499                                         return t1.compareTo(t2);\r
500                                 }\r
501                         };\r
502                         \r
503                         // ソーターの効かない項目用の計算式(重複マーク)\r
504                         final Comparator<String> noncomp = new Comparator<String>() {\r
505                                 @Override\r
506                                 public int compare(String o1, String o2) {\r
507                                         return 0;\r
508                                 }\r
509                         };\r
510                         \r
511                         sorter.setComparator(jTable_reced.getColumn(RecedColumn.TITLE.getName()).getModelIndex(),titlecomp);\r
512                         sorter.setComparator(jTable_reced.getColumn(RecedColumn.END.getName()).getModelIndex(),noncomp);\r
513                         \r
514                         // 各カラムの幅\r
515                         DefaultTableColumnModel columnModel = (DefaultTableColumnModel)jTable_reced.getColumnModel();\r
516                         TableColumn column = null;\r
517                         for ( RecedColumn rc : RecedColumn.values() ) {\r
518                                 if ( rc.getIniWidth() < 0 ) {\r
519                                         continue;\r
520                                 }\r
521                                 column = columnModel.getColumn(rc.ordinal());\r
522                                 //column.setPreferredWidth(bounds.getRecordedColumnSize().get(rc.toString()));\r
523                                 column.setPreferredWidth(rc.getIniWidth());\r
524                         }\r
525                         \r
526                         // 詳細表示\r
527                         jTable_reced.getSelectionModel().addListSelectionListener(lsSelectListner);\r
528                 }\r
529                 return jTable_reced;\r
530         }\r
531 \r
532         \r
533         \r
534         \r
535         /*******************************************************************************\r
536          * 表表示\r
537          ******************************************************************************/\r
538         \r
539         private class JNETableRecorded extends JNETable {\r
540 \r
541                 private static final long serialVersionUID = 1L;\r
542 \r
543                 private Color failedColor = new Color(255,255,0);\r
544                 private Color dropMpegColor = new Color(255,0,0);\r
545                 private Color dropColor = new Color(255,204,204);\r
546                 \r
547                 private int prechkrow = -1;\r
548                 private boolean prechksucceeded = false;\r
549                 \r
550                 @Override\r
551                 public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {\r
552                         Component c = super.prepareRenderer(tcr, row, column);\r
553                         Color fgColor = null;\r
554                         Color bgColor = null;\r
555                         if(isRowSelected(row)) {\r
556                                 fgColor = this.getSelectionForeground();\r
557                                 bgColor = this.getSelectionBackground();\r
558                         }\r
559                         else {\r
560                                 fgColor = this.getForeground();\r
561                                 \r
562                                 int xrow = this.convertRowIndexToModel(row);\r
563                                 RecordedItem item = rowView.get(xrow);\r
564                                 \r
565                                 if ( ! item.hide_succeeded ) {\r
566                                         bgColor = failedColor;\r
567                                 }\r
568                                 else if ( item.dropmpeg > 0 ) {\r
569                                         bgColor = dropMpegColor;\r
570                                 }\r
571                                 else if ( item.drop > 0 ) {\r
572                                         bgColor = dropColor;\r
573                                 }\r
574                                 else {\r
575                                         bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());\r
576                                 }\r
577                         }\r
578                         c.setForeground(fgColor);\r
579                         c.setBackground(bgColor);\r
580                         return c;\r
581                 }\r
582                 \r
583                 // 連続して同じ行へのアクセスがあったら計算を行わず前回のままにする\r
584                 private boolean isRowPassed(int prow) {\r
585                         \r
586                         if(prechkrow == prow) {\r
587                                 return prechksucceeded;\r
588                         }\r
589 \r
590                         int row = this.convertRowIndexToModel(prow);\r
591                         RecordedItem c = rowView.get(row);\r
592 \r
593                         {\r
594                                 // 実行可能かどうか\r
595                                 prechkrow = prow;\r
596                                 prechksucceeded = c.hide_succeeded;\r
597                                 return prechksucceeded;\r
598                         }\r
599                 }\r
600                 \r
601                 //\r
602                 @Override\r
603                 public void tableChanged(TableModelEvent e) {\r
604                         reset();\r
605                         super.tableChanged(e);\r
606                 }\r
607                 \r
608                 private void reset() {\r
609                         prechkrow = -1;\r
610                         prechksucceeded = true;\r
611                 }\r
612                 \r
613                 /*\r
614                  * コンストラクタ\r
615                  */\r
616                 public JNETableRecorded(boolean b) {\r
617                         super(b);\r
618                 }\r
619                 public JNETableRecorded(TableModel d, boolean b) {\r
620                         super(d,b);\r
621                 }\r
622         }\r
623 \r
624 }\r