• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

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


Commit MetaInfo

Revision7180630f2ab4a15dff1534100897db19a391b5f6 (tree)
Zeit2020-08-08 10:49:51
AutorMasahiko Kimura <mkimura@u01....>
CommiterMasahiko Kimura

Log Message

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で異常終了する問題の対応

Ändern Zusammenfassung

Diff

--- 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-
44- while (list.size() > maxwords){
45- list.remove(list.size()-1);
46- }
47-
48- return true;
49- }
50-
51- /**
52- * 検索ワードの一覧を返す
53- */
54- public ArrayList<SearchWordItem> getWordList() {
55- return list;
56- }
57-
58- public void clear() {
59- list.clear();
60- }
61-
62- public boolean load() {
63- if ( ! new File(filename).exists() ) {
64- System.err.println(ERRID+"設定が読み込めませんでした、検索ワードは無効です: "+filename);
65- return false;
66- }
67-
68- @SuppressWarnings("unchecked")
69- ArrayList<SearchWordItem> tlist = (ArrayList<SearchWordItem>) CommonUtils.readXML(filename);
70- if ( tlist == null ) {
71- System.err.println(ERRID+"設定の読み込みに失敗しました、検索ワードは無効です: "+filename);
72- return false;
73- }
74-
75- System.out.println(MSGID+"設定を読み込みました: "+filename);
76-
77- list = tlist;
78- return true;
79- }
80-
81- public boolean save() {
82- if ( ! CommonUtils.writeXML(filename, list) ) {
83- System.err.println(ERRID+"設定の保存に失敗しました: "+filename);
84- return false;
85- }
86-
87- return true;
88- }
89-
90- private class SearchWordComparator implements Comparator<SearchWordItem>{
91- @Override
92- public int compare(SearchWordItem p1, SearchWordItem p2) {
93- int rc = p2.getCount() - p1.getCount();
94- if (rc != 0)
95- return (rc > 0) ? 1 : -1;
96-
97- return p2.getLastUsedTime().compareTo(p1.getLastUsedTime());
98- }
99- }
100-
101- protected void sortList(){
102- list.sort(new SearchWordComparator());
103- }
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+
44+ while (list.size() > maxwords){
45+ list.remove(list.size()-1);
46+ }
47+
48+ return true;
49+ }
50+
51+ /**
52+ * 検索ワードの一覧を返す
53+ */
54+ public ArrayList<SearchWordItem> getWordList() {
55+ return list;
56+ }
57+
58+ public void clear() {
59+ list.clear();
60+ }
61+
62+ public boolean load() {
63+ if ( ! new File(filename).exists() ) {
64+ System.err.println(ERRID+"設定が読み込めませんでした、検索ワードは無効です: "+filename);
65+ return false;
66+ }
67+
68+ @SuppressWarnings("unchecked")
69+ ArrayList<SearchWordItem> tlist = (ArrayList<SearchWordItem>) CommonUtils.readXML(filename);
70+ if ( tlist == null ) {
71+ System.err.println(ERRID+"設定の読み込みに失敗しました、検索ワードは無効です: "+filename);
72+ return false;
73+ }
74+
75+ System.out.println(MSGID+"設定を読み込みました: "+filename);
76+
77+ list = tlist;
78+ return true;
79+ }
80+
81+ public boolean save() {
82+ if ( ! CommonUtils.writeXML(filename, list) ) {
83+ System.err.println(ERRID+"設定の保存に失敗しました: "+filename);
84+ return false;
85+ }
86+
87+ return true;
88+ }
89+
90+ private class SearchWordComparator implements Comparator<SearchWordItem>{
91+ @Override
92+ public int compare(SearchWordItem p1, SearchWordItem p2) {
93+ int rc = p2.getCount() - p1.getCount();
94+ if (rc != 0)
95+ return (rc > 0) ? 1 : -1;
96+
97+ return p2.getLastUsedTime().compareTo(p1.getLastUsedTime());
98+ }
99+ }
100+
101+ protected void sortList(){
102+ list.sort(new SearchWordComparator());
103+ }
104+}
--- a/TinyBannavi/src/tainavi/TabInfoList.java
+++ b/TinyBannavi/src/tainavi/TabInfoList.java
@@ -1,139 +1,139 @@
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 TabInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"tabinfolist.xml";
15-
16- @Override
17- public Object clone(){
18- TabInfoList p = new TabInfoList();
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- public boolean getVisibleAt(String name){
43- if (name == null)
44- return false;
45-
46- for (ListColumnInfo li : this){
47- if (name.equals(li.getName()))
48- return li.getVisible();
49-
50- }
51-
52- return false;
53- }
54-
55- /*
56- * JTableに指定する列名の配列を作成する
57- */
58- public String [] getColNames(){
59- // カラム名の初期化
60- ArrayList<String> cola = new ArrayList<String>();
61-
62- for ( ListColumnInfo li : this ) {
63- if ( li.getVisible() ) {
64- cola.add(li.getName());
65- }
66- }
67-
68- return cola.toArray(new String[0]);
69- }
70-
71- /*
72- * ファイルに保存する
73- */
74- public boolean save() {
75- System.out.println("タブ表示項目設定を保存します: "+listFile);
76-
77- if ( ! CommonUtils.writeXML(listFile, this) ) {
78- System.err.println("タブ表示項目設定の保存に失敗しました: "+listFile);
79- return false;
80- }
81-
82- return true;
83- }
84-
85- /*
86- * ファイルから読み込む
87- */
88- public boolean load() {
89- System.out.println("タブ表示項目設定を読み込みます: "+listFile);
90-
91- ArrayList<ListColumnInfo> cl = null;
92-
93- if ( new File(listFile).exists() ) {
94- // ファイルがあるならロード
95- cl = (TabInfoList) CommonUtils.readXML(listFile);
96- }
97-
98- if ( cl == null || cl.size() == 0 ) {
99- System.err.println("タブ表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
100-
101- // 初期化してみよう
102- setDefault();
103-
104- return false;
105- }
106-
107- this.clear();
108- for (ListColumnInfo c : cl) {
109- this.add(c);
110- }
111-
112- return true;
113- }
114-
115- /*
116- * デフォルトの設定にする
117- */
118- public void setDefault(){
119- this.clear();
120-
121- int idx = 1;
122- Object[][] o = {
123- {true, "リスト形式", idx++},
124- {true, "新聞形式", idx++},
125- {true, "本体予約一覧", idx++},
126- {true, "録画結果一覧", idx++},
127- {true, "自動予約一覧", idx++},
128- {true, "タイトル一覧", idx++},
129- {true, "設定一覧", idx++},
130- };
131- for (int i=0; i<o.length; i++) {
132- ListColumnInfo cb = new ListColumnInfo();
133- cb.setVisible((Boolean) o[i][0]);
134- cb.setName((String) o[i][1]);
135- cb.setId((Integer) o[i][2]);
136- this.add(cb);
137- }
138- }
139-}
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 TabInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"tabinfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ TabInfoList p = new TabInfoList();
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+ public boolean getVisibleAt(String name){
43+ if (name == null)
44+ return false;
45+
46+ for (ListColumnInfo li : this){
47+ if (name.equals(li.getName()))
48+ return li.getVisible();
49+
50+ }
51+
52+ return false;
53+ }
54+
55+ /*
56+ * JTableに指定する列名の配列を作成する
57+ */
58+ public String [] getColNames(){
59+ // カラム名の初期化
60+ ArrayList<String> cola = new ArrayList<String>();
61+
62+ for ( ListColumnInfo li : this ) {
63+ if ( li.getVisible() ) {
64+ cola.add(li.getName());
65+ }
66+ }
67+
68+ return cola.toArray(new String[0]);
69+ }
70+
71+ /*
72+ * ファイルに保存する
73+ */
74+ public boolean save() {
75+ System.out.println("タブ表示項目設定を保存します: "+listFile);
76+
77+ if ( ! CommonUtils.writeXML(listFile, this) ) {
78+ System.err.println("タブ表示項目設定の保存に失敗しました: "+listFile);
79+ return false;
80+ }
81+
82+ return true;
83+ }
84+
85+ /*
86+ * ファイルから読み込む
87+ */
88+ public boolean load() {
89+ System.out.println("タブ表示項目設定を読み込みます: "+listFile);
90+
91+ ArrayList<ListColumnInfo> cl = null;
92+
93+ if ( new File(listFile).exists() ) {
94+ // ファイルがあるならロード
95+ cl = (TabInfoList) CommonUtils.readXML(listFile);
96+ }
97+
98+ if ( cl == null || cl.size() == 0 ) {
99+ System.err.println("タブ表示項目設定が読み込めなかったのでデフォルト設定で起動します.");
100+
101+ // 初期化してみよう
102+ setDefault();
103+
104+ return false;
105+ }
106+
107+ this.clear();
108+ for (ListColumnInfo c : cl) {
109+ this.add(c);
110+ }
111+
112+ return true;
113+ }
114+
115+ /*
116+ * デフォルトの設定にする
117+ */
118+ public void setDefault(){
119+ this.clear();
120+
121+ int idx = 1;
122+ Object[][] o = {
123+ {true, "リスト形式", idx++},
124+ {true, "新聞形式", idx++},
125+ {true, "本体予約一覧", idx++},
126+ {true, "録画結果一覧", idx++},
127+ {true, "自動予約一覧", idx++},
128+ {true, "タイトル一覧", idx++},
129+ {true, "設定一覧", idx++},
130+ };
131+ for (int i=0; i<o.length; i++) {
132+ ListColumnInfo cb = new ListColumnInfo();
133+ cb.setVisible((Boolean) o[i][0]);
134+ cb.setName((String) o[i][1]);
135+ cb.setId((Integer) o[i][2]);
136+ this.add(cb);
137+ }
138+ }
139+}
--- a/TinyBannavi/src/tainavi/TitleEditorSelectable.java
+++ b/TinyBannavi/src/tainavi/TitleEditorSelectable.java
@@ -1,9 +1,9 @@
1-package tainavi;
2-
3-public interface TitleEditorSelectable {
4-
5- /**
6- * 開始、終了時刻が変わった
7- */
8- public boolean notifyTimeChange();
1+package tainavi;
2+
3+public interface TitleEditorSelectable {
4+
5+ /**
6+ * 開始、終了時刻が変わった
7+ */
8+ public boolean notifyTimeChange();
99 }
\ No newline at end of file
--- a/TinyBannavi/src/tainavi/TitleInfo.java
+++ b/TinyBannavi/src/tainavi/TitleInfo.java
@@ -1,213 +1,213 @@
1-package tainavi;
2-
3-import java.util.ArrayList;
4-import java.util.HashMap;
5-import java.util.regex.Matcher;
6-import java.util.regex.Pattern;
7-
8-/**
9- * <P>個々のタイトルの情報を保持します。
10- */
11-public class TitleInfo implements Cloneable {
12- public static final String MOVEONLY = "移動のみ";
13- public static final String RECORDING = "録画中";
14-
15- private int serial=0;
16- private String id=null;
17- private String rec_mode="";
18- private String rec_date="";
19- private String ahh="";
20- private String amm="";
21- private String zhh="";
22- private String zmm="";
23- private String rec_min="";
24- private String title="";
25- private String channel="";
26- private String ch_name="";
27-
28- private String content_id="";
29-
30- private String rec_device=null;
31- private ArrayList<TextValueSet> rec_folder=null;
32- private ArrayList<TextValueSet> rec_genre=null;
33- private String rec_subgenre="";
34-
35- private String startDateTime="";
36- private String endDateTime="";
37-
38- private boolean recording=false;
39-
40- private ArrayList<ChapterInfo> chapter=null;
41-
42- private boolean detail_loaded;
43-
44- private HashMap<String, String> hidden_params = new HashMap<String, String>();
45-
46- //
47- @Override
48- public TitleInfo clone() {
49- try {
50- TitleInfo p = (TitleInfo) super.clone();
51- p.rec_device = (this.rec_device!=null)?(this.rec_device):("");
52- return p;
53- } catch (CloneNotSupportedException e) {
54- throw new InternalError(e.toString());
55- }
56- }
57-
58- /*
59- * 外部インターフェース
60- */
61- public int getSerial(){ return serial; }
62- public void setSerial(int n){ serial = n; }
63- public String getId() {return id;}
64- public void setId(String s) { id=s;}
65- public String getRec_date() {return rec_date;}
66- public void setRec_date(String s) { rec_date=s;}
67- public String getAhh() {return ahh;}
68- public void setAhh(String s) { ahh=s;}
69- public String getAmm() {return amm;}
70- public void setAmm(String s) { amm=s;}
71- public String getZhh() {return zhh;}
72- public void setZhh(String s) { zhh=s;}
73- public String getZmm() {return zmm;}
74- public void setZmm(String s) { zmm=s;}
75- public String getRec_min() {return rec_min;}
76- public void setRec_min(String s) { rec_min=s;}
77-
78- public String getRec_mode() {return rec_mode;}
79- public void setRec_mode(String s) { rec_mode=s;}
80- public String getTitle() {return title;}
81- public void setTitle(String s) { title=s;}
82- public String getChannel() {return channel;}
83- public void setChannel(String s) { channel=s;}
84- public String getCh_name() {return ch_name;}
85- public void setCh_name(String s) { ch_name=s;}
86-
87- public void setDetail(String s) {}
88-
89- public String getContentId() {return content_id;}
90- public void setContentId(String s) { content_id=s;}
91-
92- public String getRec_device() {return rec_device;}
93- public void setRec_device(String s) { rec_device=s;}
94- public ArrayList<TextValueSet> getRec_folder(){ return rec_folder; }
95- public void setRec_folder(ArrayList<TextValueSet> tvs){ rec_folder=tvs; }
96- public boolean containsFolder(String id){
97- for ( TextValueSet tvs : rec_folder ){
98- if (id.equals(tvs.getValue()))
99- return(true);
100- }
101-
102- return(false);
103- }
104-
105- public String getFolderNameList(){
106- String list = "";
107-
108- for ( TextValueSet tvs : rec_folder ){
109- if (list.length() > 0){
110- list += ",";
111-
112- Matcher ma = Pattern.compile("\\[[^\\[]+\\](.*)$").matcher(tvs.getText());
113- if ( ma.find() ) {
114- list += ma.group(1);
115- }
116- else{
117- list += tvs.getText();
118- }
119- }
120- else{
121- list = tvs.getText();
122- }
123- }
124-
125- return list;
126- }
127-
128- public ArrayList<TextValueSet> getRec_genre(){ return rec_genre; }
129- public void setRec_genre(ArrayList<TextValueSet> tvs){ rec_genre=tvs; }
130- public boolean containsGenre(String id){
131- for ( TextValueSet tvs : rec_genre ){
132- if (id.equals(tvs.getValue()))
133- return(true);
134- }
135-
136- return(false);
137- }
138-
139- public String getGenreNameList(){
140- String list = "";
141-
142- for ( TextValueSet tvs : rec_genre ){
143- if (list.length() > 0)
144- list += ",";
145- list += tvs.getValue() + ":" + tvs.getText();
146- }
147-
148- return list;
149- }
150-
151- public String getStartDateTime() {return startDateTime;}
152- public void setStartDateTime(String s) { startDateTime=s;}
153- public String getEndDateTime() {return endDateTime;}
154- public void setEndDateTime(String s) { endDateTime=s;}
155-
156- public ArrayList<ChapterInfo> getChapter(){ return chapter; }
157- public void setChapter(ArrayList<ChapterInfo> ci){ chapter = ci; }
158-
159- public boolean getDetailLoaded(){ return detail_loaded; }
160- public void setDetailLoaded(boolean b){ detail_loaded = b; }
161-
162- public boolean getRecording(){ return recording; }
163- public void setRecording(boolean b){ recording = b; }
164-
165- public HashMap<String,String> getHidden_params() { return hidden_params; }
166- public void setHidden_params(HashMap<String,String> a) { hidden_params = a; }
167-
168- // コピー回数を整形する
169- public String formatCopyCount(){
170- String copycount = getHidden_params().get("copycount");
171- if (copycount == null)
172- return "";
173- else if (copycount.equals("1"))
174- return MOVEONLY;
175- else if (copycount.equals("238"))
176- return RECORDING;
177- else{
178- try{
179- int count = Integer.parseInt(copycount);
180- return String.format("%d回", count-1);
181- }
182- catch(NumberFormatException e){
183- }
184-
185- return copycount + "回";
186- }
187- }
188-
189- // 詳細情報を整形する
190- public String formatDetail(){
191- String detail =
192- "番組タイトル:" + getTitle() + "\n" +
193- "録画モード:" + getRec_mode() + "\n" +
194- "フォルダ:" + getFolderNameList() + "\n";
195-
196- String g = "";
197- for (TextValueSet ts : getRec_genre()){
198- if (!g.equals(""))
199- g += ",";
200- g += ts.getValue() + ":" + ts.getText();
201- }
202-
203- detail += "ジャンル:" + g + "\n";
204- detail += "コピー回数:" + formatCopyCount() + "\n";
205-// detail += "CONTENT ID:" + getContentId() + "\n";
206-
207- String dlnaObjectID = getHidden_params().get("dlnaObjectID");
208- if (dlnaObjectID != null)
209- detail += "DLNA OID:" + dlnaObjectID + "\n";
210-
211- return detail;
212- }
213-}
1+package tainavi;
2+
3+import java.util.ArrayList;
4+import java.util.HashMap;
5+import java.util.regex.Matcher;
6+import java.util.regex.Pattern;
7+
8+/**
9+ * <P>個々のタイトルの情報を保持します。
10+ */
11+public class TitleInfo implements Cloneable {
12+ public static final String MOVEONLY = "移動のみ";
13+ public static final String RECORDING = "録画中";
14+
15+ private int serial=0;
16+ private String id=null;
17+ private String rec_mode="";
18+ private String rec_date="";
19+ private String ahh="";
20+ private String amm="";
21+ private String zhh="";
22+ private String zmm="";
23+ private String rec_min="";
24+ private String title="";
25+ private String channel="";
26+ private String ch_name="";
27+
28+ private String content_id="";
29+
30+ private String rec_device=null;
31+ private ArrayList<TextValueSet> rec_folder=null;
32+ private ArrayList<TextValueSet> rec_genre=null;
33+ private String rec_subgenre="";
34+
35+ private String startDateTime="";
36+ private String endDateTime="";
37+
38+ private boolean recording=false;
39+
40+ private ArrayList<ChapterInfo> chapter=null;
41+
42+ private boolean detail_loaded;
43+
44+ private HashMap<String, String> hidden_params = new HashMap<String, String>();
45+
46+ //
47+ @Override
48+ public TitleInfo clone() {
49+ try {
50+ TitleInfo p = (TitleInfo) super.clone();
51+ p.rec_device = (this.rec_device!=null)?(this.rec_device):("");
52+ return p;
53+ } catch (CloneNotSupportedException e) {
54+ throw new InternalError(e.toString());
55+ }
56+ }
57+
58+ /*
59+ * 外部インターフェース
60+ */
61+ public int getSerial(){ return serial; }
62+ public void setSerial(int n){ serial = n; }
63+ public String getId() {return id;}
64+ public void setId(String s) { id=s;}
65+ public String getRec_date() {return rec_date;}
66+ public void setRec_date(String s) { rec_date=s;}
67+ public String getAhh() {return ahh;}
68+ public void setAhh(String s) { ahh=s;}
69+ public String getAmm() {return amm;}
70+ public void setAmm(String s) { amm=s;}
71+ public String getZhh() {return zhh;}
72+ public void setZhh(String s) { zhh=s;}
73+ public String getZmm() {return zmm;}
74+ public void setZmm(String s) { zmm=s;}
75+ public String getRec_min() {return rec_min;}
76+ public void setRec_min(String s) { rec_min=s;}
77+
78+ public String getRec_mode() {return rec_mode;}
79+ public void setRec_mode(String s) { rec_mode=s;}
80+ public String getTitle() {return title;}
81+ public void setTitle(String s) { title=s;}
82+ public String getChannel() {return channel;}
83+ public void setChannel(String s) { channel=s;}
84+ public String getCh_name() {return ch_name;}
85+ public void setCh_name(String s) { ch_name=s;}
86+
87+ public void setDetail(String s) {}
88+
89+ public String getContentId() {return content_id;}
90+ public void setContentId(String s) { content_id=s;}
91+
92+ public String getRec_device() {return rec_device;}
93+ public void setRec_device(String s) { rec_device=s;}
94+ public ArrayList<TextValueSet> getRec_folder(){ return rec_folder; }
95+ public void setRec_folder(ArrayList<TextValueSet> tvs){ rec_folder=tvs; }
96+ public boolean containsFolder(String id){
97+ for ( TextValueSet tvs : rec_folder ){
98+ if (id.equals(tvs.getValue()))
99+ return(true);
100+ }
101+
102+ return(false);
103+ }
104+
105+ public String getFolderNameList(){
106+ String list = "";
107+
108+ for ( TextValueSet tvs : rec_folder ){
109+ if (list.length() > 0){
110+ list += ",";
111+
112+ Matcher ma = Pattern.compile("\\[[^\\[]+\\](.*)$").matcher(tvs.getText());
113+ if ( ma.find() ) {
114+ list += ma.group(1);
115+ }
116+ else{
117+ list += tvs.getText();
118+ }
119+ }
120+ else{
121+ list = tvs.getText();
122+ }
123+ }
124+
125+ return list;
126+ }
127+
128+ public ArrayList<TextValueSet> getRec_genre(){ return rec_genre; }
129+ public void setRec_genre(ArrayList<TextValueSet> tvs){ rec_genre=tvs; }
130+ public boolean containsGenre(String id){
131+ for ( TextValueSet tvs : rec_genre ){
132+ if (id.equals(tvs.getValue()))
133+ return(true);
134+ }
135+
136+ return(false);
137+ }
138+
139+ public String getGenreNameList(){
140+ String list = "";
141+
142+ for ( TextValueSet tvs : rec_genre ){
143+ if (list.length() > 0)
144+ list += ",";
145+ list += tvs.getValue() + ":" + tvs.getText();
146+ }
147+
148+ return list;
149+ }
150+
151+ public String getStartDateTime() {return startDateTime;}
152+ public void setStartDateTime(String s) { startDateTime=s;}
153+ public String getEndDateTime() {return endDateTime;}
154+ public void setEndDateTime(String s) { endDateTime=s;}
155+
156+ public ArrayList<ChapterInfo> getChapter(){ return chapter; }
157+ public void setChapter(ArrayList<ChapterInfo> ci){ chapter = ci; }
158+
159+ public boolean getDetailLoaded(){ return detail_loaded; }
160+ public void setDetailLoaded(boolean b){ detail_loaded = b; }
161+
162+ public boolean getRecording(){ return recording; }
163+ public void setRecording(boolean b){ recording = b; }
164+
165+ public HashMap<String,String> getHidden_params() { return hidden_params; }
166+ public void setHidden_params(HashMap<String,String> a) { hidden_params = a; }
167+
168+ // コピー回数を整形する
169+ public String formatCopyCount(){
170+ String copycount = getHidden_params().get("copycount");
171+ if (copycount == null)
172+ return "";
173+ else if (copycount.equals("1"))
174+ return MOVEONLY;
175+ else if (copycount.equals("238"))
176+ return RECORDING;
177+ else{
178+ try{
179+ int count = Integer.parseInt(copycount);
180+ return String.format("%d回", count-1);
181+ }
182+ catch(NumberFormatException e){
183+ }
184+
185+ return copycount + "回";
186+ }
187+ }
188+
189+ // 詳細情報を整形する
190+ public String formatDetail(){
191+ String detail =
192+ "番組タイトル:" + getTitle() + "\n" +
193+ "録画モード:" + getRec_mode() + "\n" +
194+ "フォルダ:" + getFolderNameList() + "\n";
195+
196+ String g = "";
197+ for (TextValueSet ts : getRec_genre()){
198+ if (!g.equals(""))
199+ g += ",";
200+ g += ts.getValue() + ":" + ts.getText();
201+ }
202+
203+ detail += "ジャンル:" + g + "\n";
204+ detail += "コピー回数:" + formatCopyCount() + "\n";
205+// detail += "CONTENT ID:" + getContentId() + "\n";
206+
207+ String dlnaObjectID = getHidden_params().get("dlnaObjectID");
208+ if (dlnaObjectID != null)
209+ detail += "DLNA OID:" + dlnaObjectID + "\n";
210+
211+ return detail;
212+ }
213+}
--- a/TinyBannavi/src/tainavi/TitleListColumnInfoList.java
+++ b/TinyBannavi/src/tainavi/TitleListColumnInfoList.java
@@ -1,156 +1,156 @@
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 TitleListColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"tlcinfolist.xml";
15-
16- @Override
17- public Object clone(){
18- TitleListColumnInfoList p = new TitleListColumnInfoList();
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- /*
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 = (TitleListColumnInfoList) 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- addNewColumns();
100-
101- return true;
102- }
103-
104- /*
105- * 新しい列を追加する
106- */
107- private void addNewColumns(){
108- TitleListColumnInfoList cl = new TitleListColumnInfoList();
109- cl.setDefault();
110-
111- for (int n=0; n<cl.size(); n++){
112- ListColumnInfo c = cl.get(n);
113-
114- int n2=0;
115- for (n2=0; n2<this.size(); n2++){
116- ListColumnInfo c2 = this.get(n2);
117- if (c.getName().equals(c2.getName()))
118- break;
119- }
120-
121- if (n2 == this.size()){
122- this.add((ListColumnInfo)c.clone());
123- }
124- }
125- }
126-
127- /*
128- * デフォルトの設定にする
129- */
130- public void setDefault(){
131- this.clear();
132-
133- int idx = 1;
134- Object[][] o = {
135- {true, "開始", idx++},
136- {true, "終了", idx++},
137- {true, "長さ", idx++},
138- {true, "画質", idx++},
139- {true, "番組タイトル", idx++},
140- {true, "チャンネル名", idx++},
141- {true, "デバイス", idx++},
142- {true, "フォルダ", idx++},
143- {true, "ジャンル", idx++},
144- {true, "レコーダ", idx++},
145- {true, "コピー", idx++},
146- {false, "DLNA OID", idx++},
147- };
148- for (int i=0; i<o.length; i++) {
149- ListColumnInfo cb = new ListColumnInfo();
150- cb.setVisible((Boolean) o[i][0]);
151- cb.setName((String) o[i][1]);
152- cb.setId((Integer) o[i][2]);
153- this.add(cb);
154- }
155- }
156-}
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 TitleListColumnInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"tlcinfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ TitleListColumnInfoList p = new TitleListColumnInfoList();
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+ /*
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 = (TitleListColumnInfoList) 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+ addNewColumns();
100+
101+ return true;
102+ }
103+
104+ /*
105+ * 新しい列を追加する
106+ */
107+ private void addNewColumns(){
108+ TitleListColumnInfoList cl = new TitleListColumnInfoList();
109+ cl.setDefault();
110+
111+ for (int n=0; n<cl.size(); n++){
112+ ListColumnInfo c = cl.get(n);
113+
114+ int n2=0;
115+ for (n2=0; n2<this.size(); n2++){
116+ ListColumnInfo c2 = this.get(n2);
117+ if (c.getName().equals(c2.getName()))
118+ break;
119+ }
120+
121+ if (n2 == this.size()){
122+ this.add((ListColumnInfo)c.clone());
123+ }
124+ }
125+ }
126+
127+ /*
128+ * デフォルトの設定にする
129+ */
130+ public void setDefault(){
131+ this.clear();
132+
133+ int idx = 1;
134+ Object[][] o = {
135+ {true, "開始", idx++},
136+ {true, "終了", idx++},
137+ {true, "長さ", idx++},
138+ {true, "画質", idx++},
139+ {true, "番組タイトル", idx++},
140+ {true, "チャンネル名", idx++},
141+ {true, "デバイス", idx++},
142+ {true, "フォルダ", idx++},
143+ {true, "ジャンル", idx++},
144+ {true, "レコーダ", idx++},
145+ {true, "コピー", idx++},
146+ {false, "DLNA OID", idx++},
147+ };
148+ for (int i=0; i<o.length; i++) {
149+ ListColumnInfo cb = new ListColumnInfo();
150+ cb.setVisible((Boolean) o[i][0]);
151+ cb.setName((String) o[i][1]);
152+ cb.setId((Integer) o[i][2]);
153+ this.add(cb);
154+ }
155+ }
156+}
--- a/TinyBannavi/src/tainavi/ToolBarIconInfo.java
+++ b/TinyBannavi/src/tainavi/ToolBarIconInfo.java
@@ -1,69 +1,69 @@
1-package tainavi;
2-
3-/*
4- * ツールバーに表示するアイコンの情報
5- */
6-public enum ToolBarIconInfo{
7- KEYWORD ("キーワード", true),
8- SEARCH ("キーワード検索", true),
9- ADDKEYWORD ("キーワード追加", true),
10- SEPARATOR1 ("セパレータ1", true),
11- RELOADPROGS ("番組情報再取得", true),
12- SHOWMATCHBORDER ("予約待機表示", true),
13- SHOWOFFRESERVE ("実行OFF予約表示", true),
14- MOVETONOW ("現在日時表示", true),
15- SEPARATOR2 ("セパレータ2", true),
16- PREVPAGE ("ページャー前ページ", true),
17- PAGER ("ページャー", true),
18- NEXTPAGE ("ページャー次ページ", true),
19- SEPARATOR3 ("セパレータ3", true),
20- BATCHRESERVATION ("一括登録", true),
21- RELOADRSVED ("レコーダー情報再取得", true),
22- SEPARATOR4 ("セパレータ4", true),
23- WAKEUP ("レコーダー電源入", true),
24- SHUTDOWN ("レコーダー電源切", true),
25- SELECT_RECORDER ("レコーダー選択", true),
26- SEPARATOR5 ("セパレータ5", true),
27- SNAPSHOT ("スナップショット", true),
28- PAGER_COLORS ("ジャンル別背景色設定", true),
29- PAGER_ZOOM ("番組表示枠拡大", true),
30- LOGVIEWER ("ログビューア表示", true),
31- TIMER ("タイマー", true),
32- SEPARATOR6 ("セパレータ6", true),
33- SHOWSTATUS ("ステータス表示", true),
34- FULL_SCREEN ("フルスクリーンモード", true),
35- SEPARATOR7 ("セパレータ7", true),
36- UPDATE ("オンラインアップデート", true),
37- HELP ("ヘルプ", true),
38- ;
39-
40- private String name;
41- private boolean visible;
42-
43- private ToolBarIconInfo(String name, boolean visible){
44- this.name = name;
45- this.visible = visible;
46- }
47-
48- public String getName(){ return name; }
49- public boolean getVisible(){ return visible; }
50-
51- public static ToolBarIconInfo getAt(int idx) {
52- if ( idx >= ToolBarIconInfo.values().length ) {
53- return null;
54- }
55- return (ToolBarIconInfo.values())[idx];
56- }
57-
58- public static ToolBarIconInfo getByName(String name){
59- if (name == null)
60- return null;
61-
62- for (ToolBarIconInfo info : values()){
63- if (name.equals(info.getName()))
64- return info;
65- }
66-
67- return null;
68- }
1+package tainavi;
2+
3+/*
4+ * ツールバーに表示するアイコンの情報
5+ */
6+public enum ToolBarIconInfo{
7+ KEYWORD ("キーワード", true),
8+ SEARCH ("キーワード検索", true),
9+ ADDKEYWORD ("キーワード追加", true),
10+ SEPARATOR1 ("セパレータ1", true),
11+ RELOADPROGS ("番組情報再取得", true),
12+ SHOWMATCHBORDER ("予約待機表示", true),
13+ SHOWOFFRESERVE ("実行OFF予約表示", true),
14+ MOVETONOW ("現在日時表示", true),
15+ SEPARATOR2 ("セパレータ2", true),
16+ PREVPAGE ("ページャー前ページ", true),
17+ PAGER ("ページャー", true),
18+ NEXTPAGE ("ページャー次ページ", true),
19+ SEPARATOR3 ("セパレータ3", true),
20+ BATCHRESERVATION ("一括登録", true),
21+ RELOADRSVED ("レコーダー情報再取得", true),
22+ SEPARATOR4 ("セパレータ4", true),
23+ WAKEUP ("レコーダー電源入", true),
24+ SHUTDOWN ("レコーダー電源切", true),
25+ SELECT_RECORDER ("レコーダー選択", true),
26+ SEPARATOR5 ("セパレータ5", true),
27+ SNAPSHOT ("スナップショット", true),
28+ PAGER_COLORS ("ジャンル別背景色設定", true),
29+ PAGER_ZOOM ("番組表示枠拡大", true),
30+ LOGVIEWER ("ログビューア表示", true),
31+ TIMER ("タイマー", true),
32+ SEPARATOR6 ("セパレータ6", true),
33+ SHOWSTATUS ("ステータス表示", true),
34+ FULL_SCREEN ("フルスクリーンモード", true),
35+ SEPARATOR7 ("セパレータ7", true),
36+ UPDATE ("オンラインアップデート", true),
37+ HELP ("ヘルプ", true),
38+ ;
39+
40+ private String name;
41+ private boolean visible;
42+
43+ private ToolBarIconInfo(String name, boolean visible){
44+ this.name = name;
45+ this.visible = visible;
46+ }
47+
48+ public String getName(){ return name; }
49+ public boolean getVisible(){ return visible; }
50+
51+ public static ToolBarIconInfo getAt(int idx) {
52+ if ( idx >= ToolBarIconInfo.values().length ) {
53+ return null;
54+ }
55+ return (ToolBarIconInfo.values())[idx];
56+ }
57+
58+ public static ToolBarIconInfo getByName(String name){
59+ if (name == null)
60+ return null;
61+
62+ for (ToolBarIconInfo info : values()){
63+ if (name.equals(info.getName()))
64+ return info;
65+ }
66+
67+ return null;
68+ }
6969 }
\ No newline at end of file
--- a/TinyBannavi/src/tainavi/ToolBarIconInfoList.java
+++ b/TinyBannavi/src/tainavi/ToolBarIconInfoList.java
@@ -1,164 +1,164 @@
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.15.6
9- */
10-public class ToolBarIconInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11-
12- private static final long serialVersionUID = 1L;
13-
14- private static final String listFile = "env"+File.separator+"tbiinfolist.xml";
15-
16- @Override
17- public Object clone(){
18- ToolBarIconInfoList p = new ToolBarIconInfoList();
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- /*
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 = (ToolBarIconInfoList) 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- addNewColumns();
100-
101- return true;
102- }
103-
104- /*
105- * 新しい列を追加する
106- */
107- private void addNewColumns(){
108- ToolBarIconInfoList cl = new ToolBarIconInfoList();
109- cl.setDefault();
110-
111- for (int n=0; n<cl.size(); n++){
112- ListColumnInfo c = cl.get(n);
113-
114- int n2=0;
115- for (n2=0; n2<this.size(); n2++){
116- ListColumnInfo c2 = this.get(n2);
117- if (c.getName().equals(c2.getName()))
118- break;
119- }
120-
121- if (n2 == this.size()){
122- this.add((ListColumnInfo)c.clone());
123- }
124- }
125- }
126-
127- /*
128- * デフォルトの設定にする
129- */
130- public void setDefault(){
131- this.clear();
132-
133- int idx = 1;
134- for (ToolBarIconInfo info : ToolBarIconInfo.values()) {
135- ListColumnInfo cb = new ListColumnInfo();
136- cb.setVisible(info.getVisible());
137- cb.setName(info.getName());
138- cb.setId(idx++);
139-
140- this.add(cb);
141- }
142- }
143-
144- /*
145- * デフォルトの設定かどうかを返す
146- */
147- public boolean isDefault(){
148- ToolBarIconInfoList cl = new ToolBarIconInfoList();
149- cl.setDefault();
150-
151- if (cl.size() != this.size())
152- return false;
153-
154- for (int n=0; n<cl.size(); n++){
155- ListColumnInfo c = cl.get(n);
156- ListColumnInfo c2 = this.get(n);
157-
158- if (!c.getName().equals(c2.getName()) || c.getVisible() != c2.getVisible())
159- return false;
160- }
161-
162- return true;
163- }
164-}
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.15.6
9+ */
10+public class ToolBarIconInfoList extends ArrayList<ListColumnInfo> implements Cloneable{
11+
12+ private static final long serialVersionUID = 1L;
13+
14+ private static final String listFile = "env"+File.separator+"tbiinfolist.xml";
15+
16+ @Override
17+ public Object clone(){
18+ ToolBarIconInfoList p = new ToolBarIconInfoList();
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+ /*
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 = (ToolBarIconInfoList) 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+ addNewColumns();
100+
101+ return true;
102+ }
103+
104+ /*
105+ * 新しい列を追加する
106+ */
107+ private void addNewColumns(){
108+ ToolBarIconInfoList cl = new ToolBarIconInfoList();
109+ cl.setDefault();
110+
111+ for (int n=0; n<cl.size(); n++){
112+ ListColumnInfo c = cl.get(n);
113+
114+ int n2=0;
115+ for (n2=0; n2<this.size(); n2++){
116+ ListColumnInfo c2 = this.get(n2);
117+ if (c.getName().equals(c2.getName()))
118+ break;
119+ }
120+
121+ if (n2 == this.size()){
122+ this.add((ListColumnInfo)c.clone());
123+ }
124+ }
125+ }
126+
127+ /*
128+ * デフォルトの設定にする
129+ */
130+ public void setDefault(){
131+ this.clear();
132+
133+ int idx = 1;
134+ for (ToolBarIconInfo info : ToolBarIconInfo.values()) {
135+ ListColumnInfo cb = new ListColumnInfo();
136+ cb.setVisible(info.getVisible());
137+ cb.setName(info.getName());
138+ cb.setId(idx++);
139+
140+ this.add(cb);
141+ }
142+ }
143+
144+ /*
145+ * デフォルトの設定かどうかを返す
146+ */
147+ public boolean isDefault(){
148+ ToolBarIconInfoList cl = new ToolBarIconInfoList();
149+ cl.setDefault();
150+
151+ if (cl.size() != this.size())
152+ return false;
153+
154+ for (int n=0; n<cl.size(); n++){
155+ ListColumnInfo c = cl.get(n);
156+ ListColumnInfo c2 = this.get(n);
157+
158+ if (!c.getName().equals(c2.getName()) || c.getVisible() != c2.getVisible())
159+ return false;
160+ }
161+
162+ return true;
163+ }
164+}
--- a/TinyBannavi/src/tainavi/VWFolderDialog.java
+++ b/TinyBannavi/src/tainavi/VWFolderDialog.java
@@ -1,310 +1,310 @@
1-package tainavi;
2-
3-import java.awt.Color;
4-import java.awt.Dimension;
5-import java.awt.event.ActionEvent;
6-import java.awt.event.ActionListener;
7-import java.awt.event.KeyEvent;
8-import java.awt.event.KeyListener;
9-import java.nio.charset.Charset;
10-
11-import javax.swing.JButton;
12-import javax.swing.JDialog;
13-import javax.swing.JLabel;
14-import javax.swing.JOptionPane;
15-import javax.swing.JPanel;
16-import javax.swing.JTextField;
17-import javax.swing.event.DocumentEvent;
18-import javax.swing.event.DocumentListener;
19-
20-
21-/**
22- * フォルダー作成画面クラス
23- */
24-public class VWFolderDialog extends JDialog {
25-
26- /*******************************************************************************
27- * 抽象メソッド
28- ******************************************************************************/
29-
30- /*******************************************************************************
31- * 定数
32- ******************************************************************************/
33-
34- // レイアウト関連
35-
36- private static final int SEP_WIDTH = 10;
37- private static final int SEP_HEIGHT = 10;
38-
39- private static final int PARTS_HEIGHT = 30;
40- private static final int TEXT_HEIGHT = 30;
41-
42- private static final int LABEL_WIDTH = 500;
43- private static final int TEXT_WIDTH = 500;
44- private static final int BUTTON_WIDTH_S = 75;
45-
46- private static final int MAX_FOLDER_LENGTH = 80;
47-
48- /*******************************************************************************
49- * 部品
50- ******************************************************************************/
51-
52- // コンポーネント
53-
54- private JPanel jPanel = null;
55- private JLabel jLabel_folder = null;
56- private JTextField jTextField_folder = null;
57- private JButton jButton_cancel = null;
58- private JButton jButton_create = null;
59-
60- // コンポーネント以外
61- private boolean reg = false;
62-
63- // 入力したフォルダ名
64- public String getFolderName(){return jTextField_folder.getText(); }
65-
66- /*******************************************************************************
67- * コンストラクタ
68- ******************************************************************************/
69-
70- public VWFolderDialog() {
71- super();
72-
73- reg = false;
74-
75- this.setModal(true);
76- this.setContentPane(getJPanel());
77-
78- // タイトルバーの高さも考慮する必要がある
79- Dimension d = getJPanel().getPreferredSize();
80- this.pack();
81- this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));
82- this.setResizable(false);
83- this.setTitle("フォルダ名称");
84- }
85-
86- /*******************************************************************************
87- * アクション
88- ******************************************************************************/
89-
90- // 公開メソッド
91-
92- /**
93- * フォルダーが登録されたかな?
94- */
95- public boolean isRegistered() { return reg; }
96-
97- // オープン
98- public void open(String name) {
99- jTextField_folder.setText(name);
100- updateFolderLabel();
101- }
102-
103- /*******************************************************************************
104- * リスナー
105- ******************************************************************************/
106-
107- /**
108- * フォルダーを登録する
109- */
110- private final ActionListener al_create = new ActionListener() {
111- @Override
112- public void actionPerformed(ActionEvent e) {
113- registerData();
114- }
115- };
116-
117- private void registerData(){
118- String name = jTextField_folder.getText();
119- if (name.equals("")) {
120- JOptionPane.showMessageDialog(jPanel, "フォルダ名がブランクです。");
121- return;
122- }
123-
124- int lenrb = name.getBytes(Charset.forName("Shift_JIS")).length;
125- if (lenrb > MAX_FOLDER_LENGTH){
126- JOptionPane.showMessageDialog(jPanel, "フォルダ名が長すぎます。(" + String.valueOf(lenrb) + "バイト)");
127- return;
128- }
129-
130- reg = true;
131-
132- // ウィンドウを閉じる
133- dispose();
134- }
135-
136- /**
137- * キャンセルしたい
138- */
139- private final ActionListener al_cancel = new ActionListener() {
140- @Override
141- public void actionPerformed(ActionEvent e) {
142- dispose();
143- }
144- };
145-
146- /**
147- * キー入力イベント処理
148- */
149- private final KeyListener kl_okcancel = new KeyListener() {
150- @Override
151- public void keyTyped(KeyEvent e) {
152- }
153- @Override
154- public void keyPressed(KeyEvent e) {
155- }
156- @Override
157- public void keyReleased(KeyEvent e) {
158- if (e.getKeyCode() == KeyEvent.VK_ENTER){
159- registerData();
160- }
161- else if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
162- dispose();
163- }
164- }
165- };
166-
167- private final KeyListener kl_cancel = new KeyListener() {
168- @Override
169- public void keyTyped(KeyEvent e) {
170- }
171- @Override
172- public void keyPressed(KeyEvent e) {
173- }
174- @Override
175- public void keyReleased(KeyEvent e) {
176- if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
177- dispose();
178- }
179- }
180- };
181-
182- /**
183- * 文書変更イベント処理
184- */
185- private final DocumentListener dl_folderChanged = new DocumentListener(){
186- @Override
187- public void insertUpdate(DocumentEvent e) {
188- updateFolderLabel();
189- }
190-
191- @Override
192- public void removeUpdate(DocumentEvent e) {
193- updateFolderLabel();
194- }
195-
196- @Override
197- public void changedUpdate(DocumentEvent e) {
198- updateFolderLabel();
199- }
200- };
201-
202- /*
203- * タイトルラベルを更新する
204- */
205- private void updateFolderLabel(){
206- String name = jTextField_folder.getText();
207- int lenrb = name.getBytes(Charset.forName("Shift_JIS")).length;
208- int restrb = MAX_FOLDER_LENGTH - lenrb;
209-
210- if (jLabel_folder != null){
211- if (restrb >= 0){
212- jLabel_folder.setText("フォルダ名(残り" + String.valueOf(restrb) + "バイト)");
213- jLabel_folder.setForeground(Color.BLACK);
214- }
215- else{
216- jLabel_folder.setText("フォルダ名(" + String.valueOf(-restrb) + "バイトオーバー)");
217- jLabel_folder.setForeground(Color.RED);
218- }
219- }
220- }
221-
222- /*******************************************************************************
223- * コンポーネント
224- ******************************************************************************/
225-
226- private JPanel getJPanel() {
227- if (jPanel == null) {
228- jPanel = new JPanel();
229- jPanel.setLayout(null);
230-
231- int y = SEP_HEIGHT;
232- int x = SEP_WIDTH;
233-
234- x = SEP_WIDTH;
235- JLabel label = getJLabel_folder("フォルダ名(最大80バイトまで)");
236- label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
237- jPanel.add(label);
238-
239- y += PARTS_HEIGHT;
240- JTextField field = getJTextField_folder();
241- field.setBounds(x, y, TEXT_WIDTH, TEXT_HEIGHT);
242- jPanel.add(field);
243-
244- y += TEXT_HEIGHT + SEP_HEIGHT;
245- x += TEXT_WIDTH - (BUTTON_WIDTH_S*2 + SEP_WIDTH);
246- JButton btnCreate = getJButton_create("登録");
247- btnCreate.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
248- jPanel.add(btnCreate);
249-
250- x += BUTTON_WIDTH_S+SEP_WIDTH;
251- JButton btnCancel = getJButton_cancel("キャンセル");
252- btnCancel.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
253- jPanel.add(btnCancel);
254-
255- x += BUTTON_WIDTH_S+SEP_WIDTH;
256- y += PARTS_HEIGHT+SEP_HEIGHT;
257-
258- jPanel.setPreferredSize(new Dimension(x, y));
259- }
260-
261- return jPanel;
262- }
263-
264- //
265- private JLabel getJLabel_folder(String s) {
266- if (jLabel_folder == null) {
267- jLabel_folder = new JLabel(s);
268- }
269- return(jLabel_folder);
270- }
271-
272- //
273- private JTextField getJTextField_folder() {
274- if (jTextField_folder == null) {
275- jTextField_folder = new JTextField();
276- jTextField_folder.addActionListener(al_create);
277- jTextField_folder.addKeyListener(kl_cancel);
278- jTextField_folder.getDocument().addDocumentListener(dl_folderChanged);
279- }
280- return(jTextField_folder);
281- }
282-
283- //
284- private JButton getJButton_create(String s) {
285- if (jButton_create == null) {
286- jButton_create = new JButton();
287- jButton_create.setText(s);
288-
289- jButton_create.addActionListener(al_create);
290- jButton_create.addKeyListener(kl_cancel);
291- }
292- return(jButton_create);
293- }
294-
295- //
296- private JButton getJButton_cancel(String s) {
297- if (jButton_cancel == null) {
298- jButton_cancel = new JButton();
299- jButton_cancel.setText(s);
300-
301- jButton_cancel.addActionListener(al_cancel);
302- jButton_cancel.addKeyListener(kl_cancel);
303- }
304- return jButton_cancel;
305- }
306-
307- /*******************************************************************************
308- * 独自コンポーネント
309- ******************************************************************************/
310-}
1+package tainavi;
2+
3+import java.awt.Color;
4+import java.awt.Dimension;
5+import java.awt.event.ActionEvent;
6+import java.awt.event.ActionListener;
7+import java.awt.event.KeyEvent;
8+import java.awt.event.KeyListener;
9+import java.nio.charset.Charset;
10+
11+import javax.swing.JButton;
12+import javax.swing.JDialog;
13+import javax.swing.JLabel;
14+import javax.swing.JOptionPane;
15+import javax.swing.JPanel;
16+import javax.swing.JTextField;
17+import javax.swing.event.DocumentEvent;
18+import javax.swing.event.DocumentListener;
19+
20+
21+/**
22+ * フォルダー作成画面クラス
23+ */
24+public class VWFolderDialog extends JDialog {
25+
26+ /*******************************************************************************
27+ * 抽象メソッド
28+ ******************************************************************************/
29+
30+ /*******************************************************************************
31+ * 定数
32+ ******************************************************************************/
33+
34+ // レイアウト関連
35+
36+ private static final int SEP_WIDTH = 10;
37+ private static final int SEP_HEIGHT = 10;
38+
39+ private static final int PARTS_HEIGHT = 30;
40+ private static final int TEXT_HEIGHT = 30;
41+
42+ private static final int LABEL_WIDTH = 500;
43+ private static final int TEXT_WIDTH = 500;
44+ private static final int BUTTON_WIDTH_S = 75;
45+
46+ private static final int MAX_FOLDER_LENGTH = 80;
47+
48+ /*******************************************************************************
49+ * 部品
50+ ******************************************************************************/
51+
52+ // コンポーネント
53+
54+ private JPanel jPanel = null;
55+ private JLabel jLabel_folder = null;
56+ private JTextField jTextField_folder = null;
57+ private JButton jButton_cancel = null;
58+ private JButton jButton_create = null;
59+
60+ // コンポーネント以外
61+ private boolean reg = false;
62+
63+ // 入力したフォルダ名
64+ public String getFolderName(){return jTextField_folder.getText(); }
65+
66+ /*******************************************************************************
67+ * コンストラクタ
68+ ******************************************************************************/
69+
70+ public VWFolderDialog() {
71+ super();
72+
73+ reg = false;
74+
75+ this.setModal(true);
76+ this.setContentPane(getJPanel());
77+
78+ // タイトルバーの高さも考慮する必要がある
79+ Dimension d = getJPanel().getPreferredSize();
80+ this.pack();
81+ this.setPreferredSize(new Dimension(d.width, d.height+this.getInsets().top));
82+ this.setResizable(false);
83+ this.setTitle("フォルダ名称");
84+ }
85+
86+ /*******************************************************************************
87+ * アクション
88+ ******************************************************************************/
89+
90+ // 公開メソッド
91+
92+ /**
93+ * フォルダーが登録されたかな?
94+ */
95+ public boolean isRegistered() { return reg; }
96+
97+ // オープン
98+ public void open(String name) {
99+ jTextField_folder.setText(name);
100+ updateFolderLabel();
101+ }
102+
103+ /*******************************************************************************
104+ * リスナー
105+ ******************************************************************************/
106+
107+ /**
108+ * フォルダーを登録する
109+ */
110+ private final ActionListener al_create = new ActionListener() {
111+ @Override
112+ public void actionPerformed(ActionEvent e) {
113+ registerData();
114+ }
115+ };
116+
117+ private void registerData(){
118+ String name = jTextField_folder.getText();
119+ if (name.equals("")) {
120+ JOptionPane.showMessageDialog(jPanel, "フォルダ名がブランクです。");
121+ return;
122+ }
123+
124+ int lenrb = name.getBytes(Charset.forName("Shift_JIS")).length;
125+ if (lenrb > MAX_FOLDER_LENGTH){
126+ JOptionPane.showMessageDialog(jPanel, "フォルダ名が長すぎます。(" + String.valueOf(lenrb) + "バイト)");
127+ return;
128+ }
129+
130+ reg = true;
131+
132+ // ウィンドウを閉じる
133+ dispose();
134+ }
135+
136+ /**
137+ * キャンセルしたい
138+ */
139+ private final ActionListener al_cancel = new ActionListener() {
140+ @Override
141+ public void actionPerformed(ActionEvent e) {
142+ dispose();
143+ }
144+ };
145+
146+ /**
147+ * キー入力イベント処理
148+ */
149+ private final KeyListener kl_okcancel = new KeyListener() {
150+ @Override
151+ public void keyTyped(KeyEvent e) {
152+ }
153+ @Override
154+ public void keyPressed(KeyEvent e) {
155+ }
156+ @Override
157+ public void keyReleased(KeyEvent e) {
158+ if (e.getKeyCode() == KeyEvent.VK_ENTER){
159+ registerData();
160+ }
161+ else if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
162+ dispose();
163+ }
164+ }
165+ };
166+
167+ private final KeyListener kl_cancel = new KeyListener() {
168+ @Override
169+ public void keyTyped(KeyEvent e) {
170+ }
171+ @Override
172+ public void keyPressed(KeyEvent e) {
173+ }
174+ @Override
175+ public void keyReleased(KeyEvent e) {
176+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
177+ dispose();
178+ }
179+ }
180+ };
181+
182+ /**
183+ * 文書変更イベント処理
184+ */
185+ private final DocumentListener dl_folderChanged = new DocumentListener(){
186+ @Override
187+ public void insertUpdate(DocumentEvent e) {
188+ updateFolderLabel();
189+ }
190+
191+ @Override
192+ public void removeUpdate(DocumentEvent e) {
193+ updateFolderLabel();
194+ }
195+
196+ @Override
197+ public void changedUpdate(DocumentEvent e) {
198+ updateFolderLabel();
199+ }
200+ };
201+
202+ /*
203+ * タイトルラベルを更新する
204+ */
205+ private void updateFolderLabel(){
206+ String name = jTextField_folder.getText();
207+ int lenrb = name.getBytes(Charset.forName("Shift_JIS")).length;
208+ int restrb = MAX_FOLDER_LENGTH - lenrb;
209+
210+ if (jLabel_folder != null){
211+ if (restrb >= 0){
212+ jLabel_folder.setText("フォルダ名(残り" + String.valueOf(restrb) + "バイト)");
213+ jLabel_folder.setForeground(Color.BLACK);
214+ }
215+ else{
216+ jLabel_folder.setText("フォルダ名(" + String.valueOf(-restrb) + "バイトオーバー)");
217+ jLabel_folder.setForeground(Color.RED);
218+ }
219+ }
220+ }
221+
222+ /*******************************************************************************
223+ * コンポーネント
224+ ******************************************************************************/
225+
226+ private JPanel getJPanel() {
227+ if (jPanel == null) {
228+ jPanel = new JPanel();
229+ jPanel.setLayout(null);
230+
231+ int y = SEP_HEIGHT;
232+ int x = SEP_WIDTH;
233+
234+ x = SEP_WIDTH;
235+ JLabel label = getJLabel_folder("フォルダ名(最大80バイトまで)");
236+ label.setBounds(x, y, LABEL_WIDTH, PARTS_HEIGHT);
237+ jPanel.add(label);
238+
239+ y += PARTS_HEIGHT;
240+ JTextField field = getJTextField_folder();
241+ field.setBounds(x, y, TEXT_WIDTH, TEXT_HEIGHT);
242+ jPanel.add(field);
243+
244+ y += TEXT_HEIGHT + SEP_HEIGHT;
245+ x += TEXT_WIDTH - (BUTTON_WIDTH_S*2 + SEP_WIDTH);
246+ JButton btnCreate = getJButton_create("登録");
247+ btnCreate.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
248+ jPanel.add(btnCreate);
249+
250+ x += BUTTON_WIDTH_S+SEP_WIDTH;
251+ JButton btnCancel = getJButton_cancel("キャンセル");
252+ btnCancel.setBounds(x, y, BUTTON_WIDTH_S, PARTS_HEIGHT);
253+ jPanel.add(btnCancel);
254+
255+ x += BUTTON_WIDTH_S+SEP_WIDTH;
256+ y += PARTS_HEIGHT+SEP_HEIGHT;
257+
258+ jPanel.setPreferredSize(new Dimension(x, y));
259+ }
260+
261+ return jPanel;
262+ }
263+
264+ //
265+ private JLabel getJLabel_folder(String s) {
266+ if (jLabel_folder == null) {
267+ jLabel_folder = new JLabel(s);
268+ }
269+ return(jLabel_folder);
270+ }
271+
272+ //
273+ private JTextField getJTextField_folder() {
274+ if (jTextField_folder == null) {
275+ jTextField_folder = new JTextField();
276+ jTextField_folder.addActionListener(al_create);
277+ jTextField_folder.addKeyListener(kl_cancel);
278+ jTextField_folder.getDocument().addDocumentListener(dl_folderChanged);
279+ }
280+ return(jTextField_folder);
281+ }
282+
283+ //
284+ private JButton getJButton_create(String s) {
285+ if (jButton_create == null) {
286+ jButton_create = new JButton();
287+ jButton_create.setText(s);
288+
289+ jButton_create.addActionListener(al_create);
290+ jButton_create.addKeyListener(kl_cancel);
291+ }
292+ return(jButton_create);
293+ }
294+
295+ //
296+ private JButton getJButton_cancel(String s) {
297+ if (jButton_cancel == null) {
298+ jButton_cancel = new JButton();
299+ jButton_cancel.setText(s);
300+
301+ jButton_cancel.addActionListener(al_cancel);
302+ jButton_cancel.addKeyListener(kl_cancel);
303+ }
304+ return jButton_cancel;
305+ }
306+
307+ /*******************************************************************************
308+ * 独自コンポーネント
309+ ******************************************************************************/
310+}
--- a/TinyBannavi/src/tainavi/VersionInfo.java
+++ b/TinyBannavi/src/tainavi/VersionInfo.java
@@ -5,7 +5,7 @@ import java.util.regex.Pattern;
55
66
77 public class VersionInfo {
8- private static final String Version = "タイニー番組ナビゲータ for DBR-T2007 3.22.18β+1.12.13";
8+ private static final String Version = "タイニー番組ナビゲータ for DBR-T2007 3.22.18β+1.12.14";
99
1010 private static final String OSname = System.getProperty("os.name");
1111 private static final String OSvers = System.getProperty("os.version");
--- a/TinyBannavi/src/tainavi/pluginrec/PlugIn_RecDBR_M2008.java
+++ b/TinyBannavi/src/tainavi/pluginrec/PlugIn_RecDBR_M2008.java
@@ -1,2815 +1,2815 @@
1-package tainavi.pluginrec;
2-
3-import java.io.File;
4-import java.io.UnsupportedEncodingException;
5-import java.net.Authenticator;
6-import java.net.URLEncoder;
7-import java.text.ParseException;
8-import java.text.SimpleDateFormat;
9-import java.util.ArrayList;
10-import java.util.Comparator;
11-import java.util.Date;
12-import java.util.HashMap;
13-import java.util.Locale;
14-import java.util.regex.Matcher;
15-import java.util.regex.Pattern;
16-
17-import tainavi.AribCharMap;
18-import tainavi.ChannelCode;
19-import tainavi.ChapterInfo;
20-import tainavi.CommonUtils;
21-import tainavi.DeviceInfo;
22-import tainavi.GetRDStatus;
23-import tainavi.HDDRecorder;
24-import tainavi.HDDRecorderUtils;
25-import tainavi.ReserveList;
26-import tainavi.TVProgram.ProgGenre;
27-import tainavi.TextValueSet;
28-import tainavi.TitleInfo;
29-import tainavi.TraceProgram;
30-
31-/**
32- * REGZA DBR-T2007用のレコーダプラグインです。
33- * @author original:tmskmr
34- * @version x.xx.xx
35- */
36-public class PlugIn_RecDBR_M2008 extends HDDRecorderUtils implements HDDRecorder,Cloneable {
37- public PlugIn_RecDBR_M2008() {
38- super();
39- this.setTunerNum(3);
40- setSettingFixed();
41- }
42-
43- public PlugIn_RecDBR_M2008 clone() {
44- return (PlugIn_RecDBR_M2008) super.clone();
45- }
46-
47- private static final String thisEncoding = "UTF-8";
48-
49- /*******************************************************************************
50- * 種族の特性
51- ******************************************************************************/
52-
53- // 種族の特性
54- @Override
55- public String getRecorderId() { return "REGZA DBR-M2008"; }
56- @Override
57- public RecType getType() { return RecType.RECORDER; }
58-
59- // chvalueを使っていいよ
60- @Override
61- public boolean isChValueAvailable() { return true; }
62- // CHコードは入力しなくていい
63- @Override
64- public boolean isChCodeNeeded() { return false; }
65-
66- // 録画タイトルに対応している
67- @Override
68- public boolean isTitleListSupported() { return true; }
69-
70- // 持ち出しに対応している
71- @Override
72- public boolean isPortableSupported() { return true; }
73-
74- // フォルダー作成に対応している
75- @Override
76- public boolean isFolderCreationSupported() { return true; }
77-
78- /*******************************************************************************
79- * 予約ダイアログなどのテキストのオーバーライド
80- ******************************************************************************/
81-
82- @Override
83- public String getChDatHelp() { return
84- "「レコーダの放送局名」はネットdeナビの録画予約一覧で表示される「CH」の欄の値を設定してください。\n"+
85- "予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"
86- ;
87- }
88-
89- /*******************************************************************************
90- * 定数
91- ******************************************************************************/
92-
93- protected final String MSGID = "["+getRecorderId()+"] ";
94- protected final String ERRID = "[ERROR]"+MSGID;
95- protected final String DBGID = "[DEBUG]"+MSGID;
96-
97- protected final String NETWORK_UVD = "0";
98- protected final String NETWORK_BSD = "1";
99- protected final String NETWORK_CSD = "2";
100- protected final String NETWORK_L1 = "3";
101- protected final String NETWORK_L2 = "4";
102- protected final String NETWORK_L3 = "5";
103- protected final String NETWORK_L4 = "6";
104- protected final String NETWORK_NET = "7";
105-
106- protected final String CHTYPE_UVD = "uvd";
107- protected final String CHTYPE_BSD = "bsd";
108- protected final String CHTYPE_CSD = "csd";
109- protected final String CHTYPE_L1 = "L1";
110- protected final String CHTYPE_L2 = "L2";
111- protected final String CHTYPE_L3 = "L3";
112- protected final String CHTYPE_L4 = "L4";
113- protected final String CHTYPE_NET = "NET";
114-
115- protected final String CHPREFIX_BS = "BS";
116- protected final String CHPREFIX_CS = "CS";
117- protected final String CH_L1 = "L1";
118- protected final String CH_L2 = "L2";
119- protected final String CH_L3 = "L3";
120- protected final String CH_L4 = "L4";
121- protected final String CH_NET = "NET";
122-
123- protected final String OPTION_DATETIME = "1";
124- protected final String OPTION_PROGRAM = "2";
125-
126- protected final String REPEAT_NONE = "0";
127-
128- protected final int RPTPTN_ID_TUE2SAT = 12;
129-
130- protected final String EXEC_YES = "1";
131- protected final String EXEC_NO = "0";
132-
133- protected final String BRANCH_NO = "0";
134- protected final String BRANCH_YES = "1";
135-
136- protected final String DEVICE_HDD = "0";
137- protected final String DEVICE_DISC = "1";
138- protected final String FOLDER_NONE = "0";
139- protected final String FOLDER_NAME_NONE = "指定なし";
140-
141- protected final String RECMODE_DR = "0";
142- protected final String RECMODE_NAME_DR = "[DR]";
143-
144- protected final String MOCHIDASHI_NONE = "0";
145-
146- protected final String RESULT_OK = "0";
147- protected final String RESULT_OTHER = "1";
148- protected final String RESULT_BUSY = "2";
149- protected final String RESULT_INVALID_TITLE = "17";
150-
151- protected final String ERRMSG_NORESPONSE = "レコーダーが反応しません";
152- protected final String ERRMSG_INVALIDRESPONSE = "レコーダーからの応答が不正です。";
153- protected final String ERRMSG_USB_BUSY = "USB HDDが使用中です。";
154-
155- protected final double MIN_PER_MB = 171.777;
156-
157- protected final String TITLE_NOCHANGED = "__NETdeNAVI_TitleNameNotChanged__";
158-
159- /*******************************************************************************
160- * CHコード設定、エラーメッセージ
161- ******************************************************************************/
162-
163- public ChannelCode getChCode() {
164- return cc;
165- }
166-
167- private ChannelCode cc = new ChannelCode(getRecorderId());
168-
169- protected void setErrmsg(String s) { errmsg = s; }
170-
171- public String getErrmsg() {
172- return(errmsg.replaceAll("\\\\r\\\\n", ""));
173- }
174-
175- private String errmsg = "";
176-
177- /*******************************************************************************
178- * 部品
179- ******************************************************************************/
180-
181- private GetRDStatus gs = new GetRDStatus();
182-
183- private String rsvedFile = "";
184- private String folderTFile = "";
185- private String titleFile = "";
186- private String devinfoTFile = "";
187- private ArrayList<TextValueSet> tvsBranch = new ArrayList<TextValueSet>();
188- private ArrayList<TextValueSet> tvsPatternCode = new ArrayList<TextValueSet>();
189- private ArrayList<TextValueSet> tvsPatternName = new ArrayList<TextValueSet>();
190-
191- /*******************************************************************************
192- * コンストラクタ
193- ******************************************************************************/
194-
195- /*******************************************************************************
196- * チャンネルリモコン機能
197- ******************************************************************************/
198-
199- /*******************************************************************************
200- * レコーダーから各種設定情報を取得する
201- ******************************************************************************/
202- @Override
203- public boolean GetRdSettings(boolean force) {
204-
205- System.out.println("レコーダの各種設定情報を取得します.");
206-
207- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
208-
209- String deviceTFile = "env/device."+myFileId+".xml";
210- devinfoTFile = "env/devinfo."+myFileId+".xml";
211- folderTFile = "env/folders."+myFileId+".xml";
212- String chValueTFile = "env/chvalue."+myFileId+".xml";
213- String chTypeTFile = "env/chtype."+myFileId+".xml";
214- String branchTFile = "env/branch."+myFileId+".xml";
215-
216- // 固定の各種設定情報を初期化する
217- setSettingFixed();
218-
219- File f = new File(deviceTFile);
220- if ( !force){
221- if (!f.exists())
222- return(false);
223-
224- // キャッシュから読み出し(録画設定ほか)
225- device = TVSload(deviceTFile);
226- setDeviceInfos(DeviceInfosFromFile(devinfoTFile));
227- folder = TVSload(folderTFile);
228- chvalue = TVSload(chValueTFile);
229- chtype = TVSload(chTypeTFile);
230- tvsBranch = TVSload(branchTFile);
231-
232- // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
233- if (device.size() > 0 && chvalue.size() > 0 && chtype.size() > 0 && tvsBranch.size() > 0) {
234- return(true);
235- }
236- }
237-
238- // 各種設定情報をレコーダから取得する
239- if (!setSettingVariable()){
240- return (false);
241- }
242-
243- TVSsave(device, deviceTFile);
244- saveDeviceInfos();
245- saveFolders();
246- TVSsave(chvalue, chValueTFile);
247- TVSsave(chtype, chTypeTFile);
248- TVSsave(tvsBranch, branchTFile);
249-
250- return(true);
251- }
252-
253- /*******************************************************************************
254- * レコーダーから予約一覧を取得する
255- ******************************************************************************/
256-
257- public boolean GetRdReserve(boolean force)
258- {
259- System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
260-
261- errmsg = "";
262-
263- //
264- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
265-
266- rsvedFile = "env/reserved."+myFileId+".xml";
267-
268- File f = new File(rsvedFile);
269- if ( !force && f.exists()) {
270- // キャッシュから読み出し(予約一覧)
271- setReserves(ReservesFromFile(rsvedFile));
272-
273- // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
274- if (getReserves().size() > 0) {
275- return(true);
276- }
277- }
278-
279- // 録画予約の一覧をレコーダから取得する
280- ArrayList<ReserveList> ra = getReserveList();
281- if (ra == null){
282- return(false);
283- }
284-
285- setReserves(ra);
286-
287- // キャッシュに保存
288- ReservesToFile(getReserves(), rsvedFile);
289-
290- // 取得した情報の表示
291- if (getDebug()){
292- System.out.println("---Reserved List Start---");
293- for ( int i = 0; i<getReserves().size(); i++ ) {
294- // 詳細情報の取得
295- ReserveList e = getReserves().get(i);
296- System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",
297- (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));
298- }
299- System.out.println("---Reserved List End---");
300- }
301-
302- return(true);
303- }
304-
305- /*******************************************************************************
306- * 予約詳細情報の取得
307- ******************************************************************************/
308- @Override
309- public boolean isThereAdditionalDetails() {
310- return true;
311- }
312-
313- /*
314- * 予約の詳細情報を取得する
315- */
316- @Override
317- public boolean GetRdReserveDetails(){
318- int rno = 0;
319- ArrayList<ReserveList> ra = getReserves();
320- for (ReserveList entry : ra) {
321-
322- reportProgress("+番組詳細を取得します("+rno+"/"+ra.size()+").");
323- getReserveDetail(entry);
324-
325- // 放送局名変換
326- entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
327-
328- // TS->DR
329- translateAttributeTuner(entry);
330-
331- // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ
332- copyAttributesT2007(entry, getReserves());
333-
334- rno++;
335- }
336-
337- // キャッシュに保存
338- ReservesToFile(getReserves(), rsvedFile);
339-
340- return(true);
341- }
342-
343- /*******************************************************************************
344- * 新規予約
345- ******************************************************************************/
346- @Override
347- public boolean PostRdEntry(ReserveList r) {
348- errmsg = "";
349-
350- String chcode = cc.getCH_WEB2CODE(r.getCh_name());
351- if (chcode == null) {
352- errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
353- System.out.println(errmsg);
354- return(false);
355- }
356-
357- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
358- if ( !loadDialogInitData("0")){
359- return(false);
360- }
361-
362- // RDへの情報作成
363- String pstr = createPostData(r, "");
364-
365- // RDへ情報送信
366- reportProgress(MSGID+"レコーダーに新規予約を要求します.");
367- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
368-// String header = d[0];
369- String response = d[1];
370-
371- // 登録結果の確認
372- if ( !checkReserveResponse(r, response) ){
373- return(false);
374- }
375-
376- // 予約情報の調整
377- adjustReserve(r);
378-
379- // 予約リストを更新
380- getReserves().add(r);
381-
382- // キャッシュに保存
383- ReservesToFile(getReserves(), rsvedFile);
384-
385- reportProgress(MSGID+"正常に登録できました。");
386-
387- return(true);
388- }
389-
390- /*******************************************************************************
391- * 予約更新
392- ******************************************************************************/
393- @Override
394- public boolean UpdateRdEntry(ReserveList o, ReserveList r) {
395- errmsg = "";
396-
397- String chcode = cc.getCH_WEB2CODE(r.getCh_name());
398- if (chcode == null) {
399- errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
400- System.out.println(errmsg);
401- return(false);
402- }
403-
404- String id = o.getId();
405-
406- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
407- if ( !loadDialogInitData(id) ){
408- return(false);
409- }
410-
411- // RDへの情報作成
412- String pstr = createPostData(r, id);
413-
414- // RDへ情報送信
415- reportProgress(MSGID+"レコーダーに予約更新を要求します.");
416- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
417-// String header = d[0];
418- String response = d[1];
419-
420- // 登録結果の確認
421- if ( !checkReserveResponse(r, response) ){
422- return(false);
423- }
424-
425- // 予約情報の調整
426- adjustReserve(r);
427-
428- // 情報置き換え
429- getReserves().remove(o);
430- getReserves().add(r);
431-
432- // キャッシュに保存
433- ReservesToFile(getReserves(), rsvedFile);
434-
435- reportProgress(MSGID+"正常に更新できました。");
436-
437- return(true);
438- }
439-
440- /*******************************************************************************
441- * 予約削除
442- ******************************************************************************/
443- @Override
444- public ReserveList RemoveRdEntry(String delid) {
445- errmsg = "";
446-
447- // 削除対象を探す
448- ReserveList r = null;
449- for ( ReserveList reserve : getReserves() ) {
450- if (reserve.getId().equals(delid)) {
451- r = reserve;
452- break;
453- }
454- }
455- if (r == null) {
456- return(null);
457- }
458-
459- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
460- if ( !loadDialogInitData(delid)){
461- return(null);
462- }
463-
464- // RDへの情報作成
465- String pstr = createPostData(r, delid);
466-
467- // RDへ情報送信
468- reportProgress(MSGID+"レコーダーに予約削除を要求します.");
469- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DeleteReserve.php", pstr, null, thisEncoding);
470-// String header = d[0];
471- String response = d[1];
472-
473- if ( !checkGeneralResponse( "予約削除", response) ){
474- return(null);
475- }
476-
477- // 予約リストを更新
478- getReserves().remove(r);
479-
480- // キャッシュに保存
481- ReservesToFile(getReserves(), rsvedFile);
482-
483- reportProgress(MSGID+"正常に削除できました。");
484- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
485- return(r);
486- }
487-
488- /*******************************************************************************
489- * フォルダー作成
490- ******************************************************************************/
491- @Override
492- public boolean CreateRdFolder(String device_id, String folder_name) {
493- return setFolderName( device_id, null, folder_name );
494- }
495-
496- /*******************************************************************************
497- * フォルダー名更新
498- ******************************************************************************/
499- @Override
500- public boolean UpdateRdFolderName(String device_id, String folder_id, String folder_name) {
501- return setFolderName( device_id, folder_id, folder_name );
502- }
503-
504- /*******************************************************************************
505- * フォルダー削除
506- ******************************************************************************/
507- @Override
508- public boolean RemoveRdFolder(String device_id, String fol_id) {
509- String action = "フォルダ削除";
510- String folder_id = extractFolderID(fol_id);
511-
512- String pstr =
513- "drive_id=" + device_id + "&" +
514- "folder_id=" + folder_id;
515-
516- for (int n=0; n<3; n++){
517- // おまじない
518- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
519-
520- // RDへ情報送信
521- reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
522- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/folderset/DeleteFolder.php?" + pstr, null, thisEncoding);
523-// String header = d[0];
524- String response = d[1];
525-
526- // レスポンスから処理結果を取得する
527- String [] rc = getReasonFromResponse( response );
528- if (rc == null) {
529- errmsg = ERRID+ERRMSG_NORESPONSE;
530- return(false);
531- }
532-
533- String result = rc[0];
534- String reason = rc[1];
535-
536- if (result.equals(RESULT_OK))
537- break;
538-
539- if (result.equals(RESULT_OTHER) || result.equals(RESULT_BUSY)){
540- if (mountUsbDrive(device_id))
541- continue;
542- }
543-
544- errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
545- return(false);
546- }
547-
548- // フォルダ一覧を取得し直す
549- setSettingFolders();
550- saveFolders();
551-
552- return true;
553- }
554-
555- /*******************************************************************************
556- * タイトル一覧取得
557- ******************************************************************************/
558- @Override
559- public boolean GetRdTitles(String device_id, boolean force, boolean detail, boolean mountedOnly) {
560- System.out.println("レコーダからタイトル一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
561-
562- errmsg = "";
563-
564- DEVICE_ID = device_id;
565-
566- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
567-
568- ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
569-
570- for (DeviceInfo di : getDeviceInfos() ) {
571- String devid = di.getId();
572- if (devid.equals(DEVICE_ALL))
573- continue;
574- if (!device_id.equals(DEVICE_ALL) && !device_id.equals(devid))
575- continue;
576-
577- titleFile = "env/title."+myFileId+"." + devid + ".xml";
578-
579- if (!force){
580- File f = new File(titleFile);
581- if (!f.exists())
582- return(false);
583-
584- // キャッシュから読み出し(タイトル一覧)
585- ArrayList<TitleInfo> ta = TitlesFromFile(titleFile);
586-
587- list.addAll(ta);
588- }
589- else{
590- // タイトル一覧をレコーダから取得する
591- ArrayList<TitleInfo> ta = getTitleList(devid, mountedOnly);
592- if (ta == null){
593- if (errmsg.length() > 0)
594- return(false);
595-
596- File f = new File(titleFile);
597- // キャッシュから読み出し(タイトル一覧)
598- if (f.exists())
599- ta = TitlesFromFile(titleFile);
600- }
601- else{
602- // タイトルの詳細情報を取得し、内容を調整する
603- for (TitleInfo entry : ta) {
604- // 放送局名変換
605- entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
606- }
607-
608- if (detail){
609- getTitleDetails(devid, ta, false);
610- }
611- }
612-
613- list.addAll(ta);
614- }
615- }
616-
617- setTitles(list);
618-
619- // キャッシュに保存
620- if (force){
621- saveTitles(device_id);
622-
623- // 取得した情報の表示
624- if (getDebug()){
625- System.out.println("---Title List Start---");
626- for ( int i = 0; i<getTitles().size(); i++ ) {
627- // 詳細情報の取得
628- TitleInfo t = getTitles().get(i);
629- System.out.println(String.format("[%s] %s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s",
630- (i+1), t.getId(), t.getRec_date(), t.getAhh(), t.getAmm(), t.getZhh(), t.getZmm(), t.getRec_min(),
631- t.getTitle(), t.getChannel(), t.getCh_name()));
632- }
633- System.out.println("---Title List End---");
634- }
635-
636- // 各種設定情報をレコーダから取得する
637- if (setSettingDevice()){
638- saveDeviceInfos();
639- }
640- }
641-
642- return(true);
643- }
644-
645- /*******************************************************************************
646- * タイトル詳細情報取得
647- ******************************************************************************/
648- @Override
649- public boolean GetRdTitleDetails(String devid, boolean force){
650- ArrayList<TitleInfo> ta = getTitles();
651-
652- getTitleDetails(devid, ta, force);
653-
654- // キャッシュに保存
655- saveTitles(devid);
656-
657- return(true);
658- }
659-
660- /*
661- * タイトル詳細情報を取得する
662- */
663- private boolean getTitleDetails(String devid, ArrayList<TitleInfo>ta, boolean force){
664- int tno = 0;
665-
666- for (TitleInfo ti : ta){
667- tno++;
668-
669- if (ti.getDetailLoaded() && !ti.getRecording() && !force)
670- continue;
671-
672- reportProgress("+タイトル詳細を取得します("+tno+"/"+ta.size()+").");
673- getTitleDetail(ti);
674- }
675-
676- return(true);
677- }
678-
679- /*******************************************************************************
680- * タイトル詳細情報取得
681- ******************************************************************************/
682- @Override
683- public boolean GetRdTitleDetail(TitleInfo t) {
684- if (t == null)
685- return(false);
686-
687- if (!getTitleDetail(t))
688- return(false);
689-
690- // キャッシュに保存
691- saveTitles(t.getRec_device());
692-
693- return(true);
694- }
695-
696- /*******************************************************************************
697- * タイトル更新
698- ******************************************************************************/
699- @Override
700- public boolean UpdateRdTitleInfo(String device_id, TitleInfo o, TitleInfo t) {
701- errmsg = "";
702-
703- if (t == null) {
704- return(false);
705- }
706-
707- for (int n=0; n<3; n++){
708- // タイトルの編集をレコーダに通知する
709- notifyTitleEdit(device_id, t.getId());
710-
711- // おまじない
712- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
713-
714- // RDへの情報作成
715- String pstr = createTitlePostData(t, o, device_id);
716-
717- // RDへ情報送信
718- reportProgress(MSGID+"レコーダーにタイトル更新を要求します:"+device_id);
719- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/UpdateTitleInfo.php", pstr, null, thisEncoding);
720-// String header = d[0];
721- String response = d[1];
722-
723- // レスポンスから処理結果を取得する
724- String [] rc = getReasonFromResponse( response );
725- if (rc == null) {
726- errmsg = ERRID+ERRMSG_NORESPONSE;
727- return(false);
728- }
729-
730- String result = rc[0];
731- String reason = rc[1];
732-
733- if (result.equals(RESULT_OK))
734- break;
735-
736- if (result.equals(RESULT_OTHER)){
737- if (mountUsbDrive(device_id))
738- continue;
739- }
740-
741- errmsg = ERRID+"タイトル更新に失敗しました。(result=" + result + ",reason=" + reason + ")";
742- return(false);
743- }
744-
745- // 録画タイトルリストを更新
746- ArrayList<TitleInfo> list = getTitles();
747- list.remove(o);
748- list.add(t);
749- list.sort(new TitleInfoComparator());
750-
751- // キャッシュに保存
752- saveTitles(t.getRec_device());
753-
754- // 各種設定情報をレコーダから取得する
755- if (setSettingDevice()){
756- saveDeviceInfos();
757- }
758-
759- reportProgress(MSGID+"正常に更新できました。");
760- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");
761-
762- return(true);
763- }
764-
765- /*******************************************************************************
766- * タイトル削除
767- ******************************************************************************/
768- @Override
769- public TitleInfo RemoveRdTitle(String device_id, String title_id) {
770- errmsg = "";
771-
772- // 削除対象を探す
773- TitleInfo t = null;
774- for ( TitleInfo ttl : getTitles() ) {
775- if (ttl.getId().equals(title_id)) {
776- t = ttl;
777- break;
778- }
779- }
780- if (t == null) {
781- return(null);
782- }
783-
784- for (int n=0; n<3; n++){
785- // おまじない
786- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
787-
788- // RDへの情報作成
789- String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
790-
791- // RDへ情報送信
792- reportProgress(MSGID+"レコーダーにタイトル削除を要求します.");
793- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/DeleteTitle.php?"+pstr, null, thisEncoding);
794-// String header = d[0];
795- String response = d[1];
796-
797- // レスポンスから処理結果を取得する
798- String [] rc = getReasonFromResponse( response );
799- if (rc == null) {
800- errmsg = ERRID+ERRMSG_NORESPONSE;
801- return(null);
802- }
803-
804- String result = rc[0];
805- String reason = rc[1];
806-
807- if (result.equals(RESULT_OK))
808- break;
809-
810- if (result.equals(RESULT_OTHER) || result.equals(RESULT_INVALID_TITLE)){
811- if (mountUsbDrive(device_id))
812- continue;
813- }
814-
815- errmsg = ERRID+"タイトル削除に失敗しました。(result=" + result + ",reason=" + reason + ")";
816- return(null);
817- }
818-
819- // タイトルリストを更新
820- getTitles().remove(t);
821-
822- // キャッシュに保存
823- saveTitles(t.getRec_device());
824-
825- setSettingDevice();
826- saveDeviceInfos();
827-
828- reportProgress(MSGID+"正常に削除できました。");
829- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
830- return(t);
831- }
832-
833- /*******************************************************************************
834- * タイトル再生の開始・終了
835- ******************************************************************************/
836- @Override
837- public boolean StartStopPlayRdTitle(String device_id, String title_id, boolean start) {
838- errmsg = "";
839-
840- // RDへのURL
841- String action = start ? "タイトル再生開始" : "タイトル再生終了";
842- String url = start ? "/titlelist/PlayTitle.php" : "/titlelist/PlayStop.php";
843-
844- // RDへの情報作成
845- String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
846-
847- for (int n=0; n<3; n++){
848- // おまじない
849- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
850-
851- // RDへ情報送信
852- reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
853- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+url+ "?"+pstr, null, thisEncoding);
854-// String header = d[0];
855- String response = d[1];
856-
857- // レスポンスから処理結果を取得する
858- String [] rc = getReasonFromResponse( response );
859- if (rc == null) {
860- errmsg = ERRID+ERRMSG_NORESPONSE;
861- return(false);
862- }
863-
864- String result = rc[0];
865- String reason = rc[1];
866-
867- if (result.equals(RESULT_OK))
868- break;
869-
870- if (result.equals(RESULT_OTHER)){
871- if (mountUsbDrive(device_id))
872- continue;
873- }
874-
875- errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
876- return(false);
877- }
878-
879- reportProgress(MSGID+"正常に" + action + "できました。");
880-
881- return(true);
882- }
883-
884- /* ここまで */
885-
886- /* 個別コード-ここから最後まで */
887- /*******************************************************************************
888- * 非公開メソッド
889- ******************************************************************************/
890- /*
891- * 録画予約の一覧を取得する
892- */
893- protected ArrayList<ReserveList> getReserveList() {
894- // おまじない
895- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
896-
897- // RDから予約一覧を取り出す
898- reportProgress(MSGID+"予約一覧を取得します.");
899- String response = "";
900- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadReserveList.php",null, thisEncoding);
901-// String header = d[0];
902- response= d[1];
903-
904- if (response == null) {
905- errmsg = ERRID+ERRMSG_NORESPONSE;
906- return(null);
907- }
908-
909- // 先頭部分をチェックする
910- Matcher mr = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
911- if ( ! mr.find()) {
912- errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
913- return (null);
914- }
915-
916- ArrayList<ReserveList> list = new ArrayList<ReserveList>();
917-
918- Matcher ma = Pattern.compile("\\{" +
919- "\"num\":(\\d+)," + // 1
920- "\"id\":(\\d+)," + // 2
921- "\"exec\":(\\d+)," + // 3
922- "\"option\":(\\d+)," + // 4
923- "\"eventname\":\"([^\"]+)\"," + // 5
924- "\"network\":(\\d+)," + // 6
925- "\"ch\":\"([^\"]+)\"," + // 7
926- "\"repeat\":(\\d+)," + // 8
927- "\"datetime\":(\\d+)," + // 9
928- "\"duration\":(\\d+)," + // 10
929- "\"conflictstart\":(\\d+)," + // 11
930- "\"conflictend\":(\\d+)," + // 12
931- "\"recording\":(\\d+)," + // 13
932- "\"last\":(\\d+)," + // 14
933- "\"mochidashi\":(\\d+)\\}") // 15
934- .matcher(response);
935-
936- while ( ma.find() ) {
937- // 個々のデータを取り出す
938- ReserveList entry = new ReserveList();
939-
940- String id = ma.group(2);
941- String exec = ma.group(3);
942- String option = ma.group(4);
943- String eventname = ma.group(5);
944- String network = ma.group(6);
945- String ch = ma.group(7);
946- String repeat = ma.group(8);
947- String datetime = ma.group(9);
948- String duration = ma.group(10);
949- String recording = ma.group(13);
950- String mochidashi = ma.group(15);
951-
952- // 予約ID
953- entry.setId(id);
954-
955- // 基本情報をセットする
956- setReserveBasicInfo(entry, exec, option, eventname, network, ch, repeat, datetime, duration);
957-
958- // 持ち出し
959- String portable_name = value2text(portable, mochidashi);
960- entry.setRec_portable(portable_name);
961-
962- // 予約情報を保存
963- list.add(entry.clone());
964- }
965-
966- return(list);
967- }
968-
969- /***
970- * 予約の基本情報をセットする
971- */
972- protected void setReserveBasicInfo(ReserveList entry, String exec, String option, String eventname,
973- String network, String ch, String repeat, String datetime, String duration){
974- // 実行ON/OFF
975- entry.setExec(exec.equals(EXEC_YES));
976-
977- // オプション(1=日付指定, 2=PGM指定)
978- entry.setRec_option(option);
979-
980- // 開始日、終了日
981- int nbsecs = Integer.parseInt(datetime); // 開始日時(UNIX時間/1000)
982- int secs = Integer.parseInt(duration); // 録画時間(sec)
983- int nesecs = nbsecs + secs; // 終了日時(UNIX時間/1000)
984- Date bdate = new Date(nbsecs*1000L); // 開始日時(Date)
985- Date edate = new Date(nesecs*1000L); // 終了日時(Date)
986-
987- SimpleDateFormat sfd = new SimpleDateFormat("yyyy/MM/dd(E)", Locale.JAPAN);
988- String pattern = sfd.format(bdate);
989-
990- // 繰り返しパターン
991- String pid = String.valueOf(RPTPTN_ID_BYDATE);
992-
993- if (!repeat.equals(REPEAT_NONE)){
994- pattern = value2text(tvsPatternName, repeat);
995- pid = value2text(tvsPatternCode, repeat);
996- }
997-
998- entry.setRec_pattern_id(Integer.parseInt(pid));
999- entry.setRec_pattern(pattern);
1000-
1001- // 開始、終了時刻
1002- SimpleDateFormat sfh = new SimpleDateFormat("HH");
1003- SimpleDateFormat sfm = new SimpleDateFormat("mm");
1004- String ahh = sfh.format(bdate);
1005- String amm = sfm.format(bdate);
1006- String zhh = sfh.format(edate);
1007- String zmm = sfm.format(edate);
1008- entry.setAhh(ahh);
1009- entry.setAmm(amm);
1010- entry.setZhh(zhh);
1011- entry.setZmm(zmm);
1012-
1013- // 次の録画日などを計算する
1014- entry.setRec_nextdate(CommonUtils.getNextDate(entry));
1015- entry.setRec_min(String.valueOf(secs/60));
1016- getStartEndDateTime(entry);
1017-
1018- // チューナーは固定
1019- entry.setTuner("R1");
1020-
1021- // 録画モードもとりあえず固定(後で詳細情報で上書きする)
1022- entry.setRec_mode(RECMODE_NAME_DR);
1023-
1024- // タイトル
1025- String title = unescapeJavaString(eventname);
1026- entry.setTitle(title);
1027- entry.setTitlePop(TraceProgram.replacePop(title));
1028-
1029- // チャンネル
1030- entry.setChannel(getFormalChFromNetworkAndCh(network, ch));
1031-
1032- // 放送局名変換
1033- entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
1034- }
1035-
1036- /*
1037- * network と ch から 正式なチャンネルIDを取得する
1038- */
1039- protected String getFormalChFromNetworkAndCh(String network, String ch){
1040- // チャンネル
1041- switch(network){
1042- case NETWORK_UVD:
1043- break;
1044- case NETWORK_BSD:
1045- ch = CHPREFIX_BS + ch;
1046- break;
1047- case NETWORK_CSD:
1048- ch = CHPREFIX_CS + ch;
1049- break;
1050- case NETWORK_L1:
1051- ch = CH_L1;
1052- break;
1053- case NETWORK_L2:
1054- ch = CH_L2;
1055- break;
1056- case NETWORK_L3:
1057- ch = CH_L3;
1058- break;
1059- case NETWORK_L4:
1060- ch = CH_L4;
1061- break;
1062- case NETWORK_NET:
1063- ch = CH_NET;
1064- break;
1065- }
1066-
1067- return ch;
1068- }
1069-
1070- /*
1071- * 予約詳細情報を取得する
1072- */
1073- protected boolean getReserveDetail( ReserveList r) {
1074- // おまじない
1075- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1076-
1077- String id = r.getId();
1078- String response = "";
1079-
1080- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadReserveDetailData.php?reserve_id=" + id, null, thisEncoding);
1081-// String header = d[0];
1082- response= d[1];
1083-
1084- if (response == null) {
1085- errmsg = ERRID+ERRMSG_NORESPONSE;
1086- System.out.println(errmsg);
1087- return(false);
1088- }
1089-
1090- // {"NETdeNAVI":{"reserveInfo":{"id":103,"exec":1,"option":2,"eventname":"Re:CREATORS(レクリエイターズ)",
1091- // "network":0,"chnum":91,"branchexists":false,"branchnum":0,"ch":"091","repeat":64,"datetime":1495290600,"duration":1800,
1092- // "conflictstart":0,"conflictend":0,"drive_id":512,"folder_id":33,"recmode":0,"mochidashi":0,"video_es":0,"audio_es":0,
1093- // "data_es":0,"recording":0}}}
1094- Matcher ma = Pattern.compile("\\{" +
1095- "\"id\":(\\d+)," + // 1
1096- "\"exec\":(\\d+)," + // 2
1097- "\"option\":(\\d+)," + // 3
1098- "\"eventname\":\"([^\"]+)\"," + // 4
1099- "\"network\":(\\d+)," + // 5
1100- "\"chnum\":(\\d+)," + // 6
1101- "\"branchexists\":(false|true)," + // 7
1102- "\"branchnum\":(\\d+)," + // 8
1103- "\"ch\":\"([^\"]+)\"," + // 9
1104- "\"repeat\":(\\d+)," + // 10
1105- "\"datetime\":(\\d+)," + // 11
1106- "\"duration\":(\\d+)," + // 12
1107- "\"conflictstart\":(\\d+)," + // 13
1108- "\"conflictend\":(\\d+)," + // 14
1109- "\"drive_id\":(\\d+)," + // 15
1110- "\"folder_id\":(\\d+)," + // 16
1111- "\"recmode\":(\\d+)," + // 17
1112- "\"mochidashi\":(\\d+)," + // 18
1113- "\"video_es\":(\\d+)," + // 19
1114- "\"audio_es\":(\\d+)," + // 20
1115- "\"data_es\":(\\d+)," + // 21
1116- "\"recording\":(\\d+)\\}") // 22
1117- .matcher(response);
1118-
1119- if ( !ma.find() ) {
1120- reportProgress(ERRID+"予約詳細情報が取得できません.ID=" + id);
1121- return (false);
1122- }
1123-
1124- String exec = ma.group(2);
1125- String option = ma.group(3);
1126- String eventname = ma.group(4);
1127- String network = ma.group(5);
1128- // chnum
1129- // branchexists
1130- // branchnum
1131- String ch = ma.group(9);
1132- String repeat = ma.group(10);
1133- String datetime = ma.group(11);
1134- String duration = ma.group(12);
1135- // conflictstart
1136- // conflictend
1137- String device_id = ma.group(15);
1138- String folder_id = ma.group(16);
1139- String recmode = ma.group(17);
1140- String mochidashi = ma.group(18);
1141- // video_es
1142- // audio_es
1143- // data_es
1144- // recording
1145-
1146- // 基本情報をセットする
1147- String tuner = r.getTuner();
1148- setReserveBasicInfo(r, exec, option, eventname, network, ch, repeat, datetime, duration);
1149- // チューナはそのまま
1150- r.setTuner(tuner);
1151-
1152- // 保存先ドライブ
1153- String device_name = value2text(device, device_id);
1154- r.setRec_device(device_name);
1155-
1156- // 保存先フォルダー
1157- String folder_name = value2text(folder, device_id + ":" + folder_id);
1158- r.setRec_folder(folder_name);
1159-
1160- // 録画モード
1161- String recmode_name = value2text(vrate, recmode);
1162- r.setRec_mode(recmode_name);
1163-
1164- // 持ち出し
1165- String portable_name = value2text(portable, mochidashi);
1166- r.setRec_portable(portable_name);
1167-
1168- return(true);
1169- }
1170-
1171- /**
1172- * レコーダーから取得できない情報は直接コピー(既存のリストから探して)
1173- */
1174- protected void copyAttributesT2007(ReserveList to, ArrayList<ReserveList> fromlist) {
1175- ReserveList olde = null;
1176- for ( ReserveList from : fromlist ) {
1177- if ( from.getId() != null && from.getId().equals(to.getId()) ) {
1178- copyAttributeT2007(to, olde = from);
1179- break;
1180- }
1181- }
1182-
1183- // DIGAの終了時間"未定"対応だけど、別にDIGAかどうか確認したりはしない。
1184- setAttributesDiga(to,olde);
1185- }
1186-
1187- /**
1188- * レコーダーから取得できない情報は直接コピー(既存エントリから直に)
1189- */
1190- protected void copyAttributeT2007(ReserveList to, ReserveList from) {
1191- // 鯛ナビの内部フラグ
1192- to.setAutocomplete(from.getAutocomplete());
1193-
1194- // 予約一覧からは取得できない情報
1195- to.setDetail(from.getDetail());
1196- to.setRec_genre(from.getRec_genre());
1197- to.setRec_autodel(from.getRec_autodel());
1198-
1199- // BZ700以降の取得一覧から取得できない画質の対応
1200- if (to.getRec_mode().equals("")) {
1201- to.setRec_mode(from.getRec_mode());
1202- }
1203- }
1204-
1205- //
1206- protected void translateAttributeTuner(ReserveList entry) {
1207- if (entry.getTuner().startsWith("TS")) {
1208- entry.setTuner(entry.getTuner().replaceFirst("^TS", "DR"));
1209- }
1210- }
1211-
1212- /*
1213- * 登録/削除要求のPOSTデータを生成する
1214- */
1215- protected String createPostData(ReserveList r, String idBefore) {
1216-
1217- String id = idBefore;
1218- String exec = EXEC_YES;
1219- String option = OPTION_PROGRAM;
1220- String eventname = "";
1221- String network = NETWORK_UVD;
1222- String chnum = "0";
1223- String branchexists = BRANCH_NO;
1224- String branchnum = "0";
1225- String ch = "";
1226- String repeat = REPEAT_NONE;
1227- String datetime = "";
1228- String duration = "";
1229- String conflictstart = "0";
1230- String conflictend = "0";
1231- String drive_id = DEVICE_HDD;
1232- String folder_id = FOLDER_NONE;
1233- String recmode = RECMODE_DR;
1234- String mochidashi = MOCHIDASHI_NONE;
1235- String video_es = "0";
1236- String audio_es = "0";
1237- String data_es = "0";
1238- String recording = "0";
1239-
1240- boolean bUpdate = !idBefore.equals("");
1241-
1242- // スキップ
1243- if (!r.getExec())
1244- exec = EXEC_NO;
1245-
1246- // オプション
1247- if (bUpdate)
1248- option = r.getRec_option();
1249-
1250- // タイトル(eventname)
1251- try {
1252- if (!r.getAutocomplete())
1253- eventname = URLEncoder.encode(CommonUtils.substringrb(r.getTitle(),80), thisEncoding);
1254- } catch (UnsupportedEncodingException e) {
1255- // TODO Auto-generated catch block
1256- e.printStackTrace();
1257- }
1258-
1259- // チャンネル(ch)
1260- String channel = cc.getCH_WEB2CODE(r.getCh_name());
1261- ch = cc.getCH_CODE2REC(channel);
1262-
1263- // 枝番有(branchexists,branchnum)
1264- branchnum = text2value(tvsBranch, channel);
1265- branchexists = Integer.parseInt(branchnum) > 0 ? BRANCH_YES : BRANCH_NO;
1266-
1267- // ネットワーク(network)
1268- String typ = text2value(chtype, channel);
1269- switch(typ){
1270- case CHTYPE_UVD:
1271- network = NETWORK_UVD; // 地上デジタル
1272- break;
1273- case CHTYPE_BSD:
1274- network = NETWORK_BSD; // BSデジタル
1275- ch = ch.substring(CHPREFIX_BS.length());
1276- break;
1277- case CHTYPE_CSD:
1278- network = NETWORK_CSD; // 110度CSデジタル
1279- ch = ch.substring(CHPREFIX_CS.length());
1280- break;
1281- case CHTYPE_L1:
1282- network = NETWORK_L1; // 外部入力(L1)
1283- break;
1284- case CHTYPE_L2:
1285- network = NETWORK_L2; // 外部入力(L2)
1286- break;
1287- case CHTYPE_L3:
1288- network = NETWORK_L3; // 外部入力(L3)
1289- break;
1290- case CHTYPE_L4:
1291- network = NETWORK_L4; // 外部入力(L4)
1292- break;
1293- case CHTYPE_NET:
1294- network = NETWORK_NET; // 外部入力(NET)
1295- break;
1296- default:
1297- // 普通ここには落ちない
1298- if (ch.startsWith("C")) {
1299- network = NETWORK_L1; // "C***"は外部入力(L1)
1300- }
1301- else if (ch.startsWith("SP")) {
1302- network = NETWORK_L3; // "SP***"は外部入力(L3)
1303- }
1304- else {
1305- network = NETWORK_L2; // 未定義は全部外部入力(L2)
1306- }
1307- break;
1308- }
1309-
1310- chnum = ch;
1311-
1312- // 繰り返しパターン(repeat)
1313- String pattern = r.getRec_pattern();
1314- repeat = text2value(tvsPatternName, pattern);
1315- if (repeat.equals(""))
1316- repeat = REPEAT_NONE; // 繰り返しなし
1317-
1318- // 開始日時(datetime)
1319- if (!repeat.equals(REPEAT_NONE)){
1320- String pid = value2text(tvsPatternCode, repeat);
1321- r.setRec_pattern_id(Integer.parseInt(pid));
1322-
1323- pattern = CommonUtils.getNextDate(r);
1324- }
1325-
1326- Matcher ma = Pattern.compile("^(\\d+)/(\\d+)/(\\d+)").matcher(pattern);
1327- if (ma.find()){
1328- String startDateTime = String.format("%04d%02d%02d %02d:%02d:00",
1329- Integer.parseInt(ma.group(1)),
1330- Integer.parseInt(ma.group(2)),
1331- Integer.parseInt(ma.group(3)),
1332- Integer.parseInt(r.getAhh()),
1333- Integer.parseInt(r.getAmm()));
1334-
1335- SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
1336-
1337- // Date型に変換する
1338- Date date = new Date();
1339- try {
1340- date = sdf.parse(startDateTime);
1341- } catch (ParseException e) {
1342- // TODO 自動生成された catch ブロック
1343- e.printStackTrace();
1344- }
1345-
1346- // UNIX時刻/1000を取得する(単位秒)
1347- datetime = String.valueOf(date.getTime()/1000);
1348- }
1349-
1350- // 録画時間(duration)(単位秒)
1351- String min = CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm());
1352- duration = String.valueOf(Integer.parseInt(min)*60);
1353-
1354- // ドライブ(drive_id)/フォルダ(folder_id)
1355- drive_id = text2value(device, r.getRec_device());
1356- String s = text2value(folder, r.getRec_folder());
1357-
1358- // <drive_id>:<folder_id> から<folder_id>を取り出す。ただし「指定なし」は"0"
1359- int idx = s.indexOf(':');
1360- if (idx != -1)
1361- folder_id = s.substring(idx+1);
1362-
1363- // 録画モード(recmode)
1364- recmode = text2value(vrate, r.getRec_mode());
1365-
1366- // 持ち出し
1367- mochidashi = text2value(portable, r.getRec_portable());
1368-
1369- String postData =
1370- "reserveInfo[id]=" + id + "&" +
1371- "reserveInfo[exec]=" + exec + "&" +
1372- "reserveInfo[option]=" + option + "&" +
1373- "reserveInfo[eventname]=" + eventname + "&" +
1374- "reserveInfo[network]=" + network + "&" +
1375- "reserveInfo[chnum]=" + chnum + "&" +
1376- "reserveInfo[branchexists]=" + branchexists + "&" +
1377- "reserveInfo[branchnum]=" + branchnum + "&" +
1378- "reserveInfo[ch]=" + ch + "&" +
1379- "reserveInfo[repeat]=" + repeat + "&" +
1380- "reserveInfo[datetime]=" + datetime + "&" +
1381- "reserveInfo[duration]=" + duration + "&" +
1382- "reserveInfo[conflictstart]=" + conflictstart + "&" +
1383- "reserveInfo[conflictend]=" + conflictend + "&" +
1384- "reserveInfo[drive_id]=" + drive_id + "&" +
1385- "reserveInfo[folder_id]=" + folder_id + "&" +
1386- "reserveInfo[recmode]=" + recmode + "&" +
1387- "reserveInfo[mochidashi]=" + mochidashi + "&" +
1388- "reserveInfo[video_es]=" + video_es + "&" +
1389- "reserveInfo[audio_es]=" + audio_es + "&" +
1390- "reserveInfo[data_es]=" + data_es + "&" +
1391- "reserveInfo[recording]=" + recording;
1392-
1393- if (getDebug())
1394- System.out.println("PostData=[" + postData + "]");
1395-
1396- return postData;
1397- }
1398-
1399- /*
1400- * 予約登録画面の初期化情報を取得する。これを呼ばないと、後で登録要求した時にERR_EXCLUSIVEエラーになる
1401- */
1402- protected boolean loadDialogInitData( String id) {
1403- // おまじない
1404- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1405-
1406- reportProgress(MSGID+"予約登録画面を初期化します.");
1407- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadDialogInitializationData.php?schId=0", null, thisEncoding);
1408-// String header = d[0];
1409- String response= d[1];
1410-
1411- if (response == null) {
1412- errmsg = ERRID+ERRMSG_NORESPONSE;
1413- System.out.println(errmsg);
1414- return(false);
1415- }
1416-
1417- return(true);
1418- }
1419-
1420- /*
1421- * 登録要求の応答をチェックする
1422- */
1423- protected boolean checkReserveResponse( ReserveList r, String response ) {
1424- if (response == null) {
1425- errmsg = ERRID+ERRMSG_NORESPONSE;
1426- return(false);
1427- }
1428-
1429- // {"result":24,"reason":"ERR_EXCLUSIVE","schid":"0"}
1430- Matcher ma = Pattern.compile("\\{" +
1431- "\"result\":(\\d+)," +
1432- "\"reason\":\"([^\"]*)\"," +
1433- "\"schid\":\"(\\d+)\"\\}").matcher(response);
1434-
1435- if ( ma.find() ) {
1436- String result = ma.group(1);
1437- String reason = ma.group(2);
1438- String schid = ma.group(3);
1439-
1440- switch(result){
1441- case RESULT_OK:
1442- break;
1443- case "3":
1444- reportProgress("3:番組が見つからなかったため、日時指定予約として登録しました。");
1445- break;
1446- case "4":
1447- reportProgress("4:予約が重複しています。");
1448- break;
1449- case "5":
1450- reportProgress("5:持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1451- break;
1452- case "6":
1453- reportProgress("6:持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1454- break;
1455- case "7":
1456- reportProgress("7:番組が見つからなかったため、日時指定予約として登録しました。");
1457- break;
1458- case "8":
1459- reportProgress("8:番組が見つからなかったため、日時指定予約として登録しました。\n" +
1460- "持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1461- break;
1462- case "9":
1463- reportProgress("9:番組が見つからなかったため、日時指定予約として登録しました。\n" +
1464- "持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1465- break;
1466- default:
1467- errmsg = ERRID+"予約登録に失敗しました。(result=" + result + ",reason=" + reason + ")";
1468- reportProgress(errmsg);
1469- return(false);
1470- }
1471-
1472- if (! schid.equals("0")){
1473- r.setId(schid);
1474- reportProgress(MSGID+"予約IDは"+r.getId()+"です。");
1475- }
1476-
1477- if (!result.equals(RESULT_OK))
1478- getReserveDetail(r);
1479- }
1480- else{
1481- errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1482- reportProgress(errmsg);
1483- return(false);
1484- }
1485-
1486- return(true);
1487- }
1488-
1489- /*
1490- * 予約内容を調整する
1491- */
1492- protected void adjustReserve(ReserveList r) {
1493- // 予約パターンID
1494- r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));
1495-
1496- // 次回予定日
1497- r.setRec_nextdate(CommonUtils.getNextDate(r));
1498-
1499- // 録画長
1500- r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));
1501-
1502- // 開始日時・終了日時
1503- getStartEndDateTime(r);
1504- }
1505-
1506- /*
1507- * フォルダの作成ないしフォルダ名称を変更する
1508- */
1509- protected boolean setFolderName(String device_id, String fol_id, String folder_name) {
1510- String action = fol_id != null ? "フォルダ名更新" : "フォルダ作成";
1511- String folder_id = extractFolderID(fol_id);
1512-
1513- String fnameEnc = "";
1514- try {
1515- fnameEnc = URLEncoder.encode(CommonUtils.substringrb(folder_name,80), thisEncoding);
1516- } catch (UnsupportedEncodingException e) {
1517- // TODO 自動生成された catch ブロック
1518- e.printStackTrace();
1519- }
1520-
1521- String pstr =
1522- "drive_id=" + device_id + "&" +
1523- "folder_id=" + (folder_id != null ? folder_id : "NEWFOLDER") + "&" +
1524- "folder_name=" + fnameEnc;
1525-
1526- for (int n=0; n<3; n++){
1527- // おまじない
1528- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1529-
1530- // RDへ情報送信
1531- reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
1532- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/folderset/SetFolderName.php?" + pstr, null, thisEncoding);
1533-// String header = d[0];
1534- String response = d[1];
1535-
1536- // レスポンスから処理結果を取得する
1537- String [] rc = getReasonFromResponse( response );
1538- if (rc == null) {
1539- errmsg = ERRID+ERRMSG_NORESPONSE;
1540- return(false);
1541- }
1542-
1543- String result = rc[0];
1544- String reason = rc[1];
1545-
1546- if (result.equals(RESULT_OK))
1547- break;
1548-
1549- if (result.equals(RESULT_OTHER) || result.equals(RESULT_BUSY)){
1550- if (mountUsbDrive(device_id))
1551- continue;
1552- }
1553-
1554- errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
1555- return(false);
1556- }
1557-
1558- // フォルダ一覧を取得し直す
1559- setSettingFolders();
1560- saveFolders();
1561-
1562- // タイトルに含まれるフォルダ名を更新する
1563- updateFolderNameOfTitles();
1564-
1565- return (true);
1566- }
1567-
1568- /*
1569- * タイトルに含まれるフォルダ名を更新する
1570- */
1571- protected void updateFolderNameOfTitles(){
1572- ArrayList<TitleInfo> list = getTitles();
1573-
1574- for (TitleInfo ti : list){
1575- ArrayList<TextValueSet> ts = ti.getRec_folder();
1576-
1577- for (TextValueSet t : ts){
1578- t.setText(value2text(folder, t.getValue()));
1579- }
1580-
1581- ti.setRec_folder(ts);
1582- }
1583-
1584- setTitles(list);
1585-
1586- saveTitles(DEVICE_ID);
1587- }
1588- /*
1589- * USB-HDDをマウントする
1590- */
1591- protected boolean mountUsbDrive(String device_id) {
1592- reportProgress(MSGID+"ドライブをマウントします:"+device_id);
1593-
1594- String pstr = "drive_id=" + device_id;
1595-
1596- // おまじない
1597- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1598-
1599- // RDへ情報送信
1600- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/ChangeMountedUSB.php?" + pstr, null, thisEncoding);
1601-// String header = d[0];
1602- String response = d[1];
1603-
1604- // 結果の確認
1605- if ( !checkGeneralResponse( "USB HDDのマウント", response) ){
1606- return(false);
1607- }
1608-
1609- return true;
1610- }
1611-
1612- /*
1613- * 一般的な応答をチェックする
1614- */
1615- protected boolean checkGeneralResponse( String action, String response){
1616- String [] rc = getReasonFromResponse( response );
1617-
1618- if (rc == null) {
1619- errmsg = ERRID+ERRMSG_NORESPONSE;
1620- return(false);
1621- }
1622-
1623- String result = rc[0];
1624- String reason = rc[1];
1625-
1626- if (! result.equals(RESULT_OK)){
1627- errmsg = action + "に失敗しました。(result=" + result + ",reason=" + reason + ")";
1628- reportProgress(errmsg);
1629- return(false);
1630- }
1631-
1632- return(true);
1633- }
1634-
1635- /*
1636- * 応答メッセージから結果を取得する
1637- */
1638- protected String[] getReasonFromResponse( String response){
1639- if (response == null) {
1640- return(null);
1641- }
1642-
1643- // 先頭部分をチェックする
1644- Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1645- if ( ! mh.find()) {
1646- return (null);
1647- }
1648-
1649- // 応答メッセージをパースする
1650- // {"result":24,"reason":"ERR_EXCLUSIVE"}
1651- Matcher ma = Pattern.compile("\\{" +
1652- "\"result\":(\\d+)," +
1653- "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1654- if ( !ma.find() )
1655- return(null);
1656-
1657- String [] rc = new String[2];
1658-
1659- rc[0] = ma.group(1);
1660- rc[1] = ma.group(2);
1661-
1662- return(rc);
1663- }
1664-
1665- /*
1666- * TitleInfoを startDateTime, cotent_id順にソートする
1667- *
1668- */
1669- public class TitleInfoComparator implements Comparator<TitleInfo>{
1670-
1671- @Override
1672- public int compare(TitleInfo p1, TitleInfo p2) {
1673- int rc = p1.getStartDateTime().compareTo(p2.getStartDateTime());
1674- if (rc != 0){
1675- return rc;
1676- }
1677-
1678- return p1.getSerial() - p2.getSerial();
1679- }
1680- }
1681- /*
1682- * タイトル一覧を取得する
1683- */
1684- protected ArrayList<TitleInfo> getTitleList(String device_id, boolean mountedOnly) {
1685- String str = "drive=" + device_id + "&org_pl=0";
1686- String response = "";
1687-
1688- for (int n=0; n<3; n++){
1689- // おまじない
1690- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1691-
1692- // RDからタイトル一覧を取り出す
1693- reportProgress(MSGID+"タイトル一覧を取得します:"+device_id);
1694- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadTitleList.php?" + str, null, thisEncoding);
1695-// String header = d[0];
1696- response= d[1];
1697-
1698- if (response == null) {
1699- errmsg = ERRID+ERRMSG_NORESPONSE;
1700- return(null);
1701- }
1702-
1703- // 先頭部分をチェックする
1704- Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1705- if ( ! mh.find()) {
1706- errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1707- return(null);
1708- }
1709-
1710- // 応答メッセージをパースする
1711- // {"result":24,"reason":"ERR_EXCLUSIVE"}
1712- Matcher mr = Pattern.compile("\\{" +
1713- "\"result\":(\\d+)," +
1714- "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1715-
1716- if ( !mr.find() )
1717- break;
1718-
1719- String result = mr.group(1);
1720- String reason = mr.group(2);
1721-
1722- if (result.equals("1")){
1723- if (mountedOnly){
1724- reportProgress(MSGID+"ドライブがマウントされていないので取得を中止します:"+device_id);
1725- return(null);
1726- }
1727-
1728- if (mountUsbDrive(device_id))
1729- continue;
1730-
1731- return(null);
1732- }
1733-
1734- errmsg = "録画タイトルの取得に失敗しました。(result=" + result + ",reason=" + reason + ")";
1735- reportProgress(errmsg);
1736- return(null);
1737- }
1738-
1739- ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
1740-
1741- // {"num":4,"id":"51a6a4900016","titlename":"[映][SS]スティーヴン・キング 骨の袋[前編]",
1742- // "folders":[{"folder_id":9}],"genres":[{"genrecode":15}],"ch":"BS201",
1743- // "datetime":1369843200,"duration":4858,"newflag":true,"autorecflag":false},
1744- Matcher ma = Pattern.compile("\\{" +
1745- "\"num\":(\\d+)," + // 1
1746- "\"id\":\"([^\"]+)\"," + // 2
1747- "\"titlename\":\"([^\"]+)\"," + // 3
1748- "\"folders\":\\[([^\\]]*)\\]," + // 4
1749- "\"genres\":\\[([^\\]]*)\\]," + // 5
1750- "\"network\":(\\d+)," + // 6
1751- "\"ch\":\"([^\"]+)\"," + // 7
1752- "\"datetime\":(\\d+)," + // 8
1753- "\"duration\":(\\d+)," + // 9
1754- "\"newflag\":(false|true)," + // 10
1755- "\"autorecflag\":(false|true)," + // 11
1756- "\"autodelflag\":(false|true)," + // 12
1757- "\"protection\":(false|true)," + // 13
1758- "\"mochidashiState\":(\\d+)," + // 14
1759- "\"recmode\":(\\d+)," + // 15
1760- "\"recording\":(false|true)," + // 16
1761- "\"portableEnable\":(false|true)" + // 17
1762- "\\}").matcher(response);
1763-
1764- int serial = 0;
1765-
1766- while ( ma.find() ) {
1767- // 個々のデータを取り出す
1768- TitleInfo entry = new TitleInfo();
1769-
1770- String id = ma.group(2);
1771- String titlename = unescapeJavaString(ma.group(3));
1772- String folders = ma.group(4);
1773- String genres = ma.group(5);
1774- String network = ma.group(6);
1775- String ch = ma.group(7);
1776- String datetime = ma.group(8);
1777- String duration = ma.group(9);
1778- String recmode = ma.group(15);
1779- Boolean recording = ma.group(16).equals("true");
1780-// String newflag = ma.group(9);
1781-// String autorecflag = ma.group(10);
1782-
1783- // チャンネルを変換する
1784- ch = getFormalChFromNetworkAndCh(network, ch);
1785-
1786- // タイトルID
1787- entry.setId(id);
1788-
1789- // すでに同じIDのタイトルがある場合はその情報を引き継ぐ
1790- TitleInfo tiOld = getTitleInfo(id);
1791- if (tiOld != null)
1792- entry = tiOld.clone();
1793-
1794- // 基本情報をセットする
1795- entry.setSerial(++serial);
1796- setTitleBasicInfo(entry, titlename, ch, datetime, duration, device_id, folders, genres);
1797-
1798- // 録画モード
1799- String recmode_name = value2text(vrate, recmode);
1800- entry.setRec_mode(recmode_name);
1801-
1802-
1803- entry.setRecording(recording);
1804-
1805- // 予約情報を保存
1806- list.add(entry.clone());
1807- }
1808-
1809- list.sort(new TitleInfoComparator());
1810-
1811- return(list);
1812- }
1813-
1814- /***
1815- * タイトルの基本情報をセットする
1816- */
1817- protected void setTitleBasicInfo(TitleInfo entry, String titlename,
1818- String ch, String datetime, String duration, String device_id, String folders, String genres){
1819-
1820- // 開始日、終了日
1821- int nbsecs = Integer.parseInt(datetime); // 開始日時(UNIX時間/1000)
1822- int secs = Integer.parseInt(duration); // 録画時間(sec)
1823- int nesecs = nbsecs + secs; // 終了日時(UNIX時間/1000)
1824- Date bdate = new Date(nbsecs*1000L); // 開始日時(Date)
1825- Date edate = new Date(nesecs*1000L); // 終了日時(Date)
1826-
1827- SimpleDateFormat sfdm = new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.JAPAN);
1828- entry.setStartDateTime(sfdm.format(bdate));
1829- entry.setEndDateTime(sfdm.format(edate));
1830-
1831- // 開始、終了時刻
1832- SimpleDateFormat sfh = new SimpleDateFormat("HH");
1833- SimpleDateFormat sfm = new SimpleDateFormat("mm");
1834- String ahh = sfh.format(bdate);
1835- String amm = sfm.format(bdate);
1836- String zhh = sfh.format(edate);
1837- String zmm = sfm.format(edate);
1838- entry.setAhh(ahh);
1839- entry.setAmm(amm);
1840- entry.setZhh(zhh);
1841- entry.setZmm(zmm);
1842-
1843-
1844- // 次の録画日などを計算する
1845- SimpleDateFormat sfd = new SimpleDateFormat("yyyy/MM/dd(E)", Locale.JAPAN);
1846- entry.setRec_date(sfd.format(bdate));
1847- entry.setRec_min(String.valueOf(secs/60));
1848-
1849- // タイトル
1850- entry.setTitle(titlename);
1851-
1852- // チャンネル
1853- entry.setChannel(ch);
1854-
1855- // デバイス
1856- String device_name = value2text(device, device_id);
1857- entry.setRec_device(device_name);
1858-
1859- // フォルダー
1860- entry.setRec_folder(parseFolders(device_id, folders));
1861-
1862- // ジャンル
1863- entry.setRec_genre(parseGenres(genres));
1864- }
1865-
1866- /*
1867- * タイトル詳細情報を取得する
1868- */
1869- protected boolean getTitleDetail( TitleInfo t) {
1870-
1871- String id = t.getId();
1872- String device_name = t.getRec_device();
1873- String device_id = text2value(device, device_name);
1874- String title_id = t.getId();
1875- String str = "drive_id=" + device_id + "&title_id=" + title_id;
1876- String response = "";
1877-
1878- for (int n=0; n<3; n++){
1879- // おまじない
1880- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1881-
1882- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadTitleDetailData.php?" + str, null, thisEncoding);
1883-// String header = d[0];
1884- response= d[1];
1885-
1886- if (response == null) {
1887- errmsg = ERRID+ERRMSG_NORESPONSE;
1888- System.out.println(errmsg);
1889- return(false);
1890- }
1891-
1892- // 先頭部分をチェックする
1893- Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1894- if ( ! mh.find()) {
1895- errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1896- return (false);
1897- }
1898-
1899- // 応答メッセージをパースする
1900- // {"result":24,"reason":"ERR_EXCLUSIVE"}
1901- Matcher mr = Pattern.compile("\\{" +
1902- "\"result\":(\\d+)," +
1903- "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1904-
1905- if ( !mr.find() )
1906- break;
1907-
1908- String result = mr.group(1);
1909- String reason = mr.group(2);
1910-
1911- if (result.equals("1")){
1912- if (mountUsbDrive(device_id))
1913- continue;
1914- }
1915-
1916- errmsg = "タイトル詳細情報の取得に失敗しました。(result=" + result + ",reason=" + reason + ")";
1917- reportProgress(errmsg);
1918- return(false);
1919- }
1920-
1921- // {"NETdeNAVI":{"titleDetailInfo":{"id":"589285bc000b","unique_id":"589285bc000b",
1922- // "titlename":"この素晴らしい世界に祝福を!2 第4話「この貴族の令嬢に良縁を!」",
1923- // "folders":[{"folder_id":7}],"ch":"091","recmode":"DR","genres":[{"genrecode":7}],
1924- // "datetime":1485965100,"duration":1807,"copycount":10,"dlnaObjectID":"RD_0001000B_0000010200",
1925- // "recording":false,
1926- // "chapter":[{"chaptername":"@","duration":39,"changeFlag":0},
1927- // {"chaptername":"","duration":168,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1928- // {"chaptername":"","duration":509,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1929- // {"chaptername":"","duration":708,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1930- // {"chaptername":"","duration":17,"changeFlag":0},{"chaptername":"@","duration":179,"changeFlag":0}]}}}
1931- Matcher ma = Pattern.compile("\\{" +
1932- "\"id\":\"([^\"]+)\"," + // 1
1933- "\"unique_id\":\"([^\"]+)\"," + // 2
1934- "\"titlename\":\"([^\"]+)\"," + // 3
1935- "\"folders\":\\[([^\\]]*)\\]," + // 4
1936- "\"network\":(\\d+)," + // 5
1937- "\"chNum\":\"([^\"]+)\"," + // 6
1938- "\"branchNum\":(\\d+)," + // 7
1939- "\"branchNumExists\":(false|true)," +// 8
1940- "\"ch\":\"([^\"]+)\"," + // 9
1941- "\"recmode\":\\{\"id\":(\\d+),\"name\":\"([^\"]+)\"\\}," + // 10,11
1942- "\"genres\":\\[([^\\]]*)\\]," + // 12
1943- "\"datetime\":(\\d+)," + // 13
1944- "\"duration\":(\\d+)," + // 14
1945- "\"copycount\":(\\d+)," + // 15
1946- "\"dlnaObjectID\":\"([^\"]*)\"," + // 16
1947- "\"rating\":(\\d+)," + // 17
1948- "\"recording\":(false|true)," + // 18
1949- "\"chapter\":\\[([^\\]]*)\\]," + // 19
1950- "\"autodel\":(false|true)," + // 20
1951- "\"protection\":(false|true)," + // 21
1952- "\"mochidashiContentId\":\"([^\"]*)\"," + // 22
1953- "\"resumeTime\":(\\d+)," + // 23
1954- "\"shortTimeInfo\":\\[([^\\]]*)\\]," +// 24
1955- "\"detailDescription\":\"([^\"]*)\"," + // 25
1956- "\"profiles\":\\[([^\\]]*)\\]," + // 26
1957- "\"mochidashiState\":(\\d+)," + // 27
1958- "\"defective\":(false|true)," + // 28
1959- "\"portableEnable\":(false|true)" + // 29
1960- "\\}")
1961- .matcher(response);
1962-
1963- if ( !ma.find() ) {
1964- reportProgress(ERRID+"タイトル詳細情報が取得できません.ID=" + id);
1965- return (false);
1966- }
1967-
1968- String unique_id = ma.group(2);
1969- String titlename = unescapeJavaString(ma.group(3));
1970- String folders = ma.group(4);
1971- String ch = ma.group(9);
1972- String recmode = ma.group(11);
1973- String genres = ma.group(12);
1974- String datetime = ma.group(13);
1975- String duration = ma.group(14);
1976- String copycount = ma.group(15);
1977- String dlnaObjectID = ma.group(16);
1978- Boolean recording = ma.group(18).equals("true");
1979-// String recording = ma.group(12);
1980- String chapter = ma.group(19);
1981- String portableEnable = ma.group(29);
1982-
1983- // 基本情報をセットする
1984- setTitleBasicInfo(t, titlename, ch, datetime, duration, device_id, folders, genres);
1985-
1986- // チャプター情報
1987- t.setChapter(parseChapters(chapter));
1988- t.setContentId(unique_id);
1989-
1990- // 録画モード
1991-// String recmode_name = value2text(vrate, recmode);
1992- t.setRec_mode("[" + recmode + "]");
1993-
1994- // それ以外の詳細情報
1995- HashMap<String,String> hmap = new HashMap<String,String>();
1996- hmap.put("copycount", copycount);
1997- hmap.put("dlnaObjectID", dlnaObjectID);
1998- t.setHidden_params(hmap);
1999-
2000- t.setRecording(recording);
2001-
2002- t.setDetailLoaded(true);
2003-
2004- return(true);
2005- }
2006-
2007- /**
2008- * フォルダーのJSONテキストを解析する
2009- */
2010- protected ArrayList<TextValueSet> parseFolders(String device_id, String s) {
2011-
2012- ArrayList<TextValueSet> list = new ArrayList<TextValueSet>();
2013-
2014- // "folders":[{"folder_id":9}],
2015- Matcher ma = Pattern.compile("\\{" +
2016- "\"folder_id\":(\\d+)\\}") // 1
2017- .matcher(s);
2018-
2019- while ( ma.find() ) {
2020- String folder_id = device_id + ":" + ma.group(1);
2021- String folder_name = value2text(folder, folder_id);
2022-
2023- TextValueSet t = new TextValueSet();
2024- t.setText(folder_name);
2025- t.setValue(folder_id);
2026- list.add(t);
2027- }
2028-
2029- return(list);
2030- }
2031-
2032- /**
2033- * ジャンルのJSONテキストを解析する
2034- */
2035- protected ArrayList<TextValueSet> parseGenres(String s) {
2036-
2037- ArrayList<TextValueSet> list = new ArrayList<TextValueSet>();
2038-
2039- // "genres":[{"genrecode":7}],
2040- Matcher ma = Pattern.compile("\\{" +
2041- "\"genrecode\":(\\d+)\\}") // 1
2042- .matcher(s);
2043-
2044- while ( ma.find() ) {
2045- String genre_code = Integer.toHexString(Integer.parseInt(ma.group(1))).toUpperCase();
2046- ProgGenre pg = ProgGenre.getByIEPG(genre_code);
2047- String genre_name = pg != null ? pg.toString() : "";
2048-
2049- TextValueSet t = new TextValueSet();
2050- t.setText(genre_name);
2051- t.setValue(genre_code);
2052- list.add(t);
2053- }
2054-
2055- return(list);
2056- }
2057-
2058- /**
2059- * チャプターのJSONテキストを解析する
2060- */
2061- protected ArrayList<ChapterInfo> parseChapters(String s) {
2062-
2063- ArrayList<ChapterInfo> list = new ArrayList<ChapterInfo>();
2064-
2065- // {"chaptername":"","duration":168,"changeFlag":0},
2066- Matcher ma = Pattern.compile("\\{" +
2067- "\"chaptername\":\"([^\"]*)\"," +
2068- "\"duration\":(\\d+)," +
2069- "\"changeFlag\":(\\d+)\\}")
2070- .matcher(s);
2071-
2072- while ( ma.find() ) {
2073- String chaptername = unescapeJavaString(ma.group(1));
2074- String duration = ma.group(2);
2075- String changeFlag = ma.group(3);
2076-
2077- ChapterInfo c = new ChapterInfo();
2078- c.setName(chaptername);
2079- c.setDuration(Integer.parseInt(duration));
2080- c.setChangeFlag(changeFlag.equals("1"));
2081-
2082- list.add(c);
2083- }
2084-
2085- return(list);
2086- }
2087-
2088- /**
2089- * タイトルの編集をレコーダに通知する。これを先に呼ばないとタイトル情報更新時にエラーになる
2090- */
2091- private boolean notifyTitleEdit(String devid, String ttlid) {
2092- errmsg = "";
2093-
2094- // おまじない
2095- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2096-
2097- // RDへの情報作成
2098- String pstr = "device_id=" + devid + "&title_id=" + ttlid;
2099-
2100- // RDへ情報送信
2101- reportProgress(MSGID+"レコーダーにタイトル編集を通知します.");
2102- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/NotifyTitleEdit.php?" + pstr, null, thisEncoding);
2103-// String header = d[0];
2104- String response = d[1];
2105-
2106- if ( !checkGeneralResponse( "タイトル編集通知", response) ){
2107- return(false);
2108- }
2109-
2110- return(true);
2111- }
2112-
2113- /*
2114- * タイトル更新要求のPOSTデータを生成する
2115- */
2116- protected String createTitlePostData(TitleInfo t, TitleInfo o, String devId) {
2117- String postData = "";
2118- try {
2119- String title = o != null && t.getTitle().equals(o.getTitle()) ? TITLE_NOCHANGED :
2120- // ARIB外字を変換した文字列があれば元の外字に戻す
2121- URLEncoder.encode(AribCharMap.ConvStringToArib(t.getTitle()), thisEncoding);
2122-
2123- postData =
2124- "drive_id=" + devId + "&" +
2125- "title_id=" + t.getId() + "&" +
2126- "titleName=" + title + "&" +
2127- encodeFolders(t.getRec_folder()) + "&" +
2128- "folder_change=1&" +
2129- encodeChapters(t.getChapter());
2130- } catch (UnsupportedEncodingException e) {
2131- // TODO 自動生成された catch ブロック
2132- e.printStackTrace();
2133- }
2134-
2135- return postData;
2136- }
2137-
2138- /**
2139- * フォルダー情報をJSONから変換したPOSTデータの形式にエンコードする
2140- */
2141- protected String encodeFolders(ArrayList<TextValueSet> tvs){
2142- String s = "";
2143- int n=0;
2144- for (TextValueSet t : tvs){
2145- if ( !s.equals("") )
2146- s += "&";
2147- s += "folders[" + String.valueOf(n) + "][folder_id]=" + extractFolderID( t.getValue() );
2148- n++;
2149- }
2150-
2151- return s;
2152- }
2153-
2154- /**
2155- * チャプター情報をJSONから変換したPOSTデータの形式にエンコードする
2156- */
2157- protected String encodeChapters(ArrayList<ChapterInfo> ci){
2158- String s = "";
2159- int n=0;
2160- for (ChapterInfo t : ci){
2161- if ( !s.equals("") )
2162- s += "&";
2163- try {
2164- s += "chapters[" + String.valueOf(n) + "][chnum]=" + String.valueOf(n) + "&" +
2165- "chapters[" + String.valueOf(n) + "][chname]=" + URLEncoder.encode(t.getName(), thisEncoding);
2166- } catch (UnsupportedEncodingException e) {
2167- // TODO 自動生成された catch ブロック
2168- e.printStackTrace();
2169- }
2170- n++;
2171- }
2172-
2173- return s;
2174- }
2175-
2176- /*
2177- * 固定の各種設定情報を初期化する
2178- */
2179- protected void setSettingFixed() {
2180- // 録画設定
2181- setSettingVrate(vrate);
2182-
2183- // パターン情報
2184- setSettingPattern(tvsPatternCode, tvsPatternName);
2185-
2186- // エンコーダー
2187- setSettingEncoder(encoder);
2188-
2189- // 持ち出し情報
2190- setSettingPortable(portable);
2191- }
2192-
2193- /*
2194- * 録画モードを初期化する
2195- */
2196- protected void setSettingVrate(ArrayList<TextValueSet> tvs) {
2197- // {"str":"DR","val":0}, {"str":"AF","val":64}, {"str":"AN","val":65}, {"str":"AS","val":66},
2198- // {"str":"AL","val":67}, {"str":"AE","val":68},
2199- // {"str":"XP","val":32}, {"str":"SP","val":33}, {"str":"LP","val":34}, {"str":"EP","val":35},
2200- // {"str":"自動(HD 4.7GB)","val":192}, {"str":"自動(HD 8.5GB)","val":193}, {"str":"自動(HD 25GB)","val":194},
2201- // {"str":"自動(HD 50GB)","val":195}, {"str":"自動(標準 4.7GB)","val":197}
2202-
2203- String texts[] = {
2204- RECMODE_NAME_DR, "[AF]", "[AN]", "[AS]", "[AL]", "[AE]",
2205- "[XP]", "[SP]", "[LP]", "[EP]",
2206- "[自動(HD 4.7GB)]", "[自動(HD 8.5GB)]", "[自動(HD 25GB)]", "[自動(HD 50GB)]",
2207- "[自動(標準 4.7GB)]",
2208- null};
2209- String values[] = {
2210- RECMODE_DR, "64", "65", "66", "67", "68",
2211- "32", "33", "34", "35",
2212- "192", "193", "194", "195", "197",
2213- null};
2214-
2215- tvs.clear();
2216-
2217- for (int n=0; values[n] != null; n++){
2218- TextValueSet t = new TextValueSet();
2219- t.setText(texts[n]);
2220- t.setValue(values[n]);
2221- tvs.add(t);
2222- }
2223- }
2224-
2225- /*
2226- * 繰り返しパターンを初期化する
2227- */
2228- protected void setSettingPattern(ArrayList<TextValueSet> tvsC, ArrayList<TextValueSet> tvsN) {
2229- // {"num":0,"str":"しない"},{"num":1,"str":"毎週日"},{"num":2,"str":"毎週月"},{"num":4,"str":"毎週火"},
2230- // {"num":8,"str":"毎週水"},{"num":16,"str":"毎週木"},{"num":32,"str":"毎週金"},{"num":64,"str":"毎週土"},
2231- // {"num":62,"str":"月~金"},{"num":126,"str":"月~土"},{"num":124,"str":"火~土"},{"num":127,"str":"毎日"}
2232- int codesRd[]={
2233- 0, 1, 2, 3, 4, 5, RPTPTN_ID_SAT,
2234- RPTPTN_ID_MON2THU, RPTPTN_ID_MON2FRI, RPTPTN_ID_MON2SAT, RPTPTN_ID_TUE2SAT, RPTPTN_ID_EVERYDAY, -1};
2235- String codes[] = {
2236- "1", "2", "4", "8", "16", "32", "64",
2237- "30", "62", "126", "124", "127", null};
2238- String names[] = {
2239- "毎日曜日", "毎月曜日", "毎火曜日", "毎水曜日", "毎木曜日", "毎金曜日", "毎土曜日",
2240- "毎月~木", "毎月~金", "毎月~土", "毎火~土", "毎日", null};
2241-
2242- tvsC.clear();
2243- tvsN.clear();
2244-
2245- for (int n=0; codes[n] != null; n++){
2246- TextValueSet tc = new TextValueSet();
2247- tc.setText(String.valueOf(codesRd[n]));
2248- tc.setValue(codes[n]);
2249- tvsC.add(tc);
2250-
2251- TextValueSet tn = new TextValueSet();
2252- tn.setText(names[n]);
2253- tn.setValue(codes[n]);
2254- tvsN.add(tn);
2255- }
2256- }
2257-
2258- /**
2259- * エンコーダー情報を自動生成する
2260- */
2261- protected void setSettingEncoder(ArrayList<TextValueSet> tvs) {
2262- tvs.clear();
2263-
2264- // チューナー情報を自動生成する
2265- if (getTunerNum() >= 2){
2266- for (int i=1; i<=getTunerNum(); i++){
2267- TextValueSet t = new TextValueSet();
2268- t.setText("R" + i);
2269- t.setValue("R" + i);
2270- tvs.add(t);
2271- }
2272- }
2273- }
2274-
2275- /*
2276- * 持ち出し情報を初期化する
2277- */
2278- protected void setSettingPortable(ArrayList<TextValueSet> tvs) {
2279- // {"portableId":0,"portableStr":"しない"},{"portableId":1,"portableStr":"スマホ持ち出し"},
2280- // {"portableId":3,"portableStr":"DVD持ち出し(VR)"},{"portableId":2,"portableStr":"SeeQVault対応SDカード転送"}
2281- String codes[] = {
2282- MOCHIDASHI_NONE, "1", "3", "2", null};
2283- String names[] = {
2284- "しない", "スマホ持ち出し", "DVD持ち出し(VR)", "SeeQVault対応SDカード転送", null};
2285-
2286- tvs.clear();
2287-
2288- for (int n=0; codes[n] != null; n++){
2289- TextValueSet t = new TextValueSet();
2290- t.setText(names[n]);
2291- t.setValue(codes[n]);
2292- tvs.add(t);
2293- }
2294- }
2295-
2296- /*
2297- * 各種設定情報をレコーダーから取得する
2298- */
2299- protected boolean setSettingVariable() {
2300- // 設定情報
2301-// ArrayList<TextValueSet>discs = new ArrayList<TextValueSet>();
2302-// ArrayList<TextValueSet>repeats = new ArrayList<TextValueSet>();
2303-// setSettingSelect(repeats, discs, folder, vrate);
2304-
2305- // 記録先デバイス
2306- setSettingDevice();
2307-
2308- // フォルダ一覧
2309- setSettingFolders();
2310-
2311- // チャンネルコードバリュー - uva、bsaは廃止 -
2312- ArrayList<TextValueSet> tvsCV = new ArrayList<TextValueSet>();
2313- ArrayList<TextValueSet> tvsCT = new ArrayList<TextValueSet>();
2314- ArrayList<TextValueSet> tvsBR = new ArrayList<TextValueSet>();
2315- setSettingChCodeValue(tvsCV, tvsCT, tvsBR);
2316- if ( tvsCV.size() == 0 && tvsCT.size() == 0 && tvsBR.size() == 0) {
2317- System.err.println(errmsg = ERRID+"【致命的エラー】 チャンネルコードバリューが取得できません");
2318- return (false);
2319- }
2320- chvalue = tvsCV;
2321- chtype = tvsCT;
2322- tvsBranch = tvsBR;
2323-
2324- return (true);
2325- }
2326-
2327- /*
2328- * デバイス情報を取得する
2329- */
2330- protected boolean setSettingDevice() {
2331- ArrayList<TextValueSet>tvsD = new ArrayList<TextValueSet>();
2332- ArrayList<DeviceInfo>tvsDI = new ArrayList<DeviceInfo>();
2333-
2334- reportProgress(MSGID+"ドライブ一覧を取得します.");
2335-
2336- // おまじない
2337- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2338-
2339- String res = "";
2340-
2341- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadSelectInfo.php",null, thisEncoding);
2342- res = d[1];
2343-
2344- if (res == null) {
2345- errmsg = ERRID+ERRMSG_NORESPONSE;
2346- return (false);
2347- }
2348-
2349- //{"drive_id":0,"drivename":"HDD","drivetype":0,"playlist_enable":true,"folder_enable":true,
2350- // "allsize":1897740,"freesize":1187093,"connected":true,"canwrite":true,"protected":false,
2351- // "mounted":true,"ready":true,"formatType":12},
2352- Matcher ma = Pattern.compile("\\{" +
2353- "\"drive_id\":(\\d+)," + // 1
2354- "\"drivename\":\"([^\"]+)\"," + // 2
2355- "\"drivetype\":(\\d+)," + // 3
2356- "\"playlist_enable\":(true|false)," + // 4
2357- "\"folder_enable\":(true|false)," + // 5
2358- "\"allsize\":(\\d+)," + // 6
2359- "\"freesize\":(\\d+)," + // 7
2360- "\"connected\":(true|false)," + // 8
2361- "\"protected\":(true|false)," + // 9
2362- "\"mounted\":(true|false)," + // 10
2363- "\"ready\":(true|false)," + // 11
2364- "\"pin_setting\":(true|false)," + // 12
2365- "\"formatType\":(\\d+)" + // 13
2366- "\\}").matcher(res);
2367-
2368- int allsizeALL = 0;
2369- int freesizeALL = 0;
2370-
2371- while ( ma.find() ) {
2372- String drive_id = ma.group(1);
2373- String drive_name = ma.group(2);
2374- String drive_type = ma.group(3);
2375- boolean playlist_enable = ma.group(4).equals("true");
2376- boolean folder_enable = ma.group(5).equals("true");
2377- int allsize = Integer.parseInt(ma.group(6));
2378- int freesize = Integer.parseInt(ma.group(7));
2379- boolean connected = ma.group(8).equals("true");
2380- boolean isprotected = ma.group(9).equals("true");
2381- boolean mounted = ma.group(10).equals("true");
2382- boolean ready = ma.group(11).equals("true");
2383- boolean pin_setting = ma.group(12).equals("true");
2384- int formatType = Integer.parseInt(ma.group(13));
2385-
2386- TextValueSet t = new TextValueSet();
2387- t.setValue(drive_id);
2388- // デバイス名のコロン以降は無視する
2389- t.setText(GetDevicePrefix(drive_name));
2390- tvsD.add(t);
2391-
2392- DeviceInfo di = new DeviceInfo();
2393- di.setId(drive_id);
2394- di.setName(drive_name);
2395- di.setType(drive_type);
2396- di.setPlaylistEnable(playlist_enable);
2397- di.setFolderEnable(folder_enable);
2398- di.setAllSize(allsize);
2399- di.setFreeSize(freesize);
2400- di.setFreeMin((int)Math.round((double)freesize/MIN_PER_MB));
2401- di.setConnected(connected);
2402- di.setProtected(isprotected);
2403- di.setMounted(mounted);
2404- di.setReady(ready);
2405- di.setFormatType(formatType);
2406- tvsDI.add(di);
2407-
2408- allsizeALL += allsize;
2409- freesizeALL += freesize;
2410- }
2411-
2412- TextValueSet t = new TextValueSet();
2413- t.setValue(DEVICE_ALL);
2414- t.setText(DEVICE_NAME_ALL);
2415- tvsD.add(t);
2416-
2417- DeviceInfo di = new DeviceInfo();
2418- di.setId(DEVICE_ALL);
2419- di.setName(DEVICE_NAME_ALL);
2420- di.setAllSize(allsizeALL);
2421- di.setFreeSize(freesizeALL);
2422- di.setFreeMin((int)Math.round((double)freesizeALL/MIN_PER_MB));
2423- tvsDI.add(di);
2424-
2425- device = tvsD;
2426- setDeviceInfos(tvsDI);
2427-
2428- return (true);
2429- }
2430-
2431- /*
2432- * デバイス情報を保存する
2433- */
2434- protected void saveDeviceInfos(){
2435- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2436- devinfoTFile = "env/devinfo."+myFileId+".xml";
2437-
2438- DeviceInfosToFile(getDeviceInfos(), devinfoTFile);
2439- }
2440- /*
2441- * フォルダ一覧を取得する
2442- */
2443- protected boolean setSettingFolders() {
2444- ArrayList<TextValueSet> tvsD = device;
2445- ArrayList<TextValueSet> tvsF = new ArrayList<TextValueSet>();
2446-
2447- reportProgress(MSGID+"フォルダ一覧を取得します.");
2448-
2449- TextValueSet t0 = new TextValueSet();
2450- t0.setText(FOLDER_NAME_NONE);
2451- t0.setValue(FOLDER_NONE);
2452- tvsF.add(t0);
2453-
2454- for (int n=0; n<tvsD.size(); n++){
2455- TextValueSet tvs = tvsD.get(n);
2456-
2457- // RDへの情報作成
2458- String drive_id = tvs.getValue();
2459- String pstr = "drive_id=" + drive_id;
2460-
2461- // おまじない
2462- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2463-
2464- // RDへ情報送信
2465- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/ReloadFolderInfo.php?" + pstr, null, thisEncoding);
2466- String res = d[1];
2467-
2468- if (res == null) {
2469- errmsg = ERRID+ERRMSG_NORESPONSE;
2470- return (false);
2471- }
2472-
2473- Matcher ma = Pattern.compile("\\{" +
2474- "\"folder_id\":(\\d+)," + // 1
2475- "\"str\":\"([^\"]+)\"\\}") // 2
2476- .matcher(res);
2477-
2478- while ( ma.find() ) {
2479- TextValueSet t = new TextValueSet();
2480-
2481- String device_name = tvs.getText();
2482- String folder_id = ma.group(1);
2483- String folder_name = "[" + device_name + "] " + unescapeJavaString(ma.group(2));
2484-
2485- t.setText(folder_name);
2486- t.setValue(drive_id + ":" + folder_id);
2487- tvsF.add(t);
2488- }
2489- }
2490-
2491- folder = tvsF;
2492-
2493- return (true);
2494- }
2495-
2496- /*
2497- * フォルダー一覧をファイルに保存する
2498- */
2499- private void saveFolders(){
2500- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2501- folderTFile = "env/folders."+myFileId+".xml";
2502-
2503- TVSsave(folder, folderTFile);
2504- }
2505-
2506- /*
2507- * タイトル一覧をファイルに保存する
2508- */
2509- private void saveTitles(String devId){
2510- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2511-
2512- if (!DEVICE_ID.equals(DEVICE_ALL)){
2513- titleFile = "env/title."+myFileId+"." + DEVICE_ID + ".xml";
2514-
2515- TitlesToFile(getTitles(), titleFile);
2516- }
2517- else{
2518- for (DeviceInfo di : getDeviceInfos() ) {
2519- if (di.getId().equals(DEVICE_ALL))
2520- continue;
2521-
2522- ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
2523-
2524- for (TitleInfo ti : getTitles()){
2525- if (di.getName().startsWith(ti.getRec_device()))
2526- list.add(ti);
2527- }
2528-
2529- titleFile = "env/title."+myFileId+"." + di.getId() + ".xml";
2530- TitlesToFile(list, titleFile);
2531- }
2532- }
2533- }
2534-
2535- /*
2536- * チャンネル一覧を取得する
2537- */
2538- private void setSettingChCodeValue(ArrayList<TextValueSet> tvsvalue, ArrayList<TextValueSet> tvstype,
2539- ArrayList<TextValueSet> tvsbranch) {
2540- tvsvalue.clear();
2541- tvstype.clear();
2542- tvsbranch.clear();
2543-
2544- reportProgress(MSGID+"チャンネル一覧を取得します.");
2545-
2546- // おまじない
2547- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2548-
2549- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadChannelList.php",null, thisEncoding);
2550- String res = d[1];
2551-
2552- if (res == null) {
2553- errmsg = ERRID+ERRMSG_NORESPONSE;
2554- return;
2555- }
2556-
2557- // {"network":"地上","service":[... ]}
2558- Matcher ma = Pattern.compile("\\{\"network\":\"([^\"]+)\",\"service\":\\[([^\\]]+)\\]\\}").matcher(res);
2559- while ( ma.find() ) {
2560- String network = ma.group(1);
2561- String chlist = ma.group(2);
2562- String prefix = "";
2563-
2564- // "uvd","bsd","csd","l1","l2","l3"
2565- String typ = "";
2566- switch(network){
2567- case "地上":
2568- typ = CHTYPE_UVD;
2569- break;
2570- case "BS":
2571- typ = CHTYPE_BSD;
2572- prefix = CHPREFIX_BS;
2573- break;
2574- case "CS":
2575- typ = CHTYPE_CSD;
2576- prefix = CHPREFIX_CS;
2577- break;
2578- }
2579-
2580- // {"channelid":"G7FE00400","channelNr":"011","channelName":"NHK総合1・東京","chnum":"011","branch":0,
2581- // "branchNumExists":0,"networkId":32736,"serviceId":1024,"multiSub":false},
2582- Matcher mb = Pattern.compile("\\{" +
2583- "\"channelid\":\"([^\"]+)\"," + // 1
2584- "\"channelNr\":\"([^\"]+)\"," + // 2
2585- "\"channelName\":\"([^\"]+)\"," + // 3
2586- "\"chnum\":\"([^\"]+)\"," + // 4
2587- "\"branch\":(\\d+)," + // 5
2588- "\"branchNumExists\":(\\d+)," + // 6
2589- "\"networkId\":(\\d+)," + // 7
2590- "\"serviceId\":(\\d+)," + // 8
2591- "\"multiSub\":([^}]+)}") // 9
2592- .matcher(chlist);
2593-
2594- // var uvd_ch_text = new Array(
2595- // "011-1",
2596- // "012-1",
2597- // var uvd_ch_value = new Array(
2598- // "011:32736:1024",
2599- // "012:32736:1025",
2600-
2601- while ( mb.find() ) {
2602- String chno = prefix + mb.group(2);
2603- String chid = mb.group(4) + ":" + mb.group(7) + ":" + mb.group(8);
2604-
2605- TextValueSet t = new TextValueSet();
2606- t.setText(chno);
2607- t.setValue(chid);
2608- tvsvalue.add(t);
2609-
2610- TextValueSet x = new TextValueSet();
2611- x.setText(chid);
2612- x.setValue(typ);
2613- tvstype.add(x);
2614-
2615- TextValueSet b = new TextValueSet();
2616- b.setText(chid);
2617- b.setValue(mb.group(5));
2618- tvsbranch.add(b);
2619- }
2620- }
2621- }
2622-
2623- /*
2624- * 録画設定情報を取得する(未使用)
2625- */
2626- protected void setSettingSelect(ArrayList<TextValueSet> tvsR, ArrayList<TextValueSet> tvsD,
2627- ArrayList<TextValueSet> tvsF, ArrayList<TextValueSet> tvsV) {
2628- tvsR.clear();
2629- tvsD.clear();
2630- tvsF.clear();
2631- tvsV.clear();
2632-
2633- reportProgress(MSGID+"録画設定情報を取得します.");
2634-
2635- // おまじない
2636- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2637-
2638- String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadDialogInitializationData.php?schId=0", null, thisEncoding);
2639- String res = d[1];
2640-
2641- if (res == null) {
2642- errmsg = ERRID+ERRMSG_NORESPONSE;
2643- return;
2644- }
2645-
2646- Matcher ma = Pattern.compile("\\{\"day_select\":\\[([^\\]]+)\\],").matcher(res);
2647- if ( ma.find() ) {
2648- Matcher mb = Pattern.compile("\\{\"str\":\"([^\"]+)\",\"val\":\"([^\"]+)\"\\}").matcher(ma.group(1));
2649-
2650- while( mb.find() ){
2651- TextValueSet t = new TextValueSet();
2652- String repeat_id = ma.group(2);
2653- String repeat_name = ma.group(1);
2654- t.setValue(repeat_id);
2655- t.setText(repeat_name);
2656- tvsR.add(t);
2657- }
2658- }
2659-
2660- Matcher mc = Pattern.compile("\\{\"disc_select\":\\[([^\\]]+)\\],").matcher(res);
2661- if ( mc.find() ) {
2662- Matcher md = Pattern.compile("\\{" +
2663- "\"driveid\":\"([^\"]+)\"," + // 1
2664- "\"str\":\"([^\"]+)\"," + // 2
2665- "\"folder_select\":\\[([^\\]]+)\\]," + // 3
2666- "\"pqmode_select\":\\[([^\\]]+)\\]") // 4
2667- .matcher(mc.group(1));
2668-
2669- while( md.find() ){
2670- String drive_id = md.group(1);
2671- String drive_name = md.group(2);
2672- String folders = md.group(3);
2673- String vrates = md.group(4);
2674-
2675- TextValueSet t = new TextValueSet();
2676- t.setValue(drive_id);
2677- t.setText(drive_name);
2678- tvsD.add(t);
2679-
2680- Matcher me = Pattern.compile("\\{" +
2681- "\"folderid\":\"([^\"]+)\"," + // 1
2682- "\"str\":\"([^\"]+)\"\\}") // 2
2683- .matcher(folders);
2684-
2685- while( me.find() ){
2686- String folder_id = mc.group(1);
2687- String folder_name = "[" + drive_name + "] " + unescapeJavaString(me.group(2));
2688-
2689- TextValueSet tc = new TextValueSet();
2690- tc.setValue(folder_id);
2691- tc.setText(folder_name);
2692- tvsF.add(tc);
2693- }
2694-
2695- if (!drive_id.equals(DEVICE_HDD)){
2696- continue;
2697- }
2698-
2699- Matcher mf = Pattern.compile("\\{" +
2700- "\"str\":\"([^\"]+)\"," + // 1
2701- "\"pqmode\":\\[([^\\]]+)\\]\\}") // 2
2702- .matcher(vrates);
2703-
2704- while( mf.find() ){
2705- String vrate_id = mf.group(1);
2706- String vrate_name = "[" + drive_name + "] " + unescapeJavaString(me.group(2));
2707-
2708- TextValueSet tc = new TextValueSet();
2709- tc.setValue(vrate_id);
2710- tc.setText(vrate_name);
2711- tvsV.add(tc);
2712- }
2713- }
2714- }
2715- }
2716-
2717- /*
2718- * <device_id>:<folder_id> から<folder_id>を切り出す
2719- */
2720- protected String extractFolderID( String fol_id ) {
2721- if (fol_id == null)
2722- return null;
2723-
2724- int idx = fol_id.indexOf(':');
2725- if (idx == -1)
2726- return fol_id;
2727-
2728- return fol_id.substring(idx+1);
2729- }
2730-
2731- public String unescapeJavaString(String st) {
2732- StringBuilder sb = new StringBuilder(st.length());
2733-
2734- for (int i = 0; i < st.length(); i++) {
2735- char ch = st.charAt(i);
2736- if (ch == '\\') {
2737- char nextChar = (i == st.length() - 1) ? '\\' : st
2738- .charAt(i + 1);
2739- // Octal escape?
2740- if (nextChar >= '0' && nextChar <= '7') {
2741- String code = "" + nextChar;
2742- i++;
2743- if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2744- && st.charAt(i + 1) <= '7') {
2745- code += st.charAt(i + 1);
2746- i++;
2747- if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2748- && st.charAt(i + 1) <= '7') {
2749- code += st.charAt(i + 1);
2750- i++;
2751- }
2752- }
2753- sb.append((char) Integer.parseInt(code, 8));
2754- continue;
2755- }
2756- switch (nextChar) {
2757- case '\\':
2758- ch = '\\';
2759- break;
2760- case 'b':
2761- ch = '\b';
2762- break;
2763- case 'f':
2764- ch = '\f';
2765- break;
2766- case 'n':
2767- ch = '\n';
2768- break;
2769- case 'r':
2770- ch = '\r';
2771- break;
2772- case 't':
2773- ch = '\t';
2774- break;
2775- case '\"':
2776- ch = '\"';
2777- break;
2778- case '\'':
2779- ch = '\'';
2780- break;
2781- case '/':
2782- ch = '/';
2783- break;
2784- // Hex Unicode: u????
2785- case 'u':
2786- if (i >= st.length() - 5) {
2787- ch = 'u';
2788- break;
2789- }
2790- int code = Integer.parseInt(
2791- "" + st.charAt(i + 2) + st.charAt(i + 3)
2792- + st.charAt(i + 4) + st.charAt(i + 5), 16);
2793- sb.append(Character.toChars(code));
2794- i += 5;
2795- continue;
2796- }
2797- i++;
2798- }
2799- sb.append(ch);
2800- }
2801- return sb.toString();
2802- }
2803-
2804- /*
2805- * デバイス名からコロンの前の部分だけを取り出す
2806- */
2807- static String GetDevicePrefix(String device_name){
2808- Matcher ma = Pattern.compile("^(.*):").matcher(device_name);
2809- if (ma.find()){
2810- return ma.group(1);
2811- }
2812-
2813- return device_name;
2814- }
2815-}
1+package tainavi.pluginrec;
2+
3+import java.io.File;
4+import java.io.UnsupportedEncodingException;
5+import java.net.Authenticator;
6+import java.net.URLEncoder;
7+import java.text.ParseException;
8+import java.text.SimpleDateFormat;
9+import java.util.ArrayList;
10+import java.util.Comparator;
11+import java.util.Date;
12+import java.util.HashMap;
13+import java.util.Locale;
14+import java.util.regex.Matcher;
15+import java.util.regex.Pattern;
16+
17+import tainavi.AribCharMap;
18+import tainavi.ChannelCode;
19+import tainavi.ChapterInfo;
20+import tainavi.CommonUtils;
21+import tainavi.DeviceInfo;
22+import tainavi.GetRDStatus;
23+import tainavi.HDDRecorder;
24+import tainavi.HDDRecorderUtils;
25+import tainavi.ReserveList;
26+import tainavi.TVProgram.ProgGenre;
27+import tainavi.TextValueSet;
28+import tainavi.TitleInfo;
29+import tainavi.TraceProgram;
30+
31+/**
32+ * REGZA DBR-T2007用のレコーダプラグインです。
33+ * @author original:tmskmr
34+ * @version x.xx.xx
35+ */
36+public class PlugIn_RecDBR_M2008 extends HDDRecorderUtils implements HDDRecorder,Cloneable {
37+ public PlugIn_RecDBR_M2008() {
38+ super();
39+ this.setTunerNum(3);
40+ setSettingFixed();
41+ }
42+
43+ public PlugIn_RecDBR_M2008 clone() {
44+ return (PlugIn_RecDBR_M2008) super.clone();
45+ }
46+
47+ private static final String thisEncoding = "UTF-8";
48+
49+ /*******************************************************************************
50+ * 種族の特性
51+ ******************************************************************************/
52+
53+ // 種族の特性
54+ @Override
55+ public String getRecorderId() { return "REGZA DBR-M2008"; }
56+ @Override
57+ public RecType getType() { return RecType.RECORDER; }
58+
59+ // chvalueを使っていいよ
60+ @Override
61+ public boolean isChValueAvailable() { return true; }
62+ // CHコードは入力しなくていい
63+ @Override
64+ public boolean isChCodeNeeded() { return false; }
65+
66+ // 録画タイトルに対応している
67+ @Override
68+ public boolean isTitleListSupported() { return true; }
69+
70+ // 持ち出しに対応している
71+ @Override
72+ public boolean isPortableSupported() { return true; }
73+
74+ // フォルダー作成に対応している
75+ @Override
76+ public boolean isFolderCreationSupported() { return true; }
77+
78+ /*******************************************************************************
79+ * 予約ダイアログなどのテキストのオーバーライド
80+ ******************************************************************************/
81+
82+ @Override
83+ public String getChDatHelp() { return
84+ "「レコーダの放送局名」はネットdeナビの録画予約一覧で表示される「CH」の欄の値を設定してください。\n"+
85+ "予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"
86+ ;
87+ }
88+
89+ /*******************************************************************************
90+ * 定数
91+ ******************************************************************************/
92+
93+ protected final String MSGID = "["+getRecorderId()+"] ";
94+ protected final String ERRID = "[ERROR]"+MSGID;
95+ protected final String DBGID = "[DEBUG]"+MSGID;
96+
97+ protected final String NETWORK_UVD = "0";
98+ protected final String NETWORK_BSD = "1";
99+ protected final String NETWORK_CSD = "2";
100+ protected final String NETWORK_L1 = "3";
101+ protected final String NETWORK_L2 = "4";
102+ protected final String NETWORK_L3 = "5";
103+ protected final String NETWORK_L4 = "6";
104+ protected final String NETWORK_NET = "7";
105+
106+ protected final String CHTYPE_UVD = "uvd";
107+ protected final String CHTYPE_BSD = "bsd";
108+ protected final String CHTYPE_CSD = "csd";
109+ protected final String CHTYPE_L1 = "L1";
110+ protected final String CHTYPE_L2 = "L2";
111+ protected final String CHTYPE_L3 = "L3";
112+ protected final String CHTYPE_L4 = "L4";
113+ protected final String CHTYPE_NET = "NET";
114+
115+ protected final String CHPREFIX_BS = "BS";
116+ protected final String CHPREFIX_CS = "CS";
117+ protected final String CH_L1 = "L1";
118+ protected final String CH_L2 = "L2";
119+ protected final String CH_L3 = "L3";
120+ protected final String CH_L4 = "L4";
121+ protected final String CH_NET = "NET";
122+
123+ protected final String OPTION_DATETIME = "1";
124+ protected final String OPTION_PROGRAM = "2";
125+
126+ protected final String REPEAT_NONE = "0";
127+
128+ protected final int RPTPTN_ID_TUE2SAT = 12;
129+
130+ protected final String EXEC_YES = "1";
131+ protected final String EXEC_NO = "0";
132+
133+ protected final String BRANCH_NO = "0";
134+ protected final String BRANCH_YES = "1";
135+
136+ protected final String DEVICE_HDD = "0";
137+ protected final String DEVICE_DISC = "1";
138+ protected final String FOLDER_NONE = "0";
139+ protected final String FOLDER_NAME_NONE = "指定なし";
140+
141+ protected final String RECMODE_DR = "0";
142+ protected final String RECMODE_NAME_DR = "[DR]";
143+
144+ protected final String MOCHIDASHI_NONE = "0";
145+
146+ protected final String RESULT_OK = "0";
147+ protected final String RESULT_OTHER = "1";
148+ protected final String RESULT_BUSY = "2";
149+ protected final String RESULT_INVALID_TITLE = "17";
150+
151+ protected final String ERRMSG_NORESPONSE = "レコーダーが反応しません";
152+ protected final String ERRMSG_INVALIDRESPONSE = "レコーダーからの応答が不正です。";
153+ protected final String ERRMSG_USB_BUSY = "USB HDDが使用中です。";
154+
155+ protected final double MIN_PER_MB = 171.777;
156+
157+ protected final String TITLE_NOCHANGED = "__NETdeNAVI_TitleNameNotChanged__";
158+
159+ /*******************************************************************************
160+ * CHコード設定、エラーメッセージ
161+ ******************************************************************************/
162+
163+ public ChannelCode getChCode() {
164+ return cc;
165+ }
166+
167+ private ChannelCode cc = new ChannelCode(getRecorderId());
168+
169+ protected void setErrmsg(String s) { errmsg = s; }
170+
171+ public String getErrmsg() {
172+ return(errmsg.replaceAll("\\\\r\\\\n", ""));
173+ }
174+
175+ private String errmsg = "";
176+
177+ /*******************************************************************************
178+ * 部品
179+ ******************************************************************************/
180+
181+ private GetRDStatus gs = new GetRDStatus();
182+
183+ private String rsvedFile = "";
184+ private String folderTFile = "";
185+ private String titleFile = "";
186+ private String devinfoTFile = "";
187+ private ArrayList<TextValueSet> tvsBranch = new ArrayList<TextValueSet>();
188+ private ArrayList<TextValueSet> tvsPatternCode = new ArrayList<TextValueSet>();
189+ private ArrayList<TextValueSet> tvsPatternName = new ArrayList<TextValueSet>();
190+
191+ /*******************************************************************************
192+ * コンストラクタ
193+ ******************************************************************************/
194+
195+ /*******************************************************************************
196+ * チャンネルリモコン機能
197+ ******************************************************************************/
198+
199+ /*******************************************************************************
200+ * レコーダーから各種設定情報を取得する
201+ ******************************************************************************/
202+ @Override
203+ public boolean GetRdSettings(boolean force) {
204+
205+ System.out.println("レコーダの各種設定情報を取得します.");
206+
207+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
208+
209+ String deviceTFile = "env/device."+myFileId+".xml";
210+ devinfoTFile = "env/devinfo."+myFileId+".xml";
211+ folderTFile = "env/folders."+myFileId+".xml";
212+ String chValueTFile = "env/chvalue."+myFileId+".xml";
213+ String chTypeTFile = "env/chtype."+myFileId+".xml";
214+ String branchTFile = "env/branch."+myFileId+".xml";
215+
216+ // 固定の各種設定情報を初期化する
217+ setSettingFixed();
218+
219+ File f = new File(deviceTFile);
220+ if ( !force){
221+ if (!f.exists())
222+ return(false);
223+
224+ // キャッシュから読み出し(録画設定ほか)
225+ device = TVSload(deviceTFile);
226+ setDeviceInfos(DeviceInfosFromFile(devinfoTFile));
227+ folder = TVSload(folderTFile);
228+ chvalue = TVSload(chValueTFile);
229+ chtype = TVSload(chTypeTFile);
230+ tvsBranch = TVSload(branchTFile);
231+
232+ // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
233+ if (device.size() > 0 && chvalue.size() > 0 && chtype.size() > 0 && tvsBranch.size() > 0) {
234+ return(true);
235+ }
236+ }
237+
238+ // 各種設定情報をレコーダから取得する
239+ if (!setSettingVariable()){
240+ return (false);
241+ }
242+
243+ TVSsave(device, deviceTFile);
244+ saveDeviceInfos();
245+ saveFolders();
246+ TVSsave(chvalue, chValueTFile);
247+ TVSsave(chtype, chTypeTFile);
248+ TVSsave(tvsBranch, branchTFile);
249+
250+ return(true);
251+ }
252+
253+ /*******************************************************************************
254+ * レコーダーから予約一覧を取得する
255+ ******************************************************************************/
256+
257+ public boolean GetRdReserve(boolean force)
258+ {
259+ System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
260+
261+ errmsg = "";
262+
263+ //
264+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
265+
266+ rsvedFile = "env/reserved."+myFileId+".xml";
267+
268+ File f = new File(rsvedFile);
269+ if ( !force && f.exists()) {
270+ // キャッシュから読み出し(予約一覧)
271+ setReserves(ReservesFromFile(rsvedFile));
272+
273+ // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
274+ if (getReserves().size() > 0) {
275+ return(true);
276+ }
277+ }
278+
279+ // 録画予約の一覧をレコーダから取得する
280+ ArrayList<ReserveList> ra = getReserveList();
281+ if (ra == null){
282+ return(false);
283+ }
284+
285+ setReserves(ra);
286+
287+ // キャッシュに保存
288+ ReservesToFile(getReserves(), rsvedFile);
289+
290+ // 取得した情報の表示
291+ if (getDebug()){
292+ System.out.println("---Reserved List Start---");
293+ for ( int i = 0; i<getReserves().size(); i++ ) {
294+ // 詳細情報の取得
295+ ReserveList e = getReserves().get(i);
296+ System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",
297+ (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));
298+ }
299+ System.out.println("---Reserved List End---");
300+ }
301+
302+ return(true);
303+ }
304+
305+ /*******************************************************************************
306+ * 予約詳細情報の取得
307+ ******************************************************************************/
308+ @Override
309+ public boolean isThereAdditionalDetails() {
310+ return true;
311+ }
312+
313+ /*
314+ * 予約の詳細情報を取得する
315+ */
316+ @Override
317+ public boolean GetRdReserveDetails(){
318+ int rno = 0;
319+ ArrayList<ReserveList> ra = getReserves();
320+ for (ReserveList entry : ra) {
321+
322+ reportProgress("+番組詳細を取得します("+rno+"/"+ra.size()+").");
323+ getReserveDetail(entry);
324+
325+ // 放送局名変換
326+ entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
327+
328+ // TS->DR
329+ translateAttributeTuner(entry);
330+
331+ // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ
332+ copyAttributesT2007(entry, getReserves());
333+
334+ rno++;
335+ }
336+
337+ // キャッシュに保存
338+ ReservesToFile(getReserves(), rsvedFile);
339+
340+ return(true);
341+ }
342+
343+ /*******************************************************************************
344+ * 新規予約
345+ ******************************************************************************/
346+ @Override
347+ public boolean PostRdEntry(ReserveList r) {
348+ errmsg = "";
349+
350+ String chcode = cc.getCH_WEB2CODE(r.getCh_name());
351+ if (chcode == null) {
352+ errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
353+ System.out.println(errmsg);
354+ return(false);
355+ }
356+
357+ // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
358+ if ( !loadDialogInitData("0")){
359+ return(false);
360+ }
361+
362+ // RDへの情報作成
363+ String pstr = createPostData(r, "");
364+
365+ // RDへ情報送信
366+ reportProgress(MSGID+"レコーダーに新規予約を要求します.");
367+ String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
368+// String header = d[0];
369+ String response = d[1];
370+
371+ // 登録結果の確認
372+ if ( !checkReserveResponse(r, response) ){
373+ return(false);
374+ }
375+
376+ // 予約情報の調整
377+ adjustReserve(r);
378+
379+ // 予約リストを更新
380+ getReserves().add(r);
381+
382+ // キャッシュに保存
383+ ReservesToFile(getReserves(), rsvedFile);
384+
385+ reportProgress(MSGID+"正常に登録できました。");
386+
387+ return(true);
388+ }
389+
390+ /*******************************************************************************
391+ * 予約更新
392+ ******************************************************************************/
393+ @Override
394+ public boolean UpdateRdEntry(ReserveList o, ReserveList r) {
395+ errmsg = "";
396+
397+ String chcode = cc.getCH_WEB2CODE(r.getCh_name());
398+ if (chcode == null) {
399+ errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
400+ System.out.println(errmsg);
401+ return(false);
402+ }
403+
404+ String id = o.getId();
405+
406+ // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
407+ if ( !loadDialogInitData(id) ){
408+ return(false);
409+ }
410+
411+ // RDへの情報作成
412+ String pstr = createPostData(r, id);
413+
414+ // RDへ情報送信
415+ reportProgress(MSGID+"レコーダーに予約更新を要求します.");
416+ String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
417+// String header = d[0];
418+ String response = d[1];
419+
420+ // 登録結果の確認
421+ if ( !checkReserveResponse(r, response) ){
422+ return(false);
423+ }
424+
425+ // 予約情報の調整
426+ adjustReserve(r);
427+
428+ // 情報置き換え
429+ getReserves().remove(o);
430+ getReserves().add(r);
431+
432+ // キャッシュに保存
433+ ReservesToFile(getReserves(), rsvedFile);
434+
435+ reportProgress(MSGID+"正常に更新できました。");
436+
437+ return(true);
438+ }
439+
440+ /*******************************************************************************
441+ * 予約削除
442+ ******************************************************************************/
443+ @Override
444+ public ReserveList RemoveRdEntry(String delid) {
445+ errmsg = "";
446+
447+ // 削除対象を探す
448+ ReserveList r = null;
449+ for ( ReserveList reserve : getReserves() ) {
450+ if (reserve.getId().equals(delid)) {
451+ r = reserve;
452+ break;
453+ }
454+ }
455+ if (r == null) {
456+ return(null);
457+ }
458+
459+ // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
460+ if ( !loadDialogInitData(delid)){
461+ return(null);
462+ }
463+
464+ // RDへの情報作成
465+ String pstr = createPostData(r, delid);
466+
467+ // RDへ情報送信
468+ reportProgress(MSGID+"レコーダーに予約削除を要求します.");
469+ String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DeleteReserve.php", pstr, null, thisEncoding);
470+// String header = d[0];
471+ String response = d[1];
472+
473+ if ( !checkGeneralResponse( "予約削除", response) ){
474+ return(null);
475+ }
476+
477+ // 予約リストを更新
478+ getReserves().remove(r);
479+
480+ // キャッシュに保存
481+ ReservesToFile(getReserves(), rsvedFile);
482+
483+ reportProgress(MSGID+"正常に削除できました。");
484+ System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
485+ return(r);
486+ }
487+
488+ /*******************************************************************************
489+ * フォルダー作成
490+ ******************************************************************************/
491+ @Override
492+ public boolean CreateRdFolder(String device_id, String folder_name) {
493+ return setFolderName( device_id, null, folder_name );
494+ }
495+
496+ /*******************************************************************************
497+ * フォルダー名更新
498+ ******************************************************************************/
499+ @Override
500+ public boolean UpdateRdFolderName(String device_id, String folder_id, String folder_name) {
501+ return setFolderName( device_id, folder_id, folder_name );
502+ }
503+
504+ /*******************************************************************************
505+ * フォルダー削除
506+ ******************************************************************************/
507+ @Override
508+ public boolean RemoveRdFolder(String device_id, String fol_id) {
509+ String action = "フォルダ削除";
510+ String folder_id = extractFolderID(fol_id);
511+
512+ String pstr =
513+ "drive_id=" + device_id + "&" +
514+ "folder_id=" + folder_id;
515+
516+ for (int n=0; n<3; n++){
517+ // おまじない
518+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
519+
520+ // RDへ情報送信
521+ reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
522+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/folderset/DeleteFolder.php?" + pstr, null, thisEncoding);
523+// String header = d[0];
524+ String response = d[1];
525+
526+ // レスポンスから処理結果を取得する
527+ String [] rc = getReasonFromResponse( response );
528+ if (rc == null) {
529+ errmsg = ERRID+ERRMSG_NORESPONSE;
530+ return(false);
531+ }
532+
533+ String result = rc[0];
534+ String reason = rc[1];
535+
536+ if (result.equals(RESULT_OK))
537+ break;
538+
539+ if (result.equals(RESULT_OTHER) || result.equals(RESULT_BUSY)){
540+ if (mountUsbDrive(device_id))
541+ continue;
542+ }
543+
544+ errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
545+ return(false);
546+ }
547+
548+ // フォルダ一覧を取得し直す
549+ setSettingFolders();
550+ saveFolders();
551+
552+ return true;
553+ }
554+
555+ /*******************************************************************************
556+ * タイトル一覧取得
557+ ******************************************************************************/
558+ @Override
559+ public boolean GetRdTitles(String device_id, boolean force, boolean detail, boolean mountedOnly) {
560+ System.out.println("レコーダからタイトル一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
561+
562+ errmsg = "";
563+
564+ DEVICE_ID = device_id;
565+
566+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
567+
568+ ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
569+
570+ for (DeviceInfo di : getDeviceInfos() ) {
571+ String devid = di.getId();
572+ if (devid.equals(DEVICE_ALL))
573+ continue;
574+ if (!device_id.equals(DEVICE_ALL) && !device_id.equals(devid))
575+ continue;
576+
577+ titleFile = "env/title."+myFileId+"." + devid + ".xml";
578+
579+ if (!force){
580+ File f = new File(titleFile);
581+ if (!f.exists())
582+ return(false);
583+
584+ // キャッシュから読み出し(タイトル一覧)
585+ ArrayList<TitleInfo> ta = TitlesFromFile(titleFile);
586+
587+ list.addAll(ta);
588+ }
589+ else{
590+ // タイトル一覧をレコーダから取得する
591+ ArrayList<TitleInfo> ta = getTitleList(devid, mountedOnly);
592+ if (ta == null){
593+ if (errmsg.length() > 0)
594+ return(false);
595+
596+ File f = new File(titleFile);
597+ // キャッシュから読み出し(タイトル一覧)
598+ if (f.exists())
599+ ta = TitlesFromFile(titleFile);
600+ }
601+ else{
602+ // タイトルの詳細情報を取得し、内容を調整する
603+ for (TitleInfo entry : ta) {
604+ // 放送局名変換
605+ entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
606+ }
607+
608+ if (detail){
609+ getTitleDetails(devid, ta, false);
610+ }
611+ }
612+
613+ list.addAll(ta);
614+ }
615+ }
616+
617+ setTitles(list);
618+
619+ // キャッシュに保存
620+ if (force){
621+ saveTitles(device_id);
622+
623+ // 取得した情報の表示
624+ if (getDebug()){
625+ System.out.println("---Title List Start---");
626+ for ( int i = 0; i<getTitles().size(); i++ ) {
627+ // 詳細情報の取得
628+ TitleInfo t = getTitles().get(i);
629+ System.out.println(String.format("[%s] %s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s",
630+ (i+1), t.getId(), t.getRec_date(), t.getAhh(), t.getAmm(), t.getZhh(), t.getZmm(), t.getRec_min(),
631+ t.getTitle(), t.getChannel(), t.getCh_name()));
632+ }
633+ System.out.println("---Title List End---");
634+ }
635+
636+ // 各種設定情報をレコーダから取得する
637+ if (setSettingDevice()){
638+ saveDeviceInfos();
639+ }
640+ }
641+
642+ return(true);
643+ }
644+
645+ /*******************************************************************************
646+ * タイトル詳細情報取得
647+ ******************************************************************************/
648+ @Override
649+ public boolean GetRdTitleDetails(String devid, boolean force){
650+ ArrayList<TitleInfo> ta = getTitles();
651+
652+ getTitleDetails(devid, ta, force);
653+
654+ // キャッシュに保存
655+ saveTitles(devid);
656+
657+ return(true);
658+ }
659+
660+ /*
661+ * タイトル詳細情報を取得する
662+ */
663+ private boolean getTitleDetails(String devid, ArrayList<TitleInfo>ta, boolean force){
664+ int tno = 0;
665+
666+ for (TitleInfo ti : ta){
667+ tno++;
668+
669+ if (ti.getDetailLoaded() && !ti.getRecording() && !force)
670+ continue;
671+
672+ reportProgress("+タイトル詳細を取得します("+tno+"/"+ta.size()+").");
673+ getTitleDetail(ti);
674+ }
675+
676+ return(true);
677+ }
678+
679+ /*******************************************************************************
680+ * タイトル詳細情報取得
681+ ******************************************************************************/
682+ @Override
683+ public boolean GetRdTitleDetail(TitleInfo t) {
684+ if (t == null)
685+ return(false);
686+
687+ if (!getTitleDetail(t))
688+ return(false);
689+
690+ // キャッシュに保存
691+ saveTitles(t.getRec_device());
692+
693+ return(true);
694+ }
695+
696+ /*******************************************************************************
697+ * タイトル更新
698+ ******************************************************************************/
699+ @Override
700+ public boolean UpdateRdTitleInfo(String device_id, TitleInfo o, TitleInfo t) {
701+ errmsg = "";
702+
703+ if (t == null) {
704+ return(false);
705+ }
706+
707+ for (int n=0; n<3; n++){
708+ // タイトルの編集をレコーダに通知する
709+ notifyTitleEdit(device_id, t.getId());
710+
711+ // おまじない
712+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
713+
714+ // RDへの情報作成
715+ String pstr = createTitlePostData(t, o, device_id);
716+
717+ // RDへ情報送信
718+ reportProgress(MSGID+"レコーダーにタイトル更新を要求します:"+device_id);
719+ String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/UpdateTitleInfo.php", pstr, null, thisEncoding);
720+// String header = d[0];
721+ String response = d[1];
722+
723+ // レスポンスから処理結果を取得する
724+ String [] rc = getReasonFromResponse( response );
725+ if (rc == null) {
726+ errmsg = ERRID+ERRMSG_NORESPONSE;
727+ return(false);
728+ }
729+
730+ String result = rc[0];
731+ String reason = rc[1];
732+
733+ if (result.equals(RESULT_OK))
734+ break;
735+
736+ if (result.equals(RESULT_OTHER)){
737+ if (mountUsbDrive(device_id))
738+ continue;
739+ }
740+
741+ errmsg = ERRID+"タイトル更新に失敗しました。(result=" + result + ",reason=" + reason + ")";
742+ return(false);
743+ }
744+
745+ // 録画タイトルリストを更新
746+ ArrayList<TitleInfo> list = getTitles();
747+ list.remove(o);
748+ list.add(t);
749+ list.sort(new TitleInfoComparator());
750+
751+ // キャッシュに保存
752+ saveTitles(t.getRec_device());
753+
754+ // 各種設定情報をレコーダから取得する
755+ if (setSettingDevice()){
756+ saveDeviceInfos();
757+ }
758+
759+ reportProgress(MSGID+"正常に更新できました。");
760+ System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");
761+
762+ return(true);
763+ }
764+
765+ /*******************************************************************************
766+ * タイトル削除
767+ ******************************************************************************/
768+ @Override
769+ public TitleInfo RemoveRdTitle(String device_id, String title_id) {
770+ errmsg = "";
771+
772+ // 削除対象を探す
773+ TitleInfo t = null;
774+ for ( TitleInfo ttl : getTitles() ) {
775+ if (ttl.getId().equals(title_id)) {
776+ t = ttl;
777+ break;
778+ }
779+ }
780+ if (t == null) {
781+ return(null);
782+ }
783+
784+ for (int n=0; n<3; n++){
785+ // おまじない
786+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
787+
788+ // RDへの情報作成
789+ String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
790+
791+ // RDへ情報送信
792+ reportProgress(MSGID+"レコーダーにタイトル削除を要求します.");
793+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/DeleteTitle.php?"+pstr, null, thisEncoding);
794+// String header = d[0];
795+ String response = d[1];
796+
797+ // レスポンスから処理結果を取得する
798+ String [] rc = getReasonFromResponse( response );
799+ if (rc == null) {
800+ errmsg = ERRID+ERRMSG_NORESPONSE;
801+ return(null);
802+ }
803+
804+ String result = rc[0];
805+ String reason = rc[1];
806+
807+ if (result.equals(RESULT_OK))
808+ break;
809+
810+ if (result.equals(RESULT_OTHER) || result.equals(RESULT_INVALID_TITLE)){
811+ if (mountUsbDrive(device_id))
812+ continue;
813+ }
814+
815+ errmsg = ERRID+"タイトル削除に失敗しました。(result=" + result + ",reason=" + reason + ")";
816+ return(null);
817+ }
818+
819+ // タイトルリストを更新
820+ getTitles().remove(t);
821+
822+ // キャッシュに保存
823+ saveTitles(t.getRec_device());
824+
825+ setSettingDevice();
826+ saveDeviceInfos();
827+
828+ reportProgress(MSGID+"正常に削除できました。");
829+ System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
830+ return(t);
831+ }
832+
833+ /*******************************************************************************
834+ * タイトル再生の開始・終了
835+ ******************************************************************************/
836+ @Override
837+ public boolean StartStopPlayRdTitle(String device_id, String title_id, boolean start) {
838+ errmsg = "";
839+
840+ // RDへのURL
841+ String action = start ? "タイトル再生開始" : "タイトル再生終了";
842+ String url = start ? "/titlelist/PlayTitle.php" : "/titlelist/PlayStop.php";
843+
844+ // RDへの情報作成
845+ String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
846+
847+ for (int n=0; n<3; n++){
848+ // おまじない
849+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
850+
851+ // RDへ情報送信
852+ reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
853+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+url+ "?"+pstr, null, thisEncoding);
854+// String header = d[0];
855+ String response = d[1];
856+
857+ // レスポンスから処理結果を取得する
858+ String [] rc = getReasonFromResponse( response );
859+ if (rc == null) {
860+ errmsg = ERRID+ERRMSG_NORESPONSE;
861+ return(false);
862+ }
863+
864+ String result = rc[0];
865+ String reason = rc[1];
866+
867+ if (result.equals(RESULT_OK))
868+ break;
869+
870+ if (result.equals(RESULT_OTHER)){
871+ if (mountUsbDrive(device_id))
872+ continue;
873+ }
874+
875+ errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
876+ return(false);
877+ }
878+
879+ reportProgress(MSGID+"正常に" + action + "できました。");
880+
881+ return(true);
882+ }
883+
884+ /* ここまで */
885+
886+ /* 個別コード-ここから最後まで */
887+ /*******************************************************************************
888+ * 非公開メソッド
889+ ******************************************************************************/
890+ /*
891+ * 録画予約の一覧を取得する
892+ */
893+ protected ArrayList<ReserveList> getReserveList() {
894+ // おまじない
895+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
896+
897+ // RDから予約一覧を取り出す
898+ reportProgress(MSGID+"予約一覧を取得します.");
899+ String response = "";
900+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadReserveList.php",null, thisEncoding);
901+// String header = d[0];
902+ response= d[1];
903+
904+ if (response == null) {
905+ errmsg = ERRID+ERRMSG_NORESPONSE;
906+ return(null);
907+ }
908+
909+ // 先頭部分をチェックする
910+ Matcher mr = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
911+ if ( ! mr.find()) {
912+ errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
913+ return (null);
914+ }
915+
916+ ArrayList<ReserveList> list = new ArrayList<ReserveList>();
917+
918+ Matcher ma = Pattern.compile("\\{" +
919+ "\"num\":(\\d+)," + // 1
920+ "\"id\":(\\d+)," + // 2
921+ "\"exec\":(\\d+)," + // 3
922+ "\"option\":(\\d+)," + // 4
923+ "\"eventname\":\"([^\"]+)\"," + // 5
924+ "\"network\":(\\d+)," + // 6
925+ "\"ch\":\"([^\"]+)\"," + // 7
926+ "\"repeat\":(\\d+)," + // 8
927+ "\"datetime\":(\\d+)," + // 9
928+ "\"duration\":(\\d+)," + // 10
929+ "\"conflictstart\":(\\d+)," + // 11
930+ "\"conflictend\":(\\d+)," + // 12
931+ "\"recording\":(\\d+)," + // 13
932+ "\"last\":(\\d+)," + // 14
933+ "\"mochidashi\":(\\d+)\\}") // 15
934+ .matcher(response);
935+
936+ while ( ma.find() ) {
937+ // 個々のデータを取り出す
938+ ReserveList entry = new ReserveList();
939+
940+ String id = ma.group(2);
941+ String exec = ma.group(3);
942+ String option = ma.group(4);
943+ String eventname = ma.group(5);
944+ String network = ma.group(6);
945+ String ch = ma.group(7);
946+ String repeat = ma.group(8);
947+ String datetime = ma.group(9);
948+ String duration = ma.group(10);
949+ String recording = ma.group(13);
950+ String mochidashi = ma.group(15);
951+
952+ // 予約ID
953+ entry.setId(id);
954+
955+ // 基本情報をセットする
956+ setReserveBasicInfo(entry, exec, option, eventname, network, ch, repeat, datetime, duration);
957+
958+ // 持ち出し
959+ String portable_name = value2text(portable, mochidashi);
960+ entry.setRec_portable(portable_name);
961+
962+ // 予約情報を保存
963+ list.add(entry.clone());
964+ }
965+
966+ return(list);
967+ }
968+
969+ /***
970+ * 予約の基本情報をセットする
971+ */
972+ protected void setReserveBasicInfo(ReserveList entry, String exec, String option, String eventname,
973+ String network, String ch, String repeat, String datetime, String duration){
974+ // 実行ON/OFF
975+ entry.setExec(exec.equals(EXEC_YES));
976+
977+ // オプション(1=日付指定, 2=PGM指定)
978+ entry.setRec_option(option);
979+
980+ // 開始日、終了日
981+ int nbsecs = Integer.parseInt(datetime); // 開始日時(UNIX時間/1000)
982+ int secs = Integer.parseInt(duration); // 録画時間(sec)
983+ int nesecs = nbsecs + secs; // 終了日時(UNIX時間/1000)
984+ Date bdate = new Date(nbsecs*1000L); // 開始日時(Date)
985+ Date edate = new Date(nesecs*1000L); // 終了日時(Date)
986+
987+ SimpleDateFormat sfd = new SimpleDateFormat("yyyy/MM/dd(E)", Locale.JAPAN);
988+ String pattern = sfd.format(bdate);
989+
990+ // 繰り返しパターン
991+ String pid = String.valueOf(RPTPTN_ID_BYDATE);
992+
993+ if (!repeat.equals(REPEAT_NONE)){
994+ pattern = value2text(tvsPatternName, repeat);
995+ pid = value2text(tvsPatternCode, repeat);
996+ }
997+
998+ entry.setRec_pattern_id(Integer.parseInt(pid));
999+ entry.setRec_pattern(pattern);
1000+
1001+ // 開始、終了時刻
1002+ SimpleDateFormat sfh = new SimpleDateFormat("HH");
1003+ SimpleDateFormat sfm = new SimpleDateFormat("mm");
1004+ String ahh = sfh.format(bdate);
1005+ String amm = sfm.format(bdate);
1006+ String zhh = sfh.format(edate);
1007+ String zmm = sfm.format(edate);
1008+ entry.setAhh(ahh);
1009+ entry.setAmm(amm);
1010+ entry.setZhh(zhh);
1011+ entry.setZmm(zmm);
1012+
1013+ // 次の録画日などを計算する
1014+ entry.setRec_nextdate(CommonUtils.getNextDate(entry));
1015+ entry.setRec_min(String.valueOf(secs/60));
1016+ getStartEndDateTime(entry);
1017+
1018+ // チューナーは固定
1019+ entry.setTuner("R1");
1020+
1021+ // 録画モードもとりあえず固定(後で詳細情報で上書きする)
1022+ entry.setRec_mode(RECMODE_NAME_DR);
1023+
1024+ // タイトル
1025+ String title = unescapeJavaString(eventname);
1026+ entry.setTitle(title);
1027+ entry.setTitlePop(TraceProgram.replacePop(title));
1028+
1029+ // チャンネル
1030+ entry.setChannel(getFormalChFromNetworkAndCh(network, ch));
1031+
1032+ // 放送局名変換
1033+ entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
1034+ }
1035+
1036+ /*
1037+ * network と ch から 正式なチャンネルIDを取得する
1038+ */
1039+ protected String getFormalChFromNetworkAndCh(String network, String ch){
1040+ // チャンネル
1041+ switch(network){
1042+ case NETWORK_UVD:
1043+ break;
1044+ case NETWORK_BSD:
1045+ ch = CHPREFIX_BS + ch;
1046+ break;
1047+ case NETWORK_CSD:
1048+ ch = CHPREFIX_CS + ch;
1049+ break;
1050+ case NETWORK_L1:
1051+ ch = CH_L1;
1052+ break;
1053+ case NETWORK_L2:
1054+ ch = CH_L2;
1055+ break;
1056+ case NETWORK_L3:
1057+ ch = CH_L3;
1058+ break;
1059+ case NETWORK_L4:
1060+ ch = CH_L4;
1061+ break;
1062+ case NETWORK_NET:
1063+ ch = CH_NET;
1064+ break;
1065+ }
1066+
1067+ return ch;
1068+ }
1069+
1070+ /*
1071+ * 予約詳細情報を取得する
1072+ */
1073+ protected boolean getReserveDetail( ReserveList r) {
1074+ // おまじない
1075+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1076+
1077+ String id = r.getId();
1078+ String response = "";
1079+
1080+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadReserveDetailData.php?reserve_id=" + id, null, thisEncoding);
1081+// String header = d[0];
1082+ response= d[1];
1083+
1084+ if (response == null) {
1085+ errmsg = ERRID+ERRMSG_NORESPONSE;
1086+ System.out.println(errmsg);
1087+ return(false);
1088+ }
1089+
1090+ // {"NETdeNAVI":{"reserveInfo":{"id":103,"exec":1,"option":2,"eventname":"Re:CREATORS(レクリエイターズ)",
1091+ // "network":0,"chnum":91,"branchexists":false,"branchnum":0,"ch":"091","repeat":64,"datetime":1495290600,"duration":1800,
1092+ // "conflictstart":0,"conflictend":0,"drive_id":512,"folder_id":33,"recmode":0,"mochidashi":0,"video_es":0,"audio_es":0,
1093+ // "data_es":0,"recording":0}}}
1094+ Matcher ma = Pattern.compile("\\{" +
1095+ "\"id\":(\\d+)," + // 1
1096+ "\"exec\":(\\d+)," + // 2
1097+ "\"option\":(\\d+)," + // 3
1098+ "\"eventname\":\"([^\"]+)\"," + // 4
1099+ "\"network\":(\\d+)," + // 5
1100+ "\"chnum\":(\\d+)," + // 6
1101+ "\"branchexists\":(false|true)," + // 7
1102+ "\"branchnum\":(\\d+)," + // 8
1103+ "\"ch\":\"([^\"]+)\"," + // 9
1104+ "\"repeat\":(\\d+)," + // 10
1105+ "\"datetime\":(\\d+)," + // 11
1106+ "\"duration\":(\\d+)," + // 12
1107+ "\"conflictstart\":(\\d+)," + // 13
1108+ "\"conflictend\":(\\d+)," + // 14
1109+ "\"drive_id\":(\\d+)," + // 15
1110+ "\"folder_id\":(\\d+)," + // 16
1111+ "\"recmode\":(\\d+)," + // 17
1112+ "\"mochidashi\":(\\d+)," + // 18
1113+ "\"video_es\":(\\d+)," + // 19
1114+ "\"audio_es\":(\\d+)," + // 20
1115+ "\"data_es\":(\\d+)," + // 21
1116+ "\"recording\":(\\d+)\\}") // 22
1117+ .matcher(response);
1118+
1119+ if ( !ma.find() ) {
1120+ reportProgress(ERRID+"予約詳細情報が取得できません.ID=" + id);
1121+ return (false);
1122+ }
1123+
1124+ String exec = ma.group(2);
1125+ String option = ma.group(3);
1126+ String eventname = ma.group(4);
1127+ String network = ma.group(5);
1128+ // chnum
1129+ // branchexists
1130+ // branchnum
1131+ String ch = ma.group(9);
1132+ String repeat = ma.group(10);
1133+ String datetime = ma.group(11);
1134+ String duration = ma.group(12);
1135+ // conflictstart
1136+ // conflictend
1137+ String device_id = ma.group(15);
1138+ String folder_id = ma.group(16);
1139+ String recmode = ma.group(17);
1140+ String mochidashi = ma.group(18);
1141+ // video_es
1142+ // audio_es
1143+ // data_es
1144+ // recording
1145+
1146+ // 基本情報をセットする
1147+ String tuner = r.getTuner();
1148+ setReserveBasicInfo(r, exec, option, eventname, network, ch, repeat, datetime, duration);
1149+ // チューナはそのまま
1150+ r.setTuner(tuner);
1151+
1152+ // 保存先ドライブ
1153+ String device_name = value2text(device, device_id);
1154+ r.setRec_device(device_name);
1155+
1156+ // 保存先フォルダー
1157+ String folder_name = value2text(folder, device_id + ":" + folder_id);
1158+ r.setRec_folder(folder_name);
1159+
1160+ // 録画モード
1161+ String recmode_name = value2text(vrate, recmode);
1162+ r.setRec_mode(recmode_name);
1163+
1164+ // 持ち出し
1165+ String portable_name = value2text(portable, mochidashi);
1166+ r.setRec_portable(portable_name);
1167+
1168+ return(true);
1169+ }
1170+
1171+ /**
1172+ * レコーダーから取得できない情報は直接コピー(既存のリストから探して)
1173+ */
1174+ protected void copyAttributesT2007(ReserveList to, ArrayList<ReserveList> fromlist) {
1175+ ReserveList olde = null;
1176+ for ( ReserveList from : fromlist ) {
1177+ if ( from.getId() != null && from.getId().equals(to.getId()) ) {
1178+ copyAttributeT2007(to, olde = from);
1179+ break;
1180+ }
1181+ }
1182+
1183+ // DIGAの終了時間"未定"対応だけど、別にDIGAかどうか確認したりはしない。
1184+ setAttributesDiga(to,olde);
1185+ }
1186+
1187+ /**
1188+ * レコーダーから取得できない情報は直接コピー(既存エントリから直に)
1189+ */
1190+ protected void copyAttributeT2007(ReserveList to, ReserveList from) {
1191+ // 鯛ナビの内部フラグ
1192+ to.setAutocomplete(from.getAutocomplete());
1193+
1194+ // 予約一覧からは取得できない情報
1195+ to.setDetail(from.getDetail());
1196+ to.setRec_genre(from.getRec_genre());
1197+ to.setRec_autodel(from.getRec_autodel());
1198+
1199+ // BZ700以降の取得一覧から取得できない画質の対応
1200+ if (to.getRec_mode().equals("")) {
1201+ to.setRec_mode(from.getRec_mode());
1202+ }
1203+ }
1204+
1205+ //
1206+ protected void translateAttributeTuner(ReserveList entry) {
1207+ if (entry.getTuner().startsWith("TS")) {
1208+ entry.setTuner(entry.getTuner().replaceFirst("^TS", "DR"));
1209+ }
1210+ }
1211+
1212+ /*
1213+ * 登録/削除要求のPOSTデータを生成する
1214+ */
1215+ protected String createPostData(ReserveList r, String idBefore) {
1216+
1217+ String id = idBefore;
1218+ String exec = EXEC_YES;
1219+ String option = OPTION_PROGRAM;
1220+ String eventname = "";
1221+ String network = NETWORK_UVD;
1222+ String chnum = "0";
1223+ String branchexists = BRANCH_NO;
1224+ String branchnum = "0";
1225+ String ch = "";
1226+ String repeat = REPEAT_NONE;
1227+ String datetime = "";
1228+ String duration = "";
1229+ String conflictstart = "0";
1230+ String conflictend = "0";
1231+ String drive_id = DEVICE_HDD;
1232+ String folder_id = FOLDER_NONE;
1233+ String recmode = RECMODE_DR;
1234+ String mochidashi = MOCHIDASHI_NONE;
1235+ String video_es = "0";
1236+ String audio_es = "0";
1237+ String data_es = "0";
1238+ String recording = "0";
1239+
1240+ boolean bUpdate = !idBefore.equals("");
1241+
1242+ // スキップ
1243+ if (!r.getExec())
1244+ exec = EXEC_NO;
1245+
1246+ // オプション
1247+ if (bUpdate)
1248+ option = r.getRec_option();
1249+
1250+ // タイトル(eventname)
1251+ try {
1252+ if (!r.getAutocomplete())
1253+ eventname = URLEncoder.encode(CommonUtils.substringrb(r.getTitle(),80), thisEncoding);
1254+ } catch (UnsupportedEncodingException e) {
1255+ // TODO Auto-generated catch block
1256+ e.printStackTrace();
1257+ }
1258+
1259+ // チャンネル(ch)
1260+ String channel = cc.getCH_WEB2CODE(r.getCh_name());
1261+ ch = cc.getCH_CODE2REC(channel);
1262+
1263+ // 枝番有(branchexists,branchnum)
1264+ branchnum = text2value(tvsBranch, channel);
1265+ branchexists = Integer.parseInt(branchnum) > 0 ? BRANCH_YES : BRANCH_NO;
1266+
1267+ // ネットワーク(network)
1268+ String typ = text2value(chtype, channel);
1269+ switch(typ){
1270+ case CHTYPE_UVD:
1271+ network = NETWORK_UVD; // 地上デジタル
1272+ break;
1273+ case CHTYPE_BSD:
1274+ network = NETWORK_BSD; // BSデジタル
1275+ ch = ch.substring(CHPREFIX_BS.length());
1276+ break;
1277+ case CHTYPE_CSD:
1278+ network = NETWORK_CSD; // 110度CSデジタル
1279+ ch = ch.substring(CHPREFIX_CS.length());
1280+ break;
1281+ case CHTYPE_L1:
1282+ network = NETWORK_L1; // 外部入力(L1)
1283+ break;
1284+ case CHTYPE_L2:
1285+ network = NETWORK_L2; // 外部入力(L2)
1286+ break;
1287+ case CHTYPE_L3:
1288+ network = NETWORK_L3; // 外部入力(L3)
1289+ break;
1290+ case CHTYPE_L4:
1291+ network = NETWORK_L4; // 外部入力(L4)
1292+ break;
1293+ case CHTYPE_NET:
1294+ network = NETWORK_NET; // 外部入力(NET)
1295+ break;
1296+ default:
1297+ // 普通ここには落ちない
1298+ if (ch.startsWith("C")) {
1299+ network = NETWORK_L1; // "C***"は外部入力(L1)
1300+ }
1301+ else if (ch.startsWith("SP")) {
1302+ network = NETWORK_L3; // "SP***"は外部入力(L3)
1303+ }
1304+ else {
1305+ network = NETWORK_L2; // 未定義は全部外部入力(L2)
1306+ }
1307+ break;
1308+ }
1309+
1310+ chnum = ch;
1311+
1312+ // 繰り返しパターン(repeat)
1313+ String pattern = r.getRec_pattern();
1314+ repeat = text2value(tvsPatternName, pattern);
1315+ if (repeat.equals(""))
1316+ repeat = REPEAT_NONE; // 繰り返しなし
1317+
1318+ // 開始日時(datetime)
1319+ if (!repeat.equals(REPEAT_NONE)){
1320+ String pid = value2text(tvsPatternCode, repeat);
1321+ r.setRec_pattern_id(Integer.parseInt(pid));
1322+
1323+ pattern = CommonUtils.getNextDate(r);
1324+ }
1325+
1326+ Matcher ma = Pattern.compile("^(\\d+)/(\\d+)/(\\d+)").matcher(pattern);
1327+ if (ma.find()){
1328+ String startDateTime = String.format("%04d%02d%02d %02d:%02d:00",
1329+ Integer.parseInt(ma.group(1)),
1330+ Integer.parseInt(ma.group(2)),
1331+ Integer.parseInt(ma.group(3)),
1332+ Integer.parseInt(r.getAhh()),
1333+ Integer.parseInt(r.getAmm()));
1334+
1335+ SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
1336+
1337+ // Date型に変換する
1338+ Date date = new Date();
1339+ try {
1340+ date = sdf.parse(startDateTime);
1341+ } catch (ParseException e) {
1342+ // TODO 自動生成された catch ブロック
1343+ e.printStackTrace();
1344+ }
1345+
1346+ // UNIX時刻/1000を取得する(単位秒)
1347+ datetime = String.valueOf(date.getTime()/1000);
1348+ }
1349+
1350+ // 録画時間(duration)(単位秒)
1351+ String min = CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm());
1352+ duration = String.valueOf(Integer.parseInt(min)*60);
1353+
1354+ // ドライブ(drive_id)/フォルダ(folder_id)
1355+ drive_id = text2value(device, r.getRec_device());
1356+ String s = text2value(folder, r.getRec_folder());
1357+
1358+ // <drive_id>:<folder_id> から<folder_id>を取り出す。ただし「指定なし」は"0"
1359+ int idx = s.indexOf(':');
1360+ if (idx != -1)
1361+ folder_id = s.substring(idx+1);
1362+
1363+ // 録画モード(recmode)
1364+ recmode = text2value(vrate, r.getRec_mode());
1365+
1366+ // 持ち出し
1367+ mochidashi = text2value(portable, r.getRec_portable());
1368+
1369+ String postData =
1370+ "reserveInfo[id]=" + id + "&" +
1371+ "reserveInfo[exec]=" + exec + "&" +
1372+ "reserveInfo[option]=" + option + "&" +
1373+ "reserveInfo[eventname]=" + eventname + "&" +
1374+ "reserveInfo[network]=" + network + "&" +
1375+ "reserveInfo[chnum]=" + chnum + "&" +
1376+ "reserveInfo[branchexists]=" + branchexists + "&" +
1377+ "reserveInfo[branchnum]=" + branchnum + "&" +
1378+ "reserveInfo[ch]=" + ch + "&" +
1379+ "reserveInfo[repeat]=" + repeat + "&" +
1380+ "reserveInfo[datetime]=" + datetime + "&" +
1381+ "reserveInfo[duration]=" + duration + "&" +
1382+ "reserveInfo[conflictstart]=" + conflictstart + "&" +
1383+ "reserveInfo[conflictend]=" + conflictend + "&" +
1384+ "reserveInfo[drive_id]=" + drive_id + "&" +
1385+ "reserveInfo[folder_id]=" + folder_id + "&" +
1386+ "reserveInfo[recmode]=" + recmode + "&" +
1387+ "reserveInfo[mochidashi]=" + mochidashi + "&" +
1388+ "reserveInfo[video_es]=" + video_es + "&" +
1389+ "reserveInfo[audio_es]=" + audio_es + "&" +
1390+ "reserveInfo[data_es]=" + data_es + "&" +
1391+ "reserveInfo[recording]=" + recording;
1392+
1393+ if (getDebug())
1394+ System.out.println("PostData=[" + postData + "]");
1395+
1396+ return postData;
1397+ }
1398+
1399+ /*
1400+ * 予約登録画面の初期化情報を取得する。これを呼ばないと、後で登録要求した時にERR_EXCLUSIVEエラーになる
1401+ */
1402+ protected boolean loadDialogInitData( String id) {
1403+ // おまじない
1404+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1405+
1406+ reportProgress(MSGID+"予約登録画面を初期化します.");
1407+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadDialogInitializationData.php?schId=0", null, thisEncoding);
1408+// String header = d[0];
1409+ String response= d[1];
1410+
1411+ if (response == null) {
1412+ errmsg = ERRID+ERRMSG_NORESPONSE;
1413+ System.out.println(errmsg);
1414+ return(false);
1415+ }
1416+
1417+ return(true);
1418+ }
1419+
1420+ /*
1421+ * 登録要求の応答をチェックする
1422+ */
1423+ protected boolean checkReserveResponse( ReserveList r, String response ) {
1424+ if (response == null) {
1425+ errmsg = ERRID+ERRMSG_NORESPONSE;
1426+ return(false);
1427+ }
1428+
1429+ // {"result":24,"reason":"ERR_EXCLUSIVE","schid":"0"}
1430+ Matcher ma = Pattern.compile("\\{" +
1431+ "\"result\":(\\d+)," +
1432+ "\"reason\":\"([^\"]*)\"," +
1433+ "\"schid\":\"(\\d+)\"\\}").matcher(response);
1434+
1435+ if ( ma.find() ) {
1436+ String result = ma.group(1);
1437+ String reason = ma.group(2);
1438+ String schid = ma.group(3);
1439+
1440+ switch(result){
1441+ case RESULT_OK:
1442+ break;
1443+ case "3":
1444+ reportProgress("3:番組が見つからなかったため、日時指定予約として登録しました。");
1445+ break;
1446+ case "4":
1447+ reportProgress("4:予約が重複しています。");
1448+ break;
1449+ case "5":
1450+ reportProgress("5:持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1451+ break;
1452+ case "6":
1453+ reportProgress("6:持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1454+ break;
1455+ case "7":
1456+ reportProgress("7:番組が見つからなかったため、日時指定予約として登録しました。");
1457+ break;
1458+ case "8":
1459+ reportProgress("8:番組が見つからなかったため、日時指定予約として登録しました。\n" +
1460+ "持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1461+ break;
1462+ case "9":
1463+ reportProgress("9:番組が見つからなかったため、日時指定予約として登録しました。\n" +
1464+ "持ち出し変換の上限を超えているため、持ち出し設定を「なし」として登録しました。");
1465+ break;
1466+ default:
1467+ errmsg = ERRID+"予約登録に失敗しました。(result=" + result + ",reason=" + reason + ")";
1468+ reportProgress(errmsg);
1469+ return(false);
1470+ }
1471+
1472+ if (! schid.equals("0")){
1473+ r.setId(schid);
1474+ reportProgress(MSGID+"予約IDは"+r.getId()+"です。");
1475+ }
1476+
1477+ if (!result.equals(RESULT_OK))
1478+ getReserveDetail(r);
1479+ }
1480+ else{
1481+ errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1482+ reportProgress(errmsg);
1483+ return(false);
1484+ }
1485+
1486+ return(true);
1487+ }
1488+
1489+ /*
1490+ * 予約内容を調整する
1491+ */
1492+ protected void adjustReserve(ReserveList r) {
1493+ // 予約パターンID
1494+ r.setRec_pattern_id(getRec_pattern_Id(r.getRec_pattern()));
1495+
1496+ // 次回予定日
1497+ r.setRec_nextdate(CommonUtils.getNextDate(r));
1498+
1499+ // 録画長
1500+ r.setRec_min(CommonUtils.getRecMin(r.getAhh(),r.getAmm(),r.getZhh(),r.getZmm()));
1501+
1502+ // 開始日時・終了日時
1503+ getStartEndDateTime(r);
1504+ }
1505+
1506+ /*
1507+ * フォルダの作成ないしフォルダ名称を変更する
1508+ */
1509+ protected boolean setFolderName(String device_id, String fol_id, String folder_name) {
1510+ String action = fol_id != null ? "フォルダ名更新" : "フォルダ作成";
1511+ String folder_id = extractFolderID(fol_id);
1512+
1513+ String fnameEnc = "";
1514+ try {
1515+ fnameEnc = URLEncoder.encode(CommonUtils.substringrb(folder_name,80), thisEncoding);
1516+ } catch (UnsupportedEncodingException e) {
1517+ // TODO 自動生成された catch ブロック
1518+ e.printStackTrace();
1519+ }
1520+
1521+ String pstr =
1522+ "drive_id=" + device_id + "&" +
1523+ "folder_id=" + (folder_id != null ? folder_id : "NEWFOLDER") + "&" +
1524+ "folder_name=" + fnameEnc;
1525+
1526+ for (int n=0; n<3; n++){
1527+ // おまじない
1528+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1529+
1530+ // RDへ情報送信
1531+ reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
1532+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/folderset/SetFolderName.php?" + pstr, null, thisEncoding);
1533+// String header = d[0];
1534+ String response = d[1];
1535+
1536+ // レスポンスから処理結果を取得する
1537+ String [] rc = getReasonFromResponse( response );
1538+ if (rc == null) {
1539+ errmsg = ERRID+ERRMSG_NORESPONSE;
1540+ return(false);
1541+ }
1542+
1543+ String result = rc[0];
1544+ String reason = rc[1];
1545+
1546+ if (result.equals(RESULT_OK))
1547+ break;
1548+
1549+ if (result.equals(RESULT_OTHER) || result.equals(RESULT_BUSY)){
1550+ if (mountUsbDrive(device_id))
1551+ continue;
1552+ }
1553+
1554+ errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
1555+ return(false);
1556+ }
1557+
1558+ // フォルダ一覧を取得し直す
1559+ setSettingFolders();
1560+ saveFolders();
1561+
1562+ // タイトルに含まれるフォルダ名を更新する
1563+ updateFolderNameOfTitles();
1564+
1565+ return (true);
1566+ }
1567+
1568+ /*
1569+ * タイトルに含まれるフォルダ名を更新する
1570+ */
1571+ protected void updateFolderNameOfTitles(){
1572+ ArrayList<TitleInfo> list = getTitles();
1573+
1574+ for (TitleInfo ti : list){
1575+ ArrayList<TextValueSet> ts = ti.getRec_folder();
1576+
1577+ for (TextValueSet t : ts){
1578+ t.setText(value2text(folder, t.getValue()));
1579+ }
1580+
1581+ ti.setRec_folder(ts);
1582+ }
1583+
1584+ setTitles(list);
1585+
1586+ saveTitles(DEVICE_ID);
1587+ }
1588+ /*
1589+ * USB-HDDをマウントする
1590+ */
1591+ protected boolean mountUsbDrive(String device_id) {
1592+ reportProgress(MSGID+"ドライブをマウントします:"+device_id);
1593+
1594+ String pstr = "drive_id=" + device_id;
1595+
1596+ // おまじない
1597+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1598+
1599+ // RDへ情報送信
1600+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/ChangeMountedUSB.php?" + pstr, null, thisEncoding);
1601+// String header = d[0];
1602+ String response = d[1];
1603+
1604+ // 結果の確認
1605+ if ( !checkGeneralResponse( "USB HDDのマウント", response) ){
1606+ return(false);
1607+ }
1608+
1609+ return true;
1610+ }
1611+
1612+ /*
1613+ * 一般的な応答をチェックする
1614+ */
1615+ protected boolean checkGeneralResponse( String action, String response){
1616+ String [] rc = getReasonFromResponse( response );
1617+
1618+ if (rc == null) {
1619+ errmsg = ERRID+ERRMSG_NORESPONSE;
1620+ return(false);
1621+ }
1622+
1623+ String result = rc[0];
1624+ String reason = rc[1];
1625+
1626+ if (! result.equals(RESULT_OK)){
1627+ errmsg = action + "に失敗しました。(result=" + result + ",reason=" + reason + ")";
1628+ reportProgress(errmsg);
1629+ return(false);
1630+ }
1631+
1632+ return(true);
1633+ }
1634+
1635+ /*
1636+ * 応答メッセージから結果を取得する
1637+ */
1638+ protected String[] getReasonFromResponse( String response){
1639+ if (response == null) {
1640+ return(null);
1641+ }
1642+
1643+ // 先頭部分をチェックする
1644+ Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1645+ if ( ! mh.find()) {
1646+ return (null);
1647+ }
1648+
1649+ // 応答メッセージをパースする
1650+ // {"result":24,"reason":"ERR_EXCLUSIVE"}
1651+ Matcher ma = Pattern.compile("\\{" +
1652+ "\"result\":(\\d+)," +
1653+ "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1654+ if ( !ma.find() )
1655+ return(null);
1656+
1657+ String [] rc = new String[2];
1658+
1659+ rc[0] = ma.group(1);
1660+ rc[1] = ma.group(2);
1661+
1662+ return(rc);
1663+ }
1664+
1665+ /*
1666+ * TitleInfoを startDateTime, cotent_id順にソートする
1667+ *
1668+ */
1669+ public class TitleInfoComparator implements Comparator<TitleInfo>{
1670+
1671+ @Override
1672+ public int compare(TitleInfo p1, TitleInfo p2) {
1673+ int rc = p1.getStartDateTime().compareTo(p2.getStartDateTime());
1674+ if (rc != 0){
1675+ return rc;
1676+ }
1677+
1678+ return p1.getSerial() - p2.getSerial();
1679+ }
1680+ }
1681+ /*
1682+ * タイトル一覧を取得する
1683+ */
1684+ protected ArrayList<TitleInfo> getTitleList(String device_id, boolean mountedOnly) {
1685+ String str = "drive=" + device_id + "&org_pl=0";
1686+ String response = "";
1687+
1688+ for (int n=0; n<3; n++){
1689+ // おまじない
1690+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1691+
1692+ // RDからタイトル一覧を取り出す
1693+ reportProgress(MSGID+"タイトル一覧を取得します:"+device_id);
1694+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadTitleList.php?" + str, null, thisEncoding);
1695+// String header = d[0];
1696+ response= d[1];
1697+
1698+ if (response == null) {
1699+ errmsg = ERRID+ERRMSG_NORESPONSE;
1700+ return(null);
1701+ }
1702+
1703+ // 先頭部分をチェックする
1704+ Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1705+ if ( ! mh.find()) {
1706+ errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1707+ return(null);
1708+ }
1709+
1710+ // 応答メッセージをパースする
1711+ // {"result":24,"reason":"ERR_EXCLUSIVE"}
1712+ Matcher mr = Pattern.compile("\\{" +
1713+ "\"result\":(\\d+)," +
1714+ "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1715+
1716+ if ( !mr.find() )
1717+ break;
1718+
1719+ String result = mr.group(1);
1720+ String reason = mr.group(2);
1721+
1722+ if (result.equals("1")){
1723+ if (mountedOnly){
1724+ reportProgress(MSGID+"ドライブがマウントされていないので取得を中止します:"+device_id);
1725+ return(null);
1726+ }
1727+
1728+ if (mountUsbDrive(device_id))
1729+ continue;
1730+
1731+ return(null);
1732+ }
1733+
1734+ errmsg = "録画タイトルの取得に失敗しました。(result=" + result + ",reason=" + reason + ")";
1735+ reportProgress(errmsg);
1736+ return(null);
1737+ }
1738+
1739+ ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
1740+
1741+ // {"num":4,"id":"51a6a4900016","titlename":"[映][SS]スティーヴン・キング 骨の袋[前編]",
1742+ // "folders":[{"folder_id":9}],"genres":[{"genrecode":15}],"ch":"BS201",
1743+ // "datetime":1369843200,"duration":4858,"newflag":true,"autorecflag":false},
1744+ Matcher ma = Pattern.compile("\\{" +
1745+ "\"num\":(\\d+)," + // 1
1746+ "\"id\":\"([^\"]+)\"," + // 2
1747+ "\"titlename\":\"([^\"]+)\"," + // 3
1748+ "\"folders\":\\[([^\\]]*)\\]," + // 4
1749+ "\"genres\":\\[([^\\]]*)\\]," + // 5
1750+ "\"network\":(\\d+)," + // 6
1751+ "\"ch\":\"([^\"]+)\"," + // 7
1752+ "\"datetime\":(\\d+)," + // 8
1753+ "\"duration\":(\\d+)," + // 9
1754+ "\"newflag\":(false|true)," + // 10
1755+ "\"autorecflag\":(false|true)," + // 11
1756+ "\"autodelflag\":(false|true)," + // 12
1757+ "\"protection\":(false|true)," + // 13
1758+ "\"mochidashiState\":(\\d+)," + // 14
1759+ "\"recmode\":(\\d+)," + // 15
1760+ "\"recording\":(false|true)," + // 16
1761+ "\"portableEnable\":(false|true)" + // 17
1762+ "\\}").matcher(response);
1763+
1764+ int serial = 0;
1765+
1766+ while ( ma.find() ) {
1767+ // 個々のデータを取り出す
1768+ TitleInfo entry = new TitleInfo();
1769+
1770+ String id = ma.group(2);
1771+ String titlename = unescapeJavaString(ma.group(3));
1772+ String folders = ma.group(4);
1773+ String genres = ma.group(5);
1774+ String network = ma.group(6);
1775+ String ch = ma.group(7);
1776+ String datetime = ma.group(8);
1777+ String duration = ma.group(9);
1778+ String recmode = ma.group(15);
1779+ Boolean recording = ma.group(16).equals("true");
1780+// String newflag = ma.group(9);
1781+// String autorecflag = ma.group(10);
1782+
1783+ // チャンネルを変換する
1784+ ch = getFormalChFromNetworkAndCh(network, ch);
1785+
1786+ // タイトルID
1787+ entry.setId(id);
1788+
1789+ // すでに同じIDのタイトルがある場合はその情報を引き継ぐ
1790+ TitleInfo tiOld = getTitleInfo(id);
1791+ if (tiOld != null)
1792+ entry = tiOld.clone();
1793+
1794+ // 基本情報をセットする
1795+ entry.setSerial(++serial);
1796+ setTitleBasicInfo(entry, titlename, ch, datetime, duration, device_id, folders, genres);
1797+
1798+ // 録画モード
1799+ String recmode_name = value2text(vrate, recmode);
1800+ entry.setRec_mode(recmode_name);
1801+
1802+
1803+ entry.setRecording(recording);
1804+
1805+ // 予約情報を保存
1806+ list.add(entry.clone());
1807+ }
1808+
1809+ list.sort(new TitleInfoComparator());
1810+
1811+ return(list);
1812+ }
1813+
1814+ /***
1815+ * タイトルの基本情報をセットする
1816+ */
1817+ protected void setTitleBasicInfo(TitleInfo entry, String titlename,
1818+ String ch, String datetime, String duration, String device_id, String folders, String genres){
1819+
1820+ // 開始日、終了日
1821+ int nbsecs = Integer.parseInt(datetime); // 開始日時(UNIX時間/1000)
1822+ int secs = Integer.parseInt(duration); // 録画時間(sec)
1823+ int nesecs = nbsecs + secs; // 終了日時(UNIX時間/1000)
1824+ Date bdate = new Date(nbsecs*1000L); // 開始日時(Date)
1825+ Date edate = new Date(nesecs*1000L); // 終了日時(Date)
1826+
1827+ SimpleDateFormat sfdm = new SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.JAPAN);
1828+ entry.setStartDateTime(sfdm.format(bdate));
1829+ entry.setEndDateTime(sfdm.format(edate));
1830+
1831+ // 開始、終了時刻
1832+ SimpleDateFormat sfh = new SimpleDateFormat("HH");
1833+ SimpleDateFormat sfm = new SimpleDateFormat("mm");
1834+ String ahh = sfh.format(bdate);
1835+ String amm = sfm.format(bdate);
1836+ String zhh = sfh.format(edate);
1837+ String zmm = sfm.format(edate);
1838+ entry.setAhh(ahh);
1839+ entry.setAmm(amm);
1840+ entry.setZhh(zhh);
1841+ entry.setZmm(zmm);
1842+
1843+
1844+ // 次の録画日などを計算する
1845+ SimpleDateFormat sfd = new SimpleDateFormat("yyyy/MM/dd(E)", Locale.JAPAN);
1846+ entry.setRec_date(sfd.format(bdate));
1847+ entry.setRec_min(String.valueOf(secs/60));
1848+
1849+ // タイトル
1850+ entry.setTitle(titlename);
1851+
1852+ // チャンネル
1853+ entry.setChannel(ch);
1854+
1855+ // デバイス
1856+ String device_name = value2text(device, device_id);
1857+ entry.setRec_device(device_name);
1858+
1859+ // フォルダー
1860+ entry.setRec_folder(parseFolders(device_id, folders));
1861+
1862+ // ジャンル
1863+ entry.setRec_genre(parseGenres(genres));
1864+ }
1865+
1866+ /*
1867+ * タイトル詳細情報を取得する
1868+ */
1869+ protected boolean getTitleDetail( TitleInfo t) {
1870+
1871+ String id = t.getId();
1872+ String device_name = t.getRec_device();
1873+ String device_id = text2value(device, device_name);
1874+ String title_id = t.getId();
1875+ String str = "drive_id=" + device_id + "&title_id=" + title_id;
1876+ String response = "";
1877+
1878+ for (int n=0; n<3; n++){
1879+ // おまじない
1880+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
1881+
1882+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadTitleDetailData.php?" + str, null, thisEncoding);
1883+// String header = d[0];
1884+ response= d[1];
1885+
1886+ if (response == null) {
1887+ errmsg = ERRID+ERRMSG_NORESPONSE;
1888+ System.out.println(errmsg);
1889+ return(false);
1890+ }
1891+
1892+ // 先頭部分をチェックする
1893+ Matcher mh = Pattern.compile("\\{\"NETdeNAVI\"").matcher(response);
1894+ if ( ! mh.find()) {
1895+ errmsg = ERRID+ERRMSG_INVALIDRESPONSE;
1896+ return (false);
1897+ }
1898+
1899+ // 応答メッセージをパースする
1900+ // {"result":24,"reason":"ERR_EXCLUSIVE"}
1901+ Matcher mr = Pattern.compile("\\{" +
1902+ "\"result\":(\\d+)," +
1903+ "\"reason\":\"([^\"]*)\"\\}").matcher(response);
1904+
1905+ if ( !mr.find() )
1906+ break;
1907+
1908+ String result = mr.group(1);
1909+ String reason = mr.group(2);
1910+
1911+ if (result.equals("1")){
1912+ if (mountUsbDrive(device_id))
1913+ continue;
1914+ }
1915+
1916+ errmsg = "タイトル詳細情報の取得に失敗しました。(result=" + result + ",reason=" + reason + ")";
1917+ reportProgress(errmsg);
1918+ return(false);
1919+ }
1920+
1921+ // {"NETdeNAVI":{"titleDetailInfo":{"id":"589285bc000b","unique_id":"589285bc000b",
1922+ // "titlename":"この素晴らしい世界に祝福を!2 第4話「この貴族の令嬢に良縁を!」",
1923+ // "folders":[{"folder_id":7}],"ch":"091","recmode":"DR","genres":[{"genrecode":7}],
1924+ // "datetime":1485965100,"duration":1807,"copycount":10,"dlnaObjectID":"RD_0001000B_0000010200",
1925+ // "recording":false,
1926+ // "chapter":[{"chaptername":"@","duration":39,"changeFlag":0},
1927+ // {"chaptername":"","duration":168,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1928+ // {"chaptername":"","duration":509,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1929+ // {"chaptername":"","duration":708,"changeFlag":0},{"chaptername":"@","duration":61,"changeFlag":0},
1930+ // {"chaptername":"","duration":17,"changeFlag":0},{"chaptername":"@","duration":179,"changeFlag":0}]}}}
1931+ Matcher ma = Pattern.compile("\\{" +
1932+ "\"id\":\"([^\"]+)\"," + // 1
1933+ "\"unique_id\":\"([^\"]+)\"," + // 2
1934+ "\"titlename\":\"([^\"]+)\"," + // 3
1935+ "\"folders\":\\[([^\\]]*)\\]," + // 4
1936+ "\"network\":(\\d+)," + // 5
1937+ "\"chNum\":\"([^\"]+)\"," + // 6
1938+ "\"branchNum\":(\\d+)," + // 7
1939+ "\"branchNumExists\":(false|true)," +// 8
1940+ "\"ch\":\"([^\"]+)\"," + // 9
1941+ "\"recmode\":\\{\"id\":(\\d+),\"name\":\"([^\"]+)\"\\}," + // 10,11
1942+ "\"genres\":\\[([^\\]]*)\\]," + // 12
1943+ "\"datetime\":(\\d+)," + // 13
1944+ "\"duration\":(\\d+)," + // 14
1945+ "\"copycount\":(\\d+)," + // 15
1946+ "\"dlnaObjectID\":\"([^\"]*)\"," + // 16
1947+ "\"rating\":(\\d+)," + // 17
1948+ "\"recording\":(false|true)," + // 18
1949+ "\"chapter\":\\[([^\\]]*)\\]," + // 19
1950+ "\"autodel\":(false|true)," + // 20
1951+ "\"protection\":(false|true)," + // 21
1952+ "\"mochidashiContentId\":\"([^\"]*)\"," + // 22
1953+ "\"resumeTime\":(\\d+)," + // 23
1954+ "\"shortTimeInfo\":\\[([^\\]]*)\\]," +// 24
1955+ "\"detailDescription\":\"([^\"]*)\"," + // 25
1956+ "\"profiles\":\\[([^\\]]*)\\]," + // 26
1957+ "\"mochidashiState\":(\\d+)," + // 27
1958+ "\"defective\":(false|true)," + // 28
1959+ "\"portableEnable\":(false|true)" + // 29
1960+ "\\}")
1961+ .matcher(response);
1962+
1963+ if ( !ma.find() ) {
1964+ reportProgress(ERRID+"タイトル詳細情報が取得できません.ID=" + id);
1965+ return (false);
1966+ }
1967+
1968+ String unique_id = ma.group(2);
1969+ String titlename = unescapeJavaString(ma.group(3));
1970+ String folders = ma.group(4);
1971+ String ch = ma.group(9);
1972+ String recmode = ma.group(11);
1973+ String genres = ma.group(12);
1974+ String datetime = ma.group(13);
1975+ String duration = ma.group(14);
1976+ String copycount = ma.group(15);
1977+ String dlnaObjectID = ma.group(16);
1978+ Boolean recording = ma.group(18).equals("true");
1979+// String recording = ma.group(12);
1980+ String chapter = ma.group(19);
1981+ String portableEnable = ma.group(29);
1982+
1983+ // 基本情報をセットする
1984+ setTitleBasicInfo(t, titlename, ch, datetime, duration, device_id, folders, genres);
1985+
1986+ // チャプター情報
1987+ t.setChapter(parseChapters(chapter));
1988+ t.setContentId(unique_id);
1989+
1990+ // 録画モード
1991+// String recmode_name = value2text(vrate, recmode);
1992+ t.setRec_mode("[" + recmode + "]");
1993+
1994+ // それ以外の詳細情報
1995+ HashMap<String,String> hmap = new HashMap<String,String>();
1996+ hmap.put("copycount", copycount);
1997+ hmap.put("dlnaObjectID", dlnaObjectID);
1998+ t.setHidden_params(hmap);
1999+
2000+ t.setRecording(recording);
2001+
2002+ t.setDetailLoaded(true);
2003+
2004+ return(true);
2005+ }
2006+
2007+ /**
2008+ * フォルダーのJSONテキストを解析する
2009+ */
2010+ protected ArrayList<TextValueSet> parseFolders(String device_id, String s) {
2011+
2012+ ArrayList<TextValueSet> list = new ArrayList<TextValueSet>();
2013+
2014+ // "folders":[{"folder_id":9}],
2015+ Matcher ma = Pattern.compile("\\{" +
2016+ "\"folder_id\":(\\d+)\\}") // 1
2017+ .matcher(s);
2018+
2019+ while ( ma.find() ) {
2020+ String folder_id = device_id + ":" + ma.group(1);
2021+ String folder_name = value2text(folder, folder_id);
2022+
2023+ TextValueSet t = new TextValueSet();
2024+ t.setText(folder_name);
2025+ t.setValue(folder_id);
2026+ list.add(t);
2027+ }
2028+
2029+ return(list);
2030+ }
2031+
2032+ /**
2033+ * ジャンルのJSONテキストを解析する
2034+ */
2035+ protected ArrayList<TextValueSet> parseGenres(String s) {
2036+
2037+ ArrayList<TextValueSet> list = new ArrayList<TextValueSet>();
2038+
2039+ // "genres":[{"genrecode":7}],
2040+ Matcher ma = Pattern.compile("\\{" +
2041+ "\"genrecode\":(\\d+)\\}") // 1
2042+ .matcher(s);
2043+
2044+ while ( ma.find() ) {
2045+ String genre_code = Integer.toHexString(Integer.parseInt(ma.group(1))).toUpperCase();
2046+ ProgGenre pg = ProgGenre.getByIEPG(genre_code);
2047+ String genre_name = pg != null ? pg.toString() : "";
2048+
2049+ TextValueSet t = new TextValueSet();
2050+ t.setText(genre_name);
2051+ t.setValue(genre_code);
2052+ list.add(t);
2053+ }
2054+
2055+ return(list);
2056+ }
2057+
2058+ /**
2059+ * チャプターのJSONテキストを解析する
2060+ */
2061+ protected ArrayList<ChapterInfo> parseChapters(String s) {
2062+
2063+ ArrayList<ChapterInfo> list = new ArrayList<ChapterInfo>();
2064+
2065+ // {"chaptername":"","duration":168,"changeFlag":0},
2066+ Matcher ma = Pattern.compile("\\{" +
2067+ "\"chaptername\":\"([^\"]*)\"," +
2068+ "\"duration\":(\\d+)," +
2069+ "\"changeFlag\":(\\d+)\\}")
2070+ .matcher(s);
2071+
2072+ while ( ma.find() ) {
2073+ String chaptername = unescapeJavaString(ma.group(1));
2074+ String duration = ma.group(2);
2075+ String changeFlag = ma.group(3);
2076+
2077+ ChapterInfo c = new ChapterInfo();
2078+ c.setName(chaptername);
2079+ c.setDuration(Integer.parseInt(duration));
2080+ c.setChangeFlag(changeFlag.equals("1"));
2081+
2082+ list.add(c);
2083+ }
2084+
2085+ return(list);
2086+ }
2087+
2088+ /**
2089+ * タイトルの編集をレコーダに通知する。これを先に呼ばないとタイトル情報更新時にエラーになる
2090+ */
2091+ private boolean notifyTitleEdit(String devid, String ttlid) {
2092+ errmsg = "";
2093+
2094+ // おまじない
2095+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2096+
2097+ // RDへの情報作成
2098+ String pstr = "device_id=" + devid + "&title_id=" + ttlid;
2099+
2100+ // RDへ情報送信
2101+ reportProgress(MSGID+"レコーダーにタイトル編集を通知します.");
2102+ String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/NotifyTitleEdit.php?" + pstr, null, thisEncoding);
2103+// String header = d[0];
2104+ String response = d[1];
2105+
2106+ if ( !checkGeneralResponse( "タイトル編集通知", response) ){
2107+ return(false);
2108+ }
2109+
2110+ return(true);
2111+ }
2112+
2113+ /*
2114+ * タイトル更新要求のPOSTデータを生成する
2115+ */
2116+ protected String createTitlePostData(TitleInfo t, TitleInfo o, String devId) {
2117+ String postData = "";
2118+ try {
2119+ String title = o != null && t.getTitle().equals(o.getTitle()) ? TITLE_NOCHANGED :
2120+ // ARIB外字を変換した文字列があれば元の外字に戻す
2121+ URLEncoder.encode(AribCharMap.ConvStringToArib(t.getTitle()), thisEncoding);
2122+
2123+ postData =
2124+ "drive_id=" + devId + "&" +
2125+ "title_id=" + t.getId() + "&" +
2126+ "titleName=" + title + "&" +
2127+ encodeFolders(t.getRec_folder()) + "&" +
2128+ "folder_change=1&" +
2129+ encodeChapters(t.getChapter());
2130+ } catch (UnsupportedEncodingException e) {
2131+ // TODO 自動生成された catch ブロック
2132+ e.printStackTrace();
2133+ }
2134+
2135+ return postData;
2136+ }
2137+
2138+ /**
2139+ * フォルダー情報をJSONから変換したPOSTデータの形式にエンコードする
2140+ */
2141+ protected String encodeFolders(ArrayList<TextValueSet> tvs){
2142+ String s = "";
2143+ int n=0;
2144+ for (TextValueSet t : tvs){
2145+ if ( !s.equals("") )
2146+ s += "&";
2147+ s += "folders[" + String.valueOf(n) + "][folder_id]=" + extractFolderID( t.getValue() );
2148+ n++;
2149+ }
2150+
2151+ return s;
2152+ }
2153+
2154+ /**
2155+ * チャプター情報をJSONから変換したPOSTデータの形式にエンコードする
2156+ */
2157+ protected String encodeChapters(ArrayList<ChapterInfo> ci){
2158+ String s = "";
2159+ int n=0;
2160+ for (ChapterInfo t : ci){
2161+ if ( !s.equals("") )
2162+ s += "&";
2163+ try {
2164+ s += "chapters[" + String.valueOf(n) + "][chnum]=" + String.valueOf(n) + "&" +
2165+ "chapters[" + String.valueOf(n) + "][chname]=" + URLEncoder.encode(t.getName(), thisEncoding);
2166+ } catch (UnsupportedEncodingException e) {
2167+ // TODO 自動生成された catch ブロック
2168+ e.printStackTrace();
2169+ }
2170+ n++;
2171+ }
2172+
2173+ return s;
2174+ }
2175+
2176+ /*
2177+ * 固定の各種設定情報を初期化する
2178+ */
2179+ protected void setSettingFixed() {
2180+ // 録画設定
2181+ setSettingVrate(vrate);
2182+
2183+ // パターン情報
2184+ setSettingPattern(tvsPatternCode, tvsPatternName);
2185+
2186+ // エンコーダー
2187+ setSettingEncoder(encoder);
2188+
2189+ // 持ち出し情報
2190+ setSettingPortable(portable);
2191+ }
2192+
2193+ /*
2194+ * 録画モードを初期化する
2195+ */
2196+ protected void setSettingVrate(ArrayList<TextValueSet> tvs) {
2197+ // {"str":"DR","val":0}, {"str":"AF","val":64}, {"str":"AN","val":65}, {"str":"AS","val":66},
2198+ // {"str":"AL","val":67}, {"str":"AE","val":68},
2199+ // {"str":"XP","val":32}, {"str":"SP","val":33}, {"str":"LP","val":34}, {"str":"EP","val":35},
2200+ // {"str":"自動(HD 4.7GB)","val":192}, {"str":"自動(HD 8.5GB)","val":193}, {"str":"自動(HD 25GB)","val":194},
2201+ // {"str":"自動(HD 50GB)","val":195}, {"str":"自動(標準 4.7GB)","val":197}
2202+
2203+ String texts[] = {
2204+ RECMODE_NAME_DR, "[AF]", "[AN]", "[AS]", "[AL]", "[AE]",
2205+ "[XP]", "[SP]", "[LP]", "[EP]",
2206+ "[自動(HD 4.7GB)]", "[自動(HD 8.5GB)]", "[自動(HD 25GB)]", "[自動(HD 50GB)]",
2207+ "[自動(標準 4.7GB)]",
2208+ null};
2209+ String values[] = {
2210+ RECMODE_DR, "64", "65", "66", "67", "68",
2211+ "32", "33", "34", "35",
2212+ "192", "193", "194", "195", "197",
2213+ null};
2214+
2215+ tvs.clear();
2216+
2217+ for (int n=0; values[n] != null; n++){
2218+ TextValueSet t = new TextValueSet();
2219+ t.setText(texts[n]);
2220+ t.setValue(values[n]);
2221+ tvs.add(t);
2222+ }
2223+ }
2224+
2225+ /*
2226+ * 繰り返しパターンを初期化する
2227+ */
2228+ protected void setSettingPattern(ArrayList<TextValueSet> tvsC, ArrayList<TextValueSet> tvsN) {
2229+ // {"num":0,"str":"しない"},{"num":1,"str":"毎週日"},{"num":2,"str":"毎週月"},{"num":4,"str":"毎週火"},
2230+ // {"num":8,"str":"毎週水"},{"num":16,"str":"毎週木"},{"num":32,"str":"毎週金"},{"num":64,"str":"毎週土"},
2231+ // {"num":62,"str":"月~金"},{"num":126,"str":"月~土"},{"num":124,"str":"火~土"},{"num":127,"str":"毎日"}
2232+ int codesRd[]={
2233+ 0, 1, 2, 3, 4, 5, RPTPTN_ID_SAT,
2234+ RPTPTN_ID_MON2THU, RPTPTN_ID_MON2FRI, RPTPTN_ID_MON2SAT, RPTPTN_ID_TUE2SAT, RPTPTN_ID_EVERYDAY, -1};
2235+ String codes[] = {
2236+ "1", "2", "4", "8", "16", "32", "64",
2237+ "30", "62", "126", "124", "127", null};
2238+ String names[] = {
2239+ "毎日曜日", "毎月曜日", "毎火曜日", "毎水曜日", "毎木曜日", "毎金曜日", "毎土曜日",
2240+ "毎月~木", "毎月~金", "毎月~土", "毎火~土", "毎日", null};
2241+
2242+ tvsC.clear();
2243+ tvsN.clear();
2244+
2245+ for (int n=0; codes[n] != null; n++){
2246+ TextValueSet tc = new TextValueSet();
2247+ tc.setText(String.valueOf(codesRd[n]));
2248+ tc.setValue(codes[n]);
2249+ tvsC.add(tc);
2250+
2251+ TextValueSet tn = new TextValueSet();
2252+ tn.setText(names[n]);
2253+ tn.setValue(codes[n]);
2254+ tvsN.add(tn);
2255+ }
2256+ }
2257+
2258+ /**
2259+ * エンコーダー情報を自動生成する
2260+ */
2261+ protected void setSettingEncoder(ArrayList<TextValueSet> tvs) {
2262+ tvs.clear();
2263+
2264+ // チューナー情報を自動生成する
2265+ if (getTunerNum() >= 2){
2266+ for (int i=1; i<=getTunerNum(); i++){
2267+ TextValueSet t = new TextValueSet();
2268+ t.setText("R" + i);
2269+ t.setValue("R" + i);
2270+ tvs.add(t);
2271+ }
2272+ }
2273+ }
2274+
2275+ /*
2276+ * 持ち出し情報を初期化する
2277+ */
2278+ protected void setSettingPortable(ArrayList<TextValueSet> tvs) {
2279+ // {"portableId":0,"portableStr":"しない"},{"portableId":1,"portableStr":"スマホ持ち出し"},
2280+ // {"portableId":3,"portableStr":"DVD持ち出し(VR)"},{"portableId":2,"portableStr":"SeeQVault対応SDカード転送"}
2281+ String codes[] = {
2282+ MOCHIDASHI_NONE, "1", "3", "2", null};
2283+ String names[] = {
2284+ "しない", "スマホ持ち出し", "DVD持ち出し(VR)", "SeeQVault対応SDカード転送", null};
2285+
2286+ tvs.clear();
2287+
2288+ for (int n=0; codes[n] != null; n++){
2289+ TextValueSet t = new TextValueSet();
2290+ t.setText(names[n]);
2291+ t.setValue(codes[n]);
2292+ tvs.add(t);
2293+ }
2294+ }
2295+
2296+ /*
2297+ * 各種設定情報をレコーダーから取得する
2298+ */
2299+ protected boolean setSettingVariable() {
2300+ // 設定情報
2301+// ArrayList<TextValueSet>discs = new ArrayList<TextValueSet>();
2302+// ArrayList<TextValueSet>repeats = new ArrayList<TextValueSet>();
2303+// setSettingSelect(repeats, discs, folder, vrate);
2304+
2305+ // 記録先デバイス
2306+ setSettingDevice();
2307+
2308+ // フォルダ一覧
2309+ setSettingFolders();
2310+
2311+ // チャンネルコードバリュー - uva、bsaは廃止 -
2312+ ArrayList<TextValueSet> tvsCV = new ArrayList<TextValueSet>();
2313+ ArrayList<TextValueSet> tvsCT = new ArrayList<TextValueSet>();
2314+ ArrayList<TextValueSet> tvsBR = new ArrayList<TextValueSet>();
2315+ setSettingChCodeValue(tvsCV, tvsCT, tvsBR);
2316+ if ( tvsCV.size() == 0 && tvsCT.size() == 0 && tvsBR.size() == 0) {
2317+ System.err.println(errmsg = ERRID+"【致命的エラー】 チャンネルコードバリューが取得できません");
2318+ return (false);
2319+ }
2320+ chvalue = tvsCV;
2321+ chtype = tvsCT;
2322+ tvsBranch = tvsBR;
2323+
2324+ return (true);
2325+ }
2326+
2327+ /*
2328+ * デバイス情報を取得する
2329+ */
2330+ protected boolean setSettingDevice() {
2331+ ArrayList<TextValueSet>tvsD = new ArrayList<TextValueSet>();
2332+ ArrayList<DeviceInfo>tvsDI = new ArrayList<DeviceInfo>();
2333+
2334+ reportProgress(MSGID+"ドライブ一覧を取得します.");
2335+
2336+ // おまじない
2337+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2338+
2339+ String res = "";
2340+
2341+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/LoadSelectInfo.php",null, thisEncoding);
2342+ res = d[1];
2343+
2344+ if (res == null) {
2345+ errmsg = ERRID+ERRMSG_NORESPONSE;
2346+ return (false);
2347+ }
2348+
2349+ //{"drive_id":0,"drivename":"HDD","drivetype":0,"playlist_enable":true,"folder_enable":true,
2350+ // "allsize":1897740,"freesize":1187093,"connected":true,"canwrite":true,"protected":false,
2351+ // "mounted":true,"ready":true,"formatType":12},
2352+ Matcher ma = Pattern.compile("\\{" +
2353+ "\"drive_id\":(\\d+)," + // 1
2354+ "\"drivename\":\"([^\"]+)\"," + // 2
2355+ "\"drivetype\":(\\d+)," + // 3
2356+ "\"playlist_enable\":(true|false)," + // 4
2357+ "\"folder_enable\":(true|false)," + // 5
2358+ "\"allsize\":(\\d+)," + // 6
2359+ "\"freesize\":(\\d+)," + // 7
2360+ "\"connected\":(true|false)," + // 8
2361+ "\"protected\":(true|false)," + // 9
2362+ "\"mounted\":(true|false)," + // 10
2363+ "\"ready\":(true|false)," + // 11
2364+ "\"pin_setting\":(true|false)," + // 12
2365+ "\"formatType\":(\\d+)" + // 13
2366+ "\\}").matcher(res);
2367+
2368+ int allsizeALL = 0;
2369+ int freesizeALL = 0;
2370+
2371+ while ( ma.find() ) {
2372+ String drive_id = ma.group(1);
2373+ String drive_name = ma.group(2);
2374+ String drive_type = ma.group(3);
2375+ boolean playlist_enable = ma.group(4).equals("true");
2376+ boolean folder_enable = ma.group(5).equals("true");
2377+ int allsize = Integer.parseInt(ma.group(6));
2378+ int freesize = Integer.parseInt(ma.group(7));
2379+ boolean connected = ma.group(8).equals("true");
2380+ boolean isprotected = ma.group(9).equals("true");
2381+ boolean mounted = ma.group(10).equals("true");
2382+ boolean ready = ma.group(11).equals("true");
2383+ boolean pin_setting = ma.group(12).equals("true");
2384+ int formatType = Integer.parseInt(ma.group(13));
2385+
2386+ TextValueSet t = new TextValueSet();
2387+ t.setValue(drive_id);
2388+ // デバイス名のコロン以降は無視する
2389+ t.setText(GetDevicePrefix(drive_name));
2390+ tvsD.add(t);
2391+
2392+ DeviceInfo di = new DeviceInfo();
2393+ di.setId(drive_id);
2394+ di.setName(drive_name);
2395+ di.setType(drive_type);
2396+ di.setPlaylistEnable(playlist_enable);
2397+ di.setFolderEnable(folder_enable);
2398+ di.setAllSize(allsize);
2399+ di.setFreeSize(freesize);
2400+ di.setFreeMin((int)Math.round((double)freesize/MIN_PER_MB));
2401+ di.setConnected(connected);
2402+ di.setProtected(isprotected);
2403+ di.setMounted(mounted);
2404+ di.setReady(ready);
2405+ di.setFormatType(formatType);
2406+ tvsDI.add(di);
2407+
2408+ allsizeALL += allsize;
2409+ freesizeALL += freesize;
2410+ }
2411+
2412+ TextValueSet t = new TextValueSet();
2413+ t.setValue(DEVICE_ALL);
2414+ t.setText(DEVICE_NAME_ALL);
2415+ tvsD.add(t);
2416+
2417+ DeviceInfo di = new DeviceInfo();
2418+ di.setId(DEVICE_ALL);
2419+ di.setName(DEVICE_NAME_ALL);
2420+ di.setAllSize(allsizeALL);
2421+ di.setFreeSize(freesizeALL);
2422+ di.setFreeMin((int)Math.round((double)freesizeALL/MIN_PER_MB));
2423+ tvsDI.add(di);
2424+
2425+ device = tvsD;
2426+ setDeviceInfos(tvsDI);
2427+
2428+ return (true);
2429+ }
2430+
2431+ /*
2432+ * デバイス情報を保存する
2433+ */
2434+ protected void saveDeviceInfos(){
2435+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2436+ devinfoTFile = "env/devinfo."+myFileId+".xml";
2437+
2438+ DeviceInfosToFile(getDeviceInfos(), devinfoTFile);
2439+ }
2440+ /*
2441+ * フォルダ一覧を取得する
2442+ */
2443+ protected boolean setSettingFolders() {
2444+ ArrayList<TextValueSet> tvsD = device;
2445+ ArrayList<TextValueSet> tvsF = new ArrayList<TextValueSet>();
2446+
2447+ reportProgress(MSGID+"フォルダ一覧を取得します.");
2448+
2449+ TextValueSet t0 = new TextValueSet();
2450+ t0.setText(FOLDER_NAME_NONE);
2451+ t0.setValue(FOLDER_NONE);
2452+ tvsF.add(t0);
2453+
2454+ for (int n=0; n<tvsD.size(); n++){
2455+ TextValueSet tvs = tvsD.get(n);
2456+
2457+ // RDへの情報作成
2458+ String drive_id = tvs.getValue();
2459+ String pstr = "drive_id=" + drive_id;
2460+
2461+ // おまじない
2462+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2463+
2464+ // RDへ情報送信
2465+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/ReloadFolderInfo.php?" + pstr, null, thisEncoding);
2466+ String res = d[1];
2467+
2468+ if (res == null) {
2469+ errmsg = ERRID+ERRMSG_NORESPONSE;
2470+ return (false);
2471+ }
2472+
2473+ Matcher ma = Pattern.compile("\\{" +
2474+ "\"folder_id\":(\\d+)," + // 1
2475+ "\"str\":\"([^\"]+)\"\\}") // 2
2476+ .matcher(res);
2477+
2478+ while ( ma.find() ) {
2479+ TextValueSet t = new TextValueSet();
2480+
2481+ String device_name = tvs.getText();
2482+ String folder_id = ma.group(1);
2483+ String folder_name = "[" + device_name + "] " + unescapeJavaString(ma.group(2));
2484+
2485+ t.setText(folder_name);
2486+ t.setValue(drive_id + ":" + folder_id);
2487+ tvsF.add(t);
2488+ }
2489+ }
2490+
2491+ folder = tvsF;
2492+
2493+ return (true);
2494+ }
2495+
2496+ /*
2497+ * フォルダー一覧をファイルに保存する
2498+ */
2499+ private void saveFolders(){
2500+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2501+ folderTFile = "env/folders."+myFileId+".xml";
2502+
2503+ TVSsave(folder, folderTFile);
2504+ }
2505+
2506+ /*
2507+ * タイトル一覧をファイルに保存する
2508+ */
2509+ private void saveTitles(String devId){
2510+ String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
2511+
2512+ if (!DEVICE_ID.equals(DEVICE_ALL)){
2513+ titleFile = "env/title."+myFileId+"." + DEVICE_ID + ".xml";
2514+
2515+ TitlesToFile(getTitles(), titleFile);
2516+ }
2517+ else{
2518+ for (DeviceInfo di : getDeviceInfos() ) {
2519+ if (di.getId().equals(DEVICE_ALL))
2520+ continue;
2521+
2522+ ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
2523+
2524+ for (TitleInfo ti : getTitles()){
2525+ if (di.getName().startsWith(ti.getRec_device()))
2526+ list.add(ti);
2527+ }
2528+
2529+ titleFile = "env/title."+myFileId+"." + di.getId() + ".xml";
2530+ TitlesToFile(list, titleFile);
2531+ }
2532+ }
2533+ }
2534+
2535+ /*
2536+ * チャンネル一覧を取得する
2537+ */
2538+ private void setSettingChCodeValue(ArrayList<TextValueSet> tvsvalue, ArrayList<TextValueSet> tvstype,
2539+ ArrayList<TextValueSet> tvsbranch) {
2540+ tvsvalue.clear();
2541+ tvstype.clear();
2542+ tvsbranch.clear();
2543+
2544+ reportProgress(MSGID+"チャンネル一覧を取得します.");
2545+
2546+ // おまじない
2547+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2548+
2549+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadChannelList.php",null, thisEncoding);
2550+ String res = d[1];
2551+
2552+ if (res == null) {
2553+ errmsg = ERRID+ERRMSG_NORESPONSE;
2554+ return;
2555+ }
2556+
2557+ // {"network":"地上","service":[... ]}
2558+ Matcher ma = Pattern.compile("\\{\"network\":\"([^\"]+)\",\"service\":\\[([^\\]]+)\\]\\}").matcher(res);
2559+ while ( ma.find() ) {
2560+ String network = ma.group(1);
2561+ String chlist = ma.group(2);
2562+ String prefix = "";
2563+
2564+ // "uvd","bsd","csd","l1","l2","l3"
2565+ String typ = "";
2566+ switch(network){
2567+ case "地上":
2568+ typ = CHTYPE_UVD;
2569+ break;
2570+ case "BS":
2571+ typ = CHTYPE_BSD;
2572+ prefix = CHPREFIX_BS;
2573+ break;
2574+ case "CS":
2575+ typ = CHTYPE_CSD;
2576+ prefix = CHPREFIX_CS;
2577+ break;
2578+ }
2579+
2580+ // {"channelid":"G7FE00400","channelNr":"011","channelName":"NHK総合1・東京","chnum":"011","branch":0,
2581+ // "branchNumExists":0,"networkId":32736,"serviceId":1024,"multiSub":false},
2582+ Matcher mb = Pattern.compile("\\{" +
2583+ "\"channelid\":\"([^\"]+)\"," + // 1
2584+ "\"channelNr\":\"([^\"]+)\"," + // 2
2585+ "\"channelName\":\"([^\"]+)\"," + // 3
2586+ "\"chnum\":\"([^\"]+)\"," + // 4
2587+ "\"branch\":(\\d+)," + // 5
2588+ "\"branchNumExists\":(\\d+)," + // 6
2589+ "\"networkId\":(\\d+)," + // 7
2590+ "\"serviceId\":(\\d+)," + // 8
2591+ "\"multiSub\":([^}]+)}") // 9
2592+ .matcher(chlist);
2593+
2594+ // var uvd_ch_text = new Array(
2595+ // "011-1",
2596+ // "012-1",
2597+ // var uvd_ch_value = new Array(
2598+ // "011:32736:1024",
2599+ // "012:32736:1025",
2600+
2601+ while ( mb.find() ) {
2602+ String chno = prefix + mb.group(2);
2603+ String chid = mb.group(4) + ":" + mb.group(7) + ":" + mb.group(8);
2604+
2605+ TextValueSet t = new TextValueSet();
2606+ t.setText(chno);
2607+ t.setValue(chid);
2608+ tvsvalue.add(t);
2609+
2610+ TextValueSet x = new TextValueSet();
2611+ x.setText(chid);
2612+ x.setValue(typ);
2613+ tvstype.add(x);
2614+
2615+ TextValueSet b = new TextValueSet();
2616+ b.setText(chid);
2617+ b.setValue(mb.group(5));
2618+ tvsbranch.add(b);
2619+ }
2620+ }
2621+ }
2622+
2623+ /*
2624+ * 録画設定情報を取得する(未使用)
2625+ */
2626+ protected void setSettingSelect(ArrayList<TextValueSet> tvsR, ArrayList<TextValueSet> tvsD,
2627+ ArrayList<TextValueSet> tvsF, ArrayList<TextValueSet> tvsV) {
2628+ tvsR.clear();
2629+ tvsD.clear();
2630+ tvsF.clear();
2631+ tvsV.clear();
2632+
2633+ reportProgress(MSGID+"録画設定情報を取得します.");
2634+
2635+ // おまじない
2636+ Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
2637+
2638+ String[] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/LoadDialogInitializationData.php?schId=0", null, thisEncoding);
2639+ String res = d[1];
2640+
2641+ if (res == null) {
2642+ errmsg = ERRID+ERRMSG_NORESPONSE;
2643+ return;
2644+ }
2645+
2646+ Matcher ma = Pattern.compile("\\{\"day_select\":\\[([^\\]]+)\\],").matcher(res);
2647+ if ( ma.find() ) {
2648+ Matcher mb = Pattern.compile("\\{\"str\":\"([^\"]+)\",\"val\":\"([^\"]+)\"\\}").matcher(ma.group(1));
2649+
2650+ while( mb.find() ){
2651+ TextValueSet t = new TextValueSet();
2652+ String repeat_id = ma.group(2);
2653+ String repeat_name = ma.group(1);
2654+ t.setValue(repeat_id);
2655+ t.setText(repeat_name);
2656+ tvsR.add(t);
2657+ }
2658+ }
2659+
2660+ Matcher mc = Pattern.compile("\\{\"disc_select\":\\[([^\\]]+)\\],").matcher(res);
2661+ if ( mc.find() ) {
2662+ Matcher md = Pattern.compile("\\{" +
2663+ "\"driveid\":\"([^\"]+)\"," + // 1
2664+ "\"str\":\"([^\"]+)\"," + // 2
2665+ "\"folder_select\":\\[([^\\]]+)\\]," + // 3
2666+ "\"pqmode_select\":\\[([^\\]]+)\\]") // 4
2667+ .matcher(mc.group(1));
2668+
2669+ while( md.find() ){
2670+ String drive_id = md.group(1);
2671+ String drive_name = md.group(2);
2672+ String folders = md.group(3);
2673+ String vrates = md.group(4);
2674+
2675+ TextValueSet t = new TextValueSet();
2676+ t.setValue(drive_id);
2677+ t.setText(drive_name);
2678+ tvsD.add(t);
2679+
2680+ Matcher me = Pattern.compile("\\{" +
2681+ "\"folderid\":\"([^\"]+)\"," + // 1
2682+ "\"str\":\"([^\"]+)\"\\}") // 2
2683+ .matcher(folders);
2684+
2685+ while( me.find() ){
2686+ String folder_id = mc.group(1);
2687+ String folder_name = "[" + drive_name + "] " + unescapeJavaString(me.group(2));
2688+
2689+ TextValueSet tc = new TextValueSet();
2690+ tc.setValue(folder_id);
2691+ tc.setText(folder_name);
2692+ tvsF.add(tc);
2693+ }
2694+
2695+ if (!drive_id.equals(DEVICE_HDD)){
2696+ continue;
2697+ }
2698+
2699+ Matcher mf = Pattern.compile("\\{" +
2700+ "\"str\":\"([^\"]+)\"," + // 1
2701+ "\"pqmode\":\\[([^\\]]+)\\]\\}") // 2
2702+ .matcher(vrates);
2703+
2704+ while( mf.find() ){
2705+ String vrate_id = mf.group(1);
2706+ String vrate_name = "[" + drive_name + "] " + unescapeJavaString(me.group(2));
2707+
2708+ TextValueSet tc = new TextValueSet();
2709+ tc.setValue(vrate_id);
2710+ tc.setText(vrate_name);
2711+ tvsV.add(tc);
2712+ }
2713+ }
2714+ }
2715+ }
2716+
2717+ /*
2718+ * <device_id>:<folder_id> から<folder_id>を切り出す
2719+ */
2720+ protected String extractFolderID( String fol_id ) {
2721+ if (fol_id == null)
2722+ return null;
2723+
2724+ int idx = fol_id.indexOf(':');
2725+ if (idx == -1)
2726+ return fol_id;
2727+
2728+ return fol_id.substring(idx+1);
2729+ }
2730+
2731+ public String unescapeJavaString(String st) {
2732+ StringBuilder sb = new StringBuilder(st.length());
2733+
2734+ for (int i = 0; i < st.length(); i++) {
2735+ char ch = st.charAt(i);
2736+ if (ch == '\\') {
2737+ char nextChar = (i == st.length() - 1) ? '\\' : st
2738+ .charAt(i + 1);
2739+ // Octal escape?
2740+ if (nextChar >= '0' && nextChar <= '7') {
2741+ String code = "" + nextChar;
2742+ i++;
2743+ if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2744+ && st.charAt(i + 1) <= '7') {
2745+ code += st.charAt(i + 1);
2746+ i++;
2747+ if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
2748+ && st.charAt(i + 1) <= '7') {
2749+ code += st.charAt(i + 1);
2750+ i++;
2751+ }
2752+ }
2753+ sb.append((char) Integer.parseInt(code, 8));
2754+ continue;
2755+ }
2756+ switch (nextChar) {
2757+ case '\\':
2758+ ch = '\\';
2759+ break;
2760+ case 'b':
2761+ ch = '\b';
2762+ break;
2763+ case 'f':
2764+ ch = '\f';
2765+ break;
2766+ case 'n':
2767+ ch = '\n';
2768+ break;
2769+ case 'r':
2770+ ch = '\r';
2771+ break;
2772+ case 't':
2773+ ch = '\t';
2774+ break;
2775+ case '\"':
2776+ ch = '\"';
2777+ break;
2778+ case '\'':
2779+ ch = '\'';
2780+ break;
2781+ case '/':
2782+ ch = '/';
2783+ break;
2784+ // Hex Unicode: u????
2785+ case 'u':
2786+ if (i >= st.length() - 5) {
2787+ ch = 'u';
2788+ break;
2789+ }
2790+ int code = Integer.parseInt(
2791+ "" + st.charAt(i + 2) + st.charAt(i + 3)
2792+ + st.charAt(i + 4) + st.charAt(i + 5), 16);
2793+ sb.append(Character.toChars(code));
2794+ i += 5;
2795+ continue;
2796+ }
2797+ i++;
2798+ }
2799+ sb.append(ch);
2800+ }
2801+ return sb.toString();
2802+ }
2803+
2804+ /*
2805+ * デバイス名からコロンの前の部分だけを取り出す
2806+ */
2807+ static String GetDevicePrefix(String device_name){
2808+ Matcher ma = Pattern.compile("^(.*):").matcher(device_name);
2809+ if (ma.find()){
2810+ return ma.group(1);
2811+ }
2812+
2813+ return device_name;
2814+ }
2815+}
--- a/TinyBannavi/src/tainavi/pluginrec/PlugIn_RecDBR_T2007.java
+++ b/TinyBannavi/src/tainavi/pluginrec/PlugIn_RecDBR_T2007.java
@@ -1,2781 +1,2781 @@
1-package tainavi.pluginrec;
2-
3-import java.io.File;
4-import java.io.UnsupportedEncodingException;
5-import java.net.Authenticator;
6-import java.net.URLEncoder;
7-import java.text.ParseException;
8-import java.text.SimpleDateFormat;
9-import java.util.ArrayList;
10-import java.util.Comparator;
11-import java.util.Date;
12-import java.util.HashMap;
13-import java.util.Locale;
14-import java.util.regex.Matcher;
15-import java.util.regex.Pattern;
16-
17-import tainavi.AribCharMap;
18-import tainavi.ChannelCode;
19-import tainavi.ChapterInfo;
20-import tainavi.CommonUtils;
21-import tainavi.DeviceInfo;
22-import tainavi.GetRDStatus;
23-import tainavi.HDDRecorder;
24-import tainavi.HDDRecorderUtils;
25-import tainavi.ReserveList;
26-import tainavi.TVProgram.ProgGenre;
27-import tainavi.TextValueSet;
28-import tainavi.TitleInfo;
29-import tainavi.TraceProgram;
30-
31-/**
32- * REGZA DBR-T2007用のレコーダプラグインです。
33- * @author original:tmskmr
34- * @version x.xx.xx
35- */
36-public class PlugIn_RecDBR_T2007 extends HDDRecorderUtils implements HDDRecorder,Cloneable {
37- public PlugIn_RecDBR_T2007() {
38- super();
39- this.setTunerNum(3);
40- setSettingFixed();
41- }
42-
43- public PlugIn_RecDBR_T2007 clone() {
44- return (PlugIn_RecDBR_T2007) super.clone();
45- }
46-
47- private static final String thisEncoding = "UTF-8";
48-
49- /*******************************************************************************
50- * 種族の特性
51- ******************************************************************************/
52-
53- // 種族の特性
54- @Override
55- public String getRecorderId() { return "REGZA DBR-T2007"; }
56- @Override
57- public RecType getType() { return RecType.RECORDER; }
58-
59- // chvalueを使っていいよ
60- @Override
61- public boolean isChValueAvailable() { return true; }
62- // CHコードは入力しなくていい
63- @Override
64- public boolean isChCodeNeeded() { return false; }
65-
66- // 録画タイトルに対応している
67- @Override
68- public boolean isTitleListSupported() { return true; }
69-
70- // 持ち出しに対応している
71- @Override
72- public boolean isPortableSupported() { return true; }
73-
74- // フォルダー作成に対応している
75- @Override
76- public boolean isFolderCreationSupported() { return true; }
77-
78- /*******************************************************************************
79- * 予約ダイアログなどのテキストのオーバーライド
80- ******************************************************************************/
81-
82- @Override
83- public String getChDatHelp() { return
84- "「レコーダの放送局名」はネットdeナビの録画予約一覧で表示される「CH」の欄の値を設定してください。\n"+
85- "予約一覧取得が正常に完了していれば設定候補がコンボボックスで選択できるようになります。"
86- ;
87- }
88-
89- /*******************************************************************************
90- * 定数
91- ******************************************************************************/
92-
93- protected final String MSGID = "["+getRecorderId()+"] ";
94- protected final String ERRID = "[ERROR]"+MSGID;
95- protected final String DBGID = "[DEBUG]"+MSGID;
96-
97- protected final String NETWORK_UVD = "0";
98- protected final String NETWORK_BSD = "1";
99- protected final String NETWORK_CSD = "2";
100- protected final String NETWORK_L1 = "3";
101- protected final String NETWORK_L2 = "4";
102- protected final String NETWORK_L3 = "5";
103- protected final String NETWORK_L4 = "6";
104- protected final String NETWORK_NET = "7";
105-
106- protected final String CHTYPE_UVD = "uvd";
107- protected final String CHTYPE_BSD = "bsd";
108- protected final String CHTYPE_CSD = "csd";
109- protected final String CHTYPE_L1 = "L1";
110- protected final String CHTYPE_L2 = "L2";
111- protected final String CHTYPE_L3 = "L3";
112- protected final String CHTYPE_L4 = "L4";
113- protected final String CHTYPE_NET = "NET";
114-
115- protected final String CHPREFIX_BS = "BS";
116- protected final String CHPREFIX_CS = "CS";
117- protected final String CH_L1 = "L1";
118- protected final String CH_L2 = "L2";
119- protected final String CH_L3 = "L3";
120- protected final String CH_L4 = "L4";
121- protected final String CH_NET = "NET";
122-
123- protected final String OPTION_DATETIME = "1";
124- protected final String OPTION_PROGRAM = "2";
125-
126- protected final String REPEAT_NONE = "0";
127-
128- protected final int RPTPTN_ID_TUE2SAT = 12;
129-
130- protected final String EXEC_YES = "1";
131- protected final String EXEC_NO = "0";
132-
133- protected final String BRANCH_NO = "0";
134- protected final String BRANCH_YES = "1";
135-
136- protected final String DEVICE_HDD = "0";
137- protected final String DEVICE_DISC = "1";
138- protected final String FOLDER_NONE = "0";
139- protected final String FOLDER_NAME_NONE = "指定なし";
140-
141- protected final String RECMODE_DR = "0";
142- protected final String RECMODE_NAME_DR = "[DR]";
143-
144- protected final String MOCHIDASHI_NONE = "0";
145-
146- protected final String RESULT_OK = "0";
147- protected final String RESULT_OTHER = "1";
148- protected final String RESULT_BUSY = "2";
149- protected final String RESULT_INVALID_TITLE = "17";
150-
151- protected final String ERRMSG_NORESPONSE = "レコーダーが反応しません";
152- protected final String ERRMSG_INVALIDRESPONSE = "レコーダーからの応答が不正です。";
153- protected final String ERRMSG_USB_BUSY = "USB HDDが使用中です。";
154-
155- protected final double MIN_PER_MB = 171.777;
156-
157- protected final String TITLE_NOCHANGED = "__NETdeNAVI_TitleNameNotChanged__";
158-
159- /*******************************************************************************
160- * CHコード設定、エラーメッセージ
161- ******************************************************************************/
162-
163- public ChannelCode getChCode() {
164- return cc;
165- }
166-
167- private ChannelCode cc = new ChannelCode(getRecorderId());
168-
169- protected void setErrmsg(String s) { errmsg = s; }
170-
171- public String getErrmsg() {
172- return(errmsg.replaceAll("\\\\r\\\\n", ""));
173- }
174-
175- private String errmsg = "";
176-
177- /*******************************************************************************
178- * 部品
179- ******************************************************************************/
180-
181- private GetRDStatus gs = new GetRDStatus();
182-
183- private String rsvedFile = "";
184- private String folderTFile = "";
185- private String titleFile = "";
186- private String devinfoTFile = "";
187- private ArrayList<TextValueSet> tvsBranch = new ArrayList<TextValueSet>();
188- private ArrayList<TextValueSet> tvsPatternCode = new ArrayList<TextValueSet>();
189- private ArrayList<TextValueSet> tvsPatternName = new ArrayList<TextValueSet>();
190-
191- /*******************************************************************************
192- * コンストラクタ
193- ******************************************************************************/
194-
195- /*******************************************************************************
196- * チャンネルリモコン機能
197- ******************************************************************************/
198-
199- /*******************************************************************************
200- * レコーダーから各種設定情報を取得する
201- ******************************************************************************/
202- @Override
203- public boolean GetRdSettings(boolean force) {
204-
205- System.out.println("レコーダの各種設定情報を取得します.");
206-
207- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
208-
209- String deviceTFile = "env/device."+myFileId+".xml";
210- devinfoTFile = "env/devinfo."+myFileId+".xml";
211- folderTFile = "env/folders."+myFileId+".xml";
212- String chValueTFile = "env/chvalue."+myFileId+".xml";
213- String chTypeTFile = "env/chtype."+myFileId+".xml";
214- String branchTFile = "env/branch."+myFileId+".xml";
215-
216- // 固定の各種設定情報を初期化する
217- setSettingFixed();
218-
219- File f = new File(deviceTFile);
220- if ( !force){
221- if (!f.exists())
222- return(false);
223-
224- // キャッシュから読み出し(録画設定ほか)
225- device = TVSload(deviceTFile);
226- setDeviceInfos(DeviceInfosFromFile(devinfoTFile));
227- folder = TVSload(folderTFile);
228- chvalue = TVSload(chValueTFile);
229- chtype = TVSload(chTypeTFile);
230- tvsBranch = TVSload(branchTFile);
231-
232- // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
233- if (device.size() > 0 && chvalue.size() > 0 && chtype.size() > 0 && tvsBranch.size() > 0) {
234- return(true);
235- }
236- }
237-
238- // 各種設定情報をレコーダから取得する
239- if (!setSettingVariable()){
240- return (false);
241- }
242-
243- TVSsave(device, deviceTFile);
244- saveDeviceInfos();
245- saveFolders();
246- TVSsave(chvalue, chValueTFile);
247- TVSsave(chtype, chTypeTFile);
248- TVSsave(tvsBranch, branchTFile);
249-
250- return(true);
251- }
252-
253- /*******************************************************************************
254- * レコーダーから予約一覧を取得する
255- ******************************************************************************/
256-
257- public boolean GetRdReserve(boolean force)
258- {
259- System.out.println("レコーダから予約一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
260-
261- errmsg = "";
262-
263- //
264- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
265-
266- rsvedFile = "env/reserved."+myFileId+".xml";
267-
268- File f = new File(rsvedFile);
269- if ( !force && f.exists()) {
270- // キャッシュから読み出し(予約一覧)
271- setReserves(ReservesFromFile(rsvedFile));
272-
273- // なぜか設定ファイルが空になっている場合があるので、その際は再取得する
274- if (getReserves().size() > 0) {
275- return(true);
276- }
277- }
278-
279- // 録画予約の一覧をレコーダから取得する
280- ArrayList<ReserveList> ra = getReserveList();
281- if (ra == null){
282- return(false);
283- }
284-
285- // 番組詳細情報を取得する
286-// reportProgress(MSGID+"予約詳細情報を取得します.");
287-
288-// // 録画予約の詳細情報を取得し、内容を調整する
289-// int rno=1;
290-// for (ReserveList entry : ra) {
291-// reportProgress("+予約詳細を取得します("+rno+"/"+ra.size()+").");
292-// rno++;
293-//
294-// // 予約詳細情報を取得する
295-// getReserveDetail(entry);
296-//
297-// // 放送局名変換
298-// entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
299-//
300-// // TS->DR
301-// translateAttributeTuner(entry);
302-//
303-// // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ
304-// copyAttributesT2007(entry, getReserves());
305-// }
306-
307- setReserves(ra);
308-
309- // キャッシュに保存
310- ReservesToFile(getReserves(), rsvedFile);
311-
312- // 取得した情報の表示
313- if (getDebug()){
314- System.out.println("---Reserved List Start---");
315- for ( int i = 0; i<getReserves().size(); i++ ) {
316- // 詳細情報の取得
317- ReserveList e = getReserves().get(i);
318- System.out.println(String.format("[%s] %s\t%s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s(%s)\t%s\t%s",
319- (i+1), e.getId(), e.getRec_pattern(), e.getRec_nextdate(), e.getAhh(), e.getAmm(), e.getZhh(), e.getZmm(), e.getRec_min(), e.getRec_mode(), e.getTitle(), e.getTitlePop(), e.getChannel(), e.getCh_name()));
320- }
321- System.out.println("---Reserved List End---");
322- }
323-
324- return(true);
325- }
326-
327- /*******************************************************************************
328- * 予約詳細情報の取得
329- ******************************************************************************/
330- @Override
331- public boolean isThereAdditionalDetails() {
332- return true;
333- }
334-
335- /*
336- * 予約の詳細情報を取得する
337- */
338- @Override
339- public boolean GetRdReserveDetails(){
340- int rno = 0;
341- ArrayList<ReserveList> ra = getReserves();
342- for (ReserveList entry : ra) {
343-
344- reportProgress("+番組詳細を取得します("+rno+"/"+ra.size()+").");
345- getReserveDetail(entry);
346-
347- // 放送局名変換
348- entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
349-
350- // TS->DR
351- translateAttributeTuner(entry);
352-
353- // タイトル自動補完フラグなど本体からは取得できない情報を引き継ぐ
354- copyAttributesT2007(entry, getReserves());
355-
356- rno++;
357- }
358-
359- // キャッシュに保存
360- ReservesToFile(getReserves(), rsvedFile);
361-
362- return(true);
363- }
364-
365- /*******************************************************************************
366- * 新規予約
367- ******************************************************************************/
368- @Override
369- public boolean PostRdEntry(ReserveList r) {
370- errmsg = "";
371-
372- String chcode = cc.getCH_WEB2CODE(r.getCh_name());
373- if (chcode == null) {
374- errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
375- System.out.println(errmsg);
376- return(false);
377- }
378-
379- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
380- if ( !loadDialogInitData("0")){
381- return(false);
382- }
383-
384- // RDへの情報作成
385- String pstr = createPostData(r, "");
386-
387- // RDへ情報送信
388- reportProgress(MSGID+"レコーダーに新規予約を要求します.");
389- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
390-// String header = d[0];
391- String response = d[1];
392-
393- // 登録結果の確認
394- if ( !checkReserveResponse(r, response) ){
395- return(false);
396- }
397-
398- // 予約情報の調整
399- adjustReserve(r);
400-
401- // 予約リストを更新
402- getReserves().add(r);
403-
404- // キャッシュに保存
405- ReservesToFile(getReserves(), rsvedFile);
406-
407- reportProgress(MSGID+"正常に登録できました。");
408-
409- return(true);
410- }
411-
412- /*******************************************************************************
413- * 予約更新
414- ******************************************************************************/
415- @Override
416- public boolean UpdateRdEntry(ReserveList o, ReserveList r) {
417- errmsg = "";
418-
419- String chcode = cc.getCH_WEB2CODE(r.getCh_name());
420- if (chcode == null) {
421- errmsg = "【警告】Web番組表の放送局名「"+r.getCh_name()+"」をCHコードに変換できません。CHコード設定を修正してください。" ;
422- System.out.println(errmsg);
423- return(false);
424- }
425-
426- String id = o.getId();
427-
428- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
429- if ( !loadDialogInitData(id) ){
430- return(false);
431- }
432-
433- // RDへの情報作成
434- String pstr = createPostData(r, id);
435-
436- // RDへ情報送信
437- reportProgress(MSGID+"レコーダーに予約更新を要求します.");
438- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DoReserve.php", pstr, null, thisEncoding);
439-// String header = d[0];
440- String response = d[1];
441-
442- // 登録結果の確認
443- if ( !checkReserveResponse(r, response) ){
444- return(false);
445- }
446-
447- // 予約情報の調整
448- adjustReserve(r);
449-
450- // 情報置き換え
451- getReserves().remove(o);
452- getReserves().add(r);
453-
454- // キャッシュに保存
455- ReservesToFile(getReserves(), rsvedFile);
456-
457- reportProgress(MSGID+"正常に更新できました。");
458-
459- return(true);
460- }
461-
462- /*******************************************************************************
463- * 予約削除
464- ******************************************************************************/
465- @Override
466- public ReserveList RemoveRdEntry(String delid) {
467- errmsg = "";
468-
469- // 削除対象を探す
470- ReserveList r = null;
471- for ( ReserveList reserve : getReserves() ) {
472- if (reserve.getId().equals(delid)) {
473- r = reserve;
474- break;
475- }
476- }
477- if (r == null) {
478- return(null);
479- }
480-
481- // RDから予約登録画面の初期情報を取り出す。これを呼ばないと ERR_EXCLUSIVEエラーになる
482- if ( !loadDialogInitData(delid)){
483- return(null);
484- }
485-
486- // RDへの情報作成
487- String pstr = createPostData(r, delid);
488-
489- // RDへ情報送信
490- reportProgress(MSGID+"レコーダーに予約削除を要求します.");
491- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/torunavi/DeleteReserve.php", pstr, null, thisEncoding);
492-// String header = d[0];
493- String response = d[1];
494-
495- if ( !checkGeneralResponse( "予約削除", response) ){
496- return(null);
497- }
498-
499- // 予約リストを更新
500- getReserves().remove(r);
501-
502- // キャッシュに保存
503- ReservesToFile(getReserves(), rsvedFile);
504-
505- reportProgress(MSGID+"正常に削除できました。");
506- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
507- return(r);
508- }
509-
510- /*******************************************************************************
511- * フォルダー作成
512- ******************************************************************************/
513- @Override
514- public boolean CreateRdFolder(String device_id, String folder_name) {
515- return setFolderName( device_id, null, folder_name );
516- }
517-
518- /*******************************************************************************
519- * フォルダー名更新
520- ******************************************************************************/
521- @Override
522- public boolean UpdateRdFolderName(String device_id, String folder_id, String folder_name) {
523- return setFolderName( device_id, folder_id, folder_name );
524- }
525-
526- /*******************************************************************************
527- * フォルダー削除
528- ******************************************************************************/
529- @Override
530- public boolean RemoveRdFolder(String device_id, String fol_id) {
531- String action = "フォルダ削除";
532- String folder_id = extractFolderID(fol_id);
533-
534- String pstr =
535- "drive_id=" + device_id + "&" +
536- "folder_id=" + folder_id;
537-
538- for (int n=0; n<3; n++){
539- // おまじない
540- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
541-
542- // RDへ情報送信
543- reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
544- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/folderset/DeleteFolder.php?" + pstr, null, thisEncoding);
545-// String header = d[0];
546- String response = d[1];
547-
548- // レスポンスから処理結果を取得する
549- String [] rc = getReasonFromResponse( response );
550- if (rc == null) {
551- errmsg = ERRID+ERRMSG_NORESPONSE;
552- return(false);
553- }
554-
555- String result = rc[0];
556- String reason = rc[1];
557-
558- if (result.equals(RESULT_OK))
559- break;
560-
561- if (result.equals(RESULT_OTHER) || result.equals(RESULT_BUSY)){
562- if (mountUsbDrive(device_id))
563- continue;
564- }
565-
566- errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
567- return(false);
568- }
569-
570- // フォルダ一覧を取得し直す
571- setSettingFolders();
572- saveFolders();
573-
574- return true;
575- }
576-
577- /*******************************************************************************
578- * タイトル一覧取得
579- ******************************************************************************/
580- @Override
581- public boolean GetRdTitles(String device_id, boolean force, boolean detail, boolean mountedOnly) {
582- System.out.println("レコーダからタイトル一覧を取得します("+force+"): "+getRecorderId()+"("+getIPAddr()+":"+getPortNo()+")");
583-
584- errmsg = "";
585-
586- DEVICE_ID = device_id;
587-
588- String myFileId = getIPAddr()+"_"+getPortNo()+"_"+getRecorderId();
589-
590- ArrayList<TitleInfo> list = new ArrayList<TitleInfo>();
591-
592- for (DeviceInfo di : getDeviceInfos() ) {
593- String devid = di.getId();
594- if (devid.equals(DEVICE_ALL))
595- continue;
596- if (!device_id.equals(DEVICE_ALL) && !device_id.equals(devid))
597- continue;
598-
599- titleFile = "env/title."+myFileId+"." + devid + ".xml";
600-
601- if (!force){
602- File f = new File(titleFile);
603- if (!f.exists())
604- return(false);
605-
606- // キャッシュから読み出し(タイトル一覧)
607- ArrayList<TitleInfo> ta = TitlesFromFile(titleFile);
608-
609- list.addAll(ta);
610- }
611- else{
612- // タイトル一覧をレコーダから取得する
613- ArrayList<TitleInfo> ta = getTitleList(devid, mountedOnly);
614- if (ta == null){
615- if (errmsg.length() > 0)
616- return(false);
617-
618- File f = new File(titleFile);
619- // キャッシュから読み出し(タイトル一覧)
620- if (f.exists())
621- ta = TitlesFromFile(titleFile);
622- }
623- else{
624- // タイトルの詳細情報を取得し、内容を調整する
625- for (TitleInfo entry : ta) {
626- // 放送局名変換
627- entry.setCh_name(getChCode().getCH_REC2WEB(entry.getChannel()));
628- }
629-
630- if (detail){
631- getTitleDetails(devid, ta, false);
632- }
633- }
634-
635- list.addAll(ta);
636- }
637- }
638-
639- setTitles(list);
640-
641- // キャッシュに保存
642- if (force){
643- saveTitles(device_id);
644-
645- // 取得した情報の表示
646- if (getDebug()){
647- System.out.println("---Title List Start---");
648- for ( int i = 0; i<getTitles().size(); i++ ) {
649- // 詳細情報の取得
650- TitleInfo t = getTitles().get(i);
651- System.out.println(String.format("[%s] %s\t%s\t%s:%s\t%s:%s\t%sm\t%s\t%s\t%s",
652- (i+1), t.getId(), t.getRec_date(), t.getAhh(), t.getAmm(), t.getZhh(), t.getZmm(), t.getRec_min(),
653- t.getTitle(), t.getChannel(), t.getCh_name()));
654- }
655- System.out.println("---Title List End---");
656- }
657-
658- // 各種設定情報をレコーダから取得する
659- if (setSettingDevice()){
660- saveDeviceInfos();
661- }
662- }
663-
664- return(true);
665- }
666-
667- /*******************************************************************************
668- * タイトル詳細情報取得
669- ******************************************************************************/
670- @Override
671- public boolean GetRdTitleDetails(String devid, boolean force){
672- ArrayList<TitleInfo> ta = getTitles();
673-
674- getTitleDetails(devid, ta, force);
675-
676- // キャッシュに保存
677- saveTitles(devid);
678-
679- return(true);
680- }
681-
682- /*
683- * タイトル詳細情報を取得する
684- */
685- private boolean getTitleDetails(String devid, ArrayList<TitleInfo>ta, boolean force){
686- int tno = 0;
687-
688- for (TitleInfo ti : ta){
689- tno++;
690-
691- if (ti.getDetailLoaded() && !ti.getRecording() && !force)
692- continue;
693-
694- reportProgress("+タイトル詳細を取得します("+tno+"/"+ta.size()+").");
695- getTitleDetail(ti);
696- }
697-
698- return(true);
699- }
700-
701- /*******************************************************************************
702- * タイトル詳細情報取得
703- ******************************************************************************/
704- @Override
705- public boolean GetRdTitleDetail(TitleInfo t) {
706- if (t == null)
707- return(false);
708-
709- if (!getTitleDetail(t))
710- return(false);
711-
712- // キャッシュに保存
713- saveTitles(t.getRec_device());
714-
715- return(true);
716- }
717-
718- /*******************************************************************************
719- * タイトル更新
720- ******************************************************************************/
721- @Override
722- public boolean UpdateRdTitleInfo(String device_id, TitleInfo o, TitleInfo t) {
723- errmsg = "";
724-
725- if (t == null) {
726- return(false);
727- }
728-
729- for (int n=0; n<3; n++){
730- // タイトルの編集をレコーダに通知する
731- notifyTitleEdit(device_id, t.getId());
732-
733- // おまじない
734- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
735-
736- // RDへの情報作成
737- String pstr = createTitlePostData(t, o, device_id);
738-
739- // RDへ情報送信
740- reportProgress(MSGID+"レコーダーにタイトル更新を要求します:"+device_id);
741- String [] d = reqPOST("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/UpdateTitleInfo.php", pstr, null, thisEncoding);
742-// String header = d[0];
743- String response = d[1];
744-
745- // レスポンスから処理結果を取得する
746- String [] rc = getReasonFromResponse( response );
747- if (rc == null) {
748- errmsg = ERRID+ERRMSG_NORESPONSE;
749- return(false);
750- }
751-
752- String result = rc[0];
753- String reason = rc[1];
754-
755- if (result.equals(RESULT_OK))
756- break;
757-
758- if (result.equals(RESULT_OTHER)){
759- if (mountUsbDrive(device_id))
760- continue;
761- }
762-
763- errmsg = ERRID+"タイトル更新に失敗しました。(result=" + result + ",reason=" + reason + ")";
764- return(false);
765- }
766-
767- // 録画タイトルリストを更新
768- ArrayList<TitleInfo> list = getTitles();
769- list.remove(o);
770- list.add(t);
771- list.sort(new TitleInfoComparator());
772-
773- // キャッシュに保存
774- saveTitles(t.getRec_device());
775-
776- // 各種設定情報をレコーダから取得する
777- if (setSettingDevice()){
778- saveDeviceInfos();
779- }
780-
781- reportProgress(MSGID+"正常に更新できました。");
782- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に更新できました。");
783-
784- return(true);
785- }
786-
787- /*******************************************************************************
788- * タイトル削除
789- ******************************************************************************/
790- @Override
791- public TitleInfo RemoveRdTitle(String device_id, String title_id) {
792- errmsg = "";
793-
794- // 削除対象を探す
795- TitleInfo t = null;
796- for ( TitleInfo ttl : getTitles() ) {
797- if (ttl.getId().equals(title_id)) {
798- t = ttl;
799- break;
800- }
801- }
802- if (t == null) {
803- return(null);
804- }
805-
806- for (int n=0; n<3; n++){
807- // おまじない
808- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
809-
810- // RDへの情報作成
811- String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
812-
813- // RDへ情報送信
814- reportProgress(MSGID+"レコーダーにタイトル削除を要求します.");
815- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+"/titlelist/DeleteTitle.php?"+pstr, null, thisEncoding);
816-// String header = d[0];
817- String response = d[1];
818-
819- // レスポンスから処理結果を取得する
820- String [] rc = getReasonFromResponse( response );
821- if (rc == null) {
822- errmsg = ERRID+ERRMSG_NORESPONSE;
823- return(null);
824- }
825-
826- String result = rc[0];
827- String reason = rc[1];
828-
829- if (result.equals(RESULT_OK))
830- break;
831-
832- if (result.equals(RESULT_OTHER) || result.equals(RESULT_INVALID_TITLE)){
833- if (mountUsbDrive(device_id))
834- continue;
835- }
836-
837- errmsg = ERRID+"タイトル削除に失敗しました。(result=" + result + ",reason=" + reason + ")";
838- return(null);
839- }
840-
841- // タイトルリストを更新
842- getTitles().remove(t);
843-
844- // キャッシュに保存
845- saveTitles(t.getRec_device());
846-
847- setSettingDevice();
848- saveDeviceInfos();
849-
850- reportProgress(MSGID+"正常に削除できました。");
851- System.out.printf("\n<<< Message from RD >>> \"%s\"\n\n", "正常に削除できました。");
852- return(t);
853- }
854-
855- /*******************************************************************************
856- * タイトル再生の開始・終了
857- ******************************************************************************/
858- @Override
859- public boolean StartStopPlayRdTitle(String device_id, String title_id, boolean start) {
860- errmsg = "";
861-
862- // RDへのURL
863- String action = start ? "タイトル再生開始" : "タイトル再生終了";
864- String url = start ? "/titlelist/PlayTitle.php" : "/titlelist/PlayStop.php";
865-
866- // RDへの情報作成
867- String pstr = "drive_id=" + device_id + "&title_id=" + title_id;
868-
869- for (int n=0; n<3; n++){
870- // おまじない
871- Authenticator.setDefault(new MyAuthenticator(getUser(), getPasswd()));
872-
873- // RDへ情報送信
874- reportProgress(MSGID+"レコーダーに" + action + "を要求します.");
875- String [] d = reqGET("http://"+getIPAddr()+":"+getPortNo()+url+ "?"+pstr, null, thisEncoding);
876-// String header = d[0];
877- String response = d[1];
878-
879- // レスポンスから処理結果を取得する
880- String [] rc = getReasonFromResponse( response );
881- if (rc == null) {
882- errmsg = ERRID+ERRMSG_NORESPONSE;
883- return(false);
884- }
885-
886- String result = rc[0];
887- String reason = rc[1];
888-
889- if (result.equals(RESULT_OK))
890- break;
891-
892- if (result.equals(RESULT_OTHER)){
893- if (mountUsbDrive(device_id))
894- continue;
895- }
896-
897- errmsg = ERRID+action+"に失敗しました。(result=" + result + ",reason=" + reason + ")";
898- return(false);
899- }
900-
901- reportProgress(MSGID+"正常に" + action + "できました。");
902-
903- return(true);
904- }
905-
906- /* ここまで */
907-
908- /* 個別コード-ここから最後まで */
909- /*******************************************************************************
910- * 非公開メ

Ein Teil der Diff wurde aufgrund der Größenbeschränkung abgeschnitten. Verwenden Sie Ihren lokalen Client, um die vollständige Diff.