OSDN Git Service

0ad31b0a218fba510da0c5062156d4a134de635f
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / Viewer.java
1 package tainavi;\r
2 \r
3 import java.awt.AWTException;\r
4 import java.awt.BorderLayout;\r
5 import java.awt.Color;\r
6 import java.awt.Component;\r
7 import java.awt.Container;\r
8 import java.awt.Desktop;\r
9 import java.awt.Dimension;\r
10 import java.awt.Font;\r
11 import java.awt.Frame;\r
12 import java.awt.Image;\r
13 import java.awt.Insets;\r
14 import java.awt.MenuItem;\r
15 import java.awt.Point;\r
16 import java.awt.PopupMenu;\r
17 import java.awt.Rectangle;\r
18 import java.awt.SystemTray;\r
19 import java.awt.Toolkit;\r
20 import java.awt.TrayIcon;\r
21 import java.awt.datatransfer.Clipboard;\r
22 import java.awt.datatransfer.StringSelection;\r
23 import java.awt.event.ActionEvent;\r
24 import java.awt.event.ActionListener;\r
25 import java.awt.event.ComponentAdapter;\r
26 import java.awt.event.ComponentEvent;\r
27 import java.awt.event.MouseAdapter;\r
28 import java.awt.event.MouseEvent;\r
29 import java.awt.event.WindowAdapter;\r
30 import java.awt.event.WindowEvent;\r
31 import java.io.File;\r
32 import java.io.IOException;\r
33 import java.io.UnsupportedEncodingException;\r
34 import java.lang.management.ManagementFactory;\r
35 import java.lang.management.MemoryMXBean;\r
36 import java.lang.management.MemoryUsage;\r
37 import java.lang.reflect.InvocationTargetException;\r
38 import java.net.URI;\r
39 import java.net.URISyntaxException;\r
40 import java.net.URLEncoder;\r
41 import java.security.NoSuchAlgorithmException;\r
42 import java.util.ArrayList;\r
43 import java.util.Arrays;\r
44 import java.util.Calendar;\r
45 import java.util.GregorianCalendar;\r
46 import java.util.HashMap;\r
47 import java.util.LinkedHashMap;\r
48 import java.util.ServiceLoader;\r
49 import java.util.regex.Matcher;\r
50 import java.util.regex.Pattern;\r
51 \r
52 import javax.imageio.ImageIO;\r
53 import javax.swing.ImageIcon;\r
54 import javax.swing.JComponent;\r
55 import javax.swing.JFrame;\r
56 import javax.swing.JLabel;\r
57 import javax.swing.JMenuItem;\r
58 import javax.swing.JOptionPane;\r
59 import javax.swing.JPopupMenu;\r
60 import javax.swing.SwingUtilities;\r
61 import javax.swing.ToolTipManager;\r
62 import javax.swing.UIManager;\r
63 import javax.swing.event.ChangeEvent;\r
64 import javax.swing.event.ChangeListener;\r
65 \r
66 import tainavi.HDDRecorder.RecType;\r
67 import tainavi.SearchKey.TargetId;\r
68 import tainavi.TVProgram.ProgFlags;\r
69 import tainavi.TVProgram.ProgGenre;\r
70 import tainavi.TVProgram.ProgOption;\r
71 import tainavi.TVProgram.ProgSubgenre;\r
72 import tainavi.TVProgram.ProgSubtype;\r
73 import tainavi.TVProgram.ProgType;\r
74 import tainavi.VWMainWindow.MWinTab;\r
75 import tainavi.VWUpdate.UpdateResult;\r
76 \r
77 \r
78 /**\r
79  * メインな感じ\r
80  */\r
81 public class Viewer extends JFrame implements ChangeListener,VWTimerRiseListener {\r
82 \r
83         private static final long serialVersionUID = 1L;\r
84         \r
85         @Override\r
86         public void stateChanged(ChangeEvent e){\r
87                 StdAppendMessage("イベント発生");\r
88         }\r
89 \r
90         \r
91         /*\r
92          * メソッド的な\r
93          */\r
94         \r
95         private void StdAppendMessage(String message)   { System.out.println(message); }\r
96         private void StdAppendError(String message)             { System.err.println(message); }\r
97         // \r
98         private void MWinSetVisible(boolean b)                  { mwin.setVisible(b); }\r
99         // \r
100         private void StWinClear()                                               { stwin.clear(); }\r
101         private void StWinSetVisible(boolean b)                 { stwin.setVisible(b); }\r
102         private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)stwin); }\r
103         private void StWinSetLocationUnder(Component frame)  { CommonSwingUtils.setLocationUnder(frame, (VWStatusWindow)stwin); }\r
104         \r
105         private void ringBeep() { if (env!=null && ! env.getDisableBeep()) { Toolkit.getDefaultToolkit().beep(); if ( env.getDebug() ) CommonUtils.printStackTrace(); } }\r
106         \r
107         \r
108         /*\r
109          * オブジェクト的な\r
110          */\r
111         \r
112         // 設定値をいれるところ\r
113         private final Env env = new Env();                                                                                      // 主要な設定\r
114         private final Bounds bounds = new Bounds();                                                                     // ウィンドウサイズとか動的に変化するもの\r
115         private final ClipboardInfoList cbitems = new ClipboardInfoList();                      // クリップボード対応機能でどの項目をコピーするかとかの設定 \r
116         private final PaperColorsMap pColors = new PaperColorsMap();                            // 新聞形式のジャンル別背景色の設定\r
117         private final AVSetting avs = new AVSetting();                                                          // ジャンル別録画画質・音質等設定\r
118         private final CHAVSetting chavs = new CHAVSetting();                                            // CH別録画画質・音質等設定\r
119         private final ChannelSort chsort = new ChannelSort();                                           // CHソート設定\r
120         private final ChannelConvert chconv = new ChannelConvert();                                     // ChannelConvert.dat\r
121         private final MarkChar markchar = new MarkChar(env);                                            // タイトルにつけるマークを操作する\r
122         \r
123         private final MarkedProgramList mpList = new MarkedProgramList();                       // 検索結果のキャッシュ(表示高速化用)\r
124         private final TraceProgram trKeys = new TraceProgram();                                         // 番組追跡の設定\r
125         private final SearchProgram srKeys = new SearchProgram();                                       // キーワード検索の設定\r
126         private final SearchGroupList srGrps = new SearchGroupList();                           // キーワード検索グループの設定\r
127         private final ExtProgram extKeys = new ExtProgram();                                            // 延長警告管理の設定\r
128         \r
129         private final RecorderInfoList recInfoList = new RecorderInfoList();            // レコーダ一覧の設定\r
130         \r
131         private final HDDRecorderList recPlugins = new HDDRecorderList();                       // レコーダプラグイン(テンプレート)\r
132         private final HDDRecorderList recorders = new HDDRecorderList();                        // レコーダプラグイン(実際に利用するもの)\r
133         \r
134         private final TVProgramList progPlugins = new TVProgramList();                          // Web番組表プラグイン(テンプレート)\r
135         private final TVProgramList tvprograms = new TVProgramList();                           // Web番組表プラグイン(実際に利用するもの)\r
136         \r
137         private final VWTimer timer_now = new VWTimer();                                                        // 毎分00秒に起動して処理をキックするタイマー\r
138         \r
139         // 初期化的な\r
140         private boolean logging = true;                                                                                 // ログ出力する\r
141         private boolean runRecWakeup = false;                                                                           // 起動時にレコーダを起こす\r
142         private boolean runRecLoad = false;                                                                                     // 起動時にレコーダから予約一覧を取得する\r
143         private boolean enableWebAccess = true;                                                                         // 起動時のWeb番組表へのアクセスを禁止する\r
144         private boolean onlyLoadProgram = false;\r
145         private String pxaddr = null;                                                                                           // ProxyAddress指定\r
146         private String pxport = null;                                                                                           // ProxtPort指定\r
147         \r
148         \r
149         /*******************************************************************************\r
150          * 定数\r
151          ******************************************************************************/\r
152         \r
153         public static final String LOG_FILE = "log.txt";                                                        // ログファイル名\r
154         public static final String HISTORY_FILE = "05_history.txt";                             // 更新履歴だよ\r
155         \r
156         private static final String ICONFILE_SYSTRAY = "icon"+File.separator+"tainavi16.png";\r
157         private static final String ICONFILE_TAINAVI = "icon"+File.separator+"tainavi.png";\r
158         \r
159         public static final int TIMEBAR_START = 5;                                      // 新聞形式の開始時刻\r
160         private static final int OPENING_WIAT = 500;                            // まあ起動時しか使わないんですけども\r
161 \r
162         private static final String MSGID = "[鯛ナビ] ";\r
163         private static final String ERRID = "[ERROR]"+MSGID;\r
164         private static final String DBGID = "[DEBUG]"+MSGID;\r
165         \r
166         /**\r
167          * Web番組表のどれとどれを読めばいいのか\r
168          */\r
169         public static enum LoadFor {\r
170                 TERRA   ("地上波&BSのみ取得"),\r
171                 CS              ("CSのみ取得"),\r
172                 CSo1    ("CS[プライマリ]のみ取得"),\r
173                 CSo2    ("CS[セカンダリ]のみ取得"),\r
174                 CSwSD   ("CSのみ取得(取得後シャットダウン)"),\r
175                 RADIO   ("ラジオのみ取得"),\r
176                 SYOBO   ("しょぼかるのみ取得"),\r
177                 ALL             ("すべて取得");\r
178                 \r
179                 private String name;\r
180                 \r
181                 private LoadFor(String name) {\r
182                         this.name = name;\r
183                 }\r
184 \r
185                 public String getName() {\r
186                         return this.name;\r
187                 }\r
188                 \r
189                 public static LoadFor get(String s) {\r
190                         for ( LoadFor lf : LoadFor.values() ) {\r
191                                 if ( lf.name.equals(s) ) {\r
192                                         return lf;\r
193                                 }\r
194                         }\r
195                         return null;\r
196                 }\r
197         };\r
198 \r
199         /**\r
200          * レコーダ情報のどれとどれを読めばいいのか\r
201          */\r
202         public static enum LoadRsvedFor {\r
203                 SETTING         ( "設定情報のみ取得(future use.)" ),\r
204                 RECORDED        ( "録画結果のみ取得" ),\r
205                 ;\r
206                 \r
207                 private String name;\r
208                 \r
209                 private LoadRsvedFor(String name) {\r
210                         this.name = name;\r
211                 }\r
212                 \r
213                 public String getName() {\r
214                         return name;\r
215                 }\r
216                 \r
217                 public static LoadRsvedFor get(String s) {\r
218                         for ( LoadRsvedFor lrf : LoadRsvedFor.values() ) {\r
219                                 if ( lrf.name.equals(s) ) {\r
220                                         return lrf;\r
221                                 }\r
222                         }\r
223                         return null;\r
224                 }\r
225         }\r
226         \r
227         /**\r
228          *  リスト形式のカラム定義\r
229          * @deprecated しっぱいした 半年くらいしたら削除する\r
230          */\r
231         public static enum ListedColumn {\r
232                 RSVMARK         ("予約",                      35),\r
233                 DUPMARK         ("重複",                      35),\r
234                 CHNAME          ("チャンネル名",  100),\r
235                 TITLE           ("番組タイトル",  300),\r
236                 DETAIL          ("番組詳細",                200),\r
237                 START           ("開始時刻",                150),\r
238                 END                     ("終了",                      50),\r
239                 LENGTH          ("長さ",                      50),\r
240                 GENRE           ("ジャンル",                85),\r
241                 SITEM           ("検索アイテム名",       100),\r
242                 STAR            ("お気に入り度",  100),\r
243                 SCORE           ("スコア",                   35),\r
244                 THRESHOLD       ("閾値",                      35),\r
245                 HID_PRGID       ("PRGID",               -1),\r
246                 HID_STIME       ("STIME",               -1),\r
247                 HID_ETIME       ("ETIME",               -1),\r
248                 HID_EXFLG       ("EXFLG",               -1),\r
249                 HID_TITLE       ("TITLE",               -1),\r
250                 ;\r
251 \r
252                 @SuppressWarnings("unused")\r
253                 private String name;\r
254                 private int iniWidth;\r
255 \r
256                 private ListedColumn(String name, int iniWidth) {\r
257                         this.name = name;\r
258                         this.iniWidth = iniWidth;\r
259                 }\r
260 \r
261                 /* なんだかなー\r
262                 @Override\r
263                 public String toString() {\r
264                         return name;\r
265                 }\r
266                 */\r
267 \r
268                 public int getIniWidth() {\r
269                         return iniWidth;\r
270                 }\r
271                 \r
272                 public int getColumn() {\r
273                         return ordinal();\r
274                 }\r
275         };\r
276         \r
277         /**\r
278          *  本体予約一覧のカラム定義\r
279          * @deprecated しっぱいした 半年くらいしたら削除する\r
280          */\r
281         public static enum RsvedColumn {\r
282                 PATTERN         ("パタン",                   110),\r
283                 DUPMARK         ("重複",                      35),\r
284                 EXEC            ("実行",                      35),\r
285                 TRACE           ("追跡",                      35),\r
286                 NEXTSTART       ("次回実行予定",  150),\r
287                 END                     ("終了",                      50),\r
288                 LENGTH          ("長さ",                      50),\r
289                 ENCODER         ("エンコーダ",          50),\r
290                 VRATE           ("画質",                      100),\r
291                 ARATE           ("音質",                      50),\r
292                 TITLE           ("番組タイトル",  300),\r
293                 CHNAME          ("チャンネル名",  150),\r
294                 RECORDER        ("レコーダ",                200),\r
295                 HID_INDEX       ("INDEX",               -1),\r
296                 HID_RSVID       ("RSVID",               -1),\r
297                 ;\r
298 \r
299                 @SuppressWarnings("unused")\r
300                 private String name;\r
301                 private int iniWidth;\r
302 \r
303                 private RsvedColumn(String name, int iniWidth) {\r
304                         this.name = name;\r
305                         this.iniWidth = iniWidth;\r
306                 }\r
307 \r
308                 /*\r
309                 @Override\r
310                 public String toString() {\r
311                         return name;\r
312                 }\r
313                 */\r
314 \r
315                 public int getIniWidth() {\r
316                         return iniWidth;\r
317                 }\r
318                 \r
319                 public int getColumn() {\r
320                         return ordinal();\r
321                 }\r
322         };\r
323         \r
324         \r
325         \r
326         /*\r
327          * コンポーネント\r
328          */\r
329         \r
330         // 起動時に固定で用意しておくもの\r
331         private final VWStatusWindow stwin = new VWStatusWindow();\r
332         private final VWStatusTextArea mwin = new VWStatusTextArea();\r
333         private final VWColorChooserDialog ccwin = new VWColorChooserDialog();\r
334         private final VWPaperColorsDialog pcwin = new VWPaperColorsDialog();\r
335         private final VWReserveDialog rdialog = new VWReserveDialog(0, 0);\r
336         \r
337         // 初期化処理の中で生成していくもの\r
338         private VWMainWindow mainWindow = null;\r
339         private VWToolBar toolBar = null;\r
340         private VWListedView listed = null;\r
341         private VWPaperView paper = null;\r
342         private VWReserveListView reserved = null;\r
343         private VWRecordedListView recorded = null;\r
344         private VWAutoReserveListView autores = null;\r
345         private VWSettingView setting = null;\r
346         private VWRecorderSettingView recsetting = null;\r
347         private VWChannelSettingView chsetting = null;\r
348         private VWChannelDatSettingView chdatsetting = null;\r
349         private VWChannelSortView chsortsetting = null;\r
350         private VWChannelConvertView chconvsetting = null;\r
351         private VWLookAndFeel vwlaf = null;\r
352         private VWFont vwfont = null;\r
353         \r
354         private TrayIcon trayicon = null;\r
355         \r
356         \r
357         \r
358         /*******************************************************************************\r
359          * タブやダイアログのインスタンス作成用クラス定義\r
360          ******************************************************************************/\r
361         \r
362         /***\r
363          * リスト形式の内部クラス\r
364          */\r
365         private class VWListedView extends AbsListedView {\r
366 \r
367                 private static final long serialVersionUID = 1L;\r
368 \r
369                 // 環境設定の入れ物を渡す\r
370                 @Override\r
371                 protected Env getEnv() { return env; }\r
372                 @Override\r
373                 protected Bounds getBoundsEnv() { return bounds; }\r
374                 @Override\r
375                 protected ChannelSort getChannelSort() { return chsort; }\r
376 \r
377                 @Override\r
378                 protected MarkedProgramList getMarkedProgramList() { return mpList; }\r
379                 @Override\r
380                 protected TraceProgram getTraceProgram() { return trKeys; }\r
381                 @Override\r
382                 protected SearchProgram getSearchProgram() { return srKeys; }\r
383                 @Override\r
384                 protected SearchGroupList getSearchGroupList() { return srGrps; }\r
385                 @Override\r
386                 protected ExtProgram getExtProgram() { return extKeys; }\r
387 \r
388                 @Override\r
389                 protected TVProgramList getTVProgramList() { return tvprograms; }\r
390                 @Override\r
391                 protected HDDRecorderList getRecorderList() { return recorders; }\r
392 \r
393                 // メッセージ出力関連\r
394                 @Override\r
395                 protected StatusWindow getStWin() { return stwin; }\r
396                 @Override\r
397                 protected StatusTextArea getMWin() { return mwin; }\r
398                 \r
399                 // コンポーネントを渡す\r
400                 @Override\r
401                 protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
402                 @Override\r
403                 protected Component getParentComponent() { return Viewer.this; }\r
404 \r
405                 @Override\r
406                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
407                 \r
408                 /*\r
409                  * AbsListedView内でのイベントから呼び出されるメソッド群\r
410                  */\r
411 \r
412                 @Override\r
413                 protected void onShown() {\r
414                         // キーワード登録ボタンはリスト形式のみ\r
415                         toolBar.setAddkeywordEnabled(true);\r
416                         // 一括予約はリスト形式のみ\r
417                         toolBar.setBatchReservationEnabled(true);\r
418                         // スナップショットを有効にする\r
419                         toolBar.setSnapShotEnabled(true);\r
420                 }\r
421 \r
422                 @Override\r
423                 protected void onHidden() {\r
424                         // キーワード登録ボタンはリスト形式のみ\r
425                         toolBar.setAddkeywordEnabled(false);\r
426                         // 一括予約はリスト形式のみ\r
427                         toolBar.setBatchReservationEnabled(false);\r
428                         // スナップショットを無効にする\r
429                         toolBar.setSnapShotEnabled(false);\r
430                 }\r
431 \r
432                 @Override\r
433                 protected void showPopupForTraceProgram(\r
434                                 final JComponent comp,\r
435                                 final ProgDetailList tvd, final String keyword, final int threshold,\r
436                                 final int x, final int y, final int h) {\r
437                         \r
438                         timer_now.pause();\r
439                         Viewer.this.showPopupForTraceProgram(comp, tvd, keyword, threshold, x, y, h);\r
440                         timer_now.start();\r
441                 }\r
442 \r
443                 @Override\r
444                 protected void updateReserveDisplay(String chname) {\r
445                         timer_now.pause();\r
446                         paper.updateReserveBorder(chname);\r
447                         reserved.redrawReservedList();\r
448                         timer_now.start();\r
449                 }\r
450 \r
451                 @Override\r
452                 protected void updateBangumiColumns() {\r
453                         timer_now.pause();\r
454                         paper.updateBangumiColumns();\r
455                         timer_now.start();\r
456                 }\r
457 \r
458                 @Override\r
459                 protected void clearPaper() {\r
460                         timer_now.pause();\r
461                         paper.clearPanel();\r
462                         timer_now.start();\r
463                 }\r
464 \r
465                 @Override\r
466                 protected void previewKeywordSearch(SearchKey search) {\r
467                         //timer_now.pause();\r
468                         if (search.alTarget.size() > 0) {\r
469                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
470                                 listed.redrawListByPreview(search);\r
471                         }\r
472                         //timer_now.start();\r
473                 }\r
474 \r
475                 @Override\r
476                 protected void jumpToPaper(String Center, String StartDateTime) {\r
477                         //timer_now.pause();\r
478                         paper.jumpToBangumi(Center,StartDateTime);\r
479                         //timer_now.start();\r
480                 }\r
481 \r
482                 @Override\r
483                 protected boolean addToPickup(ProgDetailList tvd) { return Viewer.this.addToPickup(tvd); }\r
484 \r
485                 @Override\r
486                 protected boolean isTabSelected(MWinTab tab) { return mainWindow.isTabSelected(tab); }\r
487                 @Override\r
488                 protected void setSelectedTab(MWinTab tab) { mainWindow.setSelectedTab(tab); }\r
489 \r
490                 @Override\r
491                 protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
492                 @Override\r
493                 protected boolean isFullScreen() { return toolBar.isFullScreen(); }\r
494                 @Override\r
495                 protected void setPagerEnabled(boolean b) { toolBar.setPagerEnabled(b); }\r
496                 @Override\r
497                 protected int getPagerCount() { return toolBar.getPagerCount(); }\r
498                 @Override\r
499                 protected int getSelectedPagerIndex() { return toolBar.getSelectedPagerIndex(); }\r
500 \r
501                 @Override\r
502                 protected void setDividerEnvs(int loc) {\r
503                         if ( ! toolBar.isFullScreen() && mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
504                                 if (env.getSyncTreeWidth()) {\r
505                                         bounds.setTreeWidth(loc);\r
506                                         bounds.setTreeWidthPaper(loc);\r
507                                 }\r
508                                 else {\r
509                                         bounds.setTreeWidth(loc);\r
510                                 }\r
511                         }\r
512                 }\r
513         }\r
514         \r
515         \r
516         \r
517         /**\r
518          * 新聞形式の内部クラス\r
519          */\r
520         private class VWPaperView extends AbsPaperView {\r
521 \r
522                 private static final long serialVersionUID = 1L;\r
523 \r
524                 // 環境設定の入れ物を渡す\r
525                 @Override\r
526                 protected Env getEnv() { return env; }\r
527                 @Override\r
528                 protected Bounds getBoundsEnv() { return bounds; }\r
529                 @Override\r
530                 protected PaperColorsMap getPaperColorMap() { return pColors; }\r
531                 @Override\r
532                 protected ChannelSort getChannelSort() { return chsort; }\r
533                 \r
534                 @Override\r
535                 protected TVProgramList getTVProgramList() { return tvprograms; }\r
536                 @Override\r
537                 protected HDDRecorderList getRecorderList() { return recorders; }\r
538 \r
539                 // メッセージ出力関連\r
540                 @Override\r
541                 protected StatusWindow getStWin() { return stwin; }\r
542                 @Override\r
543                 protected StatusTextArea getMWin() { return mwin; }\r
544                 \r
545                 // コンポーネントを渡す\r
546                 @Override\r
547                 protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
548                 @Override\r
549                 protected Component getParentComponent() { return Viewer.this; }\r
550 \r
551                 @Override\r
552                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
553                 \r
554                 /*\r
555                  * AbsPaperView内でのイベントから呼び出されるメソッド群\r
556                  */\r
557 \r
558                 @Override\r
559                 protected void onShown() {\r
560                         // ページャーコンボボックスを有効にする(状況次第で有効にならない場合もある)(ツリーの選択次第で変わるのでもどし)\r
561                         //toolBar.setPagerEnabled(true);\r
562                         // スナップショットを有効にする\r
563                         toolBar.setSnapShotEnabled(true);\r
564                         // ジャンル別背景色を有効にする\r
565                         toolBar.setPaperColorDialogEnabled(true);\r
566                         // マッチ枠を有効にする\r
567                         toolBar.setBorderToggleEnabled(true);\r
568                 }\r
569 \r
570                 @Override\r
571                 protected void onHidden() {\r
572                         // 新聞形式以外ではページャーコンボボックスを無効にする(ツリーの選択次第で変わるのでもどし)\r
573                         //toolBar.setPagerEnabled(false);\r
574                         // 新聞形式以外ではスナップショットを無効にする\r
575                         toolBar.setSnapShotEnabled(false);\r
576                         // 新聞形式以外ではジャンル別背景色を無効にする\r
577                         toolBar.setPaperColorDialogEnabled(false);\r
578                         // 新聞形式以外ではマッチ枠を無効にする\r
579                         toolBar.setBorderToggleEnabled(false);\r
580                 }\r
581 \r
582                 @Override\r
583                 protected void showPopupForTraceProgram(\r
584                                 final JComponent comp,\r
585                                 final ProgDetailList tvd, final String keyword, final int threshold,\r
586                                 final int x, final int y, final int h) {\r
587                         \r
588                         timer_now.pause();\r
589                         Viewer.this.showPopupForTraceProgram(comp, tvd, keyword, threshold, x, y, h);\r
590                         timer_now.start();\r
591                 }\r
592 \r
593                 @Override\r
594                 protected void updateReserveDisplay() {\r
595                         timer_now.pause();\r
596                         listed.updateReserveMark();\r
597                         reserved.redrawReservedList();\r
598                         timer_now.start();\r
599                 }\r
600 \r
601                 @Override\r
602                 protected void addToPickup(ProgDetailList tvd) { Viewer.this.addToPickup(tvd); }\r
603 \r
604                 @Override\r
605                 protected boolean isTabSelected(MWinTab tab) { return mainWindow.isTabSelected(tab); }\r
606                 @Override\r
607                 protected void setSelectedTab(MWinTab tab) { mainWindow.setSelectedTab(tab); }\r
608 \r
609                 @Override\r
610                 protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
611                 @Override\r
612                 protected boolean isFullScreen() { return toolBar.isFullScreen(); }\r
613                 @Override\r
614                 protected void setSelectedPagerIndex(int idx) {\r
615                         toolBar.setSelectedPagerIndex(idx);\r
616                 }\r
617                 @Override\r
618                 protected void setPagerEnabled(boolean b) { toolBar.setPagerEnabled(b); }\r
619                 @Override\r
620                 protected int getPagerCount() { return toolBar.getPagerCount(); }\r
621                 @Override\r
622                 protected int getSelectedPagerIndex() { return toolBar.getSelectedPagerIndex(); }\r
623                 @Override\r
624                 protected void setPagerItems(TVProgramIterator pli, int curindex) {\r
625                         toolBar.setPagerItems(pli,curindex);\r
626                 }\r
627 \r
628                 @Override\r
629                 protected String getExtensionMark(ProgDetailList tvd) { return markchar.getExtensionMark(tvd); }\r
630                 @Override\r
631                 protected String getOptionMark(ProgDetailList tvd) { return markchar.getOptionMark(tvd)+markchar.getNewLastMark(tvd); }\r
632                 @Override\r
633                 protected String getPostfixMark(ProgDetailList tvd) { return markchar.getPostfixMark(tvd); }\r
634 \r
635                 @Override\r
636                 protected void setDividerEnvs(int loc) {\r
637                         if ( ! toolBar.isFullScreen() && mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
638                                 if (env.getSyncTreeWidth()) {\r
639                                         bounds.setTreeWidth(loc);\r
640                                         bounds.setTreeWidthPaper(loc);\r
641                                 }\r
642                                 else {\r
643                                         bounds.setTreeWidthPaper(loc);\r
644                                 }\r
645                         }\r
646                 }\r
647         }\r
648         \r
649         \r
650         \r
651         /**\r
652          * \r
653          * 本体予約一覧の内部クラス\r
654          * \r
655          */\r
656         private class VWReserveListView extends AbsReserveListView {\r
657 \r
658                 private static final long serialVersionUID = 1L;\r
659 \r
660                 // 環境設定の入れ物を渡す\r
661                 @Override\r
662                 protected Env getEnv() { return env; }\r
663                 @Override\r
664                 protected Bounds getBoundsEnv() { return bounds; }\r
665 \r
666                 @Override\r
667                 protected HDDRecorderList getRecorderList() { return recorders; }\r
668 \r
669                 // ログ関係はないのか\r
670                 \r
671                 // コンポーネントを渡す\r
672                 @Override\r
673                 protected AbsReserveDialog getReserveDialog() { return rdialog; }\r
674                 @Override\r
675                 protected Component getParentComponent() { return Viewer.this; }\r
676 \r
677                 @Override\r
678                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
679 \r
680                 /*\r
681                  * AbsReserveListView内でのイベントから呼び出されるメソッド群\r
682                  */\r
683                 \r
684                 @Override\r
685                 protected void updateReserveDisplay(String chname) {\r
686                         timer_now.pause();\r
687                         listed.updateReserveMark();\r
688                         paper.updateReserveBorder(chname);\r
689                         timer_now.start();\r
690                 }\r
691 \r
692                 @Override\r
693                 protected boolean doExecOnOff(boolean fexec, String title, String chnam, String rsvId, String recId) {\r
694                         return Viewer.this.doExecOnOff(fexec, title, chnam, rsvId, recId);\r
695                 }\r
696                 \r
697                 @Override\r
698                 protected JMenuItem getExecOnOffMenuItem(boolean fexec, String title,\r
699                                 String chnam, String rsvId, String recId) {\r
700 \r
701                         return Viewer.this.getExecOnOffMenuItem(fexec, title, chnam, rsvId, recId, 0);\r
702                 }\r
703 \r
704                 @Override\r
705                 protected JMenuItem getRemoveRsvMenuItem(String title, String chnam,\r
706                                 String rsvId, String recId) {\r
707                         \r
708                         return Viewer.this.getRemoveRsvMenuItem(title, chnam, rsvId, recId, 0);\r
709                 }\r
710 \r
711                 @Override\r
712                 protected JMenuItem getJumpMenuItem(String title, String chnam,\r
713                                 String startDT) {\r
714                         \r
715                         return Viewer.this.getJumpMenuItem(title, chnam, startDT);\r
716                 }\r
717 \r
718                 @Override\r
719                 protected JMenuItem getJumpToLastWeekMenuItem(String title,\r
720                                 String chnam, String startDT) {\r
721                         \r
722                         return Viewer.this.getJumpToLastWeekMenuItem(title, chnam, startDT);\r
723                 }\r
724 \r
725                 @Override\r
726                 protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
727         }\r
728         \r
729         \r
730         /**\r
731          * \r
732          * 録画結果一覧の内部クラス\r
733          * \r
734          */\r
735         private class VWRecordedListView extends AbsRecordedListView {\r
736 \r
737                 private static final long serialVersionUID = 1L;\r
738 \r
739                 // 環境設定の入れ物を渡す\r
740                 @Override\r
741                 protected Env getEnv() { return env; }\r
742                 @Override\r
743                 protected Bounds getBoundsEnv() { return bounds; }\r
744 \r
745                 @Override\r
746                 protected HDDRecorderList getRecorderList() { return recorders; }\r
747 \r
748                 // ログ関係はないのか\r
749                 \r
750                 // コンポーネントを渡す\r
751                 @Override\r
752                 protected Component getParentComponent() { return Viewer.this; }\r
753 \r
754                 @Override\r
755                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
756 \r
757                 /*\r
758                  * AbsReserveListView内でのイベントから呼び出されるメソッド群\r
759                  */\r
760                 \r
761                 @Override\r
762                 protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
763         }\r
764         \r
765         \r
766         /**\r
767          * \r
768          * 録画結果一覧の内部クラス\r
769          * \r
770          */\r
771         private class VWAutoReserveListView extends AbsAutoReserveListView {\r
772 \r
773                 private static final long serialVersionUID = 1L;\r
774 \r
775                 // 環境設定の入れ物を渡す\r
776                 @Override\r
777                 protected Env getEnv() { return env; }\r
778                 @Override\r
779                 protected Bounds getBoundsEnv() { return bounds; }\r
780                 \r
781         }\r
782         \r
783         /***\r
784          * 各種設定の内部クラス\r
785          */\r
786         private class VWSettingView extends AbsSettingView {\r
787 \r
788                 private static final long serialVersionUID = 1L;\r
789 \r
790                 // 環境設定の入れ物を渡す\r
791                 @Override\r
792                 protected Env getEnv() { return env; }\r
793                 @Override\r
794                 protected ClipboardInfoList getCbItemEnv() { return cbitems; }\r
795                 @Override\r
796                 protected VWLookAndFeel getLAFEnv() { return vwlaf; }\r
797                 @Override\r
798                 protected VWFont getFontEnv() { return vwfont; }\r
799                 \r
800                 // メッセージ出力関連\r
801                 @Override\r
802                 protected StatusWindow getStWin() { return stwin; }\r
803                 @Override\r
804                 protected StatusTextArea getMWin() { return mwin; }\r
805                 \r
806                 // コンポーネントを渡す\r
807                 @Override\r
808                 protected Component getParentComponent() { return Viewer.this; }\r
809                 @Override\r
810                 protected VWColorChooserDialog getCcWin() { return ccwin; }\r
811 \r
812                 /*\r
813                  * AbsSettingView内でのイベントから呼び出されるメソッド群\r
814                  */\r
815                 \r
816                 @Override\r
817                 protected void lafChanged(String lafname) {\r
818                         vwlaf.update(lafname);\r
819                         Viewer.this.updateComponentTreeUI();\r
820                         StdAppendMessage("Set LookAndFeel="+lafname);\r
821                 }\r
822 \r
823                 @Override\r
824                 protected void fontChanged(String fn, int fontSize) {\r
825                         vwfont.update(fn, fontSize);\r
826                         Viewer.this.updateComponentTreeUI();\r
827                         StdAppendMessage("システムのフォントを変更しました: "+fn+", size="+fontSize);\r
828                 }\r
829 \r
830                 @Override\r
831                 protected void setEnv(final boolean reload_prog) {\r
832                         \r
833                         //listed.pauseTimer();\r
834                         timer_now.pause();\r
835                         \r
836                         bounds.save();\r
837                         cbitems.save();\r
838                         env.save();\r
839 \r
840                         // CommonUtilsの設定変更\r
841                         CommonUtils.setAdjLateNight(env.getAdjLateNight());\r
842                         CommonUtils.setExpandTo8(env.getExpandTo8());\r
843                         CommonUtils.setUseRundll32(env.getUseRundll32());\r
844                         CommonUtils.setDisplayPassedReserve(env.getDisplayPassedReserve());\r
845                         CommonUtils.setDebug(env.getDebug());\r
846                         \r
847                         SwingBackgroundWorker.setDebug(env.getDebug());\r
848 \r
849                         // ほにゃらら\r
850                         toolBar.setDebug(env.getDebug());\r
851                         autores.setDebug(env.getDebug());\r
852 \r
853                         // PassedProgramListの設定変更\r
854                         tvprograms.getPassed().setPassedDir(env.getPassedDir());\r
855 \r
856                         // レコーダプラグインの設定変更\r
857                         for ( HDDRecorder rec : recorders ) {\r
858                                 // 拡張設定だけ\r
859                                 setSettingRecPluginExt(rec, env);\r
860                         }\r
861 \r
862                         // Web番組表共通設定\r
863                         setSettingProgPluginCommon(env);\r
864                         \r
865                         // web番組表のリフレッシュ\r
866                         setSettingProgPluginAll(env);\r
867                         \r
868                         // リロードメニューの書き換え\r
869                         toolBar.updateReloadProgramExtention();\r
870                         \r
871                         // ページャーコンボボックスの書き換え\r
872                         toolBar.setPagerItems();\r
873                         \r
874                         // 列の表示・非表示\r
875                         listed.setMarkColumnVisible(env.getSplitMarkAndTitle());\r
876                         listed.setDetailColumnVisible(env.getShowDetailOnList());\r
877                         listed.setRowHeaderVisible(env.getRowHeaderVisible());\r
878                         reserved.setRowHeaderVisible(env.getRowHeaderVisible());\r
879                         \r
880                         // 強調色\r
881                         listed.setMatchedKeywordColor(env.getMatchedKeywordColor());\r
882                         listed.setRsvdLineColor((env.getRsvdLineEnhance())?(env.getRsvdLineColor()):(null));\r
883                         listed.setPickedLineColor((env.getRsvdLineEnhance())?(env.getPickedLineColor()):(null));\r
884                         listed.setCurrentLineColor((env.getCurrentLineEnhance())?(env.getCurrentLineColor()):(null));\r
885                         \r
886                         // システムトレイアイコン\r
887                         setTrayIconVisible(env.getShowSysTray());\r
888                         setXButtonAction(env.getShowSysTray() && env.getHideToTray());\r
889                         \r
890                         // 新聞形式のツールチップの表示時間を変更する\r
891                         setTooltipDelay();\r
892                         \r
893                         // Web番組表の再構築\r
894                         mpList.setHistoryOnlyUpdateOnce(env.getHistoryOnlyUpdateOnce());\r
895                         mpList.setShowOnlyNonrepeated(env.getShowOnlyNonrepeated());\r
896                         \r
897                         // 番組情報の再取得\r
898                         if ( reload_prog ) {\r
899                                 loadTVProgram(false,LoadFor.ALL);       // 部品呼び出し\r
900                         }\r
901                         \r
902                         // 新聞描画枠のリセット\r
903                         paper.clearPanel();\r
904                         paper.buildMainViewByDate();\r
905                         \r
906                         // 再度ツリーの再構築\r
907                         paper.redrawTreeByDate();\r
908                         paper.redrawTreeByPassed();\r
909                         \r
910                         listed.redrawTreeByHistory();\r
911                         listed.redrawTreeByCenter();\r
912                         \r
913                         // 再描画\r
914                         paper.reselectTree();\r
915                         listed.reselectTree();\r
916 \r
917                         //listed.continueTimer();       // まあreselectTree()で再開しているはずだが\r
918                         timer_now.start();\r
919                 }\r
920         }\r
921         \r
922         /**\r
923          * レコーダ設定タブの内部クラス\r
924          * @see AbsRecorderSettingView\r
925          */\r
926         private class VWRecorderSettingView extends AbsRecorderSettingView {\r
927 \r
928                 private static final long serialVersionUID = 1L;\r
929 \r
930                 // 環境設定の入れ物を渡す\r
931                 @Override\r
932                 protected Env getEnv() { return env; }\r
933                 @Override\r
934                 protected RecorderInfoList getRecInfos() { return recInfoList; }\r
935                 @Override\r
936                 protected HDDRecorderList getRecPlugins() { return recPlugins; }\r
937 \r
938                 // ログ関連\r
939                 @Override\r
940                 protected VWStatusWindow getStWin() { return stwin; }\r
941                 @Override\r
942                 protected StatusTextArea getMWin() { return mwin; }\r
943 \r
944                 // コンポーネントを渡す\r
945                 @Override\r
946                 protected Component getParentComponent() { return Viewer.this; }\r
947                 @Override\r
948                 protected VWColorChooserDialog getCcWin() { return ccwin; }\r
949 \r
950                 @Override\r
951                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
952 \r
953                 /*\r
954                  * AbsRecorderSettingView内でのイベントから呼び出されるメソッド群\r
955                  */\r
956                 \r
957                 @Override\r
958                 protected void setRecInfos() {\r
959                         \r
960                         timer_now.pause();\r
961                         \r
962                         // 設定を保存\r
963                         recInfoList.save();\r
964                         \r
965                         // レコーダプラグインのリフレッシュ\r
966                         initRecPluginAll();\r
967                         \r
968                         // レコーダ一覧をツールバーに設定\r
969                         toolBar.updateRecorderComboBox();\r
970                         \r
971                         // 予約一覧のリフレッシュ\r
972                         loadRdReserve(false, null);             // toolBarの内容がリセットされているので recId = null で\r
973                         \r
974                         // レコーダのエンコーダ表示の更新\r
975                         this.redrawRecorderEncoderEntry();\r
976 \r
977                         // レコーダ一覧をCHコード設定のコンボボックスに設定 \r
978                         chdatsetting.updateRecorderComboBox();\r
979                         \r
980                         // Web番組表の再構築(予約マークのリフレッシュ)\r
981                         paper.updateReserveBorder(null);\r
982                         listed.updateReserveMark();\r
983                         \r
984                         timer_now.start();\r
985                 }\r
986 \r
987         }\r
988 \r
989         \r
990         /***\r
991          * CH設定の内部クラス\r
992          */\r
993         private class VWChannelSettingView extends AbsChannelSettingView {\r
994 \r
995                 private static final long serialVersionUID = 1L;\r
996                 \r
997                 // 環境設定の入れ物を渡す\r
998                 @Override\r
999                 protected Env getEnv() { return Viewer.this.env; }\r
1000                 @Override\r
1001                 protected TVProgramList getProgPlugins() { return progPlugins; }\r
1002 \r
1003                 // ログ関連\r
1004                 @Override\r
1005                 protected StatusWindow getStWin() { return stwin; }\r
1006                 @Override\r
1007                 protected StatusTextArea getMWin() { return mwin; }\r
1008 \r
1009                 // コンポーネントを渡す\r
1010                 @Override\r
1011                 protected Component getParentComponent() { return Viewer.this; }\r
1012                 @Override\r
1013                 protected VWColorChooserDialog getCcWin() { return ccwin; }\r
1014 \r
1015                 @Override\r
1016                 protected void ringBeep() {\r
1017                         Viewer.this.ringBeep();\r
1018                 }\r
1019                 @Override\r
1020                 protected void updateProgPlugin() {\r
1021                         \r
1022                         timer_now.pause();\r
1023                         \r
1024                         // 設定を保存(プラグイン内部の設定はChannelSettingPanel内で実施)\r
1025                         env.save();\r
1026                         \r
1027                         // Web番組表プラグインのリフレッシュ\r
1028                         setSelectedProgPlugin();\r
1029                         initProgPluginAll();\r
1030                         \r
1031                         // CHソート設定に反映\r
1032                         chsortsetting.updateChannelSortTable();\r
1033                         \r
1034                         // CHコンバート設定をリフレッシュ\r
1035                         chconvsetting.updateChannelConvertTable();\r
1036                         \r
1037                         // CHコード設定にも反映\r
1038                         chdatsetting.updateChannelDatTable();\r
1039 \r
1040                         // 番組情報の再取得\r
1041                         loadTVProgram(false,LoadFor.ALL);       // 部品呼び出し\r
1042                         \r
1043                         // ツールバーに反映\r
1044                         toolBar.setPagerItems();\r
1045                         \r
1046                         // 新聞描画枠のリセット\r
1047                         paper.clearPanel();\r
1048                         paper.buildMainViewByDate();\r
1049                         \r
1050                         // サイドツリーの再構築\r
1051                         paper.redrawTreeByCenter();\r
1052                         \r
1053                         listed.redrawTreeByCenter();\r
1054                         \r
1055                         // 再構築\r
1056                         paper.reselectTree();\r
1057                         listed.reselectTree();\r
1058                         \r
1059                         timer_now.start();\r
1060                 }\r
1061                 \r
1062         }\r
1063         \r
1064         /***\r
1065          * CHコード設定の内部クラス\r
1066          */\r
1067         private class VWChannelDatSettingView extends AbsChannelDatSettingView {\r
1068 \r
1069                 private static final long serialVersionUID = 1L;\r
1070 \r
1071                 // 環境設定の入れ物を渡す\r
1072                 @Override\r
1073                 protected Env getEnv() { return Viewer.this.env; }\r
1074                 @Override\r
1075                 protected TVProgramList getTVProgramList() { return tvprograms; }\r
1076                 @Override\r
1077                 protected ChannelSort getChannelSort() { return chsort; }\r
1078                 @Override\r
1079                 protected HDDRecorderList getHDDRecorderList() { return recorders; }\r
1080 \r
1081                 // ログ関連\r
1082                 @Override\r
1083                 protected StatusWindow getStWin() { return stwin; }\r
1084                 @Override\r
1085                 protected StatusTextArea getMWin() { return mwin; }\r
1086 \r
1087                 // コンポーネントを渡す\r
1088                 @Override\r
1089                 protected Component getParentComponent() { return Viewer.this; }\r
1090                 \r
1091                 @Override\r
1092                 protected void ringBeep() {\r
1093                         Viewer.this.ringBeep();\r
1094                 }\r
1095                 \r
1096         }\r
1097 \r
1098         /**\r
1099          * CHソート設定タブの内部クラス\r
1100          */\r
1101         private class VWChannelSortView extends AbsChannelSortView {\r
1102 \r
1103                 private static final long serialVersionUID = 1L;\r
1104 \r
1105                 @Override\r
1106                 protected Env getEnv() { return Viewer.this.env; }\r
1107                 @Override\r
1108                 protected TVProgramList getTVProgramList() { return tvprograms; }\r
1109                 @Override\r
1110                 protected ChannelSort getChannelSort() { return chsort; }\r
1111                 \r
1112                 // ログ関連\r
1113                 @Override\r
1114                 protected StatusTextArea getMWin() { return mwin; }\r
1115                 \r
1116                 @Override\r
1117                 protected void updProc() {\r
1118                         \r
1119                         timer_now.pause();\r
1120                         \r
1121                         env.save();\r
1122                         \r
1123                         toolBar.setPagerItems();\r
1124                         toolBar.setSelectedPagerIndex(toolBar.getSelectedPagerIndex());\r
1125                         \r
1126                         // 新聞描画枠のリセット\r
1127                         paper.clearPanel();\r
1128                         paper.buildMainViewByDate();\r
1129                         \r
1130                         // サイドツリーの再構築\r
1131                         paper.redrawTreeByCenter();\r
1132                         \r
1133                         listed.redrawTreeByCenter();\r
1134                         \r
1135                         // 再描画 \r
1136                         paper.reselectTree();\r
1137                         listed.reselectTree();\r
1138                         \r
1139                         timer_now.start();\r
1140                 }\r
1141         }\r
1142         \r
1143         /**\r
1144          * CHコンバート設定タブの内部クラス\r
1145          */\r
1146         private class VWChannelConvertView extends AbsChannelConvertView {\r
1147 \r
1148                 private static final long serialVersionUID = 1L;\r
1149 \r
1150                 // 環境設定の入れ物を渡す\r
1151                 @Override\r
1152                 protected Env getEnv() { return env; }\r
1153                 @Override\r
1154                 protected TVProgramList getProgPlugins() { return progPlugins; }\r
1155                 @Override\r
1156                 protected ChannelConvert getChannelConvert() { return chconv; }\r
1157                 \r
1158         }\r
1159         \r
1160         /***\r
1161          * 予約ウィンドウの内部クラス\r
1162          */\r
1163         private class VWReserveDialog extends AbsReserveDialog {\r
1164 \r
1165                 private static final long serialVersionUID = 1L;\r
1166 \r
1167                 // コンストラクタ\r
1168                 public VWReserveDialog(int x, int y) {\r
1169                         super(x, y);\r
1170                 }\r
1171 \r
1172                 // 環境設定の入れ物を渡す\r
1173                 @Override\r
1174                 protected Env getEnv() { return env; }\r
1175                 @Override\r
1176                 protected TVProgramList getTVProgramList() { return tvprograms; }\r
1177                 @Override\r
1178                 protected HDDRecorderList getRecorderList() { return recorders; }\r
1179                 @Override\r
1180                 protected AVSetting getAVSetting() { return avs; }\r
1181                 @Override\r
1182                 protected CHAVSetting getCHAVSetting() { return chavs; }\r
1183 \r
1184                 // ログ関連\r
1185                 @Override\r
1186                 protected StatusWindow getStWin() { return stwin; }\r
1187                 @Override\r
1188                 protected StatusTextArea getMWin() { return mwin; }\r
1189                 \r
1190                 // コンポーネントを渡す\r
1191                 @Override\r
1192                 protected Component getParentComponent() { return Viewer.this; }\r
1193 \r
1194                 @Override\r
1195                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
1196 \r
1197                 /*\r
1198                  * ReserveDialog内でのイベントから呼び出されるメソッド群\r
1199                  */\r
1200                 \r
1201                 @Override\r
1202                 protected void searchLikeRsv(LikeReserveList likeRsvList, ProgDetailList tvd, String keyword, int threshold) {\r
1203                         Viewer.this.searchLikeRsv(likeRsvList, tvd, keyword, threshold);\r
1204                 }\r
1205 \r
1206                 @Override\r
1207                 protected String getSelectedRecorderOnToolbar() { return toolBar.getSelectedRecorder(); }\r
1208         }\r
1209         \r
1210         /**\r
1211          * 新聞の表示形式を操作するダイアログ\r
1212          */\r
1213         private class VWPaperColorsDialog extends AbsPaperColorsDialog {\r
1214 \r
1215                 private static final long serialVersionUID = 1L;\r
1216 \r
1217                 @Override\r
1218                 protected Env getEnv() { return env; }\r
1219                 @Override\r
1220                 protected Bounds getBoundsEnv() { return bounds; }\r
1221                 @Override\r
1222                 protected PaperColorsMap getPaperColorMap() { return pColors; }\r
1223                 \r
1224                 @Override\r
1225                 protected VWColorChooserDialog getCCWin() { return ccwin; }\r
1226                 \r
1227                 /*\r
1228                  * PaperColorsDialog内でのイベントから呼び出されるメソッド群\r
1229                  */\r
1230                 \r
1231                 // 背景色設定の反映\r
1232                 @Override\r
1233                 protected void updatePaperColors(Env ec,PaperColorsMap pc) {\r
1234                         paper.updateColors(ec,pc);\r
1235                 }\r
1236 \r
1237                 // フォント設定の反映\r
1238                 @Override\r
1239                 protected void updatePaperFonts(Env ec) {\r
1240                         paper.updateFonts(ec);\r
1241                 }\r
1242 \r
1243                 // サイズ設定の反映\r
1244                 @Override\r
1245                 protected void updatePaperBounds(Env ec, Bounds bc) {\r
1246                         paper.updateBounds(ec,bc);\r
1247                 }\r
1248                 \r
1249                 // 再描画?\r
1250                 @Override\r
1251                 protected void updatePaperRepaint() {\r
1252                         paper.updateRepaint();\r
1253                 }\r
1254         }\r
1255         \r
1256         /**\r
1257          * キーワード検索ウィンドウの内部クラス\r
1258          */\r
1259         private class VWKeywordDialog extends AbsKeywordDialog {\r
1260 \r
1261                 private static final long serialVersionUID = 1L;\r
1262 \r
1263                 @Override\r
1264                 void preview(SearchKey search) {\r
1265                         // 検索実行\r
1266                         if (search.alTarget.size() > 0) {\r
1267                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
1268                                 listed.redrawListByPreview(search);\r
1269                         }\r
1270                 }\r
1271         }\r
1272 \r
1273         /**\r
1274          * 延長警告管理ウィンドウの内部クラス\r
1275          */\r
1276         private class VWExtensionDialog extends AbsExtensionDialog {\r
1277 \r
1278                 private static final long serialVersionUID = 1L;\r
1279 \r
1280                 @Override\r
1281                 void preview(SearchKey search) {\r
1282                         // 検索実行\r
1283                         if (search.alTarget.size() > 0) {\r
1284                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
1285                                 listed.redrawListByPreview(search);\r
1286                         }\r
1287                 }\r
1288         }\r
1289         \r
1290         /***\r
1291          * \r
1292          * ツールバーの内部クラス\r
1293          * \r
1294          */\r
1295         private class VWToolBar extends AbsToolBar {\r
1296 \r
1297                 private static final long serialVersionUID = 1L;\r
1298 \r
1299                 @Override\r
1300                 protected Env getEnv() { return env; }\r
1301                 @Override\r
1302                 protected Bounds getBoundsEnv() { return bounds; }\r
1303                 @Override\r
1304                 protected TVProgramList getTVPrograms() { return tvprograms; }\r
1305                 @Override\r
1306                 protected ChannelSort getChannelSort() { return chsort; }\r
1307                 @Override\r
1308                 protected HDDRecorderList getHDDRecorders() { return recorders; }\r
1309 \r
1310                 @Override\r
1311                 protected StatusWindow getStWin() { return stwin; }\r
1312                 @Override\r
1313                 protected StatusTextArea getMWin() { return mwin; }\r
1314                 @Override\r
1315                 protected Component getParentComponent() { return Viewer.this; }\r
1316 \r
1317                 @Override\r
1318                 protected void ringBeep() { Viewer.this.ringBeep(); }\r
1319 \r
1320                 @Override\r
1321                 protected boolean doKeywordSerach(SearchKey search, String kStr, String sStr, boolean doFilter) {\r
1322                         \r
1323                         timer_now.pause();\r
1324                         \r
1325                         if ( mainWindow.getSelectedTab() == MWinTab.RSVED ) {\r
1326                                 reserved.redrawListByKeywordFilter(search, kStr);\r
1327                         }\r
1328                         else if ( mainWindow.getSelectedTab() == MWinTab.RECED ) {\r
1329                                 recorded.redrawListByKeywordFilter(search, kStr);\r
1330                         }\r
1331                         else {\r
1332                                 if ( search != null ) {\r
1333                                         mainWindow.setSelectedTab(MWinTab.LISTED);\r
1334                                         if ( doFilter ) {\r
1335                                                 // 絞り込み検索\r
1336                                                 listed.clearSelection();\r
1337                                                 listed.redrawListByKeywordFilter(search, kStr);\r
1338                                         }\r
1339                                         else if (sStr != null) {\r
1340                                                 // 過去ログ検索\r
1341                                                 searchPassedProgram(search, sStr);\r
1342                                                 listed.clearSelection();\r
1343                                                 listed.redrawListBySearched(ProgType.PASSED, 0);\r
1344                                                 \r
1345                                                 listed.redrawTreeByHistory();\r
1346                                         }\r
1347                                         else {\r
1348                                                 // キーワード検索\r
1349                                                 listed.clearSelection();\r
1350                                                 listed.redrawListByKeywordDyn(search, kStr);\r
1351                                         }\r
1352                                 }\r
1353                         }\r
1354                         \r
1355                         timer_now.start();\r
1356                         \r
1357                         return true;\r
1358                 }\r
1359 \r
1360                 @Override\r
1361                 protected boolean doBatchReserve() {\r
1362                         timer_now.pause();\r
1363                         listed.doBatchReserve();\r
1364                         timer_now.start();\r
1365                         return true;\r
1366                 }\r
1367 \r
1368                 @Override\r
1369                 protected boolean jumpToNow() {\r
1370                         timer_now.pause();\r
1371                         if ( ! mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
1372                                 mainWindow.setSelectedTab(MWinTab.PAPER);\r
1373                         }\r
1374                         paper.jumpToNow();\r
1375                         timer_now.start();\r
1376                         return true;\r
1377                 }\r
1378 \r
1379                 @Override\r
1380                 protected boolean jumpToPassed(String passed) {\r
1381                         timer_now.pause();\r
1382                         boolean b = paper.jumpToBangumi(null,passed);\r
1383                         timer_now.start();\r
1384                         return b;\r
1385                 }\r
1386 \r
1387                 @Override\r
1388                 protected boolean redrawByPager() {\r
1389                         timer_now.pause();\r
1390                         boolean b = paper.redrawByPager();\r
1391                         timer_now.start();\r
1392                         return b;\r
1393                 }\r
1394 \r
1395                 @Override\r
1396                 protected void toggleMatchBorder() {\r
1397                         timer_now.pause();\r
1398                         paper.toggleMatchBorder();\r
1399                         timer_now.start();\r
1400                 }\r
1401 \r
1402                 @Override\r
1403                 protected void setPaperColorDialogVisible(boolean b) {\r
1404                         //paper.stopTimer(); xxxx\r
1405                         timer_now.pause();\r
1406                         CommonSwingUtils.setLocationCenter(Viewer.this,pcwin);\r
1407                         pcwin.setVisible(true);\r
1408                         timer_now.start();\r
1409                 }\r
1410 \r
1411                 @Override\r
1412                 protected void setPaperZoom(int n) {\r
1413                         timer_now.pause();\r
1414                         paper.setZoom(n);\r
1415                         timer_now.start();\r
1416                 }\r
1417 \r
1418                 @Override\r
1419                 protected boolean recorderSelectorChanged() {\r
1420                         \r
1421                         timer_now.pause();\r
1422                         \r
1423                         if (mainWindow.isTabSelected(MWinTab.LISTED)) {\r
1424                                 listed.updateReserveMark();\r
1425                                 listed.selectBatchTarget();\r
1426                         }\r
1427                         else if (mainWindow.isTabSelected(MWinTab.RSVED)) {\r
1428                                 reserved.redrawReservedList();\r
1429                         }\r
1430                         else if (mainWindow.isTabSelected(MWinTab.RECED)) {\r
1431                                 recorded.redrawRecordedList();\r
1432                         }\r
1433                         \r
1434                         // 新聞形式の予約枠を書き換えるかもよ?\r
1435                         if (env.getEffectComboToPaper()) {\r
1436                                 paper.updateReserveBorder(null);\r
1437                         }\r
1438                         \r
1439                         timer_now.start();\r
1440                         \r
1441                         return true;\r
1442                 }\r
1443 \r
1444                 @Override\r
1445                 protected void takeSnapShot() {\r
1446                         \r
1447                         timer_now.pause();\r
1448                         \r
1449                         try {\r
1450                                 String fname;\r
1451                                 if ( mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
1452                                         fname = String.format("snapshot.%s",env.getSnapshotFmt().getExtension());\r
1453                                         CommonSwingUtils.saveComponentAsJPEG(listed.getCurrentView(), listed.getTableHeader(), null, listed.getTableBody(), fname, env.getSnapshotFmt(), Viewer.this);\r
1454                                 }\r
1455                                 else if ( mainWindow.isTabSelected(MWinTab.PAPER) ){\r
1456                                         if ( env.getDrawcacheEnable() || ! env.isPagerEnabled() ) {\r
1457                                                 fname = String.format("snapshot.%s",env.getSnapshotFmt().getExtension());\r
1458                                         }\r
1459                                         else {\r
1460                                                 int pcur = getSelectedPagerIndex();\r
1461                                                 int pmax = getPagerCount();\r
1462                                                 if ( env.getAllPageSnapshot() ) {\r
1463                                                         for ( int i=0; i<pmax; i++ ) {\r
1464                                                                 if ( i != pcur ) {\r
1465                                                                         setSelectedPagerIndex(i);\r
1466                                                                         fname = String.format("snapshot%02d.%s",i+1,env.getSnapshotFmt().getExtension());\r
1467                                                                         CommonSwingUtils.saveComponentAsJPEG(paper.getCurrentView(), paper.getCenterPane(), paper.getTimebarPane(), paper.getCurrentPane(), fname, env.getSnapshotFmt(), Viewer.this);\r
1468                                                                 }\r
1469                                                         }\r
1470                                                 }\r
1471                                                 fname = String.format("snapshot%02d.%s",pcur+1,env.getSnapshotFmt().getExtension());\r
1472                                                 setSelectedPagerIndex(pcur);\r
1473                                         }\r
1474                                         CommonSwingUtils.saveComponentAsJPEG(paper.getCurrentView(), paper.getCenterPane(), paper.getTimebarPane(), paper.getCurrentPane(), fname, env.getSnapshotFmt(), Viewer.this);\r
1475                                 }\r
1476                                 else {\r
1477                                         return; // おかしーよ\r
1478                                 }\r
1479                                 Desktop desktop = Desktop.getDesktop();\r
1480                                 if (env.getPrintSnapshot()) {\r
1481                                         desktop.print(new File(fname));\r
1482                                 }\r
1483                                 else {\r
1484                                         String emsg = CommonUtils.openFile(fname);\r
1485                                         if (emsg != null) {\r
1486                                                 mwin.appendError(emsg);\r
1487                                         }\r
1488                                 }\r
1489                         } catch (IOException e1) {\r
1490                                 e1.printStackTrace();\r
1491                         }\r
1492                         finally {\r
1493                                 timer_now.start();\r
1494                         }\r
1495                 }\r
1496 \r
1497                 @Override\r
1498                 protected void setStatusVisible(boolean b) {\r
1499                         Viewer.this.setStatusVisible(b);\r
1500                 }\r
1501 \r
1502                 @Override\r
1503                 protected void setFullScreen(boolean b) {\r
1504                         Viewer.this.setFullScreen(b);\r
1505                 }\r
1506 \r
1507                 @Override\r
1508                 protected void toggleSettingTabVisible() {\r
1509                         mainWindow.toggleShowSettingTabs();\r
1510                 }\r
1511 \r
1512                 @Override\r
1513                 protected boolean isTabSelected(MWinTab tab) {\r
1514                         return mainWindow.isTabSelected(tab);\r
1515                 }\r
1516 \r
1517                 @Override\r
1518                 protected boolean addKeywordSearch(SearchKey search) {\r
1519                         \r
1520                         timer_now.pause();\r
1521                         \r
1522                         AbsKeywordDialog kD = new VWKeywordDialog();\r
1523                         CommonSwingUtils.setLocationCenter(Viewer.this,kD);\r
1524                         \r
1525                         kD.open(search.getLabel(), search, srKeys, srGrps);\r
1526                         kD.setVisible(true);\r
1527                         \r
1528                         if (kD.isRegistered()) {\r
1529                                 // 検索結果の再構築\r
1530                                 mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
1531                                 mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
1532                                 \r
1533                                 // ツリーに反映する\r
1534                                 listed.redrawTreeByKeyword();\r
1535                                 \r
1536                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
1537                         }\r
1538                         \r
1539                         timer_now.start();\r
1540                         \r
1541                         return true;\r
1542                 }\r
1543 \r
1544                 @Override\r
1545                 protected boolean reLoadTVProgram(LoadFor lf) {\r
1546                         timer_now.pause();\r
1547                         boolean b = Viewer.this.reLoadTVProgram(lf);\r
1548                         timer_now.start();\r
1549                         return b;\r
1550                 }\r
1551 \r
1552                 @Override\r
1553                 protected boolean reLoadRdReserve(String myself) {\r
1554                         timer_now.pause();\r
1555                         boolean b = Viewer.this.reLoadRdReserve(myself);\r
1556                         timer_now.start();\r
1557                         return b;\r
1558                 }\r
1559 \r
1560                 @Override\r
1561                 protected boolean reLoadRdRecorded(String myself) {\r
1562                         timer_now.pause();\r
1563                         boolean b = Viewer.this.reLoadRdRecorded(myself);\r
1564                         timer_now.start();\r
1565                         return b;\r
1566                 }\r
1567 \r
1568         }\r
1569         \r
1570         \r
1571         \r
1572         /*******************************************************************************\r
1573          * 共通メソッド群\r
1574          ******************************************************************************/\r
1575         \r
1576         /**\r
1577          * 類似予約をさがす\r
1578          */\r
1579         private void searchLikeRsv(LikeReserveList likeRsvList, ProgDetailList tvd, String keyword, int threshold) {\r
1580                 \r
1581                 likeRsvList.clear();\r
1582                 \r
1583                 // 曖昧検索のための初期化\r
1584                 String keywordPop = null;\r
1585                 int thresholdVal = 0;\r
1586                 if (threshold > 0) {\r
1587                         // キーワード指定がある場合\r
1588                         keywordPop = TraceProgram.replacePop(keyword);\r
1589                         thresholdVal = threshold;\r
1590                 }\r
1591                 else {\r
1592                         // キーワード指定がない場合\r
1593                         keywordPop = tvd.titlePop;\r
1594                         thresholdVal = env.getDefaultFazzyThreshold();\r
1595                 }\r
1596 \r
1597                 // 検索範囲\r
1598                 long rangeLikeRsv = env.getRangeLikeRsv()*3600000;\r
1599                 \r
1600                 HashMap<String,Boolean> misCN = new HashMap<String, Boolean>();\r
1601                 for ( HDDRecorder recorder : recorders ) {\r
1602                         \r
1603                         // 終了した予約を整理する\r
1604                         recorder.refreshReserves();\r
1605                         \r
1606                         for ( ReserveList r : recorder.getReserves() ) {\r
1607                                 \r
1608                                 // タイトルのマッチング\r
1609                                 boolean isExist = false;\r
1610                                 if (env.getDisableFazzySearch() == false) {\r
1611                                         // 双方向の比較を行う・正引き\r
1612                                         int fazScore = TraceProgram.sumScore(keywordPop, r.getTitlePop());\r
1613                                         if ( fazScore >= thresholdVal) {\r
1614                                                 isExist = true;\r
1615                                         }\r
1616                                         else if ( ! env.getDisableFazzySearchReverse()) {\r
1617                                                 // 逆引き\r
1618                                                 fazScore = TraceProgram.sumScore(r.getTitlePop(), keywordPop);\r
1619                                                 if ( fazScore >= thresholdVal) {\r
1620                                                         isExist = true;\r
1621                                                 }\r
1622                                         }\r
1623                                 }\r
1624                                 else {\r
1625                                         if (r.getTitlePop().equals(tvd.titlePop)) {\r
1626                                                 isExist = true;\r
1627                                         }\r
1628                                 }\r
1629                                 if ( ! isExist) {\r
1630                                         continue;\r
1631                                 }\r
1632                                 \r
1633                                 // 放送局のマッチング\r
1634                                 if (r.getCh_name() == null) {\r
1635                                         if(r.getChannel().length() > 0) {\r
1636                                                 misCN.put(r.getChannel(),true);\r
1637                                         }\r
1638                                         continue;\r
1639                                 }\r
1640                                 if ( ! r.getCh_name().equals(tvd.center)) {\r
1641                                         continue;\r
1642                                 }\r
1643                                 \r
1644                                 // 近接時間チェック\r
1645                                 boolean inRange = true;\r
1646                                 if (rangeLikeRsv > 0) {\r
1647                                         \r
1648                                         inRange = false;\r
1649                                         \r
1650                                         ArrayList<String> starts = new ArrayList<String>();\r
1651                                         ArrayList<String> ends = new ArrayList<String>();\r
1652                                         CommonUtils.getStartEndList(starts, ends, r);\r
1653                                         for (int j=0; j<starts.size(); j++) {\r
1654                                                 long d = CommonUtils.getDiffDateTime(tvd.startDateTime, starts.get(j));\r
1655                                                 //StdAppendMessage(String.format("%s %s %d", tvd.startDateTime, starts.get(j),d));\r
1656                                                 if (d <= rangeLikeRsv) {\r
1657                                                         inRange = true;\r
1658                                                         break;\r
1659                                                 }\r
1660                                         }\r
1661                                 }\r
1662                                 if ( ! inRange) {\r
1663                                         continue;\r
1664                                 }\r
1665                                 \r
1666                                 // 類似予約あり!\r
1667                                 likeRsvList.add(new LikeReserveItem(recorder, r));\r
1668                         }\r
1669                 }\r
1670                 \r
1671                 return;\r
1672         }\r
1673         \r
1674         \r
1675         /***\r
1676          * \r
1677          * リスト・新聞形式共通\r
1678          * \r
1679          */\r
1680 \r
1681         /**\r
1682          *  番組追跡への追加とgoogle検索\r
1683          */\r
1684         public void showPopupForTraceProgram(\r
1685                         final JComponent comp,\r
1686                         final ProgDetailList tvd, final String keyword, final int threshold,\r
1687                         final int x, final int y, final int h)\r
1688         {\r
1689                 JPopupMenu pop = new JPopupMenu();\r
1690         \r
1691                 // 予約する\r
1692                 if ( tvd.type == ProgType.PASSED ||\r
1693                                 (tvd.type == ProgType.PROG && tvd.subtype == ProgSubtype.RADIO) ||\r
1694                                 recorders.size() == 0 ) {\r
1695                         // 過去ログは処理対象外です\r
1696                 }\r
1697                 else {\r
1698                         JMenuItem menuItem = new JMenuItem("予約する【"+tvd.title+" ("+tvd.center+")】");\r
1699                         \r
1700                         menuItem.addActionListener(new ActionListener() {\r
1701                                 public void actionPerformed(ActionEvent e) {\r
1702 \r
1703                                         CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
1704 \r
1705                                         if ( rdialog.open(tvd) ) {\r
1706                                                 rdialog.setVisible(true);\r
1707                                         }\r
1708                                         else {\r
1709                                                 rdialog.setVisible(false);\r
1710                                         }\r
1711                                         \r
1712                                         //\r
1713                                         if (rdialog.isReserved()) {\r
1714                                                 listed.updateReserveMark();\r
1715                                                 paper.updateReserveBorder(tvd.center);\r
1716                                                 reserved.redrawReservedList();\r
1717                                         }\r
1718                                 }\r
1719                         });\r
1720                         pop.add(menuItem);\r
1721                 }\r
1722                 \r
1723                 pop.addSeparator();\r
1724                 \r
1725                 // 類似予約検索\r
1726                 LikeReserveList likeRsvList = new LikeReserveList();\r
1727                 searchLikeRsv(likeRsvList, tvd, "", 0);\r
1728                 \r
1729                 // 重複予約検索\r
1730                 LikeReserveList overlapRsvList = new LikeReserveList();\r
1731                 searchOverlapRsv(overlapRsvList, tvd, h);\r
1732                 \r
1733                 // 類似と重複で被るものを重複から除外\r
1734                 for ( LikeReserveItem ll : likeRsvList ) {\r
1735                         int i=0;\r
1736                         for ( ; i<overlapRsvList.size(); i++ ) {\r
1737                                 if ( ll.getRsv() == overlapRsvList.getRsv(i) ) {\r
1738                                         break;\r
1739                                 }\r
1740                         }\r
1741                         if ( i < overlapRsvList.size() ) {\r
1742                                 overlapRsvList.remove(i);\r
1743                         }\r
1744                 }\r
1745                 \r
1746                 // 予約実行ON・OFF\r
1747                 if ( tvd.type != ProgType.PASSED )\r
1748                 {\r
1749                         for ( int n=0; n<2; n++ ) {\r
1750                                 \r
1751                                 LikeReserveList rsvList = null;\r
1752                                 if ( n == 0 ) {\r
1753                                         rsvList = likeRsvList;\r
1754                                 }\r
1755                                 else {\r
1756                                         rsvList = overlapRsvList;\r
1757                                 }\r
1758                                 \r
1759                                 for ( int i=0; i<rsvList.size(); i++ ) {\r
1760                                         \r
1761                                         final boolean fexec = rsvList.getRsv(i).getExec();\r
1762                                         final String title = rsvList.getRsv(i).getTitle();\r
1763                                         final String chnam = rsvList.getRsv(i).getCh_name();\r
1764                                         final String rsvId = rsvList.getRsv(i).getId();\r
1765                                         final String recId = rsvList.getRec(i).Myself();\r
1766                                         \r
1767                                         pop.add(getExecOnOffMenuItem(fexec,title,chnam,rsvId,recId,n));\r
1768                                 }\r
1769                                 \r
1770                                 pop.addSeparator();\r
1771                         }\r
1772                 }\r
1773                 \r
1774                 pop.addSeparator();\r
1775                 \r
1776                 // 削除する\r
1777                 if ( tvd.type != ProgType.PASSED )      // 過去ログは処理対象外です\r
1778                 {\r
1779                         for ( int n=0; n<2; n++ ) {\r
1780                                 \r
1781                                 LikeReserveList rsvList = null;\r
1782                                 if ( n == 0 ) {\r
1783                                         rsvList = likeRsvList;\r
1784                                 }\r
1785                                 else {\r
1786                                         rsvList = overlapRsvList;\r
1787                                 }\r
1788                                 \r
1789                                 for (int i=0; i<rsvList.size(); i++) {\r
1790                                         \r
1791                                         final String title = rsvList.getRsv(i).getTitle();\r
1792                                         final String chnam = rsvList.getRsv(i).getCh_name();\r
1793                                         final String rsvId = rsvList.getRsv(i).getId();\r
1794                                         final String recId = rsvList.getRec(i).Myself();\r
1795                                         \r
1796                                         pop.add(getRemoveRsvMenuItem(title,chnam,rsvId,recId,n));\r
1797                                 }\r
1798                                 \r
1799                                 pop.addSeparator();\r
1800                         }\r
1801                 }\r
1802                 else {\r
1803                         pop.addSeparator();\r
1804                         pop.addSeparator();\r
1805                 }\r
1806                 \r
1807                 // ジャンプする\r
1808                 {\r
1809                         if ( mainWindow.isTabSelected(MWinTab.LISTED) ) {\r
1810                                 pop.add(getJumpMenuItem(tvd.title,tvd.center,tvd.startDateTime));\r
1811                         }\r
1812                         if ( mainWindow.isTabSelected(MWinTab.LISTED) || mainWindow.isTabSelected(MWinTab.PAPER) ) {\r
1813                                 JMenuItem mi = getJumpToLastWeekMenuItem(tvd.title,tvd.center,tvd.startDateTime);\r
1814                                 if ( mi != null ) {\r
1815                                         pop.add(mi);\r
1816                                 }\r
1817                         }\r
1818                 }\r
1819                 \r
1820                 pop.addSeparator();\r
1821                 \r
1822                 // 番組追跡へ追加する\r
1823                 {\r
1824                         final String label = tvd.title+" ("+tvd.center+")";\r
1825                         JMenuItem menuItem = new JMenuItem("番組追跡への追加【"+label+"】");\r
1826                         menuItem.addActionListener(new ActionListener() {\r
1827                                 public void actionPerformed(ActionEvent e) {\r
1828                                         //\r
1829                                         for (TraceKey tr : trKeys.getTraceKeys()) {\r
1830                                                 if (tr.getLabel().equals(label)) {\r
1831                                                         mwin.appendMessage("【警告】すでに番組追跡に登録されています:"+label);\r
1832                                                         ringBeep();\r
1833                                                         return;\r
1834                                                 }\r
1835                                         }\r
1836                                         \r
1837                                         //\r
1838                                         trKeys.add(label, tvd.title, tvd.center, env.getDefaultFazzyThreshold());\r
1839 \r
1840                                         VWTraceKeyDialog tD = new VWTraceKeyDialog(0,0);\r
1841                                         CommonSwingUtils.setLocationCenter(mainWindow,tD);\r
1842                                         \r
1843                                         tD.reopen(label, trKeys);\r
1844                                         tD.setVisible(true);\r
1845                                         \r
1846                                         if (tD.isRegistered()) { \r
1847                                                 //\r
1848                                                 trKeys.save();\r
1849                                                 \r
1850                                                 // 検索結果の再構築\r
1851                                                 mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
1852                                                 mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
1853                                                 \r
1854                                                 // ツリーに反映する\r
1855                                                 listed.redrawTreeByTrace();\r
1856 \r
1857                                                 // 表示を更新する\r
1858                                                 paper.updateBangumiColumns();\r
1859                                                 listed.reselectTree();\r
1860                                                 \r
1861                                                 mwin.appendMessage("番組追跡へ追加しました【"+label+"】");\r
1862                                         }\r
1863                                         else {\r
1864                                                 trKeys.remove(label);\r
1865                                         }\r
1866                                 }\r
1867                         });\r
1868                         pop.add(menuItem);\r
1869                 }\r
1870                 \r
1871                 // キーワード検索へ追加する\r
1872                 {\r
1873                         final String label = tvd.title+" ("+tvd.center+")";\r
1874                         JMenuItem menuItem = new JMenuItem("キーワード検索への追加【"+label+"】");\r
1875                         menuItem.addActionListener(new ActionListener(){\r
1876                                 public void actionPerformed(ActionEvent e){\r
1877                                         //\r
1878                                         for (SearchKey sr : srKeys.getSearchKeys()) {\r
1879                                                 if (sr.getLabel().equals(tvd.title)) {\r
1880                                                         ringBeep();\r
1881                                                         String msg = "すでにキーワード検索に登録されています: "+tvd.title;\r
1882                                                         mwin.appendMessage(msg);\r
1883                                                         JOptionPane.showConfirmDialog(null, msg, "警告", JOptionPane.CLOSED_OPTION);                                                  // キーワード検索の追加ではダイアログで修正できるので止めない\r
1884                                                         //return;\r
1885                                                 }\r
1886                                         }\r
1887                                         // 「キーワード検索の設定」ウィンドウを開く\r
1888                                         SearchKey search = new SearchKey();\r
1889                                         {\r
1890                                                 search.setCondition("0");\r
1891                                                 search.alTarget.add(TargetId.TITLE);\r
1892                                                 search.alContain.add("0");\r
1893                                                 search.alKeyword.add(tvd.title);\r
1894                                         }\r
1895                                         {\r
1896                                                 search.setCondition("0");\r
1897                                                 search.alTarget.add(TargetId.CHANNEL);\r
1898                                                 search.alContain.add("0");\r
1899                                                 search.alKeyword.add(tvd.center);\r
1900                                         }\r
1901                                         {\r
1902                                                 AbsKeywordDialog kD = new VWKeywordDialog();\r
1903                                                 CommonSwingUtils.setLocationCenter(mainWindow,kD);\r
1904                                                 \r
1905                                                 kD.open(tvd.title, search, srKeys, srGrps);\r
1906                                                 kD.setVisible(true);\r
1907                                                 \r
1908                                                 if (kD.isRegistered()) {\r
1909                                                         // 検索結果の再構築\r
1910                                                         mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
1911                                                         mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
1912                                                         \r
1913                                                         // ツリーに反映する\r
1914                                                         listed.redrawTreeByKeyword();\r
1915 \r
1916                                                         // 表示を更新する\r
1917                                                         paper.updateBangumiColumns();\r
1918                                                         listed.reselectTree();\r
1919                                                         \r
1920                                                         mwin.appendMessage("キーワード検索へ追加しました【"+label+"】");\r
1921                                                 }\r
1922                                         }\r
1923                                 }\r
1924                         });\r
1925                         pop.add(menuItem);\r
1926                 }\r
1927                 \r
1928                 // ピックアップへ追加する\r
1929                 {\r
1930                         boolean isRemoveItem = false;\r
1931                         if ( mainWindow.isTabSelected(MWinTab.LISTED) && tvd.type == ProgType.PICKED ) {\r
1932                                 isRemoveItem = true;\r
1933                         }\r
1934                         else {\r
1935                                 PickedProgram tvp = tvprograms.getPickup();\r
1936                                 if ( tvp != null ) {\r
1937                                         isRemoveItem = tvp.remove(tvd, tvd.center, tvd.accurateDate, false);\r
1938                                 }\r
1939                         }\r
1940                         \r
1941                         if ( ! isRemoveItem )   // 過去ログは処理対象外です\r
1942                         {\r
1943                                 final String label = tvd.title+" ("+tvd.center+")";\r
1944                                 JMenuItem menuItem = new JMenuItem("ピックアップへの追加【"+label+"】");\r
1945                                 menuItem.addActionListener(new ActionListener() {\r
1946                                         public void actionPerformed(ActionEvent e) {\r
1947                                                 //\r
1948                                                 PickedProgram tvp = tvprograms.getPickup();\r
1949                                                 if ( tvp != null ) {\r
1950                                                         tvp.refresh();\r
1951                                                         tvp.add(tvd);\r
1952                                                         tvp.save();\r
1953                                                         /*\r
1954                                                         if ( listed.isNodeSelected(ListedTreeNode.PICKUP) ) {\r
1955                                                                 // ピックアップノードが選択されていたらリストを更新する\r
1956                                                                 listed.reselectTree();\r
1957                                                         }\r
1958                                                         */\r
1959                                                         listed.updateReserveMark();\r
1960                                                         listed.refocus();\r
1961                                                         paper.updateReserveBorder(tvd.center);\r
1962                                                         mwin.appendMessage("【ピックアップ】追加しました: "+tvd.title+" ("+tvd.center+")");\r
1963                                                         return;\r
1964                                                 }\r
1965                                         }\r
1966                                 });\r
1967                                 pop.add(menuItem);\r
1968                         }\r
1969                         else {\r
1970                                 final String label = tvd.title+" ("+tvd.center+")";\r
1971                                 JMenuItem menuItem = new JMenuItem("ピックアップからの削除【"+label+"】");\r
1972                                 menuItem.setForeground(Color.RED);\r
1973                                 menuItem.addActionListener(new ActionListener() {\r
1974                                         public void actionPerformed(ActionEvent e) {\r
1975                                                 //\r
1976                                                 PickedProgram tvp = tvprograms.getPickup();\r
1977                                                 if ( tvp != null ) {\r
1978                                                         tvp.refresh();\r
1979                                                         tvp.remove(tvd, tvd.center, tvd.accurateDate, true);\r
1980                                                         tvp.save();\r
1981                                                         /*\r
1982                                                         if ( listed.isNodeSelected(ListedTreeNode.PICKUP) || listed.isNodeSelected(ListedTreeNode.STANDBY) ) {\r
1983                                                                 // ピックアップノードが選択されていたらリストを更新する\r
1984                                                                 listed.reselectTree();\r
1985                                                         }\r
1986                                                         */\r
1987                                                         listed.updateReserveMark();\r
1988                                                         paper.updateReserveBorder(tvd.center);\r
1989                                                         mwin.appendMessage("【ピックアップ】削除しました: "+tvd.title+" ("+tvd.center+")");\r
1990                                                         return;\r
1991                                                 }\r
1992                                         }\r
1993                                 });\r
1994                                 pop.add(menuItem);\r
1995                         }\r
1996                 }\r
1997 \r
1998                 pop.addSeparator();\r
1999                 \r
2000                 // googleで検索する\r
2001                 {\r
2002                         for (final TextValueSet tv : env.getTvCommand()) {\r
2003                                 JMenuItem menuItem = new JMenuItem(tv.getText());\r
2004                                 String escepedTitle = "";\r
2005                                 String escepedChName = "";\r
2006                                 String escepedDetail = "";\r
2007                                 try {\r
2008                                         escepedTitle = URLEncoder.encode(tvd.title,"UTF-8");\r
2009                                         escepedDetail = URLEncoder.encode(tvd.detail,"UTF-8");\r
2010                                         escepedChName = URLEncoder.encode(tvd.center,"UTF-8");\r
2011                                 } catch (UnsupportedEncodingException e2) {\r
2012                                         //\r
2013                                 }\r
2014                                 \r
2015                                 String cmd = tv.getValue();\r
2016                                 if ( cmd.matches(".*%DETAILURL%.*") ) {\r
2017                                         if ( tvd.link == null || tvd.link.length() == 0 ) {\r
2018                                                 // このメニューは利用できません!\r
2019                                                 menuItem.setEnabled(false);\r
2020                                                 menuItem.setForeground(Color.lightGray);\r
2021                                         }\r
2022                                 }\r
2023                                 cmd = cmd.replaceAll("%ENCTITLE%", escepedTitle);\r
2024                                 cmd = cmd.replaceAll("%ENCDETAIL%", escepedDetail);\r
2025                                 cmd = cmd.replaceAll("%ENCCHNAME%", escepedChName);\r
2026                                 cmd = cmd.replaceAll("%TITLE%", tvd.title);\r
2027                                 cmd = cmd.replaceAll("%DETAIL%", tvd.detail);\r
2028                                 cmd = cmd.replaceAll("%CHNAME%", tvd.center);\r
2029                                 cmd = cmd.replaceAll("%DATE%", tvd.accurateDate);\r
2030                                 cmd = cmd.replaceAll("%START%", tvd.start);\r
2031                                 cmd = cmd.replaceAll("%END%", tvd.end);\r
2032                                 cmd = cmd.replaceAll("%DETAILURL%", tvd.link); \r
2033                                 \r
2034                                 // CHAN-TORU対応\r
2035                                 if ( cmd.matches(".*%TVKAREACODE%.*") && cmd.matches(".*%TVKPID%.*") ) {\r
2036                                         Center cr = null;\r
2037                                         for ( TVProgram tvp : progPlugins ) {\r
2038                                                 if ( tvp.getTVProgramId().startsWith("Gガイド.テレビ王国") ) {\r
2039                                                         for ( Center tempcr : tvp.getCRlist() ) {\r
2040                                                                 // CH設定が完了している必要がある\r
2041                                                                 if ( tvp.getSubtype() == ProgSubtype.TERRA && tvp.getSelectedCode().equals(TVProgram.allCode) && ! tempcr.getAreaCode().equals(TVProgram.bsCode) ) {\r
2042                                                                         // 地域が全国の地デジの場合のみ、有効局かどうかを確認する必要がある\r
2043                                                                         if ( tempcr.getCenter().equals(tvd.center) && tempcr.getOrder() > 0 ) {\r
2044                                                                                 // このメニューは利用できます!\r
2045                                                                                 cr = tempcr;\r
2046                                                                                 break;\r
2047                                                                         }\r
2048                                                                 }\r
2049                                                                 else {\r
2050                                                                         if ( tempcr.getCenter().equals(tvd.center) ) {\r
2051                                                                                 // このメニューは利用できます!\r
2052                                                                                 cr = tempcr;\r
2053                                                                                 break;\r
2054                                                                         }\r
2055                                                                 }\r
2056                                                         }\r
2057                                                         \r
2058                                                         if ( cr != null ) {\r
2059                                                                 break;\r
2060                                                         }\r
2061                                                 }\r
2062                                         }\r
2063                                         if ( cr != null ) {\r
2064                                                 String areacode = null;\r
2065                                                 String centercode = cr.getLink();\r
2066                                                 String cat = cr.getLink().substring(0,1);\r
2067                                                 if ( cat.equals("1") ) {\r
2068                                                         areacode = cr.getAreaCode();\r
2069                                                 }\r
2070                                                 else {\r
2071                                                         if ( cat.equals("4") ) {\r
2072                                                                 cat = "5";\r
2073                                                         }\r
2074                                                         else if ( cat.equals("5") ) {\r
2075                                                                 cat = "4";\r
2076                                                         }\r
2077                                                         areacode = "10";\r
2078                                                 }\r
2079                                                 \r
2080                                                 cmd = cmd.replaceAll("%TVKAREACODE%", areacode);\r
2081                                                 cmd = cmd.replaceAll("%TVKCAT%", cat);\r
2082                                                 cmd = cmd.replaceAll("%TVKPID%", centercode+CommonUtils.getDateTimeYMD(CommonUtils.getCalendar(tvd.startDateTime)).replaceFirst("..$", ""));\r
2083                                                 System.out.println("[DEBUG] "+cmd);\r
2084                                                 \r
2085                                                 menuItem.setEnabled(true);\r
2086                                                 menuItem.setForeground(Color.BLACK);\r
2087                                         }\r
2088                                         else {\r
2089                                                 menuItem.setEnabled(false);\r
2090                                                 menuItem.setForeground(Color.lightGray);\r
2091                                         }\r
2092                                 }\r
2093                                 \r
2094                                 final String run = cmd;\r
2095                                 \r
2096                                 menuItem.addActionListener(new ActionListener() {\r
2097                                         @Override\r
2098                                         public void actionPerformed(ActionEvent e) {\r
2099                                                 try {\r
2100                                                         if (run.indexOf("http") == 0) {\r
2101                                                                 Desktop desktop = Desktop.getDesktop();\r
2102                                                                 desktop.browse(new URI(run));\r
2103                                                         }\r
2104                                                         else {\r
2105                                                                 CommonUtils.executeCommand(run);\r
2106                                                         }\r
2107                                                 } catch (IOException e1) {\r
2108                                                         e1.printStackTrace();\r
2109                                                 } catch (URISyntaxException e1) {\r
2110                                                         e1.printStackTrace();\r
2111                                                 }\r
2112                                         }\r
2113                                 });\r
2114 \r
2115                                 pop.add(menuItem);\r
2116                         }\r
2117                 }\r
2118                 \r
2119                 pop.addSeparator();\r
2120                 \r
2121                 // クリップボードへコピーする\r
2122                 {\r
2123                         JMenuItem menuItem = new JMenuItem("番組名をコピー【"+tvd.title+"】");\r
2124                         menuItem.addActionListener(new ActionListener() {\r
2125                                 public void actionPerformed(ActionEvent e) {\r
2126                                         String msg = tvd.title;\r
2127                                         Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
2128                                         StringSelection s = new StringSelection(msg);\r
2129                                         cb.setContents(s, null);\r
2130                                 }\r
2131                         });\r
2132                         pop.add(menuItem);\r
2133                 }\r
2134                 {\r
2135                         JMenuItem menuItem = new JMenuItem("番組名と詳細をコピー【"+tvd.title+"】");\r
2136                         menuItem.addActionListener(new ActionListener() {\r
2137                                 public void actionPerformed(ActionEvent e) {\r
2138                                         String msg = tvd.title+System.getProperty("line.separator")+tvd.detail+"\0"+tvd.getAddedDetail();\r
2139                                         Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
2140                                         StringSelection s = new StringSelection(msg);\r
2141                                         cb.setContents(s, null);\r
2142                                 }\r
2143                         });\r
2144                         pop.add(menuItem);\r
2145                 }\r
2146                 {\r
2147                         JMenuItem menuItem = new JMenuItem("番組情報をコピー【"+tvd.title+"】");\r
2148                         menuItem.addActionListener(new ActionListener() {\r
2149                                 public void actionPerformed(ActionEvent e) {\r
2150                                         String msg = "";\r
2151                                         int preId = 0;\r
2152                                         for (ClipboardInfo cb : cbitems) {\r
2153                                                 if (cb.getB()) {\r
2154                                                         switch (cb.getId()) {\r
2155                                                         case 1:\r
2156                                                                 msg += tvd.title+"\t";\r
2157                                                                 break;\r
2158                                                         case 2:\r
2159                                                                 msg += tvd.center+"\t";\r
2160                                                                 break;\r
2161                                                         case 3:\r
2162                                                                 msg += tvd.accurateDate+"\t";\r
2163                                                                 break;\r
2164                                                         case 4:\r
2165                                                                 msg += tvd.start+"\t";\r
2166                                                                 break;\r
2167                                                         case 5:\r
2168                                                                 if (preId == 4) {\r
2169                                                                         msg = msg.substring(0,msg.length()-1)+"-";\r
2170                                                                 }\r
2171                                                                 msg += tvd.end+"\t";\r
2172                                                                 break;\r
2173                                                         case 6:\r
2174                                                                 msg += tvd.genre+"\t";\r
2175                                                                 break;\r
2176                                                         case 7:\r
2177                                                                 msg += tvd.detail+"\0"+tvd.getAddedDetail()+"\t";\r
2178                                                                 break;\r
2179                                                         }\r
2180                                                 }\r
2181                                                 preId = cb.getId();\r
2182                                         }\r
2183                                         if (msg.length() > 0) {\r
2184                                                 msg = msg.substring(0,msg.length()-1);\r
2185                                         }\r
2186                                         Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();\r
2187                                         StringSelection s = new StringSelection(msg);\r
2188                                         cb.setContents(s, null);\r
2189                                 }\r
2190                         });\r
2191                         pop.add(menuItem);\r
2192                 }\r
2193 \r
2194                 pop.addSeparator();\r
2195                 \r
2196                 // 延長感染源へ追加する\r
2197                 if (\r
2198                                 tvd.type == ProgType.SYOBO ||\r
2199                                 tvd.type == ProgType.PASSED ||\r
2200                                 tvd.type == ProgType.PICKED ||\r
2201                                 (tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO) )       // ラジオは処理対象外です\r
2202                 {\r
2203                         JMenuItem menuItem = new JMenuItem("延長感染源にしない【"+tvd.title+" ("+tvd.center+")】");\r
2204                         menuItem.addActionListener(new ActionListener() {\r
2205                                 public void actionPerformed(ActionEvent e) {\r
2206                                         //\r
2207                                         mwin.appendMessage("延長感染源を隔離します【"+tvd.title+"("+tvd.center+")】");\r
2208                                         //\r
2209                                         AbsExtensionDialog eD = new VWExtensionDialog();\r
2210                                         CommonSwingUtils.setLocationCenter(mainWindow,eD);\r
2211                                         \r
2212                                         eD.open(tvd.title,tvd.center,false,extKeys);\r
2213                                         eD.setVisible(true);\r
2214                                         \r
2215                                         if (eD.isRegistered()) {\r
2216                                                 // 番組表の状態を更新する\r
2217                                                 for (TVProgram tvp : tvprograms) {\r
2218                                                         if (tvp.getType() == ProgType.PROG) {\r
2219                                                                 tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
2220                                                         }\r
2221                                                 }\r
2222                                                 \r
2223                                                 // ツリーに反映する\r
2224                                                 listed.redrawTreeByExtension();\r
2225                                                 \r
2226                                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
2227                                         }\r
2228                                 }\r
2229                         });\r
2230                         pop.add(menuItem);\r
2231                 }\r
2232                 if ( tvd.type == ProgType.PASSED || (tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO) )   // ラジオは処理対象外です\r
2233                 {\r
2234                         JMenuItem menuItem = new JMenuItem("延長感染源にする【"+tvd.title+" ("+tvd.center+")】");\r
2235                         menuItem.addActionListener(new ActionListener() {\r
2236                                 public void actionPerformed(ActionEvent e) {\r
2237                                         //\r
2238                                         AbsExtensionDialog eD = new VWExtensionDialog();\r
2239                                         CommonSwingUtils.setLocationCenter(mainWindow,eD);\r
2240                                         \r
2241                                         eD.open(tvd.title,tvd.center,true,extKeys);\r
2242                                         eD.setVisible(true);\r
2243                                         \r
2244                                         if (eD.isRegistered()) {\r
2245                                                 // 番組表の状態を更新する\r
2246                                                 for (TVProgram tvp : tvprograms) {\r
2247                                                         if (tvp.getType() == ProgType.PROG) {\r
2248                                                                 tvp.setExtension(null, null, false, extKeys.getSearchKeys());\r
2249                                                         }\r
2250                                                 }\r
2251                                                 \r
2252                                                 // ツリーに反映する\r
2253                                                 listed.redrawTreeByExtension();\r
2254                                                 \r
2255                                                 mainWindow.setSelectedTab(MWinTab.LISTED);\r
2256                                         }\r
2257                                 }\r
2258                         });\r
2259                         pop.add(menuItem);\r
2260                 }\r
2261                 \r
2262                 pop.addSeparator();\r
2263                 \r
2264                 // 視聴する\r
2265                 if ( tvd.type == ProgType.PROG && tvd.subtype != ProgSubtype.RADIO)     // ラジオは処理対象外です\r
2266                 {\r
2267                         for (HDDRecorder recorder : recorders ) {\r
2268                                 \r
2269                                 if (recorder.ChangeChannel(null) == false) {\r
2270                                         continue;\r
2271                                 }\r
2272                                 \r
2273                                 final String recorderName = recorder.Myself();\r
2274                                 JMenuItem menuItem = new JMenuItem("【"+recorderName+"】で【"+tvd.center+"】を視聴する");\r
2275                                 \r
2276                                 menuItem.addActionListener(new ActionListener() {\r
2277                                         public void actionPerformed(ActionEvent e) {\r
2278                                                 for (HDDRecorder recorder : recorders ) {\r
2279                                                         if (recorder.isMyself(recorderName)) {\r
2280                                                                 if (recorder.ChangeChannel(tvd.center) == false) {\r
2281                                                                         ringBeep();\r
2282                                                                         mwin.appendError("【警告】チャンネルを変更できませんでした:"+recorder.getErrmsg());\r
2283                                                                 }\r
2284                                                                 else if (recorder.getErrmsg() !=null && recorder.getErrmsg().length() > 0) {\r
2285                                                                         mwin.appendError("[追加情報] "+recorder.getErrmsg());\r
2286                                                                 }\r
2287                                                         }\r
2288                                                 }\r
2289                                         }\r
2290                                 });\r
2291                                 \r
2292                                 menuItem.setEnabled(recorder.getUseChChange());\r
2293                                 \r
2294                                 pop.add(menuItem);\r
2295                         }\r
2296                 }\r
2297                 \r
2298                 pop.show(comp, x, y);\r
2299         }\r
2300         \r
2301         // ピックアップへ追加する\r
2302         public boolean addToPickup(final ProgDetailList tvd) {\r
2303                 \r
2304                 if (tvd.start.equals("")) {\r
2305                         // 番組情報がありません\r
2306                         return false;\r
2307                 }\r
2308                 \r
2309                 PickedProgram tvp = tvprograms.getPickup();\r
2310                 if ( tvp == null ) {\r
2311                         // ピックアップ先がありません\r
2312                         return true;\r
2313                 }\r
2314                 \r
2315                 // 削除かな?\r
2316                 if ( tvp.remove(tvd, tvd.center, tvd.accurateDate, true) ) {\r
2317                         tvp.save();\r
2318                         if ( listed.isNodeSelected(JTreeLabel.Nodes.PICKUP) || listed.isNodeSelected(JTreeLabel.Nodes.STANDBY) ) {\r
2319                                 // ピックアップノードor予約待機ノードが選択されていたらリストを更新する\r
2320                                 listed.reselectTree();\r
2321                                 //listed.updateReserveMark();\r
2322                         }\r
2323                         else {\r
2324                                 // 予約マークだけ変えておけばいいよね\r
2325                                 listed.updateReserveMark();\r
2326                                 listed.refocus();\r
2327                         }\r
2328                         paper.updateReserveBorder(tvd.center);\r
2329                         mwin.appendMessage("【ピックアップ】削除しました: "+tvd.title+" ("+tvd.center+")");\r
2330                         return false;\r
2331                 }\r
2332                 \r
2333                 // 追加です\r
2334                 if ( tvd.endDateTime.compareTo(CommonUtils.getDateTime(0)) > 0 ) {\r
2335                         tvp.refresh();\r
2336                         tvp.add(tvd);\r
2337                         tvp.save();\r
2338                         if ( listed.isNodeSelected(JTreeLabel.Nodes.PICKUP) ) {\r
2339                                 // ピックアップノードが選択されていたらリストを更新する\r
2340                                 listed.reselectTree();\r
2341                                 //listed.updateReserveMark();\r
2342                         }\r
2343                         else {\r
2344                                 listed.updateReserveMark();\r
2345                                 listed.refocus();\r
2346                         }\r
2347                         paper.updateReserveBorder(tvd.center);\r
2348                         mwin.appendMessage("【ピックアップ】追加しました: "+tvd.title+" ("+tvd.center+")");\r
2349                         return true;\r
2350                 }\r
2351 \r
2352                 // 過去ログは登録できないよ\r
2353                 mwin.appendMessage("【ピックアップ】過去情報はピックアップできません.");\r
2354                 return false;\r
2355         }\r
2356         \r
2357         /**\r
2358          *  予約を削除するメニューアイテム\r
2359          */\r
2360         private JMenuItem getRemoveRsvMenuItem(final String title, final String chnam, final String rsvId, final String recId, int n) {\r
2361                 //\r
2362                 JMenuItem menuItem = new JMenuItem(((n==0)?"予約を削除する【":"隣接予約を削除する【")+title+"("+chnam+")/"+recId+"】");\r
2363                 menuItem.setForeground(Color.RED);\r
2364                 if ( recId.equals(toolBar.getSelectedRecorder()) ) {\r
2365                         // 選択中のレコーダのものは太字に\r
2366                         Font f = menuItem.getFont();\r
2367                         menuItem.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
2368                 }\r
2369                 \r
2370                 menuItem.addActionListener(new ActionListener() {\r
2371                         public void actionPerformed(ActionEvent e) {\r
2372                                 \r
2373                                 if (env.getShowWarnDialog()) {\r
2374                                         Container cp = getContentPane();\r
2375                                         int ret = JOptionPane.showConfirmDialog(cp, "削除しますか?【"+title+"("+chnam+")】("+recId+")", "確認", JOptionPane.YES_NO_OPTION);\r
2376                                         if (ret != JOptionPane.YES_OPTION) {\r
2377                                                 return;\r
2378                                         }\r
2379                                 }\r
2380                                 \r
2381                                 stwin.clear();\r
2382                                 \r
2383                                 // 削除本体\r
2384                                 new SwingBackgroundWorker(false) {\r
2385                                         \r
2386                                         @Override\r
2387                                         protected Object doWorks() throws Exception {\r
2388                                                 \r
2389                                                 for (HDDRecorder recorder : recorders) {\r
2390                                                         if (recorder.isMyself(recId)) { // IPAddr:PortNo:RecorderIdで比較\r
2391                                                                 \r
2392                                                                 String title = "";\r
2393                                                                 for (ReserveList r : recorder.getReserves()) {\r
2394                                                                         if (r.getId().equals(rsvId)) {\r
2395                                                                                 title = r.getTitle();\r
2396                                                                                 break;\r
2397                                                                         }\r
2398                                                                 }\r
2399                                                                 \r
2400                                                                 stwin.appendMessage("予約を削除します:"+title+"("+rsvId+")");\r
2401                                                                 //recorder.setProgressArea(stwin);\r
2402                                                                 ReserveList r = recorder.RemoveRdEntry(rsvId);  // Noで検索\r
2403                                                                 if (r != null) {\r
2404                                                                         mwin.appendMessage("正常に削除できました:"+r.getTitle()+"("+r.getCh_name()+")");\r
2405                                                                         \r
2406                                                                         if ( ! r.getTitle().equals(title) || ! r.getId().equals(rsvId)) {\r
2407                                                                                 mwin.appendError("【警告】削除結果が一致しません!:"+title+"/"+r.getTitle());\r
2408                                                                         }\r
2409                                                                         \r
2410                                                                         if ( recorder.getUseCalendar()) {\r
2411                                                                                 // カレンダーから削除する\r
2412                                                                                 for ( HDDRecorder calendar : recorders ) {\r
2413                                                                                         if (calendar.getType() == RecType.CALENDAR) {\r
2414                                                                                                 stwin.appendMessage("カレンダーから予約情報を削除します");\r
2415                                                                                                 //calendar.setProgressArea(stwin);\r
2416                                                                                                 if ( ! calendar.UpdateRdEntry(r, null)) {\r
2417                                                                                                         mwin.appendError("【カレンダー】"+calendar.getErrmsg());\r
2418                                                                                                         ringBeep();\r
2419                                                                                                 }\r
2420                                                                                         }\r
2421                                                                                 }\r
2422                                                                         }\r
2423                                                                         \r
2424                                                                         r = null;\r
2425                                                                 }\r
2426                                                                 else {\r
2427                                                                         mwin.appendError("削除に失敗しました:"+title);\r
2428                                                                 }\r
2429                                                                 \r
2430                                                                 //\r
2431                                                                 if ( ! recorder.getErrmsg().equals("")) {\r
2432                                                                         mwin.appendError("【追加情報】"+recorder.getErrmsg());\r
2433                                                                         ringBeep();\r
2434                                                                 }\r
2435                                                                 break;\r
2436                                                         }\r
2437                                                 }\r
2438                                                 return null;\r
2439                                         }\r
2440                                         \r
2441                                         @Override\r
2442                                         protected void doFinally() {\r
2443                                                 stwin.setVisible(false);\r
2444                                         }\r
2445                                 }.execute();\r
2446                                 \r
2447                                 CommonSwingUtils.setLocationCenter(Viewer.this, stwin);\r
2448                                 stwin.setVisible(true);\r
2449                                 \r
2450                                 // 予約状況を更新\r
2451                                 listed.updateReserveMark();\r
2452                                 paper.updateReserveBorder(chnam);\r
2453                                 reserved.redrawReservedList();\r
2454                         }\r
2455                 });\r
2456                 \r
2457                 return menuItem;\r
2458         }\r
2459         \r
2460         \r
2461         \r
2462         \r
2463         /*\r
2464          * 他のクラスに分離できなかったというか、しなかったというか、そんなメソッド群\r
2465          */\r
2466         \r
2467         /**\r
2468          * \r
2469          */\r
2470         private boolean doExecOnOff(final boolean fexec, final String title, final String chnam, final String rsvId, final String recId) {\r
2471                 \r
2472                 CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
2473                 \r
2474                 String mode = (fexec ? "ON" : "OFF");\r
2475                 \r
2476                 if ( rdialog.open(recId,rsvId) ) {\r
2477                         rdialog.setOnlyUpdateExec(fexec);\r
2478                         rdialog.doUpdate();\r
2479                         \r
2480                         if (rdialog.isReserved()) {\r
2481                                 // 予約状況を更新\r
2482                                 listed.updateReserveMark();\r
2483                                 paper.updateReserveBorder(chnam);\r
2484                                 reserved.redrawReservedList();\r
2485                                 \r
2486                                 {\r
2487                                         String msg = "予約を"+mode+"にしました【"+title+"("+chnam+")/"+recId+"】";\r
2488                                         //StdAppendMessage(msg);\r
2489                                         mwin.appendMessage(msg);\r
2490                                 }\r
2491                                 \r
2492                                 return true;\r
2493                         }\r
2494                 }\r
2495                 \r
2496                 return false;\r
2497         }\r
2498         \r
2499         /**\r
2500          *  予約実行をONOFFするメニューアイテム\r
2501          */\r
2502         private JMenuItem getExecOnOffMenuItem(final boolean fexec, final String title, final String chnam, final String rsvId, final String recId, int n) {\r
2503                 \r
2504                 JMenuItem menuItem = new JMenuItem();\r
2505                 \r
2506                 String mode;\r
2507                 if ( ! fexec ) {\r
2508                         mode = "ON";\r
2509                         menuItem.setForeground(Color.BLUE);\r
2510                 }\r
2511                 else {\r
2512                         mode = "OFF";\r
2513                 }\r
2514                 \r
2515                 menuItem.setText(((n==0)?"予約を":"隣接予約を")+mode+"にする【"+title+"("+chnam+")/"+recId+")】");\r
2516                 \r
2517                 if ( recId.equals(toolBar.getSelectedRecorder()) ) {\r
2518                         // 選択中のレコーダのものは太字に\r
2519                         Font f = menuItem.getFont();\r
2520                         menuItem.setFont(f.deriveFont(f.getStyle()|Font.BOLD));\r
2521                 }\r
2522 \r
2523                 final String xmode = mode;\r
2524                 menuItem.addActionListener(new ActionListener() {\r
2525                         public void actionPerformed(ActionEvent e) {\r
2526                                 \r
2527                                 //VWReserveDialog rD = new VWReserveDialog(0, 0, env, tvprograms, recorders, avs, chavs, stwin);\r
2528                                 //rdialog.clear();\r
2529                                 CommonSwingUtils.setLocationCenter(mainWindow,rdialog);\r
2530                                 \r
2531                                 if ( rdialog.open(recId,rsvId) ) {\r
2532                                         rdialog.setOnlyUpdateExec( ! fexec);\r
2533                                         rdialog.doUpdate();\r
2534                                         \r
2535                                         if (rdialog.isReserved()) {\r
2536                                                 // 予約状況を更新\r
2537                                                 listed.updateReserveMark();\r
2538                                                 paper.updateReserveBorder(chnam);\r
2539                                                 reserved.redrawReservedList();\r
2540                                                 \r
2541                                                 {\r
2542                                                         String msg = "予約を"+xmode+"にしました【"+title+"("+chnam+")/"+recId+"】";\r
2543                                                         StdAppendMessage(msg);\r
2544                                                         mwin.appendMessage(msg);\r
2545                                                 }\r
2546                                         }\r
2547                                 }\r
2548                                 else {\r
2549                                         //rdialog.setVisible(false);\r
2550                                 }\r
2551                         }\r
2552                 });\r
2553                 \r
2554                 return menuItem;\r
2555         }\r
2556         \r
2557         /**\r
2558          *  新聞形式へジャンプするメニューアイテム\r
2559          */\r
2560         private JMenuItem getJumpMenuItem(final String title, final String chnam, final String startDT) {\r
2561                 JMenuItem menuItem = new JMenuItem("番組欄へジャンプする【"+title+" ("+chnam+")】");\r
2562                 menuItem.addActionListener(new ActionListener() {\r
2563                         public void actionPerformed(ActionEvent e) {\r
2564                                 paper.jumpToBangumi(chnam,startDT);\r
2565                         }\r
2566                 });\r
2567                 return menuItem;\r
2568         }\r
2569         private JMenuItem getJumpToLastWeekMenuItem(final String title, final String chnam, final String startDT) {\r
2570                 GregorianCalendar cal = CommonUtils.getCalendar(startDT);\r
2571                 if ( cal != null ) {\r
2572                         JMenuItem menuItem = new JMenuItem("先週の番組欄へジャンプする【"+title+" ("+chnam+")】");\r
2573                         cal.add(Calendar.DATE, -7);\r
2574                         final String lastweek = CommonUtils.getDateTime(cal);\r
2575                         menuItem.addActionListener(new ActionListener() {\r
2576                                 public void actionPerformed(ActionEvent e) {\r
2577                                         paper.jumpToBangumi(chnam,lastweek);\r
2578                                 }\r
2579                         });\r
2580                         return menuItem;\r
2581                 }\r
2582                 return null;\r
2583         }\r
2584         \r
2585         // カーソル位置にかかる予約枠の検索\r
2586         private void searchOverlapRsv(LikeReserveList overlapRsvList, ProgDetailList tvd, int h)\r
2587         {\r
2588                 String clicked = "";\r
2589                 if ( h >= 0 && tvd.start.length() != 0 ) {\r
2590                         // 新聞形式ならクリック位置の日時を算出する\r
2591                         GregorianCalendar cala = CommonUtils.getCalendar(tvd.startDateTime);\r
2592                         if ( CommonUtils.isLateNight(cala.get(Calendar.HOUR_OF_DAY)) ) {\r
2593                                 cala.set(Calendar.HOUR_OF_DAY, TIMEBAR_START);\r
2594                                 cala.set(Calendar.MINUTE, 0);\r
2595                         }\r
2596                         cala.add(Calendar.MINUTE, Math.round(h/bounds.getPaperHeightMultiplier()));\r
2597                         clicked = CommonUtils.getDateTime(cala);\r
2598                         //StdAppendError("clicked:"+clicked);\r
2599                 }\r
2600                 \r
2601                 HashMap<String,Boolean> misCN = new HashMap<String, Boolean>();\r
2602                 for ( HDDRecorder recorder : recorders ) {\r
2603                         \r
2604                         // 終了した予約を整理する\r
2605                         recorder.refreshReserves();\r
2606                         \r
2607                         for ( ReserveList r : recorder.getReserves() ) {\r
2608                                 \r
2609                                 // 放送局のマッチング\r
2610                                 if (r.getCh_name() == null) {\r
2611                                         if ( r.getChannel() == null ) {\r
2612                                                 System.err.println(ERRID+"予約情報にCHコードが設定されていません。バグの可能性があります。 recid="+recorder.Myself()+" chname="+r.getCh_name());\r
2613                                                 continue;\r
2614                                         }\r
2615                                         if(r.getChannel().length() > 0) {\r
2616                                                 misCN.put(r.getChannel(),true);\r
2617                                         }\r
2618                                         continue;\r
2619                                 }\r
2620                                 if ( ! r.getCh_name().equals(tvd.center)) {\r
2621                                         continue;\r
2622                                 }\r
2623                                 \r
2624                                 // 重複時間チェック\r
2625                                 boolean inRange = false;\r
2626                                 {\r
2627                                         ArrayList<String> starts = new ArrayList<String>();\r
2628                                         ArrayList<String> ends = new ArrayList<String>();\r
2629                                         CommonUtils.getStartEndList(starts, ends, r);\r
2630                                         if ( h >= 0 ) {\r
2631                                                 // 新聞形式はピンポイント(マウスポインタのある位置の時刻)\r
2632                                                 for (int j=0; j<starts.size(); j++) {\r
2633                                                         if ( clicked.compareTo(starts.get(j)) >= 0 && clicked.compareTo(ends.get(j)) <= 0 ) {\r
2634                                                                 inRange = true;\r
2635                                                                 break;\r
2636                                                         }\r
2637                                                 }\r
2638                                         }\r
2639                                         else {\r
2640                                                 // リスト形式は幅がある(開始~終了までの間のいずれかの時刻)\r
2641                                                 for (int j=0; j<starts.size(); j++) {\r
2642                                                         if ( CommonUtils.isOverlap(tvd.startDateTime, tvd.endDateTime, starts.get(j), ends.get(j), false) ) {\r
2643                                                                 inRange = true;\r
2644                                                                 break;\r
2645                                                         }\r
2646                                                 }\r
2647                                         }\r
2648                                 }\r
2649                                 if ( ! inRange) {\r
2650                                         continue;\r
2651                                 }\r
2652                                 \r
2653                                 // 類似予約あり!\r
2654                                 overlapRsvList.add(new LikeReserveItem(recorder, r));\r
2655                         }\r
2656                 }\r
2657                 \r
2658                 return;\r
2659         }\r
2660         \r
2661         \r
2662         /*******************************************************************************\r
2663          * タイマー関連\r
2664          ******************************************************************************/\r
2665         \r
2666                 \r
2667         /*******************************************************************************\r
2668          * ここからおおむね初期化処理にかかわるメソッド群\r
2669          ******************************************************************************/\r
2670         \r
2671         // レコーダから取得したエンコーダ情報で、登録済みレコーダ一覧を更新する\r
2672         private void setEncoderInfo2RecorderList(HDDRecorder recorder) {\r
2673                 for (RecorderInfo ri : recInfoList ) {\r
2674                         //if (rl.getRecorderEncoderList().size() == 0)\r
2675                         {\r
2676                                 String mySelf = ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+":"+ri.getRecorderId();\r
2677                                 String myMail = "MAIL"+":"+ri.getRecorderMacAddr()+":"+ri.getRecorderId();\r
2678                                 if (recorder.isMyself(mySelf) || recorder.isMyself(myMail)) {\r
2679                                         ri.clearEncoders();\r
2680                                         for (TextValueSet enc : recorder.getEncoderList()) {\r
2681                                                 ri.addEncoder(enc.getText());\r
2682                                         }\r
2683                                         break;\r
2684                                 }\r
2685                         }\r
2686                 }\r
2687         }\r
2688         \r
2689         /**\r
2690          *  レコーダの予約情報をDLする\r
2691          */\r
2692         private boolean reLoadRdReserve(final String recId) {\r
2693                 //\r
2694                 StWinClear();\r
2695                 \r
2696                 new SwingBackgroundWorker(false) {\r
2697                         \r
2698                         @Override\r
2699                         protected Object doWorks() throws Exception {\r
2700                                 \r
2701                                 TatCount tc = new TatCount();\r
2702                                 \r
2703                                 loadRdReserve(true, recId);\r
2704                                 \r
2705                                 // エンコーダ情報が更新されるかもしれないので、一覧のエンコーダ表示にも反映する\r
2706                                 recsetting.redrawRecorderEncoderEntry();\r
2707                                 \r
2708                                 // 各タブに反映する\r
2709                                 paper.updateReserveBorder(null);\r
2710                                 listed.updateReserveMark();\r
2711                                 reserved.redrawReservedList();\r
2712                                 recorded.redrawRecordedList();\r
2713                                 \r
2714                                 mwin.appendMessage(String.format("【予約一覧の取得処理が完了しました】 所要時間: %.2f秒",tc.end()));\r
2715                                 return null;\r
2716                         }\r
2717                         \r
2718                         @Override\r
2719                         protected void doFinally() {\r
2720                                 StWinSetVisible(false);\r
2721                         }\r
2722                 }.execute();\r
2723                 \r
2724                 StWinSetLocationCenter(this);\r
2725                 StWinSetVisible(true);\r
2726                 \r
2727                 return true;\r
2728         }\r
2729         private void loadRdReserve(final boolean force, final String myself) {\r
2730 \r
2731                 //\r
2732                 new SwingBackgroundWorker(true) {\r
2733                         \r
2734                         @Override\r
2735                         protected Object doWorks() throws Exception {\r
2736                                 \r
2737                                 HDDRecorderList recs;\r
2738                                 if ( myself != null ) {\r
2739                                         recs = recorders.findInstance(myself);\r
2740                                 }\r
2741                                 else {\r
2742                                         recs = recorders;\r
2743                                 }\r
2744                                 for ( HDDRecorder recorder : recs ) {\r
2745                                         switch ( recorder.getType() ) {\r
2746                                         case RECORDER:\r
2747                                         case EPG:\r
2748                                         case MAIL:\r
2749                                         case NULL:\r
2750                                         case TUNER:\r
2751                                                 loadRdReserveOnce(recorder, force);\r
2752                                                 break;\r
2753                                         default:\r
2754                                                 break;\r
2755                                         }\r
2756                                 }\r
2757                                 \r
2758                                 return null;\r
2759                         }\r
2760                         \r
2761                         @Override\r
2762                         protected void doFinally() {\r
2763                         }\r
2764                 }.execute();\r
2765         }\r
2766         \r
2767         private boolean loadRdReserveOnce(HDDRecorder recorder, boolean force) {\r
2768                 \r
2769                 mwin.appendMessage("【レコーダ情報取得】レコーダから情報を取得します: "+recorder.getRecorderId()+"("+recorder.getIPAddr()+":"+recorder.getPortNo()+")");\r
2770                 if ( recorder.isThereAdditionalDetails() && ! env.getForceGetRdReserveDetails() ) {\r
2771                         mwin.appendMessage("<<<注意!>>>このレコーダでは予約詳細の個別取得を実行しないと正確な情報を得られない場合があります。");\r
2772                 }\r
2773                 \r
2774                 try {\r
2775                         \r
2776                         // 各種設定の取得\r
2777                         if ( ! recorder.GetRdSettings(force) ) {\r
2778                                 // 取得に失敗\r
2779                                 mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2780                                 ringBeep();\r
2781                                 return false;\r
2782                         }\r
2783                         \r
2784                         // 予約一覧の取得\r
2785                         if ( ! recorder.GetRdReserve(force) ) {\r
2786                                 // 取得に失敗\r
2787                                 mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2788                                 ringBeep();\r
2789                                 return false;\r
2790                         }\r
2791                         \r
2792                         // レコーダから取得したエンコーダ情報で、登録済みレコーダ一覧を更新する\r
2793                         setEncoderInfo2RecorderList(recorder);\r
2794                         if ( force ) {\r
2795                                 recInfoList.save();\r
2796                         }\r
2797                         \r
2798                         // 予約詳細の取得\r
2799                         if ( env.getNeverGetRdReserveDetails() ) {\r
2800                                 mwin.appendMessage("【!】予約詳細情報の取得はスキップされました");\r
2801                         }\r
2802                         else if ( force && recorder.isThereAdditionalDetails() ) {\r
2803                                 boolean getDetails = true;\r
2804                                 if ( ! env.getForceGetRdReserveDetails() ) {\r
2805                                         int ret = JOptOptionPane.showConfirmDialog(null, "詳細情報を取得しますか?(時間がかかります)", "今回の選択を既定の動作とする", "確認", JOptionPane.YES_NO_OPTION);\r
2806                                         getDetails = (ret == JOptOptionPane.YES_OPTION);\r
2807                                         if ( JOptOptionPane.isSelected() ) {\r
2808                                                 // 今回の選択を既定の動作とする\r
2809                                                 env.setForceGetRdReserveDetails(getDetails);\r
2810                                                 env.setNeverGetRdReserveDetails( ! getDetails);\r
2811                                                 env.save();\r
2812                                                 setting.updateSelections();\r
2813                                         }\r
2814                                 }\r
2815                                 if ( ! getDetails ) {\r
2816                                         mwin.appendMessage("【!】予約詳細情報の取得はスキップされました");\r
2817                                 }\r
2818                                 else {\r
2819                                         if ( ! recorder.GetRdReserveDetails()) {\r
2820                                                 // 取得に失敗\r
2821                                                 mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2822                                                 ringBeep();\r
2823                                         }\r
2824                                 }\r
2825                         }\r
2826                         \r
2827                         // レコーダの放送局名をWeb番組表の放送局名に置き換え\r
2828                         {       \r
2829                                 HashMap<String,String> misCN = new HashMap<String,String>();\r
2830                                 for ( ReserveList r : recorder.getReserves() ) {\r
2831                                         if ( r.getCh_name() == null ) {\r
2832                                                 misCN.put(r.getChannel(),recorder.getRecorderId());\r
2833                                         }\r
2834                                 }\r
2835                                 if ( misCN.size() > 0 ) {\r
2836                                         for ( String cn : misCN.keySet() ) {\r
2837                                                 String msg = "【警告(予約一覧)】 <"+misCN.get(cn)+"> \"レコーダの放送局名\"を\"Web番組表の放送局名\"に変換できません。CHコード設定に設定を追加してください:\"レコーダの放送局名\"="+cn;\r
2838                                                 mwin.appendMessage(msg);\r
2839                                         }\r
2840                                         ringBeep();\r
2841                                 }\r
2842                         }\r
2843                         \r
2844                         // 自動予約一覧の取得\r
2845                         if ( recorder.isEditAutoReserveSupported() ) {\r
2846                                 if ( ! recorder.GetRdAutoReserve(force) ) {\r
2847                                         // 取得に失敗\r
2848                                         mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2849                                         ringBeep();\r
2850                                         return false;\r
2851                                 }\r
2852                         }\r
2853                         \r
2854                         // 録画結果一覧の取得\r
2855                         if ( env.getSkipGetRdRecorded() ) {\r
2856                                 mwin.appendMessage("【!】録画結果一覧の取得はスキップされました");\r
2857                         }\r
2858                         else {\r
2859                                 if ( ! recorder.GetRdRecorded(force) ) {\r
2860                                         // 取得に失敗\r
2861                                         mwin.appendError(recorder.getErrmsg()+" "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2862                                         ringBeep();\r
2863                                         return false;\r
2864                                 }\r
2865                         }\r
2866                         \r
2867                 }\r
2868                 catch (Exception e) {\r
2869                         e.printStackTrace();\r
2870                         mwin.appendError("【致命的エラー】予約一覧の取得で例外が発生 "+recorder.getIPAddr()+":"+recorder.getPortNo()+":"+recorder.getRecorderId());\r
2871                         ringBeep();\r
2872                         return false;\r
2873                 }\r
2874                 return true;\r
2875         }\r
2876 \r
2877         /**\r
2878          * 録画結果一覧をDLする\r
2879          */\r
2880         private boolean reLoadRdRecorded(final String myself) {\r
2881                 //\r
2882                 StWinClear();\r
2883                 \r
2884                 new SwingBackgroundWorker(false) {\r
2885                         \r
2886                         @Override\r
2887                         protected Object doWorks() throws Exception {\r
2888                                 \r
2889                                 TatCount tc = new TatCount();\r
2890                         \r
2891                                 boolean succeeded = true;\r
2892                                 \r
2893                                 HDDRecorderList recs;\r
2894                                 if ( myself != null ) {\r
2895                                         recs = recorders.findInstance(myself);\r
2896                                 }\r
2897                                 else {\r
2898                                         recs = recorders;\r
2899                                 }\r
2900                                 for ( HDDRecorder recorder : recs ) {\r
2901                                         switch ( recorder.getType() ) {\r
2902                                         case RECORDER:\r
2903                                         case EPG:\r
2904                                         case MAIL:\r
2905                                         case NULL:\r
2906                                         case TUNER:\r
2907                                                 if ( ! recorder.GetRdSettings(true) ) {\r
2908                                                         succeeded = false;\r
2909                                                 }\r
2910                                                 if ( ! recorder.GetRdRecorded(true) ) {\r
2911                                                         succeeded = false;\r
2912                                                 }\r
2913                                                 break;\r
2914                                         default:\r
2915                                                 break;\r
2916                                         }\r
2917                                 }\r
2918                                 \r
2919                                 if ( succeeded ) {\r
2920                                         reserved.redrawReservedList();\r
2921                                         recorded.redrawRecordedList();\r
2922                                         \r
2923                                         mwin.appendMessage(String.format("【録画結果一覧の取得処理が完了しました】 所要時間: %.2f秒",tc.end()));\r
2924                                 }\r
2925                                 else {\r
2926                                         ringBeep();\r
2927                                         mwin.appendMessage(String.format("【録画結果一覧の取得処理に失敗しました】 所要時間: %.2f秒",tc.end()));\r
2928                                 }\r
2929                                 return null;\r
2930                         }\r
2931                         \r
2932                         @Override\r
2933                         protected void doFinally() {\r
2934                                 StWinSetVisible(false);\r
2935                         }\r
2936                 }.execute();\r
2937                 \r
2938                 StWinSetLocationCenter(this);\r
2939                 StWinSetVisible(true);\r
2940                 \r
2941                 return true;\r
2942         }\r
2943 \r
2944         /**\r
2945          * Web番組表をDLする\r
2946          * <P>単体実行の場合はこちらを呼び出す\r
2947          * <P>部品実行の場合はこちらを呼び出す:{@link #loadTVProgram(boolean, LoadFor)}\r
2948          */\r
2949         private boolean reLoadTVProgram(final LoadFor lf) {\r
2950                 //\r
2951                 StWinClear();\r
2952                 \r
2953                 new SwingBackgroundWorker(false) {\r
2954                         \r
2955                         @Override\r
2956                         protected Object doWorks() throws Exception {\r
2957                                 \r
2958                                 TatCount tc = new TatCount();\r
2959                                 \r
2960                                 loadTVProgram(true, lf);\r
2961                                 \r
2962                                 // 新聞描画枠のリセット\r
2963                                 paper.clearPanel();\r
2964                                 paper.buildMainViewByDate();\r
2965                                 \r
2966                                 // サイドツリーの再構築\r
2967                                 paper.redrawTreeByPassed();\r
2968                                 \r
2969                                 // 再描画\r
2970                                 paper.reselectTree();\r
2971                                 listed.reselectTree();\r
2972                                 \r
2973                                 mwin.appendMessage(String.format("[Web番組表取得] 【完了しました】 所要時間: %.2f秒",tc.end()));\r
2974                                 return null;\r
2975                         }\r
2976                         \r
2977                         @Override\r
2978                         protected void doFinally() {\r
2979                                 StWinSetVisible(false);\r
2980                         }\r
2981                 }.execute();\r
2982                 \r
2983                 StWinSetLocationCenter(this);\r
2984                 StWinSetVisible(true);\r
2985                 \r
2986                 return true;\r
2987         }\r
2988         \r
2989         /**\r
2990          * Web番組表をDLする\r
2991          * <P>単体実行の場合はこちらを呼び出す:{@link #reLoadTVProgram(LoadFor)}\r
2992          * <P>部品実行の場合はこちらを呼び出す\r
2993          */\r
2994         private void loadTVProgram(final boolean b, final LoadFor lf) {\r
2995                 \r
2996                 final String FUNCID = "[Web番組表取得] ";\r
2997                 final String ERRID = "[ERROR]"+FUNCID;\r
2998                 //\r
2999                 new SwingBackgroundWorker(true) {\r
3000                         \r
3001                         @Override\r
3002                         protected Object doWorks() throws Exception {\r
3003                                 try {\r
3004                                         String msg;\r
3005                                         TVProgram tvp;\r
3006                                         \r
3007                                         tvp = tvprograms.getTvProgPlugin(null);\r
3008                                         if ( tvp != null )\r
3009                                         {\r
3010                                                 String sType = "地上波&BS番組表";\r
3011                                                 if (lf == LoadFor.ALL || lf == LoadFor.TERRA) {\r
3012                                                         loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
3013                                                 }\r
3014                                                 else {\r
3015                                                         stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
3016                                                 }\r
3017                                         }\r
3018                                         \r
3019                                         tvp = tvprograms.getCsProgPlugin(null);\r
3020                                         if ( tvp != null )\r
3021                                         {\r
3022                                                 String sType = "CS番組表[プライマリ]";\r
3023                                                 if (lf == LoadFor.ALL || lf == LoadFor.CS || lf == LoadFor.CSo1) {\r
3024                                                         loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
3025                                                 }\r
3026                                                 else {\r
3027                                                         stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
3028                                                 }\r
3029                                         }\r
3030                                         \r
3031                                         tvp = tvprograms.getCs2ProgPlugin(null);\r
3032                                         if ( tvp != null )\r
3033                                         {\r
3034                                                 String sType = "CS番組表[セカンダリ]";\r
3035                                                 if (lf == LoadFor.ALL || lf == LoadFor.CS || lf == LoadFor.CSo2) {\r
3036                                                         loadTVProgramOnce(tvp, sType, tvp.getSelectedArea(), false, b);\r
3037                                                 }\r
3038                                                 else {\r
3039                                                         stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました: "+tvp.getTVProgramId());\r
3040                                                 }\r
3041                                         }\r
3042                                         \r
3043                                         tvp = tvprograms.getSyobo();\r
3044                                         if ( tvp != null ) {\r
3045                                                 String sType = "しょぼかる";\r
3046                                                 if ( (lf == LoadFor.ALL || lf == LoadFor.SYOBO) && enableWebAccess && env.getUseSyobocal()) {\r
3047                                                         tvp.loadCenter(tvp.getSelectedCode(), b);       // しょぼかるには放送局リストを取得するイベントが他にないので\r
3048                                                         loadTVProgramOnce(tvp, sType, null, true, b);\r
3049                                                 }\r
3050                                                 else {\r
3051                                                         stwin.appendMessage(FUNCID+sType+"へのアクセスはスキップされました.");\r
3052                                                 }\r
3053                                                 \r
3054                                                 // しょぼかるの新番組マークを引き継ぐ\r
3055                                                 attachSyoboNew();\r
3056                                         }\r
3057                                 \r
3058                                         PickedProgram pickup = tvprograms.getPickup();\r
3059                                         if ( tvp != null ) {\r
3060                                                 pickup.refresh();\r
3061                                                 //pickup.save();\r
3062                                         }\r
3063                                         \r
3064                                         // 番組タイトルを整形する\r
3065                                         fixTitle();\r
3066                                         fixDetail();\r
3067                                         \r
3068                                         // 検索結果の再構築\r
3069                                         stwin.appendMessage(FUNCID+"検索結果を生成します.");\r
3070                                         mpList.clear(env.getDisableFazzySearch(), env.getDisableFazzySearchReverse());\r
3071                                         mpList.build(tvprograms, trKeys.getTraceKeys(), srKeys.getSearchKeys());\r
3072                                         \r
3073                                         // 過去ローグ\r
3074                                         if ( env.getUsePassedProgram() ) {\r
3075                                                 TatCount tc = new TatCount();\r
3076                                                 stwin.appendMessage(FUNCID+"過去ログを生成します.");\r
3077                                                 if ( tvprograms.getPassed().save(tvprograms.getIterator(), chsort.getClst(), env.getPrepPassedProgramCount()) ) {\r
3078                                                         msg = String.format(FUNCID+"過去ログを生成しました [%.2f秒].",tc.end());\r
3079                                                         StdAppendMessage(msg);\r
3080                                                 }\r
3081                                                 //PassedProgramList.getDateList(env.getPassedLogLimit());\r
3082                                         }\r
3083                                         else {\r
3084                                                 stwin.appendMessage(FUNCID+"過去ログは記録されません.");\r
3085                                         }\r
3086                                 }\r
3087                                 catch (Exception e) {\r
3088                                         e.printStackTrace();\r
3089                                         mwin.appendError(ERRID+"番組情報の取得で例外が発生");\r
3090                                         ringBeep();\r
3091                                         return null;\r
3092                                 }\r
3093                                 return null;\r
3094                         }\r
3095                         \r
3096                         @Override\r
3097                         protected void doFinally() {\r
3098                         }\r
3099                 }.execute();\r
3100         }\r
3101         \r
3102         private void loadTVProgramOnce(TVProgram tvp, String sType, String aName, boolean loadonly, boolean force) {\r
3103                 \r
3104                 final String FUNCID = "[Web番組表取得] ";\r
3105                 final String ERRID = "[ERROR]"+FUNCID;\r
3106                 \r
3107                 // ログ\r
3108                 String msg = FUNCID+sType+"を取得します: "+tvp.getTVProgramId();\r
3109                 stwin.appendMessage(msg);\r
3110                 if (aName!=null) stwin.appendMessage(FUNCID+"+選択されているエリア="+aName);\r
3111                 \r
3112                 // 読み込み\r
3113                 //tvp.setProgressArea(stwin);\r
3114                 tvp.loadProgram(tvp.getSelectedCode(), force);\r
3115                 \r
3116                 if (loadonly) {\r
3117                         return;\r
3118                 }\r
3119                 \r
3120                 // 延長警告\r
3121                 tvp.setExtension(null, null, false, extKeys.getSearchKeys());   // 最初の3引数は盲腸。ダミー\r
3122                 // NGワード\r
3123                 tvp.abon(env.getNgword());\r
3124                 // 抜けチェック\r
3125                 String errmsg = tvp.chkComplete(); \r
3126                 if (errmsg != null) {\r
3127                         stwin.appendError(FUNCID+"取得した情報が不正です:"+errmsg);\r
3128                         if (mainWindow!=null) mwin.appendMessage(msg);\r
3129                         ringBeep();\r
3130                 }\r
3131         }\r
3132         \r
3133         // しょぼかるの番組詳細を番組表に反映する\r
3134         private void attachSyoboNew() {\r
3135                 TVProgram syobo = tvprograms.getSyobo();\r
3136                 if (syobo == null) {\r
3137                         return;\r
3138                 }\r
3139                 \r
3140                 for ( TVProgram tvp : tvprograms ) {\r
3141                         \r
3142                         if ( tvp.getType() != ProgType.PROG ) {\r
3143                                 continue;\r
3144                         }\r
3145                         if ( ! (tvp.getSubtype() == ProgSubtype.TERRA || tvp.getSubtype() == ProgSubtype.CS || tvp.getSubtype() == ProgSubtype.CS2) ) {\r
3146                                 continue;\r
3147                         }\r
3148                         \r
3149                         for ( ProgList tvpl : tvp.getCenters() ) {\r
3150                                 if ( ! tvpl.enabled) {\r
3151                                         continue;\r
3152                                 }\r
3153                                 for ( ProgList svpl : syobo.getCenters() ) {\r
3154                                         if ( ! tvpl.Center.equals(svpl.Center)) {\r
3155                                                 continue;\r
3156                                         }\r
3157                                         for ( ProgDateList tvc : tvpl.pdate ) {\r
3158                                                 \r
3159                                                 ProgDateList mSvc = null;\r
3160                                                 for ( ProgDateList svc : svpl.pdate ) {\r
3161                                                         if (tvc.Date.equals(svc.Date) ) {\r
3162                                                                 mSvc = svc;\r
3163                                                                 break;\r
3164                                                         }\r
3165                                                 }\r
3166                                                 if (mSvc == null) {\r
3167                                                         // しょぼかる側に該当する日付自体ないので全部フラグを立てっぱなしでいい\r
3168                                                         for ( ProgDetailList tvd : tvc.pdetail ) {\r
3169                                                                 if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
3170                                                                         tvd.addOption(ProgOption.NOSYOBO);\r
3171                                                                 }\r
3172                                                         }\r
3173                                                 }\r
3174                                                 else {\r
3175                                                         // しょぼかる側に該当する日付があるのでマッチング。アニメと映画と音楽\r
3176                                                         for ( ProgDetailList tvd : tvc.pdetail ) {\r
3177                                                                 \r
3178                                                                 // アニメはいったんフラグを立てる\r
3179                                                                 if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
3180                                                                         tvd.addOption(ProgOption.NOSYOBO);\r
3181                                                                 }\r
3182                                                                 \r
3183                                                                 boolean isFind = false;\r
3184                                                                 for ( ProgDetailList svd : mSvc.pdetail ) {\r
3185                                                                         if ( tvd.start.equals(svd.start) ) {\r
3186                                                                                 \r
3187                                                                                 // 番組ID\r
3188                                                                                 {\r
3189                                                                                         //svd.progid = tvd.progid;\r
3190                                                                                         svd.setContentIdStr();\r
3191                                                                                 }\r
3192                                                                                 \r
3193                                                                                 boolean isAnime = tvd.isEqualsGenre(ProgGenre.ANIME, null);\r
3194                                                                                 if ( ! isAnime && ! tvd.isEqualsGenre(ProgGenre.MOVIE, null) && ! tvd.isEqualsGenre(ProgGenre.MUSIC, null) ) {\r
3195                                                                                         break;\r
3196                                                                                 }\r
3197                                                                                 \r
3198                                                                                 // みつけた\r
3199                                                                                 isFind = true;\r
3200                                                                                 \r
3201                                                                                 // しょぼかるとWeb番組表の両方に存在する\r
3202                                                                                 svd.nosyobo = true;\r
3203                         \r
3204                                                                                 // 各種フラグ\r
3205                                                                                 {\r
3206                                                                                         boolean isAttached = false;\r
3207                                                                                         \r
3208                                                                                         // 新番組フラグ\r
3209                                                                                         if ( svd.flag == ProgFlags.NEW && tvd.flag != ProgFlags.NEW ) {\r
3210                                                                                                 tvd.flag = ProgFlags.NEW;\r
3211                                                                                                 isAttached = true;\r
3212                                                                                         }\r
3213                                                                                         \r
3214                                                                                         // 最終回フラグ\r
3215                                                                                         if ( svd.flag == ProgFlags.LAST && tvd.flag != ProgFlags.LAST ) {\r
3216                                                                                                 tvd.flag = ProgFlags.LAST;\r
3217                                                                                                 isAttached = true;\r
3218                                                                                         }\r
3219                                                                                         \r
3220                                                                                         // ジャンル\r
3221                                                                                         if ( tvd.isEqualsGenre(ProgGenre.MOVIE, null) && ! tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) ) {\r
3222                                                                                                 if ( tvd.genrelist == null ) {\r
3223                                                                                                         tvd.genrelist = new ArrayList<ProgGenre>();\r
3224                                                                                                         tvd.genrelist.add(tvd.genre);\r
3225                                                                                                         tvd.genrelist.add(ProgGenre.MOVIE);\r
3226                                                                                                         tvd.subgenrelist = new ArrayList<ProgSubgenre>();\r
3227                                                                                                         tvd.subgenrelist.add(tvd.subgenre);\r
3228                                                                                                         tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
3229                                                                                                 }\r
3230                                                                                                 else {\r
3231                                                                                                         tvd.genrelist.add(ProgGenre.MOVIE);\r
3232                                                                                                         tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
3233                                                                                                 }\r
3234                                                                                                 isAttached = true;\r
3235                                                                                         }\r
3236                                                                                         \r
3237                                                                                         // その他のフラグ\r
3238                                                                                         for ( ProgOption sopt : svd.getOption() ) {\r
3239                                                                                                 if ( tvd.addOption(sopt) && isAttached == false ) {\r
3240                                                                                                         isAttached = true;\r
3241                                                                                                 }\r
3242                                                                                         }\r
3243                                                                                         \r
3244                                                                                         // ログ\r
3245                                                                                         if (isAttached && env.getDebug()) {\r
3246                                                                                                 StdAppendMessage("しょぼかるのフラグを引き継ぎました: ("+tvpl.Center+") "+tvd.title);\r
3247                                                                                         }\r
3248                                                                                 }\r
3249                                                                                 \r
3250                                                                                 // 番組詳細\r
3251                                                                                 if ( tvd.detail.length() < svd.detail.length() ) {\r
3252                                                                                         tvd.detail = svd.detail;\r
3253                                                                                 }\r
3254                                                                                 else {\r
3255                                                                                         int idx = svd.detail.indexOf("<!");\r
3256                                                                                         if (idx != -1) {\r
3257                                                                                                 tvd.detail += svd.detail.substring(idx);\r
3258                                                                                         }\r
3259                                                                                 }\r
3260                                                                                 \r
3261                                                                                 // 「しょぼかるにのみ存在」フラグの上げ下げ(これはアニメ限定)\r
3262                                                                                 if ( isAnime ) {\r
3263                                                                                         if ( isFind ) {\r
3264                                                                                                 tvd.removeOption(ProgOption.NOSYOBO);   // NOSYOBOって…\r
3265                                                                                         }\r
3266                                                                                         else {\r
3267                                                                                                 //tvd.addOption(ProgOption.NOSYOBO);\r
3268                                                                                         }\r
3269                                                                                 }\r
3270                                                                                 \r
3271                                                                                 break;\r
3272                                                                         }\r
3273                                                                 }\r
3274                                                         }\r
3275                                                 }\r
3276                                         }\r
3277                                         break;\r
3278                                 }\r
3279                         }\r
3280                 }\r
3281         }\r
3282         \r
3283         // 番組タイトルを整形する\r
3284         private void fixTitle() {\r
3285                 //\r
3286                 if ( ! env.getFixTitle()) {\r
3287                         return;\r
3288                 }\r
3289                 //\r
3290                 for ( TVProgram tvp : tvprograms ) {\r
3291                         //if ( ! (tvp.getType() == ProgType.PROG && tvp.getSubtype() == ProgSubtype.TERRA) ) {\r
3292                         if ( tvp.getType() != ProgType.PROG ) {\r
3293                                 continue;\r
3294                         }\r
3295                         //\r
3296                         for ( ProgList pl : tvp.getCenters() ) {\r
3297                                 if ( ! pl.enabled ) {\r
3298                                         continue;\r
3299                                 }\r
3300 \r
3301                                 for ( ProgDateList pcl : pl.pdate ) {\r
3302                                         //\r
3303                                         for ( ProgDetailList tvd : pcl.pdetail ) {\r
3304                                                 if ( tvd.isEqualsGenre(ProgGenre.ANIME, null) ) {\r
3305                                                         if ( pl.Center.startsWith("NHK") || pl.Center.startsWith("NHK") ) {\r
3306                                                                 // NHK系で先頭が「アニメ 」ではじまるものから「アニメ 」を削除する\r
3307                                                                 tvd.title = tvd.title.replaceFirst("^アニメ[  ・]+","");\r
3308                                                                 tvd.titlePop = TraceProgram.replacePop(tvd.title);\r
3309                                                                 tvd.SearchStrKeys = TraceProgram.splitKeys(tvd.titlePop);\r
3310                                                         }\r
3311                                                         if ( (tvd.title.contains("劇場版") || tvd.detail.contains("映画")) && ! tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) ) {\r
3312                                                                 // ジャンル=アニメだがタイトルに「劇場版」が含まれるならジャンル=映画(アニメ映画)を追加する\r
3313                                                                 if ( tvd.genrelist == null ) {\r
3314                                                                         tvd.genrelist = new ArrayList<ProgGenre>();\r
3315                                                                         tvd.genrelist.add(tvd.genre);\r
3316                                                                         tvd.genrelist.add(ProgGenre.MOVIE);\r
3317                                                                         tvd.subgenrelist = new ArrayList<ProgSubgenre>();\r
3318                                                                         tvd.subgenrelist.add(tvd.subgenre);\r
3319                                                                         tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
3320                                                                 }\r
3321                                                                 else {\r
3322                                                                         tvd.genrelist.add(ProgGenre.MOVIE);\r
3323                                                                         tvd.subgenrelist.add(ProgSubgenre.MOVIE_ANIME);\r
3324                                                                 }\r
3325                                                         }\r
3326                                                 }\r
3327                                                 else if ( tvd.isEqualsGenre(ProgGenre.MOVIE, ProgSubgenre.MOVIE_ANIME) && tvd.subgenre != ProgSubgenre.MOVIE_ANIME ) {\r
3328                                                         // ジャンル=映画でサブジャンルが複数ありアニメが優先されてないものはアニメを優先する\r
3329                                                         tvd.subgenre = ProgSubgenre.MOVIE_ANIME;\r
3330                                                 }\r
3331                                                 \r
3332                                                 // サブタイトルを番組追跡の対象から外す\r
3333                                                 if ( env.getTraceOnlyTitle() && tvd.title != tvd.splitted_title ) {\r
3334                                                         tvd.SearchStrKeys = TraceProgram.splitKeys(TraceProgram.replacePop(tvd.splitted_title));        // 番組追跡の検索用インデックスは、サブタイトルを削除したもので置き換える\r
3335                                                 }\r
3336                                         }\r
3337                                 }\r
3338                         }\r
3339                 }\r
3340         }\r
3341         \r
3342         /**\r
3343          * {@link ProgDetailList} の情報を整形する\r
3344          */\r
3345         private void fixDetail() {\r
3346                 for ( TVProgram tvp : tvprograms ) {\r
3347                         for ( ProgList pl : tvp.getCenters() ) {\r
3348                                 if ( ! pl.enabled ) {\r
3349                                         continue;\r
3350                                 }\r
3351                                 for ( ProgDateList pcl : pl.pdate ) {\r
3352                                         for ( ProgDetailList tvd : pcl.pdetail ) {\r
3353                                                 if ( tvd.start == null || tvd.start.length() == 0 ) {\r
3354                                                         continue;\r
3355                                                 }\r
3356                                                 \r
3357                                                 fixDetailSub(tvp, pl, tvd);\r
3358                                         }\r
3359                                 }\r
3360                         }\r
3361                 }\r
3362         }\r
3363         \r
3364         private void fixDetailSub(TVProgram tvp, ProgList pl, ProgDetailList tvd) {\r
3365                 tvd.type = tvp.getType();\r
3366                 tvd.subtype = tvp.getSubtype();\r
3367                 tvd.center = pl.Center;\r
3368                 \r
3369                 tvd.recmin = CommonUtils.getRecMinVal(tvd.startDateTime, tvd.endDateTime);\r
3370                 \r
3371                 tvd.extension_mark = markchar.getExtensionMark(tvd);\r
3372                 tvd.prefix_mark = markchar.getOptionMark(tvd);\r
3373                 tvd.newlast_mark = markchar.getNewLastMark(tvd);\r
3374                 tvd.postfix_mark = markchar.getPostfixMark(tvd);\r
3375                 \r
3376                 tvd.dontoverlapdown = (tvd.center.startsWith("NHK") || tvd.center.startsWith("NHK"));\r
3377         }\r
3378         \r
3379         /**\r
3380          * <P>過去ログから検索キーワードにマッチする情報を取得する\r
3381          * <P>全部検索がヒットした結果がかえるのだから {@link ProgDetailList} ではなく {@link MarkedProgramList} を使うべきなのだが…\r
3382          */\r
3383         private boolean searchPassedProgram(final SearchKey sKey, final String target) {\r
3384                 \r
3385                 Matcher ma = Pattern.compile("^(\\d\\d\\d\\d/\\d\\d/\\d\\d)-(\\d\\d\\d\\d/\\d\\d/\\d\\d)$").matcher(target);\r
3386                 if ( ! ma.find() ) {\r
3387                         return false;\r
3388                 }\r
3389                 \r
3390                 final GregorianCalendar s = CommonUtils.getCalendar(ma.group(1));\r
3391                 final GregorianCalendar e = CommonUtils.getCalendar(ma.group(2));\r
3392                 final long dDays = (e.getTimeInMillis() - s.getTimeInMillis())/86400000 + 1;\r
3393 \r
3394                 final ArrayList<ProgDetailList> srchpdl = tvprograms.getSearched().getResultBuffer(sKey.getLabel()) ;\r
3395 \r
3396                 stwin.clear();\r
3397                 \r
3398                 // 検索実行(時間がかかるので状況表示する)\r
3399                 new SwingBackgroundWorker(false) {\r
3400                         \r
3401                         @Override\r
3402                         protected Object doWorks() throws Exception {\r
3403                                 \r
3404                                 TatCount tc = new TatCount();\r
3405                                 \r
3406                                 // 検索中\r
3407                                 int resultCnt = 0;\r
3408                                 for (int cnt=1; cnt<=dDays; cnt++) {\r
3409                                         \r
3410                                         String passdt = CommonUtils.getDate(e);\r
3411                                         stwin.appendMessage(String.format("[過去ログ検索] 検索中:(%d/%d) %s", cnt, dDays, passdt));\r
3412                                         \r
3413                                         PassedProgram tvp = new PassedProgram();\r
3414                                         if ( tvp.loadAllCenters(passdt) ) {\r
3415                                                 for ( ProgList pl : tvp.getCenters() ) {\r
3416                                                         if ( ! pl.enabled ) {\r
3417                                                                 continue;\r
3418                                                         }\r
3419                                                         \r
3420                                                         for ( ProgDateList pcl : pl.pdate ) {\r
3421                                                                 for ( ProgDetailList tvd : pcl.pdetail ) {\r
3422                                                                         if ( tvd.start == null || tvd.start.length() == 0 ) {\r
3423                                                                                 continue;\r
3424                                                                         }\r
3425                                                                         \r
3426                                                                         if ( SearchProgram.isMatchKeyword(sKey, pl.Center, tvd) ) {\r
3427                                                                                 tvd.dynKey = sKey;\r
3428                                                                                 tvd.dynMatched = SearchProgram.getMatchedString();\r
3429                                                                                 fixDetailSub(tvp, pl, tvd);\r
3430                                                                                 srchpdl.add(tvd);\r
3431                                                                                 if ( ++resultCnt >= env.getSearchResultMax() ) {\r
3432                                                                                         mwin.appendMessage(String.format("[過去ログ検索] 検索件数の上限に到達しました。所要時間: %.2f秒",tc.end()));\r
3433                                                                                         return null;\r
3434                                                                                 }\r
3435                                                                         }\r
3436                                                                 }\r
3437                                                         }\r
3438                                                 }\r
3439                                         }\r
3440                                         \r
3441                                         e.add(Calendar.DATE,-1);\r
3442                                 }\r
3443 \r
3444                                 mwin.appendMessage(String.format("[過去ログ検索] 検索完了。所要時間: %.2f秒",tc.end()));\r
3445                                 return null;\r
3446                         }\r
3447                         \r
3448                         @Override\r
3449                         protected void doFinally() {\r
3450                                 StWinSetVisible(false);\r
3451                         }\r
3452                 }.execute();\r
3453                 \r
3454                 StWinSetLocationCenter(this);\r
3455                 StWinSetVisible(true);\r
3456 \r
3457                 return true;\r
3458         }\r
3459 \r
3460         // システムトレイ関係\r
3461         private void getTrayIcon() {\r
3462                 if ( trayicon != null ) {\r
3463                         return;\r
3464                 }\r
3465                 \r
3466                 try {\r
3467                         Image image = ImageIO.read(new File(ICONFILE_SYSTRAY));\r
3468                         trayicon = new TrayIcon(image,"Tainavi");\r
3469                         \r
3470                         final Viewer thisClass = this;\r
3471                         \r
3472                         // メニューの追加\r
3473                         PopupMenu popup = new PopupMenu();\r
3474                         {\r
3475                                 MenuItem item = new MenuItem("開く");\r
3476                                 item.addActionListener(new ActionListener() {\r
3477                                         @Override\r
3478                                         public void actionPerformed(ActionEvent e) {\r
3479                                                 thisClass.setVisible(true);\r
3480                                                 thisClass.setState(Frame.NORMAL);\r
3481                                         }\r
3482                                 });\r
3483                                 popup.add(item);\r
3484                         }\r
3485                         {\r
3486                                 MenuItem item = new MenuItem("終了する");\r
3487                                 item.addActionListener(new ActionListener() {\r
3488                                         @Override\r
3489                                         public void actionPerformed(ActionEvent e) {\r
3490                                                 ExitOnClose();\r
3491                                                 System.exit(0);\r
3492                                         }\r
3493                                 });\r
3494                                 popup.add(item);\r
3495                         }\r
3496                         trayicon.setPopupMenu(popup);\r
3497                         \r
3498                         // 左クリックで復帰\r
3499                         trayicon.addMouseListener(new MouseAdapter() {\r
3500                                 //\r
3501                                 public void mouseClicked(MouseEvent e) {\r
3502                                         if (e.getButton() == MouseEvent.BUTTON1) {\r
3503                                                 thisClass.setVisible(true);\r
3504                                                 thisClass.setState(Frame.NORMAL);\r
3505                                         }\r
3506                                 }\r
3507                         });\r
3508                 \r
3509                 } catch (IOException e) {\r
3510                         StdAppendError("アイコンファイルが読み込めませんでした: "+ICONFILE_SYSTRAY);\r
3511                         e.printStackTrace();\r
3512                 }\r
3513         }\r
3514         private void setTrayIconVisible(boolean b) {\r
3515                 \r
3516                 if ( ! SystemTray.isSupported() || trayicon == null ) {\r
3517                         return;\r
3518                 }\r
3519                 \r
3520                 try {\r
3521                         if ( b ) {\r
3522                                 // システムトレイに追加\r
3523                                 SystemTray.getSystemTray().remove(trayicon);\r
3524                                 SystemTray.getSystemTray().add(trayicon);\r
3525                         }\r
3526                         else {\r
3527                                 // システムトレイから削除\r
3528                                 SystemTray.getSystemTray().remove(trayicon);\r
3529                         }\r
3530                 } catch (AWTException e) {\r
3531                         e.printStackTrace();\r
3532                 }\r
3533         }\r
3534         private void HideToTray() {\r
3535                 if ( SystemTray.isSupported() && trayicon != null && (env.getShowSysTray() && env.getHideToTray()) ) {\r
3536                         this.setVisible(false);\r
3537                 }\r
3538         }\r
3539         private void setXButtonAction(boolean b) {\r
3540                 if ( b ) {\r
3541                         this.setDefaultCloseOperation(JFrame.ICONIFIED);\r
3542                 }\r
3543                 else {\r
3544                         this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
3545                 }\r
3546         }\r
3547         \r
3548         // コマンドライン引数の処理\r
3549         private void procArgs(String[] args) {\r
3550                 int flag = 0;\r
3551                 for (String arg : args) {\r
3552                         switch (flag) {\r
3553                         case 0:\r
3554                                 if (arg.compareTo("-L") == 0) {\r
3555                                         // -l : ロギング\r
3556                                         //logging = false;\r
3557                                 }\r
3558                                 else if (arg.compareTo("-L") == 0) {\r
3559                                         // -L : ロギング不可\r
3560                                         logging = false;\r
3561                                 }\r
3562                                 else if (arg.compareTo("-w") == 0) {\r
3563                                         // -w : レコーダ起動\r
3564                                         runRecWakeup = true;\r
3565                                 }\r
3566                                 else if (arg.compareTo("-nowebaccess") == 0) {\r
3567                                         // -nowebaccess : 起動時のWeb番組表へのアクセス無効\r
3568                                         enableWebAccess = false;\r
3569                                 }\r
3570                                 else if (arg.compareTo("-proxy") == 0) {\r
3571                                         // -proxy : Web番組表へのアクセスにProxy経由を強制する\r
3572                                         flag = 1;\r
3573                                 }\r
3574                                 else if (arg.compareTo("-loadrec") == 0) {\r
3575                                         // -loadrec : 起動時にレコーダにアクセスする\r
3576                                         runRecLoad = true;\r
3577                                 }\r
3578                                 else if (arg.compareTo("-onlyLoadProgram") == 0) {\r
3579                                         // -onlyLoadProgram : 番組表の取得だけ行う\r
3580                                         onlyLoadProgram = true;\r
3581                                 }\r
3582                                 break;\r
3583                         case 1:\r
3584                                 String[] dat = arg.split(":");\r
3585                                 if (dat.length == 1 ) {\r
3586                                         pxaddr = dat[0];\r
3587                                         pxport = "8080";\r
3588                                 } if (dat.length >= 2 ) {\r
3589                                         pxaddr = dat[0];\r
3590                                         pxport = dat[1];\r
3591                                 }\r
3592                                 flag = 0;\r
3593                                 break;\r
3594                         }\r
3595                 }\r
3596         }\r
3597         \r
3598         // メインの環境設定ファイルを読みだす\r
3599         private void loadEnvfile() {\r
3600                 StdAppendMessage("【環境設定】環境設定ファイルを読み込みます.");\r
3601                 env.load();\r
3602         }\r
3603         \r
3604         // 引き続きその他の環境設定ファイルも読みだす\r
3605         private void procEnvs() {\r
3606                 \r
3607                 StdAppendMessage("【環境設定】環境設定ファイル類を読み込みます.");\r
3608 \r
3609                 // 各種設定\r
3610                 env.makeEnvDir();\r
3611                 \r
3612                 // レコーダ一覧\r
3613                 recInfoList.load();\r
3614 \r
3615                 // Proxyサーバ\r
3616                 if (pxaddr != null) {\r
3617                         env.setUseProxy(true);\r
3618                         env.setProxyAddr(pxaddr);\r
3619                         env.setProxyPort(pxport);\r
3620                 }\r
3621                 \r
3622                 // Cookieの処理を入れようとしたけど無理だった\r
3623                 /*\r
3624                 {\r
3625                         CookieManager manager = new CookieManager();\r
3626                         manager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);\r
3627                         CookieHandler.setDefault(manager);\r
3628                 }\r
3629                 */\r
3630                 \r
3631                 // ジャンル別背景色\r
3632                 pColors.load();\r
3633                 \r
3634                 // 深夜の帯予約の補正(一日前にずらす)\r
3635                 // 可能なら番組表を8日分取得する\r
3636                 // 【WIN】ファイルオープンにrundll32を使用する\r
3637                 CommonUtils.setAdjLateNight(env.getAdjLateNight());\r
3638                 CommonUtils.setExpandTo8(env.getExpandTo8());\r
3639                 CommonUtils.setUseRundll32(env.getUseRundll32());\r
3640                 CommonUtils.setDisplayPassedReserve(env.getDisplayPassedReserve());\r
3641                 CommonUtils.setDebug(env.getDebug());\r
3642                 \r
3643                 SwingBackgroundWorker.setDebug(env.getDebug());\r
3644                 \r
3645                 // クリップボードアイテム\r
3646                 cbitems.load();\r
3647                 \r
3648                 // サイズ・位置情報取得\r
3649                 bounds.setLoaded(bounds.load());\r
3650                 \r
3651                 // 番組追跡キーワード取得\r
3652                 trKeys.load();\r
3653                 \r
3654                 // 検索キーワード取得\r
3655                 srKeys.load();\r
3656                 \r
3657                 // 検索キーワードグループ取得\r
3658                 srGrps.load();\r
3659                 \r
3660                 // 延長警告源設定取得\r
3661                 extKeys.load();\r
3662                 \r
3663                 // デフォルトAV設定取得\r
3664                 avs.load();\r
3665                 chavs.load();\r
3666 \r
3667                 // スポーツ延長警告のデフォルト設定のコードはもういらないので削除(3.15.4β) \r
3668                 \r
3669                 // 簡易描画はもういらないので削除\r
3670                 \r
3671                 // ChannelConvert\r
3672                 chconv.load();\r
3673         }\r
3674         \r
3675         // 二重起動チェック\r
3676         private void chkDualBoot() {\r
3677                 if ( ! env.getOnlyOneInstance() ) {\r
3678                         return;\r
3679                 }\r
3680                 \r
3681                 if ( ! CommonUtils.getLock() ) {\r
3682                         // 既にロックされている\r
3683                         ringBeep();\r
3684                         System.exit(1);\r
3685                 }\r
3686                 \r
3687                 Runtime.getRuntime().addShutdownHook(new Thread() {\r
3688                         public void run() {\r
3689                                 // 鯛ナビ終了時にロックを解除する\r
3690                                 CommonUtils.getUnlock();\r
3691                         }\r
3692                 });\r
3693         }\r
3694         \r
3695         // アップデートの有無チェック\r
3696         private void chkVerUp() {\r
3697                 if ( ! enableWebAccess || onlyLoadProgram ) {\r
3698                         stwin.appendError("【オンラインアップデート】オンラインアップデートは無効です");\r
3699                         return;\r
3700                 }\r
3701                 \r
3702                 VWUpdate vu = new VWUpdate(stwin);\r
3703                 if ( ! vu.isExpired(env.getUpdateMethod()) ) {\r
3704                         // メッセージはVWUpdate内で出力されます\r
3705                         return;\r
3706                 }\r
3707                 if ( doVerUp(vu) ) {\r
3708                         System.exit(0);\r
3709                 }\r
3710         }\r
3711         \r
3712         private boolean doVerUp(VWUpdate vu) {\r
3713                 UpdateResult res = vu.checkUpdate(VersionInfo.getVersion());\r
3714                 switch ( res ) {\r
3715                 case DONE:\r
3716                         // 成功\r
3717                         // 履歴は更新しない(連続アップデートがあるかも知れないので)\r
3718                         LogViewer lv = new LogViewer(HISTORY_FILE);\r
3719                         lv.setModal(true);\r
3720                         lv.setCaretPosition(0);\r
3721                         lv.setVisible(true);\r
3722                         return true;\r
3723                 case PASS:\r
3724                         // キャンセル\r
3725                         // 履歴は更新しない(次回に持ち越し)\r
3726                         break;\r
3727                 case NOUPDATE:\r
3728                         // アップデートなし\r
3729                         vu.updateHistory();\r
3730                         break;\r
3731                 default:\r
3732                         // 失敗\r
3733                         // 履歴は更新しない(次回再挑戦)\r
3734                         break;\r
3735                 }\r
3736                 return false;\r
3737         }\r
3738         \r
3739         /**\r
3740          *  レコーダプラグインをすべて読み込みます。\r
3741          */\r
3742         private boolean loadRecPlugins() {\r
3743                 \r
3744                 stwin.appendMessage("【レコーダプラグイン】プラグインを読み込みます.");\r
3745                 \r
3746                 boolean isMailPluginEnabled = false;\r
3747                 try {\r
3748                         Class.forName("javax.mail.Session");\r
3749                         isMailPluginEnabled = true;\r
3750                 }\r
3751                 catch ( Exception e ) {\r
3752                         System.err.println("【レコーダプラグイン】メール系プラグイン用の外部ライブラリがみつかりません: "+e.toString());\r
3753                 }\r
3754 \r
3755                 boolean isCalendarPluginEnabled = false;\r
3756                 try {\r
3757                         Class.forName("com.google.gdata.client.calendar.CalendarService");\r
3758                         isCalendarPluginEnabled = true;\r
3759                 }\r
3760                 catch ( Exception e ) {\r
3761                         System.err.println("【レコーダプラグイン】カレンダー系プラグイン用の外部ライブラリがみつかりません: "+e.toString());\r
3762                 }\r
3763                 \r
3764                 //\r
3765                 ArrayList<String> recIda = new ArrayList<String>();\r
3766                 for ( File f : new File(CommonUtils.joinPath(new String[]{"bin","tainavi"})).listFiles() ) {\r
3767                         Matcher ma = Pattern.compile("^(PlugIn_Rec[^$]+)[^$]*\\.class$").matcher(f.getName());\r
3768                         if ( ma.find() ) {\r
3769                                 if ( ! isMailPluginEnabled && f.getName().toLowerCase().contains("mail") ) {\r
3770                                         System.out.println("【レコーダプラグイン】メール系プラグインは無効です: "+f.getName());\r
3771                                         continue;\r
3772                                 }\r
3773                                 if ( ! isCalendarPluginEnabled && f.getName().toLowerCase().contains("calendar") ) {\r
3774                                         System.out.println("【レコーダプラグイン】カレンダー系プラグインは無効です: "+f.getName());\r
3775                                         continue;\r
3776                                 }\r
3777                                 \r
3778                                 recIda.add(ma.group(1));\r
3779                         }\r
3780                 }\r
3781                 String[] recIdd = recIda.toArray(new String[0]);\r
3782                 Arrays.sort(recIdd);\r
3783                 \r
3784                 // servicesに追記\r
3785                 StringBuilder sb = new StringBuilder();\r
3786                 for ( String recId : recIdd ) {\r
3787                         sb.append("tainavi.");\r
3788                         sb.append(recId);\r
3789                         sb.append("\n");\r
3790                 }\r
3791                 if ( ! CommonUtils.write2file(CommonUtils.joinPath(new String[] {"bin","META-INF","services","tainavi.HDDRecorder"}), sb.toString()) ) {\r
3792                         stwin.appendError("【レコーダプラグイン】プラグインの読み込みに失敗しました: ");\r
3793                         return false;\r
3794                 }\r
3795 \r
3796                 // ここで例外が起きてもトラップできない、スレッドが落ちる\r
3797                 ServiceLoader<HDDRecorder> r = ServiceLoader.load(HDDRecorder.class);\r
3798                 \r
3799                 recPlugins.clear();\r
3800                 for ( HDDRecorder recorder : r ) {\r
3801                         if (env.getDebug()) StdAppendMessage("+追加します: "+recorder.getRecorderId());\r
3802                         recPlugins.add(recorder.clone());\r
3803                         StdAppendMessage("+追加しました: "+recorder.getRecorderId());\r
3804                 }\r
3805                 \r
3806                 return true;\r
3807         }\r
3808         \r
3809         /**\r
3810          * レコーダ設定をもとにレコーダプラグインから実レコーダのインスタンスを生成します。\r
3811          */\r
3812         private void initRecPluginAll() {\r
3813                 //\r
3814                 recorders.clear();\r
3815                 for ( RecorderInfo ri : recInfoList ) {\r
3816                         ArrayList<HDDRecorder> rl = recPlugins.findPlugin(ri.getRecorderId());\r
3817                         if ( rl.size() == 0 ) {\r
3818                                 stwin.appendError("【レコーダプラグイン】プラグインがみつかりません: "+ri.getRecorderId()+"("+ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+")");\r
3819                         }\r
3820                         else { \r
3821                                 stwin.appendMessage("【レコーダプラグイン】プラグインを初期化します: "+ri.getRecorderId()+"("+ri.getRecorderIPAddr()+":"+ri.getRecorderPortNo()+")");\r
3822                                 for ( HDDRecorder rPlugin : rl ) {\r
3823                                         initRecPlugin(rPlugin, ri);\r
3824                                 }\r
3825                         }\r
3826                 }\r
3827         }\r
3828         protected HDDRecorder initRecPlugin(HDDRecorder rPlugin, RecorderInfo ri) {\r
3829                 HDDRecorder rec = rPlugin.clone();\r
3830                 recorders.add(rec);\r
3831                 \r
3832                 rec.getChCode().load(true);     // true : ログ出力あり\r
3833                 setSettingRecPluginBase(rec, ri);\r
3834                 setSettingRecPluginExt(rec,env);\r
3835                 rec.setProgressArea(stwin);\r
3836                 return rec;\r
3837         }\r
3838         protected void setSettingRecPluginBase(HDDRecorder to, RecorderInfo from) {\r
3839                 to.setIPAddr(from.getRecorderIPAddr());\r
3840                 to.setPortNo(from.getRecorderPortNo());\r
3841                 to.setUser(from.getRecorderUser());\r
3842                 to.setPasswd(from.getRecorderPasswd());\r
3843                 to.setMacAddr(from.getRecorderMacAddr());\r
3844                 to.setBroadcast(from.getRecorderBroadcast());\r
3845                 to.setUseCalendar(from.getUseCalendar());\r
3846                 to.setUseChChange(from.getUseChChange());\r
3847                 to.setRecordedCheckScope(from.getRecordedCheckScope());\r
3848                 to.setTunerNum(from.getTunerNum());\r
3849                 to.setColor(from.getRecorderColor());\r
3850         }\r
3851         protected void setSettingRecPluginExt(HDDRecorder recorder, Env nEnv) {\r
3852                 recorder.setUserAgent(nEnv.getUserAgent());\r
3853                 recorder.setDebug(nEnv.getDebug());\r
3854                 recorder.setAdjNotRep(nEnv.getAdjoiningNotRepetition());\r
3855                 recorder.setRecordedSaveScope(nEnv.getRecordedSaveScope());\r
3856         }\r
3857         \r
3858         //\r
3859         protected void doRecWakeup() {\r
3860                 for ( HDDRecorder rec : recorders ) {\r
3861                         if ( ! rec.getMacAddr().equals("") && ! rec.getBroadcast().equals("") ) {\r
3862                                 rec.wakeup();\r
3863                         }\r
3864                 }\r
3865         }\r
3866         \r
3867         /**\r
3868          * 一時間は再実行させないんだ\r
3869          */\r
3870         private boolean isOLPExpired(int expire) {\r
3871                 String fname = "env"+File.separator+"olp.history";\r
3872                 if ( ! new File(fname).exists() || ! new File(fname).canWrite() ) {\r
3873                         stwin.appendError("【警告】実行履歴ファイルがないから実行させないよ!");\r
3874                         ringBeep();\r
3875                         return false;\r
3876                 }\r
3877                 String dat = CommonUtils.read4file(fname, true);\r
3878                 if ( dat == null ) {\r
3879                         stwin.appendError("【警告】実行履歴を取得できなかったから実行させないよ!");\r
3880                         ringBeep();\r
3881                         return false;\r
3882                 }\r
3883                 GregorianCalendar ca = null;\r
3884                 dat = EncryptPassword.dec(b64.dec(dat));\r
3885                 if ( dat != null ) {\r
3886                         ca = CommonUtils.getCalendar(dat);\r
3887                 }\r
3888                 if ( ca == null ) {\r
3889                         stwin.appendError("【警告】実行履歴の内容が不正だったから実行させないよ! "+dat);\r
3890                         ringBeep();\r
3891                         return false;\r
3892                 }\r
3893                 if ( CommonUtils.getCompareDateTime(ca, CommonUtils.getCalendar(-expire*3600)) >= 0 ) {\r
3894                         ca.add(Calendar.HOUR,expire);\r
3895                         stwin.appendError("【警告】"+expire+"時間以内の再実行は許さないよ!"+CommonUtils.getDateTime(ca)+"まで待って!");\r
3896                         ringBeep();\r
3897                         return false;\r
3898                 }\r
3899                 if ( ! CommonUtils.write2file(fname, b64.enc(EncryptPassword.enc(CommonUtils.getDateTime(0)))) ) {\r
3900                         stwin.appendError("【警告】実行履歴を保存できなかったから実行させないよ!");\r
3901                         ringBeep();\r
3902                         return false;\r
3903                 }\r
3904                 \r
3905                 return true;\r
3906         }\r
3907         \r
3908         /**\r
3909          *  Web番組表プラグインをすべて読み込みます。\r
3910          */\r
3911         private boolean loadProgPlugins() {\r
3912         \r
3913                 final String FUNCID = "[Web番組表プラグイン組込] ";\r
3914                 final String ERRID = "[ERROR]"+FUNCID;\r
3915                 \r
3916                 // Web番組表プラグインの処理\r
3917                 stwin.appendMessage(FUNCID+"プラグインを読み込みます.");\r
3918                 \r
3919                 // Web番組表共通設定\r
3920                 setSettingProgPluginCommon(env);\r
3921                 \r
3922                 /*\r
3923                  * 重要 - ここから\r
3924                  */\r
3925                 \r
3926                 // TVProgramListのインスタンスは別途初期化が必要\r
3927                 progPlugins.clear();\r
3928                 tvprograms.clear();\r
3929 \r
3930                 /*\r
3931                  * 重要 - ここまで\r
3932                  */\r
3933 \r
3934                 ArrayList<String> prgIda = new ArrayList<String>();\r
3935                 for ( File f : new File(CommonUtils.joinPath("bin","tainavi")).listFiles() ) {\r
3936                         Matcher ma = Pattern.compile("^(PlugIn_(TV|CS|RAD)P[^$]+)\\.class$").matcher(f.getName());\r
3937                         if (ma.find()) {\r
3938                                 prgIda.add(ma.group(1));\r
3939                         }\r
3940                 }\r
3941                 String[] prgIdd = prgIda.toArray(new String[0]);\r
3942                 Arrays.sort(prgIdd);\r
3943                 \r
3944                 // servicesに追記\r
3945                 StringBuilder sb = new StringBuilder();\r
3946                 for ( String prgId : prgIdd ) {\r
3947                         sb.append("tainavi.");\r
3948                         sb.append(prgId);\r
3949                         sb.append("\n");\r
3950                 }\r
3951                 if ( ! CommonUtils.write2file(CommonUtils.joinPath("bin","META-INF","services","tainavi.TVProgram"), sb.toString()) ) {\r
3952                         stwin.appendError(ERRID+"プラグインの読み込みに失敗しました: ");\r
3953                         return false;\r
3954                 }\r
3955                 \r
3956                 ServiceLoader<TVProgram> p = ServiceLoader.load(TVProgram.class);\r
3957 \r
3958                 // 実際必要ないのだが、プラグインのインスタンスはclone()して使う\r
3959                 for ( TVProgram pg : p ) {\r
3960                         TVProgram prog = pg.clone();\r
3961                         \r
3962                         stwin.appendMessage("+追加しました: "+prog.getTVProgramId());\r
3963                         \r
3964                         // CH設定タブではプラグイン側のインスタンスを使うので情報を追加してやる必要があるのであった\r
3965                         setSettingProgPlugin(prog, env);\r
3966                         \r
3967                         progPlugins.add(prog);\r
3968                 }\r
3969                 \r
3970                 p = null;\r
3971                 \r
3972                 return true;\r
3973         }\r
3974         \r
3975         /**\r
3976          *  設定にあわせてWeb番組表プラグインを絞り込みます。\r
3977          */\r
3978         private void setSelectedProgPlugin() {\r
3979                 \r
3980                 // この3つは保存しておく\r
3981                 Syobocal syobo = tvprograms.getSyobo();\r
3982                 PassedProgram passed = tvprograms.getPassed();\r
3983                 PickedProgram pickup = tvprograms.getPickup();\r
3984                 SearchResult searched = tvprograms.getSearched();\r
3985                 \r
3986                 tvprograms.clear();\r
3987                 \r
3988                 {\r
3989                         TVProgram tvp = progPlugins.getTvProgPlugin(env.getTVProgramSite());\r
3990                         if ( tvp == null ) {\r
3991                                 // デフォルトもなければ先頭にあるもの\r
3992                                 tvp = progPlugins.getTvProgPlugin(null);\r
3993                         }\r
3994                         if ( tvp == null ) {\r
3995                                 // てか一個もなくね?\r
3996                                 StdAppendError("【Web番組表選択】地上波&BS番組表が選択されていません: "+env.getTVProgramSite());\r
3997                         }\r
3998                         else {\r
3999                                 StdAppendMessage("【Web番組表選択】地上波&BS番組表が選択されました: "+tvp.getTVProgramId());\r
4000                                 tvprograms.add(tvp.clone());\r
4001                         }\r
4002                 }\r
4003                 {\r
4004                         TVProgram tvp = progPlugins.getCsProgPlugin(env.getCSProgramSite());\r
4005                         if ( tvp == null ) {\r
4006                                 tvp = progPlugins.getCsProgPlugin(null);\r
4007                         }\r
4008                         if ( tvp == null ) {\r
4009                                 StdAppendError("【Web番組表選択】CS番組表[プライマリ]が選択されていません: "+env.getCSProgramSite());\r
4010                         }\r
4011                         else {\r
4012                                 StdAppendMessage("【Web番組表選択】CS番組表[プライマリ]が選択されました: "+tvp.getTVProgramId());\r
4013                                 tvprograms.add(tvp.clone());\r
4014                         }\r
4015                 }\r
4016                 {\r
4017                         TVProgram tvp = progPlugins.getCs2ProgPlugin(env.getCS2ProgramSite());\r
4018                         if ( tvp == null ) {\r
4019                                 tvp = progPlugins.getCs2ProgPlugin(null);\r
4020                         }\r
4021                         if ( tvp == null ) {\r
4022                                 StdAppendError("【Web番組表選択】CS番組表[プライマリ]が選択されていません: "+env.getCS2ProgramSite());\r
4023                         }\r
4024                         else {\r
4025                                 StdAppendMessage("【Web番組表選択】CS番組表[プライマリ]が選択されました: "+tvp.getTVProgramId());\r
4026                                 tvprograms.add(tvp.clone());\r
4027                         }\r
4028                 }\r
4029                 /*\r
4030                 if ( progPlugins.getRadioProgPlugins().size() > 0 )\r
4031                 {\r
4032                         TVProgram tvp = progPlugins.getCsProgPlugin(env.getRadioProgramSite());\r
4033                         if ( tvp == null ) {\r
4034                                 tvp = progPlugins.getCsProgPlugin(null);\r
4035                         }\r
4036                         if ( tvp == null ) {\r
4037                                 StdAppendError("【Web番組表選択】ラジオ番組表が選択されていません: "+env.getRadioProgramSite());\r
4038                         }\r
4039                         else {\r
4040                                 StdAppendMessage("【Web番組表選択】ラジオ番組表が選択されました: "+tvp.getTVProgramId());\r
4041                                 tvprograms.add(tvp.clone());\r
4042                         }\r
4043                 }\r
4044                 */\r
4045                 \r
4046                 {\r
4047                         if ( syobo == null ) {\r
4048                                 syobo = new Syobocal();\r
4049                         }\r
4050                         tvprograms.add(syobo);\r
4051                 }\r
4052                 {\r
4053                         if ( passed == null ) {\r
4054                                 passed = new PassedProgram();\r
4055                         }\r
4056                         tvprograms.add(passed);\r
4057                 }\r
4058                 {\r
4059                         if ( pickup == null ) {\r
4060                                 pickup = new PickedProgram();\r
4061                                 pickup.loadProgram(null, false);\r
4062                         }\r
4063                         tvprograms.add(pickup);\r
4064                 }\r
4065                 {\r
4066                         if ( searched == null ) {\r
4067                                 searched = new SearchResult();\r
4068                         }\r
4069                         tvprograms.add(searched);\r
4070                 }\r
4071         }\r
4072         \r
4073         /**\r
4074          * Web番組表設定をもとにレコーダプラグインのインスタンスを生成します。\r
4075          */\r
4076         private void initProgPluginAll() {\r
4077 \r
4078                 final String FUNCID = "[Web番組表プラグイン初期化] ";\r
4079                 final LinkedHashMap<ArrayList<TVProgram>,String> map = new LinkedHashMap<ArrayList<TVProgram>, String>();\r
4080                 map.put(tvprograms.getTvProgPlugins(), "地上波&BS番組表");\r
4081                 map.put(tvprograms.getCsProgPlugins(), "CS番組表[プライマリ]");\r
4082                 map.put(tvprograms.getCs2ProgPlugins(), "CS番組表[セカンダリ]");\r
4083                 //map.put(progPlugins.getRadioProgPlugins(), "ラジオ番組表");\r
4084                 \r
4085                 new SwingBackgroundWorker(true) {\r
4086                         \r
4087                         @Override\r
4088                         protected Object doWorks() throws Exception {\r
4089 \r
4090                                 for ( ArrayList<TVProgram> tvpa : map.keySet() ) {\r
4091                                         stwin.appendMessage(FUNCID+map.get(tvpa)+"のベース情報(放送局リストなど)を取得します.");\r
4092                                         for ( TVProgram p : tvpa ) {\r
4093                                                 stwin.appendMessage(FUNCID+"プラグインを初期化します: "+p.getTVProgramId());\r
4094                                                 \r
4095                                                 try {\r
4096                                                         // 個別設定(2) …(1)と(2)の順番が逆だったので前に移動してきました(3.17.3β)\r
4097                                                         setSettingProgPlugin(p,env);                            // 他からも呼び出される部分だけ分離\r
4098                                                         \r
4099                                                         // 個別設定(1)\r
4100                                                         p.setOptString(null);                                           // フリーオプション初期化\r
4101                                                         p.loadAreaCode();                                                       // 放送エリア情報取得\r
4102                                                         p.loadCenter(p.getSelectedCode(),false);        // 放送局情報取得\r
4103                                                         p.setSortedCRlist();                                            // 有効放送局だけよりわける\r
4104                                                 }\r
4105                                                 catch (Exception e) {\r
4106                                                         stwin.appendError(FUNCID+"ベース情報の取得に失敗しました.");\r
4107                                                         e.printStackTrace();\r
4108                                                 }\r
4109                                         }\r
4110                                 }\r
4111                                 \r
4112                                 // 共通設定部分の一斉更新\r
4113                                 //setSettingProgPluginAll(env);\r
4114                                 \r
4115                                 if ( env.getUseSyobocal() ) {\r
4116                                         TVProgram syobo = tvprograms.getSyobo();\r
4117                                         if ( syobo != null ) {\r
4118                                                 stwin.appendMessage(FUNCID+"しょぼかるを初期化します.");\r
4119                                                 setSettingProgPlugin(syobo,env);                                // 他からも呼び出される部分だけ分離\r
4120                                                 syobo.setUserAgent("tainavi");\r
4121                                                 syobo.setOptString(null);                                               // フリーオプション初期化\r
4122                                                 syobo.loadCenter(syobo.getSelectedCode(), false);\r
4123                                         }\r
4124                                 }\r
4125                                 \r
4126                                 return null;\r
4127                         }\r
4128                         \r
4129                         @Override\r
4130                         protected void doFinally() {\r
4131                         }\r
4132                 }.execute();\r
4133         }\r
4134         protected void setSettingProgPluginAll(Env nEnv) {\r
4135                 // 通常\r
4136                 setSettingProgPlugin(tvprograms.getTvProgPlugin(null),nEnv);\r
4137                 setSettingProgPlugin(tvprograms.getCsProgPlugin(null),nEnv);\r
4138                 setSettingProgPlugin(tvprograms.getCs2ProgPlugin(null),nEnv);\r
4139                 //setSettingProgPlugin(tvprograms.getRadioProgPlugin(null),nEnv);\r
4140                 setSettingProgPlugin(tvprograms.getSyobo(),nEnv);\r
4141                 \r
4142                 // しょぼかるは特殊\r
4143                 tvprograms.getSyobo().setUserAgent("tainavi");\r
4144                 // 検索結果も特殊\r
4145                 tvprograms.getSearched().setResultBufferMax(nEnv.getSearchResultBufferMax());\r
4146         }\r
4147         protected void setSettingProgPlugin(TVProgram p, Env nEnv) {\r
4148                 if ( p == null ) {\r
4149                         return;\r
4150                 }               \r
4151                 p.setUserAgent(nEnv.getUserAgent());\r
4152                 p.setProgDir(nEnv.getProgDir());\r
4153                 p.setCacheExpired((enableWebAccess)?(nEnv.getCacheTimeLimit()):(0));\r
4154                 p.setContinueTomorrow(nEnv.getContinueTomorrow());\r
4155                 p.setExpandTo8(nEnv.getExpandTo8());\r
4156                 //p.setUseDetailCache(nEnv.getUseDetailCache());\r
4157                 p.setUseDetailCache(false);\r
4158                 p.setSplitEpno(nEnv.getSplitEpno());\r
4159         }\r
4160         \r
4161         /**\r
4162          * staticで持っている共通設定の更新\r
4163          */\r
4164         protected void setSettingProgPluginCommon(Env nEnv) {\r
4165                 \r
4166                 if ( nEnv.getUseProxy() && (nEnv.getProxyAddr().length() > 0 && nEnv.getProxyPort().length() > 0) ) {\r
4167                         stwin.appendMessage("+Web番組表へのアクセスにProxyが設定されています: "+nEnv.getProxyAddr()+":"+nEnv.getProxyPort());\r
4168                         TVProgramUtils.setProxy(nEnv.getProxyAddr(),nEnv.getProxyPort());\r
4169                 }\r
4170                 else {\r
4171                         TVProgramUtils.setProxy(null,null);\r
4172                 }\r
4173                 \r
4174                 TVProgramUtils.setProgressArea(stwin);\r
4175                 TVProgramUtils.setChConv(chconv);\r
4176         }\r
4177         \r
4178         //\r
4179         private void initMpList() {\r
4180                 //mpList = new MarkedProgramList();                     // 検索結果リスト\r
4181                 mpList.setHistoryOnlyUpdateOnce(env.getHistoryOnlyUpdateOnce());\r
4182                 mpList.setShowOnlyNonrepeated(env.getShowOnlyNonrepeated());\r
4183         }\r
4184         \r
4185         // L&FとFontを設定\r
4186         private void initLookAndFeelAndFont() {\r
4187 \r
4188                 try {\r
4189                         {\r
4190                                 vwlaf = new VWLookAndFeel();\r
4191                                 \r
4192                                 String lafname = vwlaf.update(env.getLookAndFeel());\r
4193                                 if ( lafname != null && ! lafname.equals(env.getLookAndFeel())) {\r
4194                                         env.setLookAndFeel(lafname);\r
4195                                 }\r
4196                                 \r
4197                                 if ( CommonUtils.isMac() ) {\r
4198                                         UIManager.getDefaults().put("Table.gridColor", new Color(128,128,128));\r
4199                                         //UIManager.getDefaults().put("Table.selectionBackground", new Color(182,207,229));\r
4200                                         //UIManager.getDefaults().put("Table.selectionForeground", new Color(0,0,0));\r
4201                                 }\r
4202                         }\r
4203                         \r
4204                         {\r
4205                                 vwfont = new VWFont();\r
4206                                 \r
4207                                 String fname = vwfont.update(env.getFontName(),env.getFontSize());\r
4208                                 if ( fname != null && ! fname.equals(env.getFontName())) {\r
4209                                         env.setFontName(fname);\r
4210                                 }\r
4211                         }\r
4212                 }\r
4213                 catch ( Exception e ) {\r
4214                         // 落ちられると困るからトラップしておこうぜ\r
4215                         e.printStackTrace();\r
4216                 }\r
4217         }\r
4218         \r
4219         // L&FやFontを変えたらコンポーネントに通知が必要\r
4220         protected void updateComponentTreeUI() {\r
4221                 try {\r
4222                         SwingUtilities.updateComponentTreeUI(this);\r
4223                         SwingUtilities.updateComponentTreeUI(stwin);\r
4224                         SwingUtilities.updateComponentTreeUI(mwin);\r
4225                         SwingUtilities.updateComponentTreeUI(pcwin);\r
4226                         SwingUtilities.updateComponentTreeUI(rdialog);\r
4227                         SwingUtilities.updateComponentTreeUI(ccwin);\r
4228                 }\r
4229                 catch ( Exception e ) {\r
4230                         // 落ちられると困るからトラップしておこうぜ\r
4231                         e.printStackTrace();\r
4232                 }\r
4233         }\r
4234 \r
4235         // ツールチップの表示遅延時間を設定する\r
4236         private void setTooltipDelay() {\r
4237                 ToolTipManager tp = ToolTipManager.sharedInstance();\r
4238                 tp.setInitialDelay(env.getTooltipInitialDelay()*100);\r
4239                 tp.setDismissDelay(env.getTooltipDismissDelay()*100);\r
4240         }\r
4241         \r
4242         /**\r
4243          * \r
4244          * @return true:前回終了時の設定がある場合\r
4245          */\r
4246         private boolean buildMainWindow() {\r
4247                 //\r
4248                 mainWindow.addToolBar(toolBar);\r
4249                 mainWindow.addStatusArea(mwin);\r
4250                 \r
4251                 mainWindow.addTab(listed, MWinTab.LISTED);\r
4252                 mainWindow.addTab(paper, MWinTab.PAPER);\r
4253                 mainWindow.addTab(reserved, MWinTab.RSVED);\r
4254                 mainWindow.addTab(recorded, MWinTab.RECED);\r
4255                 mainWindow.addTab(autores, MWinTab.AUTORES);\r
4256                 mainWindow.addTab(setting, MWinTab.SETTING);\r
4257                 mainWindow.addTab(recsetting, MWinTab.RECSET);\r
4258                 mainWindow.addTab(chsetting, MWinTab.CHSET);\r
4259                 mainWindow.addTab(chsortsetting, MWinTab.CHSORT);\r
4260                 mainWindow.addTab(chconvsetting, MWinTab.CHCONV);\r
4261                 mainWindow.addTab(chdatsetting, MWinTab.CHDAT);\r
4262                 \r
4263                 //新聞描画枠のリセット\r
4264                 paper.clearPanel();\r
4265                 paper.buildMainViewByDate();\r
4266                 \r
4267                 // サイドツリーのデフォルトノードの選択\r
4268                 paper.selectTreeDefault();\r
4269                 listed.selectTreeDefault();\r
4270                 \r
4271                 if ( recInfoList.size() > 0 ) {\r
4272                         // 前回終了時設定が存在する場合\r
4273                         \r
4274                         // 開いていたタブ\r
4275                         mainWindow.setShowSettingTabs(bounds.getShowSettingTabs());\r
4276                         \r
4277                         // ステータスエリアの高さ\r
4278                         mwin.setRows(bounds.getStatusRows());\r
4279                         \r
4280                         return false;\r
4281                 }\r
4282                 \r
4283                 // 前回終了時設定が存在しない場合\r
4284                 return true;\r
4285         }\r
4286         \r
4287         private void ShowInitTab() {\r
4288                 \r
4289                 // いったん無選択状態にしてから\r
4290                 mainWindow.setSelectedTab(null);\r
4291                 \r
4292                 if ( recInfoList.size() <= 0 ) {\r
4293                         // 設定が存在しない場合\r
4294                         mainWindow.setSelectedTab(MWinTab.RECSET);\r
4295                 }\r
4296                 else {\r
4297                         // 設定が存在する場合\r
4298                         mainWindow.setSelectedTab(MWinTab.getAt(bounds.getSelectedTab()));\r
4299                 }\r
4300         }\r
4301         \r
4302         //\r
4303         private void setInitBounds() {\r
4304                 // ウィンドウのサイズと表示位置を設定する\r
4305                 Rectangle window = bounds.getWinRectangle();\r
4306                 if (bounds.isLoaded()) {\r
4307                         // 設定ファイルを読み込んであったらそれを設定する\r
4308                         System.out.println(DBGID+"set bounds "+window);\r
4309                         this.setBounds(window.x, window.y, window.width, window.height);\r
4310                 }\r
4311                 else {\r
4312                         // 設定ファイルがなければ自動設定する\r
4313                         Rectangle screen = this.getGraphicsConfiguration().getBounds();\r
4314                         int x = 0;\r
4315                         int w = window.width;\r
4316                         if (window.width > screen.width) {\r
4317                                 x = 0;\r
4318                                 w = screen.width;\r
4319                         }\r
4320                         else {\r
4321                                 x = (screen.width - window.width)/2;\r
4322                         }\r
4323                         int y = 0;\r
4324                         int h = window.height;\r
4325                         if (window.height > screen.height) {\r
4326                                 y = 0;\r
4327                                 h = screen.height;\r
4328                         }\r
4329                         else {\r
4330                                 y = (screen.height - window.height)/2;\r
4331                         }\r
4332                         this.setBounds(x, y, w, h);\r
4333                 }\r
4334         }\r
4335 \r
4336         /**\r
4337          * <P>ステータスエリアを隠す\r
4338          * {@link VWMainWindow#setStatusVisible(boolean)}の置き換え\r
4339          */\r
4340         private void setStatusVisible(boolean b) {\r
4341                 \r
4342                 if (b) {\r
4343                         listed.setDetailVisible(true);\r
4344                         paper.setDetailVisible(true);\r
4345                         MWinSetVisible(true);\r
4346                 }\r
4347                 else {\r
4348                         listed.setDetailVisible(false);\r
4349                         paper.setDetailVisible(false);\r
4350                         MWinSetVisible(false);\r
4351                 }\r
4352         }\r
4353         \r
4354         // フルスクリーンモードをトグル切り替え\r
4355         private Dimension f_dim;\r
4356         private Point f_pnt;\r
4357         private int divloc_l = 0;\r
4358         private int divloc_p = 0;\r
4359         \r
4360         private void setFullScreen(boolean b) {\r
4361                 \r
4362                 if ( b == true ) {\r
4363                         // 枠の撤去\r
4364                         this.dispose();\r
4365                         this.setUndecorated(true);\r
4366                         this.setVisible(true);\r
4367                         \r
4368                         //全画面表示へ\r
4369                         Toolkit tk = getToolkit();\r
4370                         Insets in = tk.getScreenInsets(getGraphicsConfiguration());\r
4371                         Dimension d = tk.getScreenSize();\r
4372                         f_dim = this.getSize();\r
4373                         f_pnt = this.getLocation();\r
4374                         this.setBounds(in.left, in.top, d.width-(in.left+in.right), d.height-(in.top+in.bottom));\r
4375                         \r
4376                         divloc_l = bounds.getTreeWidth();\r
4377                         divloc_p = bounds.getTreeWidthPaper();\r
4378                         \r
4379                         // ツリーを閉じる\r
4380                         paper.setCollapseTree();\r
4381                         listed.setCollapseTree();\r
4382                 }\r
4383                 else {\r
4384                         if ( f_pnt != null && f_dim != null ) { // 起動直後などは値がないですしね\r
4385                                 \r
4386                                 // 枠の復帰\r
4387                                 this.dispose();\r
4388                                 this.setUndecorated(false);\r
4389                                 this.setVisible(true);\r
4390                                 \r
4391                                 //全画面表示終了\r
4392                                 this.setBounds(f_pnt.x, f_pnt.y, f_dim.width, f_dim.height);\r
4393                                 \r
4394                                 bounds.setTreeWidth(divloc_l);\r
4395                                 bounds.setTreeWidthPaper(divloc_p);\r
4396                                 \r
4397                                 // ツリーの幅を元に戻す\r
4398                                 paper.setExpandTree();\r
4399                                 listed.setExpandTree();\r
4400                         }\r
4401                 }\r
4402         }\r
4403         \r
4404         // タイトルバー\r
4405         private void setTitleBar() {\r
4406                 MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();\r
4407                 MemoryUsage heapUsage = mbean.getHeapMemoryUsage();\r
4408                 \r
4409                 this.setTitle(\r
4410                                 String.format(\r
4411                                                 "%s - %s - Memory Usage Max:%dM Committed:%dM Used:%dM - FrameBuffer Status:%s",\r
4412                                                 VersionInfo.getVersion(),\r
4413                                                 CommonUtils.getDateTime(0),\r
4414                                                 heapUsage.getMax()/(1024*1024),\r
4415                                                 heapUsage.getCommitted()/(1024*1024),\r
4416                                                 heapUsage.getUsed()/(1024*1024),\r
4417                                                 (paper!=null)?(paper.getFrameBufferStatus()):("N/A")\r
4418                                 )\r
4419                 );\r
4420         }\r
4421         \r
4422         @Override\r
4423         public void timerRised(VWTimerRiseEvent e) {\r
4424                 if (env.getDebug()) System.out.println("Timer Rised: now="+CommonUtils.getDateTimeYMDx(e.getCalendar()));\r
4425                 setTitleBar();\r
4426         }\r
4427         \r
4428         // 終了処理関連\r
4429         private void ExitOnClose() {\r
4430                 // 座標・サイズ\r
4431                 if ( ! this.toolBar.isFullScreen()) {\r
4432                         Rectangle r = this.getBounds();\r
4433                         bounds.setWinRectangle(r);\r
4434                 }\r
4435                 else {\r
4436                         Rectangle r = new Rectangle();\r
4437                         r.x = this.f_pnt.x;\r
4438                         r.y = this.f_pnt.y;\r
4439                         r.width = this.f_dim.width;\r
4440                         r.height = this.f_dim.height;\r
4441                         bounds.setWinRectangle(r);\r
4442                 }\r
4443                 listed.copyColumnWidth();\r
4444                 reserved.copyColumnWidth();\r
4445                 \r
4446                 bounds.setStatusRows(mwin.getRows());\r
4447 \r
4448                 // 動作状態\r
4449                 bounds.setSelectedTab(mainWindow.getSelectedTab().getIndex());\r
4450                 bounds.setShowSettingTabs(mainWindow.getShowSettingTabs());\r
4451                 bounds.setSelectedRecorderId(toolBar.getSelectedRecorder());\r
4452                 bounds.setShowStatus(toolBar.isStatusShown());\r
4453                 \r
4454                 // 保存する\r
4455                 bounds.save();\r
4456                 \r
4457                 // ツリーの展開状態の保存\r
4458                 listed.saveTreeExpansion();\r
4459                 paper.saveTreeExpansion();\r
4460         }\r
4461         \r
4462 \r
4463         /*******************************************************************************\r
4464          * main()\r
4465          ******************************************************************************/\r
4466         \r
4467         // 初期化が完了したら立てる\r
4468         private static boolean initialized = false;\r
4469         private static Viewer myClass = null;\r
4470         \r
4471         /**\r
4472          * めいーん\r
4473          * @param args\r
4474          * @throws NoSuchAlgorithmException\r
4475          * @version 今まで初期化を行ってからウィンドウを作成していたが<BR>\r
4476          * 途中で例外が起こるとダンマリの上にゾンビになってたりとヒドかったので<BR>\r
4477          * 先にウィンドウを作成してから初期化を行うように変えました\r
4478          * @throws InterruptedException \r
4479          * @throws InvocationTargetException \r
4480          */\r
4481         public static void main(final String[] args) throws NoSuchAlgorithmException, InvocationTargetException, InterruptedException {\r
4482                 \r
4483                 if ( myClass != null ) {\r
4484                         // 既に起動していたらフォアグラウンドにする\r
4485                         SwingUtilities.invokeAndWait(new Runnable() {\r
4486                                 @Override\r
4487                                 public void run() {\r
4488                                         // うーん、いいのかこのコード?\r
4489                                         myClass.setVisible(true);\r
4490                                         myClass.setState(Frame.NORMAL);\r
4491                                 }\r
4492                         });\r
4493                         return;\r
4494                 }\r
4495                 \r
4496                 SwingUtilities.invokeLater(new Runnable() {\r
4497                         public void run() {\r
4498                                 \r
4499                                 final Viewer thisClass = myClass = new Viewer(args);\r
4500                                 \r
4501                                 thisClass.addComponentListener(new ComponentAdapter() {\r
4502                                         @Override\r
4503                                         public void componentShown(ComponentEvent e) {\r
4504                                                 \r
4505                                                 // 一回実行したらもういらないよ\r
4506                                                 thisClass.removeComponentListener(this);\r
4507                                                 \r
4508                                                 // 初期化するよ\r
4509                                                 thisClass.initialize(args);\r
4510                                                 \r
4511                                         }\r
4512                                 });\r
4513                                 \r
4514                                 thisClass.setVisible(true);\r
4515                         }\r
4516                 });\r
4517         }\r
4518 \r
4519         \r
4520         \r
4521         /*******************************************************************************\r
4522          * コンストラクタ\r
4523          ******************************************************************************/\r
4524 \r
4525         /**\r
4526          * デフォルトコンストラクタ\r
4527          */\r
4528         public Viewer(final String[] args) {\r
4529                 \r
4530                 super();\r
4531                 \r
4532                 env.loadText();\r
4533                 bounds.loadText();\r
4534                 \r
4535                 \r
4536                 // 初期化が終わるまでは閉じられないよ → どうせステータスウィンドウにブロックされて操作できない\r
4537                 //setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);\r
4538                 //setResizable(false);\r
4539                 \r
4540                 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
4541                 \r
4542                 setTitleBar();\r
4543                 \r
4544                 try {\r
4545                         Image image = ImageIO.read(new File(ICONFILE_TAINAVI));\r
4546                         setIconImage(image);\r
4547                 }\r
4548                 catch (IOException e) {\r
4549                         StdAppendError("[ERROR] アイコンが設定できない: "+e.toString());\r
4550                 }\r
4551                 \r
4552                 JLabel jLabel_splash_img = new JLabel(new ImageIcon("splash.gif"));\r
4553                 jLabel_splash_img.setPreferredSize(new Dimension(400,300));\r
4554                 //getContentPane().setLayout(new BorderLayout());\r
4555                 getContentPane().add(jLabel_splash_img, BorderLayout.CENTER);\r
4556                 pack();\r
4557                 \r
4558                 setLocationRelativeTo(null);    // 画面の真ん中に\r
4559                 \r
4560                 // SwingLocker共有設定\r
4561                 SwingLocker.setOwner(this);\r
4562                 \r
4563                 // とりあえずルックアンドフィールはリセットしておかないとだめっぽいよ\r
4564                 initLookAndFeelAndFont();\r
4565                 updateComponentTreeUI();\r
4566         }\r
4567         \r
4568         // 初期化をバックグラウンドで行う\r
4569         private void initialize(final String[] args) {\r
4570                 \r
4571                 StWinClear();\r
4572                 \r
4573                 // 初期化処理はバックグラウンドで行う\r
4574                 new SwingBackgroundWorker(false) {\r
4575                         \r
4576                         @Override\r
4577                         protected Object doWorks() throws Exception {\r
4578                                 \r
4579                                 TatCount tc = new TatCount();\r
4580                                 \r
4581                                 // 初期化処理\r
4582                                 _initialize(args);\r
4583                                 \r
4584                                 // 終わったら閉じられるようにするよ\r
4585                                 //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\r
4586                                 //setResizable(true);\r
4587 \r
4588                                 stwin.append("");\r
4589                                 stwin.appendMessage(String.format("【タイニー番組ナビゲータが起動しました】 所要時間: %.2f秒",tc.end()));\r
4590                                 return null;\r
4591                         }\r
4592                         \r
4593                         @Override\r
4594                         protected void doFinally() {\r
4595                                 if ( ! initialized ) System.err.println("[ERROR][鯛ナビ] 【致命的エラー】 初期化処理を行っていたスレッドが異常終了しました。");\r
4596                                 stwin.setClosingEnabled(false);\r
4597                                 CommonUtils.milSleep(OPENING_WIAT);\r
4598                                 StWinSetVisible(false);\r
4599                         }\r
4600                 }.execute();\r
4601                 \r
4602                 StWinSetLocationUnder(this);\r
4603                 StWinSetVisible(true);\r
4604         }\r
4605         \r
4606         // 初期化の本体\r
4607         private void _initialize(final String[] args) {\r
4608                 \r
4609                 // コマンドライン引数を処理する\r
4610                 procArgs(args);\r
4611                 \r
4612                 // ログ出力を設定する(Windowsの場合は文字コードをMS932にする) →DOS窓を殺したので終了\r
4613                 System.setOut(new DebugPrintStream(System.out,LOG_FILE,logging));\r
4614                 System.setErr(new DebugPrintStream(System.err,LOG_FILE,logging));\r
4615                 \r
4616                 // 起動メッセージ\r
4617                 StdAppendMessage("================================================================================");\r
4618                 StdAppendMessage("以下のメッセージは無視してください(原因調査中)");\r
4619                 StdAppendMessage("Exception occurred during event dispatching:");\r
4620                 StdAppendMessage("      java.lang.NullPointerException");\r
4621                 StdAppendMessage("              at javax.swing.plaf.basic.BasicScrollBarUI.layoutHScrollbar(Unknown Source)");\r
4622                 StdAppendMessage("              (以下略)");\r
4623                 StdAppendMessage("================================================================================");\r
4624                 stwin.appendMessage(CommonUtils.getDateTime(0));\r
4625                 stwin.appendMessage(String.format("タイニー番組ナビゲータが起動を開始しました(VersionInfo:%s on %s)",VersionInfo.getVersion(),VersionInfo.getEnvironment()));\r
4626                 \r
4627                 // 起動時にアップデートを確認する\r
4628                 chkVerUp();\r
4629                 \r
4630                 try {\r
4631                         // メインの環境設定ファイルを読み込む\r
4632                         loadEnvfile();\r
4633                         \r
4634                         // 二重起動防止\r
4635                         chkDualBoot();\r
4636                         \r
4637                         // その他の環境設定ファイルを読み込む\r
4638                         procEnvs();\r
4639                         \r
4640                         if ( onlyLoadProgram ) {\r
4641                                 if ( ! isOLPExpired(4) ) {\r
4642                                         CommonUtils.milSleep(3000);\r
4643                                         System.exit(1);\r
4644                                 }\r
4645                                 // プラグインのロード\r
4646                                 loadProgPlugins();\r
4647                                 // プラグインの初期化\r
4648                                 setSelectedProgPlugin();\r
4649                                 initProgPluginAll();\r
4650                                 // 検索結果リストの初期化(loadTVProgram()中で使うので)\r
4651                                 initMpList();\r
4652                                 // データのロード\r
4653                                 loadTVProgram(true,LoadFor.ALL);\r
4654                                 stwin.appendMessage("番組表を取得したので終了します");\r
4655                                 CommonUtils.milSleep(3000);\r
4656                                 System.exit(1);\r
4657                         }\r
4658                         \r
4659                         // プラグインのロード\r
4660                         loadProgPlugins();\r
4661                         loadRecPlugins();\r
4662 \r
4663                         // プラグインの初期化\r
4664                         setSelectedProgPlugin();\r
4665                         initProgPluginAll();\r
4666 \r
4667                         initRecPluginAll();\r
4668 \r
4669                         // WOL指定があったなら\r
4670                         if ( runRecWakeup ) {\r
4671                                 doRecWakeup();\r
4672                         }\r
4673 \r
4674                         // 検索結果リストの初期化(loadTVProgram()中で使うので)\r
4675                         initMpList();\r
4676                         \r
4677                         // データのロード\r
4678                         loadTVProgram(false,LoadFor.ALL);\r
4679                         \r
4680                         // 放送局の並び順もロード\r
4681                         chsort.load();\r
4682                         \r
4683                         loadRdReserve(runRecLoad, null);\r
4684                 }\r
4685                 catch ( Exception e ) {\r
4686                         System.err.println("【致命的エラー】設定の初期化に失敗しました");\r
4687                         e.printStackTrace();\r
4688                         System.exit(1);\r
4689                 }\r
4690                 \r
4691                 // 背景色設定ダイアログにフォント名の一覧を設定する\r
4692                 pcwin.setFontList(vwfont);\r
4693                 \r
4694                 // (新聞形式の)ツールチップの表示時間を変更する\r
4695                 setTooltipDelay();\r
4696 \r
4697                 boolean firstRun = true;\r
4698                 try {\r
4699                         // メインウィンドウの作成\r
4700                         mainWindow = new VWMainWindow();\r
4701                 \r
4702                         // 内部クラスのインスタンス生成\r
4703                         toolBar = new VWToolBar();\r
4704                         listed = new VWListedView();\r
4705                         paper = new VWPaperView();\r
4706                         reserved = new VWReserveListView();\r
4707                         recorded = new VWRecordedListView();\r
4708                         autores = new VWAutoReserveListView();\r
4709                         setting = new VWSettingView();\r
4710                         recsetting = new VWRecorderSettingView();\r
4711                         chsetting = new VWChannelSettingView();\r
4712                         chdatsetting = new VWChannelDatSettingView();\r
4713                         chsortsetting = new VWChannelSortView();\r
4714                         chconvsetting = new VWChannelConvertView();\r
4715                         \r
4716                         // 設定のほにゃらら\r
4717                         toolBar.setDebug(env.getDebug());\r
4718                         autores.setDebug(env.getDebug());\r
4719 \r
4720                         // ページャーの設定\r
4721                         toolBar.setPagerItems();\r
4722                         \r
4723                         // ウィンドウを構築\r
4724                         firstRun = buildMainWindow();\r
4725                         \r
4726                         // ステータスエリアを開く\r
4727                         setStatusVisible(bounds.getShowStatus());\r
4728                 }\r
4729                 catch ( Exception e ) {\r
4730                         System.err.println("【致命的エラー】ウィンドウの構築に失敗しました");\r
4731                         e.printStackTrace();\r
4732                         System.exit(1);\r
4733                 }\r
4734                 \r
4735                 // ★★★★★★★★★★\r
4736                 //int x = 2/0;  // サブスレッドの突然死のトラップを確認するためのコード\r
4737                 // ★★★★★★★★★★\r
4738                 \r
4739                 // トレイアイコンを作る\r
4740                 getTrayIcon();\r
4741                 setTrayIconVisible(env.getShowSysTray());\r
4742                 \r
4743                 // ウィンドウを閉じたときの処理\r
4744                 setXButtonAction(env.getShowSysTray() && env.getHideToTray());\r
4745                 \r
4746                 // ウィンドウ操作のリスナー登録\r
4747                 this.addWindowListener(new WindowAdapter() {\r
4748                         // ウィンドウを最小化したときの処理\r
4749                         @Override\r
4750                         public void windowIconified(WindowEvent e) {\r
4751                                 HideToTray();\r
4752                         }\r
4753                 \r
4754                         // ウィンドウを閉じたときの処理\r
4755                         @Override\r
4756                         public void windowClosing(WindowEvent e) {\r
4757                                 ExitOnClose();\r
4758                         }\r
4759                 });\r
4760                 \r
4761                 // タブを選択\r
4762                 ShowInitTab();\r
4763                 \r
4764                 // 初回起動時はレコーダの登録を促す\r
4765                 if (firstRun) {\r
4766                         Container cp = getContentPane();\r
4767                         JOptionPane.showMessageDialog(cp, "レコーダが登録されていません。\n最初に登録を行ってください。\n番組表だけを使いたい場合は、\nNULLプラグインを登録してください。");\r
4768                 }\r
4769                 \r
4770                 // メインウィンドウをスプラッシュからコンポーネントに入れ替える\r
4771                 this.setVisible(false);\r
4772                 this.setContentPane(mainWindow);\r
4773                 setInitBounds();\r
4774                 this.setVisible(true);\r
4775                 \r
4776                 // タイトル更新\r
4777                 setTitleBar();\r
4778                 \r
4779                 // [ツールバー/共通] レコーダ情報変更\r
4780                 toolBar.addHDDRecorderChangeListener(autores);\r
4781                 \r
4782                 // [ツールバー/レコーダ選択] 自動予約一覧\r
4783                 toolBar.addHDDRecorderSelectionListener(autores);\r
4784 \r
4785 \r
4786                 // レコーダ選択イベントキック\r
4787                 toolBar.setSelectedRecorder(bounds.getSelectedRecorderId());\r
4788                 \r
4789                 // [タイマー] タイトルバー更新/リスト形式の現在時刻ノード/新聞形式の現在時刻ノード\r
4790                 timer_now.addVWTimerRiseListener(this);\r
4791                 timer_now.addVWTimerRiseListener(listed);\r
4792                 timer_now.addVWTimerRiseListener(paper);\r
4793                 \r
4794                 // タイマー起動\r
4795                 timer_now.start();\r
4796                 \r
4797                 // メッセージだ\r
4798                 mwin.appendMessage(String.format("タイニー番組ナビゲータが起動しました (VersionInfo:%s on %s)",VersionInfo.getVersion(),VersionInfo.getEnvironment()));\r
4799                 \r
4800                 initialized = true;\r
4801         }\r
4802 }\r