• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

javaandroidc++linuxc#objective-ccocoa誰得qtrubypythonwindowsphpgamebathyscapheguic翻訳omegattwitterframeworkbtronvb.net計画中(planning stage)testdomarduinodirectxpreviewerゲームエンジン

タイニー番組ナビゲータ本体


Commit MetaInfo

Révision7180630f2ab4a15dff1534100897db19a391b5f6 (tree)
l'heure2020-08-08 10:49:51
AuteurMasahiko Kimura <mkimura@u01....>
CommiterMasahiko Kimura

Message de Log

Ver.3.22.18β+1.12.14 (2020/08/08)

1.[テレビ王国]ドメインの変更に対応(https://tv.so-net.ne.jp/https://www.tvkingdom.jp/)
2.[新聞形式]深夜0時前から始まり翌日5時以降に終わる長時間番組を予約すると予約枠が翌日全体に表示される問題の対応
3.[予約一覧]java.lang.NumberFormatExceptionで異常終了する問題の対応

Change Summary

Modification

--- a/TinyBannavi/src/tainavi/AbsPaperView.java
+++ b/TinyBannavi/src/tainavi/AbsPaperView.java
@@ -2037,8 +2037,11 @@ public abstract class AbsPaperView extends JPanel implements TickTimerListener,H
20372037 }
20382038 else if (startDateTime.compareTo(topDateTime) < 0 && topDateTime.compareTo(endDateTime) < 0) {
20392039 // 表示開始位置が番組の途中にある
2040+ GregorianCalendar ct = CommonUtils.getCalendar(topDateTime);
2041+ int add = ca.get(Calendar.DAY_OF_MONTH) == ct.get(Calendar.DAY_OF_MONTH) ? 0 : -1;
2042+
20402043 row = 0;
2041- length = recmin - (TIMEBAR_START*60 - ahh*60 - amm);
2044+ length = recmin - (TIMEBAR_START*60 - add*24*60 - ahh*60 - amm);
20422045 }
20432046 else {
20442047 return;
--- a/TinyBannavi/src/tainavi/AbsTitleDialog.java
+++ b/TinyBannavi/src/tainavi/AbsTitleDialog.java
@@ -1,1197 +1,1197 @@
1-package tainavi;
2-
3-import java.awt.Color;
4-import java.awt.Component;
5-import java.awt.Dimension;
6-import java.awt.Point;
7-import java.awt.event.ActionEvent;
8-import java.awt.event.ActionListener;
9-import java.awt.event.KeyEvent;
10-import java.awt.event.KeyListener;
11-import java.awt.event.MouseEvent;
12-import java.awt.event.MouseListener;
13-import java.nio.charset.Charset;
14-import java.text.Normalizer;
15-import java.util.ArrayList;
16-import java.util.regex.Matcher;
17-import java.util.regex.Pattern;
18-
19-import javax.swing.DefaultListModel;
20-import javax.swing.JButton;
21-import javax.swing.JCheckBox;
22-import javax.swing.JDialog;
23-import javax.swing.JLabel;
24-import javax.swing.JList;
25-import javax.swing.JOptionPane;
26-import javax.swing.JPanel;
27-import javax.swing.JScrollPane;
28-import javax.swing.JTable;
29-import javax.swing.JTextField;
30-import javax.swing.ListCellRenderer;
31-import javax.swing.ListModel;
32-import javax.swing.ListSelectionModel;
33-import javax.swing.event.CellEditorListener;
34-import javax.swing.event.ChangeEvent;
35-import javax.swing.event.DocumentEvent;
36-import javax.swing.event.DocumentListener;
37-import javax.swing.event.ListSelectionEvent;
38-import javax.swing.event.ListSelectionListener;
39-import javax.swing.table.DefaultTableColumnModel;
40-import javax.swing.table.DefaultTableModel;
41-import javax.swing.table.TableCellRenderer;
42-import javax.swing.table.TableColumn;
43-
44-
45-/**
46- * フォルダー作成画面クラス
47- */
48-abstract class AbsTitleDialog extends JDialog {
49-
50- /*******************************************************************************
51- * 抽象メソッド
52- ******************************************************************************/
53- protected abstract StatusWindow getStWin();
54- protected abstract StatusTextArea getMWin();
55-
56- protected abstract HDDRecorder getSelectedRecorder();
57-
58- /*******************************************************************************
59- * 定数
60- ******************************************************************************/
61-
62- private static final String MSGID = "[タイトル情報] ";
63- private static final String ERRID = "[ERROR]"+MSGID;
64- private static final String DBGID = "[DEBUG]"+MSGID;
65-
66- // レイアウト関連
67-
68- private static final int SEP_WIDTH = 10;
69- private static final int SEP_HEIGHT = 10;
70-
71- private static final int PARTS_HEIGHT = 30;
72- private static final int TEXT_HEIGHT = 30;
73- private static final int RESERV_HEIGHT = 70;
74- private static final int LIST_HEIGHT = 300;
75-
76- private static final int LABEL_WIDTH = 500;
77- private static final int TEXT_WIDTH = 760;
78- private static final int LIST_WIDTH = 300;
79- private static final int CHAP_WIDTH = 450;
80-
81- private static final int BUTTON_WIDTH_S = 80;
82-
83- // その他の定数
84- private static final int MAX_TITLE_LENGTH = 80;
85-
86- private String folderNameWorking = "";
87- private HDDRecorder recorder = null;
88- ArrayList<TextValueSet> tvsFolder = null;
89- private final StatusWindow StWin = getStWin(); // これは起動時に作成されたまま変更されないオブジェクト
90- private final StatusTextArea MWin = getMWin(); // これは起動時に作成されたまま変更されないオブジェクト
91-
92- /*
93- * チャプターの列定義
94- */
95- public static enum ChapterColumn {
96- NO ("番号", 40),
97- TITLE ("チャプター名", 300),
98- DURATION ("時間", 80),
99- ;
100-
101- private String name;
102- private int iniWidth;
103-
104- private ChapterColumn(String name, int iniWidth) {
105- this.name = name;
106- this.iniWidth = iniWidth;
107- }
108-
109- public String getName() {
110- return name;
111- }
112-
113- public int getIniWidth() {
114- return iniWidth;
115- }
116-
117- public int getColumn() {
118- return ordinal();
119- }
120-
121- public boolean equals(String s) {
122- return name.equals(s);
123- }
124- };
125-
126- /*******************************************************************************
127- * 部品
128- ******************************************************************************/
129-
130- // コンポーネント
131-
132- private JPanel jPanel = null;
133- private JLabel jLabel_title = null;
134- private JComboBoxWithPopup jComboBox_title = null;
135- private JTextField jTextField_title = null;
136-
137- private JLabel jLabel_prog = null;
138- private JTextAreaWithPopup jTextArea_prog = null;
139-
140- private JLabel jLabel_folder = null;
141- private JList jList_folder = null;
142- private JButton jButton_selectAll = null;
143- private JButton jButton_deselectAll = null;
144- private JButton jButton_newFolder = null;
145-
146- private JLabel jLabel_chapter = null;
147- private JScrollPane chappane = null;
148- private ChapterTable chaptable = null;
149-
150- private JButton jButton_cancel = null;
151- private JButton jButton_ok = null;
152-
153- // コンポーネント以外
154- private boolean folderOnly = false;
155- private TitleInfo info = null;
156- private ProgDetailList prog = null;
157- private TVProgramList tvprograms = null;
158-
159- private boolean reg = false;
160-
161- public TitleInfo getTitleInfo() { return info; }
162-
163- /*
164- * フォルダリストのセルレンダラー
165- */
166- class FolderCellRenderer extends JCheckBox implements ListCellRenderer{
167- public FolderCellRenderer() {
168- }
169-
170- public Component getListCellRendererComponent(JList list, Object value,
171- int index, boolean isSelected, boolean cellHasFocus){
172-
173- /* 項目の値を読み出して改めて表示する */
174- JCheckBox checkBox = (JCheckBox)value;
175- setText(checkBox.getText());
176- setSelected(checkBox.isSelected());
177- return this;
178- }
179- }
180-
181- /*******************************************************************************
182- * コンストラクタ
183- ******************************************************************************/
184-
185- public AbsTitleDialog(boolean b) {
186- super();
187-
188- folderOnly = b;
189- reg = false;
190-
191- this.setModal(true);
192- this.setContentPane(getJPanel());
193-
194- // タイトルバーの高さも考慮する必要がある
195- Dimension d = getJPanel().getPreferredSize();
196- this.pack();
197- this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));
198- this.setResizable(false);
199- this.setTitle("タイトル情報");
200- }
201-
202- /*******************************************************************************
203- * アクション
204- ******************************************************************************/
205-
206- // 公開メソッド
207-
208- /**
209- * フォルダーが登録されたかな?
210- */
211- public boolean isRegistered() { return reg; }
212-
213- /*
214- * 画面をオープンする
215- * @param t 編集対象のタイトル情報
216- * @param tvs フォルダリスト
217- */
218- public void open(TitleInfo t, ProgDetailList l, String otitle) {
219- info = t;
220- prog = l;
221- recorder = getSelectedRecorder();
222- tvsFolder = recorder.getFolderList();
223-
224- updateTitleLabel();
225- updateTitleInfo(t, prog, otitle);
226-
227- String device_name = "[" + t.getRec_device() + "]";
228-
229- DefaultListModel model = new DefaultListModel();
230-
231- for (TextValueSet ts : tvsFolder ){
232- String folder_id = ts.getValue();
233- String folder_name = ts.getText();
234- if (folder_id.equals("0") || !folder_name.startsWith(device_name) )
235- continue;
236- JCheckBox box = new JCheckBox(ts.getText());
237-
238- if (t.containsFolder(ts.getValue()))
239- box.setSelected(true);
240-
241- model.addElement(box);
242- }
243-
244- jList_folder.setModel(model);
245-
246- updateFolderLabel();
247- }
248-
249- /*
250- * タイトル情報を更新する
251- */
252- private void updateTitleInfo(TitleInfo t, ProgDetailList pdl, String titleOld){
253- String title = t.getTitle();
254- jTextField_title.setText(title);
255-
256- jComboBox_title.removeAllItems();
257- jComboBox_title.addItem(title);
258- jComboBox_title.setSelectedIndex(0);
259-
260- // <番組名> #<No>「<サブタイトル>」形式の候補を用意する
261- if (pdl != null){
262- String no = getProgramNo(title, pdl);
263- String subtitle = getSubtitle(title, pdl);
264- String ptitle = getProgramTitle(title);
265- String ptitleOld = getProgramTitle(titleOld);
266-
267- String cand = formatTitleFromOld(ptitleOld, no, subtitle, titleOld);
268- if (cand != null)
269- jComboBox_title.addItem(cand);
270- else{
271- cand = formatDefaultTitle(ptitle, no, subtitle);
272- if (cand != null)
273- jComboBox_title.addItem(cand);
274- }
275- }
276-
277- // 番組情報が見つかったら、タイトルと詳細をセットする
278- if (pdl != null){
279- jTextArea_prog.setText(pdl.prefix_mark + pdl.title + pdl.postfix_mark + "\r\n" + pdl.detail);
280- }
281-
282- }
283-
284- /*
285- * プログラムタイトルを取得する
286- */
287- protected String getProgramTitle(String title){
288- if (title == null)
289- return null;
290-
291- String[] patterns = {
292- "^(.*?)(#|#|♯)",
293- "^(.*?)第.*?(話|回|羽)",
294- "^(.*?)(\\(|()[0-90-9]+(\\)|))"};
295-
296- for (String pat : patterns){
297- Matcher m = Pattern.compile(pat).matcher(title);
298- if (m.find()){
299- return m.group(1);
300- }
301- }
302-
303- return null;
304- }
305- /*
306- * 話数を取得する
307- */
308- protected String getProgramNo(String title, ProgDetailList pdl){
309- String pno1 = searchProgramNo(title);
310- if (pno1 != null)
311- return pno1;
312-
313- String pno2 = searchProgramNo(pdl.title + " " + pdl.detail);
314- if (pno2 != null)
315- return pno2;
316-
317- return "";
318- }
319-
320- /*
321- * 話数を抽出する
322- */
323- protected String searchProgramNo(String title){
324- if (title == null)
325- return null;
326-
327- String[] patterns = {
328- "(#|#|♯)( | ){0,1}([0-90-9]{1,3})",
329- "(\\(|()( | ){0,1}([0-90-9]{1,3})(\\)|)){0,1}",
330- "(第)( | ){0,1}([0-90-9]{1,3})(話|回|羽)"};
331- String ntitle = Normalizer.normalize(title, Normalizer.Form.NFKC);
332-
333- for (String pat : patterns){
334- Matcher m = Pattern.compile(pat).matcher(ntitle);
335- if (m.find()){
336- return m.group(3);
337- }
338- }
339-
340- return null;
341- }
342-
343- /*
344- * デフォルトのタイトルを整形する
345- */
346- protected String formatDefaultTitle(String ptitle, String no, String subtitle){
347- if (ptitle == null || no == null || subtitle == null)
348- return null;
349-
350- if (no.length() == 1){
351- Matcher m = Pattern.compile("0-9").matcher(no);
352- if (m.find())
353- no = "0" + no;
354- else
355- no = "0" + no;
356- }
357-
358- return ptitle + "#" + no + "「" + subtitle + "」";
359- }
360-
361- /*
362- * 古いタイトルから新らしいタイトルの候補を整形する
363- */
364- protected String formatTitleFromOld(String ptitle, String no, String subtitle, String otitle){
365- if (ptitle == null)
366- return null;
367-
368- String fno = formatProgramNo(no, otitle);
369- String fst = formatSubtitle(subtitle, otitle);
370- if (fno == null || fst == null)
371- return null;
372-
373- return ptitle + fno + fst;
374- }
375-
376- /*
377- * 古いタイトルから新しい話数を整形する
378- */
379- protected String formatProgramNo(String no, String otitle){
380- if (no == null || otitle == null)
381- return null;
382-
383- String[] patterns = {
384- "(#|#)([0-90-9]{1,3})",
385- "(\\(|()([0-90-9]{1,3})(\\)|))",
386- "(第)([0-90-9]{1,3})(話|回|羽)"
387- };
388-
389- for (String pat : patterns){
390- Matcher m = Pattern.compile(pat).matcher(otitle);
391- if (m.find()){
392- String pre = m.group(1);
393- String post = m.groupCount() > 2 ? m.group(3) : "";
394- String pno = m.group(2);
395- Matcher mw = Pattern.compile("(0-9)+").matcher(pno);
396- if (mw.find()){
397- no = CommonUtils.toZENNUM(no);
398- if (no.length() == 1)
399- no = "0" + no;
400- }
401- else{
402- if (no.length() == 1)
403- no = "0" + no;
404- }
405-
406- return pre + no + post;
407- }
408- }
409-
410- return null;
411- }
412-
413- /*
414- * 古いタイトルから新しいサブタイトルを整形する
415- */
416- protected String formatSubtitle(String subtitle, String otitle){
417- String[] patterns = {
418- "(「)(.*?)(」)",
419- "(『)(.*?)(』)",
420- "([)(.*?)(])",
421- "(【)(.*?)(】)",
422- "(\\[)(.*?)(\\])"
423- };
424-
425- for (String pat : patterns){
426- Matcher m = Pattern.compile(pat).matcher(otitle);
427- if (m.find())
428- return m.group(1) + subtitle + m.group(3);
429- }
430-
431- return null;
432- }
433-
434- /*
435- * サブタイトルを取得する
436- */
437- protected String getSubtitle(String title, ProgDetailList pdl){
438- String subc = searchSubtitleCombo(pdl.title + " " + pdl.detail);
439- if (subc != null)
440- return subc;
441-
442- String sub1 = searchSubtitle(title);
443- if (sub1 != null)
444- return sub1;
445-
446- String sub2 = searchSubtitle(pdl.title + " " + pdl.detail);
447- if (sub2 != null)
448- return sub2;
449-
450- return "";
451- }
452-
453- /*
454- * サブタイトルを抽出する
455- */
456- protected String searchSubtitleCombo(String title){
457- String[] patternsCombo = {
458- "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?「(.*?)」",
459- "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?『(.*?)』",
460- "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?【(.*?)】"};
461-
462- for (String pat : patternsCombo){
463- Matcher m = Pattern.compile(pat).matcher(title);
464- if (m.find())
465- return m.group(5);
466- }
467-
468- return null;
469- }
470-
471- protected String searchSubtitle(String title){
472- String[] patterns = {"「(.*?)」", "『(.*?)』", "【(.*?)】"};
473-
474- for (String pat : patterns){
475- Matcher m = Pattern.compile(pat).matcher(title);
476- if (m.find())
477- return m.group(1);
478- }
479-
480- return null;
481- }
482-
483- /*
484- * フォルダ一覧のラベルを更新する。選択中のフォルダの数を表示する
485- */
486- private void updateFolderLabel() {
487- int count = getSelectedFolderCount();
488- jLabel_folder.setText("フォルダ一覧(" + String.valueOf(count)+ "フォルダ選択中)");
489- }
490-
491- /*
492- * すべてのフォルダを選択する
493- * @param b 選択するか選択解除するか
494- */
495- private void selectAllFolders(boolean b) {
496- ListModel model = jList_folder.getModel();
497-
498- int num = model.getSize();
499-
500- for (int n=0; n<num; n++){
501- JCheckBox checkBox = (JCheckBox)model.getElementAt(n);
502- checkBox.setSelected(b);
503- }
504-
505- jList_folder.repaint();
506-
507- updateFolderLabel();
508- }
509-
510- /*
511- * 選択されているフォルダの数を返す
512- */
513- private int getSelectedFolderCount() {
514- int count = 0;
515-
516- ListModel model = jList_folder.getModel();
517-
518- int num = model.getSize();
519-
520- for (int n=0; n<num; n++){
521- JCheckBox checkBox = (JCheckBox)model.getElementAt(n);
522- if (checkBox.isSelected()){
523- count++;
524- }
525- }
526-
527- return count;
528- }
529-
530- /*******************************************************************************
531- * リスナー
532- ******************************************************************************/
533-
534- /**
535- * 「全選択」ボタンの処理
536- * フォルダーを全選択する
537- */
538- private final ActionListener al_selectAll = new ActionListener() {
539- @Override
540- public void actionPerformed(ActionEvent e) {
541- selectAllFolders(true);
542- }
543- };
544-
545- /**
546- * 「選択解除」ボタンの処理
547- * フォルダーを選択解除する
548- */
549- private final ActionListener al_deselectAll = new ActionListener() {
550- @Override
551- public void actionPerformed(ActionEvent e) {
552- selectAllFolders(false);
553- }
554- };
555-
556- /**
557- * 「新規」ボタンの処理
558- * フォルダーを新規作成する
559- */
560- private final ActionListener al_newFolder = new ActionListener() {
561- @Override
562- public void actionPerformed(ActionEvent e) {
563- // 「指定なし」が選ばれている場合は「追加」とみなし、フォルダ名の初期値は番組タイトルとする
564- // それ以外が選ばれている場合はそのフォルダの「変更」とみなし、フォルダ名の初期値は現在の値とする
565- HDDRecorder rec = recorder;
566-
567- VWFolderDialog dlg = new VWFolderDialog();
568- CommonSwingUtils.setLocationCenter(jPanel, dlg);
569-
570- String device_name = info.getRec_device();
571- String device_id = text2value(rec.getDeviceList(), device_name);
572- String title = jTextField_title.getText();
573-
574- String prefix = "[" + device_name + "] ";
575-
576- dlg.open(title);
577- dlg.setVisible(true);
578-
579- if (!dlg.isRegistered())
580- return;
581-
582- String nameNew = dlg.getFolderName();
583- String action = "作成";
584- folderNameWorking = nameNew;
585-
586- // フォルダー作成実行
587- StWin.clear();
588- new SwingBackgroundWorker(false) {
589- @Override
590- protected Object doWorks() throws Exception {
591- StWin.appendMessage(MSGID+"フォルダーを" + action + "します:"+folderNameWorking);
592-
593- boolean reg = rec.CreateRdFolder(device_id, nameNew);
594- if (reg){
595- MWin.appendMessage(MSGID+"フォルダーを正常に" + action + "できました:"+folderNameWorking);
596-
597- String folder_name = prefix + nameNew;
598-
599- tvsFolder = rec.getFolderList();
600-
601- TextValueSet t = new TextValueSet();
602- t.setText(folder_name);
603- t.setValue(text2value(tvsFolder, folder_name));
604- info.getRec_folder().add(t);
605-
606- DefaultListModel model = (DefaultListModel)jList_folder.getModel();
607- JCheckBox box = new JCheckBox(folder_name);
608- box.setSelected(true);
609- model.addElement(box);
610-
611- updateFolderLabel();
612- }
613- else {
614- MWin.appendError(ERRID+"フォルダーの" + action + "に失敗しました:"+folderNameWorking);
615-
616- if ( ! rec.getErrmsg().equals("")) {
617- MWin.appendMessage(MSGID+"[追加情報] "+rec.getErrmsg());
618- }
619- }
620-
621- return null;
622- }
623- @Override
624- protected void doFinally() {
625- StWin.setVisible(false);
626- }
627- }.execute();
628-
629- CommonSwingUtils.setLocationCenter(jPanel, (Component)StWin);
630- StWin.setVisible(true);
631- }
632- };
633-
634- /*
635- * チャプター一覧の選択変更の処理
636- * 今は特に何もしない
637- */
638- private final ListSelectionListener lsl_selected = new ListSelectionListener() {
639-
640- @Override
641- public void valueChanged(ListSelectionEvent e) {
642- System.out.println("lsl_selected "+e.toString());
643- if ( ! e.getValueIsAdjusting() ){
644- ListSelectionModel model = (ListSelectionModel) e.getSource();
645- if ( ! model.isSelectionEmpty() ){
646- int row = model.getMinSelectionIndex();
647-// ChConvItem c = rowData.get(row);
648-// jl_rel.setText(c.related);
649- }
650- }
651- }
652-
653- };
654-
655- // セルが編集された
656- private final CellEditorListener cel_edited = new CellEditorListener() {
657-
658- @Override
659- public void editingStopped(ChangeEvent e) {
660- System.out.println("cel_edited "+e.toString());
661-// jbtn_update.setEnabled(true);
662- }
663-
664- @Override
665- public void editingCanceled(ChangeEvent e) {
666- System.out.println("cel_edited "+e.toString());
667- }
668-
669- };
670-
671- /**
672- * 「登録」ボタンの処理
673- * タイトルの編集結果を登録する
674- */
675- private final ActionListener al_ok = new ActionListener() {
676- @Override
677- public void actionPerformed(ActionEvent e) {
678- registerData();
679- }
680- };
681-
682- private void registerData(){
683- // タイトル
684- String name = jTextField_title.getText();
685- if (name.equals("")) {
686- JOptionPane.showMessageDialog(jPanel, "タイトルがブランクです。");
687- return;
688- }
689-
690- String nameArib = AribCharMap.ConvStringToArib(name);
691- int lenrb = nameArib.getBytes(Charset.forName("Shift_JIS")).length;
692- if (!folderOnly && lenrb > MAX_TITLE_LENGTH){
693- JOptionPane.showMessageDialog(jPanel, "タイトルが長すぎます。(" + String.valueOf(lenrb) + "バイト)");
694- return;
695- }
696-
697- info.setTitle(name);
698-
699- String device_name = "[" + info.getRec_device() + "]";
700- ListModel model = jList_folder.getModel();
701-
702- // フォルダー
703- ArrayList<TextValueSet> tvs = new ArrayList<TextValueSet>();
704- int index = 0;
705- ArrayList<TextValueSet> tvsFolder = recorder.getFolderList();
706- for (TextValueSet ts : tvsFolder ){
707- String folder_id = ts.getValue();
708- String folder_name = ts.getText();
709- if (folder_id.equals("0") || !folder_name.startsWith(device_name) )
710- continue;
711-
712- JCheckBox checkBox = (JCheckBox)model.getElementAt(index);
713- if (checkBox.isSelected()){
714- TextValueSet t = new TextValueSet();
715- t.setValue(ts.getValue());
716- t.setText(ts.getText());
717- tvs.add(t);
718- }
719-
720- index++;
721- }
722-
723- info.setRec_folder(tvs);
724-
725- // チャプター情報はイベント処理中に取得済
726- ArrayList<ChapterInfo> chaps = info.getChapter();
727- for (int nc=0; nc<chaps.size(); nc++){
728- ChapterInfo ci = chaps.get(nc);
729-
730- String cname = ci.getName();
731- String cnameSub = CommonUtils.substringrb(cname,80);
732- if (!folderOnly && !cnameSub.equals(cname)){
733- JOptionPane.showMessageDialog(jPanel, "チャプター名が長すぎます。(CHNO=" + String.valueOf(nc+1) + ")");
734- return;
735- }
736- }
737-
738- reg = true;
739-
740- // ウィンドウを閉じる
741- dispose();
742- }
743-
744- /*
745- * タイトルラベルを更新する
746- */
747- private void updateTitleLabel(){
748- String name = jTextField_title.getText();
749- String nameArib = AribCharMap.ConvStringToArib(name);
750- int lenrb = nameArib.getBytes(Charset.forName("Shift_JIS")).length;
751- int restrb = MAX_TITLE_LENGTH - lenrb;
752-
753- if (jLabel_title != null){
754- if (restrb >= 0){
755- jLabel_title.setText("タイトル名(残り" + String.valueOf(restrb) + "バイト)");
756- jLabel_title.setForeground(Color.BLACK);
757- }
758- else{
759- jLabel_title.setText("タイトル名(" + String.valueOf(-restrb) + "バイトオーバー)");
760- jLabel_title.setForeground(Color.RED);
761- }
762- }
763- }
764-
765- /**
766- * キャンセルしたい
767- */
768- private final ActionListener al_cancel = new ActionListener() {
769- @Override
770- public void actionPerformed(ActionEvent e) {
771- dispose();
772- }
773- };
774-
775- /**
776- * 文書変更イベント処理
777- */
778- private final DocumentListener dl_titleChanged = new DocumentListener(){
779- @Override
780- public void insertUpdate(DocumentEvent e) {
781- updateTitleLabel();
782- }
783-
784- @Override
785- public void removeUpdate(DocumentEvent e) {
786- updateTitleLabel();
787- }
788-
789- @Override
790- public void changedUpdate(DocumentEvent e) {
791- updateTitleLabel();
792- }
793- };
794-
795- private final KeyListener kl_okcancel = new KeyListener() {
796- @Override
797- public void keyTyped(KeyEvent e) {
798- }
799- @Override
800- public void keyPressed(KeyEvent e) {
801- }
802- @Override
803- public void keyReleased(KeyEvent e) {
804- if (e.getKeyCode() == KeyEvent.VK_ENTER){
805- registerData();
806- }
807- else if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
808- dispose();
809- }
810- }
811- };
812-
813- private final KeyListener kl_cancel = new KeyListener() {
814- @Override
815- public void keyTyped(KeyEvent e) {
816- }
817- @Override
818- public void keyPressed(KeyEvent e) {
819- }
820- @Override
821- public void keyReleased(KeyEvent e) {
822- if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
823- dispose();
824- }
825- }
826- };
827-
828- /*******************************************************************************
829- * コンポーネント
830- ******************************************************************************/
831-
832- private JPanel getJPanel() {
833- if (jPanel == null) {
834- jPanel = new JPanel();
835- jPanel.setLayout(null);
836-
837- int y = SEP_HEIGHT;
838- int x = SEP_WIDTH;
839-
840- x = SEP_WIDTH;
841- JLabel label = getJLabel_title("タイトル名(最大80バイトまで)");
842- label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
843- jPanel.add(label);
844-
845- y += PARTS_HEIGHT;
846- JComboBoxWithPopup title = getJComboBox_title();
847- title.setBounds(x, y, TEXT_WIDTH, TEXT_HEIGHT);
848- jPanel.add(title);
849- if (folderOnly)
850- jTextField_title.disable();
851-
852- y += TEXT_HEIGHT;
853- label = getJLabel_prog("番組情報");
854- label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
855- jPanel.add(label);
856-
857- y += PARTS_HEIGHT;
858- JTextAreaWithPopup area = getJTextArea_prog();
859- area.setBounds(x, y, TEXT_WIDTH, RESERV_HEIGHT);
860- jPanel.add(area);
861- if (folderOnly)
862- area.disable();
863-
864- y += RESERV_HEIGHT;
865- label = getJLabel_folder("フォルダ一覧");
866- label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
867- jPanel.add(label);
868-
869- int xc = x+LIST_WIDTH + SEP_WIDTH;
870- label = getJLabel_chapter("チャプター一覧");
871- label.setBounds(xc, y, LABEL_WIDTH, PARTS_HEIGHT);
872- jPanel.add(label);
873-
874- y += PARTS_HEIGHT;
875- JScrollPane list = getJList_folder();
876- list.setBounds(x, y, LIST_WIDTH, LIST_HEIGHT);
877- jPanel.add(list);
878-
879- JScrollPane chap = getChapterPane();
880- chap.setBounds(xc, y, CHAP_WIDTH, LIST_HEIGHT);
881- jPanel.add(chap);
882- if (folderOnly)
883- chap.disable();
884-
885- y+= LIST_HEIGHT + SEP_HEIGHT;
886- JButton button = getJButton_selectAll("全選択");
887- button.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
888- jPanel.add(button);
889-
890- button = getJButton_deselectAll("選択解除");
891- button.setBounds(x+BUTTON_WIDTH_S+SEP_WIDTH, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
892- jPanel.add(button);
893-
894- button = getJButton_newFolder("新規");
895- button.setBounds(x+LIST_WIDTH-BUTTON_WIDTH_S, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
896- jPanel.add(button);
897-
898- x += TEXT_WIDTH - (BUTTON_WIDTH_S*2 + SEP_WIDTH);
899- JButton btnCreate = getJButton_ok("登録");
900- btnCreate.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
901- jPanel.add(btnCreate);
902-
903- x += BUTTON_WIDTH_S+SEP_WIDTH;
904- JButton btnCancel = getJButton_cancel("キャンセル");
905- btnCancel.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
906- jPanel.add(btnCancel);
907-
908- x += BUTTON_WIDTH_S+SEP_WIDTH;
909- y += PARTS_HEIGHT+SEP_HEIGHT;
910-
911- jPanel.setPreferredSize(new Dimension(x, y));
912- }
913-
914- return jPanel;
915- }
916-
917- //
918- private JLabel getJLabel_title(String s) {
919- if (jLabel_title == null) {
920- jLabel_title = new JLabel(s);
921- }
922- return(jLabel_title);
923- }
924-
925- //
926- private JComboBoxWithPopup getJComboBox_title() {
927- if (jComboBox_title == null) {
928- jComboBox_title = new JComboBoxWithPopup();
929- jComboBox_title.setEditable(true);
930-
931- }
932- if (jTextField_title == null) {
933- jTextField_title = ((JTextField)jComboBox_title.getEditor().getEditorComponent());
934- jTextField_title.addActionListener(al_ok);
935- jTextField_title.addKeyListener(kl_cancel);
936- jTextField_title.getDocument().addDocumentListener(dl_titleChanged);
937- }
938- return(jComboBox_title);
939- }
940-
941- //
942- private JLabel getJLabel_prog(String s) {
943- if (jLabel_prog == null) {
944- jLabel_prog = new JLabel(s);
945- }
946- return(jLabel_prog);
947- }
948-
949- //
950- private JTextAreaWithPopup getJTextArea_prog() {
951- if (jTextArea_prog == null) {
952- jTextArea_prog = new JTextAreaWithPopup();
953- jTextArea_prog.setLineWrap(true);
954- jTextArea_prog.addKeyListener(kl_cancel);
955- jTextArea_prog.setBorder(jComboBox_title.getBorder());
956- jTextArea_prog.setEditable(false);
957- }
958- return(jTextArea_prog);
959- }
960-
961- private JLabel getJLabel_folder(String s) {
962- if (jLabel_folder == null) {
963- jLabel_folder = new JLabel(s);
964- }
965- return(jLabel_folder);
966- }
967-
968- //
969- private JScrollPane getJList_folder() {
970- jList_folder = new JList();
971-
972- FolderCellRenderer renderer = new FolderCellRenderer();
973- jList_folder.setCellRenderer(renderer);
974- jList_folder.addMouseListener(new MouseListener() {
975- @Override
976- public void mouseClicked(MouseEvent e) {
977- /* クリックされた座標からIndex番号を取り出す */
978- Point p = e.getPoint();
979- int index = jList_folder.locationToIndex(p);
980-
981- JCheckBox checkBox = (JCheckBox)jList_folder.getModel().getElementAt(index);
982- if (checkBox.isSelected()){
983- checkBox.setSelected(false);
984- }else{
985- checkBox.setSelected(true);
986- }
987-
988- updateFolderLabel();
989-
990- /* 再描画してみる */
991- jList_folder.repaint();
992- }
993- @Override
994- public void mousePressed(MouseEvent e) {
995- }
996- @Override
997- public void mouseReleased(MouseEvent e) {
998- }
999- @Override
1000- public void mouseEntered(MouseEvent e) {
1001- }
1002- @Override
1003- public void mouseExited(MouseEvent e) {
1004- }
1005- });
1006- jList_folder.addKeyListener(kl_okcancel);
1007-
1008- JScrollPane sp = new JScrollPane();
1009- sp.getViewport().setView(jList_folder);
1010-
1011- return(sp);
1012- }
1013-
1014- //
1015- private JButton getJButton_selectAll(String s) {
1016- if (jButton_selectAll == null) {
1017- jButton_selectAll = new JButton();
1018- jButton_selectAll.setText(s);
1019-
1020- jButton_selectAll.addActionListener(al_selectAll);
1021- jButton_selectAll.addKeyListener(kl_cancel);
1022- }
1023- return(jButton_selectAll);
1024- }
1025-
1026- //
1027- private JButton getJButton_deselectAll(String s) {
1028- if (jButton_deselectAll == null) {
1029- jButton_deselectAll = new JButton();
1030- jButton_deselectAll.setText(s);
1031-
1032- jButton_deselectAll.addActionListener(al_deselectAll);
1033- jButton_deselectAll.addKeyListener(kl_cancel);
1034- }
1035- return(jButton_deselectAll);
1036- }
1037-
1038- private JButton getJButton_newFolder(String s) {
1039- if (jButton_newFolder == null) {
1040- jButton_newFolder = new JButton();
1041- jButton_newFolder.setText(s);
1042-
1043- jButton_newFolder.addActionListener(al_newFolder);
1044- jButton_newFolder.addKeyListener(kl_cancel);
1045- }
1046- return(jButton_newFolder);
1047- }
1048-
1049- private JLabel getJLabel_chapter(String s) {
1050- if (jLabel_chapter == null) {
1051- jLabel_chapter = new JLabel(s);
1052- }
1053- return(jLabel_chapter);
1054- }
1055-
1056- private JScrollPane getChapterPane() {
1057- if (chappane == null ) {
1058- chappane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
1059- chappane.setViewportView(getChapterTable());
1060- }
1061-
1062- return chappane;
1063- }
1064-
1065- private ChapterTable getChapterTable() {
1066- if (chaptable == null) {
1067- // カラム名の初期化
1068- ArrayList<String> cola = new ArrayList<String>();
1069- for ( ChapterColumn lc : ChapterColumn.values() ) {
1070- cola.add(lc.getName());
1071- }
1072- final String[] colname = cola.toArray(new String[0]);
1073-
1074- // テーブルの基本的な設定
1075- DefaultTableModel model = new DefaultTableModel(colname, 0);
1076-
1077- chaptable = new ChapterTable(model);
1078-
1079- // 各カラムの幅を設定する
1080- DefaultTableColumnModel columnModel = (DefaultTableColumnModel)chaptable.getColumnModel();
1081- TableColumn column = null;
1082- for ( ChapterColumn lc : ChapterColumn.values() ) {
1083- column = columnModel.getColumn(lc.ordinal());
1084- column.setPreferredWidth(lc.getIniWidth());
1085- }
1086-
1087- chaptable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
1088- chaptable.getTableHeader().setReorderingAllowed(false);
1089- chaptable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1090- chaptable.putClientProperty("terminateEditOnFocusLost", true);
1091- chaptable.setRowHeight(chaptable.getRowHeight()+12);
1092- chaptable.getSelectionModel().addListSelectionListener(lsl_selected);
1093-
1094- // 編集セルにリスナーを付ける
1095- TableColumn tc = chaptable.getColumn(ChapterColumn.TITLE.getName());
1096- EditorColumn ec = new EditorColumn();
1097- ec.addCellEditorListener(cel_edited);
1098- tc.setCellEditor(ec);
1099- }
1100-
1101- return chaptable;
1102- }
1103-
1104- //
1105- private JButton getJButton_ok(String s) {
1106- if (jButton_ok == null) {
1107- jButton_ok = new JButton();
1108- jButton_ok.setText(s);
1109-
1110- jButton_ok.addActionListener(al_ok);
1111- jButton_ok.addKeyListener(kl_cancel);
1112- }
1113- return(jButton_ok);
1114- }
1115-
1116- //
1117- private JButton getJButton_cancel(String s) {
1118- if (jButton_cancel == null) {
1119- jButton_cancel = new JButton();
1120- jButton_cancel.setText(s);
1121-
1122- jButton_cancel.addActionListener(al_cancel);
1123- jButton_cancel.addKeyListener(kl_cancel);
1124- }
1125- return jButton_cancel;
1126- }
1127-
1128- /*******************************************************************************
1129- * 独自コンポーネント
1130- ******************************************************************************/
1131- private class ChapterTable extends JTable {
1132-
1133- private static final long serialVersionUID = 1L;
1134-
1135- public ChapterTable(DefaultTableModel model) {
1136- this.setModel(model);
1137- }
1138-
1139- @Override
1140- public Object getValueAt(int row, int column) {
1141- if ( column == ChapterColumn.NO.ordinal() ) {
1142- return String.valueOf(row+1);
1143- }
1144- else if ( column == ChapterColumn.TITLE.ordinal() ) {
1145- return info.getChapter().get(row).getName();
1146- }
1147- else if ( column == ChapterColumn.DURATION.ordinal() ) {
1148- int duration = info.getChapter().get(row).getDuration();
1149- return String.format("%02d:%02d:%02d", duration/3600, (duration/60)%60, duration%60);
1150- }
1151- return null;
1152- }
1153-
1154- @Override
1155- public void setValueAt(Object aValue, int row, int column) {
1156- if ( column == ChapterColumn.TITLE.ordinal() ){
1157- info.getChapter().get(row).setName((String)aValue);
1158- }
1159- }
1160-
1161- @Override
1162- public int getRowCount() {
1163- if (info == null)
1164- return 0;
1165- return info.getChapter().size();
1166- }
1167-
1168- @Override
1169- public boolean isCellEditable(int row, int column) {
1170- if (folderOnly)
1171- return false;
1172-
1173- if (column == 1) {
1174- return true;
1175- }
1176- else {
1177- return false;
1178- }
1179- }
1180-
1181- @Override
1182- public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {
1183- Component comp = super.prepareRenderer(tcr, row, column);
1184- return comp;
1185- }
1186- }
1187-
1188- // 素直にHashMapつかっておけばよかった
1189- public String text2value(ArrayList<TextValueSet> tvs, String text) {
1190- for ( TextValueSet t : tvs ) {
1191- if (t.getText().equals(text)) {
1192- return(t.getValue());
1193- }
1194- }
1195- return("");
1196- }
1197-}
1+package tainavi;
2+
3+import java.awt.Color;
4+import java.awt.Component;
5+import java.awt.Dimension;
6+import java.awt.Point;
7+import java.awt.event.ActionEvent;
8+import java.awt.event.ActionListener;
9+import java.awt.event.KeyEvent;
10+import java.awt.event.KeyListener;
11+import java.awt.event.MouseEvent;
12+import java.awt.event.MouseListener;
13+import java.nio.charset.Charset;
14+import java.text.Normalizer;
15+import java.util.ArrayList;
16+import java.util.regex.Matcher;
17+import java.util.regex.Pattern;
18+
19+import javax.swing.DefaultListModel;
20+import javax.swing.JButton;
21+import javax.swing.JCheckBox;
22+import javax.swing.JDialog;
23+import javax.swing.JLabel;
24+import javax.swing.JList;
25+import javax.swing.JOptionPane;
26+import javax.swing.JPanel;
27+import javax.swing.JScrollPane;
28+import javax.swing.JTable;
29+import javax.swing.JTextField;
30+import javax.swing.ListCellRenderer;
31+import javax.swing.ListModel;
32+import javax.swing.ListSelectionModel;
33+import javax.swing.event.CellEditorListener;
34+import javax.swing.event.ChangeEvent;
35+import javax.swing.event.DocumentEvent;
36+import javax.swing.event.DocumentListener;
37+import javax.swing.event.ListSelectionEvent;
38+import javax.swing.event.ListSelectionListener;
39+import javax.swing.table.DefaultTableColumnModel;
40+import javax.swing.table.DefaultTableModel;
41+import javax.swing.table.TableCellRenderer;
42+import javax.swing.table.TableColumn;
43+
44+
45+/**
46+ * フォルダー作成画面クラス
47+ */
48+abstract class AbsTitleDialog extends JDialog {
49+
50+ /*******************************************************************************
51+ * 抽象メソッド
52+ ******************************************************************************/
53+ protected abstract StatusWindow getStWin();
54+ protected abstract StatusTextArea getMWin();
55+
56+ protected abstract HDDRecorder getSelectedRecorder();
57+
58+ /*******************************************************************************
59+ * 定数
60+ ******************************************************************************/
61+
62+ private static final String MSGID = "[タイトル情報] ";
63+ private static final String ERRID = "[ERROR]"+MSGID;
64+ private static final String DBGID = "[DEBUG]"+MSGID;
65+
66+ // レイアウト関連
67+
68+ private static final int SEP_WIDTH = 10;
69+ private static final int SEP_HEIGHT = 10;
70+
71+ private static final int PARTS_HEIGHT = 30;
72+ private static final int TEXT_HEIGHT = 30;
73+ private static final int RESERV_HEIGHT = 70;
74+ private static final int LIST_HEIGHT = 300;
75+
76+ private static final int LABEL_WIDTH = 500;
77+ private static final int TEXT_WIDTH = 760;
78+ private static final int LIST_WIDTH = 300;
79+ private static final int CHAP_WIDTH = 450;
80+
81+ private static final int BUTTON_WIDTH_S = 80;
82+
83+ // その他の定数
84+ private static final int MAX_TITLE_LENGTH = 80;
85+
86+ private String folderNameWorking = "";
87+ private HDDRecorder recorder = null;
88+ ArrayList<TextValueSet> tvsFolder = null;
89+ private final StatusWindow StWin = getStWin(); // これは起動時に作成されたまま変更されないオブジェクト
90+ private final StatusTextArea MWin = getMWin(); // これは起動時に作成されたまま変更されないオブジェクト
91+
92+ /*
93+ * チャプターの列定義
94+ */
95+ public static enum ChapterColumn {
96+ NO ("番号", 40),
97+ TITLE ("チャプター名", 300),
98+ DURATION ("時間", 80),
99+ ;
100+
101+ private String name;
102+ private int iniWidth;
103+
104+ private ChapterColumn(String name, int iniWidth) {
105+ this.name = name;
106+ this.iniWidth = iniWidth;
107+ }
108+
109+ public String getName() {
110+ return name;
111+ }
112+
113+ public int getIniWidth() {
114+ return iniWidth;
115+ }
116+
117+ public int getColumn() {
118+ return ordinal();
119+ }
120+
121+ public boolean equals(String s) {
122+ return name.equals(s);
123+ }
124+ };
125+
126+ /*******************************************************************************
127+ * 部品
128+ ******************************************************************************/
129+
130+ // コンポーネント
131+
132+ private JPanel jPanel = null;
133+ private JLabel jLabel_title = null;
134+ private JComboBoxWithPopup jComboBox_title = null;
135+ private JTextField jTextField_title = null;
136+
137+ private JLabel jLabel_prog = null;
138+ private JTextAreaWithPopup jTextArea_prog = null;
139+
140+ private JLabel jLabel_folder = null;
141+ private JList jList_folder = null;
142+ private JButton jButton_selectAll = null;
143+ private JButton jButton_deselectAll = null;
144+ private JButton jButton_newFolder = null;
145+
146+ private JLabel jLabel_chapter = null;
147+ private JScrollPane chappane = null;
148+ private ChapterTable chaptable = null;
149+
150+ private JButton jButton_cancel = null;
151+ private JButton jButton_ok = null;
152+
153+ // コンポーネント以外
154+ private boolean folderOnly = false;
155+ private TitleInfo info = null;
156+ private ProgDetailList prog = null;
157+ private TVProgramList tvprograms = null;
158+
159+ private boolean reg = false;
160+
161+ public TitleInfo getTitleInfo() { return info; }
162+
163+ /*
164+ * フォルダリストのセルレンダラー
165+ */
166+ class FolderCellRenderer extends JCheckBox implements ListCellRenderer{
167+ public FolderCellRenderer() {
168+ }
169+
170+ public Component getListCellRendererComponent(JList list, Object value,
171+ int index, boolean isSelected, boolean cellHasFocus){
172+
173+ /* 項目の値を読み出して改めて表示する */
174+ JCheckBox checkBox = (JCheckBox)value;
175+ setText(checkBox.getText());
176+ setSelected(checkBox.isSelected());
177+ return this;
178+ }
179+ }
180+
181+ /*******************************************************************************
182+ * コンストラクタ
183+ ******************************************************************************/
184+
185+ public AbsTitleDialog(boolean b) {
186+ super();
187+
188+ folderOnly = b;
189+ reg = false;
190+
191+ this.setModal(true);
192+ this.setContentPane(getJPanel());
193+
194+ // タイトルバーの高さも考慮する必要がある
195+ Dimension d = getJPanel().getPreferredSize();
196+ this.pack();
197+ this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));
198+ this.setResizable(false);
199+ this.setTitle("タイトル情報");
200+ }
201+
202+ /*******************************************************************************
203+ * アクション
204+ ******************************************************************************/
205+
206+ // 公開メソッド
207+
208+ /**
209+ * フォルダーが登録されたかな?
210+ */
211+ public boolean isRegistered() { return reg; }
212+
213+ /*
214+ * 画面をオープンする
215+ * @param t 編集対象のタイトル情報
216+ * @param tvs フォルダリスト
217+ */
218+ public void open(TitleInfo t, ProgDetailList l, String otitle) {
219+ info = t;
220+ prog = l;
221+ recorder = getSelectedRecorder();
222+ tvsFolder = recorder.getFolderList();
223+
224+ updateTitleLabel();
225+ updateTitleInfo(t, prog, otitle);
226+
227+ String device_name = "[" + t.getRec_device() + "]";
228+
229+ DefaultListModel model = new DefaultListModel();
230+
231+ for (TextValueSet ts : tvsFolder ){
232+ String folder_id = ts.getValue();
233+ String folder_name = ts.getText();
234+ if (folder_id.equals("0") || !folder_name.startsWith(device_name) )
235+ continue;
236+ JCheckBox box = new JCheckBox(ts.getText());
237+
238+ if (t.containsFolder(ts.getValue()))
239+ box.setSelected(true);
240+
241+ model.addElement(box);
242+ }
243+
244+ jList_folder.setModel(model);
245+
246+ updateFolderLabel();
247+ }
248+
249+ /*
250+ * タイトル情報を更新する
251+ */
252+ private void updateTitleInfo(TitleInfo t, ProgDetailList pdl, String titleOld){
253+ String title = t.getTitle();
254+ jTextField_title.setText(title);
255+
256+ jComboBox_title.removeAllItems();
257+ jComboBox_title.addItem(title);
258+ jComboBox_title.setSelectedIndex(0);
259+
260+ // <番組名> #<No>「<サブタイトル>」形式の候補を用意する
261+ if (pdl != null){
262+ String no = getProgramNo(title, pdl);
263+ String subtitle = getSubtitle(title, pdl);
264+ String ptitle = getProgramTitle(title);
265+ String ptitleOld = getProgramTitle(titleOld);
266+
267+ String cand = formatTitleFromOld(ptitleOld, no, subtitle, titleOld);
268+ if (cand != null)
269+ jComboBox_title.addItem(cand);
270+ else{
271+ cand = formatDefaultTitle(ptitle, no, subtitle);
272+ if (cand != null)
273+ jComboBox_title.addItem(cand);
274+ }
275+ }
276+
277+ // 番組情報が見つかったら、タイトルと詳細をセットする
278+ if (pdl != null){
279+ jTextArea_prog.setText(pdl.prefix_mark + pdl.title + pdl.postfix_mark + "\r\n" + pdl.detail);
280+ }
281+
282+ }
283+
284+ /*
285+ * プログラムタイトルを取得する
286+ */
287+ protected String getProgramTitle(String title){
288+ if (title == null)
289+ return null;
290+
291+ String[] patterns = {
292+ "^(.*?)(#|#|♯)",
293+ "^(.*?)第.*?(話|回|羽)",
294+ "^(.*?)(\\(|()[0-90-9]+(\\)|))"};
295+
296+ for (String pat : patterns){
297+ Matcher m = Pattern.compile(pat).matcher(title);
298+ if (m.find()){
299+ return m.group(1);
300+ }
301+ }
302+
303+ return null;
304+ }
305+ /*
306+ * 話数を取得する
307+ */
308+ protected String getProgramNo(String title, ProgDetailList pdl){
309+ String pno1 = searchProgramNo(title);
310+ if (pno1 != null)
311+ return pno1;
312+
313+ String pno2 = searchProgramNo(pdl.title + " " + pdl.detail);
314+ if (pno2 != null)
315+ return pno2;
316+
317+ return "";
318+ }
319+
320+ /*
321+ * 話数を抽出する
322+ */
323+ protected String searchProgramNo(String title){
324+ if (title == null)
325+ return null;
326+
327+ String[] patterns = {
328+ "(#|#|♯)( | ){0,1}([0-90-9]{1,3})",
329+ "(\\(|()( | ){0,1}([0-90-9]{1,3})(\\)|)){0,1}",
330+ "(第)( | ){0,1}([0-90-9]{1,3})(話|回|羽)"};
331+ String ntitle = Normalizer.normalize(title, Normalizer.Form.NFKC);
332+
333+ for (String pat : patterns){
334+ Matcher m = Pattern.compile(pat).matcher(ntitle);
335+ if (m.find()){
336+ return m.group(3);
337+ }
338+ }
339+
340+ return null;
341+ }
342+
343+ /*
344+ * デフォルトのタイトルを整形する
345+ */
346+ protected String formatDefaultTitle(String ptitle, String no, String subtitle){
347+ if (ptitle == null || no == null || subtitle == null)
348+ return null;
349+
350+ if (no.length() == 1){
351+ Matcher m = Pattern.compile("0-9").matcher(no);
352+ if (m.find())
353+ no = "0" + no;
354+ else
355+ no = "0" + no;
356+ }
357+
358+ return ptitle + "#" + no + "「" + subtitle + "」";
359+ }
360+
361+ /*
362+ * 古いタイトルから新らしいタイトルの候補を整形する
363+ */
364+ protected String formatTitleFromOld(String ptitle, String no, String subtitle, String otitle){
365+ if (ptitle == null)
366+ return null;
367+
368+ String fno = formatProgramNo(no, otitle);
369+ String fst = formatSubtitle(subtitle, otitle);
370+ if (fno == null || fst == null)
371+ return null;
372+
373+ return ptitle + fno + fst;
374+ }
375+
376+ /*
377+ * 古いタイトルから新しい話数を整形する
378+ */
379+ protected String formatProgramNo(String no, String otitle){
380+ if (no == null || otitle == null)
381+ return null;
382+
383+ String[] patterns = {
384+ "(#|#)([0-90-9]{1,3})",
385+ "(\\(|()([0-90-9]{1,3})(\\)|))",
386+ "(第)([0-90-9]{1,3})(話|回|羽)"
387+ };
388+
389+ for (String pat : patterns){
390+ Matcher m = Pattern.compile(pat).matcher(otitle);
391+ if (m.find()){
392+ String pre = m.group(1);
393+ String post = m.groupCount() > 2 ? m.group(3) : "";
394+ String pno = m.group(2);
395+ Matcher mw = Pattern.compile("(0-9)+").matcher(pno);
396+ if (mw.find()){
397+ no = CommonUtils.toZENNUM(no);
398+ if (no.length() == 1)
399+ no = "0" + no;
400+ }
401+ else{
402+ if (no.length() == 1)
403+ no = "0" + no;
404+ }
405+
406+ return pre + no + post;
407+ }
408+ }
409+
410+ return null;
411+ }
412+
413+ /*
414+ * 古いタイトルから新しいサブタイトルを整形する
415+ */
416+ protected String formatSubtitle(String subtitle, String otitle){
417+ String[] patterns = {
418+ "(「)(.*?)(」)",
419+ "(『)(.*?)(』)",
420+ "([)(.*?)(])",
421+ "(【)(.*?)(】)",
422+ "(\\[)(.*?)(\\])"
423+ };
424+
425+ for (String pat : patterns){
426+ Matcher m = Pattern.compile(pat).matcher(otitle);
427+ if (m.find())
428+ return m.group(1) + subtitle + m.group(3);
429+ }
430+
431+ return null;
432+ }
433+
434+ /*
435+ * サブタイトルを取得する
436+ */
437+ protected String getSubtitle(String title, ProgDetailList pdl){
438+ String subc = searchSubtitleCombo(pdl.title + " " + pdl.detail);
439+ if (subc != null)
440+ return subc;
441+
442+ String sub1 = searchSubtitle(title);
443+ if (sub1 != null)
444+ return sub1;
445+
446+ String sub2 = searchSubtitle(pdl.title + " " + pdl.detail);
447+ if (sub2 != null)
448+ return sub2;
449+
450+ return "";
451+ }
452+
453+ /*
454+ * サブタイトルを抽出する
455+ */
456+ protected String searchSubtitleCombo(String title){
457+ String[] patternsCombo = {
458+ "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?「(.*?)」",
459+ "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?『(.*?)』",
460+ "(第|#|#)([0-90-9]{1,3})(話|回|羽)?( | )?【(.*?)】"};
461+
462+ for (String pat : patternsCombo){
463+ Matcher m = Pattern.compile(pat).matcher(title);
464+ if (m.find())
465+ return m.group(5);
466+ }
467+
468+ return null;
469+ }
470+
471+ protected String searchSubtitle(String title){
472+ String[] patterns = {"「(.*?)」", "『(.*?)』", "【(.*?)】"};
473+
474+ for (String pat : patterns){
475+ Matcher m = Pattern.compile(pat).matcher(title);
476+ if (m.find())
477+ return m.group(1);
478+ }
479+
480+ return null;
481+ }
482+
483+ /*
484+ * フォルダ一覧のラベルを更新する。選択中のフォルダの数を表示する
485+ */
486+ private void updateFolderLabel() {
487+ int count = getSelectedFolderCount();
488+ jLabel_folder.setText("フォルダ一覧(" + String.valueOf(count)+ "フォルダ選択中)");
489+ }
490+
491+ /*
492+ * すべてのフォルダを選択する
493+ * @param b 選択するか選択解除するか
494+ */
495+ private void selectAllFolders(boolean b) {
496+ ListModel model = jList_folder.getModel();
497+
498+ int num = model.getSize();
499+
500+ for (int n=0; n<num; n++){
501+ JCheckBox checkBox = (JCheckBox)model.getElementAt(n);
502+ checkBox.setSelected(b);
503+ }
504+
505+ jList_folder.repaint();
506+
507+ updateFolderLabel();
508+ }
509+
510+ /*
511+ * 選択されているフォルダの数を返す
512+ */
513+ private int getSelectedFolderCount() {
514+ int count = 0;
515+
516+ ListModel model = jList_folder.getModel();
517+
518+ int num = model.getSize();
519+
520+ for (int n=0; n<num; n++){
521+ JCheckBox checkBox = (JCheckBox)model.getElementAt(n);
522+ if (checkBox.isSelected()){
523+ count++;
524+ }
525+ }
526+
527+ return count;
528+ }
529+
530+ /*******************************************************************************
531+ * リスナー
532+ ******************************************************************************/
533+
534+ /**
535+ * 「全選択」ボタンの処理
536+ * フォルダーを全選択する
537+ */
538+ private final ActionListener al_selectAll = new ActionListener() {
539+ @Override
540+ public void actionPerformed(ActionEvent e) {
541+ selectAllFolders(true);
542+ }
543+ };
544+
545+ /**
546+ * 「選択解除」ボタンの処理
547+ * フォルダーを選択解除する
548+ */
549+ private final ActionListener al_deselectAll = new ActionListener() {
550+ @Override
551+ public void actionPerformed(ActionEvent e) {
552+ selectAllFolders(false);
553+ }
554+ };
555+
556+ /**
557+ * 「新規」ボタンの処理
558+ * フォルダーを新規作成する
559+ */
560+ private final ActionListener al_newFolder = new ActionListener() {
561+ @Override
562+ public void actionPerformed(ActionEvent e) {
563+ // 「指定なし」が選ばれている場合は「追加」とみなし、フォルダ名の初期値は番組タイトルとする
564+ // それ以外が選ばれている場合はそのフォルダの「変更」とみなし、フォルダ名の初期値は現在の値とする
565+ HDDRecorder rec = recorder;
566+
567+ VWFolderDialog dlg = new VWFolderDialog();
568+ CommonSwingUtils.setLocationCenter(jPanel, dlg);
569+
570+ String device_name = info.getRec_device();
571+ String device_id = text2value(rec.getDeviceList(), device_name);
572+ String title = jTextField_title.getText();
573+
574+ String prefix = "[" + device_name + "] ";
575+
576+ dlg.open(title);
577+ dlg.setVisible(true);
578+
579+ if (!dlg.isRegistered())
580+ return;
581+
582+ String nameNew = dlg.getFolderName();
583+ String action = "作成";
584+ folderNameWorking = nameNew;
585+
586+ // フォルダー作成実行
587+ StWin.clear();
588+ new SwingBackgroundWorker(false) {
589+ @Override
590+ protected Object doWorks() throws Exception {
591+ StWin.appendMessage(MSGID+"フォルダーを" + action + "します:"+folderNameWorking);
592+
593+ boolean reg = rec.CreateRdFolder(device_id, nameNew);
594+ if (reg){
595+ MWin.appendMessage(MSGID+"フォルダーを正常に" + action + "できました:"+folderNameWorking);
596+
597+ String folder_name = prefix + nameNew;
598+
599+ tvsFolder = rec.getFolderList();
600+
601+ TextValueSet t = new TextValueSet();
602+ t.setText(folder_name);
603+ t.setValue(text2value(tvsFolder, folder_name));
604+ info.getRec_folder().add(t);
605+
606+ DefaultListModel model = (DefaultListModel)jList_folder.getModel();
607+ JCheckBox box = new JCheckBox(folder_name);
608+ box.setSelected(true);
609+ model.addElement(box);
610+
611+ updateFolderLabel();
612+ }
613+ else {
614+ MWin.appendError(ERRID+"フォルダーの" + action + "に失敗しました:"+folderNameWorking);
615+
616+ if ( ! rec.getErrmsg().equals("")) {
617+ MWin.appendMessage(MSGID+"[追加情報] "+rec.getErrmsg());
618+ }
619+ }
620+
621+ return null;
622+ }
623+ @Override
624+ protected void doFinally() {
625+ StWin.setVisible(false);
626+ }
627+ }.execute();
628+
629+ CommonSwingUtils.setLocationCenter(jPanel, (Component)StWin);
630+ StWin.setVisible(true);
631+ }
632+ };
633+
634+ /*
635+ * チャプター一覧の選択変更の処理
636+ * 今は特に何もしない
637+ */
638+ private final ListSelectionListener lsl_selected = new ListSelectionListener() {
639+
640+ @Override
641+ public void valueChanged(ListSelectionEvent e) {
642+ System.out.println("lsl_selected "+e.toString());
643+ if ( ! e.getValueIsAdjusting() ){
644+ ListSelectionModel model = (ListSelectionModel) e.getSource();
645+ if ( ! model.isSelectionEmpty() ){
646+ int row = model.getMinSelectionIndex();
647+// ChConvItem c = rowData.get(row);
648+// jl_rel.setText(c.related);
649+ }
650+ }
651+ }
652+
653+ };
654+
655+ // セルが編集された
656+ private final CellEditorListener cel_edited = new CellEditorListener() {
657+
658+ @Override
659+ public void editingStopped(ChangeEvent e) {
660+ System.out.println("cel_edited "+e.toString());
661+// jbtn_update.setEnabled(true);
662+ }
663+
664+ @Override
665+ public void editingCanceled(ChangeEvent e) {
666+ System.out.println("cel_edited "+e.toString());
667+ }
668+
669+ };
670+
671+ /**
672+ * 「登録」ボタンの処理
673+ * タイトルの編集結果を登録する
674+ */
675+ private final ActionListener al_ok = new ActionListener() {
676+ @Override
677+ public void actionPerformed(ActionEvent e) {
678+ registerData();
679+ }
680+ };
681+
682+ private void registerData(){
683+ // タイトル
684+ String name = jTextField_title.getText();
685+ if (name.equals("")) {
686+ JOptionPane.showMessageDialog(jPanel, "タイトルがブランクです。");
687+ return;
688+ }
689+
690+ String nameArib = AribCharMap.ConvStringToArib(name);
691+ int lenrb = nameArib.getBytes(Charset.forName("Shift_JIS")).length;
692+ if (!folderOnly && lenrb > MAX_TITLE_LENGTH){
693+ JOptionPane.showMessageDialog(jPanel, "タイトルが長すぎます。(" + String.valueOf(lenrb) + "バイト)");
694+ return;
695+ }
696+
697+ info.setTitle(name);
698+
699+ String device_name = "[" + info.getRec_device() + "]";
700+ ListModel model = jList_folder.getModel();
701+
702+ // フォルダー
703+ ArrayList<TextValueSet> tvs = new ArrayList<TextValueSet>();
704+ int index = 0;
705+ ArrayList<TextValueSet> tvsFolder = recorder.getFolderList();
706+ for (TextValueSet ts : tvsFolder ){
707+ String folder_id = ts.getValue();
708+ String folder_name = ts.getText();
709+ if (folder_id.equals("0") || !folder_name.startsWith(device_name) )
710+ continue;
711+
712+ JCheckBox checkBox = (JCheckBox)model.getElementAt(index);
713+ if (checkBox.isSelected()){
714+ TextValueSet t = new TextValueSet();
715+ t.setValue(ts.getValue());
716+ t.setText(ts.getText());
717+ tvs.add(t);
718+ }
719+
720+ index++;
721+ }
722+
723+ info.setRec_folder(tvs);
724+
725+ // チャプター情報はイベント処理中に取得済
726+ ArrayList<ChapterInfo> chaps = info.getChapter();
727+ for (int nc=0; nc<chaps.size(); nc++){
728+ ChapterInfo ci = chaps.get(nc);
729+
730+ String cname = ci.getName();
731+ String cnameSub = CommonUtils.substringrb(cname,80);
732+ if (!folderOnly && !cnameSub.equals(cname)){
733+ JOptionPane.showMessageDialog(jPanel, "チャプター名が長すぎます。(CHNO=" + String.valueOf(nc+1) + ")");
734+ return;
735+ }
736+ }
737+
738+ reg = true;
739+
740+ // ウィンドウを閉じる
741+ dispose();
742+ }
743+
744+ /*
745+ * タイトルラベルを更新する
746+ */
747+ private void updateTitleLabel(){
748+ String name = jTextField_title.getText();
749+ String nameArib = AribCharMap.ConvStringToArib(name);
750+ int lenrb = nameArib.getBytes(Charset.forName("Shift_JIS")).length;
751+ int restrb = MAX_TITLE_LENGTH - lenrb;
752+
753+ if (jLabel_title != null){
754+ if (restrb >= 0){
755+ jLabel_title.setText("タイトル名(残り" + String.valueOf(restrb) + "バイト)");
756+ jLabel_title.setForeground(Color.BLACK);
757+ }
758+ else{
759+ jLabel_title.setText("タイトル名(" + String.valueOf(-restrb) + "バイトオーバー)");
760+ jLabel_title.setForeground(Color.RED);
761+ }
762+ }
763+ }
764+
765+ /**
766+ * キャンセルしたい
767+ */
768+ private final ActionListener al_cancel = new ActionListener() {
769+ @Override
770+ public void actionPerformed(ActionEvent e) {
771+ dispose();
772+ }
773+ };
774+
775+ /**
776+ * 文書変更イベント処理
777+ */
778+ private final DocumentListener dl_titleChanged = new DocumentListener(){
779+ @Override
780+ public void insertUpdate(DocumentEvent e) {
781+ updateTitleLabel();
782+ }
783+
784+ @Override
785+ public void removeUpdate(DocumentEvent e) {
786+ updateTitleLabel();
787+ }
788+
789+ @Override
790+ public void changedUpdate(DocumentEvent e) {
791+ updateTitleLabel();
792+ }
793+ };
794+
795+ private final KeyListener kl_okcancel = new KeyListener() {
796+ @Override
797+ public void keyTyped(KeyEvent e) {
798+ }
799+ @Override
800+ public void keyPressed(KeyEvent e) {
801+ }
802+ @Override
803+ public void keyReleased(KeyEvent e) {
804+ if (e.getKeyCode() == KeyEvent.VK_ENTER){
805+ registerData();
806+ }
807+ else if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
808+ dispose();
809+ }
810+ }
811+ };
812+
813+ private final KeyListener kl_cancel = new KeyListener() {
814+ @Override
815+ public void keyTyped(KeyEvent e) {
816+ }
817+ @Override
818+ public void keyPressed(KeyEvent e) {
819+ }
820+ @Override
821+ public void keyReleased(KeyEvent e) {
822+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
823+ dispose();
824+ }
825+ }
826+ };
827+
828+ /*******************************************************************************
829+ * コンポーネント
830+ ******************************************************************************/
831+
832+ private JPanel getJPanel() {
833+ if (jPanel == null) {
834+ jPanel = new JPanel();
835+ jPanel.setLayout(null);
836+
837+ int y = SEP_HEIGHT;
838+ int x = SEP_WIDTH;
839+
840+ x = SEP_WIDTH;
841+ JLabel label = getJLabel_title("タイトル名(最大80バイトまで)");
842+ label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
843+ jPanel.add(label);
844+
845+ y += PARTS_HEIGHT;
846+ JComboBoxWithPopup title = getJComboBox_title();
847+ title.setBounds(x, y, TEXT_WIDTH, TEXT_HEIGHT);
848+ jPanel.add(title);
849+ if (folderOnly)
850+ jTextField_title.disable();
851+
852+ y += TEXT_HEIGHT;
853+ label = getJLabel_prog("番組情報");
854+ label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
855+ jPanel.add(label);
856+
857+ y += PARTS_HEIGHT;
858+ JTextAreaWithPopup area = getJTextArea_prog();
859+ area.setBounds(x, y, TEXT_WIDTH, RESERV_HEIGHT);
860+ jPanel.add(area);
861+ if (folderOnly)
862+ area.disable();
863+
864+ y += RESERV_HEIGHT;
865+ label = getJLabel_folder("フォルダ一覧");
866+ label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
867+ jPanel.add(label);
868+
869+ int xc = x+LIST_WIDTH + SEP_WIDTH;
870+ label = getJLabel_chapter("チャプター一覧");
871+ label.setBounds(xc, y, LABEL_WIDTH, PARTS_HEIGHT);
872+ jPanel.add(label);
873+
874+ y += PARTS_HEIGHT;
875+ JScrollPane list = getJList_folder();
876+ list.setBounds(x, y, LIST_WIDTH, LIST_HEIGHT);
877+ jPanel.add(list);
878+
879+ JScrollPane chap = getChapterPane();
880+ chap.setBounds(xc, y, CHAP_WIDTH, LIST_HEIGHT);
881+ jPanel.add(chap);
882+ if (folderOnly)
883+ chap.disable();
884+
885+ y+= LIST_HEIGHT + SEP_HEIGHT;
886+ JButton button = getJButton_selectAll("全選択");
887+ button.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
888+ jPanel.add(button);
889+
890+ button = getJButton_deselectAll("選択解除");
891+ button.setBounds(x+BUTTON_WIDTH_S+SEP_WIDTH, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
892+ jPanel.add(button);
893+
894+ button = getJButton_newFolder("新規");
895+ button.setBounds(x+LIST_WIDTH-BUTTON_WIDTH_S, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
896+ jPanel.add(button);
897+
898+ x += TEXT_WIDTH - (BUTTON_WIDTH_S*2 + SEP_WIDTH);
899+ JButton btnCreate = getJButton_ok("登録");
900+ btnCreate.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
901+ jPanel.add(btnCreate);
902+
903+ x += BUTTON_WIDTH_S+SEP_WIDTH;
904+ JButton btnCancel = getJButton_cancel("キャンセル");
905+ btnCancel.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
906+ jPanel.add(btnCancel);
907+
908+ x += BUTTON_WIDTH_S+SEP_WIDTH;
909+ y += PARTS_HEIGHT+SEP_HEIGHT;
910+
911+ jPanel.setPreferredSize(new Dimension(x, y));
912+ }
913+
914+ return jPanel;
915+ }
916+
917+ //
918+ private JLabel getJLabel_title(String s) {
919+ if (jLabel_title == null) {
920+ jLabel_title = new JLabel(s);
921+ }
922+ return(jLabel_title);
923+ }
924+
925+ //
926+ private JComboBoxWithPopup getJComboBox_title() {
927+ if (jComboBox_title == null) {
928+ jComboBox_title = new JComboBoxWithPopup();
929+ jComboBox_title.setEditable(true);
930+
931+ }
932+ if (jTextField_title == null) {
933+ jTextField_title = ((JTextField)jComboBox_title.getEditor().getEditorComponent());
934+ jTextField_title.addActionListener(al_ok);
935+ jTextField_title.addKeyListener(kl_cancel);
936+ jTextField_title.getDocument().addDocumentListener(dl_titleChanged);
937+ }
938+ return(jComboBox_title);
939+ }
940+
941+ //
942+ private JLabel getJLabel_prog(String s) {
943+ if (jLabel_prog == null) {
944+ jLabel_prog = new JLabel(s);
945+ }
946+ return(jLabel_prog);
947+ }
948+
949+ //
950+ private JTextAreaWithPopup getJTextArea_prog() {
951+ if (jTextArea_prog == null) {
952+ jTextArea_prog = new JTextAreaWithPopup();
953+ jTextArea_prog.setLineWrap(true);
954+ jTextArea_prog.addKeyListener(kl_cancel);
955+ jTextArea_prog.setBorder(jComboBox_title.getBorder());
956+ jTextArea_prog.setEditable(false);
957+ }
958+ return(jTextArea_prog);
959+ }
960+
961+ private JLabel getJLabel_folder(String s) {
962+ if (jLabel_folder == null) {
963+ jLabel_folder = new JLabel(s);
964+ }
965+ return(jLabel_folder);
966+ }
967+
968+ //
969+ private JScrollPane getJList_folder() {
970+ jList_folder = new JList();
971+
972+ FolderCellRenderer renderer = new FolderCellRenderer();
973+ jList_folder.setCellRenderer(renderer);
974+ jList_folder.addMouseListener(new MouseListener() {
975+ @Override
976+ public void mouseClicked(MouseEvent e) {
977+ /* クリックされた座標からIndex番号を取り出す */
978+ Point p = e.getPoint();
979+ int index = jList_folder.locationToIndex(p);
980+
981+ JCheckBox checkBox = (JCheckBox)jList_folder.getModel().getElementAt(index);
982+ if (checkBox.isSelected()){
983+ checkBox.setSelected(false);
984+ }else{
985+ checkBox.setSelected(true);
986+ }
987+
988+ updateFolderLabel();
989+
990+ /* 再描画してみる */
991+ jList_folder.repaint();
992+ }
993+ @Override
994+ public void mousePressed(MouseEvent e) {
995+ }
996+ @Override
997+ public void mouseReleased(MouseEvent e) {
998+ }
999+ @Override
1000+ public void mouseEntered(MouseEvent e) {
1001+ }
1002+ @Override
1003+ public void mouseExited(MouseEvent e) {
1004+ }
1005+ });
1006+ jList_folder.addKeyListener(kl_okcancel);
1007+
1008+ JScrollPane sp = new JScrollPane();
1009+ sp.getViewport().setView(jList_folder);
1010+
1011+ return(sp);
1012+ }
1013+
1014+ //
1015+ private JButton getJButton_selectAll(String s) {
1016+ if (jButton_selectAll == null) {
1017+ jButton_selectAll = new JButton();
1018+ jButton_selectAll.setText(s);
1019+
1020+ jButton_selectAll.addActionListener(al_selectAll);
1021+ jButton_selectAll.addKeyListener(kl_cancel);
1022+ }
1023+ return(jButton_selectAll);
1024+ }
1025+
1026+ //
1027+ private JButton getJButton_deselectAll(String s) {
1028+ if (jButton_deselectAll == null) {
1029+ jButton_deselectAll = new JButton();
1030+ jButton_deselectAll.setText(s);
1031+
1032+ jButton_deselectAll.addActionListener(al_deselectAll);
1033+ jButton_deselectAll.addKeyListener(kl_cancel);
1034+ }
1035+ return(jButton_deselectAll);
1036+ }
1037+
1038+ private JButton getJButton_newFolder(String s) {
1039+ if (jButton_newFolder == null) {
1040+ jButton_newFolder = new JButton();
1041+ jButton_newFolder.setText(s);
1042+
1043+ jButton_newFolder.addActionListener(al_newFolder);
1044+ jButton_newFolder.addKeyListener(kl_cancel);
1045+ }
1046+ return(jButton_newFolder);
1047+ }
1048+
1049+ private JLabel getJLabel_chapter(String s) {
1050+ if (jLabel_chapter == null) {
1051+ jLabel_chapter = new JLabel(s);
1052+ }
1053+ return(jLabel_chapter);
1054+ }
1055+
1056+ private JScrollPane getChapterPane() {
1057+ if (chappane == null ) {
1058+ chappane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
1059+ chappane.setViewportView(getChapterTable());
1060+ }
1061+
1062+ return chappane;
1063+ }
1064+
1065+ private ChapterTable getChapterTable() {
1066+ if (chaptable == null) {
1067+ // カラム名の初期化
1068+ ArrayList<String> cola = new ArrayList<String>();
1069+ for ( ChapterColumn lc : ChapterColumn.values() ) {
1070+ cola.add(lc.getName());
1071+ }
1072+ final String[] colname = cola.toArray(new String[0]);
1073+
1074+ // テーブルの基本的な設定
1075+ DefaultTableModel model = new DefaultTableModel(colname, 0);
1076+
1077+ chaptable = new ChapterTable(model);
1078+
1079+ // 各カラムの幅を設定する
1080+ DefaultTableColumnModel columnModel = (DefaultTableColumnModel)chaptable.getColumnModel();
1081+ TableColumn column = null;
1082+ for ( ChapterColumn lc : ChapterColumn.values() ) {
1083+ column = columnModel.getColumn(lc.ordinal());
1084+ column.setPreferredWidth(lc.getIniWidth());
1085+ }
1086+
1087+ chaptable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
1088+ chaptable.getTableHeader().setReorderingAllowed(false);
1089+ chaptable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1090+ chaptable.putClientProperty("terminateEditOnFocusLost", true);
1091+ chaptable.setRowHeight(chaptable.getRowHeight()+12);
1092+ chaptable.getSelectionModel().addListSelectionListener(lsl_selected);
1093+
1094+ // 編集セルにリスナーを付ける
1095+ TableColumn tc = chaptable.getColumn(ChapterColumn.TITLE.getName());
1096+ EditorColumn ec = new EditorColumn();
1097+ ec.addCellEditorListener(cel_edited);
1098+ tc.setCellEditor(ec);
1099+ }
1100+
1101+ return chaptable;
1102+ }
1103+
1104+ //
1105+ private JButton getJButton_ok(String s) {
1106+ if (jButton_ok == null) {
1107+ jButton_ok = new JButton();
1108+ jButton_ok.setText(s);
1109+
1110+ jButton_ok.addActionListener(al_ok);
1111+ jButton_ok.addKeyListener(kl_cancel);
1112+ }
1113+ return(jButton_ok);
1114+ }
1115+
1116+ //
1117+ private JButton getJButton_cancel(String s) {
1118+ if (jButton_cancel == null) {
1119+ jButton_cancel = new JButton();
1120+ jButton_cancel.setText(s);
1121+
1122+ jButton_cancel.addActionListener(al_cancel);
1123+ jButton_cancel.addKeyListener(kl_cancel);
1124+ }
1125+ return jButton_cancel;
1126+ }
1127+
1128+ /*******************************************************************************
1129+ * 独自コンポーネント
1130+ ******************************************************************************/
1131+ private class ChapterTable extends JTable {
1132+
1133+ private static final long serialVersionUID = 1L;
1134+
1135+ public ChapterTable(DefaultTableModel model) {
1136+ this.setModel(model);
1137+ }
1138+
1139+ @Override
1140+ public Object getValueAt(int row, int column) {
1141+ if ( column == ChapterColumn.NO.ordinal() ) {
1142+ return String.valueOf(row+1);
1143+ }
1144+ else if ( column == ChapterColumn.TITLE.ordinal() ) {
1145+ return info.getChapter().get(row).getName();
1146+ }
1147+ else if ( column == ChapterColumn.DURATION.ordinal() ) {
1148+ int duration = info.getChapter().get(row).getDuration();
1149+ return String.format("%02d:%02d:%02d", duration/3600, (duration/60)%60, duration%60);
1150+ }
1151+ return null;
1152+ }
1153+
1154+ @Override
1155+ public void setValueAt(Object aValue, int row, int column) {
1156+ if ( column == ChapterColumn.TITLE.ordinal() ){
1157+ info.getChapter().get(row).setName((String)aValue);
1158+ }
1159+ }
1160+
1161+ @Override
1162+ public int getRowCount() {
1163+ if (info == null)
1164+ return 0;
1165+ return info.getChapter().size();
1166+ }
1167+
1168+ @Override
1169+ public boolean isCellEditable(int row, int column) {
1170+ if (folderOnly)
1171+ return false;
1172+
1173+ if (column == 1) {
1174+ return true;
1175+ }
1176+ else {
1177+ return false;
1178+ }
1179+ }
1180+
1181+ @Override
1182+ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {
1183+ Component comp = super.prepareRenderer(tcr, row, column);
1184+ return comp;
1185+ }
1186+ }
1187+
1188+ // 素直にHashMapつかっておけばよかった
1189+ public String text2value(ArrayList<TextValueSet> tvs, String text) {
1190+ for ( TextValueSet t : tvs ) {
1191+ if (t.getText().equals(text)) {
1192+ return(t.getValue());
1193+ }
1194+ }
1195+ return("");
1196+ }
1197+}
--- a/TinyBannavi/src/tainavi/AbsTitleListView.java
+++ b/TinyBannavi/src/tainavi/AbsTitleListView.java
@@ -1,2145 +1,2145 @@
1-package tainavi;
2-
3-import java.awt.Color;
4-import java.awt.Component;
5-import java.awt.Dimension;
6-import java.awt.Point;
7-import java.awt.Toolkit;
8-import java.awt.datatransfer.Clipboard;
9-import java.awt.datatransfer.StringSelection;
10-import java.awt.event.ActionEvent;
11-import java.awt.event.ActionListener;
12-import java.awt.event.ComponentAdapter;
13-import java.awt.event.ComponentEvent;
14-import java.awt.event.ItemEvent;
15-import java.awt.event.ItemListener;
16-import java.awt.event.KeyEvent;
17-import java.awt.event.MouseAdapter;
18-import java.awt.event.MouseEvent;
19-import java.util.ArrayList;
20-import java.util.Comparator;
21-import java.util.HashMap;
22-import java.util.regex.Matcher;
23-import java.util.regex.Pattern;
24-
25-import javax.swing.AbstractAction;
26-import javax.swing.ImageIcon;
27-import javax.swing.JButton;
28-import javax.swing.JComboBox;
29-import javax.swing.JComponent;
30-import javax.swing.JLabel;
31-import javax.swing.JMenuItem;
32-import javax.swing.JPanel;
33-import javax.swing.JPopupMenu;
34-import javax.swing.JScrollPane;
35-import javax.swing.JTable;
36-import javax.swing.KeyStroke;
37-import javax.swing.SpringLayout;
38-import javax.swing.event.ListSelectionEvent;
39-import javax.swing.event.ListSelectionListener;
40-import javax.swing.event.TableModelEvent;
41-import javax.swing.table.DefaultTableModel;
42-import javax.swing.table.TableCellRenderer;
43-import javax.swing.table.TableColumn;
44-import javax.swing.table.TableModel;
45-import javax.swing.table.TableRowSorter;
46-
47-import tainavi.TVProgram.ProgGenre;
48-
49-
50-/**
51- * タイトル一覧タブのクラス
52- */
53-public abstract class AbsTitleListView extends JPanel {
54-
55- private static final long serialVersionUID = 1L;
56-
57- public static void setDebug(boolean b) {debug = b; }
58- private static boolean debug = false;
59-
60- private boolean listenerAdded = false;
61- private boolean titleUpdating = false;
62- private boolean deviceUpdating = false;
63-
64- /*******************************************************************************
65- * 抽象メソッド
66- ******************************************************************************/
67-
68- protected abstract Env getEnv();
69- protected abstract Bounds getBoundsEnv();
70- protected abstract TitleListColumnInfoList getTlItemEnv();
71-
72- protected abstract TVProgramList getTVProgramList();
73- protected abstract HDDRecorderList getRecorderList();
74-
75- protected abstract StatusWindow getStWin();
76- protected abstract StatusTextArea getMWin();
77-
78- protected abstract Component getParentComponent();
79-
80- protected abstract void ringBeep();
81-
82- /**
83- * @see Viewer.VWToolBar#getSelectedRecorder()
84- */
85- protected abstract String getSelectedRecorderOnToolbar();
86-
87- /**
88- * タイトルの詳細情報を取得するメニューアイテム
89- */
90- protected abstract JMenuItem getTitleDetailMenuItem(final String title, final String chnam,
91- final String devId, final String ttlId, final String recId);
92-
93- /**
94- * 複数のタイトルの詳細情報をまとめて取得するメニューアイテム
95- */
96- protected abstract JMenuItem getMultiTitleDetailMenuItem(final String title, final String chnam,
97- final String devId, final String [] ttlId, final String recId);
98-
99- /*
100- * 番組欄にジャンプするメニューアイテム
101- */
102- protected abstract JMenuItem getJumpMenuItem(final String title, final String chnam, final String startDT);
103-
104- /**
105- * タイトルを編集するメニューアイテム
106- */
107- protected abstract JMenuItem getEditTitleMenuItem(final String title, final String chnam,
108- final String devId, final String ttlId, final String recId, String otitle);
109-
110- /**
111- * タイトルを削除するメニューアイテム
112- */
113- protected abstract JMenuItem getRemoveTitleMenuItem(final String title, final String chnam,
114- final String devId, final String ttlId, final String recId);
115-
116- /**
117- * 複数のタイトルをまとめて削除するメニューアイテム
118- */
119- protected abstract JMenuItem getRemoveMultiTitleMenuItem(final String title, final String chnam,
120- final String devId, final String [] ttlId, final String recId);
121-
122- /**
123- * 複数のタイトルをまとめてフォルダ移動するメニューアイテム
124- */
125- protected abstract JMenuItem getMoveMultiTitleMenuItem(final String title, final String chnam,
126- final String devId, final String [] ttlId, final String recId);
127-
128- /**
129- * タイトルの再生を開始、終了するメニューアイテム
130- */
131- protected abstract JMenuItem getStartStopPlayTitleMenuItem(final boolean start, final String title, final String chnam,
132- final String devId, final String ttlId, final String recId);
133-
134- /*
135- * プログラムのブラウザーメニューを呼び出す
136- */
137- protected abstract void addBrowseMenuToPopup( JPopupMenu pop, final ProgDetailList tvd );
138-
139- /*******************************************************************************
140- * 定数
141- ******************************************************************************/
142-
143- private static final String MSGID = "[タイトル一覧] ";
144- private static final String ERRID = "[ERROR]"+MSGID;
145- private static final String DBGID = "[DEBUG]"+MSGID;
146-
147- private static final int SEP_WIDTH = 10;
148-
149- private static final int DLABEL_WIDTH = 70;
150- private static final int DCOMBO_WIDTH = 200;
151- private static final int DEVICE_WIDTH = DLABEL_WIDTH+DCOMBO_WIDTH;
152-
153- private static final int FLABEL_WIDTH = 70;
154- private static final int FCOMBO_WIDTH = 400;
155- private static final int FCOMBO_OPEN_WIDTH = 600;
156- private static final int FOLDER_WIDTH = FLABEL_WIDTH+FCOMBO_WIDTH;
157- private static final int FBUTTON_WIDTH = 70;
158-
159- private static final int GLABEL_WIDTH = 70;
160- private static final int GCOMBO_WIDTH = 200;
161- private static final int GENRE_WIDTH = GLABEL_WIDTH+GCOMBO_WIDTH;
162-
163- private static final int RELOAD_ALL_WIDTH = 100;
164- private static final int RELOAD_IND_WIDTH = 30;
165-
166- private static final int PARTS_HEIGHT = 30;
167- private static final int RELOAD_HEIGHT = PARTS_HEIGHT*2;
168- private static final int DETAIL_HEIGHT = 150;
169-
170- public static final String FOLDER_ID_ROOT = "0";
171-
172- private static final String CURRENT_COLOR_EVEN = "#f0b4b4";
173- private static final String CURRENT_COLOR_ODD = "#f88080";
174-
175- private static final String ICONFILE_PULLDOWNMENU = "icon/down-arrow.png";
176-
177- /*******************************************************************************
178- * 部品
179- ******************************************************************************/
180-
181- // オブジェクト
182- private final Env env = getEnv();
183- private final Bounds bounds = getBoundsEnv();
184- private final HDDRecorderList recorders = getRecorderList();
185-
186- private final StatusWindow StWin = getStWin(); // これは起動時に作成されたまま変更されないオブジェクト
187- private final StatusTextArea MWin = getMWin(); // これは起動時に作成されたまま変更されないオブジェクト
188-
189- private final Component parent = getParentComponent(); // これは起動時に作成されたまま変更されないオブジェクト
190-
191- private TitleListColumnInfoList tlitems = null;
192-
193- /**
194- * カラム定義
195- */
196-
197- public static HashMap<String,Integer> getColumnIniWidthMap() {
198- if (rcmap.size() == 0 ) {
199- for ( TitleColumn rc : TitleColumn.values() ) {
200- rcmap.put(rc.toString(),rc.getIniWidth()); // toString()!
201- }
202- }
203- return rcmap;
204- }
205-
206- private static final HashMap<String,Integer> rcmap = new HashMap<String, Integer>();
207-
208- public static enum TitleColumn {
209- START ("開始", 200),
210- END ("終了", 60),
211- LENGTH ("長さ", 60),
212- RECMODE ("画質", 60),
213- TITLE ("番組タイトル", 300),
214- CHNAME ("チャンネル名", 150),
215- DEVNAME ("デバイス", 100),
216- FOLDER ("フォルダ", 300),
217- GENRE ("ジャンル", 100),
218- RECORDER ("レコーダ", 250),
219- COPYCOUNT ("コピー", 60),
220- DLNAOID ("DLNA OID", 100),
221- ;
222-
223- private String name;
224- private int iniWidth;
225-
226- private TitleColumn(String name, int iniWidth) {
227- this.name = name;
228- this.iniWidth = iniWidth;
229- }
230-
231- public String toString() {
232- return name;
233- }
234-
235- public int getIniWidth() {
236- return iniWidth;
237- }
238-
239- public int getColumn() {
240- return ordinal();
241- }
242- };
243-
244- /**
245- * リスト項目定義
246- */
247- private class TitleItem extends RowItem implements Cloneable {
248-
249- String start; // YYYY/MM/DD(WD) hh:mm
250- String end; // hh:mm
251- String length;
252- String recmode;
253- String title;
254- String chname;
255- String devname;
256- String folder;
257- String genre;
258- String recname;
259- String recorder;
260- String copycount;
261- String dlna_oid;
262-
263- String hide_ttlid;
264- String hide_detail;
265- boolean hide_recording;
266-
267- @Override
268- protected void myrefresh(RowItem o) {
269- TitleItem c = (TitleItem) o;
270-
271- c.addData(start);
272- c.addData(end);
273- c.addData(length);
274- c.addData(recmode);
275- c.addData(title);
276- c.addData(chname);
277- c.addData(devname);
278- c.addData(folder);
279- c.addData(genre);
280- c.addData(recname);
281- c.addData(copycount);
282- c.addData(dlna_oid);
283- c.addData(recorder);
284-
285- c.addData(hide_ttlid);
286- c.addData(hide_detail);
287- c.addData(hide_recording);
288- }
289-
290- public TitleItem clone() {
291- return (TitleItem) super.clone();
292- }
293- }
294-
295- // ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない
296- private class TitleTableModel extends DefaultTableModel {
297-
298- private static final long serialVersionUID = 1L;
299-
300- @Override
301- public Object getValueAt(int row, int column) {
302- TitleItem c = null;
303- try{
304- c = rowView.get(row);
305- }
306- catch(IndexOutOfBoundsException e){
307- return null;
308- }
309-
310- ListColumnInfo info = tlitems.getVisibleAt(column);
311- if (info == null)
312- return null;
313-
314- // 特殊なカラム
315- int cindex = info.getId()-1;
316-
317- if ( cindex == TitleColumn.LENGTH.getColumn() ) {
318- return String.valueOf(c.length)+"m";
319- }
320-
321- if (cindex < c.size()){
322- return c.get(cindex);
323- }
324-
325- return null;
326- }
327-
328- @Override
329- public int getRowCount() {
330- return rowView.size();
331- }
332-
333- public TitleTableModel(String[] colname, int i) {
334- super(colname,i);
335- }
336-
337- }
338-
339- /*******************************************************************************
340- * コンポーネント
341- ******************************************************************************/
342-
343- private JScrollPane jsc_list = null;
344- private JScrollPane jsc_detail = null;
345- private JTextAreaWithPopup jta_detail = null;
346-
347- private JNETable jTable_title = null;
348- private JTable jTable_rowheader = null;
349-
350- private JComboBoxPanel jCBXPanel_device = null;
351- private JComboBoxPanel jCBXPanel_folder = null;
352- private JComboBoxPanel jCBXPanel_genre = null;
353- private JLabel jLabel_deviceInfo = null;
354- private JButton jButton_newFolder = null;
355- private JButton jButton_editFolder = null;
356- private JButton jButton_removeFolder = null;
357- private JButton jButton_reloadDefault = null;
358- private JButton jButton_reloadInd = null;
359- private JPopupMenu jPopupMenu_reload = null;
360-
361- private DefaultTableModel tableModel_title = null;
362-
363- private DefaultTableModel rowheaderModel_title = null;
364-
365- // 表示用のテーブル
366- private final RowItemList<TitleItem> rowView = new RowItemList<TitleItem>();
367-
368- // テーブルの実体
369- private final RowItemList<TitleItem> rowData = new RowItemList<TitleItem>();
370-
371- /*******************************************************************************
372- * コンストラクタ
373- ******************************************************************************/
374-
375- public AbsTitleListView() {
376-
377- super();
378-
379- tlitems = (TitleListColumnInfoList) getTlItemEnv().clone();
380-
381- SpringLayout layout = new SpringLayout();
382- this.setLayout(layout);
383-
384- int y1 = 0;
385- int y2 = PARTS_HEIGHT;
386- int x = SEP_WIDTH;
387- CommonSwingUtils.putComponentOn(this, jCBXPanel_device = new JComboBoxPanel("デバイス:", DLABEL_WIDTH, DCOMBO_WIDTH, true), DEVICE_WIDTH, PARTS_HEIGHT, x, y1);
388- CommonSwingUtils.putComponentOn(this, jLabel_deviceInfo = new JLabel("残量(DR):"), DEVICE_WIDTH, PARTS_HEIGHT, x, y2);
389- x += DEVICE_WIDTH + SEP_WIDTH;
390-
391- CommonSwingUtils.putComponentOn(this, getFolderComboPanel(), FOLDER_WIDTH, PARTS_HEIGHT, x, y1);
392- x += FOLDER_WIDTH;
393- CommonSwingUtils.putComponentOn(this, jButton_newFolder = new JButton("F新規"),
394- FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH*3, y2);
395- CommonSwingUtils.putComponentOn(this, jButton_editFolder = new JButton("F編集"),
396- FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH*2, y2);
397- CommonSwingUtils.putComponentOn(this, jButton_removeFolder = new JButton("F削除"),
398- FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH, y2);
399- jButton_removeFolder.setForeground(Color.RED);
400-
401- x += SEP_WIDTH;
402- CommonSwingUtils.putComponentOn(this, jCBXPanel_genre = new JComboBoxPanel("ジャンル:", GLABEL_WIDTH, GCOMBO_WIDTH, true), GENRE_WIDTH, PARTS_HEIGHT, x, y1);
403- jCBXPanel_genre.getJComboBox().setMaximumRowCount(16);
404- x += GENRE_WIDTH + SEP_WIDTH;
405-
406- CommonSwingUtils.putComponentOn(this, jButton_reloadDefault = new JButton(), RELOAD_ALL_WIDTH, RELOAD_HEIGHT, x, y1);
407- jButton_reloadDefault.setText("再取得");
408- x += RELOAD_ALL_WIDTH-2;
409- CommonSwingUtils.putComponentOn(this, getReloadIndButton(), RELOAD_IND_WIDTH, RELOAD_HEIGHT, x, y1);
410-
411- JScrollPane detail = getJTextPane_detail();
412- layout.putConstraint(SpringLayout.SOUTH, detail, 0, SpringLayout.SOUTH, this);
413- layout.putConstraint(SpringLayout.WEST, detail, 0, SpringLayout.WEST, this);
414- layout.putConstraint(SpringLayout.EAST, detail, 0, SpringLayout.EAST, this);
415- layout.putConstraint(SpringLayout.NORTH, detail, -DETAIL_HEIGHT, SpringLayout.SOUTH, this);
416-
417- JScrollPane list = getJScrollPane_list();
418- layout.putConstraint(SpringLayout.NORTH, list, 0, SpringLayout.SOUTH, jButton_reloadDefault);
419- layout.putConstraint(SpringLayout.WEST, list, 0, SpringLayout.WEST, this);
420- layout.putConstraint(SpringLayout.EAST, list, 0, SpringLayout.EAST, this);
421- layout.putConstraint(SpringLayout.SOUTH, list, 0, SpringLayout.NORTH, detail);
422-
423- this.add(jsc_list);
424- this.add(jsc_detail);
425-
426- updateGenreList();
427-
428- setDetailVisible(true);
429-
430- this.addComponentListener(cl_tabShown);
431-
432- addListeners();
433- }
434-
435- /**
436- * リスナーを追加する
437- */
438- protected void addListeners(){
439- if (listenerAdded)
440- return;
441-
442- listenerAdded = true;
443-
444- jButton_newFolder.addActionListener(al_newFolder);
445- jButton_editFolder.addActionListener(al_editFolder);
446- jButton_removeFolder.addActionListener(al_removeFolder);
447- jButton_reloadDefault.addActionListener(al_reloadDefault);
448- jCBXPanel_device.addItemListener(il_deviceChanged);
449- jCBXPanel_folder.addItemListener(il_folderChanged);
450- jCBXPanel_genre.addItemListener(il_genreChanged);
451- }
452-
453- /**
454- * リスナーを削除する
455- */
456- protected void removeListeners() {
457- if (!listenerAdded)
458- return;
459-
460- listenerAdded = false;
461-
462- jButton_newFolder.removeActionListener(al_newFolder);
463- jButton_editFolder.removeActionListener(al_editFolder);
464- jButton_removeFolder.removeActionListener(al_removeFolder);
465- jButton_reloadDefault.removeActionListener(al_reloadDefault);
466- jCBXPanel_device.removeItemListener(il_deviceChanged);
467- jCBXPanel_folder.removeItemListener(il_folderChanged);
468- jCBXPanel_genre.removeItemListener(il_genreChanged);
469- }
470-
471- /**
472- * タイトル一覧を更新する
473- * @param force レコーダからタイトル一覧を取得する
474- * @param upfolder フォルダ一覧を更新する
475- */
476- protected void updateTitleList(boolean force, boolean updevice, boolean upfolder,
477- boolean setting, boolean titles, boolean details) {
478- if (titleUpdating)
479- return;
480-
481- // 選択されたレコーダ
482- HDDRecorder rec = getSelectedRecorder();
483- if (rec == null)
484- return;
485-
486- titleUpdating = true;
487- String device_id = getSelectedDeviceId();
488- if (device_id == null)
489- return;
490-
491- String device_name = rec.getDeviceName(device_id);
492- if (device_name == null)
493- return;
494-
495- String folder_name = getSelectedFolderName();
496-
497- if (force){
498- // フォルダー作成実行
499- StWin.clear();
500-
501- new SwingBackgroundWorker(false) {
502- @Override
503- protected Object doWorks() throws Exception {
504- StWin.appendMessage(MSGID+"タイトル一覧を取得します:"+device_name);
505- removeListeners();
506-
507- boolean nodev = rec.getDeviceList().size() == 0;
508- if (setting || nodev){
509- StWin.appendMessage(MSGID+"レコーダから設定情報を取得します(force=" + String.valueOf(force) + ")");
510- if (rec.GetRdSettings(force))
511- MWin.appendMessage(MSGID+"レコーダから設定情報が正常に取得できました");
512- else
513- MWin.appendError(ERRID+"レコーダからの設定情報の取得に失敗しました");
514- }
515-
516- if (updevice || nodev){
517- updateDeviceList(device_name);
518- updateDeviceInfoLabel();
519- }
520-
521- String devId = getSelectedDeviceId();
522- if (titles && devId != null){
523- StWin.appendMessage(MSGID+"レコーダからタイトル一覧を取得します(force=" + String.valueOf(force) + ",details=" + String.valueOf(details) + "):"+devId);
524- TatCount tc = new TatCount();
525- if (rec.GetRdTitles(devId, force, details, devId.equals(HDDRecorder.DEVICE_ALL))){
526- String time = String.format(" [%.2f秒]", tc.end());
527- MWin.appendMessage(MSGID+"レコーダからタイトル一覧が正常に取得できました:"+devId + time);
528- }
529- else{
530- String time = String.format("[%.2f秒]", tc.end());
531- MWin.appendError(ERRID+"レコーダからのタイトル一覧の取得に失敗しました:"+devId + time);
532- }
533-
534- if ( ! rec.getErrmsg().equals("")) {
535- MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
536- ringBeep();
537- }
538- }
539-
540- if (upfolder || nodev){
541- updateDeviceInfoLabel();
542- updateFolderList(folder_name);
543- updateFolderButtons();
544- }
545-
546- int vrow = jTable_title.getSelectedRow();
547- _redrawTitleList();
548- if (vrow >= 0 && vrow < jTable_title.getModel().getRowCount())
549- jTable_title.setRowSelectionInterval(vrow, vrow);
550-
551- return null;
552- }
553- @Override
554- protected void doFinally() {
555- StWin.setVisible(false);
556- addListeners();
557- titleUpdating = false;
558- }
559- }.execute();
560-
561- CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
562- StWin.setVisible(true);
563- }
564- else{
565- removeListeners();
566-
567- if (setting){
568- if (rec.GetRdSettings(force)){
569-// MWin.appendMessage(MSGID+"レコーダから設定情報が正常に取得できました");
570- }
571- else
572- MWin.appendError(ERRID+"レコーダからの設定情報の取得に失敗しました");
573- }
574-
575- if (updevice){
576- updateDeviceList(device_name);
577- updateDeviceInfoLabel();
578- }
579-
580- String devId = getSelectedDeviceId();
581- if (titles && devId != null){
582- StWin.appendMessage(MSGID+"レコーダからタイトル一覧を取得します(force=" + String.valueOf(force) + ",details=" + String.valueOf(details) + "):"+devId);
583- if (rec.GetRdTitles(devId, force, details, devId.equals(HDDRecorder.DEVICE_ALL))){
584-// MWin.appendMessage(MSGID+"レコーダからタイトル一覧が正常に取得できました:"+devId);
585- }
586- else
587- MWin.appendError(ERRID+"レコーダからのタイトル一覧の取得に失敗しました:"+devId);
588-
589- if ( ! rec.getErrmsg().equals("")) {
590- MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
591- ringBeep();
592- }
593- }
594-
595- if (upfolder){
596- updateDeviceInfoLabel();
597- updateFolderList(folder_name);
598- updateFolderButtons();
599- }
600-
601- int vrow = jTable_title.getSelectedRow();
602- _redrawTitleList();
603- if (vrow >= 0 && vrow < jTable_title.getModel().getRowCount())
604- jTable_title.setRowSelectionInterval(vrow, vrow);
605-
606- addListeners();
607- titleUpdating = false;
608- }
609- }
610-
611- /*
612- * デバイスコンボを更新する
613- * @param sel 更新後選択するデバイスの名称
614- */
615- protected void updateDeviceList(String sel){
616- HDDRecorder rec = getSelectedRecorder();
617- if (rec == null)
618- return;
619- if (deviceUpdating)
620- return;
621-
622- deviceUpdating = true;
623- JComboBoxPanel combo = jCBXPanel_device;
624- ArrayList<TextValueSet> tvs = rec.getDeviceList();
625-
626- combo.removeAllItems();
627- int idx = 0;
628- int no = 0;
629- for ( TextValueSet t : tvs ) {
630- if (sel != null && t.getText().equals(sel))
631- idx = no;
632- DeviceInfo di = rec.GetRDDeviceInfo(t.getValue());
633- if (di != null)
634- combo.addItem(di.getName());
635- else
636- combo.addItem(t.getText());
637-
638- no++;
639- }
640-
641- if (no > 0)
642- combo.setSelectedIndex(idx);
643- combo.setEnabled( combo.getItemCount() > 0 );
644- deviceUpdating = false;
645- }
646-
647- /**
648- * デバイス情報ラベルを更新する
649- */
650- protected void updateDeviceInfoLabel(){
651- HDDRecorder rec = getSelectedRecorder();
652- if (rec == null)
653- return;
654-
655- String s = "残量(DR):";
656-
657- String device_id = getSelectedDeviceId();
658- DeviceInfo info = device_id != null ? rec.GetRDDeviceInfo(device_id) : null;
659- if (info != null){
660- int allsize = info.getAllSize();
661- int freesize = info.getFreeSize();
662- int freePercent = allsize > 0 ? freesize*100/allsize : 0;
663- int freemin = info.getFreeMin();
664-
665- s += String.format("%d時間%02d分(%d%)", freemin/60, freemin%60, freePercent);
666- }
667-
668- jLabel_deviceInfo.setText(s);
669- }
670-
671- /**
672- * フォルダーコンボを更新する
673- * @param sel 更新後選択するフォルダーの名称
674- */
675- protected void updateFolderList(String sel){
676- HDDRecorder rec = getSelectedRecorder();
677- if (rec == null)
678- return;
679-
680- String device_id = getSelectedDeviceId();
681- String device_name = device_id != null ? rec.getDeviceName(device_id) : null;
682- if (device_name != null)
683- device_name = "[" + device_name + "]";
684-
685- JComboBoxPanel combo = jCBXPanel_folder;
686- ArrayList<TextValueSet> tvs = rec.getFolderList();
687-
688- combo.removeAllItems();
689- int idx = 0;
690- int no = 0;
691- for ( TextValueSet t : tvs ) {
692- if (! t.getValue().equals(FOLDER_ID_ROOT) && ! t.getValue().equals("-1") && !device_id.equals(HDDRecorder.DEVICE_ALL) && !t.getText().startsWith(device_name))
693- continue;
694-
695- if (sel != null && t.getText().equals(sel))
696- idx = no;
697- combo.addItem(t.getText() + getTotalsInFolder(t.getValue()));
698- no++;
699- }
700-
701- if (no > 0)
702- combo.setSelectedIndex(idx);
703- combo.setEnabled( combo.getItemCount() > 0 );
704- }
705-
706- /**
707- * フォルダー関係のボタンを更新する
708- */
709- protected void updateFolderButtons() {
710- HDDRecorder rec = getSelectedRecorder();
711- String device_id = getSelectedDeviceId();
712-
713- boolean b = rec != null ? rec.isFolderCreationSupported() : false;
714- boolean ball = device_id != null && device_id.equals(HDDRecorder.DEVICE_ALL);
715-
716- int idx = jCBXPanel_folder.getSelectedIndex();
717- jButton_newFolder.setEnabled(b && !ball);
718- jButton_editFolder.setEnabled(b && idx != 0);
719- jButton_removeFolder.setEnabled(b && idx != 0);
720- }
721-
722- /*
723- * ジャンルコンボを更新する
724- */
725- protected void updateGenreList() {
726- JComboBoxPanel combo = jCBXPanel_genre;
727- combo.removeAllItems();
728-
729- combo.addItem("指定なし");
730-
731- for (ProgGenre pg : ProgGenre.values()) {
732- combo.addItem(pg.toIEPG() + ":" + pg.toString());
733- }
734-
735- combo.setEnabled( combo.getItemCount() > 0 );
736- }
737-
738- /**
739- * デバイスコンボで選択されているデバイスのIDを取得する
740- */
741- protected String getSelectedDeviceId() {
742- HDDRecorder recorder = getSelectedRecorder();
743- if (recorder == null)
744- return "";
745-
746- String device_name = (String)jCBXPanel_device.getSelectedItem();
747- DeviceInfo info = recorder.GetRDDeviceInfoFromName(device_name);
748- if (info != null)
749- return info.getId();
750-
751- return text2value(recorder.getDeviceList(), device_name);
752- }
753-
754- /**
755- * ツールバーで選択されている「先頭の」レコーダを取得する
756- */
757- protected HDDRecorder getSelectedRecorder() {
758- String myself = getSelectedRecorderOnToolbar();
759- HDDRecorderList recs = recorders.findInstance(myself);
760-
761- for ( HDDRecorder rec : recs ) {
762- return rec;
763- }
764-
765- return null;
766- }
767-
768- /*
769- * 指定されたフォルダに含まれるタイトルの数と録画時間を取得する
770- */
771- protected String getTotalsInFolder(String folder_id) {
772- HDDRecorder rec = getSelectedRecorder();
773- if (rec == null || folder_id == null)
774- return "";
775-
776- int tnum = 0;
777- int tmin = 0;
778-
779- for (TitleInfo t : rec.getTitles()){
780- if (!folder_id.equals(FOLDER_ID_ROOT) && !t.containsFolder(folder_id))
781- continue;
782-
783- tnum++;
784- tmin += Integer.parseInt(t.getRec_min());
785- }
786-
787- return String.format(" (%dタイトル %d時間%02d分)" , tnum, tmin/60, tmin%60);
788- }
789-
790- /*
791- * フォルダコンボの名称からフォルダ名のみを取り出す
792- */
793- protected String getSelectedFolderName(){
794- String label = (String)jCBXPanel_folder.getSelectedItem();
795- if (label == null)
796- return null;
797-
798- Matcher ma = Pattern.compile("^(.*) \\(\\d+タイトル \\d+時間\\d\\d分\\)").matcher(label);
799- if (ma.find()){
800- return ma.group(1);
801- }
802-
803- return label;
804- }
805-
806- /*
807- * 同一フォルダの次の録画タイトルを取得する
808- */
809- protected String getNextTitleInSameFolder(int vrow){
810- final int row = jTable_title.convertRowIndexToModel(vrow);
811- TitleItem ra = rowView.get(row);
812- if (ra == null || ra.devname == null || ra.folder == null)
813- return null;
814-
815- for (vrow++ ; vrow < jTable_title.getRowCount(); vrow++){
816- TitleItem rb = rowView.get(jTable_title.convertRowIndexToModel(vrow));
817- if (rb.devname != null && rb.devname.equals(ra.devname) &&
818- rb.folder != null && rb.folder.equals(ra.folder))
819- return rb.title;
820- }
821-
822- return null;
823- }
824-
825- /*******************************************************************************
826- * アクション
827- ******************************************************************************/
828-
829- // 対外的な
830-
831- /**
832- * タイトル一覧を描画してほしいかなって
833- * ★synchronized(rowData)★
834- * @see #cl_tabShown
835- */
836- public void redrawTitleList() {
837- // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★
838- synchronized ( rowView ) {
839- updateTitleList( false, true, true, true, true, false );
840- }
841- }
842-
843- /**
844- * タイトル一覧を再描画する
845- */
846- private void _redrawTitleList() {
847- // 選択されたレコーダ
848- HDDRecorder rec = getSelectedRecorder();
849- if (rec == null)
850- return;
851-
852- String folder_name = getSelectedFolderName();
853- String folder_id = folder_name != null ? text2value(rec.getFolderList(), folder_name) : "";
854-
855- // ジャンルが選択されている場合、そのジャンルに属するタイトル以外はスキップする
856- String genre_name = (String)jCBXPanel_genre.getSelectedItem();
857- ProgGenre genre = ProgGenre.get(genre_name.substring(2));
858-
859- //
860- rowData.clear();
861-
862- // 並べ替えるために新しいリストを作成する
863- for ( TitleInfo ro : rec.getTitles() ) {
864- // フォルダーが選択されている場合、そのフォルダに属するタイトル以外はスキップする
865- if (!folder_id.equals(FOLDER_ID_ROOT) && !ro.containsFolder(folder_id))
866- continue;
867-
868- // ジャンルが選択されている場合、そのフォルダに属するタイトル以外はスキップする
869- if (genre != null && !ro.containsGenre(genre.toIEPG()))
870- continue;
871-
872- TitleItem sa = new TitleItem();
873- setTitleItem(sa, ro, rec);
874-
875- addRow(sa);
876- }
877-
878- // 表示用
879- rowView.clear();
880- for ( TitleItem a : rowData ) {
881- rowView.add(a);
882- }
883-
884- tableModel_title.fireTableDataChanged();
885- ((DefaultTableModel)jTable_rowheader.getModel()).fireTableDataChanged();
886-
887- jta_detail.setText(null);
888-
889- //jta_detail.setText("レコーダから予約結果の一覧を取得して表示します。現在の対応レコーダはTvRock/EpgDataCap_Bonのみです。");
890- }
891-
892- /**
893- * リスト項目の属性をセットする
894- * @param sa セット対象のリスト項目
895- * @param ro セット元のタイトル情報
896- * @param rec セット元のレコーダ
897- */
898- private void setTitleItem(TitleItem sa, TitleInfo ro, HDDRecorder rec) {
899- sa.start = ro.getRec_date()+" "+ro.getAhh()+":"+ro.getAmm(); // YYYY/MM/DD(WD) hh:mm
900- sa.end = ro.getZhh()+":"+ro.getZmm();
901- sa.length = ro.getRec_min();
902- sa.recmode = ro.getRec_mode();
903- sa.title = ro.getTitle();
904- if (ro.getRecording())
905- sa.title += " (録画中)";
906- sa.chname = ro.getCh_name();
907- sa.devname = ro.getRec_device();
908- sa.folder = ro.getFolderNameList();
909- sa.genre = ro.getGenreNameList();
910- sa.recname = rec.getDispName();
911- sa.recorder = rec.Myself();
912- sa.copycount = ro.formatCopyCount();
913- sa.dlna_oid = ro.getHidden_params().get("dlnaObjectID");
914-
915- sa.hide_ttlid = ro.getId();
916- sa.hide_detail = ro.formatDetail();
917- sa.hide_recording = ro.getRecording();
918-
919- sa.fireChanged();
920- }
921-
922-
923- /**
924- * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)
925- */
926- public void redrawListByKeywordFilter(SearchKey keyword, String target) {
927-
928- rowView.clear();
929-
930- // 情報を一行ずつチェックする
931- if ( keyword != null ) {
932- for ( TitleItem a : rowData ) {
933-
934- ProgDetailList tvd = new ProgDetailList();
935- tvd.title = a.title;
936- tvd.titlePop = TraceProgram.replacePop(tvd.title);
937-
938- // タイトルを整形しなおす
939- boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);
940-
941- if ( isFind ) {
942- rowView.add(a);
943- }
944- }
945- }
946- else {
947- for ( TitleItem a : rowData ) {
948- rowView.add(a);
949- }
950- }
951-
952- // fire!
953- tableModel_title.fireTableDataChanged();
954- rowheaderModel_title.fireTableDataChanged();
955- }
956-
957- /**
958- * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)
959- */
960- public void copyColumnWidth() {
961- for ( TitleColumn rc : TitleColumn.values() ) {
962- if ( rc.getIniWidth() < 0 ) {
963- continue;
964- }
965- TableColumn col = getColumn(rc);
966- if (col == null)
967- continue;
968-
969- bounds.getTitleColumnSize().put(rc.toString(), col.getPreferredWidth());
970- }
971- }
972-
973- /**
974- * テーブルの行番号の表示のON/OFF
975- */
976- public void setRowHeaderVisible(boolean b) {
977- jsc_list.getRowHeader().setVisible(b);
978- }
979-
980- /*
981- * 指定した行番号のタイトルを編集する
982- */
983- public void editTitleOfRow(int vrow){
984- HDDRecorder rec = getSelectedRecorder();
985- if (rec == null)
986- return;
987-
988- final int row = jTable_title.convertRowIndexToModel(vrow);
989- TitleItem ra = rowView.get(row);
990- if (ra == null)
991- return;
992-
993- String devId = rec.getDeviceID(ra.devname);
994- String otitle = getNextTitleInSameFolder(vrow);
995- JMenuItem menuItem = getEditTitleMenuItem(ra.title, ra.chname, devId, ra.hide_ttlid, ra.recorder, otitle);
996- menuItem.doClick();
997- }
998-
999- /**
1000- * 画面下部のタイトル詳細領域の表示のON/OFF
1001- */
1002- public void setDetailVisible(boolean b) {
1003- if (!env.getShowTitleDetail())
1004- b = false;
1005-
1006- jsc_detail.setVisible(b);
1007-
1008- SpringLayout layout = (SpringLayout)this.getLayout();
1009- layout.putConstraint(SpringLayout.NORTH, jsc_detail, b ? -DETAIL_HEIGHT : 0, SpringLayout.SOUTH, this);
1010- }
1011-
1012- // 内部的な
1013- /**
1014- * テーブル(の中の人)に追加
1015- */
1016- private void addRow(TitleItem data) {
1017- // 有効データ
1018- int n=0;
1019- for ( ; n<rowData.size(); n++ ) {
1020- TitleItem c = rowData.get(n);
1021- if ( c.start.compareTo(data.start) < 0 ) {
1022- break;
1023- }
1024- }
1025- rowData.add(n,data);
1026- }
1027-
1028- /*
1029- * タイトルに対する番組情報を取得する
1030- */
1031- protected ProgDetailList getProgDetailForTitle(TitleInfo t){
1032- TVProgramList tpl = getTVProgramList();
1033-
1034- if (tpl == null || t == null)
1035- return null;
1036-
1037- // 番組表の日付に変換する
1038- String date = CommonUtils.getDate529(t.getStartDateTime(), true);
1039- if (date == null)
1040- return null;
1041-
1042- // 未来分の番組情報から該当番組の情報を取得する
1043- TVProgramIterator pli = tpl.getIterator().build(null, TVProgramIterator.IterationType.ALL);
1044- ProgDetailList pdl = getProgDetailForTitle(pli, t, date);
1045-
1046- // 見つからなかったら過去分の番組情報をロードして該当番組の情報を取得する
1047- if (pdl == null){
1048- PassedProgram passed = tpl.getPassed();
1049-
1050- if (passed.loadByCenter(date, t.getCh_name())){
1051- pli = tpl.getIterator().build(null, TVProgramIterator.IterationType.PASSED);
1052- pdl = getProgDetailForTitle(pli, t, date);
1053- }
1054- }
1055-
1056- return pdl;
1057- }
1058-
1059- /*
1060- * 指定したタイトルと放送局が同じで時間が重なる番組情報を取得する
1061- */
1062- protected ProgDetailList getProgDetailForTitle(TVProgramIterator pli, TitleInfo t, String date){
1063- String start = t.getStartDateTime();
1064- String end = t.getEndDateTime();
1065- String ch_name = t.getCh_name();
1066-
1067- pli.rewind();
1068-
1069- // 番組情報についてループする
1070- for ( ProgList pl : pli ) {
1071- // チャンネルが異なる場合はスキップする
1072- if (! pl.Center.equals(ch_name))
1073- continue;
1074-
1075- // 日付についてループする
1076- for (ProgDateList pdl : pl.pdate){
1077- // 日付が異なる場合はスキップする
1078- if (! pdl.Date.equals(date))
1079- continue;
1080-
1081- // 日付内の番組についてループする
1082- for (ProgDetailList tvd : pdl.pdetail){
1083- int bse = tvd.startDateTime.compareTo(end);
1084- int bes = tvd.endDateTime.compareTo(start);
1085-
1086- // 予約情報と時間が重なる場合はその番組情報を返す
1087- if (bse * bes < 0)
1088- return tvd;
1089- }
1090-
1091- break;
1092- }
1093-
1094- break;
1095- }
1096-
1097- return null;
1098- }
1099-
1100- /*******************************************************************************
1101- * リスナー
1102- ******************************************************************************/
1103-
1104- /**
1105- * タブが開かれたら表を書き換える
1106- * ★synchronized(rowData)★
1107- * @see #redrawTitleList()
1108- */
1109- private final ComponentAdapter cl_tabShown = new ComponentAdapter() {
1110- @Override
1111- public void componentShown(ComponentEvent e) {
1112- // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★
1113- synchronized ( rowView ) {
1114- updateTitleList( false, true, true, true, true, false );
1115- }
1116- }
1117- };
1118-
1119- /**
1120- * 「新規」ボタンの処理
1121- * フォルダーを作成する
1122- */
1123- private final ActionListener al_newFolder = new ActionListener() {
1124- @Override
1125- public void actionPerformed(ActionEvent e) {
1126- editFolderName(null, "");
1127- }
1128- };
1129-
1130- /**
1131- * 「変更」ボタンの処理
1132- * フォルダーの名称編集を行う
1133- */
1134- private final ActionListener al_editFolder = new ActionListener() {
1135- @Override
1136- public void actionPerformed(ActionEvent e) {
1137-// int idx = jCBXPanel_folder.getSelectedIndex();
1138-
1139- HDDRecorder rec = getSelectedRecorder();
1140- String folder_name = getSelectedFolderName();
1141- String folder_id = folder_name != null ? text2value(rec.getFolderList(), folder_name) : "";
1142-
1143- editFolderName(folder_id, folder_name);
1144- }
1145- };
1146-
1147-
1148- /*
1149- * フォルダーの作成ないし名称編集を行う
1150- */
1151- private void editFolderName( String folder_id, String nameOld){
1152- String nameSel = getSelectedFolderName();
1153- final boolean isSelected = nameSel != null && nameOld.equals(nameSel);
1154-
1155- VWFolderDialog dlg = new VWFolderDialog();
1156- CommonSwingUtils.setLocationCenter(parent, dlg);
1157-
1158- HDDRecorder rec = getSelectedRecorder();
1159-
1160- Matcher ma = Pattern.compile("^\\[([^\\]]*)\\] (.*)$").matcher(nameOld);
1161- final String device_name = ma.find() ? ma.group(1) : "";
1162- final String device_id = rec.getDeviceID(device_name);
1163- nameOld = device_name.length() > 0 ? ma.group(2) : nameOld;
1164-
1165- String prefix = "[" + device_name + "] ";
1166-
1167- dlg.open(nameOld);
1168- dlg.setVisible(true);
1169-
1170- if (!dlg.isRegistered())
1171- return;
1172-
1173- String nameNew = dlg.getFolderName();
1174- String action = folder_id != null ? "更新" : "作成";
1175- final String folderNameWorking = folder_id != null ? "[" + nameOld + "] -> [" + nameNew + "]" : nameNew;
1176-
1177- // フォルダー作成実行
1178- StWin.clear();
1179- new SwingBackgroundWorker(false) {
1180- @Override
1181- protected Object doWorks() throws Exception {
1182- StWin.appendMessage(MSGID+"フォルダーを" + action + "します:"+folderNameWorking);
1183-
1184- boolean reg = false;
1185- if (folder_id != null)
1186- reg = rec.UpdateRdFolderName(device_id, folder_id, nameNew);
1187- else
1188- reg = rec.CreateRdFolder(device_id, nameNew);
1189- if (reg){
1190- MWin.appendMessage(MSGID+"フォルダーを正常に" + action + "できました:"+folderNameWorking);
1191- // [<device_name>]を先頭に付ける
1192- removeListeners();
1193- updateFolderList(isSelected ? prefix + nameNew : null);
1194- updateFolderButtons();
1195- updateTitleList(false, false, false, true, false, false);
1196- addListeners();
1197- }
1198- else {
1199- MWin.appendError(ERRID+"フォルダーの" + action + "に失敗しました:"+folderNameWorking);
1200-
1201- if ( ! rec.getErrmsg().equals("")) {
1202- MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
1203- ringBeep();
1204- }
1205- }
1206-
1207- return null;
1208- }
1209- @Override
1210- protected void doFinally() {
1211- StWin.setVisible(false);
1212- }
1213- }.execute();
1214-
1215- CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
1216- StWin.setVisible(true);
1217- }
1218-
1219- /**
1220- * 「削除」ボタンの処理
1221- * フォルダーを削除する
1222- */
1223- private final ActionListener al_removeFolder = new ActionListener() {
1224- @Override
1225- public void actionPerformed(ActionEvent e) {
1226- HDDRecorder rec = getSelectedRecorder();
1227-
1228- String folder_name = getSelectedFolderName();
1229- if (folder_name == null)
1230- return;
1231-
1232- Matcher ma = Pattern.compile("^\\[(.*)\\] (.*)$").matcher(folder_name);
1233- final String device_name = ma.find() ? ma.group(1) : "";
1234- final String device_id = rec.getDeviceID(device_name);
1235- final String folderNameWorking = device_name.length() > 0 ? ma.group(2) : folder_name;
1236- final String folder_id = text2value(rec.getFolderList(), folder_name);
1237-
1238- // フォルダー削除実行
1239- StWin.clear();
1240- new SwingBackgroundWorker(false) {
1241- @Override
1242- protected Object doWorks() throws Exception {
1243- StWin.appendMessage(MSGID+"フォルダーを削除します:"+folderNameWorking);
1244-
1245- if (rec.RemoveRdFolder( device_id, folder_id )){
1246- MWin.appendMessage(MSGID+"フォルダーを正常に削除できました:"+folderNameWorking);
1247- removeListeners();
1248- updateFolderList(null);
1249- updateFolderButtons();
1250- updateTitleList(false, false, false, true, false, false);
1251- addListeners();
1252- }
1253- else {
1254- MWin.appendError(ERRID+"フォルダーの削除に失敗しました:"+folderNameWorking);
1255- }
1256- if ( ! rec.getErrmsg().equals("")) {
1257- MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
1258- ringBeep();
1259- }
1260-
1261- return null;
1262- }
1263- @Override
1264- protected void doFinally() {
1265- StWin.setVisible(false);
1266- }
1267- }.execute();
1268-
1269- CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
1270- StWin.setVisible(true);
1271- }
1272- };
1273-
1274- /*
1275- * 「再取得」ボタン右の矢印ボタンの処理
1276- * メニューをプルダウン表示する
1277- */
1278- private final MouseAdapter ma_reloadIndividual = new MouseAdapter() {
1279- @Override
1280- public void mousePressed(MouseEvent e) {
1281- jPopupMenu_reload.show(jButton_reloadDefault, 0, jButton_reloadInd.getHeight());
1282- }
1283- };
1284-
1285- /**
1286- * 「再取得」ボタンの処理
1287- * forceフラグを指定して録画タイトルのみ取得し、タイトル一覧を更新する
1288- */
1289- private final ActionListener al_reloadDefault = new ActionListener() {
1290- @Override
1291- public void actionPerformed(ActionEvent e) {
1292- updateTitleList(true, false, true, false, true, true);
1293- }
1294- };
1295-
1296- /**
1297- * 「設定情報+録画タイトルを取得」ボタンの処理
1298- * forceフラグを指定して設定情報と録画タイトルの両方を取得し、デバイスコンボ、フォルダコンボ、
1299- * タイトル一覧を更新する
1300- */
1301- private final ActionListener al_reloadAll = new ActionListener() {
1302- @Override
1303- public void actionPerformed(ActionEvent e) {
1304- updateTitleList(true, true, true, true, true, true);
1305- }
1306- };
1307-
1308- /*
1309- * 「設定情報のみ取得」メニューの処理
1310- * forceフラグを指定して設定情報のみ取得し、デバイスコンボ、フォルダコンボ、タイトル一覧を更新する
1311- */
1312- private final ActionListener al_reloadSettingsOnly = new ActionListener() {
1313- @Override
1314- public void actionPerformed(ActionEvent e) {
1315- updateTitleList(true, true, true, true, false, false);
1316- }
1317- };
1318-
1319- /*
1320- * 「録画タイトルのみ取得」メニューの処理
1321- * forceフラグを指定して録画タイトルのみ取得し、タイトル一覧のみを更新する
1322- */
1323- private final ActionListener al_reloadTitlesOnly = new ActionListener() {
1324- @Override
1325- public void actionPerformed(ActionEvent e) {
1326- updateTitleList(true, false, true, false, true, false);
1327- }
1328- };
1329-
1330- /*
1331- * 「録画タイトル+詳細情報のみ取得」メニューの処理
1332- * forceフラグを指定して録画タイトルとその詳細情報のみ取得し、タイトル一覧のみを更新する
1333- */
1334- private final ActionListener al_reloadTitleAndDetailsOnly = new ActionListener() {
1335- @Override
1336- public void actionPerformed(ActionEvent e) {
1337- updateTitleList(true, false, true, false, true, true);
1338- }
1339- };
1340-
1341- /**
1342- * デバイスコンボの選択変更時の処理
1343- * デバイス情報更新後、タイトル一覧を更新する
1344- */
1345- private final ItemListener il_deviceChanged = new ItemListener() {
1346- @Override
1347- public void itemStateChanged(ItemEvent e) {
1348- if (e.getStateChange() == ItemEvent.SELECTED) {
1349- updateDeviceInfoLabel();
1350- updateTitleList(false, false, true, false, true, false);
1351- }
1352- }
1353- };
1354-
1355- /**
1356- * フォルダーコンボの選択変更時の処理
1357- * タイトル一覧を再描画した後、フォルダー関係のボタンを更新する
1358- */
1359- private final ItemListener il_folderChanged = new ItemListener() {
1360- @Override
1361- public void itemStateChanged(ItemEvent e) {
1362- if (e.getStateChange() == ItemEvent.SELECTED) {
1363- _redrawTitleList();
1364- updateFolderButtons();
1365- }
1366- }
1367- };
1368-
1369- /**
1370- * ジャンルコンボの選択変更時の処理
1371- * タイトル一覧を再描画する
1372- */
1373- private final ItemListener il_genreChanged = new ItemListener() {
1374- @Override
1375- public void itemStateChanged(ItemEvent e) {
1376- if (e.getStateChange() == ItemEvent.SELECTED) {
1377- _redrawTitleList();
1378- }
1379- }
1380- };
1381-
1382- /**
1383- * タイトル一覧の選択変更時の処理
1384- * タイトルの詳細情報を取得後、詳細情報を画面下部に表示する
1385- */
1386- private final ListSelectionListener lsSelectListener = new ListSelectionListener() {
1387- @Override
1388- public void valueChanged(ListSelectionEvent e) {
1389- if(e.getValueIsAdjusting())
1390- return;
1391- int srow = jTable_title.getSelectedRow();
1392- if (srow < 0)
1393- return;
1394-
1395- HDDRecorder rec = getSelectedRecorder();
1396- if (rec == null)
1397- return;
1398-
1399- int row = jTable_title.convertRowIndexToModel(srow);
1400- TitleItem c = rowView.get(row);
1401- TitleInfo t = rec.getTitleInfo(c.hide_ttlid);
1402- if (!t.getDetailLoaded() && rec.GetRdTitleDetail(t)){
1403- t.setDetailLoaded(true);
1404- redrawSelectedTitle(true);
1405- }
1406- else
1407- redrawSelectedTitle(false);
1408- }
1409- };
1410-
1411- /*
1412- * 選択されているタイトルを再描画する
1413- * @param b TRUEの場合リストにタイトル情報をセットし直す
1414- */
1415- public final void redrawSelectedTitle(boolean b) {
1416- int srow = jTable_title.getSelectedRow();
1417- if (srow < 0)
1418- return;
1419-
1420- HDDRecorder rec = getSelectedRecorder();
1421- if (rec == null)
1422- return;
1423-
1424- int row = jTable_title.convertRowIndexToModel(srow);
1425- if (row < 0)
1426- return;
1427-
1428- TitleItem c = rowView.get(row);
1429- if (b){
1430- TitleInfo t = rec.getTitleInfo(c.hide_ttlid);
1431- setTitleItem(c, t, rec);
1432- _redrawTitleList();
1433- jTable_title.setRowSelectionInterval(srow, srow);
1434- }
1435-
1436- jta_detail.setText(c.hide_detail);
1437- jta_detail.setCaretPosition(0);
1438- }
1439-
1440- /*
1441- * 選択されたタイトルが複数のデバイスにまたがるか
1442- */
1443- private boolean areMultiDeviceTitles(ArrayList<TitleItem> array){
1444- String devname = null;
1445- for (TitleItem ti : array){
1446- if (devname == null)
1447- devname = ti.devname;
1448- else if (!devname.equals(ti.devname))
1449- return true;
1450- }
1451-
1452- return false;
1453- }
1454- /**
1455- * タイトル一覧でのマウスイベント処理
1456- * 右クリック時にポップアップメニューを表示する
1457- */
1458- private final MouseAdapter ma_showpopup = new MouseAdapter() {
1459- @Override
1460- public void mouseClicked(MouseEvent e) {
1461- // 選択されたレコーダ
1462- HDDRecorder rec = getSelectedRecorder();
1463- if (rec == null)
1464- return;
1465-
1466- //
1467- Point p = e.getPoint();
1468- final int vrow = jTable_title.rowAtPoint(p);
1469- final int row = jTable_title.convertRowIndexToModel(vrow);
1470-
1471- TitleItem ra = rowView.get(row);
1472- final String title = ra.title;
1473- final String chnam = ra.chname;
1474- final String startDT = ra.start;
1475- final String recId = ra.recorder;
1476- final String recName = recorders.getRecorderName(recId);
1477- final String ttlId = ra.hide_ttlid;
1478- final String devId = rec.getDeviceID(ra.devname);
1479- int num =jTable_title.getSelectedRowCount();
1480- final ArrayList<TitleItem> ras = new ArrayList<TitleItem>(num);
1481- TitleInfo ttl = rec.getTitleInfo(ttlId);
1482-
1483- final String otitle = getNextTitleInSameFolder(vrow);
1484-
1485- //
1486- if (e.getButton() == MouseEvent.BUTTON3) {
1487- if (e.getClickCount() == 1) {
1488- if (!jTable_title.isRowSelected(vrow))
1489- jTable_title.getSelectionModel().setSelectionInterval(vrow,vrow);
1490-
1491- String ttlIds[] = null;
1492- if (num > 1){
1493- ttlIds = new String[num];
1494-
1495- int rows[] = jTable_title.getSelectedRows();
1496- for (int n=0; n<num; n++){
1497- TitleItem ti = rowView.get(jTable_title.convertRowIndexToModel(rows[n]));
1498- ras.add(ti);
1499- ttlIds[n] = ti.hide_ttlid;
1500- }
1501- }
1502- final boolean muldev = areMultiDeviceTitles(ras);
1503-
1504- // 右クリックでポップアップメニューを表示
1505- JPopupMenu pop = new JPopupMenu();
1506- pop.add(getTitleDetailMenuItem(title, chnam, devId, ttlId, recId));
1507- if (ttlIds != null)
1508- pop.add(getMultiTitleDetailMenuItem(title, chnam, devId, ttlIds, recId));
1509-
1510- appendSelectDeviceMenuItem(pop, ttlId);
1511- appendSelectFolderMenuItem(pop, ttlId);
1512-
1513- pop.addSeparator();
1514- pop.add(getStartStopPlayTitleMenuItem(true, title, chnam, devId, ttlId, recId));
1515- pop.add(getStartStopPlayTitleMenuItem(false, title, chnam, devId, ttlId, recId));
1516- pop.addSeparator();
1517- pop.add(getEditTitleMenuItem(title, chnam, devId, ttlId, recId, otitle));
1518- if (ttlIds != null){
1519- JMenuItem mi = getMoveMultiTitleMenuItem(title, chnam, devId, ttlIds, recId);
1520- if (muldev)
1521- mi.setEnabled(false);
1522- pop.addSeparator();
1523- pop.add(mi);
1524- }
1525- pop.addSeparator();
1526- pop.add(getRemoveTitleMenuItem(title, chnam, devId, ttlId, recId));
1527- if (ttlIds != null)
1528- pop.add(getRemoveMultiTitleMenuItem(title, chnam, devId, ttlIds, recId));
1529-
1530- pop.addSeparator();
1531- pop.add(getJumpMenuItem(title, chnam, startDT));
1532- pop.addSeparator();
1533-
1534- // クリップボードへコピーする
1535- {
1536- JMenuItem menuItem = new JMenuItem("番組名をコピー【"+title+"】");
1537- menuItem.addActionListener(new ActionListener() {
1538- public void actionPerformed(ActionEvent e) {
1539- String msg = title;
1540- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1541- StringSelection s = new StringSelection(msg);
1542- cb.setContents(s, null);
1543- }
1544- });
1545-
1546- pop.add(menuItem);
1547- }
1548- {
1549- JMenuItem menuItem = new JMenuItem(String.format("タイトル情報をコピー【%s (%s)/%s】", title, chnam, recName));
1550- menuItem.addActionListener(new ActionListener() {
1551- public void actionPerformed(ActionEvent e) {
1552- String msg = formatTitleItem(ra, false);
1553- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1554- StringSelection s = new StringSelection(msg);
1555- cb.setContents(s, null);
1556- }
1557- });
1558-
1559- pop.add(menuItem);
1560- }
1561- if (num > 1){
1562- JMenuItem menuItem = new JMenuItem(String.format("選択中の%d個のタイトル情報をコピー【%s (%s)/%s】",
1563- num, title, chnam, recName));
1564- menuItem.addActionListener(new ActionListener() {
1565- public void actionPerformed(ActionEvent e) {
1566- String msg = formatTitleItems(ras, false);
1567- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1568- StringSelection s = new StringSelection(msg);
1569- cb.setContents(s, null);
1570- }
1571- });
1572-
1573- pop.add(menuItem);
1574- }
1575-
1576- pop.addSeparator();
1577-
1578- // CSV形式でクリップボードへコピーする
1579- {
1580- JMenuItem menuItem = new JMenuItem(String.format("タイトル情報をCSVでコピー【%s (%s)/%s】",title,chnam, recName));
1581- menuItem.addActionListener(new ActionListener() {
1582- public void actionPerformed(ActionEvent e) {
1583- String msg = formatTitleHeader(true) + formatTitleItem(ra, true);
1584- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1585- StringSelection s = new StringSelection(msg);
1586- cb.setContents(s, null);
1587- }
1588- });
1589-
1590- pop.add(menuItem);
1591- }
1592- if (num > 1){
1593- JMenuItem menuItem = new JMenuItem(String.format("選択中の%d個のタイトル情報をCSVでコピー【%s (%s)/%s】",
1594- num, title, chnam, recName));
1595- menuItem.addActionListener(new ActionListener() {
1596- public void actionPerformed(ActionEvent e) {
1597- String msg = formatTitleHeader(true) + formatTitleItems(ras, true);
1598- Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1599- StringSelection s = new StringSelection(msg);
1600- cb.setContents(s, null);
1601- }
1602- });
1603-
1604- pop.add(menuItem);
1605- }
1606-
1607- ProgDetailList pdl = getProgDetailForTitle(ttl);
1608- if (pdl != null){
1609- pop.addSeparator();
1610- addBrowseMenuToPopup(pop, pdl);
1611- }
1612-
1613- pop.show(jTable_title, e.getX(), e.getY());
1614- }
1615- }
1616- else if (e.getButton() == MouseEvent.BUTTON1) {
1617- if (e.getClickCount() == 1) {
1618- }
1619- else if (e.getClickCount() == 2) {
1620- editTitleOfRow(vrow);
1621- }
1622- }
1623- }
1624- };
1625-
1626- /*
1627- * ヘッダー情報をフォーマットする
1628- */
1629- private String formatTitleHeader(boolean csv){
1630- StringBuilder sb = new StringBuilder();
1631-
1632- for (TitleColumn col: TitleColumn.values()){
1633- String value = col.toString();
1634- boolean last = col == TitleColumn.RECORDER;
1635- if (csv){
1636- sb.append(CommonUtils.toQuoted(value));
1637- if (!last)
1638- sb.append(",");
1639- }
1640- else{
1641- sb.append(value);
1642- if (!last)
1643- sb.append("\t");
1644- }
1645- }
1646- sb.append("\n");
1647-
1648- return sb.toString();
1649- }
1650- /*
1651- * 複数のタイトル情報をテキストないしCSVでフォーマットする
1652- */
1653- private String formatTitleItems(ArrayList<TitleItem>ras, boolean csv){
1654- StringBuilder sb = new StringBuilder();
1655-
1656- for (TitleItem ra : ras){
1657- sb.append(formatTitleItem(ra, csv));
1658- }
1659-
1660- return sb.toString();
1661- }
1662-
1663- /*
1664- * タイトル情報をテキストないしCSVでフォーマットする
1665- */
1666- private String formatTitleItem(TitleItem ra, boolean csv){
1667- StringBuilder sb = new StringBuilder();
1668-
1669- for (TitleColumn col: TitleColumn.values()){
1670- String value = "";
1671- boolean last = col == TitleColumn.RECORDER;
1672- switch(col){
1673- case START:
1674- value = ra.start;
1675- break;
1676- case END:
1677- value = ra.end;
1678- break;
1679- case LENGTH:
1680- value = ra.length + "m";
1681- break;
1682- case RECMODE:
1683- value = ra.recmode;
1684- break;
1685- case TITLE:
1686- value = ra.title;
1687- break;
1688- case CHNAME:
1689- value = ra.chname;
1690- break;
1691- case DEVNAME:
1692- value = ra.devname;
1693- break;
1694- case FOLDER:
1695- value = ra.folder;
1696- break;
1697- case GENRE:
1698- value = ra.genre;
1699- break;
1700- case RECORDER:
1701- value = ra.recname;
1702- break;
1703- case COPYCOUNT:
1704- value = ra.copycount;
1705- break;
1706- case DLNAOID:
1707- value = ra.dlna_oid;
1708- break;
1709- }
1710-
1711- if (value == null)
1712- value = "";
1713-
1714- if (csv){
1715- sb.append(CommonUtils.toQuoted(value));
1716- if (!last)
1717- sb.append(",");
1718- }
1719- else{
1720- sb.append(value);
1721- if (!last)
1722- sb.append("\t");
1723- }
1724- }
1725-
1726- sb.append("\n");
1727-
1728- return sb.toString();
1729- }
1730-
1731- /*******************************************************************************
1732- * コンポーネント
1733- ******************************************************************************/
1734-
1735- /**
1736- * タイトル一覧ペイン
1737- */
1738- private JScrollPane getJScrollPane_list() {
1739-
1740- if ( jsc_list == null ) {
1741- jsc_list = new JScrollPane();
1742-
1743- jsc_list.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowView));
1744- jsc_list.setViewportView(getNETable_title());
1745-
1746- Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);
1747- jsc_list.getRowHeader().setPreferredSize(d);
1748-
1749- this.setRowHeaderVisible(env.getRowHeaderVisible());
1750- }
1751-
1752- return jsc_list;
1753- }
1754-
1755- /**
1756- * 詳細情報ペイン
1757- */
1758- private JScrollPane getJTextPane_detail() {
1759- if ( jsc_detail == null ) {
1760- jsc_detail = new JScrollPane(jta_detail = new JTextAreaWithPopup(),JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
1761- jta_detail.setRows(6);
1762- jta_detail.setEditable(false);
1763- jta_detail.setBackground(Color.LIGHT_GRAY);
1764- }
1765- return jsc_detail;
1766- }
1767-
1768- private TableColumn getColumn(TitleColumn lcol){
1769- TableColumn col = null;
1770- try{
1771- col = jTable_title.getColumn(lcol.toString());
1772- }
1773- catch(IllegalArgumentException e){
1774- return null;
1775- }
1776-
1777- return col;
1778- }
1779-
1780- /*
1781- * テーブルのソーターを初期化する
1782- */
1783- private void initTableSorter(){
1784- TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_title);
1785- jTable_title.setRowSorter(sorter);
1786-
1787- // 数値でソートする項目用の計算式(番組長とか)
1788- final Comparator<String> lengthcomp = new Comparator<String>() {
1789-
1790- @Override
1791- public int compare(String len1, String len2) {
1792- return Integer.parseInt(len1.substring(0, len1.length()-1)) -
1793- Integer.parseInt(len2.substring(0, len2.length()-1));
1794- }
1795- };
1796-
1797- TableColumn col = getColumn(TitleColumn.LENGTH);
1798- if (col != null)
1799- sorter.setComparator(col.getModelIndex(),lengthcomp);
1800-
1801- // コピー回数でソートする項目用の計算式(番組長とか)
1802- final Comparator<String> copycomp = new Comparator<String>() {
1803-
1804- @Override
1805- public int compare(String str1, String str2) {
1806- int num1 = parseCopyCount(str1);
1807- int num2 = parseCopyCount(str2);
1808- return num1 - num2;
1809- }
1810-
1811- // 整形されたコピー回数から回数を取得する
1812- int parseCopyCount(String str){
1813- try{
1814- // 「移動のみ」の場合
1815- if (str.equals(TitleInfo.MOVEONLY))
1816- return 0;
1817- // 「録画中」の場合
1818- else if (str.equals(TitleInfo.RECORDING))
1819- return -1;
1820- // 「n回」の場合
1821- else
1822- return Integer.parseInt(str.substring(0, str.length()-1));
1823- }
1824- catch(NumberFormatException e){
1825- return -2;
1826- }
1827- }
1828- };
1829-
1830- col = getColumn(TitleColumn.COPYCOUNT);
1831- if (col != null)
1832- sorter.setComparator(col.getModelIndex(), copycomp);
1833- }
1834-
1835- /*
1836- * テーブルの列幅を初期化する
1837- */
1838- private void initTableColumnWidth(){
1839- for ( TitleColumn rc : TitleColumn.values() ) {
1840- if ( rc.getIniWidth() < 0 ) {
1841- continue;
1842- }
1843-
1844- TableColumn col = getColumn(rc);
1845- if (col == null)
1846- continue;
1847-
1848- Integer width = bounds.getTitleColumnSize().get(rc.toString());
1849- if (width != null)
1850- col.setPreferredWidth(width);
1851- }
1852- }
1853-
1854- /*
1855- * 列表示のカスタマイズ結果を反映する
1856- */
1857- public void reflectColumnEnv(){
1858- tlitems = (TitleListColumnInfoList) getTlItemEnv().clone();
1859-
1860- tableModel_title.setColumnIdentifiers(tlitems.getColNames());
1861-
1862- // ソーターをつける
1863- initTableSorter();
1864-
1865- // 表示幅を初期化する
1866- initTableColumnWidth();
1867- }
1868-
1869- /**
1870- * タイトル一覧テーブル
1871- */
1872- private JNETable getNETable_title() {
1873- if (jTable_title == null) {
1874- tableModel_title = new TitleTableModel(tlitems.getColNames(), 0);
1875- jTable_title = new JNETableTitle(tableModel_title, true);
1876- jTable_title.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);
1877-
1878- // ヘッダのモデル
1879- rowheaderModel_title = (DefaultTableModel) jTable_rowheader.getModel();
1880-
1881- // ソータを付ける
1882- initTableSorter();
1883-
1884- // 各カラムの幅
1885- initTableColumnWidth();
1886-
1887- // 詳細表示
1888- jTable_title.getSelectionModel().addListSelectionListener(lsSelectListener);
1889-
1890- // 一覧表クリックで削除メニュー出現
1891- jTable_title.addMouseListener(ma_showpopup);
1892-
1893- // ENTERキーでタイトルを編集する
1894- jTable_title.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
1895- jTable_title.getActionMap().put("Enter", new AbstractAction() {
1896- @Override
1897- public void actionPerformed(ActionEvent ae) {
1898- editTitleOfRow(jTable_title.getSelectedRow());
1899- }
1900- });
1901- }
1902-
1903- return jTable_title;
1904- }
1905-
1906- /*
1907- * 「個別再取得」ボタンを取得する
1908- */
1909- private JButton getReloadIndButton() {
1910- if (jButton_reloadInd == null){
1911- ImageIcon arrow = new ImageIcon(ICONFILE_PULLDOWNMENU);
1912-
1913- jButton_reloadInd = new JButton(arrow);
1914- jButton_reloadInd.addMouseListener(ma_reloadIndividual);
1915-
1916- jPopupMenu_reload = new JPopupMenu();
1917-
1918- JMenuItem item = new JMenuItem("設定情報のみ取得");
1919- jPopupMenu_reload.add(item);
1920- item.addActionListener(al_reloadSettingsOnly);
1921-
1922- item = new JMenuItem("録画タイトルのみ取得");
1923- jPopupMenu_reload.add(item);
1924- item.addActionListener(al_reloadTitlesOnly);
1925-
1926- item = new JMenuItem("録画タイトル+詳細情報のみ取得");
1927- jPopupMenu_reload.add(item);
1928- item.addActionListener(al_reloadTitleAndDetailsOnly);
1929-
1930- item = new JMenuItem("設定情報+録画タイトル+詳細情報を取得");
1931- jPopupMenu_reload.add(item);
1932- item.addActionListener(al_reloadAll);
1933- }
1934-
1935- return jButton_reloadInd;
1936- }
1937-
1938- /*
1939- * フォルダ用コンボボックスを取得する
1940- */
1941- private JComboBoxPanel getFolderComboPanel() {
1942- if (jCBXPanel_folder == null){
1943- jCBXPanel_folder = new JComboBoxPanel("フォルダ:", FLABEL_WIDTH, FCOMBO_WIDTH, true);
1944- jCBXPanel_folder.addPopupWidth(FCOMBO_OPEN_WIDTH-FCOMBO_WIDTH);
1945-
1946- JComboBox combo = jCBXPanel_folder.getJComboBox();
1947- combo.setMaximumRowCount(32);
1948- }
1949-
1950- return jCBXPanel_folder;
1951- }
1952-
1953- /*******************************************************************************
1954- * 表表示
1955- ******************************************************************************/
1956-
1957- private class JNETableTitle extends JNETable {
1958-
1959- private static final long serialVersionUID = 1L;
1960-
1961- // 実行中のタイトルの背景色
1962- private Color currentColorEven = CommonUtils.str2color(CURRENT_COLOR_EVEN);
1963- private Color currentColorOdd = CommonUtils.str2color(CURRENT_COLOR_ODD);
1964-
1965- // 実行中のタイトルの背景色をセットする
1966- public void setCurrentColor(Color c) {
1967- if ( c == null ) {
1968- currentColorEven = null;
1969- currentColorOdd = null;
1970- }
1971- else {
1972- currentColorOdd = c;
1973- currentColorEven = new Color(
1974- ((c.getRed()>=247)?(255):(c.getRed()+8)),
1975- ((c.getGreen()>=247)?(255):(c.getGreen()+8)),
1976- ((c.getBlue()>=247)?(255):(c.getBlue()+8))
1977- );
1978- }
1979- }
1980-
1981- @Override
1982- public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {
1983- Component c = super.prepareRenderer(tcr, row, column);
1984- Color fgColor = this.getForeground();
1985- Color bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());
1986-
1987- int xrow = this.convertRowIndexToModel(row);
1988- TitleItem item = null;
1989- try{
1990- item = rowView.get(xrow);
1991- }
1992- catch(IndexOutOfBoundsException e){
1993- return c;
1994- }
1995-
1996- // 実行中のタイトルの場合
1997- if ( item.hide_recording ) {
1998- bgColor = (isSepRowColor && row%2 == 1)?(currentColorEven):(currentColorOdd);
1999- }
2000-
2001- if(isRowSelected(row)) {
2002- fgColor = this.getSelectionForeground();
2003- bgColor = CommonUtils.getSelBgColor(bgColor);
2004- }
2005-
2006- c.setForeground(fgColor);
2007- c.setBackground(bgColor);
2008- return c;
2009- }
2010-
2011- //
2012- @Override
2013- public void tableChanged(TableModelEvent e) {
2014- reset();
2015- super.tableChanged(e);
2016- }
2017-
2018- private void reset() {
2019- }
2020-
2021- /*
2022- * コンストラクタ
2023- */
2024- public JNETableTitle(boolean b) {
2025- super(b);
2026- }
2027- public JNETableTitle(TableModel d, boolean b) {
2028- super(d,b);
2029- }
2030- }
2031-
2032- // 素直にHashMapつかっておけばよかった
2033- public String text2value(ArrayList<TextValueSet> tvs, String text) {
2034- for ( TextValueSet t : tvs ) {
2035- if (t.getText().equals(text)) {
2036- return(t.getValue());
2037- }
2038- }
2039- return("");
2040- }
2041-
2042- /**
2043- * デバイス絞り込みのメニューアイテムを追加する
2044- */
2045- protected void appendSelectDeviceMenuItem(final JPopupMenu pop, final String ttlId){
2046- HDDRecorder rec = getSelectedRecorder();
2047- TitleInfo ttl = rec.getTitleInfo(ttlId);
2048-
2049- if (rec == null || ttl == null)
2050- return;
2051-
2052- String devNameOld = (String)jCBXPanel_device.getSelectedItem();
2053- String devIdOld = rec.getDeviceID(devNameOld);
2054- if (devIdOld == null)
2055- return;
2056-
2057- String devNameNew = ttl.getRec_device();
2058- String folderName = getSelectedFolderName();
2059-
2060- pop.addSeparator();
2061-
2062- if (devIdOld.equals(HDDRecorder.DEVICE_ALL)){
2063- JMenuItem menuItem = new JMenuItem("デバイスを選択する【"+devNameNew+"】");
2064- menuItem.addActionListener(new ActionListener() {
2065- public void actionPerformed(ActionEvent e) {
2066- updateDeviceList(devNameNew);
2067- updateDeviceInfoLabel();
2068- updateFolderList(folderName);
2069- updateTitleList(false, false, false , false, false, false);
2070- }
2071- });
2072-
2073- pop.add(menuItem);
2074- }
2075- else{
2076- JMenuItem menuItem = new JMenuItem("デバイスの選択を解除する");
2077- menuItem.addActionListener(new ActionListener() {
2078- public void actionPerformed(ActionEvent e) {
2079- updateDeviceList(rec.getDeviceName(HDDRecorder.DEVICE_ALL));
2080- updateDeviceInfoLabel();
2081- updateFolderList(folderName);
2082- updateTitleList(false, false, false, false, false, false);
2083- }
2084- });
2085-
2086- pop.add(menuItem);
2087- }
2088- }
2089-
2090- /**
2091- * フォルダー絞り込みのメニューアイテムを追加する
2092- */
2093- protected void appendSelectFolderMenuItem(final JPopupMenu pop, final String ttlId){
2094- HDDRecorder rec = getSelectedRecorder();
2095- TitleInfo ttl = rec.getTitleInfo(ttlId);
2096-
2097- if (ttl == null)
2098- return;
2099-
2100- ArrayList<TextValueSet> folders = ttl.getRec_folder();
2101- if (folders == null || folders.isEmpty())
2102- return;
2103-
2104- String folderName = folders.get(0).getText();
2105- String folderId = folderName != null ? text2value(rec.getFolderList(), folderName) : "";
2106-
2107- int idx = jCBXPanel_folder.getSelectedIndex();
2108- if (idx == 0){
2109- JMenuItem menuItem = new JMenuItem("フォルダを選択する【"+folderName+"】");
2110- menuItem.addActionListener(new ActionListener() {
2111- public void actionPerformed(ActionEvent e) {
2112- updateFolderList(folderName);
2113- updateFolderButtons();
2114- updateTitleList(false, false, false, false, false, false);
2115- }
2116- });
2117-
2118- pop.add(menuItem);
2119- }
2120- else{
2121- JMenuItem menuItem = new JMenuItem("フォルダの選択を解除する");
2122- menuItem.addActionListener(new ActionListener() {
2123- public void actionPerformed(ActionEvent e) {
2124- jCBXPanel_folder.setSelectedIndex(0);
2125- updateTitleList(false, false, false, false, false, false);
2126- }
2127- });
2128-
2129- pop.add(menuItem);
2130- }
2131-
2132- if (!folderId.isEmpty()){
2133- JMenuItem menuItem = new JMenuItem("フォルダを編集する【"+folderName+"】");
2134- menuItem.addActionListener(new ActionListener() {
2135- public void actionPerformed(ActionEvent e) {
2136-
2137- editFolderName(folderId, folderName);
2138- }
2139- });
2140-
2141- pop.add(menuItem);
2142- }
2143- }
2144-
2145-}
1+package tainavi;
2+
3+import java.awt.Color;
4+import java.awt.Component;
5+import java.awt.Dimension;
6+import java.awt.Point;
7+import java.awt.Toolkit;
8+import java.awt.datatransfer.Clipboard;
9+import java.awt.datatransfer.StringSelection;
10+import java.awt.event.ActionEvent;
11+import java.awt.event.ActionListener;
12+import java.awt.event.ComponentAdapter;
13+import java.awt.event.ComponentEvent;
14+import java.awt.event.ItemEvent;
15+import java.awt.event.ItemListener;
16+import java.awt.event.KeyEvent;
17+import java.awt.event.MouseAdapter;
18+import java.awt.event.MouseEvent;
19+import java.util.ArrayList;
20+import java.util.Comparator;
21+import java.util.HashMap;
22+import java.util.regex.Matcher;
23+import java.util.regex.Pattern;
24+
25+import javax.swing.AbstractAction;
26+import javax.swing.ImageIcon;
27+import javax.swing.JButton;
28+import javax.swing.JComboBox;
29+import javax.swing.JComponent;
30+import javax.swing.JLabel;
31+import javax.swing.JMenuItem;
32+import javax.swing.JPanel;
33+import javax.swing.JPopupMenu;
34+import javax.swing.JScrollPane;
35+import javax.swing.JTable;
36+import javax.swing.KeyStroke;
37+import javax.swing.SpringLayout;
38+import javax.swing.event.ListSelectionEvent;
39+import javax.swing.event.ListSelectionListener;
40+import javax.swing.event.TableModelEvent;
41+import javax.swing.table.DefaultTableModel;
42+import javax.swing.table.TableCellRenderer;
43+import javax.swing.table.TableColumn;
44+import javax.swing.table.TableModel;
45+import javax.swing.table.TableRowSorter;
46+
47+import tainavi.TVProgram.ProgGenre;
48+
49+
50+/**
51+ * タイトル一覧タブのクラス
52+ */
53+public abstract class AbsTitleListView extends JPanel {
54+
55+ private static final long serialVersionUID = 1L;
56+
57+ public static void setDebug(boolean b) {debug = b; }
58+ private static boolean debug = false;
59+
60+ private boolean listenerAdded = false;
61+ private boolean titleUpdating = false;
62+ private boolean deviceUpdating = false;
63+
64+ /*******************************************************************************
65+ * 抽象メソッド
66+ ******************************************************************************/
67+
68+ protected abstract Env getEnv();
69+ protected abstract Bounds getBoundsEnv();
70+ protected abstract TitleListColumnInfoList getTlItemEnv();
71+
72+ protected abstract TVProgramList getTVProgramList();
73+ protected abstract HDDRecorderList getRecorderList();
74+
75+ protected abstract StatusWindow getStWin();
76+ protected abstract StatusTextArea getMWin();
77+
78+ protected abstract Component getParentComponent();
79+
80+ protected abstract void ringBeep();
81+
82+ /**
83+ * @see Viewer.VWToolBar#getSelectedRecorder()
84+ */
85+ protected abstract String getSelectedRecorderOnToolbar();
86+
87+ /**
88+ * タイトルの詳細情報を取得するメニューアイテム
89+ */
90+ protected abstract JMenuItem getTitleDetailMenuItem(final String title, final String chnam,
91+ final String devId, final String ttlId, final String recId);
92+
93+ /**
94+ * 複数のタイトルの詳細情報をまとめて取得するメニューアイテム
95+ */
96+ protected abstract JMenuItem getMultiTitleDetailMenuItem(final String title, final String chnam,
97+ final String devId, final String [] ttlId, final String recId);
98+
99+ /*
100+ * 番組欄にジャンプするメニューアイテム
101+ */
102+ protected abstract JMenuItem getJumpMenuItem(final String title, final String chnam, final String startDT);
103+
104+ /**
105+ * タイトルを編集するメニューアイテム
106+ */
107+ protected abstract JMenuItem getEditTitleMenuItem(final String title, final String chnam,
108+ final String devId, final String ttlId, final String recId, String otitle);
109+
110+ /**
111+ * タイトルを削除するメニューアイテム
112+ */
113+ protected abstract JMenuItem getRemoveTitleMenuItem(final String title, final String chnam,
114+ final String devId, final String ttlId, final String recId);
115+
116+ /**
117+ * 複数のタイトルをまとめて削除するメニューアイテム
118+ */
119+ protected abstract JMenuItem getRemoveMultiTitleMenuItem(final String title, final String chnam,
120+ final String devId, final String [] ttlId, final String recId);
121+
122+ /**
123+ * 複数のタイトルをまとめてフォルダ移動するメニューアイテム
124+ */
125+ protected abstract JMenuItem getMoveMultiTitleMenuItem(final String title, final String chnam,
126+ final String devId, final String [] ttlId, final String recId);
127+
128+ /**
129+ * タイトルの再生を開始、終了するメニューアイテム
130+ */
131+ protected abstract JMenuItem getStartStopPlayTitleMenuItem(final boolean start, final String title, final String chnam,
132+ final String devId, final String ttlId, final String recId);
133+
134+ /*
135+ * プログラムのブラウザーメニューを呼び出す
136+ */
137+ protected abstract void addBrowseMenuToPopup( JPopupMenu pop, final ProgDetailList tvd );
138+
139+ /*******************************************************************************
140+ * 定数
141+ ******************************************************************************/
142+
143+ private static final String MSGID = "[タイトル一覧] ";
144+ private static final String ERRID = "[ERROR]"+MSGID;
145+ private static final String DBGID = "[DEBUG]"+MSGID;
146+
147+ private static final int SEP_WIDTH = 10;
148+
149+ private static final int DLABEL_WIDTH = 70;
150+ private static final int DCOMBO_WIDTH = 200;
151+ private static final int DEVICE_WIDTH = DLABEL_WIDTH+DCOMBO_WIDTH;
152+
153+ private static final int FLABEL_WIDTH = 70;
154+ private static final int FCOMBO_WIDTH = 400;
155+ private static final int FCOMBO_OPEN_WIDTH = 600;
156+ private static final int FOLDER_WIDTH = FLABEL_WIDTH+FCOMBO_WIDTH;
157+ private static final int FBUTTON_WIDTH = 70;
158+
159+ private static final int GLABEL_WIDTH = 70;
160+ private static final int GCOMBO_WIDTH = 200;
161+ private static final int GENRE_WIDTH = GLABEL_WIDTH+GCOMBO_WIDTH;
162+
163+ private static final int RELOAD_ALL_WIDTH = 100;
164+ private static final int RELOAD_IND_WIDTH = 30;
165+
166+ private static final int PARTS_HEIGHT = 30;
167+ private static final int RELOAD_HEIGHT = PARTS_HEIGHT*2;
168+ private static final int DETAIL_HEIGHT = 150;
169+
170+ public static final String FOLDER_ID_ROOT = "0";
171+
172+ private static final String CURRENT_COLOR_EVEN = "#f0b4b4";
173+ private static final String CURRENT_COLOR_ODD = "#f88080";
174+
175+ private static final String ICONFILE_PULLDOWNMENU = "icon/down-arrow.png";
176+
177+ /*******************************************************************************
178+ * 部品
179+ ******************************************************************************/
180+
181+ // オブジェクト
182+ private final Env env = getEnv();
183+ private final Bounds bounds = getBoundsEnv();
184+ private final HDDRecorderList recorders = getRecorderList();
185+
186+ private final StatusWindow StWin = getStWin(); // これは起動時に作成されたまま変更されないオブジェクト
187+ private final StatusTextArea MWin = getMWin(); // これは起動時に作成されたまま変更されないオブジェクト
188+
189+ private final Component parent = getParentComponent(); // これは起動時に作成されたまま変更されないオブジェクト
190+
191+ private TitleListColumnInfoList tlitems = null;
192+
193+ /**
194+ * カラム定義
195+ */
196+
197+ public static HashMap<String,Integer> getColumnIniWidthMap() {
198+ if (rcmap.size() == 0 ) {
199+ for ( TitleColumn rc : TitleColumn.values() ) {
200+ rcmap.put(rc.toString(),rc.getIniWidth()); // toString()!
201+ }
202+ }
203+ return rcmap;
204+ }
205+
206+ private static final HashMap<String,Integer> rcmap = new HashMap<String, Integer>();
207+
208+ public static enum TitleColumn {
209+ START ("開始", 200),
210+ END ("終了", 60),
211+ LENGTH ("長さ", 60),
212+ RECMODE ("画質", 60),
213+ TITLE ("番組タイトル", 300),
214+ CHNAME ("チャンネル名", 150),
215+ DEVNAME ("デバイス", 100),
216+ FOLDER ("フォルダ", 300),
217+ GENRE ("ジャンル", 100),
218+ RECORDER ("レコーダ", 250),
219+ COPYCOUNT ("コピー", 60),
220+ DLNAOID ("DLNA OID", 100),
221+ ;
222+
223+ private String name;
224+ private int iniWidth;
225+
226+ private TitleColumn(String name, int iniWidth) {
227+ this.name = name;
228+ this.iniWidth = iniWidth;
229+ }
230+
231+ public String toString() {
232+ return name;
233+ }
234+
235+ public int getIniWidth() {
236+ return iniWidth;
237+ }
238+
239+ public int getColumn() {
240+ return ordinal();
241+ }
242+ };
243+
244+ /**
245+ * リスト項目定義
246+ */
247+ private class TitleItem extends RowItem implements Cloneable {
248+
249+ String start; // YYYY/MM/DD(WD) hh:mm
250+ String end; // hh:mm
251+ String length;
252+ String recmode;
253+ String title;
254+ String chname;
255+ String devname;
256+ String folder;
257+ String genre;
258+ String recname;
259+ String recorder;
260+ String copycount;
261+ String dlna_oid;
262+
263+ String hide_ttlid;
264+ String hide_detail;
265+ boolean hide_recording;
266+
267+ @Override
268+ protected void myrefresh(RowItem o) {
269+ TitleItem c = (TitleItem) o;
270+
271+ c.addData(start);
272+ c.addData(end);
273+ c.addData(length);
274+ c.addData(recmode);
275+ c.addData(title);
276+ c.addData(chname);
277+ c.addData(devname);
278+ c.addData(folder);
279+ c.addData(genre);
280+ c.addData(recname);
281+ c.addData(copycount);
282+ c.addData(dlna_oid);
283+ c.addData(recorder);
284+
285+ c.addData(hide_ttlid);
286+ c.addData(hide_detail);
287+ c.addData(hide_recording);
288+ }
289+
290+ public TitleItem clone() {
291+ return (TitleItem) super.clone();
292+ }
293+ }
294+
295+ // ソートが必要な場合はTableModelを作る。ただし、その場合Viewのrowがわからないので行の入れ替えが行えない
296+ private class TitleTableModel extends DefaultTableModel {
297+
298+ private static final long serialVersionUID = 1L;
299+
300+ @Override
301+ public Object getValueAt(int row, int column) {
302+ TitleItem c = null;
303+ try{
304+ c = rowView.get(row);
305+ }
306+ catch(IndexOutOfBoundsException e){
307+ return null;
308+ }
309+
310+ ListColumnInfo info = tlitems.getVisibleAt(column);
311+ if (info == null)
312+ return null;
313+
314+ // 特殊なカラム
315+ int cindex = info.getId()-1;
316+
317+ if ( cindex == TitleColumn.LENGTH.getColumn() ) {
318+ return String.valueOf(c.length)+"m";
319+ }
320+
321+ if (cindex < c.size()){
322+ return c.get(cindex);
323+ }
324+
325+ return null;
326+ }
327+
328+ @Override
329+ public int getRowCount() {
330+ return rowView.size();
331+ }
332+
333+ public TitleTableModel(String[] colname, int i) {
334+ super(colname,i);
335+ }
336+
337+ }
338+
339+ /*******************************************************************************
340+ * コンポーネント
341+ ******************************************************************************/
342+
343+ private JScrollPane jsc_list = null;
344+ private JScrollPane jsc_detail = null;
345+ private JTextAreaWithPopup jta_detail = null;
346+
347+ private JNETable jTable_title = null;
348+ private JTable jTable_rowheader = null;
349+
350+ private JComboBoxPanel jCBXPanel_device = null;
351+ private JComboBoxPanel jCBXPanel_folder = null;
352+ private JComboBoxPanel jCBXPanel_genre = null;
353+ private JLabel jLabel_deviceInfo = null;
354+ private JButton jButton_newFolder = null;
355+ private JButton jButton_editFolder = null;
356+ private JButton jButton_removeFolder = null;
357+ private JButton jButton_reloadDefault = null;
358+ private JButton jButton_reloadInd = null;
359+ private JPopupMenu jPopupMenu_reload = null;
360+
361+ private DefaultTableModel tableModel_title = null;
362+
363+ private DefaultTableModel rowheaderModel_title = null;
364+
365+ // 表示用のテーブル
366+ private final RowItemList<TitleItem> rowView = new RowItemList<TitleItem>();
367+
368+ // テーブルの実体
369+ private final RowItemList<TitleItem> rowData = new RowItemList<TitleItem>();
370+
371+ /*******************************************************************************
372+ * コンストラクタ
373+ ******************************************************************************/
374+
375+ public AbsTitleListView() {
376+
377+ super();
378+
379+ tlitems = (TitleListColumnInfoList) getTlItemEnv().clone();
380+
381+ SpringLayout layout = new SpringLayout();
382+ this.setLayout(layout);
383+
384+ int y1 = 0;
385+ int y2 = PARTS_HEIGHT;
386+ int x = SEP_WIDTH;
387+ CommonSwingUtils.putComponentOn(this, jCBXPanel_device = new JComboBoxPanel("デバイス:", DLABEL_WIDTH, DCOMBO_WIDTH, true), DEVICE_WIDTH, PARTS_HEIGHT, x, y1);
388+ CommonSwingUtils.putComponentOn(this, jLabel_deviceInfo = new JLabel("残量(DR):"), DEVICE_WIDTH, PARTS_HEIGHT, x, y2);
389+ x += DEVICE_WIDTH + SEP_WIDTH;
390+
391+ CommonSwingUtils.putComponentOn(this, getFolderComboPanel(), FOLDER_WIDTH, PARTS_HEIGHT, x, y1);
392+ x += FOLDER_WIDTH;
393+ CommonSwingUtils.putComponentOn(this, jButton_newFolder = new JButton("F新規"),
394+ FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH*3, y2);
395+ CommonSwingUtils.putComponentOn(this, jButton_editFolder = new JButton("F編集"),
396+ FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH*2, y2);
397+ CommonSwingUtils.putComponentOn(this, jButton_removeFolder = new JButton("F削除"),
398+ FBUTTON_WIDTH, PARTS_HEIGHT, x-FBUTTON_WIDTH, y2);
399+ jButton_removeFolder.setForeground(Color.RED);
400+
401+ x += SEP_WIDTH;
402+ CommonSwingUtils.putComponentOn(this, jCBXPanel_genre = new JComboBoxPanel("ジャンル:", GLABEL_WIDTH, GCOMBO_WIDTH, true), GENRE_WIDTH, PARTS_HEIGHT, x, y1);
403+ jCBXPanel_genre.getJComboBox().setMaximumRowCount(16);
404+ x += GENRE_WIDTH + SEP_WIDTH;
405+
406+ CommonSwingUtils.putComponentOn(this, jButton_reloadDefault = new JButton(), RELOAD_ALL_WIDTH, RELOAD_HEIGHT, x, y1);
407+ jButton_reloadDefault.setText("再取得");
408+ x += RELOAD_ALL_WIDTH-2;
409+ CommonSwingUtils.putComponentOn(this, getReloadIndButton(), RELOAD_IND_WIDTH, RELOAD_HEIGHT, x, y1);
410+
411+ JScrollPane detail = getJTextPane_detail();
412+ layout.putConstraint(SpringLayout.SOUTH, detail, 0, SpringLayout.SOUTH, this);
413+ layout.putConstraint(SpringLayout.WEST, detail, 0, SpringLayout.WEST, this);
414+ layout.putConstraint(SpringLayout.EAST, detail, 0, SpringLayout.EAST, this);
415+ layout.putConstraint(SpringLayout.NORTH, detail, -DETAIL_HEIGHT, SpringLayout.SOUTH, this);
416+
417+ JScrollPane list = getJScrollPane_list();
418+ layout.putConstraint(SpringLayout.NORTH, list, 0, SpringLayout.SOUTH, jButton_reloadDefault);
419+ layout.putConstraint(SpringLayout.WEST, list, 0, SpringLayout.WEST, this);
420+ layout.putConstraint(SpringLayout.EAST, list, 0, SpringLayout.EAST, this);
421+ layout.putConstraint(SpringLayout.SOUTH, list, 0, SpringLayout.NORTH, detail);
422+
423+ this.add(jsc_list);
424+ this.add(jsc_detail);
425+
426+ updateGenreList();
427+
428+ setDetailVisible(true);
429+
430+ this.addComponentListener(cl_tabShown);
431+
432+ addListeners();
433+ }
434+
435+ /**
436+ * リスナーを追加する
437+ */
438+ protected void addListeners(){
439+ if (listenerAdded)
440+ return;
441+
442+ listenerAdded = true;
443+
444+ jButton_newFolder.addActionListener(al_newFolder);
445+ jButton_editFolder.addActionListener(al_editFolder);
446+ jButton_removeFolder.addActionListener(al_removeFolder);
447+ jButton_reloadDefault.addActionListener(al_reloadDefault);
448+ jCBXPanel_device.addItemListener(il_deviceChanged);
449+ jCBXPanel_folder.addItemListener(il_folderChanged);
450+ jCBXPanel_genre.addItemListener(il_genreChanged);
451+ }
452+
453+ /**
454+ * リスナーを削除する
455+ */
456+ protected void removeListeners() {
457+ if (!listenerAdded)
458+ return;
459+
460+ listenerAdded = false;
461+
462+ jButton_newFolder.removeActionListener(al_newFolder);
463+ jButton_editFolder.removeActionListener(al_editFolder);
464+ jButton_removeFolder.removeActionListener(al_removeFolder);
465+ jButton_reloadDefault.removeActionListener(al_reloadDefault);
466+ jCBXPanel_device.removeItemListener(il_deviceChanged);
467+ jCBXPanel_folder.removeItemListener(il_folderChanged);
468+ jCBXPanel_genre.removeItemListener(il_genreChanged);
469+ }
470+
471+ /**
472+ * タイトル一覧を更新する
473+ * @param force レコーダからタイトル一覧を取得する
474+ * @param upfolder フォルダ一覧を更新する
475+ */
476+ protected void updateTitleList(boolean force, boolean updevice, boolean upfolder,
477+ boolean setting, boolean titles, boolean details) {
478+ if (titleUpdating)
479+ return;
480+
481+ // 選択されたレコーダ
482+ HDDRecorder rec = getSelectedRecorder();
483+ if (rec == null)
484+ return;
485+
486+ titleUpdating = true;
487+ String device_id = getSelectedDeviceId();
488+ if (device_id == null)
489+ return;
490+
491+ String device_name = rec.getDeviceName(device_id);
492+ if (device_name == null)
493+ return;
494+
495+ String folder_name = getSelectedFolderName();
496+
497+ if (force){
498+ // フォルダー作成実行
499+ StWin.clear();
500+
501+ new SwingBackgroundWorker(false) {
502+ @Override
503+ protected Object doWorks() throws Exception {
504+ StWin.appendMessage(MSGID+"タイトル一覧を取得します:"+device_name);
505+ removeListeners();
506+
507+ boolean nodev = rec.getDeviceList().size() == 0;
508+ if (setting || nodev){
509+ StWin.appendMessage(MSGID+"レコーダから設定情報を取得します(force=" + String.valueOf(force) + ")");
510+ if (rec.GetRdSettings(force))
511+ MWin.appendMessage(MSGID+"レコーダから設定情報が正常に取得できました");
512+ else
513+ MWin.appendError(ERRID+"レコーダからの設定情報の取得に失敗しました");
514+ }
515+
516+ if (updevice || nodev){
517+ updateDeviceList(device_name);
518+ updateDeviceInfoLabel();
519+ }
520+
521+ String devId = getSelectedDeviceId();
522+ if (titles && devId != null){
523+ StWin.appendMessage(MSGID+"レコーダからタイトル一覧を取得します(force=" + String.valueOf(force) + ",details=" + String.valueOf(details) + "):"+devId);
524+ TatCount tc = new TatCount();
525+ if (rec.GetRdTitles(devId, force, details, devId.equals(HDDRecorder.DEVICE_ALL))){
526+ String time = String.format(" [%.2f秒]", tc.end());
527+ MWin.appendMessage(MSGID+"レコーダからタイトル一覧が正常に取得できました:"+devId + time);
528+ }
529+ else{
530+ String time = String.format("[%.2f秒]", tc.end());
531+ MWin.appendError(ERRID+"レコーダからのタイトル一覧の取得に失敗しました:"+devId + time);
532+ }
533+
534+ if ( ! rec.getErrmsg().equals("")) {
535+ MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
536+ ringBeep();
537+ }
538+ }
539+
540+ if (upfolder || nodev){
541+ updateDeviceInfoLabel();
542+ updateFolderList(folder_name);
543+ updateFolderButtons();
544+ }
545+
546+ int vrow = jTable_title.getSelectedRow();
547+ _redrawTitleList();
548+ if (vrow >= 0 && vrow < jTable_title.getModel().getRowCount())
549+ jTable_title.setRowSelectionInterval(vrow, vrow);
550+
551+ return null;
552+ }
553+ @Override
554+ protected void doFinally() {
555+ StWin.setVisible(false);
556+ addListeners();
557+ titleUpdating = false;
558+ }
559+ }.execute();
560+
561+ CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
562+ StWin.setVisible(true);
563+ }
564+ else{
565+ removeListeners();
566+
567+ if (setting){
568+ if (rec.GetRdSettings(force)){
569+// MWin.appendMessage(MSGID+"レコーダから設定情報が正常に取得できました");
570+ }
571+ else
572+ MWin.appendError(ERRID+"レコーダからの設定情報の取得に失敗しました");
573+ }
574+
575+ if (updevice){
576+ updateDeviceList(device_name);
577+ updateDeviceInfoLabel();
578+ }
579+
580+ String devId = getSelectedDeviceId();
581+ if (titles && devId != null){
582+ StWin.appendMessage(MSGID+"レコーダからタイトル一覧を取得します(force=" + String.valueOf(force) + ",details=" + String.valueOf(details) + "):"+devId);
583+ if (rec.GetRdTitles(devId, force, details, devId.equals(HDDRecorder.DEVICE_ALL))){
584+// MWin.appendMessage(MSGID+"レコーダからタイトル一覧が正常に取得できました:"+devId);
585+ }
586+ else
587+ MWin.appendError(ERRID+"レコーダからのタイトル一覧の取得に失敗しました:"+devId);
588+
589+ if ( ! rec.getErrmsg().equals("")) {
590+ MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
591+ ringBeep();
592+ }
593+ }
594+
595+ if (upfolder){
596+ updateDeviceInfoLabel();
597+ updateFolderList(folder_name);
598+ updateFolderButtons();
599+ }
600+
601+ int vrow = jTable_title.getSelectedRow();
602+ _redrawTitleList();
603+ if (vrow >= 0 && vrow < jTable_title.getModel().getRowCount())
604+ jTable_title.setRowSelectionInterval(vrow, vrow);
605+
606+ addListeners();
607+ titleUpdating = false;
608+ }
609+ }
610+
611+ /*
612+ * デバイスコンボを更新する
613+ * @param sel 更新後選択するデバイスの名称
614+ */
615+ protected void updateDeviceList(String sel){
616+ HDDRecorder rec = getSelectedRecorder();
617+ if (rec == null)
618+ return;
619+ if (deviceUpdating)
620+ return;
621+
622+ deviceUpdating = true;
623+ JComboBoxPanel combo = jCBXPanel_device;
624+ ArrayList<TextValueSet> tvs = rec.getDeviceList();
625+
626+ combo.removeAllItems();
627+ int idx = 0;
628+ int no = 0;
629+ for ( TextValueSet t : tvs ) {
630+ if (sel != null && t.getText().equals(sel))
631+ idx = no;
632+ DeviceInfo di = rec.GetRDDeviceInfo(t.getValue());
633+ if (di != null)
634+ combo.addItem(di.getName());
635+ else
636+ combo.addItem(t.getText());
637+
638+ no++;
639+ }
640+
641+ if (no > 0)
642+ combo.setSelectedIndex(idx);
643+ combo.setEnabled( combo.getItemCount() > 0 );
644+ deviceUpdating = false;
645+ }
646+
647+ /**
648+ * デバイス情報ラベルを更新する
649+ */
650+ protected void updateDeviceInfoLabel(){
651+ HDDRecorder rec = getSelectedRecorder();
652+ if (rec == null)
653+ return;
654+
655+ String s = "残量(DR):";
656+
657+ String device_id = getSelectedDeviceId();
658+ DeviceInfo info = device_id != null ? rec.GetRDDeviceInfo(device_id) : null;
659+ if (info != null){
660+ int allsize = info.getAllSize();
661+ int freesize = info.getFreeSize();
662+ int freePercent = allsize > 0 ? freesize*100/allsize : 0;
663+ int freemin = info.getFreeMin();
664+
665+ s += String.format("%d時間%02d分(%d%)", freemin/60, freemin%60, freePercent);
666+ }
667+
668+ jLabel_deviceInfo.setText(s);
669+ }
670+
671+ /**
672+ * フォルダーコンボを更新する
673+ * @param sel 更新後選択するフォルダーの名称
674+ */
675+ protected void updateFolderList(String sel){
676+ HDDRecorder rec = getSelectedRecorder();
677+ if (rec == null)
678+ return;
679+
680+ String device_id = getSelectedDeviceId();
681+ String device_name = device_id != null ? rec.getDeviceName(device_id) : null;
682+ if (device_name != null)
683+ device_name = "[" + device_name + "]";
684+
685+ JComboBoxPanel combo = jCBXPanel_folder;
686+ ArrayList<TextValueSet> tvs = rec.getFolderList();
687+
688+ combo.removeAllItems();
689+ int idx = 0;
690+ int no = 0;
691+ for ( TextValueSet t : tvs ) {
692+ if (! t.getValue().equals(FOLDER_ID_ROOT) && ! t.getValue().equals("-1") && !device_id.equals(HDDRecorder.DEVICE_ALL) && !t.getText().startsWith(device_name))
693+ continue;
694+
695+ if (sel != null && t.getText().equals(sel))
696+ idx = no;
697+ combo.addItem(t.getText() + getTotalsInFolder(t.getValue()));
698+ no++;
699+ }
700+
701+ if (no > 0)
702+ combo.setSelectedIndex(idx);
703+ combo.setEnabled( combo.getItemCount() > 0 );
704+ }
705+
706+ /**
707+ * フォルダー関係のボタンを更新する
708+ */
709+ protected void updateFolderButtons() {
710+ HDDRecorder rec = getSelectedRecorder();
711+ String device_id = getSelectedDeviceId();
712+
713+ boolean b = rec != null ? rec.isFolderCreationSupported() : false;
714+ boolean ball = device_id != null && device_id.equals(HDDRecorder.DEVICE_ALL);
715+
716+ int idx = jCBXPanel_folder.getSelectedIndex();
717+ jButton_newFolder.setEnabled(b && !ball);
718+ jButton_editFolder.setEnabled(b && idx != 0);
719+ jButton_removeFolder.setEnabled(b && idx != 0);
720+ }
721+
722+ /*
723+ * ジャンルコンボを更新する
724+ */
725+ protected void updateGenreList() {
726+ JComboBoxPanel combo = jCBXPanel_genre;
727+ combo.removeAllItems();
728+
729+ combo.addItem("指定なし");
730+
731+ for (ProgGenre pg : ProgGenre.values()) {
732+ combo.addItem(pg.toIEPG() + ":" + pg.toString());
733+ }
734+
735+ combo.setEnabled( combo.getItemCount() > 0 );
736+ }
737+
738+ /**
739+ * デバイスコンボで選択されているデバイスのIDを取得する
740+ */
741+ protected String getSelectedDeviceId() {
742+ HDDRecorder recorder = getSelectedRecorder();
743+ if (recorder == null)
744+ return "";
745+
746+ String device_name = (String)jCBXPanel_device.getSelectedItem();
747+ DeviceInfo info = recorder.GetRDDeviceInfoFromName(device_name);
748+ if (info != null)
749+ return info.getId();
750+
751+ return text2value(recorder.getDeviceList(), device_name);
752+ }
753+
754+ /**
755+ * ツールバーで選択されている「先頭の」レコーダを取得する
756+ */
757+ protected HDDRecorder getSelectedRecorder() {
758+ String myself = getSelectedRecorderOnToolbar();
759+ HDDRecorderList recs = recorders.findInstance(myself);
760+
761+ for ( HDDRecorder rec : recs ) {
762+ return rec;
763+ }
764+
765+ return null;
766+ }
767+
768+ /*
769+ * 指定されたフォルダに含まれるタイトルの数と録画時間を取得する
770+ */
771+ protected String getTotalsInFolder(String folder_id) {
772+ HDDRecorder rec = getSelectedRecorder();
773+ if (rec == null || folder_id == null)
774+ return "";
775+
776+ int tnum = 0;
777+ int tmin = 0;
778+
779+ for (TitleInfo t : rec.getTitles()){
780+ if (!folder_id.equals(FOLDER_ID_ROOT) && !t.containsFolder(folder_id))
781+ continue;
782+
783+ tnum++;
784+ tmin += Integer.parseInt(t.getRec_min());
785+ }
786+
787+ return String.format(" (%dタイトル %d時間%02d分)" , tnum, tmin/60, tmin%60);
788+ }
789+
790+ /*
791+ * フォルダコンボの名称からフォルダ名のみを取り出す
792+ */
793+ protected String getSelectedFolderName(){
794+ String label = (String)jCBXPanel_folder.getSelectedItem();
795+ if (label == null)
796+ return null;
797+
798+ Matcher ma = Pattern.compile("^(.*) \\(\\d+タイトル \\d+時間\\d\\d分\\)").matcher(label);
799+ if (ma.find()){
800+ return ma.group(1);
801+ }
802+
803+ return label;
804+ }
805+
806+ /*
807+ * 同一フォルダの次の録画タイトルを取得する
808+ */
809+ protected String getNextTitleInSameFolder(int vrow){
810+ final int row = jTable_title.convertRowIndexToModel(vrow);
811+ TitleItem ra = rowView.get(row);
812+ if (ra == null || ra.devname == null || ra.folder == null)
813+ return null;
814+
815+ for (vrow++ ; vrow < jTable_title.getRowCount(); vrow++){
816+ TitleItem rb = rowView.get(jTable_title.convertRowIndexToModel(vrow));
817+ if (rb.devname != null && rb.devname.equals(ra.devname) &&
818+ rb.folder != null && rb.folder.equals(ra.folder))
819+ return rb.title;
820+ }
821+
822+ return null;
823+ }
824+
825+ /*******************************************************************************
826+ * アクション
827+ ******************************************************************************/
828+
829+ // 対外的な
830+
831+ /**
832+ * タイトル一覧を描画してほしいかなって
833+ * ★synchronized(rowData)★
834+ * @see #cl_tabShown
835+ */
836+ public void redrawTitleList() {
837+ // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★
838+ synchronized ( rowView ) {
839+ updateTitleList( false, true, true, true, true, false );
840+ }
841+ }
842+
843+ /**
844+ * タイトル一覧を再描画する
845+ */
846+ private void _redrawTitleList() {
847+ // 選択されたレコーダ
848+ HDDRecorder rec = getSelectedRecorder();
849+ if (rec == null)
850+ return;
851+
852+ String folder_name = getSelectedFolderName();
853+ String folder_id = folder_name != null ? text2value(rec.getFolderList(), folder_name) : "";
854+
855+ // ジャンルが選択されている場合、そのジャンルに属するタイトル以外はスキップする
856+ String genre_name = (String)jCBXPanel_genre.getSelectedItem();
857+ ProgGenre genre = ProgGenre.get(genre_name.substring(2));
858+
859+ //
860+ rowData.clear();
861+
862+ // 並べ替えるために新しいリストを作成する
863+ for ( TitleInfo ro : rec.getTitles() ) {
864+ // フォルダーが選択されている場合、そのフォルダに属するタイトル以外はスキップする
865+ if (!folder_id.equals(FOLDER_ID_ROOT) && !ro.containsFolder(folder_id))
866+ continue;
867+
868+ // ジャンルが選択されている場合、そのフォルダに属するタイトル以外はスキップする
869+ if (genre != null && !ro.containsGenre(genre.toIEPG()))
870+ continue;
871+
872+ TitleItem sa = new TitleItem();
873+ setTitleItem(sa, ro, rec);
874+
875+ addRow(sa);
876+ }
877+
878+ // 表示用
879+ rowView.clear();
880+ for ( TitleItem a : rowData ) {
881+ rowView.add(a);
882+ }
883+
884+ tableModel_title.fireTableDataChanged();
885+ ((DefaultTableModel)jTable_rowheader.getModel()).fireTableDataChanged();
886+
887+ jta_detail.setText(null);
888+
889+ //jta_detail.setText("レコーダから予約結果の一覧を取得して表示します。現在の対応レコーダはTvRock/EpgDataCap_Bonのみです。");
890+ }
891+
892+ /**
893+ * リスト項目の属性をセットする
894+ * @param sa セット対象のリスト項目
895+ * @param ro セット元のタイトル情報
896+ * @param rec セット元のレコーダ
897+ */
898+ private void setTitleItem(TitleItem sa, TitleInfo ro, HDDRecorder rec) {
899+ sa.start = ro.getRec_date()+" "+ro.getAhh()+":"+ro.getAmm(); // YYYY/MM/DD(WD) hh:mm
900+ sa.end = ro.getZhh()+":"+ro.getZmm();
901+ sa.length = ro.getRec_min();
902+ sa.recmode = ro.getRec_mode();
903+ sa.title = ro.getTitle();
904+ if (ro.getRecording())
905+ sa.title += " (録画中)";
906+ sa.chname = ro.getCh_name();
907+ sa.devname = ro.getRec_device();
908+ sa.folder = ro.getFolderNameList();
909+ sa.genre = ro.getGenreNameList();
910+ sa.recname = rec.getDispName();
911+ sa.recorder = rec.Myself();
912+ sa.copycount = ro.formatCopyCount();
913+ sa.dlna_oid = ro.getHidden_params().get("dlnaObjectID");
914+
915+ sa.hide_ttlid = ro.getId();
916+ sa.hide_detail = ro.formatDetail();
917+ sa.hide_recording = ro.getRecording();
918+
919+ sa.fireChanged();
920+ }
921+
922+
923+ /**
924+ * 絞り込み検索の本体(現在リストアップされているものから絞り込みを行う)(親から呼ばれるよ!)
925+ */
926+ public void redrawListByKeywordFilter(SearchKey keyword, String target) {
927+
928+ rowView.clear();
929+
930+ // 情報を一行ずつチェックする
931+ if ( keyword != null ) {
932+ for ( TitleItem a : rowData ) {
933+
934+ ProgDetailList tvd = new ProgDetailList();
935+ tvd.title = a.title;
936+ tvd.titlePop = TraceProgram.replacePop(tvd.title);
937+
938+ // タイトルを整形しなおす
939+ boolean isFind = SearchProgram.isMatchKeyword(keyword, "", tvd);
940+
941+ if ( isFind ) {
942+ rowView.add(a);
943+ }
944+ }
945+ }
946+ else {
947+ for ( TitleItem a : rowData ) {
948+ rowView.add(a);
949+ }
950+ }
951+
952+ // fire!
953+ tableModel_title.fireTableDataChanged();
954+ rowheaderModel_title.fireTableDataChanged();
955+ }
956+
957+ /**
958+ * カラム幅を保存する(鯛ナビ終了時に呼び出されるメソッド)
959+ */
960+ public void copyColumnWidth() {
961+ for ( TitleColumn rc : TitleColumn.values() ) {
962+ if ( rc.getIniWidth() < 0 ) {
963+ continue;
964+ }
965+ TableColumn col = getColumn(rc);
966+ if (col == null)
967+ continue;
968+
969+ bounds.getTitleColumnSize().put(rc.toString(), col.getPreferredWidth());
970+ }
971+ }
972+
973+ /**
974+ * テーブルの行番号の表示のON/OFF
975+ */
976+ public void setRowHeaderVisible(boolean b) {
977+ jsc_list.getRowHeader().setVisible(b);
978+ }
979+
980+ /*
981+ * 指定した行番号のタイトルを編集する
982+ */
983+ public void editTitleOfRow(int vrow){
984+ HDDRecorder rec = getSelectedRecorder();
985+ if (rec == null)
986+ return;
987+
988+ final int row = jTable_title.convertRowIndexToModel(vrow);
989+ TitleItem ra = rowView.get(row);
990+ if (ra == null)
991+ return;
992+
993+ String devId = rec.getDeviceID(ra.devname);
994+ String otitle = getNextTitleInSameFolder(vrow);
995+ JMenuItem menuItem = getEditTitleMenuItem(ra.title, ra.chname, devId, ra.hide_ttlid, ra.recorder, otitle);
996+ menuItem.doClick();
997+ }
998+
999+ /**
1000+ * 画面下部のタイトル詳細領域の表示のON/OFF
1001+ */
1002+ public void setDetailVisible(boolean b) {
1003+ if (!env.getShowTitleDetail())
1004+ b = false;
1005+
1006+ jsc_detail.setVisible(b);
1007+
1008+ SpringLayout layout = (SpringLayout)this.getLayout();
1009+ layout.putConstraint(SpringLayout.NORTH, jsc_detail, b ? -DETAIL_HEIGHT : 0, SpringLayout.SOUTH, this);
1010+ }
1011+
1012+ // 内部的な
1013+ /**
1014+ * テーブル(の中の人)に追加
1015+ */
1016+ private void addRow(TitleItem data) {
1017+ // 有効データ
1018+ int n=0;
1019+ for ( ; n<rowData.size(); n++ ) {
1020+ TitleItem c = rowData.get(n);
1021+ if ( c.start.compareTo(data.start) < 0 ) {
1022+ break;
1023+ }
1024+ }
1025+ rowData.add(n,data);
1026+ }
1027+
1028+ /*
1029+ * タイトルに対する番組情報を取得する
1030+ */
1031+ protected ProgDetailList getProgDetailForTitle(TitleInfo t){
1032+ TVProgramList tpl = getTVProgramList();
1033+
1034+ if (tpl == null || t == null)
1035+ return null;
1036+
1037+ // 番組表の日付に変換する
1038+ String date = CommonUtils.getDate529(t.getStartDateTime(), true);
1039+ if (date == null)
1040+ return null;
1041+
1042+ // 未来分の番組情報から該当番組の情報を取得する
1043+ TVProgramIterator pli = tpl.getIterator().build(null, TVProgramIterator.IterationType.ALL);
1044+ ProgDetailList pdl = getProgDetailForTitle(pli, t, date);
1045+
1046+ // 見つからなかったら過去分の番組情報をロードして該当番組の情報を取得する
1047+ if (pdl == null){
1048+ PassedProgram passed = tpl.getPassed();
1049+
1050+ if (passed.loadByCenter(date, t.getCh_name())){
1051+ pli = tpl.getIterator().build(null, TVProgramIterator.IterationType.PASSED);
1052+ pdl = getProgDetailForTitle(pli, t, date);
1053+ }
1054+ }
1055+
1056+ return pdl;
1057+ }
1058+
1059+ /*
1060+ * 指定したタイトルと放送局が同じで時間が重なる番組情報を取得する
1061+ */
1062+ protected ProgDetailList getProgDetailForTitle(TVProgramIterator pli, TitleInfo t, String date){
1063+ String start = t.getStartDateTime();
1064+ String end = t.getEndDateTime();
1065+ String ch_name = t.getCh_name();
1066+
1067+ pli.rewind();
1068+
1069+ // 番組情報についてループする
1070+ for ( ProgList pl : pli ) {
1071+ // チャンネルが異なる場合はスキップする
1072+ if (! pl.Center.equals(ch_name))
1073+ continue;
1074+
1075+ // 日付についてループする
1076+ for (ProgDateList pdl : pl.pdate){
1077+ // 日付が異なる場合はスキップする
1078+ if (! pdl.Date.equals(date))
1079+ continue;
1080+
1081+ // 日付内の番組についてループする
1082+ for (ProgDetailList tvd : pdl.pdetail){
1083+ int bse = tvd.startDateTime.compareTo(end);
1084+ int bes = tvd.endDateTime.compareTo(start);
1085+
1086+ // 予約情報と時間が重なる場合はその番組情報を返す
1087+ if (bse * bes < 0)
1088+ return tvd;
1089+ }
1090+
1091+ break;
1092+ }
1093+
1094+ break;
1095+ }
1096+
1097+ return null;
1098+ }
1099+
1100+ /*******************************************************************************
1101+ * リスナー
1102+ ******************************************************************************/
1103+
1104+ /**
1105+ * タブが開かれたら表を書き換える
1106+ * ★synchronized(rowData)★
1107+ * @see #redrawTitleList()
1108+ */
1109+ private final ComponentAdapter cl_tabShown = new ComponentAdapter() {
1110+ @Override
1111+ public void componentShown(ComponentEvent e) {
1112+ // ★★★ イベントにトリガーされた処理がかちあわないように synchronized() ★★★
1113+ synchronized ( rowView ) {
1114+ updateTitleList( false, true, true, true, true, false );
1115+ }
1116+ }
1117+ };
1118+
1119+ /**
1120+ * 「新規」ボタンの処理
1121+ * フォルダーを作成する
1122+ */
1123+ private final ActionListener al_newFolder = new ActionListener() {
1124+ @Override
1125+ public void actionPerformed(ActionEvent e) {
1126+ editFolderName(null, "");
1127+ }
1128+ };
1129+
1130+ /**
1131+ * 「変更」ボタンの処理
1132+ * フォルダーの名称編集を行う
1133+ */
1134+ private final ActionListener al_editFolder = new ActionListener() {
1135+ @Override
1136+ public void actionPerformed(ActionEvent e) {
1137+// int idx = jCBXPanel_folder.getSelectedIndex();
1138+
1139+ HDDRecorder rec = getSelectedRecorder();
1140+ String folder_name = getSelectedFolderName();
1141+ String folder_id = folder_name != null ? text2value(rec.getFolderList(), folder_name) : "";
1142+
1143+ editFolderName(folder_id, folder_name);
1144+ }
1145+ };
1146+
1147+
1148+ /*
1149+ * フォルダーの作成ないし名称編集を行う
1150+ */
1151+ private void editFolderName( String folder_id, String nameOld){
1152+ String nameSel = getSelectedFolderName();
1153+ final boolean isSelected = nameSel != null && nameOld.equals(nameSel);
1154+
1155+ VWFolderDialog dlg = new VWFolderDialog();
1156+ CommonSwingUtils.setLocationCenter(parent, dlg);
1157+
1158+ HDDRecorder rec = getSelectedRecorder();
1159+
1160+ Matcher ma = Pattern.compile("^\\[([^\\]]*)\\] (.*)$").matcher(nameOld);
1161+ final String device_name = ma.find() ? ma.group(1) : "";
1162+ final String device_id = rec.getDeviceID(device_name);
1163+ nameOld = device_name.length() > 0 ? ma.group(2) : nameOld;
1164+
1165+ String prefix = "[" + device_name + "] ";
1166+
1167+ dlg.open(nameOld);
1168+ dlg.setVisible(true);
1169+
1170+ if (!dlg.isRegistered())
1171+ return;
1172+
1173+ String nameNew = dlg.getFolderName();
1174+ String action = folder_id != null ? "更新" : "作成";
1175+ final String folderNameWorking = folder_id != null ? "[" + nameOld + "] -> [" + nameNew + "]" : nameNew;
1176+
1177+ // フォルダー作成実行
1178+ StWin.clear();
1179+ new SwingBackgroundWorker(false) {
1180+ @Override
1181+ protected Object doWorks() throws Exception {
1182+ StWin.appendMessage(MSGID+"フォルダーを" + action + "します:"+folderNameWorking);
1183+
1184+ boolean reg = false;
1185+ if (folder_id != null)
1186+ reg = rec.UpdateRdFolderName(device_id, folder_id, nameNew);
1187+ else
1188+ reg = rec.CreateRdFolder(device_id, nameNew);
1189+ if (reg){
1190+ MWin.appendMessage(MSGID+"フォルダーを正常に" + action + "できました:"+folderNameWorking);
1191+ // [<device_name>]を先頭に付ける
1192+ removeListeners();
1193+ updateFolderList(isSelected ? prefix + nameNew : null);
1194+ updateFolderButtons();
1195+ updateTitleList(false, false, false, true, false, false);
1196+ addListeners();
1197+ }
1198+ else {
1199+ MWin.appendError(ERRID+"フォルダーの" + action + "に失敗しました:"+folderNameWorking);
1200+
1201+ if ( ! rec.getErrmsg().equals("")) {
1202+ MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
1203+ ringBeep();
1204+ }
1205+ }
1206+
1207+ return null;
1208+ }
1209+ @Override
1210+ protected void doFinally() {
1211+ StWin.setVisible(false);
1212+ }
1213+ }.execute();
1214+
1215+ CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
1216+ StWin.setVisible(true);
1217+ }
1218+
1219+ /**
1220+ * 「削除」ボタンの処理
1221+ * フォルダーを削除する
1222+ */
1223+ private final ActionListener al_removeFolder = new ActionListener() {
1224+ @Override
1225+ public void actionPerformed(ActionEvent e) {
1226+ HDDRecorder rec = getSelectedRecorder();
1227+
1228+ String folder_name = getSelectedFolderName();
1229+ if (folder_name == null)
1230+ return;
1231+
1232+ Matcher ma = Pattern.compile("^\\[(.*)\\] (.*)$").matcher(folder_name);
1233+ final String device_name = ma.find() ? ma.group(1) : "";
1234+ final String device_id = rec.getDeviceID(device_name);
1235+ final String folderNameWorking = device_name.length() > 0 ? ma.group(2) : folder_name;
1236+ final String folder_id = text2value(rec.getFolderList(), folder_name);
1237+
1238+ // フォルダー削除実行
1239+ StWin.clear();
1240+ new SwingBackgroundWorker(false) {
1241+ @Override
1242+ protected Object doWorks() throws Exception {
1243+ StWin.appendMessage(MSGID+"フォルダーを削除します:"+folderNameWorking);
1244+
1245+ if (rec.RemoveRdFolder( device_id, folder_id )){
1246+ MWin.appendMessage(MSGID+"フォルダーを正常に削除できました:"+folderNameWorking);
1247+ removeListeners();
1248+ updateFolderList(null);
1249+ updateFolderButtons();
1250+ updateTitleList(false, false, false, true, false, false);
1251+ addListeners();
1252+ }
1253+ else {
1254+ MWin.appendError(ERRID+"フォルダーの削除に失敗しました:"+folderNameWorking);
1255+ }
1256+ if ( ! rec.getErrmsg().equals("")) {
1257+ MWin.appendError(MSGID+"[追加情報] "+rec.getErrmsg());
1258+ ringBeep();
1259+ }
1260+
1261+ return null;
1262+ }
1263+ @Override
1264+ protected void doFinally() {
1265+ StWin.setVisible(false);
1266+ }
1267+ }.execute();
1268+
1269+ CommonSwingUtils.setLocationCenter(parent, (Component)StWin);
1270+ StWin.setVisible(true);
1271+ }
1272+ };
1273+
1274+ /*
1275+ * 「再取得」ボタン右の矢印ボタンの処理
1276+ * メニューをプルダウン表示する
1277+ */
1278+ private final MouseAdapter ma_reloadIndividual = new MouseAdapter() {
1279+ @Override
1280+ public void mousePressed(MouseEvent e) {
1281+ jPopupMenu_reload.show(jButton_reloadDefault, 0, jButton_reloadInd.getHeight());
1282+ }
1283+ };
1284+
1285+ /**
1286+ * 「再取得」ボタンの処理
1287+ * forceフラグを指定して録画タイトルのみ取得し、タイトル一覧を更新する
1288+ */
1289+ private final ActionListener al_reloadDefault = new ActionListener() {
1290+ @Override
1291+ public void actionPerformed(ActionEvent e) {
1292+ updateTitleList(true, false, true, false, true, true);
1293+ }
1294+ };
1295+
1296+ /**
1297+ * 「設定情報+録画タイトルを取得」ボタンの処理
1298+ * forceフラグを指定して設定情報と録画タイトルの両方を取得し、デバイスコンボ、フォルダコンボ、
1299+ * タイトル一覧を更新する
1300+ */
1301+ private final ActionListener al_reloadAll = new ActionListener() {
1302+ @Override
1303+ public void actionPerformed(ActionEvent e) {
1304+ updateTitleList(true, true, true, true, true, true);
1305+ }
1306+ };
1307+
1308+ /*
1309+ * 「設定情報のみ取得」メニューの処理
1310+ * forceフラグを指定して設定情報のみ取得し、デバイスコンボ、フォルダコンボ、タイトル一覧を更新する
1311+ */
1312+ private final ActionListener al_reloadSettingsOnly = new ActionListener() {
1313+ @Override
1314+ public void actionPerformed(ActionEvent e) {
1315+ updateTitleList(true, true, true, true, false, false);
1316+ }
1317+ };
1318+
1319+ /*
1320+ * 「録画タイトルのみ取得」メニューの処理
1321+ * forceフラグを指定して録画タイトルのみ取得し、タイトル一覧のみを更新する
1322+ */
1323+ private final ActionListener al_reloadTitlesOnly = new ActionListener() {
1324+ @Override
1325+ public void actionPerformed(ActionEvent e) {
1326+ updateTitleList(true, false, true, false, true, false);
1327+ }
1328+ };
1329+
1330+ /*
1331+ * 「録画タイトル+詳細情報のみ取得」メニューの処理
1332+ * forceフラグを指定して録画タイトルとその詳細情報のみ取得し、タイトル一覧のみを更新する
1333+ */
1334+ private final ActionListener al_reloadTitleAndDetailsOnly = new ActionListener() {
1335+ @Override
1336+ public void actionPerformed(ActionEvent e) {
1337+ updateTitleList(true, false, true, false, true, true);
1338+ }
1339+ };
1340+
1341+ /**
1342+ * デバイスコンボの選択変更時の処理
1343+ * デバイス情報更新後、タイトル一覧を更新する
1344+ */
1345+ private final ItemListener il_deviceChanged = new ItemListener() {
1346+ @Override
1347+ public void itemStateChanged(ItemEvent e) {
1348+ if (e.getStateChange() == ItemEvent.SELECTED) {
1349+ updateDeviceInfoLabel();
1350+ updateTitleList(false, false, true, false, true, false);
1351+ }
1352+ }
1353+ };
1354+
1355+ /**
1356+ * フォルダーコンボの選択変更時の処理
1357+ * タイトル一覧を再描画した後、フォルダー関係のボタンを更新する
1358+ */
1359+ private final ItemListener il_folderChanged = new ItemListener() {
1360+ @Override
1361+ public void itemStateChanged(ItemEvent e) {
1362+ if (e.getStateChange() == ItemEvent.SELECTED) {
1363+ _redrawTitleList();
1364+ updateFolderButtons();
1365+ }
1366+ }
1367+ };
1368+
1369+ /**
1370+ * ジャンルコンボの選択変更時の処理
1371+ * タイトル一覧を再描画する
1372+ */
1373+ private final ItemListener il_genreChanged = new ItemListener() {
1374+ @Override
1375+ public void itemStateChanged(ItemEvent e) {
1376+ if (e.getStateChange() == ItemEvent.SELECTED) {
1377+ _redrawTitleList();
1378+ }
1379+ }
1380+ };
1381+
1382+ /**
1383+ * タイトル一覧の選択変更時の処理
1384+ * タイトルの詳細情報を取得後、詳細情報を画面下部に表示する
1385+ */
1386+ private final ListSelectionListener lsSelectListener = new ListSelectionListener() {
1387+ @Override
1388+ public void valueChanged(ListSelectionEvent e) {
1389+ if(e.getValueIsAdjusting())
1390+ return;
1391+ int srow = jTable_title.getSelectedRow();
1392+ if (srow < 0)
1393+ return;
1394+
1395+ HDDRecorder rec = getSelectedRecorder();
1396+ if (rec == null)
1397+ return;
1398+
1399+ int row = jTable_title.convertRowIndexToModel(srow);
1400+ TitleItem c = rowView.get(row);
1401+ TitleInfo t = rec.getTitleInfo(c.hide_ttlid);
1402+ if (!t.getDetailLoaded() && rec.GetRdTitleDetail(t)){
1403+ t.setDetailLoaded(true);
1404+ redrawSelectedTitle(true);
1405+ }
1406+ else
1407+ redrawSelectedTitle(false);
1408+ }
1409+ };
1410+
1411+ /*
1412+ * 選択されているタイトルを再描画する
1413+ * @param b TRUEの場合リストにタイトル情報をセットし直す
1414+ */
1415+ public final void redrawSelectedTitle(boolean b) {
1416+ int srow = jTable_title.getSelectedRow();
1417+ if (srow < 0)
1418+ return;
1419+
1420+ HDDRecorder rec = getSelectedRecorder();
1421+ if (rec == null)
1422+ return;
1423+
1424+ int row = jTable_title.convertRowIndexToModel(srow);
1425+ if (row < 0)
1426+ return;
1427+
1428+ TitleItem c = rowView.get(row);
1429+ if (b){
1430+ TitleInfo t = rec.getTitleInfo(c.hide_ttlid);
1431+ setTitleItem(c, t, rec);
1432+ _redrawTitleList();
1433+ jTable_title.setRowSelectionInterval(srow, srow);
1434+ }
1435+
1436+ jta_detail.setText(c.hide_detail);
1437+ jta_detail.setCaretPosition(0);
1438+ }
1439+
1440+ /*
1441+ * 選択されたタイトルが複数のデバイスにまたがるか
1442+ */
1443+ private boolean areMultiDeviceTitles(ArrayList<TitleItem> array){
1444+ String devname = null;
1445+ for (TitleItem ti : array){
1446+ if (devname == null)
1447+ devname = ti.devname;
1448+ else if (!devname.equals(ti.devname))
1449+ return true;
1450+ }
1451+
1452+ return false;
1453+ }
1454+ /**
1455+ * タイトル一覧でのマウスイベント処理
1456+ * 右クリック時にポップアップメニューを表示する
1457+ */
1458+ private final MouseAdapter ma_showpopup = new MouseAdapter() {
1459+ @Override
1460+ public void mouseClicked(MouseEvent e) {
1461+ // 選択されたレコーダ
1462+ HDDRecorder rec = getSelectedRecorder();
1463+ if (rec == null)
1464+ return;
1465+
1466+ //
1467+ Point p = e.getPoint();
1468+ final int vrow = jTable_title.rowAtPoint(p);
1469+ final int row = jTable_title.convertRowIndexToModel(vrow);
1470+
1471+ TitleItem ra = rowView.get(row);
1472+ final String title = ra.title;
1473+ final String chnam = ra.chname;
1474+ final String startDT = ra.start;
1475+ final String recId = ra.recorder;
1476+ final String recName = recorders.getRecorderName(recId);
1477+ final String ttlId = ra.hide_ttlid;
1478+ final String devId = rec.getDeviceID(ra.devname);
1479+ int num =jTable_title.getSelectedRowCount();
1480+ final ArrayList<TitleItem> ras = new ArrayList<TitleItem>(num);
1481+ TitleInfo ttl = rec.getTitleInfo(ttlId);
1482+
1483+ final String otitle = getNextTitleInSameFolder(vrow);
1484+
1485+ //
1486+ if (e.getButton() == MouseEvent.BUTTON3) {
1487+ if (e.getClickCount() == 1) {
1488+ if (!jTable_title.isRowSelected(vrow))
1489+ jTable_title.getSelectionModel().setSelectionInterval(vrow,vrow);
1490+
1491+ String ttlIds[] = null;
1492+ if (num > 1){
1493+ ttlIds = new String[num];
1494+
1495+ int rows[] = jTable_title.getSelectedRows();
1496+ for (int n=0; n<num; n++){
1497+ TitleItem ti = rowView.get(jTable_title.convertRowIndexToModel(rows[n]));
1498+ ras.add(ti);
1499+ ttlIds[n] = ti.hide_ttlid;
1500+ }
1501+ }
1502+ final boolean muldev = areMultiDeviceTitles(ras);
1503+
1504+ // 右クリックでポップアップメニューを表示
1505+ JPopupMenu pop = new JPopupMenu();
1506+ pop.add(getTitleDetailMenuItem(title, chnam, devId, ttlId, recId));
1507+ if (ttlIds != null)
1508+ pop.add(getMultiTitleDetailMenuItem(title, chnam, devId, ttlIds, recId));
1509+
1510+ appendSelectDeviceMenuItem(pop, ttlId);
1511+ appendSelectFolderMenuItem(pop, ttlId);
1512+
1513+ pop.addSeparator();
1514+ pop.add(getStartStopPlayTitleMenuItem(true, title, chnam, devId, ttlId, recId));
1515+ pop.add(getStartStopPlayTitleMenuItem(false, title, chnam, devId, ttlId, recId));
1516+ pop.addSeparator();
1517+ pop.add(getEditTitleMenuItem(title, chnam, devId, ttlId, recId, otitle));
1518+ if (ttlIds != null){
1519+ JMenuItem mi = getMoveMultiTitleMenuItem(title, chnam, devId, ttlIds, recId);
1520+ if (muldev)
1521+ mi.setEnabled(false);
1522+ pop.addSeparator();
1523+ pop.add(mi);
1524+ }
1525+ pop.addSeparator();
1526+ pop.add(getRemoveTitleMenuItem(title, chnam, devId, ttlId, recId));
1527+ if (ttlIds != null)
1528+ pop.add(getRemoveMultiTitleMenuItem(title, chnam, devId, ttlIds, recId));
1529+
1530+ pop.addSeparator();
1531+ pop.add(getJumpMenuItem(title, chnam, startDT));
1532+ pop.addSeparator();
1533+
1534+ // クリップボードへコピーする
1535+ {
1536+ JMenuItem menuItem = new JMenuItem("番組名をコピー【"+title+"】");
1537+ menuItem.addActionListener(new ActionListener() {
1538+ public void actionPerformed(ActionEvent e) {
1539+ String msg = title;
1540+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1541+ StringSelection s = new StringSelection(msg);
1542+ cb.setContents(s, null);
1543+ }
1544+ });
1545+
1546+ pop.add(menuItem);
1547+ }
1548+ {
1549+ JMenuItem menuItem = new JMenuItem(String.format("タイトル情報をコピー【%s (%s)/%s】", title, chnam, recName));
1550+ menuItem.addActionListener(new ActionListener() {
1551+ public void actionPerformed(ActionEvent e) {
1552+ String msg = formatTitleItem(ra, false);
1553+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1554+ StringSelection s = new StringSelection(msg);
1555+ cb.setContents(s, null);
1556+ }
1557+ });
1558+
1559+ pop.add(menuItem);
1560+ }
1561+ if (num > 1){
1562+ JMenuItem menuItem = new JMenuItem(String.format("選択中の%d個のタイトル情報をコピー【%s (%s)/%s】",
1563+ num, title, chnam, recName));
1564+ menuItem.addActionListener(new ActionListener() {
1565+ public void actionPerformed(ActionEvent e) {
1566+ String msg = formatTitleItems(ras, false);
1567+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1568+ StringSelection s = new StringSelection(msg);
1569+ cb.setContents(s, null);
1570+ }
1571+ });
1572+
1573+ pop.add(menuItem);
1574+ }
1575+
1576+ pop.addSeparator();
1577+
1578+ // CSV形式でクリップボードへコピーする
1579+ {
1580+ JMenuItem menuItem = new JMenuItem(String.format("タイトル情報をCSVでコピー【%s (%s)/%s】",title,chnam, recName));
1581+ menuItem.addActionListener(new ActionListener() {
1582+ public void actionPerformed(ActionEvent e) {
1583+ String msg = formatTitleHeader(true) + formatTitleItem(ra, true);
1584+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1585+ StringSelection s = new StringSelection(msg);
1586+ cb.setContents(s, null);
1587+ }
1588+ });
1589+
1590+ pop.add(menuItem);
1591+ }
1592+ if (num > 1){
1593+ JMenuItem menuItem = new JMenuItem(String.format("選択中の%d個のタイトル情報をCSVでコピー【%s (%s)/%s】",
1594+ num, title, chnam, recName));
1595+ menuItem.addActionListener(new ActionListener() {
1596+ public void actionPerformed(ActionEvent e) {
1597+ String msg = formatTitleHeader(true) + formatTitleItems(ras, true);
1598+ Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1599+ StringSelection s = new StringSelection(msg);
1600+ cb.setContents(s, null);
1601+ }
1602+ });
1603+
1604+ pop.add(menuItem);
1605+ }
1606+
1607+ ProgDetailList pdl = getProgDetailForTitle(ttl);
1608+ if (pdl != null){
1609+ pop.addSeparator();
1610+ addBrowseMenuToPopup(pop, pdl);
1611+ }
1612+
1613+ pop.show(jTable_title, e.getX(), e.getY());
1614+ }
1615+ }
1616+ else if (e.getButton() == MouseEvent.BUTTON1) {
1617+ if (e.getClickCount() == 1) {
1618+ }
1619+ else if (e.getClickCount() == 2) {
1620+ editTitleOfRow(vrow);
1621+ }
1622+ }
1623+ }
1624+ };
1625+
1626+ /*
1627+ * ヘッダー情報をフォーマットする
1628+ */
1629+ private String formatTitleHeader(boolean csv){
1630+ StringBuilder sb = new StringBuilder();
1631+
1632+ for (TitleColumn col: TitleColumn.values()){
1633+ String value = col.toString();
1634+ boolean last = col == TitleColumn.RECORDER;
1635+ if (csv){
1636+ sb.append(CommonUtils.toQuoted(value));
1637+ if (!last)
1638+ sb.append(",");
1639+ }
1640+ else{
1641+ sb.append(value);
1642+ if (!last)
1643+ sb.append("\t");
1644+ }
1645+ }
1646+ sb.append("\n");
1647+
1648+ return sb.toString();
1649+ }
1650+ /*
1651+ * 複数のタイトル情報をテキストないしCSVでフォーマットする
1652+ */
1653+ private String formatTitleItems(ArrayList<TitleItem>ras, boolean csv){
1654+ StringBuilder sb = new StringBuilder();
1655+
1656+ for (TitleItem ra : ras){
1657+ sb.append(formatTitleItem(ra, csv));
1658+ }
1659+
1660+ return sb.toString();
1661+ }
1662+
1663+ /*
1664+ * タイトル情報をテキストないしCSVでフォーマットする
1665+ */
1666+ private String formatTitleItem(TitleItem ra, boolean csv){
1667+ StringBuilder sb = new StringBuilder();
1668+
1669+ for (TitleColumn col: TitleColumn.values()){
1670+ String value = "";
1671+ boolean last = col == TitleColumn.RECORDER;
1672+ switch(col){
1673+ case START:
1674+ value = ra.start;
1675+ break;
1676+ case END:
1677+ value = ra.end;
1678+ break;
1679+ case LENGTH:
1680+ value = ra.length + "m";
1681+ break;
1682+ case RECMODE:
1683+ value = ra.recmode;
1684+ break;
1685+ case TITLE:
1686+ value = ra.title;
1687+ break;
1688+ case CHNAME:
1689+ value = ra.chname;
1690+ break;
1691+ case DEVNAME:
1692+ value = ra.devname;
1693+ break;
1694+ case FOLDER:
1695+ value = ra.folder;
1696+ break;
1697+ case GENRE:
1698+ value = ra.genre;
1699+ break;
1700+ case RECORDER:
1701+ value = ra.recname;
1702+ break;
1703+ case COPYCOUNT:
1704+ value = ra.copycount;
1705+ break;
1706+ case DLNAOID:
1707+ value = ra.dlna_oid;
1708+ break;
1709+ }
1710+
1711+ if (value == null)
1712+ value = "";
1713+
1714+ if (csv){
1715+ sb.append(CommonUtils.toQuoted(value));
1716+ if (!last)
1717+ sb.append(",");
1718+ }
1719+ else{
1720+ sb.append(value);
1721+ if (!last)
1722+ sb.append("\t");
1723+ }
1724+ }
1725+
1726+ sb.append("\n");
1727+
1728+ return sb.toString();
1729+ }
1730+
1731+ /*******************************************************************************
1732+ * コンポーネント
1733+ ******************************************************************************/
1734+
1735+ /**
1736+ * タイトル一覧ペイン
1737+ */
1738+ private JScrollPane getJScrollPane_list() {
1739+
1740+ if ( jsc_list == null ) {
1741+ jsc_list = new JScrollPane();
1742+
1743+ jsc_list.setRowHeaderView(jTable_rowheader = new JTableRowHeader(rowView));
1744+ jsc_list.setViewportView(getNETable_title());
1745+
1746+ Dimension d = new Dimension(jTable_rowheader.getPreferredSize().width,0);
1747+ jsc_list.getRowHeader().setPreferredSize(d);
1748+
1749+ this.setRowHeaderVisible(env.getRowHeaderVisible());
1750+ }
1751+
1752+ return jsc_list;
1753+ }
1754+
1755+ /**
1756+ * 詳細情報ペイン
1757+ */
1758+ private JScrollPane getJTextPane_detail() {
1759+ if ( jsc_detail == null ) {
1760+ jsc_detail = new JScrollPane(jta_detail = new JTextAreaWithPopup(),JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
1761+ jta_detail.setRows(6);
1762+ jta_detail.setEditable(false);
1763+ jta_detail.setBackground(Color.LIGHT_GRAY);
1764+ }
1765+ return jsc_detail;
1766+ }
1767+
1768+ private TableColumn getColumn(TitleColumn lcol){
1769+ TableColumn col = null;
1770+ try{
1771+ col = jTable_title.getColumn(lcol.toString());
1772+ }
1773+ catch(IllegalArgumentException e){
1774+ return null;
1775+ }
1776+
1777+ return col;
1778+ }
1779+
1780+ /*
1781+ * テーブルのソーターを初期化する
1782+ */
1783+ private void initTableSorter(){
1784+ TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(tableModel_title);
1785+ jTable_title.setRowSorter(sorter);
1786+
1787+ // 数値でソートする項目用の計算式(番組長とか)
1788+ final Comparator<String> lengthcomp = new Comparator<String>() {
1789+
1790+ @Override
1791+ public int compare(String len1, String len2) {
1792+ return Integer.parseInt(len1.substring(0, len1.length()-1)) -
1793+ Integer.parseInt(len2.substring(0, len2.length()-1));
1794+ }
1795+ };
1796+
1797+ TableColumn col = getColumn(TitleColumn.LENGTH);
1798+ if (col != null)
1799+ sorter.setComparator(col.getModelIndex(),lengthcomp);
1800+
1801+ // コピー回数でソートする項目用の計算式(番組長とか)
1802+ final Comparator<String> copycomp = new Comparator<String>() {
1803+
1804+ @Override
1805+ public int compare(String str1, String str2) {
1806+ int num1 = parseCopyCount(str1);
1807+ int num2 = parseCopyCount(str2);
1808+ return num1 - num2;
1809+ }
1810+
1811+ // 整形されたコピー回数から回数を取得する
1812+ int parseCopyCount(String str){
1813+ try{
1814+ // 「移動のみ」の場合
1815+ if (str.equals(TitleInfo.MOVEONLY))
1816+ return 0;
1817+ // 「録画中」の場合
1818+ else if (str.equals(TitleInfo.RECORDING))
1819+ return -1;
1820+ // 「n回」の場合
1821+ else
1822+ return Integer.parseInt(str.substring(0, str.length()-1));
1823+ }
1824+ catch(NumberFormatException e){
1825+ return -2;
1826+ }
1827+ }
1828+ };
1829+
1830+ col = getColumn(TitleColumn.COPYCOUNT);
1831+ if (col != null)
1832+ sorter.setComparator(col.getModelIndex(), copycomp);
1833+ }
1834+
1835+ /*
1836+ * テーブルの列幅を初期化する
1837+ */
1838+ private void initTableColumnWidth(){
1839+ for ( TitleColumn rc : TitleColumn.values() ) {
1840+ if ( rc.getIniWidth() < 0 ) {
1841+ continue;
1842+ }
1843+
1844+ TableColumn col = getColumn(rc);
1845+ if (col == null)
1846+ continue;
1847+
1848+ Integer width = bounds.getTitleColumnSize().get(rc.toString());
1849+ if (width != null)
1850+ col.setPreferredWidth(width);
1851+ }
1852+ }
1853+
1854+ /*
1855+ * 列表示のカスタマイズ結果を反映する
1856+ */
1857+ public void reflectColumnEnv(){
1858+ tlitems = (TitleListColumnInfoList) getTlItemEnv().clone();
1859+
1860+ tableModel_title.setColumnIdentifiers(tlitems.getColNames());
1861+
1862+ // ソーターをつける
1863+ initTableSorter();
1864+
1865+ // 表示幅を初期化する
1866+ initTableColumnWidth();
1867+ }
1868+
1869+ /**
1870+ * タイトル一覧テーブル
1871+ */
1872+ private JNETable getNETable_title() {
1873+ if (jTable_title == null) {
1874+ tableModel_title = new TitleTableModel(tlitems.getColNames(), 0);
1875+ jTable_title = new JNETableTitle(tableModel_title, true);
1876+ jTable_title.setAutoResizeMode(JNETable.AUTO_RESIZE_OFF);
1877+
1878+ // ヘッダのモデル
1879+ rowheaderModel_title = (DefaultTableModel) jTable_rowheader.getModel();
1880+
1881+ // ソータを付ける
1882+ initTableSorter();
1883+
1884+ // 各カラムの幅
1885+ initTableColumnWidth();
1886+
1887+ // 詳細表示
1888+ jTable_title.getSelectionModel().addListSelectionListener(lsSelectListener);
1889+
1890+ // 一覧表クリックで削除メニュー出現
1891+ jTable_title.addMouseListener(ma_showpopup);
1892+
1893+ // ENTERキーでタイトルを編集する
1894+ jTable_title.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
1895+ jTable_title.getActionMap().put("Enter", new AbstractAction() {
1896+ @Override
1897+ public void actionPerformed(ActionEvent ae) {
1898+ editTitleOfRow(jTable_title.getSelectedRow());
1899+ }
1900+ });
1901+ }
1902+
1903+ return jTable_title;
1904+ }
1905+
1906+ /*
1907+ * 「個別再取得」ボタンを取得する
1908+ */
1909+ private JButton getReloadIndButton() {
1910+ if (jButton_reloadInd == null){
1911+ ImageIcon arrow = new ImageIcon(ICONFILE_PULLDOWNMENU);
1912+
1913+ jButton_reloadInd = new JButton(arrow);
1914+ jButton_reloadInd.addMouseListener(ma_reloadIndividual);
1915+
1916+ jPopupMenu_reload = new JPopupMenu();
1917+
1918+ JMenuItem item = new JMenuItem("設定情報のみ取得");
1919+ jPopupMenu_reload.add(item);
1920+ item.addActionListener(al_reloadSettingsOnly);
1921+
1922+ item = new JMenuItem("録画タイトルのみ取得");
1923+ jPopupMenu_reload.add(item);
1924+ item.addActionListener(al_reloadTitlesOnly);
1925+
1926+ item = new JMenuItem("録画タイトル+詳細情報のみ取得");
1927+ jPopupMenu_reload.add(item);
1928+ item.addActionListener(al_reloadTitleAndDetailsOnly);
1929+
1930+ item = new JMenuItem("設定情報+録画タイトル+詳細情報を取得");
1931+ jPopupMenu_reload.add(item);
1932+ item.addActionListener(al_reloadAll);
1933+ }
1934+
1935+ return jButton_reloadInd;
1936+ }
1937+
1938+ /*
1939+ * フォルダ用コンボボックスを取得する
1940+ */
1941+ private JComboBoxPanel getFolderComboPanel() {
1942+ if (jCBXPanel_folder == null){
1943+ jCBXPanel_folder = new JComboBoxPanel("フォルダ:", FLABEL_WIDTH, FCOMBO_WIDTH, true);
1944+ jCBXPanel_folder.addPopupWidth(FCOMBO_OPEN_WIDTH-FCOMBO_WIDTH);
1945+
1946+ JComboBox combo = jCBXPanel_folder.getJComboBox();
1947+ combo.setMaximumRowCount(32);
1948+ }
1949+
1950+ return jCBXPanel_folder;
1951+ }
1952+
1953+ /*******************************************************************************
1954+ * 表表示
1955+ ******************************************************************************/
1956+
1957+ private class JNETableTitle extends JNETable {
1958+
1959+ private static final long serialVersionUID = 1L;
1960+
1961+ // 実行中のタイトルの背景色
1962+ private Color currentColorEven = CommonUtils.str2color(CURRENT_COLOR_EVEN);
1963+ private Color currentColorOdd = CommonUtils.str2color(CURRENT_COLOR_ODD);
1964+
1965+ // 実行中のタイトルの背景色をセットする
1966+ public void setCurrentColor(Color c) {
1967+ if ( c == null ) {
1968+ currentColorEven = null;
1969+ currentColorOdd = null;
1970+ }
1971+ else {
1972+ currentColorOdd = c;
1973+ currentColorEven = new Color(
1974+ ((c.getRed()>=247)?(255):(c.getRed()+8)),
1975+ ((c.getGreen()>=247)?(255):(c.getGreen()+8)),
1976+ ((c.getBlue()>=247)?(255):(c.getBlue()+8))
1977+ );
1978+ }
1979+ }
1980+
1981+ @Override
1982+ public Component prepareRenderer(TableCellRenderer tcr, int row, int column) {
1983+ Component c = super.prepareRenderer(tcr, row, column);
1984+ Color fgColor = this.getForeground();
1985+ Color bgColor = (isSepRowColor && row%2 == 1)?(evenColor):(super.getBackground());
1986+
1987+ int xrow = this.convertRowIndexToModel(row);
1988+ TitleItem item = null;
1989+ try{
1990+ item = rowView.get(xrow);
1991+ }
1992+ catch(IndexOutOfBoundsException e){
1993+ return c;
1994+ }
1995+
1996+ // 実行中のタイトルの場合
1997+ if ( item.hide_recording ) {
1998+ bgColor = (isSepRowColor && row%2 == 1)?(currentColorEven):(currentColorOdd);
1999+ }
2000+
2001+ if(isRowSelected(row)) {
2002+ fgColor = this.getSelectionForeground();
2003+ bgColor = CommonUtils.getSelBgColor(bgColor);
2004+ }
2005+
2006+ c.setForeground(fgColor);
2007+ c.setBackground(bgColor);
2008+ return c;
2009+ }
2010+
2011+ //
2012+ @Override
2013+ public void tableChanged(TableModelEvent e) {
2014+ reset();
2015+ super.tableChanged(e);
2016+ }
2017+
2018+ private void reset() {
2019+ }
2020+
2021+ /*
2022+ * コンストラクタ
2023+ */
2024+ public JNETableTitle(boolean b) {
2025+ super(b);
2026+ }
2027+ public JNETableTitle(TableModel d, boolean b) {
2028+ super(d,b);
2029+ }
2030+ }
2031+
2032+ // 素直にHashMapつかっておけばよかった
2033+ public String text2value(ArrayList<TextValueSet> tvs, String text) {
2034+ for ( TextValueSet t : tvs ) {
2035+ if (t.getText().equals(text)) {
2036+ return(t.getValue());
2037+ }
2038+ }
2039+ return("");
2040+ }
2041+
2042+ /**
2043+ * デバイス絞り込みのメニューアイテムを追加する
2044+ */
2045+ protected void appendSelectDeviceMenuItem(final JPopupMenu pop, final String ttlId){
2046+ HDDRecorder rec = getSelectedRecorder();
2047+ TitleInfo ttl = rec.getTitleInfo(ttlId);
2048+
2049+ if (rec == null || ttl == null)
2050+ return;
2051+
2052+ String devNameOld = (String)jCBXPanel_device.getSelectedItem();
2053+ String devIdOld = rec.getDeviceID(devNameOld);
2054+ if (devIdOld == null)
2055+ return;
2056+
2057+ String devNameNew = ttl.getRec_device();
2058+ String folderName = getSelectedFolderName();
2059+
2060+ pop.addSeparator();
2061+
2062+ if (devIdOld.equals(HDDRecorder.DEVICE_ALL)){
2063+ JMenuItem menuItem = new JMenuItem("デバイスを選択する【"+devNameNew+"】");
2064+ menuItem.addActionListener(new ActionListener() {
2065+ public void actionPerformed(ActionEvent e) {
2066+ updateDeviceList(devNameNew);
2067+ updateDeviceInfoLabel();
2068+ updateFolderList(folderName);
2069+ updateTitleList(false, false, false , false, false, false);
2070+ }
2071+ });
2072+
2073+ pop.add(menuItem);
2074+ }
2075+ else{
2076+ JMenuItem menuItem = new JMenuItem("デバイスの選択を解除する");
2077+ menuItem.addActionListener(new ActionListener() {
2078+ public void actionPerformed(ActionEvent e) {
2079+ updateDeviceList(rec.getDeviceName(HDDRecorder.DEVICE_ALL));
2080+ updateDeviceInfoLabel();
2081+ updateFolderList(folderName);
2082+ updateTitleList(false, false, false, false, false, false);
2083+ }
2084+ });
2085+
2086+ pop.add(menuItem);
2087+ }
2088+ }
2089+
2090+ /**
2091+ * フォルダー絞り込みのメニューアイテムを追加する
2092+ */
2093+ protected void appendSelectFolderMenuItem(final JPopupMenu pop, final String ttlId){
2094+ HDDRecorder rec = getSelectedRecorder();
2095+ TitleInfo ttl = rec.getTitleInfo(ttlId);
2096+
2097+ if (ttl == null)
2098+ return;
2099+
2100+ ArrayList<TextValueSet> folders = ttl.getRec_folder();
2101+ if (folders == null || folders.isEmpty())
2102+ return;
2103+
2104+ String folderName = folders.get(0).getText();
2105+ String folderId = folderName != null ? text2value(rec.getFolderList(), folderName) : "";
2106+
2107+ int idx = jCBXPanel_folder.getSelectedIndex();
2108+ if (idx == 0){
2109+ JMenuItem menuItem = new JMenuItem("フォルダを選択する【"+folderName+"】");
2110+ menuItem.addActionListener(new ActionListener() {
2111+ public void actionPerformed(ActionEvent e) {
2112+ updateFolderList(folderName);
2113+ updateFolderButtons();
2114+ updateTitleList(false, false, false, false, false, false);
2115+ }
2116+ });
2117+
2118+ pop.add(menuItem);
2119+ }
2120+ else{
2121+ JMenuItem menuItem = new JMenuItem("フォルダの選択を解除する");
2122+ menuItem.addActionListener(new ActionListener() {
2123+ public void actionPerformed(ActionEvent e) {
2124+ jCBXPanel_folder.setSelectedIndex(0);
2125+ updateTitleList(false, false, false, false, false, false);
2126+ }
2127+ });
2128+
2129+ pop.add(menuItem);
2130+ }
2131+
2132+ if (!folderId.isEmpty()){
2133+ JMenuItem menuItem = new JMenuItem("フォルダを編集する【"+folderName+"】");
2134+ menuItem.addActionListener(new ActionListener() {
2135+ public void actionPerformed(ActionEvent e) {
2136+
2137+ editFolderName(folderId, folderName);
2138+ }
2139+ });
2140+
2141+ pop.add(menuItem);
2142+ }
2143+ }
2144+
2145+}
--- a/TinyBannavi/src/tainavi/AribCharMap.java
+++ b/TinyBannavi/src/tainavi/AribCharMap.java
@@ -1,99 +1,99 @@
1-package tainavi;
2-
3-import java.util.regex.Matcher;
4-import java.util.regex.Pattern;
5-
6-/*
7- * ARIB外字のマッピング情報
8- */
9-public enum AribCharMap {
10- HDTV ("HV" ,"\uE0F8"),
11- SDTV ("SD" ,"\uE0F9"),
12- PROGRESSIVE ("P" ,"\uE0FA"),
13- WIDE ("W" ,"\uE0FB"),
14- MULTIVIEW ("MV" ,"\uE0FC"),
15- SIGN ("手" ,"\uE0FD"),
16- SUBTITLE ("字" ,"\uE0FE"),
17- TWOWAY ("双" ,"\uE0FF"),
18- DATA ("デ" ,"\uE180"),
19- STEREO ("S" ,"\uE181"),
20- BILINGUAL ("二" ,"\uE182"),
21- MULTIPLEX ("多" ,"\uE183"),
22- COMMENTARY ("解" ,"\uE184"),
23- SURROUND ("SS" ,"\uE185"),
24- BMODE ("B" ,"\uE186"),
25- NEWS ("N" ,"\uE187"),
26- WEATHER ("天" ,"\uE18A"),
27- TRAFFIC ("交" ,"\uE18B"),
28- MOVIE ("映" ,"\uE18C"),
29- FREE ("無" ,"\uE18D"),
30- PAY ("料" ,"\uE18E"),
31- FORMER ("前" ,"\uE190"),
32- LATTER ("後" ,"\uE191"),
33- REAIR ("再" ,"\uE192"),
34- NEW ("新" ,"\uE193"),
35- FIRST ("初" ,"\uE194"),
36- END ("終" ,"\uE195"),
37- LIVE ("生" ,"\uE196"),
38- SHOPPING ("販" ,"\uE197"),
39- VOICE ("声" ,"\uE198"),
40- DUBBED ("吹" ,"\uE199"),
41- PPV ("PPV" ,"\uE19A"),
42-// SECRET ("秘" ,"\uE19B"),
43-// OTHER ("ほか" ,"\uE19C"),
44- ;
45-
46- String noaribStr;
47- String aribStr;
48-
49- private AribCharMap(String n, String a){
50- noaribStr = n;
51- aribStr = a;
52- }
53-
54- public String getNoAribPattern(){
55- return "\\[" + noaribStr + "\\]";
56- }
57-
58- public String getNoAribStr(){
59- return "[" + noaribStr + "]";
60- }
61-
62- public String getAribStr(){
63- return aribStr;
64- }
65-
66- /*
67- * ARIB外字を展開した文字列からARIB外字を含む文字列に変換する
68- */
69- public static String ConvStringToArib(String s){
70- if (s == null)
71- return s;
72-
73- for (AribCharMap acm : AribCharMap.values()){
74- Matcher ma = Pattern.compile(acm.getNoAribPattern()).matcher(s);
75- if (ma.find()){
76- s = ma.replaceAll(acm.getAribStr());
77- }
78- }
79-
80- return s;
81- }
82-
83- /*
84- * ARIB外字を含む文字列から展開した文字列に変換する
85- */
86- public static String ConvStringFromArib(String s){
87- if (s == null)
88- return s;
89-
90- for (AribCharMap acm : AribCharMap.values()){
91- Matcher ma = Pattern.compile(acm.getAribStr()).matcher(s);
92- if (ma.find()){
93- s = ma.replaceAll(acm.getNoAribStr());
94- }
95- }
96-
97- return s;
98- }
99-};
1+package tainavi;
2+
3+import java.util.regex.Matcher;
4+import java.util.regex.Pattern;
5+
6+/*
7+ * ARIB外字のマッピング情報
8+ */
9+public enum AribCharMap {
10+ HDTV ("HV" ,"\uE0F8"),
11+ SDTV ("SD" ,"\uE0F9"),
12+ PROGRESSIVE ("P" ,"\uE0FA"),
13+ WIDE ("W" ,"\uE0FB"),
14+ MULTIVIEW ("MV" ,"\uE0FC"),
15+ SIGN ("手" ,"\uE0FD"),
16+ SUBTITLE ("字" ,"\uE0FE"),
17+ TWOWAY ("双" ,"\uE0FF"),
18+ DATA ("デ" ,"\uE180"),
19+ STEREO ("S" ,"\uE181"),
20+ BILINGUAL ("二" ,"\uE182"),
21+ MULTIPLEX ("多" ,"\uE183"),
22+ COMMENTARY ("解" ,"\uE184"),
23+ SURROUND ("SS" ,"\uE185"),
24+ BMODE ("B" ,"\uE186"),
25+ NEWS ("N" ,"\uE187"),
26+ WEATHER ("天" ,"\uE18A"),
27+ TRAFFIC ("交" ,"\uE18B"),
28+ MOVIE ("映" ,"\uE18C"),
29+ FREE ("無" ,"\uE18D"),
30+ PAY ("料" ,"\uE18E"),
31+ FORMER ("前" ,"\uE190"),
32+ LATTER ("後" ,"\uE191"),
33+ REAIR ("再" ,"\uE192"),
34+ NEW ("新" ,"\uE193"),
35+ FIRST ("初" ,"\uE194"),
36+ END ("終" ,"\uE195"),
37+ LIVE ("生" ,"\uE196"),
38+ SHOPPING ("販" ,"\uE197"),
39+ VOICE ("声" ,"\uE198"),
40+ DUBBED ("吹" ,"\uE199"),
41+ PPV ("PPV" ,"\uE19A"),
42+// SECRET ("秘" ,"\uE19B"),
43+// OTHER ("ほか" ,"\uE19C"),
44+ ;
45+
46+ String noaribStr;
47+ String aribStr;
48+
49+ private AribCharMap(String n, String a){
50+ noaribStr = n;
51+ aribStr = a;
52+ }
53+
54+ public String getNoAribPattern(){
55+ return "\\[" + noaribStr + "\\]";
56+ }
57+
58+ public String getNoAribStr(){
59+ return "[" + noaribStr + "]";
60+ }
61+
62+ public String getAribStr(){
63+ return aribStr;
64+ }
65+
66+ /*
67+ * ARIB外字を展開した文字列からARIB外字を含む文字列に変換する
68+ */
69+ public static String ConvStringToArib(String s){
70+ if (s == null)
71+ return s;
72+
73+ for (AribCharMap acm : AribCharMap.values()){
74+ Matcher ma = Pattern.compile(acm.getNoAribPattern()).matcher(s);
75+ if (ma.find()){
76+ s = ma.replaceAll(acm.getAribStr());
77+ }
78+ }
79+
80+ return s;
81+ }
82+
83+ /*
84+ * ARIB外字を含む文字列から展開した文字列に変換する
85+ */
86+ public static String ConvStringFromArib(String s){
87+ if (s == null)
88+ return s;
89+
90+ for (AribCharMap acm : AribCharMap.values()){
91+ Matcher ma = Pattern.compile(acm.getAribStr()).matcher(s);
92+ if (ma.find()){
93+ s = ma.replaceAll(acm.getNoAribStr());
94+ }
95+ }
96+
97+ return s;
98+ }
99+};
--- a/TinyBannavi/src/tainavi/ChapterInfo.java
+++ b/TinyBannavi/src/tainavi/ChapterInfo.java
@@ -1,29 +1,29 @@
1-package tainavi;
2-
3-/**
4- * <P>個々のチャプターの情報を保持します。
5- */
6-public class ChapterInfo implements Cloneable {
7- private String name="";
8- private int duration=0;
9- private boolean changeFlag=false;
10-
11- @Override
12- public ChapterInfo clone() {
13- try {
14- ChapterInfo p = (ChapterInfo) super.clone();
15- return p;
16- } catch (CloneNotSupportedException e) {
17- throw new InternalError(e.toString());
18- }
19- }
20-
21- public String getName() {return name;}
22- public void setName(String s) { name=s;}
23-
24- public int getDuration() {return duration;}
25- public void setDuration(int d) { duration=d;}
26-
27- public boolean getChangeFlag() {return changeFlag;}
28- public void setChangeFlag(boolean b) { changeFlag=b;}
29-}
1+package tainavi;
2+
3+/**
4+ * <P>個々のチャプターの情報を保持します。
5+ */
6+public class ChapterInfo implements Cloneable {
7+ private String name="";
8+ private int duration=0;
9+ private boolean changeFlag=false;
10+
11+ @Override
12+ public ChapterInfo clone() {
13+ try {
14+ ChapterInfo p = (ChapterInfo) super.clone();
15+ return p;
16+ } catch (CloneNotSupportedException e) {
17+ throw new InternalError(e.toString());
18+ }
19+ }
20+
21+ public String getName() {return name;}
22+ public void setName(String s) { name=s;}
23+
24+ public int getDuration() {return duration;}
25+ public void setDuration(int d) { duration=d;}
26+
27+ public boolean getChangeFlag() {return changeFlag;}
28+ public void setChangeFlag(boolean b) { changeFlag=b;}
29+}
--- a/TinyBannavi/src/tainavi/CommonUtils.java
+++ b/TinyBannavi/src/tainavi/CommonUtils.java
@@ -353,11 +353,20 @@ public class CommonUtils {
353353 if ( isLateNight(r.getAhh()) ) {
354354 ca.add(Calendar.DATE, 1);
355355 }
356- ca.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getAhh()));
357- ca.set(Calendar.MINUTE, Integer.valueOf(r.getAmm()));
358- GregorianCalendar cb = (GregorianCalendar) ca.clone();
359- cb.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getZhh()));
360- cb.set(Calendar.MINUTE, Integer.valueOf(r.getZmm()));
356+ GregorianCalendar cb = null;
357+ try{
358+ ca.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getAhh()));
359+ ca.set(Calendar.MINUTE, Integer.valueOf(r.getAmm()));
360+ cb = (GregorianCalendar) ca.clone();
361+ cb.set(Calendar.HOUR_OF_DAY, Integer.valueOf(r.getZhh()));
362+ cb.set(Calendar.MINUTE, Integer.valueOf(r.getZmm()));
363+ }
364+ catch(NumberFormatException e){
365+ System.err.println("[ERROR] at getStartEndList()2: "+r.getAhh()+","+r.getAmm()+","+r.getZhh()+","+r.getZmm());
366+ e.printStackTrace();
367+ return;
368+ }
369+
361370 if ( cb.compareTo(ca) < 0 ) {
362371 cb.add(Calendar.DATE, 1); // 終了日時より開始日時が大きいならば
363372 }
--- a/TinyBannavi/src/tainavi/DeviceInfo.java
+++ b/TinyBannavi/src/tainavi/DeviceInfo.java
@@ -1,73 +1,73 @@
1-package tainavi;
2-
3-/**
4- * <P>個々のデバイスの情報を保持します。
5- */
6-public class DeviceInfo implements Cloneable {
7- private String id="";
8- private String name="";
9- private String type="";
10- private boolean playlistEnable;
11- private boolean folderEnable;
12- private int allsize;
13- private int freesize;
14- private int freemin;
15- private boolean connected;
16- private boolean canwrite;
17- private boolean isprotected;
18- private boolean mounted;
19- private boolean ready;
20- private int formatType;
21-
22- @Override
23- public DeviceInfo clone() {
24- try {
25- DeviceInfo p = (DeviceInfo) super.clone();
26- return p;
27- } catch (CloneNotSupportedException e) {
28- throw new InternalError(e.toString());
29- }
30- }
31-
32- public String getId(){ return id; }
33- public void setId(String s){ id = s; }
34-
35- public String getName() {return name;}
36- public void setName(String s){ name = s;}
37-
38- public String getType(){ return type; }
39- public void setType(String s){ type = s; }
40-
41- public boolean getPlaylistEnable(){ return playlistEnable; }
42- public void setPlaylistEnable(boolean b){ playlistEnable = b; }
43-
44- public boolean getFolderEnable(){ return folderEnable; }
45- public void setFolderEnable(boolean b){ folderEnable = b; }
46-
47- public int getAllSize(){ return allsize; }
48- public void setAllSize(int n){ allsize = n; }
49-
50- public int getFreeSize(){ return freesize; }
51- public void setFreeSize(int n){ freesize = n; }
52-
53- public int getFreeMin(){ return freemin; }
54- public void setFreeMin(int n){ freemin = n; }
55-
56- public boolean getConnected(){ return connected; }
57- public void setConnected(boolean b){ connected = b; }
58-
59- public boolean getCanWrite(){ return canwrite; }
60- public void setCanWrite(boolean b){ canwrite = b; }
61-
62- public boolean getProtected(){ return isprotected; }
63- public void setProtected(boolean b){ isprotected = b; }
64-
65- public boolean getMounted(){ return mounted; }
66- public void setMounted(boolean b){ mounted = b; }
67-
68- public boolean getReady(){ return ready; }
69- public void setReady(boolean b){ ready = b; }
70-
71- public int getFormatType(){ return formatType; }
72- public void setFormatType(int n){ formatType = n; }
73-}
1+package tainavi;
2+
3+/**
4+ * <P>個々のデバイスの情報を保持します。
5+ */
6+public class DeviceInfo implements Cloneable {
7+ private String id="";
8+ private String name="";
9+ private String type="";
10+ private boolean playlistEnable;
11+ private boolean folderEnable;
12+ private int allsize;
13+ private int freesize;
14+ private int freemin;
15+ private boolean connected;
16+ private boolean canwrite;
17+ private boolean isprotected;
18+ private boolean mounted;
19+ private boolean ready;
20+ private int formatType;
21+
22+ @Override
23+ public DeviceInfo clone() {
24+ try {
25+ DeviceInfo p = (DeviceInfo) super.clone();
26+ return p;
27+ } catch (CloneNotSupportedException e) {
28+ throw new InternalError(e.toString());
29+ }
30+ }
31+
32+ public String getId(){ return id; }
33+ public void setId(String s){ id = s; }
34+
35+ public String getName() {return name;}
36+ public void setName(String s){ name = s;}
37+
38+ public String getType(){ return type; }
39+ public void setType(String s){ type = s; }
40+
41+ public boolean getPlaylistEnable(){ return playlistEnable; }
42+ public void setPlaylistEnable(boolean b){ playlistEnable = b; }
43+
44+ public boolean getFolderEnable(){ return folderEnable; }
45+ public void setFolderEnable(boolean b){ folderEnable = b; }
46+
47+ public int getAllSize(){ return allsize; }
48+ public void setAllSize(int n){ allsize = n; }
49+
50+ public int getFreeSize(){ return freesize; }
51+ public void setFreeSize(int n){ freesize = n; }
52+
53+ public int getFreeMin(){ return freemin; }
54+ public void setFreeMin(int n){ freemin = n; }
55+
56+ public boolean getConnected(){ return connected; }
57+ public void setConnected(boolean b){ connected = b; }
58+
59+ public boolean getCanWrite(){ return canwrite; }
60+ public void setCanWrite(boolean b){ canwrite = b; }
61+
62+ public boolean getProtected(){ return isprotected; }
63+ public void setProtected(boolean b){ isprotected = b; }
64+
65+ public boolean getMounted(){ return mounted; }
66+ public void setMounted(boolean b){ mounted = b; }
67+
68+ public boolean getReady(){ return ready; }
69+ public void setReady(boolean b){ ready = b; }
70+
71+ public int getFormatType(){ return formatType; }
72+ public void setFormatType(int n){ formatType = n; }
73+}
--- a/TinyBannavi/src/tainavi/Env.java
+++ b/TinyBannavi/src/tainavi/Env.java
@@ -1316,11 +1316,6 @@ public class Env {
13161316 tv.setValue("https://twitter.com/share?text=%DATE%+%START%-%END%+%ENCCHNAME%+%ENCTITLE%");
13171317 al.add(tv);
13181318
1319- tv = new TextValueSet();
1320- tv.setText("CHAN-TORUの予約ページを開く");
1321- tv.setValue("https://tv.so-net.ne.jp/chan-toru/detail?type=bytime&cat=%TVKCAT%&area=%TVKAREACODE%&pid=%TVKPID%");
1322- al.add(tv);
1323-
13241319 this.setTvCommand(al);
13251320 }
13261321
--- a/TinyBannavi/src/tainavi/GetEventId.java
+++ b/TinyBannavi/src/tainavi/GetEventId.java
@@ -90,7 +90,7 @@ public class GetEventId extends TVProgramUtils implements TVProgram,Cloneable {
9090 break;
9191 }
9292
93- final String uri = "https://tv.so-net.ne.jp/iepg.tvpid?id="+chStr+ymd;
93+ final String uri = "https://www.tvkingdom.jp/iepg.tvpid?id="+chStr+ymd;
9494 final String res = webToBuffer(uri, thisEncoding, false);
9595 if ( res == null ) {
9696 System.err.println(errmsg = ERRID+"サイトへのアクセスが失敗しました: "+uri);
--- a/TinyBannavi/src/tainavi/JMenuItemWithShortcut.java
+++ b/TinyBannavi/src/tainavi/JMenuItemWithShortcut.java
@@ -1,27 +1,27 @@
1-package tainavi;
2-
3-import javax.swing.*;
4-import java.awt.event.KeyEvent;
5-
6-/**
7- * Created by unknown on 2014/07/02.
8- */
9-public class JMenuItemWithShortcut extends JMenuItem {
10-
11- public JMenuItemWithShortcut(String s) {
12- super(s);
13- }
14-
15- @Override
16- public String getText() {
17- int n = this.getMnemonic();
18- int k = 0;
19- if ( n >= KeyEvent.VK_0 && n >= KeyEvent.VK_0 ) {
20- k = '0' + (n - KeyEvent.VK_0);
21- }
22- if ( n >= KeyEvent.VK_A && n >= KeyEvent.VK_Z ) {
23- k = 'a' + (n - KeyEvent.VK_A);
24- }
25- return ((k > 0) ? String.format("(%c) ", n) : "") + super.getText();
26- }
27-}
1+package tainavi;
2+
3+import javax.swing.*;
4+import java.awt.event.KeyEvent;
5+
6+/**
7+ * Created by unknown on 2014/07/02.
8+ */
9+public class JMenuItemWithShortcut extends JMenuItem {
10+
11+ public JMenuItemWithShortcut(String s) {
12+ super(s);
13+ }
14+
15+ @Override
16+ public String getText() {
17+ int n = this.getMnemonic();
18+ int k = 0;
19+ if ( n >= KeyEvent.VK_0 && n >= KeyEvent.VK_0 ) {
20+ k = '0' + (n - KeyEvent.VK_0);
21+ }
22+ if ( n >= KeyEvent.VK_A && n >= KeyEvent.VK_Z ) {
23+ k = 'a' + (n - KeyEvent.VK_A);
24+ }
25+ return ((k > 0) ? String.format("(%c) ", n) : "") + super.getText();
26+ }
27+}
--- a/TinyBannavi/src/tainavi/JRTLabel.java
+++ b/TinyBannavi/src/tainavi/JRTLabel.java
@@ -1,48 +1,48 @@
1-package tainavi;
2-
3-import javax.swing.JLabel;
4-
5-public class JRTLabel extends JLabel {
6-
7- private static final long serialVersionUID = 1L;
8-
9- // 予約枠の仮想座標
10- private int vrow;
11- private int vheight;
12-
13- private int vcol;
14-
15- private int cno;
16-
17- private static float heightMultiplier = 0;
18- private static int vwidth;
19-
20- // データ操作メソッド
21- public int getVRow() { return vrow; }
22- public int getVHeight() { return vheight; }
23- public int getColorNo(){ return cno; }
24- public void setColorNo(int n){ cno = n; }
25-
26- public static void setHeightMultiplier(float f) { heightMultiplier = f; }
27- public static void setColumnWidth(int n){ vwidth = n; }
28-
29- public void setVBounds(int x, int y, int height) {
30- vcol = x;
31- vrow = y;
32- vheight = height;
33- super.setBounds(
34- vcol,
35- (int) Math.ceil(((float)vrow)*heightMultiplier),
36- vwidth,
37- (int) Math.ceil(((float)vheight)*heightMultiplier));
38- }
39-
40- public void reVBounds() {
41- super.setBounds(
42- vcol,
43- (int) Math.ceil(((float)vrow)*heightMultiplier),
44- vwidth,
45- (int) Math.ceil(((float)vheight)*heightMultiplier));
46- }
47-
48-}
1+package tainavi;
2+
3+import javax.swing.JLabel;
4+
5+public class JRTLabel extends JLabel {
6+
7+ private static final long serialVersionUID = 1L;
8+
9+ // 予約枠の仮想座標
10+ private int vrow;
11+ private int vheight;
12+
13+ private int vcol;
14+
15+ private int cno;
16+
17+ private static float heightMultiplier = 0;
18+ private static int vwidth;
19+
20+ // データ操作メソッド
21+ public int getVRow() { return vrow; }
22+ public int getVHeight() { return vheight; }
23+ public int getColorNo(){ return cno; }
24+ public void setColorNo(int n){ cno = n; }
25+
26+ public static void setHeightMultiplier(float f) { heightMultiplier = f; }
27+ public static void setColumnWidth(int n){ vwidth = n; }
28+
29+ public void setVBounds(int x, int y, int height) {
30+ vcol = x;
31+ vrow = y;
32+ vheight = height;
33+ super.setBounds(
34+ vcol,
35+ (int) Math.ceil(((float)vrow)*heightMultiplier),
36+ vwidth,
37+ (int) Math.ceil(((float)vheight)*heightMultiplier));
38+ }
39+
40+ public void reVBounds() {
41+ super.setBounds(
42+ vcol,
43+ (int) Math.ceil(((float)vrow)*heightMultiplier),
44+ vwidth,
45+ (int) Math.ceil(((float)vheight)*heightMultiplier));
46+ }
47+
48+}
--- a/TinyBannavi/src/tainavi/ListColumnInfo.java
+++ b/TinyBannavi/src/tainavi/ListColumnInfo.java
@@ -1,31 +1,31 @@
1-package tainavi;
2-
3-/**
4- * <P>リストの表示項目名を保持するクラスです。
5- * @since 3.22.18β+1.9
6- * @see ListedColumnInfoList
7- */
8-public class ListColumnInfo implements Cloneable {
9- @Override
10- public Object clone() {
11- try {
12- ListColumnInfo p = (ListColumnInfo) super.clone();
13- return p;
14- } catch (CloneNotSupportedException e) {
15- throw new InternalError(e.toString());
16- }
17- }
18-
19- private boolean visible = false ; // 表示対象か否か
20- private String name = "" ; // 項目名
21- private int id = 0 ; // ID
22-
23- public void setVisible(boolean b) { this.visible = b; }
24- public boolean getVisible() { return this.visible; }
25-
26- public void setName(String s) { this.name = s; }
27- public String getName() { return this.name; }
28-
29- public void setId(int id) { this.id = id; }
30- public int getId() { return this.id; }
31-}
1+package tainavi;
2+
3+/**
4+ * <P>リストの表示項目名を保持するクラスです。
5+ * @since 3.22.18β+1.9
6+ * @see ListedColumnInfoList
7+ */
8+public class ListColumnInfo implements Cloneable {
9+ @Override
10+ public Object clone() {
11+ try {
12+ ListColumnInfo p = (ListColumnInfo) super.clone();
13+ return p;
14+ } catch (CloneNotSupportedException e) {
15+ throw new InternalError(e.toString());
16+ }
17+ }
18+
19+ private boolean visible = false ; // 表示対象か否か
20+ private String name = "" ; // 項目名
21+ private int id = 0 ; // ID
22+
23+ public void setVisible(boolean b) { this.visible = b; }
24+ public boolean getVisible() { return this.visible; }
25+
26+ public void setName(String s) { this.name = s; }
27+ public String getName() { return this.name; }
28+
29+ public void setId(int id) { this.id = id; }
30+ public int getId() { return this.id; }
31+}
--- a/TinyBannavi/src/tainavi/ListedColumnInfoList.java
+++ b/TinyBannavi/src/tainavi/ListedColumnInfoList.java
@@ -1,148 +1,148 @@
1-package tainavi;
2-
3-import java.io.File;
4-import java.util.ArrayList;
5-
6-/**
7- * 「リスト形式」画面の {@link ListColumnInfo} のリストを実現するクラスです.
8- * @since 3.22.18β+1.9
9- */
10-public class ListedColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"lcinfolist.xml";
15-
16- @Override
17- public Object clone(){
18- ListedColumnInfoList p = new ListedColumnInfoList();
19-
20- for (ListColumnInfo li : this){
21- p.add((ListColumnInfo)li.clone());
22- }
23-
24- return p;
25- }
26-
27- public ListColumnInfo getVisibleAt(int column){
28- int cno = 0;
29- for (ListColumnInfo li : this){
30- if ( !li.getVisible() )
31- continue;
32-
33- if (cno == column)
34- return li;
35-
36- cno++;
37- }
38-
39- return null;
40- }
41-
42- /*
43- * IDを指定して取得する
44- */
45- public ListColumnInfo getById(int id){
46- for (ListColumnInfo li : this){
47- if (li.getId() == id)
48- return li;
49- }
50-
51- return null;
52- }
53-
54- /*
55- * JTableに指定する列名の配列を作成する
56- */
57- public String [] getColNames(){
58- // カラム名の初期化
59- ArrayList<String> cola = new ArrayList<String>();
60-
61- for ( ListColumnInfo li : this ) {
62- if ( li.getVisible() ) {
63- cola.add(li.getName());
64- }
65- }
66-
67- return cola.toArray(new String[0]);
68- }
69-
70- public boolean save() {
71- System.out.println("リスト形式の表示項目設定を保存します: "+listFile);
72-
73- if ( ! CommonUtils.writeXML(listFile, this) ) {
74- System.err.println("リスト形式の表示項目設定の保存に失敗しました: "+listFile);
75- return false;
76- }
77-
78- return true;
79- }
80-
81- public boolean load() {
82- System.out.println("リスト形式の表示項目設定を読み込みます: "+listFile);
83-
84- ArrayList<ListColumnInfo> cl = null;
85-
86- if ( new File(listFile).exists() ) {
87- // ファイルがあるならロード
88- cl = (ListedColumnInfoList) CommonUtils.readXML(listFile);
89- }
90-
91- if ( cl == null || cl.size() == 0 ) {
92- System.err.println("リスト形式の表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
93-
94- // 初期化してみよう
95- setDefault();
96-
97- return false;
98- }
99-
100- this.clear();
101- for (ListColumnInfo c : cl) {
102- this.add(c);
103- }
104-
105- return true;
106- }
107-
108- /*
109- * 初期化する
110- */
111- public void setDefault(){
112- this.clear();
113- int idx = 1;
114- Object[][] o = {
115- {true, "予約", idx++},
116- {true, "ピック", idx++},
117- {true, "重複", idx++},
118- {true, "チャンネル名", idx++},
119- {true, "オプション", idx++},
120- {true, "番組タイトル", idx++},
121- {true, "番組詳細", idx++},
122- {true, "開始時刻", idx++},
123- {true, "終了", idx++},
124- {true, "長さ", idx++},
125- {true, "ジャンル", idx++},
126- {true, "検索アイテム名",idx++},
127- {true, "お気に入り度", idx++},
128- {true, "スコア", idx++},
129- {true, "閾値", idx++},
130- {false, "予約1", idx++},
131- {false, "予約2", idx++},
132- {false, "予約3", idx++},
133- {false, "予約4", idx++},
134- {false, "予約5", idx++},
135- {false, "予約6", idx++},
136- {false, "予約7", idx++},
137- {false, "予約8", idx++},
138- };
139- for (int i=0; i<o.length; i++) {
140- ListColumnInfo cb = new ListColumnInfo();
141- cb.setVisible((Boolean) o[i][0]);
142- cb.setName((String) o[i][1]);
143- cb.setId((Integer) o[i][2]);
144- this.add(cb);
145- }
146- }
147-
148-}
1+package tainavi;
2+
3+import java.io.File;
4+import java.util.ArrayList;
5+
6+/**
7+ * 「リスト形式」画面の {@link ListColumnInfo} のリストを実現するクラスです.
8+ * @since 3.22.18β+1.9
9+ */
10+public class ListedColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"lcinfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ ListedColumnInfoList p = new ListedColumnInfoList();
19+
20+ for (ListColumnInfo li : this){
21+ p.add((ListColumnInfo)li.clone());
22+ }
23+
24+ return p;
25+ }
26+
27+ public ListColumnInfo getVisibleAt(int column){
28+ int cno = 0;
29+ for (ListColumnInfo li : this){
30+ if ( !li.getVisible() )
31+ continue;
32+
33+ if (cno == column)
34+ return li;
35+
36+ cno++;
37+ }
38+
39+ return null;
40+ }
41+
42+ /*
43+ * IDを指定して取得する
44+ */
45+ public ListColumnInfo getById(int id){
46+ for (ListColumnInfo li : this){
47+ if (li.getId() == id)
48+ return li;
49+ }
50+
51+ return null;
52+ }
53+
54+ /*
55+ * JTableに指定する列名の配列を作成する
56+ */
57+ public String [] getColNames(){
58+ // カラム名の初期化
59+ ArrayList<String> cola = new ArrayList<String>();
60+
61+ for ( ListColumnInfo li : this ) {
62+ if ( li.getVisible() ) {
63+ cola.add(li.getName());
64+ }
65+ }
66+
67+ return cola.toArray(new String[0]);
68+ }
69+
70+ public boolean save() {
71+ System.out.println("リスト形式の表示項目設定を保存します: "+listFile);
72+
73+ if ( ! CommonUtils.writeXML(listFile, this) ) {
74+ System.err.println("リスト形式の表示項目設定の保存に失敗しました: "+listFile);
75+ return false;
76+ }
77+
78+ return true;
79+ }
80+
81+ public boolean load() {
82+ System.out.println("リスト形式の表示項目設定を読み込みます: "+listFile);
83+
84+ ArrayList<ListColumnInfo> cl = null;
85+
86+ if ( new File(listFile).exists() ) {
87+ // ファイルがあるならロード
88+ cl = (ListedColumnInfoList) CommonUtils.readXML(listFile);
89+ }
90+
91+ if ( cl == null || cl.size() == 0 ) {
92+ System.err.println("リスト形式の表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
93+
94+ // 初期化してみよう
95+ setDefault();
96+
97+ return false;
98+ }
99+
100+ this.clear();
101+ for (ListColumnInfo c : cl) {
102+ this.add(c);
103+ }
104+
105+ return true;
106+ }
107+
108+ /*
109+ * 初期化する
110+ */
111+ public void setDefault(){
112+ this.clear();
113+ int idx = 1;
114+ Object[][] o = {
115+ {true, "予約", idx++},
116+ {true, "ピック", idx++},
117+ {true, "重複", idx++},
118+ {true, "チャンネル名", idx++},
119+ {true, "オプション", idx++},
120+ {true, "番組タイトル", idx++},
121+ {true, "番組詳細", idx++},
122+ {true, "開始時刻", idx++},
123+ {true, "終了", idx++},
124+ {true, "長さ", idx++},
125+ {true, "ジャンル", idx++},
126+ {true, "検索アイテム名",idx++},
127+ {true, "お気に入り度", idx++},
128+ {true, "スコア", idx++},
129+ {true, "閾値", idx++},
130+ {false, "予約1", idx++},
131+ {false, "予約2", idx++},
132+ {false, "予約3", idx++},
133+ {false, "予約4", idx++},
134+ {false, "予約5", idx++},
135+ {false, "予約6", idx++},
136+ {false, "予約7", idx++},
137+ {false, "予約8", idx++},
138+ };
139+ for (int i=0; i<o.length; i++) {
140+ ListColumnInfo cb = new ListColumnInfo();
141+ cb.setVisible((Boolean) o[i][0]);
142+ cb.setName((String) o[i][1]);
143+ cb.setId((Integer) o[i][2]);
144+ this.add(cb);
145+ }
146+ }
147+
148+}
--- a/TinyBannavi/src/tainavi/ListedNodeInfoList.java
+++ b/TinyBannavi/src/tainavi/ListedNodeInfoList.java
@@ -1,132 +1,132 @@
1-package tainavi;
2-
3-import java.io.File;
4-import java.util.ArrayList;
5-
6-/**
7- * リスト形式で {@link ListColumnInfo} のノードのリストを実現するクラスです.
8- * @since 3.22.18β+1.9
9- */
10-public class ListedNodeInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"lninfolist.xml";
15-
16- @Override
17- public Object clone(){
18- ListedNodeInfoList p = new ListedNodeInfoList();
19-
20- for (ListColumnInfo li : this){
21- p.add((ListColumnInfo)li.clone());
22- }
23-
24- return p;
25- }
26-
27- public ListColumnInfo getVisibleAt(int column){
28- int cno = 0;
29- for (ListColumnInfo li : this){
30- if ( !li.getVisible() )
31- continue;
32-
33- if (cno == column)
34- return li;
35-
36- cno++;
37- }
38-
39- return null;
40- }
41-
42- /*
43- * JTreeViewに指定するノード名の配列を作成する
44- */
45- public String [] getNodeNames(){
46- // カラム名の初期化
47- ArrayList<String> cola = new ArrayList<String>();
48-
49- for ( ListColumnInfo li : this ) {
50- if ( li.getVisible() ) {
51- cola.add(li.getName());
52- }
53- }
54-
55- return cola.toArray(new String[0]);
56- }
57-
58- /*
59- * ファイルに保存する
60- */
61- public boolean save() {
62- System.out.println("リスト形式のツリーの表示項目設定を保存します: "+listFile);
63-
64- if ( ! CommonUtils.writeXML(listFile, this) ) {
65- System.err.println("リスト形式のツリーの表示項目設定の保存に失敗しました: "+listFile);
66- return false;
67- }
68-
69- return true;
70- }
71-
72- /*
73- * ファイルから読み込む
74- */
75- public boolean load() {
76- System.out.println("リスト形式のツリーの表示項目設定を読み込みます: "+listFile);
77-
78- ArrayList<ListColumnInfo> cl = null;
79-
80- if ( new File(listFile).exists() ) {
81- // ファイルがあるならロード
82- cl = (ListedNodeInfoList) CommonUtils.readXML(listFile);
83- }
84-
85- if ( cl == null || cl.size() == 0 ) {
86- System.err.println("リスト形式のツリーの表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
87-
88- // 初期化してみよう
89- setDefault();
90-
91- return false;
92- }
93-
94- this.clear();
95- for (ListColumnInfo c : cl) {
96- this.add(c);
97- }
98-
99- return true;
100- }
101-
102- /*
103- * 初期化する
104- */
105- public void setDefault(){
106- this.clear();
107-
108- int idx = 1;
109- Object[][] o = {
110- {true, "過去ログ検索履歴", idx++},
111- {true, "新番組一覧", idx++},
112- {true, "最終回一覧", idx++},
113- {true, "現在放送中", idx++},
114- {true, "予約待機", idx++},
115- {true, "番組追跡", idx++},
116- {true, "キーワード検索", idx++},
117- {true, "キーワードグループ",idx++},
118- {true, "ジャンル別", idx++},
119- {true, "放送局別", idx++},
120- {true, "延長警告管理", idx++},
121- {true, "しょぼかる", idx++},
122- {false, "ピックアップ", idx++},
123- };
124- for (int i=0; i<o.length; i++) {
125- ListColumnInfo cb = new ListColumnInfo();
126- cb.setVisible((Boolean) o[i][0]);
127- cb.setName((String) o[i][1]);
128- cb.setId((Integer) o[i][2]);
129- this.add(cb);
130- }
131- }
132-}
1+package tainavi;
2+
3+import java.io.File;
4+import java.util.ArrayList;
5+
6+/**
7+ * リスト形式で {@link ListColumnInfo} のノードのリストを実現するクラスです.
8+ * @since 3.22.18β+1.9
9+ */
10+public class ListedNodeInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"lninfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ ListedNodeInfoList p = new ListedNodeInfoList();
19+
20+ for (ListColumnInfo li : this){
21+ p.add((ListColumnInfo)li.clone());
22+ }
23+
24+ return p;
25+ }
26+
27+ public ListColumnInfo getVisibleAt(int column){
28+ int cno = 0;
29+ for (ListColumnInfo li : this){
30+ if ( !li.getVisible() )
31+ continue;
32+
33+ if (cno == column)
34+ return li;
35+
36+ cno++;
37+ }
38+
39+ return null;
40+ }
41+
42+ /*
43+ * JTreeViewに指定するノード名の配列を作成する
44+ */
45+ public String [] getNodeNames(){
46+ // カラム名の初期化
47+ ArrayList<String> cola = new ArrayList<String>();
48+
49+ for ( ListColumnInfo li : this ) {
50+ if ( li.getVisible() ) {
51+ cola.add(li.getName());
52+ }
53+ }
54+
55+ return cola.toArray(new String[0]);
56+ }
57+
58+ /*
59+ * ファイルに保存する
60+ */
61+ public boolean save() {
62+ System.out.println("リスト形式のツリーの表示項目設定を保存します: "+listFile);
63+
64+ if ( ! CommonUtils.writeXML(listFile, this) ) {
65+ System.err.println("リスト形式のツリーの表示項目設定の保存に失敗しました: "+listFile);
66+ return false;
67+ }
68+
69+ return true;
70+ }
71+
72+ /*
73+ * ファイルから読み込む
74+ */
75+ public boolean load() {
76+ System.out.println("リスト形式のツリーの表示項目設定を読み込みます: "+listFile);
77+
78+ ArrayList<ListColumnInfo> cl = null;
79+
80+ if ( new File(listFile).exists() ) {
81+ // ファイルがあるならロード
82+ cl = (ListedNodeInfoList) CommonUtils.readXML(listFile);
83+ }
84+
85+ if ( cl == null || cl.size() == 0 ) {
86+ System.err.println("リスト形式のツリーの表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
87+
88+ // 初期化してみよう
89+ setDefault();
90+
91+ return false;
92+ }
93+
94+ this.clear();
95+ for (ListColumnInfo c : cl) {
96+ this.add(c);
97+ }
98+
99+ return true;
100+ }
101+
102+ /*
103+ * 初期化する
104+ */
105+ public void setDefault(){
106+ this.clear();
107+
108+ int idx = 1;
109+ Object[][] o = {
110+ {true, "過去ログ検索履歴", idx++},
111+ {true, "新番組一覧", idx++},
112+ {true, "最終回一覧", idx++},
113+ {true, "現在放送中", idx++},
114+ {true, "予約待機", idx++},
115+ {true, "番組追跡", idx++},
116+ {true, "キーワード検索", idx++},
117+ {true, "キーワードグループ",idx++},
118+ {true, "ジャンル別", idx++},
119+ {true, "放送局別", idx++},
120+ {true, "延長警告管理", idx++},
121+ {true, "しょぼかる", idx++},
122+ {false, "ピックアップ", idx++},
123+ };
124+ for (int i=0; i<o.length; i++) {
125+ ListColumnInfo cb = new ListColumnInfo();
126+ cb.setVisible((Boolean) o[i][0]);
127+ cb.setName((String) o[i][1]);
128+ cb.setId((Integer) o[i][2]);
129+ this.add(cb);
130+ }
131+ }
132+}
--- a/TinyBannavi/src/tainavi/ResTimeItem.java
+++ b/TinyBannavi/src/tainavi/ResTimeItem.java
@@ -1,34 +1,34 @@
1-package tainavi;
2-
3-/**
4- * <P>録画予約の開始・終了日時と重複度を管理します
5- */
6-public class ResTimeItem {
7- String recorder;
8- String start;
9- String end;
10- int count;
11-
12- public ResTimeItem(String r, String s, String e, int c){
13- this.recorder = r;
14- this.start = s;
15- this.end = e;
16- this.count = c;
17- }
18-
19- public ResTimeItem(String r, String s, String e){
20- this.recorder = r;
21- this.start = s;
22- this.end = e;
23- this.count = 1;
24- }
25-
26- public void setStart(String s){ start = s; }
27- public void setEnd(String e){ end = e; }
28- public void addCount(){ count++; }
29-
30- public String getRecorder(){ return recorder; }
31- public String getStart(){ return start; }
32- public String getEnd(){ return end; }
33- public int getCount(){ return count; }
34-}
1+package tainavi;
2+
3+/**
4+ * <P>録画予約の開始・終了日時と重複度を管理します
5+ */
6+public class ResTimeItem {
7+ String recorder;
8+ String start;
9+ String end;
10+ int count;
11+
12+ public ResTimeItem(String r, String s, String e, int c){
13+ this.recorder = r;
14+ this.start = s;
15+ this.end = e;
16+ this.count = c;
17+ }
18+
19+ public ResTimeItem(String r, String s, String e){
20+ this.recorder = r;
21+ this.start = s;
22+ this.end = e;
23+ this.count = 1;
24+ }
25+
26+ public void setStart(String s){ start = s; }
27+ public void setEnd(String e){ end = e; }
28+ public void addCount(){ count++; }
29+
30+ public String getRecorder(){ return recorder; }
31+ public String getStart(){ return start; }
32+ public String getEnd(){ return end; }
33+ public int getCount(){ return count; }
34+}
--- a/TinyBannavi/src/tainavi/ResTimeList.java
+++ b/TinyBannavi/src/tainavi/ResTimeList.java
@@ -1,177 +1,177 @@
1-package tainavi;
2-
3-import java.util.ArrayList;
4-import java.util.Comparator;
5-
6-/**
7- * <P>録画予約の開始・終了日時と重複度を計算します
8- */
9-public class ResTimeList extends ArrayList<ResTimeItem> {
10- /*
11- * ResTimeItemオブジェクト配列の比較関数(開始時刻順に並ぶようにする)
12- *
13- */
14- public class ResTimeItemComparator implements Comparator<ResTimeItem> {
15- @Override
16- public int compare(ResTimeItem p1, ResTimeItem p2) {
17- int rc = p1.getRecorder().compareTo(p2.getRecorder());
18- if (rc != 0)
19- return rc;
20-
21- return p1.getStart().compareTo(p2.getStart());
22- }
23- }
24-
25- /*
26- * ResTimeItemオブジェクト配列に予約時間枠をマージしながら追加する
27- */
28- public void mergeResTimeItem(String rec, String start, String end){
29- Boolean b = false;
30-
31- // 長さ0の時間枠は無視する
32- if (start.equals(end))
33- return;
34-
35- // すでにある予約枠と順に比較する
36- for (ResTimeItem item2 : this){
37- String start2 = item2.getStart();
38- String end2 = item2.getEnd();
39- String rec2 = item2.getRecorder();
40- if (!rec2.equals(rec))
41- continue;
42-
43- // 比較対象(item2)の予約枠の開始時刻より前に終わる場合はループを抜け、単に追加する
44- // start end
45- // |--------------|
46- // |<-- item2 -->|
47- if (end.compareTo(start2) <= 0){
48- break;
49- }
50- // item2の終了時刻より後に始まる場合は次の予約枠へ
51- // start end
52- // |--------------|
53- // |<-- item2 -->|
54- else if (start.compareTo(end2) >= 0){
55- continue;
56- }
57- // item2の開始時刻より前に始まる場合
58- // start
59- // |---------------?
60- // <- a ->|<-- item2 -->|
61- else if (start.compareTo(start2) < 0){
62- // item2の開始時刻までの枠(a)を再帰呼出しにより追加する
63- mergeResTimeItem(rec, start, start2);
64-
65- // item2の終了時刻より後に終わる場合
66- // start end
67- // |-----------------------------|
68- // <- a ->|<-- item2 -->|<- b ->
69- if (end.compareTo(end2) >= 0){
70- // item2のカウントを増やす
71- item2.addCount();
72-
73- // item2の終了時刻後の枠(b)を再帰呼出しにより追加する
74- mergeResTimeItem(rec, end2, end);
75- }
76- // item2の終了時刻より前に終わる場合
77- // start end
78- // |--------------|<- c ->
79- // <- a ->|<--- item2 -->|
80- else{
81- // item2の終了時間までの枠(c)を追加する
82- ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
83- addResTimeItem(itemT);
84-
85- // item2の時間を短くし、カウントを増やす
86- item2.setEnd(end);
87- item2.addCount();
88- }
89- }
90- // item2のの開始時刻より後に始まる場合
91- // start
92- // <- d ->|---------------?
93- // |<------- item2 -------->|
94- else{
95- // item2の開始時刻からの枠(d)を追加する
96- if (start.compareTo(start2) > 0){
97- ResTimeItem itemH = new ResTimeItem(rec, start2, start, item2.getCount());
98- addResTimeItem(itemH);
99- }
100-
101- // item2の終了時刻より後に終わる場合
102- // start end
103- // <- d ->|--------------------|
104- // |<------- item2 ---->|<- e ->
105- if (end.compareTo(end2) >= 0){
106- // item2の開始時刻と長さを変更し、カウントを増やす
107- item2.setStart(start);
108- item2.setEnd(end2);
109- item2.addCount();
110-
111- // item2の終了時刻後の枠(e)を再帰呼出しにより追加する
112- mergeResTimeItem(rec, end2, end);
113- }
114- // item2の予約枠の終了時間より前に終わる場合
115- // start end
116- // <- d ->|--------------|<- f ->
117- // |<----------- item2 ---------->|
118- else{
119- // item2の終了時刻前の枠(f)を追加する
120- ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
121- addResTimeItem(itemT);
122-
123- // item2の開始時刻と長さを変更し、カウントを増やす
124- item2.setStart(start);
125- item2.setEnd(end);
126- item2.addCount();
127- }
128- }
129-
130- b = true;
131- break;
132- }
133-
134- // 処理が完了しなかった場合は単に追加する
135- if (!b){
136- ResTimeItem item = new ResTimeItem(rec, start, end);
137- addResTimeItem(item);
138- }
139- }
140-
141- /*
142- * 指定した期間の最大予約数を返す
143- */
144- public int getMaxResCount(String rec, String start, String end){
145- int count = 0;
146-
147- // すでにある予約枠と順に比較する
148- for (ResTimeItem item2 : this){
149- String rec2 = item2.getRecorder();
150- if (!rec2.equals(rec))
151- continue;
152-
153- String start2 = item2.getStart();
154- String end2 = item2.getEnd();
155- int count2 = item2.getCount();
156-
157- if (start.compareTo(end2) < 0 && end.compareTo(start2) > 0){
158- if (count2 > count)
159- count = count2;
160- }
161- }
162-
163- return count;
164- }
165- /*
166- * ResTimeItemオブジェクト配列に予約時間枠を追加する
167- */
168- private void addResTimeItem(ResTimeItem item){
169- if (item == null)
170- return;
171-
172- this.add(item);
173-
174- // 開始時刻の昇順になるように並び替える
175- this.sort(new ResTimeItemComparator());
176- }
177-}
1+package tainavi;
2+
3+import java.util.ArrayList;
4+import java.util.Comparator;
5+
6+/**
7+ * <P>録画予約の開始・終了日時と重複度を計算します
8+ */
9+public class ResTimeList extends ArrayList<ResTimeItem> {
10+ /*
11+ * ResTimeItemオブジェクト配列の比較関数(開始時刻順に並ぶようにする)
12+ *
13+ */
14+ public class ResTimeItemComparator implements Comparator<ResTimeItem> {
15+ @Override
16+ public int compare(ResTimeItem p1, ResTimeItem p2) {
17+ int rc = p1.getRecorder().compareTo(p2.getRecorder());
18+ if (rc != 0)
19+ return rc;
20+
21+ return p1.getStart().compareTo(p2.getStart());
22+ }
23+ }
24+
25+ /*
26+ * ResTimeItemオブジェクト配列に予約時間枠をマージしながら追加する
27+ */
28+ public void mergeResTimeItem(String rec, String start, String end){
29+ Boolean b = false;
30+
31+ // 長さ0の時間枠は無視する
32+ if (start.equals(end))
33+ return;
34+
35+ // すでにある予約枠と順に比較する
36+ for (ResTimeItem item2 : this){
37+ String start2 = item2.getStart();
38+ String end2 = item2.getEnd();
39+ String rec2 = item2.getRecorder();
40+ if (!rec2.equals(rec))
41+ continue;
42+
43+ // 比較対象(item2)の予約枠の開始時刻より前に終わる場合はループを抜け、単に追加する
44+ // start end
45+ // |--------------|
46+ // |<-- item2 -->|
47+ if (end.compareTo(start2) <= 0){
48+ break;
49+ }
50+ // item2の終了時刻より後に始まる場合は次の予約枠へ
51+ // start end
52+ // |--------------|
53+ // |<-- item2 -->|
54+ else if (start.compareTo(end2) >= 0){
55+ continue;
56+ }
57+ // item2の開始時刻より前に始まる場合
58+ // start
59+ // |---------------?
60+ // <- a ->|<-- item2 -->|
61+ else if (start.compareTo(start2) < 0){
62+ // item2の開始時刻までの枠(a)を再帰呼出しにより追加する
63+ mergeResTimeItem(rec, start, start2);
64+
65+ // item2の終了時刻より後に終わる場合
66+ // start end
67+ // |-----------------------------|
68+ // <- a ->|<-- item2 -->|<- b ->
69+ if (end.compareTo(end2) >= 0){
70+ // item2のカウントを増やす
71+ item2.addCount();
72+
73+ // item2の終了時刻後の枠(b)を再帰呼出しにより追加する
74+ mergeResTimeItem(rec, end2, end);
75+ }
76+ // item2の終了時刻より前に終わる場合
77+ // start end
78+ // |--------------|<- c ->
79+ // <- a ->|<--- item2 -->|
80+ else{
81+ // item2の終了時間までの枠(c)を追加する
82+ ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
83+ addResTimeItem(itemT);
84+
85+ // item2の時間を短くし、カウントを増やす
86+ item2.setEnd(end);
87+ item2.addCount();
88+ }
89+ }
90+ // item2のの開始時刻より後に始まる場合
91+ // start
92+ // <- d ->|---------------?
93+ // |<------- item2 -------->|
94+ else{
95+ // item2の開始時刻からの枠(d)を追加する
96+ if (start.compareTo(start2) > 0){
97+ ResTimeItem itemH = new ResTimeItem(rec, start2, start, item2.getCount());
98+ addResTimeItem(itemH);
99+ }
100+
101+ // item2の終了時刻より後に終わる場合
102+ // start end
103+ // <- d ->|--------------------|
104+ // |<------- item2 ---->|<- e ->
105+ if (end.compareTo(end2) >= 0){
106+ // item2の開始時刻と長さを変更し、カウントを増やす
107+ item2.setStart(start);
108+ item2.setEnd(end2);
109+ item2.addCount();
110+
111+ // item2の終了時刻後の枠(e)を再帰呼出しにより追加する
112+ mergeResTimeItem(rec, end2, end);
113+ }
114+ // item2の予約枠の終了時間より前に終わる場合
115+ // start end
116+ // <- d ->|--------------|<- f ->
117+ // |<----------- item2 ---------->|
118+ else{
119+ // item2の終了時刻前の枠(f)を追加する
120+ ResTimeItem itemT = new ResTimeItem(rec, end, end2, item2.getCount());
121+ addResTimeItem(itemT);
122+
123+ // item2の開始時刻と長さを変更し、カウントを増やす
124+ item2.setStart(start);
125+ item2.setEnd(end);
126+ item2.addCount();
127+ }
128+ }
129+
130+ b = true;
131+ break;
132+ }
133+
134+ // 処理が完了しなかった場合は単に追加する
135+ if (!b){
136+ ResTimeItem item = new ResTimeItem(rec, start, end);
137+ addResTimeItem(item);
138+ }
139+ }
140+
141+ /*
142+ * 指定した期間の最大予約数を返す
143+ */
144+ public int getMaxResCount(String rec, String start, String end){
145+ int count = 0;
146+
147+ // すでにある予約枠と順に比較する
148+ for (ResTimeItem item2 : this){
149+ String rec2 = item2.getRecorder();
150+ if (!rec2.equals(rec))
151+ continue;
152+
153+ String start2 = item2.getStart();
154+ String end2 = item2.getEnd();
155+ int count2 = item2.getCount();
156+
157+ if (start.compareTo(end2) < 0 && end.compareTo(start2) > 0){
158+ if (count2 > count)
159+ count = count2;
160+ }
161+ }
162+
163+ return count;
164+ }
165+ /*
166+ * ResTimeItemオブジェクト配列に予約時間枠を追加する
167+ */
168+ private void addResTimeItem(ResTimeItem item){
169+ if (item == null)
170+ return;
171+
172+ this.add(item);
173+
174+ // 開始時刻の昇順になるように並び替える
175+ this.sort(new ResTimeItemComparator());
176+ }
177+}
--- a/TinyBannavi/src/tainavi/ReserveListColumnInfoList.java
+++ b/TinyBannavi/src/tainavi/ReserveListColumnInfoList.java
@@ -1,130 +1,130 @@
1-package tainavi;
2-
3-import java.io.File;
4-import java.util.ArrayList;
5-
6-/**
7- * 「本体予約一覧」画面の{@link ListColumnInfo} のリストを実現するクラスです.
8- * @since 3.22.18β+1.9
9- */
10-public class ReserveListColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"rlcinfolist.xml";
15-
16- @Override
17- public Object clone(){
18- ReserveListColumnInfoList p = new ReserveListColumnInfoList();
19-
20- for (ListColumnInfo li : this){
21- p.add((ListColumnInfo)li.clone());
22- }
23-
24- return p;
25- }
26-
27- public ListColumnInfo getVisibleAt(int column){
28- int cno = 0;
29- for (ListColumnInfo li : this){
30- if ( !li.getVisible() )
31- continue;
32-
33- if (cno == column)
34- return li;
35-
36- cno++;
37- }
38-
39- return null;
40- }
41-
42- /*
43- * JTableに指定する列名の配列を作成する
44- */
45- public String [] getColNames(){
46- // カラム名の初期化
47- ArrayList<String> cola = new ArrayList<String>();
48-
49- for ( ListColumnInfo li : this ) {
50- if ( li.getVisible() ) {
51- cola.add(li.getName());
52- }
53- }
54-
55- return cola.toArray(new String[0]);
56- }
57-
58- public boolean save() {
59- System.out.println("本体予約一覧の表示項目設定を保存します: "+listFile);
60-
61- if ( ! CommonUtils.writeXML(listFile, this) ) {
62- System.err.println("本体予約一覧の表示項目設定の保存に失敗しました: "+listFile);
63- return false;
64- }
65-
66- return true;
67- }
68-
69- public boolean load() {
70- System.out.println("本体予約一覧の表示項目設定を読み込みます: "+listFile);
71-
72- ArrayList<ListColumnInfo> cl = null;
73-
74- if ( new File(listFile).exists() ) {
75- // ファイルがあるならロード
76- cl = (ReserveListColumnInfoList) CommonUtils.readXML(listFile);
77- }
78-
79- if ( cl == null || cl.size() == 0 ) {
80- System.err.println("本体予約一覧の表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
81-
82- // 初期化してみよう
83- setDefault();
84-
85- return false;
86- }
87-
88- this.clear();
89- for (ListColumnInfo c : cl) {
90- this.add(c);
91- }
92-
93- return true;
94- }
95-
96- /*
97- * 初期化する
98- */
99- public void setDefault(){
100- this.clear();
101- int idx = 1;
102- Object[][] o = {
103- {true, "パタン", idx++},
104- {true, "重複", idx++},
105- {true, "実行", idx++},
106- {true, "追跡", idx++},
107- {true, "自動", idx++},
108- {true, "オプション", idx++},
109- {true, "次回実行予定", idx++},
110- {true, "終了", idx++},
111- {true, "長さ", idx++},
112- {true, "エンコーダ", idx++},
113- {true, "画質", idx++},
114- {true, "音質", idx++},
115- {true, "番組タイトル", idx++},
116- {true, "チャンネル名", idx++},
117- {true, "デバイス", idx++},
118- {true, "フォルダ", idx++},
119- {true, "レコーダ", idx++},
120- };
121- for (int i=0; i<o.length; i++) {
122- ListColumnInfo cb = new ListColumnInfo();
123- cb.setVisible((Boolean) o[i][0]);
124- cb.setName((String) o[i][1]);
125- cb.setId((Integer) o[i][2]);
126- this.add(cb);
127- }
128- }
129-
130-}
1+package tainavi;
2+
3+import java.io.File;
4+import java.util.ArrayList;
5+
6+/**
7+ * 「本体予約一覧」画面の{@link ListColumnInfo} のリストを実現するクラスです.
8+ * @since 3.22.18β+1.9
9+ */
10+public class ReserveListColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"rlcinfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ ReserveListColumnInfoList p = new ReserveListColumnInfoList();
19+
20+ for (ListColumnInfo li : this){
21+ p.add((ListColumnInfo)li.clone());
22+ }
23+
24+ return p;
25+ }
26+
27+ public ListColumnInfo getVisibleAt(int column){
28+ int cno = 0;
29+ for (ListColumnInfo li : this){
30+ if ( !li.getVisible() )
31+ continue;
32+
33+ if (cno == column)
34+ return li;
35+
36+ cno++;
37+ }
38+
39+ return null;
40+ }
41+
42+ /*
43+ * JTableに指定する列名の配列を作成する
44+ */
45+ public String [] getColNames(){
46+ // カラム名の初期化
47+ ArrayList<String> cola = new ArrayList<String>();
48+
49+ for ( ListColumnInfo li : this ) {
50+ if ( li.getVisible() ) {
51+ cola.add(li.getName());
52+ }
53+ }
54+
55+ return cola.toArray(new String[0]);
56+ }
57+
58+ public boolean save() {
59+ System.out.println("本体予約一覧の表示項目設定を保存します: "+listFile);
60+
61+ if ( ! CommonUtils.writeXML(listFile, this) ) {
62+ System.err.println("本体予約一覧の表示項目設定の保存に失敗しました: "+listFile);
63+ return false;
64+ }
65+
66+ return true;
67+ }
68+
69+ public boolean load() {
70+ System.out.println("本体予約一覧の表示項目設定を読み込みます: "+listFile);
71+
72+ ArrayList<ListColumnInfo> cl = null;
73+
74+ if ( new File(listFile).exists() ) {
75+ // ファイルがあるならロード
76+ cl = (ReserveListColumnInfoList) CommonUtils.readXML(listFile);
77+ }
78+
79+ if ( cl == null || cl.size() == 0 ) {
80+ System.err.println("本体予約一覧の表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
81+
82+ // 初期化してみよう
83+ setDefault();
84+
85+ return false;
86+ }
87+
88+ this.clear();
89+ for (ListColumnInfo c : cl) {
90+ this.add(c);
91+ }
92+
93+ return true;
94+ }
95+
96+ /*
97+ * 初期化する
98+ */
99+ public void setDefault(){
100+ this.clear();
101+ int idx = 1;
102+ Object[][] o = {
103+ {true, "パタン", idx++},
104+ {true, "重複", idx++},
105+ {true, "実行", idx++},
106+ {true, "追跡", idx++},
107+ {true, "自動", idx++},
108+ {true, "オプション", idx++},
109+ {true, "次回実行予定", idx++},
110+ {true, "終了", idx++},
111+ {true, "長さ", idx++},
112+ {true, "エンコーダ", idx++},
113+ {true, "画質", idx++},
114+ {true, "音質", idx++},
115+ {true, "番組タイトル", idx++},
116+ {true, "チャンネル名", idx++},
117+ {true, "デバイス", idx++},
118+ {true, "フォルダ", idx++},
119+ {true, "レコーダ", idx++},
120+ };
121+ for (int i=0; i<o.length; i++) {
122+ ListColumnInfo cb = new ListColumnInfo();
123+ cb.setVisible((Boolean) o[i][0]);
124+ cb.setName((String) o[i][1]);
125+ cb.setId((Integer) o[i][2]);
126+ this.add(cb);
127+ }
128+ }
129+
130+}
--- a/TinyBannavi/src/tainavi/SearchWordItem.java
+++ b/TinyBannavi/src/tainavi/SearchWordItem.java
@@ -1,47 +1,47 @@
1-package tainavi;
2-
3-/**
4- * <P>ツールバーの検索キーワードを保持するクラスです。
5- * @since 3.22.18β+1.10
6- * @see SearchWordList
7- */
8-public class SearchWordItem implements Cloneable {
9- @Override
10- public Object clone() {
11- try {
12- return super.clone();
13- } catch (CloneNotSupportedException e) {
14- throw new InternalError(e.toString());
15- }
16- }
17-
18- private String keyword;
19- private int count;
20- private String last_used_time;
21-
22- public SearchWordItem(){
23- this.keyword = null;
24- this.count = 0;
25- this.last_used_time = null;
26- }
27-
28- public SearchWordItem(String s) {
29- this.keyword = s;
30- this.count = 1;
31- this.last_used_time = CommonUtils.getDateTimeYMD(0);
32- }
33-
34- public void notifyUse(){
35- this.count++;
36- this.last_used_time = CommonUtils.getDateTimeYMD(0);
37- }
38-
39- public void setKeyword(String s){ this.keyword = s; }
40- public String getKeyword() { return this.keyword; }
41-
42- public void setCount(int n){ this.count = n; }
43- public int getCount(){ return this.count; }
44-
45- public void setLastUsedTime(String s){ this.last_used_time = s; }
46- public String getLastUsedTime(){ return this.last_used_time; }
47-}
1+package tainavi;
2+
3+/**
4+ * <P>ツールバーの検索キーワードを保持するクラスです。
5+ * @since 3.22.18β+1.10
6+ * @see SearchWordList
7+ */
8+public class SearchWordItem implements Cloneable {
9+ @Override
10+ public Object clone() {
11+ try {
12+ return super.clone();
13+ } catch (CloneNotSupportedException e) {
14+ throw new InternalError(e.toString());
15+ }
16+ }
17+
18+ private String keyword;
19+ private int count;
20+ private String last_used_time;
21+
22+ public SearchWordItem(){
23+ this.keyword = null;
24+ this.count = 0;
25+ this.last_used_time = null;
26+ }
27+
28+ public SearchWordItem(String s) {
29+ this.keyword = s;
30+ this.count = 1;
31+ this.last_used_time = CommonUtils.getDateTimeYMD(0);
32+ }
33+
34+ public void notifyUse(){
35+ this.count++;
36+ this.last_used_time = CommonUtils.getDateTimeYMD(0);
37+ }
38+
39+ public void setKeyword(String s){ this.keyword = s; }
40+ public String getKeyword() { return this.keyword; }
41+
42+ public void setCount(int n){ this.count = n; }
43+ public int getCount(){ return this.count; }
44+
45+ public void setLastUsedTime(String s){ this.last_used_time = s; }
46+ public String getLastUsedTime(){ return this.last_used_time; }
47+}
--- a/TinyBannavi/src/tainavi/SearchWordList.java
+++ b/TinyBannavi/src/tainavi/SearchWordList.java
@@ -1,104 +1,104 @@
1-package tainavi;
2-
3-import java.io.File;
4-import java.util.ArrayList;
5-import java.util.Comparator;
6-
7-/*
8- * <P>ツールバーの検索キーワードのリストを保持するクラスです。
9- * @since 3.22.18β+1.10
10- *
11- * @see ListedColumnInfoList
12- */
13-
14-public class SearchWordList {
15- /*
16- * 定数
17- */
18- private final String filename = "env"+File.separator+"searchwords.xml";
19-
20- private final String MSGID = "[検索ワード] ";
21- private final String ERRID = "[ERROR]"+MSGID;
22- private final int maxwords = 128;
23-
24- /*
25- * 部品
26- */
27- private ArrayList<SearchWordItem> list = new ArrayList<SearchWordItem>();
28-
29- public int size() { return list.size(); }
30-
31- public boolean add(String s){
32- for (SearchWordItem item : list){
33- if (item.getKeyword().equals(s)){
34- item.notifyUse();
35- sortList();
36- return true;
37- }
38- }
39-
40- SearchWordItem item = new SearchWordItem(s);
41- list.add(item);
42- sortList();
43