OSDN Git Service

0c76f79457d158b70fb836bc86ab479d7642375a
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / AbsPaperView.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.Cursor;\r
8 import java.awt.Dimension;\r
9 import java.awt.Point;\r
10 import java.awt.Rectangle;\r
11 import java.awt.Robot;\r
12 import java.awt.event.ComponentAdapter;\r
13 import java.awt.event.ComponentEvent;\r
14 import java.awt.event.ComponentListener;\r
15 import java.awt.event.MouseAdapter;\r
16 import java.awt.event.MouseEvent;\r
17 import java.awt.event.MouseListener;\r
18 import java.io.File;\r
19 import java.util.ArrayList;\r
20 import java.util.Calendar;\r
21 import java.util.GregorianCalendar;\r
22 \r
23 import javax.swing.JComponent;\r
24 import javax.swing.JLabel;\r
25 import javax.swing.JLayeredPane;\r
26 import javax.swing.JPanel;\r
27 import javax.swing.JScrollPane;\r
28 import javax.swing.JSplitPane;\r
29 import javax.swing.JTree;\r
30 import javax.swing.JViewport;\r
31 import javax.swing.SpringLayout;\r
32 import javax.swing.border.LineBorder;\r
33 import javax.swing.event.MouseInputListener;\r
34 import javax.swing.event.TreeExpansionEvent;\r
35 import javax.swing.event.TreeExpansionListener;\r
36 import javax.swing.event.TreeSelectionEvent;\r
37 import javax.swing.event.TreeSelectionListener;\r
38 import javax.swing.tree.DefaultMutableTreeNode;\r
39 import javax.swing.tree.TreeNode;\r
40 import javax.swing.tree.TreePath;\r
41 import javax.swing.tree.TreeSelectionModel;\r
42 import javax.swing.tree.DefaultTreeModel;\r
43 \r
44 import tainavi.TVProgram.ProgType;\r
45 import tainavi.TVProgramIterator.IterationType;\r
46 import tainavi.VWMainWindow.MWinTab;\r
47 \r
48 \r
49 /**\r
50  * 新聞形式タブのクラス\r
51  * @since 3.15.4β {@link Viewer}から分離\r
52  */\r
53 public abstract class AbsPaperView extends JPanel implements TickTimerListener,HDDRecorderListener {\r
54 \r
55         private static final long serialVersionUID = 1L;\r
56 \r
57         public static String getViewName() { return "新聞形式"; }\r
58         \r
59         public void setDebug(boolean b) { debug = b; }\r
60         private static boolean debug = false;\r
61         \r
62         \r
63         /*******************************************************************************\r
64          * 抽象メソッド\r
65          ******************************************************************************/\r
66         \r
67         protected abstract Env getEnv();\r
68         protected abstract Bounds getBoundsEnv();\r
69         protected abstract PaperColorsMap getPaperColorMap();\r
70         protected abstract ChannelSort getChannelSort();\r
71         \r
72         protected abstract TVProgramList getTVProgramList();\r
73         protected abstract HDDRecorderList getRecorderList();\r
74         \r
75         protected abstract StatusWindow getStWin(); \r
76         protected abstract StatusTextArea getMWin();\r
77         \r
78         protected abstract AbsReserveDialog getReserveDialog();\r
79         protected abstract Component getParentComponent();\r
80         \r
81         protected abstract void ringBeep();\r
82         \r
83         // クラス内のイベントから呼び出されるもの\r
84         \r
85         /**\r
86          * タブが開いた\r
87          */\r
88         protected abstract void onShown();\r
89         /**\r
90          * タブが閉じた\r
91          */\r
92         protected abstract void onHidden();\r
93         \r
94         /**\r
95          * マウス右クリックメニューを表示する\r
96          */\r
97         protected abstract void showPopupForTraceProgram(\r
98                         final JComponent comp,\r
99                         final ProgDetailList tvd, final String keyword, final int threshold,\r
100                         final int x, final int y, final String clickedDateTime\r
101                         );\r
102 \r
103         /**\r
104          * 予約マーク・予約枠を更新してほしい\r
105          */\r
106         protected abstract void updateReserveDisplay();\r
107         \r
108         /**\r
109          * ピックアップに追加してほしい\r
110          */\r
111         protected abstract void addToPickup(final ProgDetailList tvd);\r
112         \r
113         protected abstract boolean isTabSelected(MWinTab tab);\r
114         protected abstract void setSelectedTab(MWinTab tab);\r
115 \r
116         protected abstract boolean isFullScreen();\r
117         /**\r
118          * ページャーコンボボックスを更新してほしい\r
119          */\r
120         protected abstract void setPagerEnabled(boolean b);\r
121         protected abstract int getPagerCount();\r
122         protected abstract int getSelectedPagerIndex();\r
123         protected abstract void setSelectedPagerIndex(int idx);\r
124         protected abstract void setPagerItems(TVProgramIterator pli, int curindex);\r
125 \r
126         protected abstract String getExtensionMark(ProgDetailList tvd);\r
127         protected abstract String getOptionMark(ProgDetailList tvd);\r
128         protected abstract String getPostfixMark(ProgDetailList tvd);\r
129 \r
130         /**\r
131          * ツリーペーンの幅の変更を保存してほしい\r
132          */\r
133         protected abstract void setDividerEnvs(int loc);\r
134         \r
135         \r
136         \r
137         /*******************************************************************************\r
138          * 呼び出し元から引き継いだもの\r
139          ******************************************************************************/\r
140         \r
141         // オブジェクト\r
142         private final Env env = getEnv();\r
143         private final Bounds bounds = getBoundsEnv();\r
144         private final PaperColorsMap pColors = getPaperColorMap();\r
145         private final ChannelSort chsort = getChannelSort();\r
146         \r
147         private final TVProgramList tvprograms = getTVProgramList();\r
148         private final HDDRecorderList recorders = getRecorderList();\r
149 \r
150         private final StatusWindow StWin = getStWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
151         private final StatusTextArea MWin = getMWin();                  // これは起動時に作成されたまま変更されないオブジェクト\r
152         private final AbsReserveDialog rD = getReserveDialog(); // これは起動時に作成されたまま変更されないオブジェクト\r
153         \r
154         private final Component parent = getParentComponent();  // これは起動時に作成されたまま変更されないオブジェクト\r
155 \r
156         // メソッド\r
157         private void StdAppendMessage(String message) { System.out.println(message); }\r
158         private void StdAppendError(String message) { System.err.println(message); }\r
159         //private void StWinSetVisible(boolean b) { StWin.setVisible(b); }\r
160         //private void StWinSetLocationCenter(Component frame) { CommonSwingUtils.setLocationCenter(frame, (VWStatusWindow)StWin); }\r
161 \r
162         \r
163         \r
164         /*******************************************************************************\r
165          * 定数\r
166          ******************************************************************************/\r
167 \r
168         private final String MSGID = "["+getViewName()+"] ";\r
169         private final String ERRID = "[ERROR]"+MSGID;\r
170         private final String DBGID = "[DEBUG]"+MSGID;\r
171 \r
172         private final int DASHBORDER_LENGTH = 6;        // ダッシュの長さ\r
173         private final int DASHBORDER_SPACE = 4;         // ダッシュの間隔\r
174         \r
175         private static final String TreeExpRegFile_Paper = "env"+File.separator+"tree_expand_paper.xml";\r
176         \r
177         private static final int TIMEBAR_START = Viewer.TIMEBAR_START;\r
178 \r
179         //\r
180         private static final String TUNERLABEL_PICKUP = "PICKUP";\r
181         \r
182         // 定数ではないが\r
183         \r
184         /**\r
185          * 現在時刻追従スクロールで日付がかわったかどうかを確認するための情報を保持する\r
186          */\r
187         private String prevDT4Now = CommonUtils.getDate529(0,true);\r
188         private String prevDT4Tree = prevDT4Now;\r
189         \r
190         /**\r
191          * 番組枠フレームバッファのサイズ\r
192          */\r
193         private int framebuffersize = 512;\r
194         \r
195         \r
196         /*******************************************************************************\r
197          * 部品\r
198          ******************************************************************************/\r
199         \r
200         // コンポーネント\r
201         \r
202         private JDetailPanel jTextPane_detail = null;\r
203         private JSplitPane jSplitPane_view = null;\r
204         private JPanel jPanel_tree = null;\r
205         private JScrollPane jScrollPane_tree_top = null;\r
206         private JTreeLabel jLabel_tree = null;\r
207         private JScrollPane jScrollPane_tree = null;\r
208         private JTree jTree_tree = null;\r
209         private JScrollPane jScrollPane_space_main = null;\r
210         private JLayeredPane jLayeredPane_space_main_view = null;\r
211         private ArrayList<JTaggedLayeredPane> jLayeredPane_space_main_view_byDate = null;\r
212         private JLayeredPane jLayeredPane_space_main_view_byMakeshift = null; \r
213         private JPanel jPanel_space_top_view = null;\r
214         private JPanel jPanel_space_side_view = null;\r
215         private JViewport vport = null;\r
216         \r
217         private final JTimeline jLabel_timeline = new JTimeline();\r
218         \r
219         private DefaultMutableTreeNode paperRootNode = null;    // 新聞形式のツリー\r
220         private DefaultMutableTreeNode dateNode = null;\r
221         private DefaultMutableTreeNode dgNode = null;\r
222         private DefaultMutableTreeNode bsNode = null;\r
223         private DefaultMutableTreeNode csNode = null;\r
224         private DefaultMutableTreeNode centerNode = null;\r
225         private DefaultMutableTreeNode passedNode = null;\r
226 \r
227         private DefaultMutableTreeNode defaultNode = null;\r
228 \r
229         // コンポーネント以外\r
230         \r
231         // 番組枠をしまっておくバッファ(newが遅いので一回作ったら捨てない)\r
232         private ArrayList<JTXTButton> frameUsed = new ArrayList<JTXTButton>();                  // 画面に表示されている番組枠\r
233         private ArrayList<JTXTButton> frameUnused = new ArrayList<JTXTButton>();                // 未使用の予備\r
234         private ArrayList<JTXTButton> frameUsedByDate = new ArrayList<JTXTButton>();    // 高速描画時の日付別ペーンに表示されている番組枠。高速描画時も、過去ログはframeUsedが使われる\r
235 \r
236         // 予約枠をしまっておくバッファ(検索用)\r
237         private ArrayList<JRMLabel> reserveBorders = new ArrayList<JRMLabel>();\r
238         \r
239         // ツリーの展開状態の保存場所\r
240         TreeExpansionReg ter = null;\r
241         \r
242         DefaultMutableTreeNode nowNode = null;\r
243         \r
244         // 現在放送中のタイマー\r
245         private boolean timer_now_enabled = false;\r
246         \r
247         private IterationType cur_tuner = null;\r
248         \r
249         \r
250         // 予約待機枠と番組枠\r
251         private final DashBorder dborder = new DashBorder(Color.RED,env.getMatchedBorderThickness(),DASHBORDER_LENGTH,DASHBORDER_SPACE);\r
252         private final LineBorder lborder = new ChippedBorder(Color.BLACK,1);\r
253         \r
254         private float paperHeightZoom = 1.0F;\r
255         \r
256         /**\r
257          * 現在時刻線のオブジェクト\r
258          */\r
259         private class JTimeline extends JLabel {\r
260                 \r
261                 private static final long serialVersionUID = 1L;\r
262                 \r
263                 private int minpos = 0;\r
264                 \r
265                 public int setMinpos(int x, int minpos, float multiplier) {\r
266                         if ( minpos >= 0 ) {\r
267                                 this.minpos = minpos;\r
268                         }\r
269                         \r
270                         int timeline = Math.round(this.minpos*multiplier);\r
271                         this.setLocation(x,timeline);\r
272                         \r
273                         return timeline;\r
274                 }\r
275         }\r
276         \r
277         /*******************************************************************************\r
278          * コンストラクタ\r
279          ******************************************************************************/\r
280         \r
281         public AbsPaperView() {\r
282                 \r
283                 super();\r
284                 \r
285                 this.setLayout(new BorderLayout());\r
286                 this.add(getJTextPane_detail(), BorderLayout.PAGE_START);\r
287                 this.add(getJSplitPane_view(), BorderLayout.CENTER);\r
288                 \r
289                 // タブが開いたり閉じたりしたときの処理\r
290                 this.addComponentListener(cl_shownhidden);\r
291         }\r
292         \r
293         \r
294         \r
295         /*******************************************************************************\r
296          * アクション\r
297          ******************************************************************************/\r
298         \r
299         // 主に他のクラスから呼び出されるメソッド\r
300         \r
301         public String getFrameBufferStatus() { return String.format("%d/%d",frameUsed.size(),framebuffersize); }\r
302 \r
303         /**\r
304          * 現在日時表示にリセット\r
305          */\r
306         public void jumpToNow() {\r
307                 if ( nowNode != null ) {\r
308                         TreePath tp = new TreePath(nowNode.getPath());\r
309                         jTree_tree.setSelectionPath(null);\r
310                         jTree_tree.setSelectionPath(tp);\r
311                 }\r
312         }\r
313         \r
314         /**\r
315          * ツールバーから過去ログへのジャンプ\r
316          */\r
317         public boolean jumpToPassed(String passed) {\r
318                 \r
319                 // ページャーは効くよ\r
320                 if ( env.isPagerEnabled() ) {\r
321                         setPagerEnabled(true);\r
322                 }\r
323                 \r
324                 // タイマーは止める\r
325                 stopTimer();\r
326                 \r
327                 GregorianCalendar c = CommonUtils.getCalendar(passed);\r
328                 String adate = CommonUtils.getDate(c);\r
329                 \r
330                 // 指定日付に移動して放送局の位置を確認する\r
331                 TVProgramIterator pli = redrawByDateWithCenter(null,adate);\r
332                 if ( pli == null ) {\r
333                         // どちらにもない\r
334                         MWin.appendError(ERRID+"ジャンプ先の日付がみつかりません: "+adate);\r
335                         ringBeep();\r
336                         return false;\r
337                 }\r
338                 \r
339                 // 新聞形式に移動\r
340                 if ( ! isTabSelected(MWinTab.PAPER) ) {\r
341                         setSelectedTab(MWinTab.PAPER);\r
342                 }\r
343                 \r
344                 return true;\r
345         }\r
346         \r
347         /**\r
348          * リスト形式・本体予約一覧からの目的の番組へジャンプ\r
349          */\r
350         public boolean jumpToBangumi(String center, String startdt) {\r
351                 \r
352                 // ページャーは効くよ\r
353                 if ( env.isPagerEnabled() ) {\r
354                         setPagerEnabled(true);\r
355                 }\r
356                 \r
357                 // タイマーは止める\r
358                 stopTimer();\r
359 \r
360                 // 日付群\r
361                 GregorianCalendar c = CommonUtils.getCalendar(startdt);\r
362                 int hour = c.get(Calendar.HOUR_OF_DAY);\r
363                 int min  = c.get(Calendar.MINUTE);\r
364                 \r
365                 String adate = CommonUtils.getDate(c);\r
366                 String adate529 = CommonUtils.getDate529(c,true);\r
367                 \r
368                 \r
369                 // 指定日付に移動して放送局の位置を確認する\r
370                 TVProgramIterator pli = redrawByDateWithCenter(center,adate529);\r
371                 if ( pli == null ) {\r
372                         // どちらにもない\r
373                         MWin.appendError(ERRID+"ジャンプ先の日付がみつかりません: "+adate529);\r
374                         ringBeep();\r
375                         return false;\r
376                 }\r
377                 \r
378                 // 新聞形式に移動\r
379                 if ( ! isTabSelected(MWinTab.PAPER) ) {\r
380                         setSelectedTab(MWinTab.PAPER);\r
381                 }\r
382                 \r
383                 /*\r
384                  * マウスカーソル移動\r
385                  */\r
386                 \r
387                 // 横の列\r
388                 int crindex = pli.getIndex(center);\r
389                 if ( crindex == -1 ) {\r
390                         MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
391                         ringBeep();\r
392                         return false;\r
393                 }\r
394                 \r
395                 int x = 0;\r
396                 if ( env.isPagerEnabled() ) {\r
397                         int idx = env.getPageIndex(1+crindex);\r
398                         x = (crindex - idx*env.getCenterPerPage()) * bounds.getBangumiColumnWidth();\r
399                 }\r
400                 else {\r
401                         x = crindex * bounds.getBangumiColumnWidth();\r
402                 }\r
403                 \r
404                 // 縦の列\r
405                 int y = 0;\r
406                 if (adate529.equals(adate)) {\r
407                         if (hour < TIMEBAR_START) {\r
408                                 hour = TIMEBAR_START;\r
409                                 min = 0;\r
410                         }\r
411                 }\r
412                 else {\r
413                         hour += 24;\r
414                 }\r
415                 y = Math.round((float)((hour-TIMEBAR_START)*60+min)*bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
416                 \r
417                 // 新聞面を移動する\r
418                 {\r
419                         // Viewのサイズ変更をJavaまかせにすると実際に表示されるまで変更されないので明示的に変更しておく\r
420                         Dimension dm = vport.getView().getPreferredSize();\r
421                         vport.setViewSize(dm);\r
422                         \r
423                         // 一旦位置情報をリセットする\r
424                         Point pos = new Point(0, 0);\r
425                         //vport.setViewPosition(pos);\r
426                         \r
427                         Rectangle ra = vport.getViewRect();\r
428                         pos.x = x + bounds.getBangumiColumnWidth()/2 - ra.width/2;\r
429                         pos.y = y - ra.height/4;\r
430                         \r
431                         // ViewのサイズがViewPortのサイズより小さい場合はsetViewPosition()が正しく動作しないので0にする\r
432                         if (pos.x < 0 || dm.width < ra.width) {\r
433                                 pos.x=0;\r
434                         }\r
435                         else if ((dm.width - ra.width) < pos.x) {\r
436                                 pos.x = dm.width - ra.width;\r
437                         }\r
438                         \r
439                         if (pos.y < 0 || dm.height < ra.height)  {\r
440                                 pos.y=0;\r
441                         }\r
442                         else if ((dm.height - ra.height) < pos.y) {\r
443                                 pos.y = dm.height - ra.height;\r
444                         }\r
445                         \r
446                         vport.setViewPosition(pos);\r
447                 }\r
448                 \r
449                 // マウスカーソルを移動する\r
450                 {\r
451                         Point sc = vport.getLocationOnScreen();\r
452                         Point pos = vport.getViewPosition();\r
453                         \r
454                         Point loc = new Point();\r
455                         loc.x = sc.x + (x + bounds.getBangumiColumnWidth()/2) - pos.x;\r
456                         loc.y = sc.y + (y + Math.round(5*bounds.getPaperHeightMultiplier()*paperHeightZoom)) - pos.y;\r
457                         \r
458                         try {\r
459                                 Robot robo = new Robot();\r
460                                 robo.mouseMove(loc.x,loc.y);\r
461                                 robo = null;\r
462                         } catch (AWTException e) {\r
463                                 e.printStackTrace();\r
464                         }\r
465                 }\r
466                 \r
467                 return true;\r
468         }\r
469         \r
470         /**\r
471          * ページャーによる再描画\r
472          */\r
473         public boolean redrawByPager() {\r
474                  \r
475                 //JTreeLabel.Nodes node = jLabel_tree.getNode();\r
476                 String value = jLabel_tree.getValue();\r
477 \r
478                 if ( value != null ) {\r
479                         if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
480                                 redrawByNow(cur_tuner);\r
481                         }\r
482                         else {\r
483                                 redrawByDate(value, cur_tuner);\r
484                         }\r
485 \r
486                         return true;\r
487                 }\r
488                 \r
489                 return false;\r
490         }\r
491         \r
492         /**\r
493          * 予約待機赤枠の描画(全部)\r
494          * @see #putReserveBorder(String, String, int)\r
495          */\r
496         public void updateReserveBorder(String center) {\r
497                 \r
498                 // 予約の赤枠を表示する(上:日付別表示中、下:放送局別表示中)\r
499                 \r
500                 JTreeLabel.Nodes node = jLabel_tree.getNode();\r
501                 String value = jLabel_tree.getValue();\r
502                 \r
503                 switch ( node ) {\r
504                 case DATE:\r
505                 case TERRA:\r
506                 case BS:\r
507                 case CS:\r
508                         {\r
509                                 // 日付別は4種類ある\r
510                                 IterationType sTyp;\r
511                                 switch ( node ) {\r
512                                 case TERRA:\r
513                                         sTyp = IterationType.TERRA;\r
514                                         break;\r
515                                 case BS:\r
516                                         sTyp = IterationType.BS;\r
517                                         break;\r
518                                 case CS:\r
519                                         sTyp = IterationType.CS;\r
520                                         break;\r
521                                 default:\r
522                                         sTyp = IterationType.ALL;\r
523                                         break;\r
524                                 }\r
525                                 \r
526                                 // "現在日付"を現在日付にする\r
527                                 String dt = value;\r
528                                 if ( JTreeLabel.Nodes.NOW.getLabel().equals(dt) ) {\r
529                                         dt = CommonUtils.getDate529(0,true);\r
530                                 }\r
531                                 \r
532                                 TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), sTyp);\r
533                                 \r
534                                 // ページャーが有効なら表示すべきページ番号を取得する\r
535                                 int colmin = 0;\r
536                                 int colmax = pli.size();\r
537                                 int divider = 0;\r
538                                 if ( env.isPagerEnabled() ) {\r
539                                         int selectedpage = getSelectedPagerIndex();     // 予約枠の描画なのだから、ページ移動の必要はないはずだ\r
540                                         if ( selectedpage >= 0 ) {\r
541                                                 colmin = env.getCenterPerPage() * selectedpage;\r
542                                                 colmax = colmin + env.getCenterPerPage()-1;\r
543                                                 divider = env.getCenterPerPage();\r
544                                         }\r
545                                         else {\r
546                                                 StWin.appendError(ERRID+"ページャーコンボボックスが不正です: "+selectedpage);\r
547                                                 return;\r
548                                         }\r
549                                 }\r
550         \r
551                                 if ( center != null ) {\r
552                                         // 特定の放送局のみ更新\r
553                                         int cnt = pli.getIndex(center);\r
554                                         if ( colmin <= cnt && cnt <= colmax ) {\r
555                                                 int col = (divider==0) ? (cnt) : (cnt % divider);\r
556                                                 putReserveBorder(dt, center, col);\r
557                                         }\r
558                                 }\r
559                                 else {\r
560                                         // すべての放送局を更新\r
561                                         int cnt = -1;\r
562                                         for ( ProgList pl : pli ) {\r
563                                                 ++cnt;\r
564                                                 if ( cnt < colmin ) {\r
565                                                         continue;\r
566                                                 }\r
567                                                 else if ( cnt > colmax ) {\r
568                                                         break;\r
569                                                 }\r
570                                                 \r
571                                                 int col = (divider==0) ? (cnt) : (cnt % divider);\r
572                                                 \r
573                                                 putReserveBorder(dt, pl.Center, col);\r
574                                         }\r
575                                 }\r
576                         }\r
577                         break;\r
578                         \r
579                 case BCAST:\r
580                         {\r
581                                 \r
582                                 if (center != null && ! center.equals(value)) {\r
583                                         // 更新の必要はない\r
584                                         return;\r
585                                 }\r
586                                 if (center == null) {\r
587                                         // 選択中の放送局で更新する\r
588                                         center = value;\r
589                                 }\r
590                                 \r
591                                 TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(), IterationType.ALL);\r
592                                 int cnt = tvprograms.getIterator().getIndex(center);\r
593                                 if ( cnt == -1 ) {\r
594                                         MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
595                                 }\r
596                                 else {\r
597                                         ProgList pl = pli.getP();\r
598                                         for (int col=0; col<pl.pdate.size(); col++) {\r
599                                                 // 予約の赤枠を一週間分表示する\r
600                                                 putReserveBorder(pl.pdate.get(col).Date, center, col);\r
601                                         }\r
602                                 }\r
603                         }\r
604                         break;\r
605                 \r
606                 default:\r
607                         break;\r
608                 }\r
609         }\r
610         \r
611         /**\r
612          * \r
613          */\r
614         public void updateBangumiColumns() {\r
615                 for (JTXTButton b : frameUsed ) {\r
616                         ProgDetailList tvd = b.getInfo();\r
617                         if ( tvd.type == ProgType.PROG ) {\r
618                                 if (bounds.getShowMatchedBorder() && b.isStandby()) {\r
619                                         if ( b.getBorder() != dborder )\r
620                                                 b.setBorder(dborder);\r
621                                 }\r
622                                 else {\r
623                                         if ( b.getBorder() != lborder )\r
624                                                 b.setBorder(lborder);\r
625                                 }\r
626                         }\r
627                 }\r
628         }\r
629         \r
630         /**\r
631          * 予約待機赤枠の描画(ツールバーからのトグル操作)\r
632          */\r
633         public boolean toggleMatchBorder() {\r
634                 \r
635                 // 状態を保存\r
636                 bounds.setShowMatchedBorder( ! bounds.getShowMatchedBorder());\r
637                 \r
638                 _updPBorders(env, bounds, frameUsed);\r
639                 \r
640                 if ( env.getDrawcacheEnable() ) {\r
641                         _updPBorders(env, bounds, frameUsedByDate);\r
642                 }\r
643                 \r
644                 return bounds.getShowMatchedBorder();\r
645         }\r
646 \r
647         \r
648         /**\r
649          * 新聞枠の拡縮(ツールバーからの操作)\r
650          */\r
651         public void setZoom(int n) {\r
652                 paperHeightZoom = n * 0.01F;\r
653                 updateBounds(env, bounds);\r
654                 updateRepaint();\r
655         }\r
656 \r
657         /**\r
658          * 新聞ペーンをクリアする。\r
659          */\r
660         public void clearPanel() {\r
661                 \r
662                 // 番組枠の初期化\r
663                 /* 移動しました */\r
664                 \r
665                 // 予約枠の初期化\r
666                 for ( JRMLabel b : reserveBorders) {\r
667                         jLayeredPane_space_main_view.remove(b);\r
668                 }\r
669                 reserveBorders.clear();\r
670                 \r
671                 // タイムラインの削除\r
672                 if (jLabel_timeline != null && jLayeredPane_space_main_view != null) {\r
673                         jLayeredPane_space_main_view.remove(jLabel_timeline);\r
674                 }\r
675                 \r
676                 // 時間枠・日付枠・放送局枠の初期化\r
677                 jPanel_space_top_view.removeAll();\r
678                 redrawTimebar(jPanel_space_side_view);\r
679                 \r
680                 // 選択してないことにする\r
681                 //paper.jLabel_tree.setText("");\r
682         }\r
683         \r
684         /**\r
685          * <P>新聞ペーンを選択する。\r
686          * <P>高速描画ONの場合は、主ペーンのほかに複数の日付別ペーンが作成されるのでどれを利用するか選択する。\r
687          * @param pane : nullの場合、主ペーンを選択する。過去ログは常にnullで。\r
688          * @see #jLayeredPane_space_main_view_byMakeshift 主ペーン\r
689          * @see #jLayeredPane_space_main_view_byDate 日付別ペーン\r
690          */\r
691         private void selectMainView(JLayeredPane pane) {\r
692                 \r
693                 // 表示開始位置を記憶する\r
694                 Point p = vport.getViewPosition();\r
695                 \r
696                 if (pane == null) {\r
697                         // 番組枠の初期化\r
698                         StdAppendMessage(MSGID+"番組枠描画バッファをリセット: "+frameUsed.size()+"/"+framebuffersize);\r
699                         for (int i=frameUsed.size()-1; i>=0; i--) {\r
700                                 JTXTButton b = frameUsed.remove(i);\r
701                                 b.setToolTipText(null);\r
702                                 b.clean();\r
703                                 frameUnused.add(b);\r
704                                 //jLayeredPane_space_main_view_byMakeshift.remove(b);   // 削除しちゃダメよ?\r
705                         }\r
706                         \r
707                         if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift) {\r
708                                 return;\r
709                         }\r
710                         jScrollPane_space_main.setViewportView(jLayeredPane_space_main_view = jLayeredPane_space_main_view_byMakeshift);\r
711                 }\r
712                 else {\r
713                         if (jLayeredPane_space_main_view == pane) {\r
714                                 return;\r
715                         }\r
716                         jScrollPane_space_main.setViewportView(jLayeredPane_space_main_view = pane);\r
717                 }\r
718                 \r
719                 // 表示開始位置を戻す\r
720                 vport.setViewPosition(p);\r
721         }\r
722         \r
723         /**\r
724          * 現在時刻追従スクロールを開始する\r
725          */\r
726         private void startTimer() {\r
727                 timer_now_enabled = true;\r
728         }\r
729         \r
730         /**\r
731          * 現在時刻追従スクロールを停止する\r
732          */\r
733         private boolean stopTimer() {\r
734                 prevDT4Now = null;\r
735                 jLabel_timeline.setVisible(false);\r
736                 return (timer_now_enabled = false);\r
737         }\r
738         \r
739         /**\r
740          * サイドツリーの「現在日時」を選択する\r
741          */\r
742         public void selectTreeDefault() {\r
743                 if ( defaultNode != null ) jTree_tree.setSelectionPath(new TreePath(defaultNode.getPath()));\r
744         }\r
745         \r
746         /**\r
747          * サイドツリーの現在選択中のノードを再度選択して描画しなおす\r
748          */\r
749         public void reselectTree() {\r
750                 String[] names = new String[] { jLabel_tree.getNode().getLabel(), jLabel_tree.getValue() };\r
751                 TreeNode[] nodes = ter.getSelectedPath(paperRootNode, names, 0);\r
752                 if (nodes != null) {\r
753                         TreePath tp = new TreePath(nodes);\r
754                         if ( tp != null ) {\r
755                                 // 表示位置を記憶\r
756                                 Point vp = vport.getViewPosition(); //= SwingUtilities.convertPoint(vport,0,0,label);\r
757                                 // ツリー再選択\r
758                                 jTree_tree.setSelectionPath(null);\r
759                                 jTree_tree.setSelectionPath(tp);\r
760                                 // 表示位置を復帰\r
761                                 if (vp.x != 0 && vp.y != 0) {\r
762                                         jLayeredPane_space_main_view.scrollRectToVisible(new Rectangle(vp, vport.getSize()));\r
763                                 }\r
764                         }\r
765                 }\r
766         }\r
767         \r
768         /**\r
769          * サイドツリーを開く\r
770          */\r
771         public void setExpandTree() {\r
772                 jSplitPane_view.setDividerLocation(bounds.getTreeWidthPaper());\r
773                 jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);\r
774                 jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
775         }\r
776         \r
777         /**\r
778          * サイドツリーを閉じる\r
779          */\r
780         public void setCollapseTree() {\r
781                 jSplitPane_view.setDividerLocation(bounds.getMinDivLoc());\r
782                 jScrollPane_tree.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
783                 jScrollPane_tree.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
784         }\r
785         \r
786         /**\r
787          * サイドツリーの展開状態を設定ファイルに保存(鯛ナビ終了時に呼び出される)\r
788          */\r
789         public void saveTreeExpansion() {\r
790                 ter.save();\r
791         }\r
792         \r
793         /**\r
794          * 画面上部の番組詳細領域の表示のON/OFF\r
795          */\r
796         public void setDetailVisible(boolean aFlag) {\r
797                 jTextPane_detail.setVisible(aFlag);\r
798         }\r
799         \r
800         /**\r
801          * スクリーンショット用\r
802          */\r
803         public Component getCenterPane() {\r
804                 return jPanel_space_top_view;\r
805         }\r
806         \r
807         /**\r
808          * スクリーンショット用\r
809          */\r
810         public Component getTimebarPane() {\r
811                 return jPanel_space_side_view;\r
812         }\r
813         \r
814         /**\r
815          * スクリーンショット用\r
816          */\r
817         public Component getCurrentPane() {\r
818                 return jLayeredPane_space_main_view;\r
819         }\r
820         \r
821         /**\r
822          * スクリーンショット用\r
823          */\r
824         public String getCurrentView() {\r
825                 return jLabel_tree.getView();\r
826         }\r
827         \r
828         /**\r
829          * 高速描画ONの場合に日付別ペーンを一気に全部描画する\r
830          */\r
831         public void buildMainViewByDate() {\r
832                 \r
833                 if (env.getDebug()) System.out.println(DBGID+"CALLED buildMainViewByDate()");\r
834                 \r
835                 if (jLayeredPane_space_main_view_byMakeshift == null) {\r
836                         jLayeredPane_space_main_view_byMakeshift = new JLayeredPane();\r
837                         for (int i=0; i<framebuffersize; i++) {\r
838                                 JTXTButton b2 = new JTXTButton();\r
839                                 b2.clean();\r
840                                 jLayeredPane_space_main_view_byMakeshift.add(b2);\r
841                                 jLayeredPane_space_main_view_byMakeshift.setLayer(b2, 0);\r
842                                 \r
843                                 // リスナーを設定する\r
844                                 b2.addMouseListener(ml_risepopup);\r
845                                 b2.addMouseMotionListener(ml_risepopup);\r
846                                 frameUnused.add(b2);\r
847                         }\r
848                         StdAppendMessage(MSGID+"番組枠描画バッファを初期化: "+framebuffersize);\r
849                 }\r
850                 \r
851                 // ページャー機能とは排他\r
852                 if (env.isPagerEnabled() || ! env.getDrawcacheEnable()) {\r
853                         jLayeredPane_space_main_view_byDate = null;\r
854                         return;\r
855                 }\r
856                 \r
857                 jLayeredPane_space_main_view_byDate = new ArrayList<JTaggedLayeredPane>();\r
858                 frameUsedByDate = new ArrayList<JTXTButton>();\r
859                 new SwingBackgroundWorker(true) {\r
860                         \r
861                         @Override\r
862                         protected Object doWorks() throws Exception {\r
863                                 //\r
864                                 int dogDays = (env.getExpandTo8())?(8):(7);\r
865                                 //\r
866                                 for ( int y=0; y < dogDays; y++ ) {\r
867                                         jLayeredPane_space_main_view_byDate.add(new JTaggedLayeredPane());\r
868                                 }\r
869                                 for ( int y=0; y < dogDays; y++ ) {\r
870                                         String day = CommonUtils.getDate529(y*86400,true);\r
871                                         \r
872                                         jLayeredPane_space_main_view_byDate.get(y).setTagstr(day);\r
873                                         \r
874                                         StWin.appendMessage(MSGID+"番組表を構築します:"+day);\r
875                                         redrawByDate(day,IterationType.ALL);\r
876                                 }\r
877                                 return null;\r
878                         }\r
879                         \r
880                         @Override\r
881                         protected void doFinally() {\r
882                         }\r
883                 }.execute();\r
884         }\r
885 \r
886         /**\r
887          * ツリーのリスナーを止める\r
888          */\r
889         private void stopTreeListener() {\r
890                 jTree_tree.removeTreeSelectionListener(tsl_nodeselected);\r
891         }\r
892         \r
893         /**\r
894          * ツリーのリスナーを動かす\r
895          */\r
896         private void startTreeListener() {\r
897                 jTree_tree.addTreeSelectionListener(tsl_nodeselected);\r
898         }\r
899         \r
900         /**\r
901          * 日付でサブノード作成\r
902          */\r
903         public  void redrawTreeByDate() {\r
904                 \r
905                 stopTreeListener();\r
906                 TreePath tp = jTree_tree.getSelectionPath();\r
907                 \r
908                 _redrawTreeByDate(dateNode);\r
909                 _redrawTreeByDate(dgNode);\r
910                 _redrawTreeByDate(bsNode);\r
911                 _redrawTreeByDate(csNode);\r
912                 \r
913                 jTree_tree.setSelectionPath(tp);\r
914                 jTree_tree.updateUI();\r
915                 startTreeListener();\r
916         }\r
917         \r
918         private void _redrawTreeByDate(DefaultMutableTreeNode parent) {\r
919                 \r
920                 // ★★★ でふぉるとのーど ★★★\r
921                 DefaultMutableTreeNode nNode = new DefaultMutableTreeNode(JTreeLabel.Nodes.NOW.getLabel());\r
922                 if ( parent == dateNode ) {\r
923                         nowNode = nNode;\r
924                         defaultNode = nNode;\r
925                 }\r
926                 \r
927                 parent.removeAllChildren();\r
928                 parent.add(nNode);\r
929                 int dogDays = (env.getExpandTo8())?(8):(7);\r
930                 for ( int i=0; i<dogDays; i++ ) {\r
931                         parent.add(new DefaultMutableTreeNode(CommonUtils.getDate529(i*86400, true)));\r
932                 }\r
933         }\r
934 \r
935         /**\r
936          * 放送局名でサブノード作成\r
937          */\r
938         public void redrawTreeByCenter() {\r
939 \r
940                 stopTreeListener();\r
941                 TreePath tp = jTree_tree.getSelectionPath();\r
942                 \r
943                 centerNode.removeAllChildren();\r
944                 TVProgramIterator pli = tvprograms.getIterator();\r
945                 pli.build(chsort.getClst(), IterationType.ALL);\r
946                 for ( ProgList pl : pli ) {\r
947                         centerNode.add(new DefaultMutableTreeNode(pl.Center));\r
948                 }\r
949                 \r
950                 jTree_tree.setSelectionPath(tp);\r
951                 jTree_tree.updateUI();\r
952                 startTreeListener();\r
953         }\r
954 \r
955         /**\r
956          * 過去ログ日付でサブノード作成\r
957          */\r
958         public void redrawTreeByPassed() {\r
959 \r
960                 stopTreeListener();\r
961                 TreePath tp = jTree_tree.getSelectionPath();\r
962                 \r
963                 passedNode.removeAllChildren();\r
964                 if ( env.getUsePassedProgram() ) {\r
965                         String[] dd = new PassedProgram().getDateList(env.getPassedLogLimit());\r
966                         for ( int i=1; i<dd.length && i<=env.getPassedLogLimit(); i++ ) {\r
967                                 passedNode.add(new DefaultMutableTreeNode(dd[i]));\r
968                         }\r
969                 }\r
970                 \r
971                 jTree_tree.setSelectionPath(tp);\r
972                 jTree_tree.updateUI();\r
973                 startTreeListener();\r
974         }\r
975 \r
976         /**\r
977          *  時刻の列を生成\r
978          */\r
979         private void redrawTimebar(JPanel jp)\r
980         {\r
981                 jp.removeAll();\r
982                 \r
983                 float phm60 = 60.0F * bounds.getPaperHeightMultiplier() * paperHeightZoom;\r
984                 \r
985                 for (int row=0; row<24; row++) {\r
986                         \r
987                         int hour = row+TIMEBAR_START;\r
988                         \r
989                         JTimebarLabel b0 = new JTimebarLabel(Integer.toString(hour));\r
990                         \r
991                         if ( hour >=6 && hour <= 11 ) {\r
992                                 b0.setBackground(env.getTimebarColor());\r
993                         }\r
994                         else if ( hour >=12 && hour <= 17 ) {\r
995                                 b0.setBackground(env.getTimebarColor2());\r
996                         }\r
997                         else if ( hour >=18 && hour <= 23 ) {\r
998                                 b0.setBackground(env.getTimebarColor3());\r
999                         }\r
1000                         else {\r
1001                                 b0.setBackground(env.getTimebarColor4());\r
1002                         }\r
1003                         b0.setOpaque(true);\r
1004                         b0.setBorder(lborder);\r
1005                         b0.setHorizontalAlignment(JLabel.CENTER);\r
1006                         \r
1007                         b0.setBounds(\r
1008                                         0,\r
1009                                         (int) Math.ceil((float)row*phm60),\r
1010                                         bounds.getTimebarColumnWidth(),\r
1011                                         (int) Math.ceil(phm60));\r
1012                         \r
1013                         jp.add(b0);\r
1014                 }\r
1015                 \r
1016                 Dimension d = jp.getMaximumSize();\r
1017                 d.width = bounds.getTimebarColumnWidth();\r
1018                 d.height = (int) Math.ceil(24*phm60);\r
1019                 jp.setPreferredSize(d);\r
1020                 jp.updateUI();\r
1021         }\r
1022         \r
1023 \r
1024         /**\r
1025          * 現在日時に移動する\r
1026          * @see #redrawByDate(String, IterationType)\r
1027          */\r
1028         private void redrawByNow(final IterationType tuner) {\r
1029                 \r
1030                 // 古いタイマーの削除\r
1031                 stopTimer();\r
1032                 \r
1033                 // 移動汁!!\r
1034                 redrawByDate(CommonUtils.getDate529(0,true),tuner);\r
1035                 \r
1036                 // 時間線をひく\r
1037                 jLabel_timeline.setVisible(true);\r
1038                 Dimension dm = jLayeredPane_space_main_view.getPreferredSize();\r
1039                 //dm.height = Math.round(1*bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
1040                 dm.height = 3;\r
1041                 jLabel_timeline.setBorder(new LineBorder(Color.RED,2));\r
1042                 jLabel_timeline.setBackground(Color.RED);\r
1043                 jLabel_timeline.setOpaque(true);\r
1044                 jLabel_timeline.setBounds(0, 0, dm.width, dm.height);\r
1045                 jLayeredPane_space_main_view.add(jLabel_timeline);\r
1046                 jLayeredPane_space_main_view.setLayer(jLabel_timeline, 2);\r
1047                 \r
1048                 // 現在時刻線のリセット\r
1049                 setTimelinePos(true);\r
1050                 \r
1051                 // 新しいタイマーの作成(1分ごとに線を移動する)\r
1052                 startTimer();\r
1053         }\r
1054         \r
1055         /**\r
1056          * 日付別に表を作成する\r
1057          * @see #_redrawByDateWithCenter(String, String, IterationType)\r
1058          */\r
1059         private TVProgramIterator redrawByDate(String date, IterationType tuner) {\r
1060                 return _redrawByDateWithCenter(null,date,tuner);\r
1061         }\r
1062         \r
1063         /**\r
1064          * 日付別に表を作成する(ページャーが有効な場合は指定の放送局のあるページを開く)\r
1065          * @see #_redrawByDateWithCenter(String, String, IterationType)\r
1066          */\r
1067         private TVProgramIterator redrawByDateWithCenter(String center, String date) {\r
1068                 \r
1069                 // 今日は?\r
1070                 String ndate529 = CommonUtils.getDate529(0, true);\r
1071                 \r
1072                 // 過去ログかどうか\r
1073                 IterationType tuner;\r
1074                 JTreeLabel.Nodes node;\r
1075                 if ( ndate529.compareTo(date) > 0 ) {\r
1076                         tuner = IterationType.PASSED;\r
1077                         node = JTreeLabel.Nodes.PASSED;\r
1078                 }\r
1079                 else {\r
1080                         tuner = IterationType.ALL;\r
1081                         node = JTreeLabel.Nodes.DATE;\r
1082                 }\r
1083                 \r
1084                 if ( tuner == IterationType.PASSED ) {\r
1085                         // 過去ログの取得\r
1086                         PassedProgram passed = tvprograms.getPassed();\r
1087                         if ( ! passed.loadAllCenters(date) ) {\r
1088                                 System.err.println(ERRID+"過去ログの取得に失敗しました: "+date);\r
1089                                 return null;\r
1090                         }\r
1091                 }\r
1092                 \r
1093                 // 番組枠描画\r
1094                 TVProgramIterator pli = _redrawByDateWithCenter(center, date, tuner);\r
1095                 \r
1096                 getJTree_tree().getSelectionModel().setSelectionPath(null); // 選択は解除する\r
1097                 \r
1098                 jLabel_tree.setView(node, date);\r
1099                 \r
1100                 return pli;\r
1101         }\r
1102         \r
1103         /**\r
1104          * 日付別に表を作成する、の本体\r
1105          * @see #redrawByDate(String, IterationType)\r
1106          * @see #redrawByDateWithCenter(String, String)\r
1107          */\r
1108         private TVProgramIterator _redrawByDateWithCenter(String center, String date, IterationType tuner) {\r
1109                 \r
1110                 if (env.getDebug()) System.out.println(DBGID+"CALLED redrawByDate() date="+date+" IterationType="+tuner);\r
1111                 \r
1112                 // 古いタイマーの削除\r
1113                 stopTimer();\r
1114                 \r
1115                 cur_tuner = tuner;\r
1116                 \r
1117                 if (date == null) {\r
1118                         return null;\r
1119                 }\r
1120 \r
1121                 // イテレータ-\r
1122                 TVProgramIterator pli = tvprograms.getIterator().build(chsort.getClst(),tuner);\r
1123                 \r
1124                 // パネルの初期化\r
1125                 clearPanel();\r
1126                 \r
1127                 // 新聞ペーンの選択\r
1128                 boolean drawPrograms = true;\r
1129                 if ( tuner != IterationType.ALL || env.isPagerEnabled() || ! env.getDrawcacheEnable() ) {\r
1130                         selectMainView(null);\r
1131                 }\r
1132                 else {\r
1133                         // 描画速度優先の場合\r
1134                         boolean nopane = true;\r
1135                         for ( JTaggedLayeredPane tlp : jLayeredPane_space_main_view_byDate ) {\r
1136                                 if ( tlp.getTagstr().equals(date) ) {\r
1137                                         selectMainView(tlp);\r
1138                                         if ( tlp.getComponentCountInLayer(0) > 0 ) {\r
1139                                                 // 描画済みなら再度の描画は不要\r
1140                                                 drawPrograms = false;\r
1141                                         }\r
1142                                         nopane = false;\r
1143                                         break;\r
1144                                 }\r
1145                         }\r
1146                         if ( nopane ) {\r
1147                                 // 該当日付のPaneがない場合\r
1148                                 selectMainView(null);\r
1149                         }\r
1150                 }\r
1151                 \r
1152                 // ページ制御\r
1153                 int colmin = 0;\r
1154                 int colmax = pli.size();\r
1155                 int divider = 0;\r
1156                 if ( env.isPagerEnabled() ) {\r
1157                         \r
1158                         int selectedpage = getSelectedPagerIndex();\r
1159                         \r
1160                         if ( center == null ) {\r
1161                                 // とび先の指定がないのでもともと選択されていたページを再度選択したい\r
1162                                 if ( selectedpage == -1 ) {\r
1163                                         // なんか、選択されてないよ?\r
1164                                         selectedpage = 0;\r
1165                                 }\r
1166                                 else {\r
1167                                         int maxindex = env.getPageIndex(pli.size());\r
1168                                         if ( selectedpage > maxindex ) {\r
1169                                                 // ページ数かわったら、インデックスがはみだしちゃった\r
1170                                                 selectedpage = 0;\r
1171                                         }\r
1172                                 }\r
1173                         }\r
1174                         else {\r
1175                                 // 特定の日付の特定の放送局を表示したい\r
1176                                 int crindex = pli.getIndex(center);\r
1177                                 if ( crindex ==  -1 ) {\r
1178                                         // ここに入ったらバグ\r
1179                                         MWin.appendError(ERRID+"「"+center+"」は有効な放送局ではありません");\r
1180                                         ringBeep();\r
1181                                         crindex = 0;\r
1182                                 }\r
1183                                 selectedpage = env.getPageIndex(1+crindex);\r
1184                         }\r
1185 \r
1186                         // 開始位置・終了位置・局数\r
1187                         colmin = env.getCenterPerPage() * selectedpage;\r
1188                         colmax = colmin + (env.getCenterPerPage()-1);\r
1189                         divider = env.getCenterPerPage();\r
1190                         \r
1191                         // ページャーコンボボックスの書き換え\r
1192                         setPagerItems(pli,env.getPageIndex(1+colmin));\r
1193                         pli.rewind();\r
1194                         \r
1195                         // ページャーは有効だよ\r
1196                         //setPagerEnabled(true);\r
1197                 }\r
1198                 \r
1199                 if (env.getDebug()) System.out.println(DBGID+"[描画開始] ch_start="+colmin+" ch_end="+colmax+" ch_size="+pli.size());\r
1200                 \r
1201                 // 番組の枠表示用\r
1202                 dborder.setDashColor(env.getMatchedBorderColor());\r
1203                 dborder.setThickness(env.getMatchedBorderThickness());\r
1204                 \r
1205                 // 番組表時の共通設定\r
1206                 updateFonts(env);\r
1207                 \r
1208                 // 局列・番組表を作成\r
1209                 jPanel_space_top_view.setLayout(null);\r
1210                 int cnt = -1;\r
1211                 int col = -1;\r
1212                 for ( ProgList pl : pli ) {\r
1213 \r
1214                         ++cnt;\r
1215                         \r
1216                         if ( cnt < colmin ) {\r
1217                                 continue;\r
1218                         }\r
1219                         else if ( cnt > colmax ) {\r
1220                                 break;\r
1221                         }\r
1222                         \r
1223                         col = (divider==0) ? (cnt) : (cnt % divider);\r
1224                         \r
1225                         //TVProgram tvp = tvprograms.get(pli.getSiteId());\r
1226                         \r
1227                         //if (env.getDebug()) System.out.println(DBGID+"[描画中] "+pl.Center+" min="+colmin+" max="+colmax+" cnt="+cnt+" col="+col+" siteid="+siteid);\r
1228                         \r
1229                         // 局列\r
1230                         JLabel b1 = new JLabel(pl.Center);\r
1231                         b1.setOpaque(true);\r
1232                         b1.setBackground(pl.BgColor);\r
1233                         b1.setBorder(lborder);\r
1234                         b1.setHorizontalAlignment(JLabel.CENTER);\r
1235                         b1.setBounds(bounds.getBangumiColumnWidth()*col, 0, bounds.getBangumiColumnWidth(), bounds.getBangumiColumnHeight());\r
1236                         b1.addMouseListener(cnMouseAdapter);\r
1237                         jPanel_space_top_view.add(b1);\r
1238                         \r
1239                         // 予約の赤枠を表示する\r
1240                         if (tuner != IterationType.PASSED) {\r
1241                                 putReserveBorder(date, pl.Center, col);\r
1242                         }\r
1243 \r
1244                         // 番組表\r
1245                         if (drawPrograms == true) {\r
1246                                 putBangumiColumns(pl, col, date);\r
1247                         }\r
1248                 }\r
1249                 \r
1250                 ++col; // 描画後にパネルサイズの変更にも使う\r
1251                 \r
1252                 if ( ! env.getDrawcacheEnable()) {\r
1253                         // 番組枠描画バッファサイズの上限を確認する\r
1254                         if (framebuffersize < frameUsed.size()) {\r
1255                                 framebuffersize = frameUsed.size();\r
1256                                 StdAppendMessage(MSGID+"番組枠描画バッファの上限を変更: "+frameUsed.size()+"/"+framebuffersize);\r
1257                         }\r
1258                 }\r
1259                 \r
1260                 // ページサイズを変更する\r
1261                 //jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getTimebarColumnWidth()+cnt*bounds.getBangumiColumnWidth(),bounds.getBangumiColumnHeight()));\r
1262                 jPanel_space_top_view.setPreferredSize(new Dimension(col*bounds.getBangumiColumnWidth(),bounds.getBangumiColumnHeight()));\r
1263                 jPanel_space_top_view.updateUI();\r
1264                 \r
1265                 jLayeredPane_space_main_view.setPreferredSize(new Dimension(bounds.getBangumiColumnWidth()*col,Math.round(24*60*bounds.getPaperHeightMultiplier()*paperHeightZoom)));\r
1266                 \r
1267                 if (env.getDebug()) System.out.println(DBGID+"END redrawByDate() date="+date+" IterationType="+tuner);\r
1268                 \r
1269                 pli.rewind();\r
1270                 return pli;\r
1271         }\r
1272 \r
1273         /**\r
1274          * 放送局別に表を作成する\r
1275          */\r
1276         private void redrawByCenter(String center)\r
1277         {\r
1278                 // 古いタイマーの削除\r
1279                 stopTimer();\r
1280                 \r
1281                 // ページャーは効かないよ\r
1282                 if ( env.isPagerEnabled() ) {\r
1283                         setPagerEnabled(false);\r
1284                 }\r
1285                 \r
1286                 // パネルの初期化\r
1287                 clearPanel();\r
1288                 selectMainView(null);\r
1289                 \r
1290                 jPanel_space_top_view.setLayout(null);\r
1291                 \r
1292                 for (int a=0; a<tvprograms.size(); a++) {\r
1293                         //\r
1294                         TVProgram tvp = tvprograms.get(a);\r
1295                         //\r
1296                         if (tvp.getType() != ProgType.PROG) {\r
1297                                 continue;\r
1298                         }\r
1299                         //\r
1300                         for (ProgList pl : tvp.getCenters()) {\r
1301                                 if (pl.enabled == true && pl.Center.equals(center)) {\r
1302                                         // 日付ヘッダを描画する\r
1303                                         for (int centerid=0; centerid<pl.pdate.size(); centerid++)\r
1304                                         {\r
1305                                                 ProgDateList pcl = pl.pdate.get(centerid);\r
1306                                                 \r
1307                                                 JTXTLabel b1 = new JTXTLabel();\r
1308                                                 GregorianCalendar c = CommonUtils.getCalendar(pcl.Date);\r
1309                                                 if ( c != null ) {\r
1310                                                         String date = CommonUtils.getDate(c);\r
1311                                                         b1.setValue(date);\r
1312                                                         b1.setText(date.substring(5));\r
1313                                                         b1.setOpaque(true);\r
1314                                                         if ( c.get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY ) {\r
1315                                                                 b1.setBackground(new Color(90,90,255));\r
1316                                                         }\r
1317                                                         else if ( c.get(Calendar.DAY_OF_WEEK) == Calendar.SUNDAY ) {\r
1318                                                                 b1.setBackground(new Color(255,90,90));\r
1319                                                         }\r
1320                                                         else {\r
1321                                                                 b1.setBackground(new Color(180,180,180));\r
1322                                                         }\r
1323                                                 }\r
1324                                                 b1.setBorder(lborder);\r
1325                                                 b1.setHorizontalAlignment(JLabel.CENTER);\r
1326                                                 b1.setBounds(bounds.getBangumiColumnWidth()*centerid, 0, bounds.getBangumiColumnWidth(), bounds.getBangumiColumnHeight());\r
1327                                                 b1.addMouseListener(tbMouseAdapter);\r
1328                                                 jPanel_space_top_view.add(b1);\r
1329                                         }\r
1330                                         //jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getTimebarColumnWidth()+bounds.getBangumiColumnWidth()*tvprogram.getPlist().get(x).pcenter.size(),bounds.getBangumiColumnHeight()));\r
1331                                         jPanel_space_top_view.setPreferredSize(new Dimension(bounds.getBangumiColumnWidth()*pl.pdate.size(),bounds.getBangumiColumnHeight()));\r
1332                                         jPanel_space_top_view.updateUI();\r
1333                                         \r
1334 \r
1335                                         // 番組枠の表示\r
1336                                         {\r
1337                                                 putBangumiColumns(pl, -1, null);\r
1338                                         }\r
1339                                         \r
1340                                         // 予約枠の表示\r
1341                                         for (int progid=0; progid<pl.pdate.size(); progid++) {\r
1342                                                 putReserveBorder(pl.pdate.get(progid).Date, pl.Center, progid);\r
1343                                         }\r
1344                                         \r
1345                                         //\r
1346                                         jLayeredPane_space_main_view.setPreferredSize(new Dimension(tvp.getCenters().get(0).pdate.size()*bounds.getBangumiColumnWidth(),Math.round(24*60*bounds.getPaperHeightMultiplier()*paperHeightZoom)));\r
1347                                         //jScrollPane_space_main.updateUI();\r
1348 \r
1349                                         break;\r
1350                                 }\r
1351                         }\r
1352                 }\r
1353                 \r
1354                 // 番組枠描画バッファサイズの上限を確認する\r
1355                 if (framebuffersize < frameUsed.size()) {\r
1356                         framebuffersize = frameUsed.size();\r
1357                         StdAppendMessage(MSGID+"番組枠描画バッファの上限を変更: "+frameUsed.size()+"/"+framebuffersize);\r
1358                 }\r
1359         }\r
1360         \r
1361         \r
1362         /**\r
1363          * 予約待機赤枠の描画(個々の枠)\r
1364          * @see #updateReserveBorder(String)\r
1365          */\r
1366         private void putReserveBorder(String date, String Center, int q) {\r
1367                 \r
1368                 // 古いマークの削除(一見取りこぼしがあるように見えるが無問題)\r
1369                 for (int i=reserveBorders.size()-1; i>=0; i--) {\r
1370                         JRMLabel rb = reserveBorders.get(i);\r
1371                         if ( rb.getDate().equals(date) && rb.getCenter().equals(Center) ) {\r
1372                                 rb.setVisible(false);\r
1373                                 jLayeredPane_space_main_view.remove(rb);\r
1374                                 reserveBorders.remove(i);\r
1375                         }\r
1376                 }\r
1377                 \r
1378                 // 座標系\r
1379                 JRMLabel.setColumnWidth(bounds.getBangumiColumnWidth());\r
1380                 JRMLabel.setHeightMultiplier(bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
1381                 \r
1382                 // 表示範囲\r
1383                 GregorianCalendar cal = CommonUtils.getCritCalendar(date);\r
1384                 String topDateTime = CommonUtils.getDateTime(cal);\r
1385                 cal.add(Calendar.DATE, 1);\r
1386                 String bottomDateTime = CommonUtils.getDateTime(cal);\r
1387                 \r
1388                 // \r
1389                 String passedCritDateTime = CommonUtils.getCritDateTime(env.getDisplayPassedReserve());\r
1390                 \r
1391                 // ツールバーで選択されている実レコーダ\r
1392                 String myself = ( env.getEffectComboToPaper() ) ? (getSelectedMySelf()) : (null);\r
1393                 \r
1394                 // 予約枠の描画\r
1395                 drawReserveBorders(date, Center, q, topDateTime, bottomDateTime, passedCritDateTime, myself);\r
1396                 \r
1397                 // ピックアップ枠の描画\r
1398                 drawPickupBorders(date, Center, q, topDateTime, bottomDateTime, passedCritDateTime, TUNERLABEL_PICKUP);\r
1399         }\r
1400         private void drawReserveBorders(String date, String Center, int q, String topDateTime, String bottomDateTime, String passedCritDateTime, String myself) {\r
1401                 if ( myself == HDDRecorder.SELECTED_PICKUP ) {\r
1402                         return;\r
1403                 }\r
1404                 for ( HDDRecorder recorder : getSelectedRecorderList() ) {\r
1405                         for ( ReserveList r : recorder.getReserves()) {\r
1406                                 \r
1407                                 // 「実行のみ表示」で無効な予約は表示しない\r
1408                                 if ( env.getDisplayOnlyExecOnEntry() && ! r.getExec() ) {\r
1409                                         continue;\r
1410                                 }\r
1411                                 \r
1412                                 // 放送局名の確認\r
1413                                 if ( r.getCh_name() == null ) {\r
1414                                         if ( r.getChannel() == null ) {\r
1415                                                 // CHコードすらないのはバグだろう\r
1416                                                 System.err.println(ERRID+"予約情報にCHコードが設定されていません。バグの可能性があります。 recid="+recorder.Myself()+" chname="+r.getCh_name());\r
1417                                         }\r
1418                                         continue;\r
1419                                 }\r
1420                                 \r
1421                                 // 描画本体\r
1422                                 if (r.getCh_name().equals(Center)) {\r
1423                                         \r
1424                                         // 開始終了日時リストを生成する\r
1425                                         ArrayList<String> starts = new ArrayList<String>();\r
1426                                         ArrayList<String> ends = new ArrayList<String>();\r
1427                                         CommonUtils.getStartEndList(starts, ends, r);\r
1428                                         \r
1429                                         // 予約枠を描画する\r
1430                                         for ( int j=0; j<starts.size(); j++ ) {\r
1431                                                 if ( passedCritDateTime.compareTo(ends.get(j)) > 0 ) {\r
1432                                                         // 過去情報の表示が制限されている場合\r
1433                                                         continue;\r
1434                                                 }\r
1435                                                 \r
1436                                                 drawBorder(date,Center,topDateTime,bottomDateTime,starts.get(j),ends.get(j),r.getRec_min(),r.getTuner(),recorder.getColor(r.getTuner()),r.getExec(),q);\r
1437                                         }\r
1438                                 }\r
1439                         }\r
1440                 }\r
1441         }\r
1442         private void drawPickupBorders(String date, String Center, int q, String topDateTime, String bottomDateTime, String passedCritDateTime, String tuner) {\r
1443                 for ( ProgList pl : tvprograms.getPickup().getCenters() ) {\r
1444                         if ( ! pl.Center.equals(Center) ) {\r
1445                                 continue;\r
1446                         }\r
1447                         for ( ProgDateList pcl : pl.pdate ) {\r
1448                                 for ( ProgDetailList tvd : pcl.pdetail ) {\r
1449                                         if ( passedCritDateTime.compareTo(tvd.endDateTime) > 0 ) {\r
1450                                                 // 過去情報の表示が制限されている場合\r
1451                                                 continue;\r
1452                                         }\r
1453                                         \r
1454                                         drawBorder(date,Center,topDateTime,bottomDateTime,tvd.startDateTime,tvd.endDateTime,tvd.length,tuner,env.getPickedColor(),false,q);\r
1455                                 }\r
1456                         }\r
1457                 }\r
1458         }\r
1459         private void drawBorder(String date, String Center, String topDateTime, String bottomDateTime, String startDateTime, String endDateTime, String recmin, String tuner, String bordercol, boolean exec, int col) {\r
1460                 drawBorder(date, Center, topDateTime, bottomDateTime, startDateTime, endDateTime, Integer.valueOf(recmin), tuner, CommonUtils.str2color(bordercol), exec, col);\r
1461         }\r
1462         private void drawBorder(String date, String Center, String topDateTime, String bottomDateTime, String startDateTime, String endDateTime, int recmin, String tuner, Color bordercol, boolean exec, int col) {\r
1463                 \r
1464                 GregorianCalendar ca = CommonUtils.getCalendar(startDateTime);\r
1465                 int ahh = ca.get(Calendar.HOUR_OF_DAY);\r
1466                 int amm = ca.get(Calendar.MINUTE);\r
1467                 \r
1468                 int row = 0;\r
1469                 int length = 0;\r
1470                 if (topDateTime.compareTo(startDateTime) <= 0 && startDateTime.compareTo(bottomDateTime) < 0) {\r
1471                         // 開始時刻が表示範囲内にある\r
1472                         row = ahh - TIMEBAR_START;\r
1473                         if (row < 0) {\r
1474                                 row += 24;\r
1475                         }\r
1476                         row = row*60 + amm;\r
1477                         length = recmin;\r
1478                 }\r
1479                 else if (startDateTime.compareTo(topDateTime) < 0 && topDateTime.compareTo(endDateTime) < 0) {\r
1480                         // 表示開始位置が番組の途中にある\r
1481                         row = 0;\r
1482                         length = recmin - (TIMEBAR_START*60 - ahh*60 - amm);\r
1483                 }\r
1484                 else {\r
1485                         return;\r
1486                 }\r
1487                 \r
1488                 {\r
1489                         // 重複予約の場合のエンコーダマーク表示位置の調整\r
1490                         int rc = 0;\r
1491                         //int rw = 0;\r
1492                         for (int k=0; k<reserveBorders.size(); k++) {\r
1493                                 JRMLabel rb = reserveBorders.get(k);\r
1494                                 if ( rb.getDate().equals(date) && rb.getCenter().equals(Center) ) {\r
1495                                         int drow = rb.getVRow() - row;\r
1496                                         int dlen = rb.getVHeight() - length;\r
1497                                         if ( rb.getVColumn() == col && ((drow == 0 && dlen == 0) || ((drow == 1 || drow == -1) && (dlen == 0 || dlen == -1 || dlen == 1))) ) {\r
1498                                                 rc++;\r
1499                                         }\r
1500                                 }\r
1501                         }\r
1502                         \r
1503                         // 予約マーク追加\r
1504                         JRMLabel rb = new JRMLabel();\r
1505                         \r
1506                         if (rc == 0) {\r
1507                                 rb.setVerticalAlignment(JLabel.BOTTOM);\r
1508                                 rb.setHorizontalAlignment(JLabel.RIGHT);\r
1509                         }\r
1510                         else if (rc == 1) {\r
1511                                 rb.setVerticalAlignment(JLabel.BOTTOM);\r
1512                                 rb.setHorizontalAlignment(JLabel.LEFT);\r
1513                         }\r
1514                         else {\r
1515                                 rb.setVerticalAlignment(JLabel.TOP);\r
1516                                 rb.setHorizontalAlignment(JLabel.RIGHT);\r
1517                         }\r
1518                         \r
1519                         // エンコーダの区別がないものは"■"を表示する\r
1520                         rb.setEncBackground(bordercol);\r
1521                         rb.setBorder(new LineBorder(bordercol,4));\r
1522                         if ( tuner != null && tuner.equals(TUNERLABEL_PICKUP) ) {\r
1523                                 rb.setEncForeground(env.getPickedFontColor());\r
1524                         }\r
1525                         else if ( exec ) {\r
1526                                 rb.setEncForeground(env.getExecOnFontColor());\r
1527                         }\r
1528                         else {\r
1529                                 rb.setEncForeground(env.getExecOffFontColor());\r
1530                         }\r
1531                         if (tuner == null || tuner.equals("")) {\r
1532                                 rb.setEncoder("■");\r
1533                         }\r
1534                         else {\r
1535                                 rb.setEncoder(tuner);\r
1536                         }\r
1537 \r
1538                         // 検索用情報\r
1539                         rb.setDate(date);\r
1540                         rb.setCenter(Center);\r
1541                         rb.setExec(exec);\r
1542                         \r
1543                         jLayeredPane_space_main_view.add(rb);\r
1544                         jLayeredPane_space_main_view.setLayer(rb,1);\r
1545                         rb.setVBounds(col, row, 1, length);\r
1546                         rb.setVisible(true);\r
1547                         \r
1548                         reserveBorders.add(rb);\r
1549                 }\r
1550         }\r
1551         \r
1552         /**\r
1553          * 番組枠の表示\r
1554          * @param cnt -1:放送局別表示、>=0:日付表示\r
1555          */\r
1556         private void putBangumiColumns(ProgList pl, int cnt, String date) {\r
1557                 int ymax = pl.pdate.size();\r
1558                 int col = -1;\r
1559                 for ( int dateid=0; dateid < ymax; dateid++ ) {\r
1560                         ProgDateList pcl = pl.pdate.get(dateid);\r
1561                         \r
1562                         if ( cnt >= 0 ) {\r
1563                                 if ( ! pcl.Date.equals(date) ) {\r
1564                                         // 日付表示の場合は1列のみ描画\r
1565                                         continue;\r
1566                                 }\r
1567                                 \r
1568                                 col = cnt;\r
1569                         }\r
1570                         else if ( cnt == -1 ) {\r
1571                                 col++;\r
1572                         }\r
1573                         \r
1574                         int row = 0;\r
1575                         int pEnd = 0;\r
1576                         int zmax = pcl.pdetail.size();\r
1577                         for ( int progid=0; progid<zmax; progid++ ) {\r
1578                                 ProgDetailList tvd = pcl.pdetail.get(progid);\r
1579                                 if ( progid != 0 ) {\r
1580                                         // 2つめ以降は開始時刻から計算\r
1581                                         String[] st = tvd.start.split(":",2);\r
1582                                         if ( st.length == 2 ) {\r
1583                                                 int ahh = Integer.valueOf(st[0]);\r
1584                                                 int amm = Integer.valueOf(st[1]);\r
1585                                                 if ( CommonUtils.isLateNight(ahh) ) {\r
1586                                                         ahh += 24;\r
1587                                                 }\r
1588                                                 row = (ahh-TIMEBAR_START)*60+amm;\r
1589                                         }\r
1590                                         else {\r
1591                                                 // 「番組情報がありません」は前の番組枠のお尻に\r
1592                                                 row = pEnd;\r
1593                                         }\r
1594                                 }\r
1595                                 else {\r
1596                                         // その日の最初のエントリは5:00以前の場合もあるので強制0スタート\r
1597                                         row = 0;\r
1598                                 }\r
1599                                 \r
1600                                 // 番組枠描画\r
1601                                 putBangumiColumnSub(tvd, row, col);\r
1602                                 \r
1603                                 // 「番組情報がありません」用に保存\r
1604                                 pEnd = row + tvd.length;\r
1605                         }\r
1606                 }\r
1607         }\r
1608         private void putBangumiColumnSub(ProgDetailList tvd, int row, int col) {\r
1609                 \r
1610                 // 新規生成か既存流用かを決める\r
1611                 JTXTButton b2 = null;\r
1612                 if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift && ! frameUnused.isEmpty()) {\r
1613                         b2 = frameUnused.remove(frameUnused.size()-1);\r
1614                         //b2.setVisible(true);  // JTXTButton.clear()内でsetVisible(false)しているので\r
1615                 }\r
1616                 else {\r
1617                         // 生成する\r
1618                         b2 = new JTXTButton();\r
1619                         jLayeredPane_space_main_view.add(b2);\r
1620                         jLayeredPane_space_main_view.setLayer(b2, 0);\r
1621                         \r
1622                         // リスナーを設定する\r
1623                         b2.addMouseListener(ml_risepopup);\r
1624                         b2.addMouseMotionListener(ml_risepopup);\r
1625                 }\r
1626                 if (jLayeredPane_space_main_view == jLayeredPane_space_main_view_byMakeshift) {\r
1627                         frameUsed.add(b2);\r
1628                 }\r
1629                 else {\r
1630                         // 裏描画は十分遅いのでb2をUnusedキャッシュには入れず都度生成で構わない\r
1631                         frameUsedByDate.add(b2);\r
1632                 }\r
1633                 \r
1634                 // 情報設定\r
1635                 b2.setInfo(tvd);\r
1636                 \r
1637                 JTXTButton.setColumnWidth(bounds.getBangumiColumnWidth());\r
1638                 JTXTButton.setHeightMultiplier(bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
1639                 \r
1640                 b2.setBackground(pColors.get(tvd.genre));\r
1641                 if (bounds.getShowMatchedBorder() && b2.isStandby() ) {\r
1642                         b2.setBorder(dborder);\r
1643                 }\r
1644                 else {\r
1645                         b2.setBorder(lborder);\r
1646                 }\r
1647                 \r
1648                 // 配置を決定する\r
1649                 b2.setVBounds(col,row,1,tvd.length);\r
1650                 \r
1651                 // ツールチップを付加する\r
1652                 if ( env.getTooltipEnable() == true && ! tvd.title.equals("") && ! tvd.start.equals("") ) {\r
1653                         String t = "";\r
1654                         int tlen = bounds.getTooltipWidth();\r
1655                         for (int i=0; i<tvd.title.length(); i+=tlen) {\r
1656                                 t += tvd.title.substring(i, (i+tlen<tvd.title.length())?(i+tlen):(tvd.title.length()))+"<BR>";\r
1657                         }\r
1658                         String d = "";\r
1659                         int dlen = tlen+2;\r
1660                         for (int i=0; i<tvd.detail.length(); i+=dlen) {\r
1661                                 d += "&nbsp;&nbsp;&nbsp;&nbsp;"+tvd.detail.substring(i, (i+dlen<tvd.detail.length())?(i+dlen):(tvd.detail.length()))+"<BR>";\r
1662                         }\r
1663                         String e = getExtensionMark(tvd);\r
1664                         b2.setToolTipText(("<html>"+tvd.start+"&nbsp;<FONT COLOR=RED><EM>"+e+"</EM></FONT><BR>&nbsp;<FONT COLOR=BLUE><STRONG><U>"+t+"</U></STRONG></FONT>"+d+"</html>"));\r
1665                 }\r
1666         }\r
1667         \r
1668         /**\r
1669          * 現在時刻線の位置を変える\r
1670          * @param minpos : MINPOS.RESET=初回、MINPOS.UPDATE=自動更新時\r
1671          */\r
1672         private int setTimelinePos(boolean reset) {\r
1673                 if ( vport != null && jLabel_timeline != null && jLabel_timeline.isVisible() ) {\r
1674                         \r
1675                         int correct = 0; // 24:00-28:59迄は前日の日付になる\r
1676                         GregorianCalendar c = CommonUtils.getCalendar(0);\r
1677                         if ( CommonUtils.isLateNight(c) ) {\r
1678                                 c.add(Calendar.DATE, -1);\r
1679                                 correct += 24;\r
1680                         }\r
1681                         \r
1682                         Point vp = vport.getViewPosition();\r
1683                         Point tp = jLabel_timeline.getLocation();\r
1684                         \r
1685                         // ビュー上の位置\r
1686                         int minpos_new = (c.get(Calendar.HOUR_OF_DAY)-TIMEBAR_START+correct)*60+c.get(Calendar.MINUTE);\r
1687                         int timeline_vpos = jLabel_timeline.setMinpos(0, minpos_new, bounds.getPaperHeightMultiplier()*paperHeightZoom);\r
1688                         \r
1689                         // ビューポートの位置(05:30まではスクロールしないよ)\r
1690                         if ( env.getTimerbarScrollEnable() && minpos_new >= 30 ) {\r
1691                                 if ( reset ) {\r
1692                                         // 初回描画\r
1693                                         Rectangle ra = vport.getViewRect();\r
1694                                         ra.y =  Math.round(timeline_vpos - (float)bounds.getTimelinePosition() * bounds.getPaperHeightMultiplier() * paperHeightZoom);\r
1695                                         vport.setViewPosition(new Point(ra.x, ra.y));\r
1696                                 }\r
1697                                 else {\r
1698                                         // 自動更新\r
1699                                         vp.y += (timeline_vpos - tp.y);\r
1700                                         vport.setViewPosition(vp);\r
1701                                 }\r
1702                         }\r
1703                         \r
1704                         jLabel_timeline.updateUI();\r
1705                         \r
1706                         return minpos_new;\r
1707                 }\r
1708                 \r
1709                 return -1;\r
1710         }\r
1711         \r
1712         /**\r
1713          * 番組枠のクリック位置を日時に変換する\r
1714          */\r
1715         private String getClickedDateTime(ProgDetailList tvd, int clikedY) {\r
1716                 \r
1717                 String clickedDateTime = null;\r
1718                 \r
1719                 if ( clikedY >= 0 && tvd.start.length() != 0 ) {\r
1720                         // 新聞形式ならクリック位置の日時を算出する\r
1721                         GregorianCalendar cala = CommonUtils.getCalendar(tvd.startDateTime);\r
1722                         cala.add(Calendar.MINUTE, Math.round(((float)clikedY)/(bounds.getPaperHeightMultiplier()*paperHeightZoom)));\r
1723                         clickedDateTime = CommonUtils.getDateTime(cala);\r
1724                 }\r
1725                 \r
1726                 return clickedDateTime;\r
1727         }\r
1728         \r
1729         \r
1730         /*******************************************************************************\r
1731          * ハンドラ―メソッド\r
1732          ******************************************************************************/\r
1733         \r
1734         /**\r
1735          * ツールバーでレコーダの選択イベントが発生\r
1736          */\r
1737         @Override\r
1738         public void valueChanged(HDDRecorderSelectionEvent e) {\r
1739                 if (debug) System.out.println(DBGID+"recorder selection rised");\r
1740                 \r
1741                 // 選択中のレコーダ情報を保存する\r
1742                 src_recsel = (HDDRecorderSelectable) e.getSource();\r
1743                 \r
1744                 // 予約枠を書き換える\r
1745                 updateReserveBorder(null);\r
1746         }\r
1747         \r
1748         private String getSelectedMySelf() {\r
1749                 return ( src_recsel!=null ? src_recsel.getSelectedMySelf() : null );\r
1750         }\r
1751         \r
1752         private HDDRecorderList getSelectedRecorderList() {\r
1753                 return ( src_recsel!=null ? src_recsel.getSelectedList() : null );\r
1754         }\r
1755         \r
1756         private HDDRecorderSelectable src_recsel;\r
1757         \r
1758         \r
1759         /**\r
1760          * レコーダ情報の変更イベントが発生\r
1761          */\r
1762         @Override\r
1763         public void stateChanged(HDDRecorderChangeEvent e) {\r
1764                 // テーブルをリフレッシュする処理\r
1765                 \r
1766         }\r
1767         \r
1768         \r
1769 \r
1770         /*******************************************************************************\r
1771          * リスナー\r
1772          ******************************************************************************/\r
1773 \r
1774         /**\r
1775          * 現在時刻追従スクロール\r
1776          */\r
1777         @Override\r
1778         public void timerRised(TickTimerRiseEvent e) {\r
1779                 \r
1780                 String curDT = CommonUtils.getDate529(0,true);\r
1781                 \r
1782                 if ( prevDT4Tree != null && ! prevDT4Tree.equals(curDT) ) {\r
1783                         // 日付が変わったらツリーを書き換える\r
1784                         redrawTreeByDate();\r
1785                         redrawTreeByPassed();\r
1786                         prevDT4Tree = curDT;\r
1787                 }\r
1788                 \r
1789                 if ( timer_now_enabled ) {\r
1790 \r
1791                         if (prevDT4Now != null && ! prevDT4Now.equals(curDT)) {\r
1792                                 // 日付切り替え\r
1793                                 StdAppendError(MSGID+"日付が変わったので番組表を切り替えます("+CommonUtils.getDateTime(0)+")");\r
1794                                 redrawByNow(cur_tuner);\r
1795                         }\r
1796                         else {\r
1797                                 // 現在時刻線の移動\r
1798                                 setTimelinePos(false);\r
1799                         }\r
1800                         \r
1801                         // 前回実行日\r
1802                         prevDT4Now = curDT;\r
1803                 }\r
1804         }\r
1805         \r
1806         /**\r
1807          * タブを開いたり閉じたりしたときに動くリスナー\r
1808          */\r
1809         private ComponentListener cl_shownhidden = new ComponentAdapter() {\r
1810                 @Override\r
1811                 public void componentShown(ComponentEvent e) {\r
1812                         \r
1813                         // 前日以前の番組情報を削除する\r
1814                         for ( TVProgram tvp : tvprograms ) {\r
1815                                 tvp.refresh();\r
1816                         }\r
1817                         \r
1818                         // 終了した予約を整理する\r
1819                         for ( HDDRecorder recorder : recorders ) {\r
1820                                 recorder.removePassedReserves();\r
1821                         }\r
1822                         \r
1823                         // 他のコンポーネントと連動\r
1824                         onShown();\r
1825                         \r
1826                         setPagerEnabled(true);\r
1827                 }\r
1828                 \r
1829                 @Override\r
1830                 public void componentHidden(ComponentEvent e) {\r
1831                         \r
1832                         onHidden();\r
1833                         \r
1834                         setPagerEnabled(false);\r
1835                 }\r
1836         };\r
1837         \r
1838         /**\r
1839          * 番組枠につけるマウス操作のリスナー\r
1840          */\r
1841         private final MouseInputListener ml_risepopup = new MouseInputListener() {\r
1842                 //\r
1843                 private final Cursor defCursor = Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);\r
1844                 private final Cursor hndCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);\r
1845                 private final Point pp = new Point();\r
1846                 \r
1847                 private Color bgcolor = null;\r
1848 \r
1849                 @Override\r
1850                 public void mouseClicked(MouseEvent e) {\r
1851                         \r
1852                         // ポインタの位置\r
1853                         Point p = e.getPoint();\r
1854                         \r
1855                         // ポインタの乗っている番組\r
1856                         JTXTButton b = (JTXTButton) e.getSource();\r
1857                         ProgDetailList tvd = b.getInfo();\r
1858                         \r
1859                         if (e.getButton() == MouseEvent.BUTTON3) {\r
1860                                 if (e.getClickCount() == 1) {\r
1861                                         // 右シングルクリックでメニューの表示\r
1862                                         String clicked = getClickedDateTime(tvd, e.getY());\r
1863                                         showPopupForTraceProgram(b, tvd, tvd.title, TraceKey.noFazzyThreshold, p.x, p.y, clicked);\r
1864                                 }\r
1865                         }\r
1866                         else if (e.getButton() == MouseEvent.BUTTON1) {\r
1867                                 // 過去ログは閲覧のみ\r
1868                                 if (tvd.type == ProgType.PASSED) {\r
1869                                         if (e.getClickCount() == 2) {\r
1870                                                 MWin.appendMessage(MSGID+"過去ログでダブルクリックは利用できません");\r
1871                                                 ringBeep();\r
1872                                         }\r
1873                                         \r
1874                                         return;\r
1875                                 }\r
1876                                 \r
1877                                 if (e.getClickCount() == 2) {\r
1878                                         // 左ダブルクリックで予約ウィンドウを開く\r
1879                                         openReserveDialog(tvd);                                 \r
1880                                 }\r
1881                         }\r
1882                         else if (e.getButton() == MouseEvent.BUTTON2) {\r
1883                                 // ピックアップに追加\r
1884                                 addToPickup(tvd);\r
1885                         }\r
1886                 }\r
1887                 \r
1888                 private void openReserveDialog(ProgDetailList tvd) {\r
1889                         \r
1890                         // レコーダが登録されていない場合はなにもしない\r
1891                         if (recorders.size() == 0) {\r
1892                                 return;\r
1893                         }\r
1894 \r
1895                         // ダイアログの位置指定\r
1896                         CommonSwingUtils.setLocationCenter(parent,rD);\r
1897                         \r
1898                         // サブタイトルを番組追跡の対象から外す\r
1899                         boolean succeeded = false;\r
1900                         if ( ! env.getSplitEpno() && env.getTraceOnlyTitle() ) {\r
1901                                 succeeded = rD.open(tvd,tvd.title,TraceKey.defaultFazzyThreshold);\r
1902                         }\r
1903                         else {\r
1904                                 succeeded = rD.open(tvd);\r
1905                         }\r
1906                         \r
1907                         if (succeeded) {\r
1908                                 rD.setVisible(true);\r
1909                         }\r
1910                         else {\r
1911                                 rD.dispose();\r
1912                         }\r
1913                         \r
1914                         if (rD.isSucceededReserve()) {\r
1915                                 updateReserveDisplay();\r
1916                                 updateReserveBorder(tvd.center);\r
1917                         }\r
1918                 }\r
1919 \r
1920                 /**\r
1921                  * 詳細情報の自動表示\r
1922                  */\r
1923                 @Override\r
1924                 public void mouseEntered(MouseEvent e) {\r
1925                         \r
1926                         JTXTButton b = (JTXTButton) e.getSource();\r
1927                         ProgDetailList tvd = b.getInfo();\r
1928                         \r
1929                         if ( env.getEnableHighlight() ) {\r
1930                                 bgcolor = ((JTXTButton)e.getSource()).getBackground();\r
1931                                 ((JTXTButton)e.getSource()).setBackground(env.getHighlightColor());\r
1932                         }\r
1933                         jTextPane_detail.setLabel(tvd.start,tvd.end,tvd.title);\r
1934                         jTextPane_detail.setText(tvd.detail+"\n"+tvd.getAddedDetail());\r
1935                 }\r
1936 \r
1937                 @Override\r
1938                 public void mouseExited(MouseEvent e) {\r
1939                         if ( env.getEnableHighlight() ) {\r
1940                                 ((JTXTButton)e.getSource()).setBackground(bgcolor);\r
1941                         }\r
1942                 }\r
1943 \r
1944                 @Override\r
1945                 public void mouseDragged(final MouseEvent e) {\r
1946                         Point cp = e.getLocationOnScreen();\r
1947                         Point vp = vport.getViewPosition(); //= SwingUtilities.convertPoint(vport,0,0,label);\r
1948                         vp.translate(pp.x-cp.x, pp.y-cp.y);\r
1949                         jLayeredPane_space_main_view.scrollRectToVisible(new Rectangle(vp, vport.getSize()));\r
1950                         pp.setLocation(cp);\r
1951                 }\r
1952 \r
1953                 @Override\r
1954                 public void mousePressed(MouseEvent e) {\r
1955                         pp.setLocation(e.getLocationOnScreen());\r
1956                         jLayeredPane_space_main_view.setCursor(hndCursor);\r
1957                 }\r
1958 \r
1959                 @Override\r
1960                 public void mouseReleased(MouseEvent e) {\r
1961                         jLayeredPane_space_main_view.setCursor(defCursor);\r
1962                 }\r
1963 \r
1964                 @Override\r
1965                 public void mouseMoved(MouseEvent e) {\r
1966                 }\r
1967         };\r
1968         \r
1969         /**\r
1970          * サイドツリーにつけるリスナー(ツリーの展開状態を記憶する)\r
1971          */\r
1972         private final TreeExpansionListener tel_nodeexpansion = new TreeExpansionListener() {\r
1973                 \r
1974                 @Override\r
1975                 public void treeExpanded(TreeExpansionEvent event) {\r
1976                         ter.reg();\r
1977                 }\r
1978                 \r
1979                 @Override\r
1980                 public void treeCollapsed(TreeExpansionEvent event) {\r
1981                         ter.reg();\r
1982                 }\r
1983         };\r
1984 \r
1985         /**\r
1986          * サイドツリーにつけるリスナー(クリックで描画実行)\r
1987          */\r
1988         private final TreeSelectionListener tsl_nodeselected = new TreeSelectionListener() {\r
1989                 @Override\r
1990                 public void valueChanged(TreeSelectionEvent e){\r
1991                         \r
1992                         TreePath path = jTree_tree.getSelectionPath();\r
1993 \r
1994                         if ( path != null && path.getPathCount() == 2 ) {\r
1995                                 // 親ノードとか触られても…\r
1996                                 return;\r
1997                         }\r
1998                         \r
1999                         if ( path != null && path.getPathCount() == 3 ) {\r
2000                                 \r
2001                                 if (env.getDebug()) System.out.println(DBGID+"SELECTED treeSelListner "+path);\r
2002 \r
2003                                 stopTimer();\r
2004 \r
2005                                 JTreeLabel.Nodes node = JTreeLabel.Nodes.getNode(path.getPathComponent(1).toString());\r
2006                                 String value = path.getLastPathComponent().toString();\r
2007                         \r
2008                                 switch ( node ) {\r
2009                                 case DATE:\r
2010                                         if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
2011                                                 // 現在日時に移動する\r
2012                                                 redrawByNow(IterationType.ALL);\r
2013                                         }\r
2014                                         else {\r
2015                                                 redrawByDate(value,IterationType.ALL);\r
2016                                         }\r
2017                                         if ( env.isPagerEnabled() ) {\r
2018                                                 setPagerEnabled(true);\r
2019                                         }\r
2020                                         break;\r
2021                                 case TERRA:\r
2022                                         if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
2023                                                 redrawByNow(IterationType.TERRA);\r
2024                                         }\r
2025                                         else {\r
2026                                                 redrawByDate(value,IterationType.TERRA);\r
2027                                         }\r
2028                                         if ( env.isPagerEnabled() ) {\r
2029                                                 setPagerEnabled(true);\r
2030                                         }\r
2031                                         break;\r
2032                                 case BS:\r
2033                                         if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
2034                                                 redrawByNow(IterationType.BS);\r
2035                                         }\r
2036                                         else {\r
2037                                                 redrawByDate(path.getLastPathComponent().toString(),IterationType.BS);\r
2038                                         }\r
2039                                         if ( env.isPagerEnabled() ) {\r
2040                                                 setPagerEnabled(true);\r
2041                                         }\r
2042                                         break;\r
2043                                 case CS:\r
2044                                         if ( JTreeLabel.Nodes.NOW.getLabel().equals(value) ) {\r
2045                                                 redrawByNow(IterationType.CS);\r
2046                                         }\r
2047                                         else {\r
2048                                                 redrawByDate(path.getLastPathComponent().toString(),IterationType.CS);\r
2049                                         }\r
2050                                         if ( env.isPagerEnabled() ) {\r
2051                                                 setPagerEnabled(true);\r
2052                                         }\r
2053                                         break;\r
2054                                 case BCAST:\r
2055                                         redrawByCenter(value);\r
2056                                         if ( env.isPagerEnabled() ) {\r
2057                                                 setPagerEnabled(false);\r
2058                                         }\r
2059                                         break;\r
2060                                 case PASSED:\r
2061                                         PassedProgram passed = tvprograms.getPassed();\r
2062                                         if ( passed.loadAllCenters(value) ) {\r
2063                                                 redrawByDate(value, IterationType.PASSED);\r
2064                                         }\r
2065                                         else {\r
2066                                                 MWin.appendError(ERRID+"過去ログが存在しません: "+value);\r
2067                                                 ringBeep();\r
2068                                         }\r
2069                                         if ( env.isPagerEnabled() ) {\r
2070                                                 setPagerEnabled(true);\r
2071                                         }\r
2072                                         break;\r
2073                                 default:\r
2074                                         break;\r
2075                                 }\r
2076                                 \r
2077                                 jLabel_tree.setView(node, value);\r
2078                                 return;\r
2079                         }\r
2080                         \r
2081                         // なんかおかしいのでデフォルト選択にまわす\r
2082                         CommonUtils.printStackTrace();\r
2083                         MWin.appendError(ERRID+"バグの可能性あり");\r
2084                         //redrawByNow(IterationType.ALL);\r
2085                         //jLabel_tree.setView(JTreeLabel.Nodes.DATE, JTreeLabel.Nodes.NOW.getLabel());\r
2086                 }\r
2087         };\r
2088         \r
2089         /**\r
2090          * フルスクリーン時にツリーを隠したりするの\r
2091          */\r
2092         private final MouseListener ml_treehide = new MouseAdapter() {\r
2093                 public void mouseEntered(MouseEvent e) {\r
2094                         if (isFullScreen()) {\r
2095                                 setExpandTree();\r
2096                                 //StdAppendMessage("Show tree (N)");\r
2097                         }\r
2098                 }\r
2099                 public void mouseExited(MouseEvent e) {\r
2100                         if (isFullScreen()) {\r
2101                                 setCollapseTree();\r
2102                                 //StdAppendMessage("Hide tree (N)");\r
2103                         }\r
2104                 }\r
2105         };\r
2106         \r
2107         /**\r
2108          * 放送局名につけるリスナー(ダブルクリックで一週間表示にジャンプ)\r
2109          */\r
2110         private final MouseAdapter cnMouseAdapter = new MouseAdapter() {\r
2111                 \r
2112                 private Color bgcolor = null;\r
2113                 \r
2114                 public void mouseExited(MouseEvent e) {\r
2115                         ((JLabel)e.getSource()).setBackground(bgcolor);\r
2116                 }\r
2117                 public void mouseEntered(MouseEvent e) {\r
2118                         bgcolor = ((JLabel)e.getSource()).getBackground();\r
2119                         ((JLabel)e.getSource()).setBackground(new Color(180,180,255));\r
2120                 }\r
2121                 \r
2122                 public void mouseClicked(MouseEvent e) {\r
2123                         if (e.getButton() == MouseEvent.BUTTON1) {\r
2124                                 if (e.getClickCount() == 2) {\r
2125                                         if ( cur_tuner == IterationType.PASSED ) {\r
2126                                                 MWin.appendMessage(MSGID+"過去ログでは一局表示に切り替えられません");\r
2127                                                 return;\r
2128                                         }\r
2129                                         \r
2130                                         // 右ダブルクリックで局表示に切り替え\r
2131                                         String center = ((JLabel)e.getSource()).getText();\r
2132                                         StdAppendMessage(MSGID+"一局表示に切り替え:"+center);\r
2133                                         //redrawByCenter(center);\r
2134                                         jLabel_tree.setView(JTreeLabel.Nodes.BCAST, center);\r
2135                                         reselectTree();\r
2136                                 }\r
2137                         }\r
2138                 }\r
2139         };\r
2140         \r
2141         /**\r
2142          * 日付枠につけるリスナー(ダブルクリックで放送局別表示にジャンプ)\r
2143          */\r
2144         private final MouseAdapter tbMouseAdapter = new MouseAdapter() {\r
2145                 private Color bgcolor = null;\r
2146                 //\r
2147                 public void mouseExited(MouseEvent e) {\r
2148                         ((JTXTLabel)e.getSource()).setBackground(bgcolor);\r
2149                 }\r
2150                 public void mouseEntered(MouseEvent e) {\r
2151                         bgcolor = ((JTXTLabel)e.getSource()).getBackground();\r
2152                         ((JTXTLabel)e.getSource()).setBackground(new Color(180,180,255));\r
2153                 }\r
2154                 \r
2155                 //\r
2156                 public void mouseClicked(MouseEvent e) {\r
2157                         if (e.getButton() == MouseEvent.BUTTON1) {\r
2158                                 if (e.getClickCount() == 2) {\r
2159                                         // 右ダブルクリックで日付表示に切り替え\r
2160                                         String date = ((JTXTLabel)e.getSource()).getValue();\r
2161                                         StdAppendMessage(MSGID+"日付表示に切り替え:"+date);\r
2162                                         //redrawByDate(date, -1);\r
2163                                         jLabel_tree.setView(JTreeLabel.Nodes.DATE, date);\r
2164                                         reselectTree();\r
2165                                 }\r
2166                         }\r
2167                 }\r
2168         };\r
2169         \r
2170         \r
2171         \r
2172         /*******************************************************************************\r
2173          * コンポーネント\r
2174          ******************************************************************************/\r
2175 \r
2176         private JDetailPanel getJTextPane_detail() {\r
2177                 if (jTextPane_detail == null) {\r
2178                         jTextPane_detail = new JDetailPanel();\r
2179                         jTextPane_detail.setRows(bounds.getDetailRows());\r
2180                         //Dimension d = jTextPane_detail.getMaximumSize();\r
2181                         //d.height = bounds.getDetailAreaHeight();\r
2182                         //jTextPane_detail.setPreferredSize(d);\r
2183                         //jTextPane_detail.setVerticalAlignment(JLabel.TOP);\r
2184                         //jTextPane_detail.setHorizontalAlignment(JLabel.LEFT);\r
2185                 }\r
2186                 return jTextPane_detail;\r
2187         }\r
2188         \r
2189         private JSplitPane getJSplitPane_view() {\r
2190                 if ( jSplitPane_view == null ) {\r
2191                         \r
2192                         jSplitPane_view = new JSplitPane() {\r
2193 \r
2194                                 private static final long serialVersionUID = 1L;\r
2195 \r
2196                                 @Override\r
2197                                 public void setDividerLocation(int loc) {\r
2198                                         setDividerEnvs(loc);\r
2199                                         super.setDividerLocation(loc);\r
2200                                 }\r
2201                         };\r
2202                         \r
2203                         jSplitPane_view.setLeftComponent(getJPanel_tree());\r
2204                         jSplitPane_view.setRightComponent(getJScrollPane_space_main());\r
2205                         setExpandTree();\r
2206                 }\r
2207                 return jSplitPane_view;\r
2208         }\r
2209 \r
2210         private JPanel getJPanel_tree() {\r
2211                 if (jPanel_tree == null) {\r
2212                         jPanel_tree = new JPanel();\r
2213 \r
2214                         jPanel_tree.setLayout(new BorderLayout());\r
2215                         jPanel_tree.add(getJScrollPane_tree_top(), BorderLayout.PAGE_START);\r
2216                         jPanel_tree.add(getJScrollPane_tree(), BorderLayout.CENTER);\r
2217                 }\r
2218                 return jPanel_tree;\r
2219         }\r
2220         \r
2221         //\r
2222         private JScrollPane getJScrollPane_tree_top() {\r
2223                 if (jScrollPane_tree_top == null) {\r
2224                         jScrollPane_tree_top = new JScrollPane();\r
2225                         jScrollPane_tree_top.setViewportView(getJLabel_tree());\r
2226                         jScrollPane_tree_top.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);\r
2227                         jScrollPane_tree_top.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);\r
2228                 }\r
2229                 return jScrollPane_tree_top;\r
2230         }\r
2231         private JTreeLabel getJLabel_tree() {\r
2232                 if (jLabel_tree == null) {\r
2233                         jLabel_tree = new JTreeLabel();\r
2234                         \r
2235                         Dimension d = jLabel_tree.getMaximumSize();\r
2236                         d.height = bounds.getBangumiColumnHeight();\r
2237                         jLabel_tree.setPreferredSize(d);\r
2238                         jLabel_tree.setOpaque(true);\r
2239                         jLabel_tree.setBackground(Color.WHITE);\r
2240                 }\r
2241                 return jLabel_tree;\r
2242         }\r
2243         \r
2244         private JScrollPane getJScrollPane_tree() {\r
2245                 if (jScrollPane_tree == null) {\r
2246                         jScrollPane_tree = new JScrollPane();\r
2247 \r
2248                         jScrollPane_tree.setViewportView(getJTree_tree());\r
2249                 }\r
2250                 return jScrollPane_tree;\r
2251         }\r
2252         \r
2253         /**\r
2254          * ツリーの作成\r
2255          */\r
2256         private JTree getJTree_tree() {\r
2257                 if (jTree_tree == null) {\r
2258 \r
2259                         // ツリーの作成\r
2260                         jTree_tree = new JTree();\r
2261                         jTree_tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);\r
2262                         jTree_tree.setRootVisible(env.getRootNodeVisible());\r
2263                         \r
2264                         // ノードの作成\r
2265                         jTree_tree.setModel(new DefaultTreeModel(getTreeNodes()));\r
2266                         \r
2267                         // ツリーの展開状態の復帰\r
2268                         undoTreeExpansion();\r
2269                         \r
2270                         // ツリーの開閉時に状態を保存する\r
2271                         jTree_tree.addTreeExpansionListener(tel_nodeexpansion);\r
2272                         \r
2273                         // フルスクリーンの時に使う(新聞形式のツリーを自動的に隠す)\r
2274                         jTree_tree.addMouseListener(ml_treehide);\r
2275                 }\r
2276                 return jTree_tree;\r
2277         }\r
2278 \r
2279         /**\r
2280          * ツリーのノード作成\r
2281          */\r
2282         private DefaultMutableTreeNode getTreeNodes() {\r
2283                 \r
2284                 paperRootNode = new DefaultMutableTreeNode(JTreeLabel.Nodes.ROOT.getLabel());\r
2285                 \r
2286                 dateNode        = new DefaultMutableTreeNode(JTreeLabel.Nodes.DATE.getLabel());\r
2287                 dgNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.TERRA.getLabel());\r
2288                 bsNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.BS.getLabel());\r
2289                 csNode          = new DefaultMutableTreeNode(JTreeLabel.Nodes.CS.getLabel());\r
2290                 centerNode      = new DefaultMutableTreeNode(JTreeLabel.Nodes.BCAST.getLabel());\r
2291                 passedNode      = new DefaultMutableTreeNode(JTreeLabel.Nodes.PASSED.getLabel());\r
2292                 \r
2293                 paperRootNode.add(dateNode);\r
2294                 paperRootNode.add(dgNode);\r
2295                 paperRootNode.add(bsNode);\r
2296                 paperRootNode.add(csNode);\r
2297                 paperRootNode.add(centerNode);\r
2298                 paperRootNode.add(passedNode);\r
2299 \r
2300                 // 子の描画\r
2301                 redrawTreeByDate();\r
2302                 redrawTreeByCenter();\r
2303                 redrawTreeByPassed();\r
2304                 \r
2305                 return paperRootNode;\r
2306         }\r
2307         \r
2308         private void undoTreeExpansion() {\r
2309                         \r
2310                 // 展開状態の復帰\r
2311                 stopTreeListener();\r
2312                 \r
2313                 // 展開状態の記憶域の初期化\r
2314                 ter = new TreeExpansionReg(jTree_tree, TreeExpRegFile_Paper);\r
2315                 try {\r
2316                         ter.load();\r
2317                 }\r
2318                 catch (Exception e) {\r
2319                         MWin.appendMessage(ERRID+"ツリー展開情報の解析で問題が発生しました");\r
2320                         e.printStackTrace();\r
2321                 }\r
2322 \r
2323                 // 状態を復元する\r
2324                 ArrayList<TreePath> tpa = ter.get();\r
2325                 for ( TreePath path : tpa ) {\r
2326                         jTree_tree.expandPath(path);\r
2327                 }\r
2328                 \r
2329                 startTreeListener();\r
2330         }\r
2331 \r
2332         private JScrollPane getJScrollPane_space_main() {\r
2333                 if (jScrollPane_space_main == null) {\r
2334                         jScrollPane_space_main = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);\r
2335                         jScrollPane_space_main.getVerticalScrollBar().setUnitIncrement(bounds.getBangumiColumnHeight());\r
2336                         jScrollPane_space_main.getHorizontalScrollBar().setUnitIncrement(bounds.getBangumiColumnWidth());\r
2337                         \r
2338                         //jScrollPane_space_main.setViewportView(getJLayeredPane_space_main_view());\r
2339                         jScrollPane_space_main.setColumnHeaderView(getJPanel_space_top_view());\r
2340                         jScrollPane_space_main.setRowHeaderView(getJPanel_space_side_view());\r
2341                         \r
2342                         vport = jScrollPane_space_main.getViewport();\r
2343                 }\r
2344                 return jScrollPane_space_main;\r
2345         }\r
2346         \r
2347         private JPanel getJPanel_space_top_view() {\r
2348                 if (jPanel_space_top_view == null) {\r
2349                         jPanel_space_top_view = new JPanel();\r
2350                         jPanel_space_top_view.setLayout(new SpringLayout());\r
2351                 }\r
2352                 return jPanel_space_top_view;\r
2353         }\r
2354         \r
2355         private JPanel getJPanel_space_side_view() {\r
2356                 if (jPanel_space_side_view == null) {\r
2357                         jPanel_space_side_view = new JPanel();\r
2358                         jPanel_space_side_view.setLayout(null);\r
2359                 }\r
2360                 return jPanel_space_side_view;\r
2361         }\r
2362         \r
2363         \r
2364         /*\r
2365          * 以下は、pcwinから呼び出されるメソッドをまとめたもの \r
2366          */\r
2367         \r
2368         // 時間枠のコンポーネント\r
2369         public Component[] getTimebarComponents() {\r
2370                 return jPanel_space_side_view.getComponents();\r
2371         }\r
2372 \r
2373         // 背景色ほかの変更\r
2374         public void updateColors(Env ec,PaperColorsMap pc) {\r
2375                 _updPColors(ec, pc, frameUsed);\r
2376                 \r
2377                 if ( env.getDrawcacheEnable() ) {\r
2378                         _updPColors(ec, pc, frameUsedByDate);\r
2379                 }\r
2380                 \r
2381                 // マウスオーバー時のハイライト\r
2382                 /* no proc. */\r
2383                 \r
2384                 // タイムバーの色\r
2385                 for ( Component c : getTimebarComponents() ) {\r
2386                         if ( c instanceof JTimebarLabel ) {\r
2387                                 int j = Integer.valueOf(((JTimebarLabel) c).getTs());\r
2388                                 if ( j >=6 && j <= 11 ) {\r
2389                                         c.setBackground(ec.getTimebarColor());\r
2390                                 }\r
2391                                 else if ( j >=12 && j <= 17 ) {\r
2392                                         c.setBackground(ec.getTimebarColor2());\r
2393                                 }\r
2394                                 else if ( j >=18 && j <= 23 ) {\r
2395                                         c.setBackground(ec.getTimebarColor3());\r
2396                                 }\r
2397                                 else {\r
2398                                         c.setBackground(ec.getTimebarColor4());\r
2399                                 }\r
2400                         }\r
2401                 }\r
2402         }\r
2403         \r
2404         // サイズの変更\r
2405         public void updateBounds(Env ec, Bounds bc) {\r
2406                 \r
2407                 int maxCol = jPanel_space_top_view.getComponentCount();\r
2408                 float maxRow = 24*60;\r
2409                 \r
2410                 float phm = bc.getPaperHeightMultiplier() * paperHeightZoom ;\r
2411 \r
2412                 int vieww = maxCol * bc.getBangumiColumnWidth();\r
2413                 int viewh = (int) Math.ceil(maxRow * phm);\r
2414 \r
2415                 // 変更前のビューの位置\r
2416                 Point vp = vport.getViewPosition();\r
2417                 float vh = vport.getView().getPreferredSize().height;\r
2418                 \r
2419                 // タイムバーのサイズ変更\r
2420                 {\r
2421                         int h = (int) Math.ceil(60.0F*phm);\r
2422                         int row = 0;\r
2423                         for ( Component b0 : jPanel_space_side_view.getComponents() ) {\r
2424                                 b0.setBounds(0,(int) Math.ceil((float)row*phm),bc.getTimebarColumnWidth(),h);\r
2425                                 row += 60;\r
2426                         }\r
2427                         \r
2428                         Dimension d = jPanel_space_side_view.getPreferredSize();\r
2429                         d.height = viewh;\r
2430                         jPanel_space_side_view.setPreferredSize(d);\r
2431                 }\r
2432                 \r
2433                 // 放送局名(or日付)のサイズ変更\r
2434                 {\r
2435                         for ( int col=0; col<jPanel_space_top_view.getComponentCount(); col++ ) {\r
2436                                 Component b1 = jPanel_space_top_view.getComponent(col);\r
2437                                 b1.setBounds(\r
2438                                                 bc.getBangumiColumnWidth() * col,\r
2439                                                 0,\r
2440                                                 bc.getBangumiColumnWidth(),\r
2441                                                 bc.getBangumiColumnHeight());\r
2442                         }\r
2443                         Dimension d = jPanel_space_top_view.getPreferredSize();\r
2444                         d.width = vieww;\r
2445                         jPanel_space_top_view.setPreferredSize(d);\r
2446                 }\r
2447                 \r
2448                 // 各番組枠のサイズ変更・検索マッチ枠の表示変更\r
2449                 {\r
2450                         {\r
2451                                 _updPBounds(bc, frameUsed);\r
2452                                 _updPBorders(ec, bc, frameUsed);\r
2453                                 \r
2454                                 Dimension d = jLayeredPane_space_main_view.getPreferredSize();\r
2455                                 d.width = vieww;\r
2456                                 d.height = viewh;\r
2457                                 jLayeredPane_space_main_view.setPreferredSize(d);\r
2458                         }\r
2459                         \r
2460                         if ( ec.getDrawcacheEnable() ) {\r
2461                                 _updPBounds(bc, frameUsedByDate);\r
2462                                 _updPBorders(ec, bc, frameUsedByDate);\r
2463                                 \r
2464                                 for ( JLayeredPane pane : jLayeredPane_space_main_view_byDate ) {\r
2465                                         Dimension d = pane.getPreferredSize();\r
2466                                         d.width = vieww;\r
2467                                         d.height = viewh;\r
2468                                         pane.setPreferredSize(d);\r
2469                                 }\r
2470                         }\r
2471                 }\r
2472                 \r
2473                 // 予約枠・ピックアップ枠のサイズ変更&色変更\r
2474                 {\r
2475                         JRMLabel.setColumnWidth(bc.getBangumiColumnWidth());\r
2476                         JRMLabel.setHeightMultiplier(phm);\r
2477                         \r
2478                         for ( JRMLabel rb : reserveBorders ) {\r
2479                                 \r
2480                                 rb.reVBounds();\r
2481                                 \r
2482                                 if ( rb.getEncoder().equals(TUNERLABEL_PICKUP) ) {\r
2483                                         rb.setEncBackground(ec.getPickedColor());\r
2484                                         rb.setEncForeground(ec.getPickedFontColor());\r
2485                                         rb.setBorder(new LineBorder(ec.getPickedColor(),4));\r
2486                                 }\r
2487                                 else if ( rb.getExec() ) {\r
2488                                         rb.setEncForeground(ec.getExecOnFontColor());\r
2489                                 }\r
2490                                 else {\r
2491                                         rb.setEncForeground(ec.getExecOffFontColor());\r
2492                                 }\r
2493                                 rb.repaint();\r
2494                         }\r
2495                 }\r
2496                 \r
2497                 // 現在時刻線の位置変更\r
2498                 setTimelinePos(false);\r
2499                 \r
2500                 // 枠のサイズを更新したのでupdateUI()\r
2501                 jScrollPane_space_main.updateUI();\r
2502                 \r
2503                 // ビューの位置調整\r
2504                 vp.y = (int)Math.ceil(maxRow * (float)vp.y * phm / vh);\r
2505                 vport.setViewPosition(vp);\r
2506                 \r
2507         }\r
2508         \r
2509         // フォントの変更\r
2510         public void updateFonts(Env ec) {\r
2511                 JTXTButton.setShowStart(ec.getShowStart());\r
2512                 JTXTButton.setSplitEpno(ec.getSplitEpno());\r
2513                 JTXTButton.setShowDetail(ec.getShowDetail());\r
2514                 JTXTButton.setDetailTab(ec.getDetailTab());\r
2515                 JTXTButton.setDetailRows(ec.getDetailRows());\r
2516                 \r
2517                 JTXTButton.setTitleFont(ec.getTitleFont());\r
2518                 JTXTButton.setTitleFontStyle(ec.getTitleFontStyle());\r
2519                 JTXTButton.setDetailFont(ec.getDetailFont());\r
2520                 JTXTButton.setDetailFontStyle(ec.getDetailFontStyle());\r
2521                 JTXTButton.setTitleFontSize(ec.getTitleFontSize());\r
2522                 JTXTButton.setTitleFontColor(ec.getTitleFontColor());\r
2523                 JTXTButton.setDetailFontSize(ec.getDetailFontSize());\r
2524                 JTXTButton.setDetailFontColor(ec.getDetailFontColor());\r
2525                 JTXTButton.setAAHint(ec.getPaperAAMode().getHint());\r
2526         }\r
2527         \r
2528         // 再描画?\r
2529         public void updateRepaint() {\r
2530                 _updPRepaint(frameUsed);\r
2531                 \r
2532                 if ( env.getDrawcacheEnable() ) {\r
2533                         _updPRepaint(frameUsedByDate);\r
2534                 }\r
2535         }\r
2536         \r
2537         // 以下共通部品\r
2538         \r
2539         private void _updPColors(Env ec, PaperColorsMap pc, ArrayList<JTXTButton> fa) {\r
2540                 for ( JTXTButton b2 : fa ) {\r
2541                         b2.setBackground(pc.get(b2.getInfo().genre));\r
2542                 }\r
2543         }\r
2544 \r
2545         private void _updPBounds(Bounds bc, ArrayList<JTXTButton> fa) {\r
2546                 \r
2547                 JTXTButton.setColumnWidth(bc.getBangumiColumnWidth());\r
2548                 JTXTButton.setHeightMultiplier(bc.getPaperHeightMultiplier() * paperHeightZoom);\r
2549                 \r
2550                 for ( JTXTButton b2 :  fa ) {\r
2551                         b2.reVBounds();\r
2552                 }\r
2553         }\r
2554         \r
2555         private void _updPBorders(Env ec, Bounds bc, ArrayList<JTXTButton> fa) {\r
2556                 dborder.setDashColor(ec.getMatchedBorderColor());\r
2557                 dborder.setThickness(ec.getMatchedBorderThickness());\r
2558                 for ( JTXTButton b2 :  fa ) {\r
2559                         if ( bc.getShowMatchedBorder() && b2.isStandby() ) {\r
2560                                 if ( b2.getBorder() != dborder )\r
2561                                         b2.setBorder(dborder);\r
2562                         }\r
2563                         else {\r
2564                                 if ( b2.getBorder() != lborder )\r
2565                                         b2.setBorder(lborder);\r
2566                         }\r
2567                 }\r
2568         }\r
2569         \r
2570         private void _updPRepaint(ArrayList<JTXTButton> fa) {\r
2571                 for ( JTXTButton b2 :  fa ) {\r
2572                         b2.forceRepaint();\r
2573                 }\r
2574         }\r
2575 \r
2576 }\r